-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
3,013 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
* | ||
!src/** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# @twentyfourg-developer-sdk/lxp-package-helper | ||
|
||
[![Version](https://flat.badgen.net/npm/v/@twentyfourg-developer-sdk/lxp-package-helper)](https://github.com/twentyfourg/developer-sdk/releases) [![Installs](https://flat.badgen.net/npm/dt/@twentyfourg-developer-sdk/lxp-package-helper)](https://www.npmjs.com/package/@twentyfourg-developer-sdk/lxp-package-helper) |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"name": "@twentyfourg-developer-sdk/lxp-package-helper", | ||
"version": "1.0.0-alpha.0", | ||
"description": "", | ||
"exports": "./src/index.js", | ||
"bin": "./src/index.js", | ||
"type": "module", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"license": "UNLICENSED", | ||
"repository": { | ||
"url": "https://github.com/twentyfourg/developer-sdk", | ||
"directory": "packages/lxp-package-helper" | ||
}, | ||
"dependencies": { | ||
"@twentyfourg/cloud-sdk": "^2.9.1", | ||
"axios": "^1.6.7", | ||
"chalk": "^5.3.0", | ||
"enquirer": "^2.4.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
import chalk from 'chalk'; | ||
import enquirer from 'enquirer'; | ||
import { execSync } from 'child_process'; | ||
import axios from 'axios'; | ||
import fs from 'fs'; | ||
|
||
import formatDateTime from './util/format.time.util.js'; | ||
|
||
const { prompt } = enquirer; | ||
|
||
const log = console; | ||
|
||
class Prompt { | ||
constructor() { | ||
this.packageName = 'lxp-base'; | ||
this.continuePagination = false; | ||
this.matches = []; | ||
this.deletedFiles = []; | ||
this.selection = null; | ||
} | ||
|
||
async start() { | ||
if (!process.env.LXP_PACKAGE_HELPER_GITHUB_TOKEN) | ||
throw new Error('LXP_PACKAGE_HELPER_GITHUB_TOKEN is required'); | ||
else if (!process.env.LXP_PACKAGE_HELPER_URL) | ||
throw new Error('LXP_PACKAGE_HELPER_URL is required'); | ||
|
||
const version = await this.promptVersion(); | ||
this.matches = (await this.fetchAllReleases()).filter((obj) => obj.name.includes(version)); | ||
await this.selectVersion(); | ||
await this.downloadPackage(); | ||
|
||
const { value: options } = await prompt({ | ||
type: 'multiselect', | ||
name: 'value', | ||
message: 'Package options?', | ||
limit: 4, | ||
initial: ['update', 'install', 'delete', 'commit'], | ||
choices: [ | ||
{ | ||
name: 'update', | ||
message: `Update ${this.packageName} in package.json to ${this.selection.package.name}?`, | ||
}, | ||
{ name: 'install', message: `Install ${this.packageName} to update package-lock.json?` }, | ||
{ name: 'delete', message: 'Delete old packages?' }, | ||
{ name: 'commit', message: 'Stage and commit files' }, | ||
], | ||
}); | ||
|
||
if (options.includes('update')) { | ||
this.updatePackageJsonDependency(); | ||
|
||
if (options.includes('install')) this.npmInstall(); | ||
if (options.includes('delete')) this.deleteOldPackages(); | ||
if (options.includes('commit')) this.commitFiles(); | ||
} | ||
} | ||
|
||
commitFiles() { | ||
log.info(`${chalk.bold.cyan('⚙')} ${chalk.bold('Staging and committing files')}`); | ||
|
||
execSync( | ||
`git add package.json package-lock.json ${this.selection.package.name} ${this.deletedFiles.join(' ')}`, | ||
{ | ||
stdio: 'pipe', | ||
} | ||
); | ||
execSync( | ||
`git commit -m "build: update ${this.packageName} to ${this.selection.package.name}"`, | ||
{ | ||
stdio: 'pipe', | ||
} | ||
); | ||
log.info(`${chalk.green.bold('✔')} ${chalk.bold('Staged and committed files')}`); | ||
} | ||
|
||
deleteOldPackages() { | ||
const oldPackages = fs | ||
.readdirSync(process.cwd()) | ||
.filter( | ||
(file) => | ||
!file.includes(this.selection.package.name) && | ||
file.includes(this.packageName) && | ||
file.endsWith('.tgz') | ||
); | ||
|
||
oldPackages.forEach((file) => { | ||
fs.unlinkSync(file); | ||
this.deletedFiles.push(file); | ||
log.info(`${chalk.green.bold('✔')} ${chalk.bold(`Deleted ${file}`)}`); | ||
}); | ||
} | ||
|
||
updatePackageJsonDependency() { | ||
const packageJsonPath = `${process.cwd()}/package.json`; | ||
const packageJsonText = fs.readFileSync(packageJsonPath, 'utf8'); | ||
const packageJson = JSON.parse(packageJsonText); | ||
packageJson.dependencies[this.packageName] = `file:${this.selection.package.name}`; | ||
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); | ||
|
||
log.info( | ||
`${chalk.green.bold('✔')} ${chalk.bold(`Updated ${this.packageName} to ${this.selection.package.name}`)}` | ||
); | ||
} | ||
|
||
npmInstall() { | ||
log.info(`${chalk.bold.cyan('⚙')} ${chalk.bold('Running npm install')}`); | ||
execSync(`npm install ${this.packageName}`, { stdio: 'pipe' }); | ||
log.info( | ||
`${chalk.green.bold('✔')} ${chalk.bold(`Successfully installed ${this.packageName}`)}` | ||
); | ||
} | ||
|
||
async downloadPackage() { | ||
const path = `./${this.selection.package.name}`; | ||
const url = this.selection.package.api_download_url; | ||
log.info(`${chalk.green.cyan('↓')} ${chalk.bold('Downloading:')} ${url}`); | ||
|
||
const response = await axios({ | ||
url, | ||
method: 'GET', | ||
responseType: 'stream', | ||
headers: { | ||
Accept: 'application/octet-stream', | ||
Authorization: `token ${process.env.LXP_PACKAGE_HELPER_GITHUB_TOKEN}`, | ||
}, | ||
}); | ||
|
||
const writer = fs.createWriteStream(path); | ||
response.data.pipe(writer); | ||
|
||
return new Promise((resolve, reject) => { | ||
writer.on('finish', () => { | ||
log.info(`${chalk.green.bold('✔')} ${chalk.bold('Download complete')}`); | ||
resolve(); | ||
}); | ||
writer.on('error', reject); | ||
}); | ||
} | ||
|
||
async selectVersion() { | ||
const { version } = await prompt({ | ||
type: 'select', | ||
name: 'version', | ||
message: 'Select a version', | ||
choices: this.matches.slice(0, 10).map((obj) => ({ | ||
name: obj.tag_name, | ||
message: obj.tag_name + chalk.gray(` @ ${formatDateTime(obj.published_at)}`), | ||
})), | ||
}); | ||
this.selection = this.matches.find((obj) => obj.tag_name === version); | ||
} | ||
|
||
static parseLinkHeader(header) { | ||
if (!header || header.length === 0) { | ||
return {}; | ||
} | ||
|
||
return header.split(',').reduce((acc, part) => { | ||
const section = part.split(';'); | ||
const url = section[0].replace(/<|>|\s/g, ''); | ||
const name = section[1].replace(/rel="|"/g, '').trim(); | ||
acc[name] = url; | ||
return acc; | ||
}, {}); | ||
} | ||
|
||
async fetchAllReleases( | ||
url = `${process.env.LXP_PACKAGE_HELPER_URL}/releases?page=1&per_page=100`, | ||
allReleases = [] | ||
) { | ||
const response = await fetch(url, { | ||
headers: { | ||
Authorization: `token ${process.env.LXP_PACKAGE_HELPER_GITHUB_TOKEN}`, | ||
Accept: 'application/vnd.github.v3+json', | ||
}, | ||
}); | ||
|
||
if (!response.ok) { | ||
throw new Error(`GitHub API responded with status ${response.status}`); | ||
} | ||
|
||
const data = await response.json(); | ||
allReleases = allReleases.concat(data); | ||
|
||
const links = Prompt.parseLinkHeader(response.headers.get('Link')); | ||
if (this.continuePagination && links.next) | ||
return this.fetchAllReleases(links.next, allReleases); | ||
|
||
return allReleases | ||
.map((release) => { | ||
const pkg = release.assets.find((asset) => asset.name.includes('.tgz')); | ||
return { | ||
name: release.name, | ||
tag_name: release.tag_name, | ||
published_at: release.published_at, | ||
html_url: release.html_url, | ||
package: pkg | ||
? { | ||
name: pkg.name, | ||
api_download_url: pkg.url, | ||
browser_download_url: pkg.browser_download_url, | ||
} | ||
: null, | ||
}; | ||
}) | ||
.filter((release) => release.package); | ||
} | ||
|
||
async promptVersion() { | ||
const { version } = await prompt({ | ||
type: 'input', | ||
name: 'version', | ||
message: 'Package version', | ||
}); | ||
if (!version) { | ||
log.warn(`${chalk.red.bold('✖')} ${chalk.bold('Version is required')}`); | ||
return this.promptVersion(); | ||
} | ||
return version; | ||
} | ||
} | ||
|
||
export default Prompt; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#!/usr/bin/env node | ||
|
||
import chalk from 'chalk'; | ||
|
||
import Prompt from './Prompt.js'; | ||
|
||
(async () => { | ||
try { | ||
const prompt = new Prompt(); | ||
await prompt.start(); | ||
} catch (error) { | ||
if (error) console.error(`${chalk.red.bold('✖')} ${chalk.bold(error.message || error)}`); | ||
} | ||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
export default (dateString) => { | ||
const date = new Date(dateString); | ||
|
||
const pad = (num) => (num < 10 ? `0${num}` : num); | ||
|
||
const year = date.getFullYear(); | ||
const month = pad(date.getMonth() + 1); // getMonth() is zero-indexed | ||
const day = pad(date.getDate()); | ||
let hours = date.getHours(); | ||
const minutes = pad(date.getMinutes()); | ||
const seconds = pad(date.getSeconds()); | ||
const ampm = hours >= 12 ? 'PM' : 'AM'; | ||
|
||
// Convert hours to 12-hour format | ||
hours %= 12; | ||
hours = hours ? pad(hours) : '12'; // the hour '0' should be '12' | ||
|
||
// Format date and time | ||
return `${month}/${day}/${year} ${hours}:${minutes}:${seconds} ${ampm}`; | ||
}; |