@ossjs/release
Advanced tools
@@ -50,4 +50,3 @@ import { Command } from "../Command.js"; | ||
| }); | ||
| const tagReleaseIndex = tags.indexOf(tagPointer.tag); | ||
| const previousReleaseTag = tags[tagReleaseIndex + 1]; | ||
| const previousReleaseTag = tags[tags.indexOf(tagPointer.tag) + 1]; | ||
| const previousRelease = previousReleaseTag ? await getTag(previousReleaseTag) : void 0; | ||
@@ -75,4 +74,3 @@ if (previousRelease?.hash) this.log.info(format("found preceding release \"%s\" (%s)", previousRelease.tag, previousRelease.hash)); | ||
| static async generateReleaseNotes(context, commits) { | ||
| const releaseNotes = await getReleaseNotes(commits); | ||
| return toMarkdown(context, releaseNotes); | ||
| return toMarkdown(context, await getReleaseNotes(commits)); | ||
| } | ||
@@ -79,0 +77,0 @@ static async createRelease(context, notes) { |
@@ -7,3 +7,3 @@ import { Command } from "../Command.js"; | ||
| import { getInfo } from "../utils/git/get-info.js"; | ||
| import { demandGitHubToken, demandNpmToken } from "../utils/env.js"; | ||
| import { demandGitHubToken } from "../utils/env.js"; | ||
| import { createContext } from "../utils/create-context.js"; | ||
@@ -63,6 +63,2 @@ import { parseCommits } from "../utils/git/parse-commits.js"; | ||
| }); | ||
| await demandNpmToken().catch((error) => { | ||
| this.log.error(error.message); | ||
| process.exit(1); | ||
| }); | ||
| this.revertQueue = []; | ||
@@ -78,11 +74,3 @@ const repo = await getInfo().catch((error) => { | ||
| this.log.info(format("preparing release for \"%s/%s\" from branch \"%s\"...", repo.owner, repo.name, branchName)); | ||
| /** | ||
| * Get the latest release. | ||
| * @note This refers to the latest release tag at the current | ||
| * state of the branch. Since Release doesn't do branch analysis, | ||
| * this doesn't guarantee the latest release in general | ||
| * (consider backport releases where you checkout an old SHA). | ||
| */ | ||
| const tags = await getTags(); | ||
| const latestRelease = await getLatestRelease(tags); | ||
| const latestRelease = await getLatestRelease(await getTags()); | ||
| if (latestRelease) this.log.info(format("found latest release: %s (%s)", latestRelease.tag, latestRelease.hash)); | ||
@@ -89,0 +77,0 @@ else this.log.info("found no previous releases, creating the first one..."); |
+26
-30
| //#region schema.json | ||
| var $schema = "http://json-schema.org/draft-07/schema#"; | ||
| var type = "object"; | ||
| var required = ["profiles"]; | ||
| var properties = { "profiles": { | ||
| "type": "array", | ||
| "description": "The list of release targets.", | ||
| "items": { | ||
| "type": "object", | ||
| "required": ["name", "use"], | ||
| "properties": { | ||
| "name": { | ||
| "type": "string", | ||
| "description": "The tag of this release.", | ||
| "minLength": 1 | ||
| }, | ||
| "use": { | ||
| "type": "string", | ||
| "description": "Command to run to perform the release itself.", | ||
| "minLength": 1 | ||
| }, | ||
| "prerelease": { | ||
| "type": "boolean", | ||
| "description": "Publish the package in a pre-release mode. Breaking changes in this mode produce minor version bumps instead of the major ones." | ||
| var schema_default = { | ||
| $schema: "http://json-schema.org/draft-07/schema#", | ||
| type: "object", | ||
| required: ["profiles"], | ||
| properties: { "profiles": { | ||
| "type": "array", | ||
| "description": "The list of release targets.", | ||
| "items": { | ||
| "type": "object", | ||
| "required": ["name", "use"], | ||
| "properties": { | ||
| "name": { | ||
| "type": "string", | ||
| "description": "The tag of this release.", | ||
| "minLength": 1 | ||
| }, | ||
| "use": { | ||
| "type": "string", | ||
| "description": "Command to run to perform the release itself.", | ||
| "minLength": 1 | ||
| }, | ||
| "prerelease": { | ||
| "type": "boolean", | ||
| "description": "Publish the package in a pre-release mode. Breaking changes in this mode produce minor version bumps instead of the major ones." | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } }; | ||
| var schema_default = { | ||
| $schema, | ||
| type, | ||
| required, | ||
| properties | ||
| } } | ||
| }; | ||
@@ -35,0 +31,0 @@ |
| //#region src/utils/env.d.ts | ||
| declare function demandGitHubToken(): Promise<void>; | ||
| declare function demandNpmToken(): Promise<void>; | ||
| //#endregion | ||
| export { demandGitHubToken, demandNpmToken }; | ||
| export { demandGitHubToken }; |
@@ -10,8 +10,4 @@ import { validateAccessToken } from "./github/validate-access-token.js"; | ||
| } | ||
| async function demandNpmToken() { | ||
| const { NODE_AUTH_TOKEN, NPM_AUTH_TOKEN } = process.env; | ||
| invariant(NODE_AUTH_TOKEN || NPM_AUTH_TOKEN, "Failed to publish the package: neither \"NODE_AUTH_TOKEN\" nor \"NPM_AUTH_TOKEN\" environment variables were provided."); | ||
| } | ||
| //#endregion | ||
| export { demandGitHubToken, demandNpmToken }; | ||
| export { demandGitHubToken }; |
| //#region src/utils/format-date.ts | ||
| function formatDate(date) { | ||
| const year = date.getFullYear(); | ||
| const month = (date.getMonth() + 1).toString().padStart(2, "0"); | ||
| const day = date.getDate().toString().padStart(2, "0"); | ||
| return `${year}-${month}-${day}`; | ||
| return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`; | ||
| } | ||
@@ -8,0 +5,0 @@ |
@@ -8,11 +8,8 @@ import { execAsync } from "../exec-async.js"; | ||
| if (files) await execAsync(`git add ${files.join(" ")}`); | ||
| const args = [ | ||
| await execAsync(`git commit ${[ | ||
| `-m "${message}"`, | ||
| allowEmpty ? "--allow-empty" : "", | ||
| date ? `--date "${date.toISOString()}"` : "" | ||
| ]; | ||
| await execAsync(`git commit ${args.join(" ")}`); | ||
| const hash = await execAsync("git log --pretty=format:%H -n 1").then(({ stdout }) => stdout); | ||
| const commit$1 = await getCommit(hash); | ||
| const [commitInfo] = await parseCommits([commit$1]); | ||
| ].join(" ")}`); | ||
| const [commitInfo] = await parseCommits([await getCommit(await execAsync("git log --pretty=format:%H -n 1").then(({ stdout }) => stdout))]); | ||
| return commitInfo; | ||
@@ -19,0 +16,0 @@ } |
@@ -7,3 +7,3 @@ import { getInfo } from "../git/get-info.js"; | ||
| const repo = await getInfo(); | ||
| const response = await fetch(`https://api.github.com/repos/${repo.owner}/${repo.name}/issues/${issueId}/comments`, { | ||
| invariant((await fetch(`https://api.github.com/repos/${repo.owner}/${repo.name}/issues/${issueId}/comments`, { | ||
| method: "POST", | ||
@@ -15,4 +15,3 @@ headers: { | ||
| body: JSON.stringify({ body }) | ||
| }); | ||
| invariant(response.ok, "Failed to create GitHub comment for \"%s\" issue.", issueId); | ||
| })).ok, "Failed to create GitHub comment for \"%s\" issue.", issueId); | ||
| } | ||
@@ -19,0 +18,0 @@ |
@@ -13,4 +13,3 @@ import { log } from "../logger.js"; | ||
| for (const message of messages) { | ||
| const logLevel = message.type === "error" ? "error" : "warn"; | ||
| log[logLevel](formatMessage(message, pkg)); | ||
| log[message.type === "error" ? "error" : "warn"](formatMessage(message, pkg)); | ||
| if (message.type === "error" || message.type === "warning") isValid = false; | ||
@@ -17,0 +16,0 @@ } |
@@ -7,4 +7,3 @@ import { isBreakingChange } from "../get-next-release-type.js"; | ||
| async function getReleaseNotes(commits) { | ||
| const groupedNotes = await groupCommitsByReleaseType(commits); | ||
| return await injectReleaseContributors(groupedNotes); | ||
| return await injectReleaseContributors(await groupCommitsByReleaseType(commits)); | ||
| } | ||
@@ -11,0 +10,0 @@ async function groupCommitsByReleaseType(commits) { |
+13
-12
| { | ||
| "type": "module", | ||
| "name": "@ossjs/release", | ||
| "version": "0.10.0", | ||
| "version": "0.10.1", | ||
| "description": "Minimalistic, opinionated, and predictable release automation tool.", | ||
@@ -24,4 +24,5 @@ "main": "./bin/build/index.js", | ||
| "exports": { | ||
| "./package.json": "./package.json", | ||
| ".": { | ||
| "types": "./bin/build/index.ts", | ||
| "types": "./bin/build/index.d.ts", | ||
| "default": "./bin/build/index.js" | ||
@@ -33,19 +34,19 @@ } | ||
| "fs-teardown": "^0.3.2", | ||
| "msw": "^2.11.3", | ||
| "msw": "^2.12.1", | ||
| "node-git-server": "^1.0.0-beta.30", | ||
| "portfinder": "^1.0.28", | ||
| "prettier": "^3.6.2", | ||
| "tsdown": "^0.15.5", | ||
| "typescript": "^5.9.2", | ||
| "vitest": "^3.2.4" | ||
| "tsdown": "^0.16.3", | ||
| "typescript": "^5.9.3", | ||
| "vitest": "^4.0.8" | ||
| }, | ||
| "dependencies": { | ||
| "@open-draft/deferred-promise": "^2.2.0", | ||
| "@types/conventional-commits-parser": "^5.0.1", | ||
| "@types/conventional-commits-parser": "^5.0.2", | ||
| "@types/issue-parser": "^3.0.1", | ||
| "@types/node": "^24.5.2", | ||
| "@types/node": "^24.10.1", | ||
| "@types/semver": "^7.5.1", | ||
| "@types/yargs": "^17.0.10", | ||
| "@types/yargs": "^17.0.34", | ||
| "ajv": "^8.17.1", | ||
| "conventional-commits-parser": "^6.2.0", | ||
| "conventional-commits-parser": "^6.2.1", | ||
| "get-stream": "^6.0.1", | ||
@@ -57,6 +58,6 @@ "git-log-parser": "^1.2.0", | ||
| "pino-pretty": "^7.6.1", | ||
| "publint": "^0.3.13", | ||
| "publint": "^0.3.15", | ||
| "rc": "^1.2.8", | ||
| "registry-auth-token": "^5.1.0", | ||
| "semver": "^7.5.4", | ||
| "semver": "^7.7.3", | ||
| "until-async": "^3.0.2", | ||
@@ -63,0 +64,0 @@ "yargs": "^18.0.0" |
@@ -28,3 +28,3 @@ import { until } from 'until-async' | ||
| import { createReleaseComment } from '#/src/utils/create-release-comment.js' | ||
| import { demandGitHubToken, demandNpmToken } from '#/src/utils/env.js' | ||
| import { demandGitHubToken } from '#/src/utils/env.js' | ||
| import { Notes } from '#/src/commands/notes.js' | ||
@@ -89,7 +89,2 @@ import { type ReleaseProfile } from '#/src/utils/get-config.js' | ||
| await demandNpmToken().catch((error) => { | ||
| this.log.error(error.message) | ||
| process.exit(1) | ||
| }) | ||
| this.revertQueue = [] | ||
@@ -96,0 +91,0 @@ |
@@ -191,1 +191,73 @@ import { mockCommit } from '#/test/fixtures.js' | ||
| }) | ||
| it('ignores "breaking change" text appearing in the commit message', async () => { | ||
| expect( | ||
| getNextReleaseType( | ||
| await parseCommits([ | ||
| mockCommit({ subject: 'fix: some breaking change' }), | ||
| ]), | ||
| ), | ||
| ).toBe('patch') | ||
| expect( | ||
| getNextReleaseType( | ||
| await parseCommits([ | ||
| mockCommit({ subject: 'feat: some breaking change' }), | ||
| ]), | ||
| ), | ||
| ).toBe('minor') | ||
| expect( | ||
| getNextReleaseType( | ||
| await parseCommits([ | ||
| mockCommit({ | ||
| subject: 'feat: new feature', | ||
| body: 'this is not a breaking change', | ||
| }), | ||
| ]), | ||
| ), | ||
| ).toBe('minor') | ||
| expect( | ||
| getNextReleaseType( | ||
| await parseCommits([ | ||
| mockCommit({ | ||
| subject: 'docs: abc', | ||
| body: 'should this be a BREAKING CHANGE?', | ||
| }), | ||
| ]), | ||
| ), | ||
| ).toBe(null) | ||
| }) | ||
| it('ignores the exclamation mark in the commit message', async () => { | ||
| expect( | ||
| getNextReleaseType( | ||
| await parseCommits([ | ||
| mockCommit({ | ||
| subject: 'fix: adds "!" as supported character', | ||
| }), | ||
| ]), | ||
| ), | ||
| ).toBe('patch') | ||
| expect( | ||
| getNextReleaseType( | ||
| await parseCommits([ | ||
| mockCommit({ | ||
| subject: 'feat: adds "!" as supported character', | ||
| }), | ||
| ]), | ||
| ), | ||
| ).toBe('minor') | ||
| expect( | ||
| getNextReleaseType( | ||
| await parseCommits([ | ||
| mockCommit({ | ||
| subject: 'docs: adds "!" as supported character', | ||
| }), | ||
| ]), | ||
| ), | ||
| ).toBe(null) | ||
| }) |
+0
-9
@@ -14,10 +14,1 @@ import { invariant } from 'outvariant' | ||
| } | ||
| export async function demandNpmToken(): Promise<void> { | ||
| const { NODE_AUTH_TOKEN, NPM_AUTH_TOKEN } = process.env | ||
| invariant( | ||
| NODE_AUTH_TOKEN || NPM_AUTH_TOKEN, | ||
| 'Failed to publish the package: neither "NODE_AUTH_TOKEN" nor "NPM_AUTH_TOKEN" environment variables were provided.', | ||
| ) | ||
| } |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
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.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
6104
0.36%25
-7.41%17
-5.56%219187
-0.01%Updated
Updated
Updated
Updated