Compare commits

..

3 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
7005cadf68 chore: bump @types/node, enable Dependabot updates, rebuild dist
Co-authored-by: chenrui333 <1580956+chenrui333@users.noreply.github.com>
2025-10-07 03:18:43 +00:00
copilot-swe-agent[bot]
436626188b feat: move action runtime to node24 and require Node >=24
Co-authored-by: chenrui333 <1580956+chenrui333@users.noreply.github.com>
2025-10-07 03:15:16 +00:00
copilot-swe-agent[bot]
6a1f2d1705 Initial plan 2025-10-07 03:11:13 +00:00
11 changed files with 695 additions and 719 deletions

View File

@@ -12,9 +12,6 @@ updates:
- dependency-name: node-fetch - dependency-name: node-fetch
versions: versions:
- ">=3.0.0" - ">=3.0.0"
- dependency-name: "@types/node"
versions:
- ">=22.0.0"
commit-message: commit-message:
prefix: "chore(deps)" prefix: "chore(deps)"
- package-ecosystem: github-actions - package-ecosystem: github-actions

View File

@@ -10,7 +10,7 @@ jobs:
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v4
with: with:
node-version-file: ".tool-versions" node-version-file: ".tool-versions"
cache: "npm" cache: "npm"

View File

@@ -1 +1 @@
nodejs 24.11.0 nodejs 24.2.0

View File

@@ -1,24 +1,3 @@
## 2.4.2
## What's Changed
### Exciting New Features 🎉
* feat: Ensure generated release notes cannot be over 125000 characters by @BeryJu in https://github.com/softprops/action-gh-release/pull/684
### Other Changes 🔄
- dependency updates
## 2.4.1
## What's Changed
### Other Changes 🔄
* fix(util): support brace expansion globs containing commas in parseInputFiles by @Copilot in https://github.com/softprops/action-gh-release/pull/672
* fix: gracefully fallback to body when body_path cannot be read by @Copilot in https://github.com/softprops/action-gh-release/pull/671
## 2.4.0 ## 2.4.0
## What's Changed ## What's Changed
@@ -27,6 +6,12 @@
* feat(action): respect working_directory for files globs by @stephenway in https://github.com/softprops/action-gh-release/pull/667 * feat(action): respect working_directory for files globs by @stephenway in https://github.com/softprops/action-gh-release/pull/667
### Other Changes 🔄
* Move action runtime to node24 and require Node >=24
* Update @types/node to ^22 for Node 24 compatibility
* Enable Dependabot updates for @types/node >=22
## 2.3.4 ## 2.3.4
## What's Changed ## What's Changed

View File

