mirror of
https://github.com/softprops/action-gh-release.git
synced 2025-11-04 13:49:25 +00:00
Compare commits
19 Commits
v2.3.3
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d3324bd4e | ||
|
|
0adea5aa98 | ||
|
|
aa05f9d779 | ||
|
|
bbaccb3a0c | ||
|
|
50fda3f773 | ||
|
|
5434409c2b | ||
|
|
6da8fa9354 | ||
|
|
f38efdea4c | ||
|
|
cec1a1113b | ||
|
|
aec2ec56f9 | ||
|
|
4db716b167 | ||
|
|
14820f2cee | ||
|
|
62c96d0c4e | ||
|
|
7dc9b8ac0f | ||
|
|
0f0e0b98e9 | ||
|
|
97d42c1b50 | ||
|
|
19cd0bcd2b | ||
|
|
5d1b0b1164 | ||
|
|
f6021cf9a4 |
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||||
|
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
|
||||||
with:
|
with:
|
||||||
node-version-file: ".tool-versions"
|
node-version-file: ".tool-versions"
|
||||||
cache: "npm"
|
cache: "npm"
|
||||||
|
|||||||
29
CHANGELOG.md
29
CHANGELOG.md
@@ -1,3 +1,32 @@
|
|||||||
|
## 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
|
||||||
|
|
||||||
|
## What's Changed
|
||||||
|
|
||||||
|
### Exciting New Features 🎉
|
||||||
|
|
||||||
|
* feat(action): respect working_directory for files globs by @stephenway in https://github.com/softprops/action-gh-release/pull/667
|
||||||
|
|
||||||
|
## 2.3.4
|
||||||
|
|
||||||
|
## What's Changed
|
||||||
|
|
||||||
|
### Bug fixes 🐛
|
||||||
|
|
||||||
|
* fix(action): handle 422 already_exists race condition by @stephenway in https://github.com/softprops/action-gh-release/pull/665
|
||||||
|
|
||||||
|
### Other Changes 🔄
|
||||||
|
|
||||||
|
- dependency updates
|
||||||
|
|
||||||
## 2.3.3
|
## 2.3.3
|
||||||
|
|
||||||
## What's Changed
|
## What's Changed
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
import { asset, findTagFromReleases, mimeOrDefault, Release, Releaser } from '../src/github';
|
import {
|
||||||
|
asset,
|
||||||
|
findTagFromReleases,
|
||||||
|
mimeOrDefault,
|
||||||
|
release,
|
||||||
|
Release,
|
||||||
|
Releaser,
|
||||||
|
} from '../src/github';
|
||||||
|
|
||||||
import { assert, describe, it } from 'vitest';
|
import { assert, describe, it } from 'vitest';
|
||||||
|
|
||||||
@@ -227,4 +234,75 @@ describe('github', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('error handling', () => {
|
||||||
|
it('handles 422 already_exists error gracefully', async () => {
|
||||||
|
const mockReleaser: Releaser = {
|
||||||
|
getReleaseByTag: () => Promise.reject('Not implemented'),
|
||||||
|
createRelease: () =>
|
||||||
|
Promise.reject({
|
||||||
|
status: 422,
|
||||||
|
response: { data: { errors: [{ code: 'already_exists' }] } },
|
||||||
|
}),
|
||||||
|
updateRelease: () =>
|
||||||
|
Promise.resolve({
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
upload_url: 'test',
|
||||||
|
html_url: 'test',
|
||||||
|
tag_name: 'v1.0.0',
|
||||||
|
name: 'test',
|
||||||
|
body: 'test',
|
||||||
|
target_commitish: 'main',
|
||||||
|
draft: false,
|
||||||
|
prerelease: false,
|
||||||
|
assets: [],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
allReleases: async function* () {
|
||||||
|
yield {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
upload_url: 'test',
|
||||||
|
html_url: 'test',
|
||||||
|
tag_name: 'v1.0.0',
|
||||||
|
name: 'test',
|
||||||
|
body: 'test',
|
||||||
|
target_commitish: 'main',
|
||||||
|
draft: false,
|
||||||
|
prerelease: false,
|
||||||
|
assets: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
github_token: 'test-token',
|
||||||
|
github_ref: 'refs/tags/v1.0.0',
|
||||||
|
github_repository: 'owner/repo',
|
||||||
|
input_tag_name: undefined,
|
||||||
|
input_name: undefined,
|
||||||
|
input_body: undefined,
|
||||||
|
input_body_path: undefined,
|
||||||
|
input_files: [],
|
||||||
|
input_draft: undefined,
|
||||||
|
input_prerelease: undefined,
|
||||||
|
input_preserve_order: undefined,
|
||||||
|
input_overwrite_files: undefined,
|
||||||
|
input_fail_on_unmatched_files: false,
|
||||||
|
input_target_commitish: undefined,
|
||||||
|
input_discussion_category_name: undefined,
|
||||||
|
input_generate_release_notes: false,
|
||||||
|
input_append_body: false,
|
||||||
|
input_make_latest: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await release(config, mockReleaser, 1);
|
||||||
|
assert.ok(result);
|
||||||
|
assert.equal(result.id, 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,6 +39,18 @@ 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', () => {
|
||||||
@@ -110,6 +122,52 @@ 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', () => {
|
||||||
@@ -128,6 +186,7 @@ describe('util', () => {
|
|||||||
github_ref: '',
|
github_ref: '',
|
||||||
github_repository: '',
|
github_repository: '',
|
||||||
github_token: '',
|
github_token: '',
|
||||||
|
input_working_directory: undefined,
|
||||||
input_append_body: false,
|
input_append_body: false,
|
||||||
input_body: undefined,
|
input_body: undefined,
|
||||||
input_body_path: undefined,
|
input_body_path: undefined,
|
||||||
@@ -156,6 +215,7 @@ describe('util', () => {
|
|||||||
github_ref: '',
|
github_ref: '',
|
||||||
github_repository: '',
|
github_repository: '',
|
||||||
github_token: '',
|
github_token: '',
|
||||||
|
input_working_directory: undefined,
|
||||||
input_append_body: false,
|
input_append_body: false,
|
||||||
input_body: undefined,
|
input_body: undefined,
|
||||||
input_body_path: undefined,
|
input_body_path: undefined,
|
||||||
@@ -183,6 +243,7 @@ describe('util', () => {
|
|||||||
github_ref: '',
|
github_ref: '',
|
||||||
github_repository: '',
|
github_repository: '',
|
||||||
github_token: '',
|
github_token: '',
|
||||||
|
input_working_directory: undefined,
|
||||||
input_append_body: false,
|
input_append_body: false,
|
||||||
input_body: undefined,
|
input_body: undefined,
|
||||||
input_body_path: undefined,
|
input_body_path: undefined,
|
||||||
@@ -211,6 +272,7 @@ describe('util', () => {
|
|||||||
github_ref: '',
|
github_ref: '',
|
||||||
github_repository: '',
|
github_repository: '',
|
||||||
github_token: '',
|
github_token: '',
|
||||||
|
input_working_directory: undefined,
|
||||||
input_append_body: false,
|
input_append_body: false,
|
||||||
input_body: undefined,
|
input_body: undefined,
|
||||||
input_body_path: undefined,
|
input_body_path: undefined,
|
||||||
@@ -243,6 +305,7 @@ describe('util', () => {
|
|||||||
github_ref: '',
|
github_ref: '',
|
||||||
github_repository: '',
|
github_repository: '',
|
||||||
github_token: 'env-token',
|
github_token: 'env-token',
|
||||||
|
input_working_directory: undefined,
|
||||||
input_append_body: false,
|
input_append_body: false,
|
||||||
input_body: undefined,
|
input_body: undefined,
|
||||||
input_body_path: undefined,
|
input_body_path: undefined,
|
||||||
@@ -272,6 +335,7 @@ describe('util', () => {
|
|||||||
github_ref: '',
|
github_ref: '',
|
||||||
github_repository: '',
|
github_repository: '',
|
||||||
github_token: 'input-token',
|
github_token: 'input-token',
|
||||||
|
input_working_directory: undefined,
|
||||||
input_append_body: false,
|
input_append_body: false,
|
||||||
input_body: undefined,
|
input_body: undefined,
|
||||||
input_body_path: undefined,
|
input_body_path: undefined,
|
||||||
@@ -300,6 +364,7 @@ describe('util', () => {
|
|||||||
github_ref: '',
|
github_ref: '',
|
||||||
github_repository: '',
|
github_repository: '',
|
||||||
github_token: '',
|
github_token: '',
|
||||||
|
input_working_directory: undefined,
|
||||||
input_append_body: false,
|
input_append_body: false,
|
||||||
input_body: undefined,
|
input_body: undefined,
|
||||||
input_body_path: undefined,
|
input_body_path: undefined,
|
||||||
@@ -327,6 +392,7 @@ describe('util', () => {
|
|||||||
github_ref: '',
|
github_ref: '',
|
||||||
github_repository: '',
|
github_repository: '',
|
||||||
github_token: '',
|
github_token: '',
|
||||||
|
input_working_directory: undefined,
|
||||||
input_append_body: false,
|
input_append_body: false,
|
||||||
input_body: undefined,
|
input_body: undefined,
|
||||||
input_body_path: undefined,
|
input_body_path: undefined,
|
||||||
@@ -354,6 +420,7 @@ describe('util', () => {
|
|||||||
github_ref: '',
|
github_ref: '',
|
||||||
github_repository: '',
|
github_repository: '',
|
||||||
github_token: '',
|
github_token: '',
|
||||||
|
input_working_directory: undefined,
|
||||||
input_append_body: true,
|
input_append_body: true,
|
||||||
input_body: undefined,
|
input_body: undefined,
|
||||||
input_body_path: undefined,
|
input_body_path: undefined,
|
||||||
@@ -388,6 +455,10 @@ describe('util', () => {
|
|||||||
'tests/data/foo/bar.txt',
|
'tests/data/foo/bar.txt',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('resolves files relative to working_directory', async () => {
|
||||||
|
assert.deepStrictEqual(paths(['data/**/*'], 'tests'), ['tests/data/foo/bar.txt']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('unmatchedPatterns', () => {
|
describe('unmatchedPatterns', () => {
|
||||||
@@ -397,6 +468,12 @@ describe('util', () => {
|
|||||||
['tests/data/does/not/exist/*'],
|
['tests/data/does/not/exist/*'],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('resolves unmatched relative to working_directory', async () => {
|
||||||
|
assert.deepStrictEqual(unmatchedPatterns(['data/does/not/exist/*'], 'tests'), [
|
||||||
|
'data/does/not/exist/*',
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('replaceSpacesWithDots', () => {
|
describe('replaceSpacesWithDots', () => {
|
||||||
@@ -413,3 +490,36 @@ 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',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ inputs:
|
|||||||
files:
|
files:
|
||||||
description: "Newline-delimited list of path globs for asset files to upload"
|
description: "Newline-delimited list of path globs for asset files to upload"
|
||||||
required: false
|
required: false
|
||||||
|
working_directory:
|
||||||
|
description: "Base directory to resolve 'files' globs against (defaults to job working-directory)"
|
||||||
|
required: false
|
||||||
overwrite_files:
|
overwrite_files:
|
||||||
description: "Overwrite existing files with the same name. Defaults to true"
|
description: "Overwrite existing files with the same name. Defaults to true"
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
2
dist/index.js
vendored
2
dist/index.js
vendored
File diff suppressed because one or more lines are too long
1175
package-lock.json
generated
1175
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "action-gh-release",
|
"name": "action-gh-release",
|
||||||
"version": "2.3.3",
|
"version": "2.4.1",
|
||||||
"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",
|
||||||
@@ -24,21 +24,21 @@
|
|||||||
"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.1",
|
"@octokit/plugin-retry": "^8.0.3",
|
||||||
"@octokit/plugin-throttling": "^11.0.1",
|
"@octokit/plugin-throttling": "^11.0.3",
|
||||||
"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.11",
|
"@types/node": "^20.19.24",
|
||||||
"@vercel/ncc": "^0.38.3",
|
"@vercel/ncc": "^0.38.4",
|
||||||
"@vitest/coverage-v8": "^3.2.4",
|
"@vitest/coverage-v8": "^4.0.6",
|
||||||
"prettier": "3.6.2",
|
"prettier": "3.6.2",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.9.2",
|
"typescript": "^5.9.3",
|
||||||
"typescript-formatter": "^7.2.2",
|
"typescript-formatter": "^7.2.2",
|
||||||
"vitest": "^3.1.4"
|
"vitest": "^4.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -388,9 +388,19 @@ async function createRelease(
|
|||||||
throw error;
|
throw error;
|
||||||
|
|
||||||
case 422:
|
case 422:
|
||||||
|
// Check if this is a race condition with "already_exists" error
|
||||||
|
const errorData = error.response?.data;
|
||||||
|
if (errorData?.errors?.[0]?.code === 'already_exists') {
|
||||||
|
console.log(
|
||||||
|
'⚠️ Release already exists (race condition detected), retrying to find and update existing release...',
|
||||||
|
);
|
||||||
|
// Don't throw - allow retry to find existing release
|
||||||
|
} else {
|
||||||
console.log('Skip retry - validation failed');
|
console.log('Skip retry - validation failed');
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`retrying... (${maxRetries - 1} retries remaining)`);
|
console.log(`retrying... (${maxRetries - 1} retries remaining)`);
|
||||||
return release(config, releaser, maxRetries - 1);
|
return release(config, releaser, maxRetries - 1);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ async function run() {
|
|||||||
throw new Error(`⚠️ GitHub Releases requires a tag`);
|
throw new Error(`⚠️ GitHub Releases requires a tag`);
|
||||||
}
|
}
|
||||||
if (config.input_files) {
|
if (config.input_files) {
|
||||||
const patterns = unmatchedPatterns(config.input_files);
|
const patterns = unmatchedPatterns(config.input_files, config.input_working_directory);
|
||||||
patterns.forEach((pattern) => {
|
patterns.forEach((pattern) => {
|
||||||
if (config.input_fail_on_unmatched_files) {
|
if (config.input_fail_on_unmatched_files) {
|
||||||
throw new Error(`⚠️ Pattern '${pattern}' does not match any files.`);
|
throw new Error(`⚠️ Pattern '${pattern}' does not match any files.`);
|
||||||
@@ -50,7 +50,7 @@ async function run() {
|
|||||||
//);
|
//);
|
||||||
const rel = await release(config, new GitHubReleaser(gh));
|
const rel = await release(config, new GitHubReleaser(gh));
|
||||||
if (config.input_files && config.input_files.length > 0) {
|
if (config.input_files && config.input_files.length > 0) {
|
||||||
const files = paths(config.input_files);
|
const files = paths(config.input_files, config.input_working_directory);
|
||||||
if (files.length == 0) {
|
if (files.length == 0) {
|
||||||
if (config.input_fail_on_unmatched_files) {
|
if (config.input_fail_on_unmatched_files) {
|
||||||
throw new Error(`⚠️ ${config.input_files} does not include a valid file.`);
|
throw new Error(`⚠️ ${config.input_files} does not include a valid file.`);
|
||||||
|
|||||||
84
src/util.ts
84
src/util.ts
@@ -1,5 +1,6 @@
|
|||||||
import * as glob from 'glob';
|
import * as glob from 'glob';
|
||||||
import { statSync, readFileSync } from 'fs';
|
import { statSync, readFileSync } from 'fs';
|
||||||
|
import * as pathLib from 'path';
|
||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
github_token: string;
|
github_token: string;
|
||||||
@@ -12,6 +13,7 @@ export interface Config {
|
|||||||
input_body?: string;
|
input_body?: string;
|
||||||
input_body_path?: string;
|
input_body_path?: string;
|
||||||
input_files?: string[];
|
input_files?: string[];
|
||||||
|
input_working_directory?: string;
|
||||||
input_overwrite_files?: boolean;
|
input_overwrite_files?: boolean;
|
||||||
input_draft?: boolean;
|
input_draft?: boolean;
|
||||||
input_preserve_order?: boolean;
|
input_preserve_order?: boolean;
|
||||||
@@ -33,23 +35,53 @@ export const uploadUrl = (url: string): string => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const releaseBody = (config: Config): string | undefined => {
|
export const releaseBody = (config: Config): string | undefined => {
|
||||||
return (
|
if (config.input_body_path) {
|
||||||
(config.input_body_path && readFileSync(config.input_body_path).toString('utf8')) ||
|
try {
|
||||||
config.input_body
|
const contents = readFileSync(config.input_body_path, 'utf8');
|
||||||
|
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.split(/\r?\n/).reduce<string[]>(
|
return files
|
||||||
(acc, line) =>
|
.split(/\r?\n/)
|
||||||
acc
|
.flatMap((line) => smartSplit(line))
|
||||||
.concat(line.split(','))
|
.filter((pat) => pat.trim() !== '');
|
||||||
.filter((pat) => pat)
|
|
||||||
.map((pat) => pat.trim()),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const parseConfig = (env: Env): Config => {
|
export const parseConfig = (env: Env): Config => {
|
||||||
@@ -62,6 +94,7 @@ export const parseConfig = (env: Env): Config => {
|
|||||||
input_body: env.INPUT_BODY,
|
input_body: env.INPUT_BODY,
|
||||||
input_body_path: env.INPUT_BODY_PATH,
|
input_body_path: env.INPUT_BODY_PATH,
|
||||||
input_files: parseInputFiles(env.INPUT_FILES || ''),
|
input_files: parseInputFiles(env.INPUT_FILES || ''),
|
||||||
|
input_working_directory: env.INPUT_WORKING_DIRECTORY || undefined,
|
||||||
input_overwrite_files: env.INPUT_OVERWRITE_FILES
|
input_overwrite_files: env.INPUT_OVERWRITE_FILES
|
||||||
? env.INPUT_OVERWRITE_FILES == 'true'
|
? env.INPUT_OVERWRITE_FILES == 'true'
|
||||||
: undefined,
|
: undefined,
|
||||||
@@ -84,17 +117,34 @@ const parseMakeLatest = (value: string | undefined): 'true' | 'false' | 'legacy'
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const paths = (patterns: string[]): string[] => {
|
export const paths = (patterns: string[], cwd?: string): string[] => {
|
||||||
return patterns.reduce((acc: string[], pattern: string): string[] => {
|
return patterns.reduce((acc: string[], pattern: string): string[] => {
|
||||||
return acc.concat(glob.sync(pattern).filter((path) => statSync(path).isFile()));
|
const matches = glob.sync(pattern, { cwd, dot: true, absolute: false });
|
||||||
|
const resolved = matches
|
||||||
|
.map((p) => (cwd ? pathLib.join(cwd, p) : p))
|
||||||
|
.filter((p) => {
|
||||||
|
try {
|
||||||
|
return statSync(p).isFile();
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return acc.concat(resolved);
|
||||||
}, []);
|
}, []);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const unmatchedPatterns = (patterns: string[]): string[] => {
|
export const unmatchedPatterns = (patterns: string[], cwd?: string): string[] => {
|
||||||
return patterns.reduce((acc: string[], pattern: string): string[] => {
|
return patterns.reduce((acc: string[], pattern: string): string[] => {
|
||||||
return acc.concat(
|
const matches = glob.sync(pattern, { cwd, dot: true, absolute: false });
|
||||||
glob.sync(pattern).filter((path) => statSync(path).isFile()).length == 0 ? [pattern] : [],
|
const files = matches.filter((p) => {
|
||||||
);
|
try {
|
||||||
|
const full = cwd ? pathLib.join(cwd, p) : p;
|
||||||
|
return statSync(full).isFile();
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return acc.concat(files.length == 0 ? [pattern] : []);
|
||||||
}, []);
|
}, []);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user