@hubspot/npm-scripts
Advanced tools
+1
-1
| { | ||
| "name": "@hubspot/npm-scripts", | ||
| "version": "0.0.5-experimental.0", | ||
| "version": "0.0.5-experimental.1", | ||
| "description": "Scripts for working with npm packages in the HubSpot ecosystem", | ||
@@ -5,0 +5,0 @@ "author": "", |
+0
-4
@@ -43,7 +43,3 @@ import { TAG, TAG_OPTIONS, VERSION_INCREMENT_OPTIONS, VSCODE_VERSION_INCREMENT_OPTIONS } from './constants/release.js'; | ||
| export interface VscodeReleaseScriptBase { | ||
| repositoryUrl: string; | ||
| mainBranch?: string; | ||
| marketplaceUrl: string; | ||
| extensionName: string; | ||
| slackChannel?: string; | ||
| build?: () => Promise<void> | void; | ||
@@ -50,0 +46,0 @@ } |
+38
-145
@@ -1,2 +0,2 @@ | ||
| import { exec as _exec, spawn } from 'node:child_process'; | ||
| import { exec as _exec } from 'node:child_process'; | ||
| import { promisify } from 'util'; | ||
@@ -13,71 +13,3 @@ import yargs from 'yargs'; | ||
| const exec = promisify(_exec); | ||
| function runVsceCommand(script) { | ||
| return new Promise((resolve, reject) => { | ||
| const child = spawn('yarn', [script], { | ||
| stdio: ['pipe', 'inherit', 'inherit'], | ||
| }); | ||
| child.stdin?.write('y\n'); | ||
| child.stdin?.end(); | ||
| child.on('close', code => { | ||
| if (code !== EXIT_CODES.SUCCESS) { | ||
| reject(new Error(`"yarn ${script}" failed with exit code ${code}`)); | ||
| } | ||
| else { | ||
| resolve(); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
| async function getLastTag() { | ||
| try { | ||
| const { stdout } = await exec('git describe --tags --abbrev=0'); | ||
| return stdout.trim(); | ||
| } | ||
| catch { | ||
| return null; | ||
| } | ||
| } | ||
| async function createDraftPR({ baseBranch, title, body, }) { | ||
| logger.log('\nCreating draft pull request...'); | ||
| try { | ||
| const { stdout } = await exec(`gh pr create --draft --base ${baseBranch} --title "${title}" --body "${body.replace(/"/g, '\\"')}"`); | ||
| const prUrl = stdout.trim(); | ||
| logger.success(`Draft PR created: ${prUrl}`); | ||
| return prUrl; | ||
| } | ||
| catch (error) { | ||
| logger.error('Failed to create pull request:', error); | ||
| process.exit(EXIT_CODES.ERROR); | ||
| } | ||
| } | ||
| async function createGithubRelease({ tag, title, notes, }) { | ||
| logger.log('\nCreating GitHub release...'); | ||
| try { | ||
| const { stdout } = await exec(`gh release create ${tag} --title "${title}" --notes "${notes.replace(/"/g, '\\"')}" --draft`); | ||
| const releaseUrl = stdout.trim(); | ||
| logger.success(`Draft GitHub release created: ${releaseUrl}`); | ||
| return releaseUrl; | ||
| } | ||
| catch (error) { | ||
| logger.error('Failed to create GitHub release:', error); | ||
| process.exit(EXIT_CODES.ERROR); | ||
| } | ||
| } | ||
| function generateSlackMessage({ extensionName, version, marketplaceUrl, repositoryUrl, }) { | ||
| return [ | ||
| `\u{1F4E6} ${extensionName} VSCode Extension v${version} Released`, | ||
| '', | ||
| '*Links:*', | ||
| `- Marketplace: ${marketplaceUrl}`, | ||
| `- Changelog: ${repositoryUrl}/blob/master/CHANGELOG.md`, | ||
| ].join('\n'); | ||
| } | ||
| function printSlackMessage(message, channel) { | ||
| logger.log('\n' + '='.repeat(60)); | ||
| logger.log(`Slack message for ${channel}:`); | ||
| logger.log('='.repeat(60)); | ||
| logger.log(message); | ||
| logger.log('='.repeat(60)); | ||
| } | ||
| function buildHandler({ build, packageName, localVersion, repositoryUrl, mainBranch, marketplaceUrl, extensionName, slackChannel, }) { | ||
| function buildHandler({ build, packageName, localVersion, mainBranch, }) { | ||
| return async function handler({ versionIncrement, dryRun, skipTests, }) { | ||
@@ -87,3 +19,2 @@ try { | ||
| const isDryRun = Boolean(dryRun); | ||
| const resolvedSlackChannel = slackChannel || '#cli-dev'; | ||
| if (branch !== mainBranch) { | ||
@@ -93,2 +24,18 @@ logger.error(`Releases can only be published from the ${mainBranch} branch. Current branch: ${branch}`); | ||
| } | ||
| const newVersion = semver.inc(localVersion, versionIncrement); | ||
| if (!newVersion) { | ||
| logger.error('Error incrementing version.'); | ||
| process.exit(EXIT_CODES.ERROR); | ||
| } | ||
| if (isDryRun) { | ||
| logger.log('\nDRY RUN'); | ||
| } | ||
| logger.log(`\nCurrent version: ${localVersion}`); | ||
| logger.log(`New version to release: ${newVersion}`); | ||
| const shouldRelease = await confirm({ | ||
| message: `Release version ${newVersion}?`, | ||
| }); | ||
| if (!shouldRelease) { | ||
| process.exit(EXIT_CODES.SUCCESS); | ||
| } | ||
| if (!skipTests) { | ||
@@ -111,3 +58,3 @@ const userHasTested = await confirm({ | ||
| logger.log('\nPackaging pre-release...'); | ||
| await runVsceCommand('vsce:prerelease'); | ||
| await exec('npx vsce package --pre-release -o releases/'); | ||
| logger.success('Pre-release package created.'); | ||
@@ -130,41 +77,7 @@ logger.log('\nTo test the package locally:'); | ||
| } | ||
| const newVersion = semver.inc(localVersion, versionIncrement); | ||
| if (!newVersion) { | ||
| logger.error('Error incrementing version.'); | ||
| process.exit(EXIT_CODES.ERROR); | ||
| } | ||
| if (isDryRun) { | ||
| logger.log('\nDRY RUN'); | ||
| } | ||
| logger.log(`\nCurrent version: ${localVersion}`); | ||
| logger.log(`New version to release: ${newVersion}`); | ||
| const shouldRelease = await confirm({ | ||
| message: `Release version ${newVersion}?`, | ||
| }); | ||
| if (!shouldRelease) { | ||
| process.exit(EXIT_CODES.SUCCESS); | ||
| } | ||
| logger.log(`\nUpdating version to ${newVersion}...`); | ||
| await exec(`yarn version --no-git-tag-version --new-version ${newVersion}`); | ||
| logger.success('Version updated in package.json'); | ||
| const lastTag = await getLastTag(); | ||
| if (lastTag) { | ||
| logger.log(`\nUpdate CHANGELOG.md with the changes since ${lastTag}:`); | ||
| logger.log(` ${repositoryUrl}/compare/${lastTag}...${mainBranch}`); | ||
| await open(`${repositoryUrl}/compare/${lastTag}...${mainBranch}`); | ||
| } | ||
| else { | ||
| logger.log('\nUpdate CHANGELOG.md with the changes for this release.'); | ||
| } | ||
| const changelogUpdated = await confirm({ | ||
| message: 'Have you updated CHANGELOG.md?', | ||
| default: false, | ||
| }); | ||
| if (!changelogUpdated) { | ||
| logger.log('Release aborted.'); | ||
| logger.log('Note: package.json version has been updated locally. Revert if needed.'); | ||
| process.exit(EXIT_CODES.SUCCESS); | ||
| } | ||
| logger.log('\nPackaging regular release...'); | ||
| await runVsceCommand('vsce:package'); | ||
| await exec('npx vsce package -o releases/'); | ||
| logger.success('Regular release package created.'); | ||
@@ -177,20 +90,14 @@ const confirmRegularRelease = await confirm({ | ||
| logger.log('Release aborted after regular release packaging.'); | ||
| logger.log('Note: package.json version and CHANGELOG.md have been updated locally. Revert if needed.'); | ||
| logger.log('Note: package.json version has been updated locally. Revert if needed.'); | ||
| process.exit(EXIT_CODES.SUCCESS); | ||
| } | ||
| const releaseBranch = `release/v${newVersion}`; | ||
| const releaseNotes = 'See CHANGELOG.md for details.'; | ||
| if (isDryRun) { | ||
| logger.log(`\nDry run: would create branch ${releaseBranch}`); | ||
| logger.log('Dry run: would commit package.json + CHANGELOG.md'); | ||
| logger.log(`Dry run: would push ${releaseBranch}`); | ||
| logger.log(`Dry run: would create draft PR against ${mainBranch}`); | ||
| logger.log(`Dry run: would create draft GH release v${newVersion}`); | ||
| printSlackMessage(generateSlackMessage({ | ||
| extensionName, | ||
| version: newVersion, | ||
| marketplaceUrl, | ||
| repositoryUrl, | ||
| }), resolvedSlackChannel); | ||
| logger.log('\nDry run release finished successfully.'); | ||
| logger.log('\nDry run complete. Would have:'); | ||
| logger.log(` 1. Created branch ${releaseBranch}`); | ||
| logger.log(` 2. Committed package.json version bump`); | ||
| logger.log(` 3. Pushed ${releaseBranch} to origin`); | ||
| logger.log(` 4. Created draft PR against ${mainBranch}`); | ||
| logger.log(` 5. Created draft GH release v${newVersion}`); | ||
| logger.log(` 6. Opened VS Code Marketplace for .vsix upload`); | ||
| process.exit(EXIT_CODES.SUCCESS); | ||
@@ -201,30 +108,16 @@ } | ||
| logger.log('Committing changes...'); | ||
| await exec('git add package.json CHANGELOG.md'); | ||
| await exec('git add package.json'); | ||
| await exec(`git commit -m "Bump version to ${newVersion}"`); | ||
| logger.log(`Pushing ${releaseBranch}...`); | ||
| await exec(`git push -u origin ${releaseBranch}`); | ||
| const prUrl = await createDraftPR({ | ||
| baseBranch: mainBranch, | ||
| title: `Release v${newVersion}`, | ||
| body: `## Release v${newVersion}\n\n${releaseNotes}`, | ||
| }); | ||
| const releaseUrl = await createGithubRelease({ | ||
| tag: `v${newVersion}`, | ||
| title: `Version ${newVersion}`, | ||
| notes: releaseNotes, | ||
| }); | ||
| printSlackMessage(generateSlackMessage({ | ||
| extensionName, | ||
| version: newVersion, | ||
| marketplaceUrl, | ||
| repositoryUrl, | ||
| }), resolvedSlackChannel); | ||
| logger.log('\nCreating draft pull request...'); | ||
| const { stdout: prUrl } = await exec(`gh pr create --draft --base ${mainBranch} --title "Release v${newVersion}" --body "## Release v${newVersion}"`); | ||
| logger.success(`Draft PR created: ${prUrl.trim()}`); | ||
| logger.log('\nCreating draft GitHub release...'); | ||
| const { stdout: releaseUrl } = await exec(`gh release create v${newVersion} --title "Version ${newVersion}" --notes "" --draft`); | ||
| logger.success(`Draft GitHub release created: ${releaseUrl.trim()}`); | ||
| logger.log('\nNext steps:'); | ||
| logger.log(' 1. Upload the .vsix file to the VS Code Marketplace:'); | ||
| logger.log(` ${marketplaceUrl.replace('/items?itemName=', '/manage/publishers/')}`); | ||
| logger.log(' 2. Copy the Slack message above and post it'); | ||
| logger.log(` 3. Merge the PR: ${prUrl}`); | ||
| if (releaseUrl) { | ||
| logger.log(` 4. Publish the GH release: ${releaseUrl}`); | ||
| } | ||
| logger.log(' 1. Upload the .vsix file to the VS Code Marketplace'); | ||
| logger.log(` 2. Merge the PR: ${prUrl.trim()}`); | ||
| logger.log(` 3. Publish the GH release: ${releaseUrl.trim()}`); | ||
| await open('https://marketplace.visualstudio.com/manage/publishers/hubspot'); | ||
@@ -231,0 +124,0 @@ logger.success(`\n${packageName} version ${newVersion} release prepared successfully!`); |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
0
-100%94254
-4.08%2021
-5.21%