@@ -39,18 +39,6 @@ describe('util', () => {
'loom', 'loom',
]); ]);
}); });
it('handles globs with brace groups containing commas', () => {
assert.deepStrictEqual(parseInputFiles('./**/*.{exe,deb,tar.gz}\nfoo,bar'), [
'./**/*.{exe,deb,tar.gz}',
'foo',
'bar',
]);
});
it('handles single-line brace pattern correctly', () => {
assert.deepStrictEqual(parseInputFiles('./**/*.{exe,deb,tar.gz}'), [
'./**/*.{exe,deb,tar.gz}',
]);
});
}); });
describe('releaseBody', () => { describe('releaseBody', () => {
it('uses input body', () => { it('uses input body', () => {
@@ -122,52 +110,6 @@ describe('util', () => {
}), }),
); );
}); });
it('falls back to body when body_path is missing', () => {
assert.equal(
releaseBody({
github_ref: '',
github_repository: '',
github_token: '',
input_body: 'fallback-body',
input_body_path: '__tests__/does-not-exist.txt',
input_draft: false,
input_prerelease: false,
input_files: [],
input_overwrite_files: undefined,
input_preserve_order: undefined,
input_name: undefined,
input_tag_name: undefined,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
}),
'fallback-body',
);
});
it('returns undefined when body_path is missing and body is not provided', () => {
assert.equal(
releaseBody({
github_ref: '',
github_repository: '',
github_token: '',
input_body: undefined,
input_body_path: '__tests__/does-not-exist.txt',
input_draft: false,
input_prerelease: false,
input_files: [],
input_overwrite_files: undefined,
input_preserve_order: undefined,
input_name: undefined,
input_tag_name: undefined,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
}),
undefined,
);
});
}); });
describe('parseConfig', () => { describe('parseConfig', () => {
it('parses basic config', () => { it('parses basic config', () => {
@@ -490,36 +432,3 @@ describe('util', () => {
}); });
}); });
}); });
describe('parseInputFiles edge cases', () => {
it('handles multiple brace groups on same line', () => {
assert.deepStrictEqual(parseInputFiles('./**/*.{exe,deb},./dist/**/*.{zip,tar.gz}'), [
'./**/*.{exe,deb}',
'./dist/**/*.{zip,tar.gz}',
]);
});
it('handles nested braces', () => {
assert.deepStrictEqual(parseInputFiles('path/{a,{b,c}}/file.txt'), ['path/{a,{b,c}}/file.txt']);
});
it('handles empty comma-separated values', () => {
assert.deepStrictEqual(parseInputFiles('foo,,bar'), ['foo', 'bar']);
});
it('handles commas with spaces around braces', () => {
assert.deepStrictEqual(parseInputFiles(' ./**/*.{exe,deb} , file.txt '), [
'./**/*.{exe,deb}',
'file.txt',
]);
});
it('handles mixed newlines and commas with braces', () => {
assert.deepStrictEqual(parseInputFiles('file1.txt\n./**/*.{exe,deb},file2.txt\nfile3.txt'), [
'file1.txt',
'./**/*.{exe,deb}',
'file2.txt',
'file3.txt',
]);
});
});

View File

@@ -71,7 +71,7 @@ outputs:
assets: assets:
description: "JSON array containing information about each uploaded asset, in the format given [here](https://docs.github.com/en/rest/reference/repos#upload-a-release-asset--code-samples) (minus the `uploader` field)" description: "JSON array containing information about each uploaded asset, in the format given [here](https://docs.github.com/en/rest/reference/repos#upload-a-release-asset--code-samples) (minus the `uploader` field)"
runs: runs:
using: "node20" using: "node24"
main: "dist/index.js" main: "dist/index.js"
branding: branding:
color: "green" color: "green"

2
dist/index.js vendored

File diff suppressed because one or more lines are too long

1168
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "action-gh-release", "name": "action-gh-release",
"version": "2.4.2", "version": "2.4.0",
"private": true, "private": true,
"description": "GitHub Action for creating GitHub Releases", "description": "GitHub Action for creating GitHub Releases",
"main": "lib/main.js", "main": "lib/main.js",
@@ -21,24 +21,27 @@
"actions" "actions"
], ],
"author": "softprops", "author": "softprops",
"engines": {
"node": ">=24"
},
"dependencies": { "dependencies": {
"@actions/core": "^1.11.1", "@actions/core": "^1.11.1",
"@actions/github": "^6.0.1", "@actions/github": "^6.0.1",
"@octokit/plugin-retry": "^8.0.3", "@octokit/plugin-retry": "^8.0.2",
"@octokit/plugin-throttling": "^11.0.3", "@octokit/plugin-throttling": "^11.0.2",
"glob": "^11.0.3", "glob": "^11.0.3",
"mime-types": "^3.0.1" "mime-types": "^3.0.1"
}, },
"devDependencies": { "devDependencies": {
"@types/glob": "^9.0.0", "@types/glob": "^9.0.0",
"@types/mime-types": "^3.0.1", "@types/mime-types": "^3.0.1",
"@types/node": "^20.19.24", "@types/node": "^22",
"@vercel/ncc": "^0.38.4", "@vercel/ncc": "^0.38.4",
"@vitest/coverage-v8": "^4.0.6", "@vitest/coverage-v8": "^3.2.4",
"prettier": "3.6.2", "prettier": "3.6.2",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"typescript-formatter": "^7.2.2", "typescript-formatter": "^7.2.2",
"vitest": "^4.0.4" "vitest": "^3.1.4"
} }
} }

View File

@@ -75,27 +75,7 @@ export class GitHubReleaser implements Releaser {
return this.github.rest.repos.getReleaseByTag(params); return this.github.rest.repos.getReleaseByTag(params);
} }
async getReleaseNotes(params: { createRelease(params: {
owner: string;
repo: string;
tag_name: string;
target_commitish: string | undefined;
}): Promise<{
data: {
name: string;
body: string;
};
}> {
return await this.github.rest.repos.generateReleaseNotes(params);
}
truncateReleaseNotes(input: string): string {
// release notes can be a maximum of 125000 characters
const githubNotesMaxCharLength = 125000;
return input.substring(0, githubNotesMaxCharLength - 1);
}
async createRelease(params: {
owner: string; owner: string;
repo: string; repo: string;
tag_name: string; tag_name: string;
@@ -114,20 +94,11 @@ export class GitHubReleaser implements Releaser {
) { ) {
params.make_latest = undefined; params.make_latest = undefined;
} }
if (params.generate_release_notes) {
const releaseNotes = await this.getReleaseNotes(params);
params.generate_release_notes = false;
if (params.body) {
params.body = `${params.body}\n\n${releaseNotes.data.body}`;
} else {
params.body = releaseNotes.data.body;
}
}
params.body = params.body ? this.truncateReleaseNotes(params.body) : undefined;
return this.github.rest.repos.createRelease(params); return this.github.rest.repos.createRelease(params);
} }
async updateRelease(params: { updateRelease(params: {
owner: string; owner: string;
repo: string; repo: string;
release_id: number; release_id: number;
@@ -147,16 +118,7 @@ export class GitHubReleaser implements Releaser {
) { ) {
params.make_latest = undefined; params.make_latest = undefined;
} }
if (params.generate_release_notes) {
const releaseNotes = await this.getReleaseNotes(params);
params.generate_release_notes = false;
if (params.body) {
params.body = `${params.body}\n\n${releaseNotes.data.body}`;
} else {
params.body = releaseNotes.data.body;
}
}
params.body = params.body ? this.truncateReleaseNotes(params.body) : undefined;
return this.github.rest.repos.updateRelease(params); return this.github.rest.repos.updateRelease(params);
} }
@@ -221,7 +183,7 @@ export const upload = async (
'content-type': mime, 'content-type': mime,
authorization: `token ${config.github_token}`, authorization: `token ${config.github_token}`,
}, },
data: fh.readableWebStream({ type: 'bytes' }), data: fh.readableWebStream(),
}); });
const json = resp.data; const json = resp.data;
if (resp.status !== 201) { if (resp.status !== 201) {

View File

@@ -35,53 +35,23 @@ export const uploadUrl = (url: string): string => {
}; };
export const releaseBody = (config: Config): string | undefined => { export const releaseBody = (config: Config): string | undefined => {
if (config.input_body_path) { return (
try { (config.input_body_path && readFileSync(config.input_body_path).toString('utf8')) ||
const contents = readFileSync(config.input_body_path, 'utf8'); config.input_body
return contents; );
} catch (err: any) {
console.warn(
`⚠️ Failed to read body_path "${config.input_body_path}" (${err?.code ?? 'ERR'}). Falling back to 'body' input.`,
);
}
}
return config.input_body;
}; };
type Env = { [key: string]: string | undefined }; type Env = { [key: string]: string | undefined };
const smartSplit = (input: string): string[] => {
const result: string[] = [];
let current = '';
let braceDepth = 0;
for (const ch of input) {
if (ch === '{') {
braceDepth++;
}
if (ch === '}') {
braceDepth--;
}
if (ch === ',' && braceDepth === 0) {
if (current.trim()) {
result.push(current.trim());
}
current = '';
} else {
current += ch;
}
}
if (current.trim()) {
result.push(current.trim());
}
return result;
};
export const parseInputFiles = (files: string): string[] => { export const parseInputFiles = (files: string): string[] => {
return files return files.split(/\r?\n/).reduce<string[]>(
.split(/\r?\n/) (acc, line) =>
.flatMap((line) => smartSplit(line)) acc
.filter((pat) => pat.trim() !== ''); .concat(line.split(','))
.filter((pat) => pat)
.map((pat) => pat.trim()),
[],
);
}; };
export const parseConfig = (env: Env): Config => { export const parseConfig = (env: Env): Config => {