mirror of
https://github.com/softprops/action-gh-release.git
synced 2026-07-25 03:07:57 +08:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5018f9ec04 | |||
| c1e290273a | |||
| 1c214ab373 | |||
| d100e75a45 | |||
| 81bcd9dd59 | |||
| b05950fac2 |
+6
-3
@@ -38,8 +38,8 @@ claim a reproducible release-creation fix.
|
|||||||
|
|
||||||
`3.0.0` is a major release that moves the action runtime from Node 20 to Node 24.
|
`3.0.0` is a major release that moves the action runtime from Node 20 to Node 24.
|
||||||
Use `v3` on GitHub-hosted runners and self-hosted fleets that already support the
|
Use `v3` on GitHub-hosted runners and self-hosted fleets that already support the
|
||||||
Node 24 Actions runtime. If you still need the last Node 20-compatible line, stay on
|
Node 24 Actions runtime. `v2.6.2` was the final Node 20-compatible release and is
|
||||||
`v2.6.2`.
|
no longer maintained or supported.
|
||||||
|
|
||||||
## What's Changed
|
## What's Changed
|
||||||
|
|
||||||
@@ -47,10 +47,13 @@ Node 24 Actions runtime. If you still need the last Node 20-compatible line, sta
|
|||||||
|
|
||||||
* Move the action runtime and bundle target to Node 24
|
* Move the action runtime and bundle target to Node 24
|
||||||
* Update `@types/node` to the Node 24 line and allow future Dependabot updates
|
* Update `@types/node` to the Node 24 line and allow future Dependabot updates
|
||||||
* Keep the floating major tag on `v3`; `v2` remains pinned to the latest `2.x` release
|
* Keep the floating major tag on `v3`; freeze `v2` at the final `v2.6.2` release
|
||||||
|
|
||||||
## 2.6.2
|
## 2.6.2
|
||||||
|
|
||||||
|
`2.6.2` is the final `v2` release and is no longer maintained or supported. Upgrade
|
||||||
|
to `v3` for the supported Node 24 runtime and current fixes.
|
||||||
|
|
||||||
## What's Changed
|
## What's Changed
|
||||||
|
|
||||||
### Other Changes 🔄
|
### Other Changes 🔄
|
||||||
|
|||||||
@@ -39,8 +39,11 @@ Typically usage of this action involves adding a step to a build that
|
|||||||
is gated pushes to git tags. You may find `step.if` field helpful in accomplishing this
|
is gated pushes to git tags. You may find `step.if` field helpful in accomplishing this
|
||||||
as it maximizes the reuse value of your workflow for non-tag pushes.
|
as it maximizes the reuse value of your workflow for non-tag pushes.
|
||||||
|
|
||||||
`v3` requires a GitHub Actions runtime that supports Node 24. If you still need the
|
`v2.6.2` is the final `v2` release and is no longer maintained or supported. It
|
||||||
last Node 20-compatible line, stay on `v2.6.2`.
|
uses the [Node 20 runtime deprecated by GitHub Actions](https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/).
|
||||||
|
Upgrade to `v3`, which runs on Node 24. If a problem still reproduces on the
|
||||||
|
latest `v3`, open a new issue with the exact action ref, workflow run URL,
|
||||||
|
runner, and relevant logs.
|
||||||
|
|
||||||
Below is a simple example of `step.if` tag gating
|
Below is a simple example of `step.if` tag gating
|
||||||
|
|
||||||
|
|||||||
+23
-10
@@ -23,21 +23,34 @@ Use this checklist when cutting a new `action-gh-release` release.
|
|||||||
- `npm run build`
|
- `npm run build`
|
||||||
- `npm test`
|
- `npm test`
|
||||||
5. Commit the release prep.
|
5. Commit the release prep.
|
||||||
- Use a plain release commit message like `release 3.0.0`.
|
- Use `git commit -s` so the release commit carries a DCO sign-off.
|
||||||
6. Create the annotated tag for the release commit.
|
- Use a plain release commit message like `release X.Y.Z`.
|
||||||
- Example: `git tag -a v3.0.0 -m "v3.0.0"`
|
6. Push a release branch and open a pull request against `master`.
|
||||||
7. Push the commit and tag.
|
- Wait for required checks and reviews.
|
||||||
- Example: `git push origin master && git push origin v3.0.0`
|
- Do not bypass branch protection or tag an unmerged release branch.
|
||||||
8. Move the floating major tag to the new release tag.
|
7. After merge, fetch `origin/master` and resolve the exact merged release commit.
|
||||||
|
- Confirm that commit contains the expected package version and top changelog entry.
|
||||||
|
- When the PR is squash-merged, do not assume the release branch commit is the release commit.
|
||||||
|
8. Create and push the full annotated version tag on the merged release commit.
|
||||||
|
- Example: `git tag -a vX.Y.Z -m "vX.Y.Z" <release-commit>`
|
||||||
|
- Push only the full version tag first, then wait for its tag-triggered CI to pass.
|
||||||
|
9. Move the floating major tag to the same merged release commit.
|
||||||
- For the current major line, run `npm run updatetag` to move `v3`.
|
- For the current major line, run `npm run updatetag` to move `v3`.
|
||||||
- Keep `v2` pinned to the latest `2.x` release for consumers that still need the Node 20 runtime.
|
- Do not move `v2`; it is frozen at the final, unsupported `v2.6.2` release.
|
||||||
- Verify the floating tag points at the same commit as the new full tag.
|
- Verify `v3` and the full version tag are annotated and peel to the same commit.
|
||||||
9. Create the GitHub release from the new tag.
|
- Verify `v2` did not move, then wait for the separate `v3` tag-triggered CI run to pass.
|
||||||
|
10. Create the GitHub release from the full version tag.
|
||||||
- Prefer the release body from [CHANGELOG.md](CHANGELOG.md), then let GitHub append generated notes only if they add value.
|
- Prefer the release body from [CHANGELOG.md](CHANGELOG.md), then let GitHub append generated notes only if they add value.
|
||||||
- Verify the release shows the expected tag, title, notes, and attached artifacts.
|
- Verify the release shows the expected tag, title, notes, draft/prerelease state, and attached artifacts.
|
||||||
|
11. Run post-release consumer verification in `ruitest2/action-gh-release-test`.
|
||||||
|
- Run the generic smoke against both the full version tag and `v3`.
|
||||||
|
- Run regression workflows relevant to the fixes in the release.
|
||||||
|
- Confirm that every disposable release, tag, discussion, container, volume, and temporary credential was cleaned up.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- Behavior changes should already have matching updates in [README.md](README.md), [action.yml](action.yml), tests, and `dist/index.js` before release prep begins.
|
- Behavior changes should already have matching updates in [README.md](README.md), [action.yml](action.yml), tests, and `dist/index.js` before release prep begins.
|
||||||
- Docs-only releases still need an intentional changelog entry and version bump decision.
|
- Docs-only releases still need an intentional changelog entry and version bump decision.
|
||||||
- If a release is mainly bug fixes, keep the title and summary patch-oriented; do not bury the actual fixes under dependency noise.
|
- If a release is mainly bug fixes, keep the title and summary patch-oriented; do not bury the actual fixes under dependency noise.
|
||||||
|
- Do not move the floating major tag or publish the GitHub release until the full
|
||||||
|
version tag's CI passes.
|
||||||
|
|||||||
+516
-36
@@ -149,6 +149,55 @@ describe('github', () => {
|
|||||||
expect(pageAfterMatch).not.toHaveBeenCalled();
|
expect(pageAfterMatch).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['a top-level 404', { status: 404 }],
|
||||||
|
['a nested response 404', { response: { status: 404 } }],
|
||||||
|
[
|
||||||
|
'an invalid top-level status with a nested response 404',
|
||||||
|
{ status: '500', response: { status: 404 } },
|
||||||
|
],
|
||||||
|
])('uses release-list fallback for %s', async (_name, lookupError) => {
|
||||||
|
const draftRelease = { ...mockRelease, draft: true };
|
||||||
|
const allReleases = vi.fn(async function* () {
|
||||||
|
yield { data: [draftRelease] };
|
||||||
|
});
|
||||||
|
const releaser = {
|
||||||
|
...mockReleaser,
|
||||||
|
getReleaseByTag: vi.fn().mockRejectedValue(lookupError),
|
||||||
|
allReleases,
|
||||||
|
};
|
||||||
|
|
||||||
|
await expect(findTagFromReleases(releaser, owner, repo, draftRelease.tag_name)).resolves.toBe(
|
||||||
|
draftRelease,
|
||||||
|
);
|
||||||
|
expect(allReleases).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['a nested response 500', { response: { status: 500 } }],
|
||||||
|
['a string top-level status', { status: '404' }],
|
||||||
|
['a string nested response status', { response: { status: '404' } }],
|
||||||
|
['a status-less object', { reason: 'lookup failed' }],
|
||||||
|
[
|
||||||
|
'a numeric top-level 500 with a nested response 404',
|
||||||
|
{ status: 500, response: { status: 404 } },
|
||||||
|
],
|
||||||
|
])('does not use release-list fallback for %s', async (_name, lookupError) => {
|
||||||
|
const allReleases = vi.fn(async function* () {
|
||||||
|
yield { data: [mockRelease] };
|
||||||
|
});
|
||||||
|
const releaser = {
|
||||||
|
...mockReleaser,
|
||||||
|
getReleaseByTag: vi.fn().mockRejectedValue(lookupError),
|
||||||
|
allReleases,
|
||||||
|
};
|
||||||
|
|
||||||
|
await expect(findTagFromReleases(releaser, owner, repo, mockRelease.tag_name)).rejects.toBe(
|
||||||
|
lookupError,
|
||||||
|
);
|
||||||
|
expect(allReleases).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('does not exhaust pagination while checking a brand-new tag', async () => {
|
it('does not exhaust pagination while checking a brand-new tag', async () => {
|
||||||
let pagesRead = 0;
|
let pagesRead = 0;
|
||||||
const releaser = {
|
const releaser = {
|
||||||
@@ -346,11 +395,20 @@ describe('github', () => {
|
|||||||
expect(request).not.toHaveBeenCalled();
|
expect(request).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to release-scoped asset deletion after a standard 404', async () => {
|
it.each([
|
||||||
const deleteReleaseAsset = vi.fn().mockRejectedValue({
|
{
|
||||||
status: 404,
|
name: 'top-level status',
|
||||||
message: 'standard route not found',
|
deletionError: { status: 404, message: 'standard route not found' },
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
name: 'nested response status',
|
||||||
|
deletionError: {
|
||||||
|
response: { status: 404 },
|
||||||
|
message: 'standard route not found',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])('falls back to release-scoped asset deletion after a 404 with $name', async (testCase) => {
|
||||||
|
const deleteReleaseAsset = vi.fn().mockRejectedValue(testCase.deletionError);
|
||||||
const request = vi.fn().mockResolvedValue({ status: 204 });
|
const request = vi.fn().mockResolvedValue({ status: 204 });
|
||||||
const releaser = new GitHubReleaser({
|
const releaser = new GitHubReleaser({
|
||||||
rest: { repos: { deleteReleaseAsset } },
|
rest: { repos: { deleteReleaseAsset } },
|
||||||
@@ -376,6 +434,30 @@ describe('github', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['a string', 'not found'],
|
||||||
|
['null', null],
|
||||||
|
['an arbitrary object', { response: { data: 'not found' } }],
|
||||||
|
])('does not misclassify %s as a fallback 404', async (_name, deletionError) => {
|
||||||
|
const request = vi.fn();
|
||||||
|
const releaser = new GitHubReleaser({
|
||||||
|
rest: {
|
||||||
|
repos: { deleteReleaseAsset: vi.fn().mockRejectedValue(deletionError) },
|
||||||
|
},
|
||||||
|
request,
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
releaser.deleteReleaseAsset({
|
||||||
|
owner: 'owner',
|
||||||
|
repo: 'repo',
|
||||||
|
release_id: 42,
|
||||||
|
asset_id: 99,
|
||||||
|
}),
|
||||||
|
).rejects.toBe(deletionError);
|
||||||
|
expect(request).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('does not mask non-404 asset deletion failures', async () => {
|
it('does not mask non-404 asset deletion failures', async () => {
|
||||||
const deletionError = { status: 403, message: 'forbidden' };
|
const deletionError = { status: 403, message: 'forbidden' };
|
||||||
const request = vi.fn();
|
const request = vi.fn();
|
||||||
@@ -790,6 +872,83 @@ describe('github', () => {
|
|||||||
expect(finalizeReleaseSpy).toHaveBeenCalledTimes(1);
|
expect(finalizeReleaseSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(deleteReleaseSpy).not.toHaveBeenCalled();
|
expect(deleteReleaseSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('retries malformed tag-rule errors without deleting the draft', async () => {
|
||||||
|
const finalizeReleaseSpy = vi.fn().mockRejectedValue({
|
||||||
|
status: 422,
|
||||||
|
response: {
|
||||||
|
data: {
|
||||||
|
errors: [null, 'invalid', { field: 'pre_receive', message: 42 }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const deleteReleaseSpy = vi.fn();
|
||||||
|
const releaser = createReleaser({
|
||||||
|
finalizeRelease: finalizeReleaseSpy,
|
||||||
|
deleteRelease: deleteReleaseSpy,
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
finalizeRelease(
|
||||||
|
{
|
||||||
|
...config,
|
||||||
|
input_draft: false,
|
||||||
|
},
|
||||||
|
releaser,
|
||||||
|
draftRelease,
|
||||||
|
true,
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
).rejects.toThrow('Too many retries.');
|
||||||
|
|
||||||
|
expect(finalizeReleaseSpy).toHaveBeenCalledOnce();
|
||||||
|
expect(deleteReleaseSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('finds a valid tag-rule violation after malformed validation entries', async () => {
|
||||||
|
const finalizeReleaseSpy = vi.fn().mockRejectedValue({
|
||||||
|
response: {
|
||||||
|
status: 422,
|
||||||
|
data: {
|
||||||
|
errors: [
|
||||||
|
null,
|
||||||
|
'invalid',
|
||||||
|
{
|
||||||
|
field: 'pre_receive',
|
||||||
|
message: 'Cannot create ref due to creations being restricted.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const deleteReleaseSpy = vi.fn().mockResolvedValue(undefined);
|
||||||
|
const releaser = createReleaser({
|
||||||
|
finalizeRelease: finalizeReleaseSpy,
|
||||||
|
deleteRelease: deleteReleaseSpy,
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
finalizeRelease(
|
||||||
|
{
|
||||||
|
...config,
|
||||||
|
input_draft: false,
|
||||||
|
},
|
||||||
|
releaser,
|
||||||
|
draftRelease,
|
||||||
|
true,
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
).rejects.toThrow(
|
||||||
|
'Tag creation for v1.0.0 is blocked by repository rules. Deleted draft release 1 to avoid leaving an orphaned draft release.',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(finalizeReleaseSpy).toHaveBeenCalledOnce();
|
||||||
|
expect(deleteReleaseSpy).toHaveBeenCalledWith({
|
||||||
|
owner: 'owner',
|
||||||
|
repo: 'repo',
|
||||||
|
release_id: draftRelease.id,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('error handling', () => {
|
describe('error handling', () => {
|
||||||
@@ -897,6 +1056,52 @@ describe('github', () => {
|
|||||||
expect(createRelease).not.toHaveBeenCalled();
|
expect(createRelease).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{
|
||||||
|
name: 'a nested response 404 as repository access failure',
|
||||||
|
listingError: {
|
||||||
|
response: { status: 404 },
|
||||||
|
message: 'Not Found - list-releases',
|
||||||
|
},
|
||||||
|
expectAccessError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a nested response 500 as the original error',
|
||||||
|
listingError: {
|
||||||
|
response: { status: 500 },
|
||||||
|
message: 'Server Error - list-releases',
|
||||||
|
},
|
||||||
|
expectAccessError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a status-less object as the original error',
|
||||||
|
listingError: { reason: 'release listing failed' },
|
||||||
|
expectAccessError: false,
|
||||||
|
},
|
||||||
|
])('classifies a release-list fallback error with $name', async (testCase) => {
|
||||||
|
const createRelease = vi.fn();
|
||||||
|
const releaser = createReleaser({
|
||||||
|
getReleaseByTag: vi.fn().mockRejectedValue({ status: 404 }),
|
||||||
|
allReleases: async function* () {
|
||||||
|
throw testCase.listingError;
|
||||||
|
},
|
||||||
|
createRelease,
|
||||||
|
});
|
||||||
|
|
||||||
|
const thrown = await release(config, releaser, 1).catch((error) => error);
|
||||||
|
|
||||||
|
if (testCase.expectAccessError) {
|
||||||
|
expect(thrown).toMatchObject({
|
||||||
|
name: 'ReleaseAccessError',
|
||||||
|
status: 404,
|
||||||
|
cause: testCase.listingError,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
expect(thrown).toBe(testCase.listingError);
|
||||||
|
}
|
||||||
|
expect(createRelease).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('reports a useful create error without assuming response data exists', async () => {
|
it('reports a useful create error without assuming response data exists', async () => {
|
||||||
const releaseError = {
|
const releaseError = {
|
||||||
status: 403,
|
status: 403,
|
||||||
@@ -917,6 +1122,100 @@ describe('github', () => {
|
|||||||
expect(log).not.toHaveBeenCalledWith('undefined');
|
expect(log).not.toHaveBeenCalledWith('undefined');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{
|
||||||
|
name: 'a nested response 403',
|
||||||
|
releaseError: {
|
||||||
|
response: { status: 403 },
|
||||||
|
message: 'Resource not accessible by integration',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'a nested response 422 without already_exists',
|
||||||
|
releaseError: {
|
||||||
|
response: { status: 422, data: { errors: [{ code: 'invalid' }] } },
|
||||||
|
message: 'Validation Failed',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])('does not retry release creation for $name', async (testCase) => {
|
||||||
|
const createRelease = vi.fn().mockRejectedValue(testCase.releaseError);
|
||||||
|
const releaser = createReleaser({
|
||||||
|
getReleaseByTag: vi.fn().mockRejectedValue({ status: 404 }),
|
||||||
|
createRelease,
|
||||||
|
allReleases: async function* () {
|
||||||
|
yield { data: [] };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(release(config, releaser, 1)).rejects.toBe(testCase.releaseError);
|
||||||
|
expect(createRelease).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('classifies a nested response 404 from release creation without retrying', async () => {
|
||||||
|
const releaseError = {
|
||||||
|
response: { status: 404 },
|
||||||
|
message: 'Not Found - create-a-release',
|
||||||
|
};
|
||||||
|
const createRelease = vi.fn().mockRejectedValue(releaseError);
|
||||||
|
const releaser = createReleaser({
|
||||||
|
getReleaseByTag: vi.fn().mockRejectedValue({ status: 404 }),
|
||||||
|
createRelease,
|
||||||
|
allReleases: async function* () {
|
||||||
|
yield { data: [] };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const thrown = await release(config, releaser, 1).catch((error) => error);
|
||||||
|
|
||||||
|
expect(thrown).toMatchObject({
|
||||||
|
name: 'ReleaseCreationError',
|
||||||
|
status: 404,
|
||||||
|
cause: releaseError,
|
||||||
|
});
|
||||||
|
expect(thrown.message).toContain('GitHub returned 404 while creating the release');
|
||||||
|
expect(createRelease).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['non-object response data', { status: 422, response: { data: 'invalid' } }],
|
||||||
|
['missing validation errors', { status: 422, response: { data: {} } }],
|
||||||
|
['malformed validation errors', { status: 422, response: { data: { errors: [null] } } }],
|
||||||
|
])('does not retry a permanent 422 with %s', async (_name, releaseError) => {
|
||||||
|
const createRelease = vi.fn().mockRejectedValue(releaseError);
|
||||||
|
const releaser = createReleaser({
|
||||||
|
getReleaseByTag: vi.fn().mockRejectedValue({ status: 404 }),
|
||||||
|
createRelease,
|
||||||
|
allReleases: async function* () {
|
||||||
|
yield { data: [] };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(release(config, releaser, 1)).rejects.toBe(releaseError);
|
||||||
|
expect(createRelease).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['a string', 'transport failed'],
|
||||||
|
['null', null],
|
||||||
|
['an arbitrary object', { reason: 'transport failed' }],
|
||||||
|
['a nested response 500', { response: { status: 500 } }],
|
||||||
|
])(
|
||||||
|
'retries unclassified release creation failures without secondary TypeErrors when the error is %s',
|
||||||
|
async (_name, releaseError) => {
|
||||||
|
const createRelease = vi.fn().mockRejectedValue(releaseError);
|
||||||
|
const releaser = createReleaser({
|
||||||
|
getReleaseByTag: vi.fn().mockRejectedValue({ status: 404 }),
|
||||||
|
createRelease,
|
||||||
|
allReleases: async function* () {
|
||||||
|
yield { data: [] };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(release(config, releaser, 1)).rejects.toThrow('Too many retries.');
|
||||||
|
expect(createRelease).toHaveBeenCalledOnce();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
it('passes previous_tag_name through when creating a release with generated notes', async () => {
|
it('passes previous_tag_name through when creating a release with generated notes', async () => {
|
||||||
const createReleaseSpy = vi.fn(async () => ({
|
const createReleaseSpy = vi.fn(async () => ({
|
||||||
data: {
|
data: {
|
||||||
@@ -1063,6 +1362,154 @@ describe('github', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('reuses a published prerelease found by listing after direct tag lookup returns 404', async () => {
|
||||||
|
const existingRelease: Release = {
|
||||||
|
id: 77,
|
||||||
|
upload_url: 'existing-upload',
|
||||||
|
html_url: 'existing-html',
|
||||||
|
tag_name: 'v1.0.0',
|
||||||
|
name: 'nightly',
|
||||||
|
body: 'previous nightly body',
|
||||||
|
target_commitish: 'old-commit',
|
||||||
|
draft: false,
|
||||||
|
prerelease: true,
|
||||||
|
assets: [{ id: 5, name: 'nightly.zip' }],
|
||||||
|
};
|
||||||
|
const updatedRelease: Release = {
|
||||||
|
...existingRelease,
|
||||||
|
name: 'updated nightly',
|
||||||
|
body: 'updated nightly body',
|
||||||
|
target_commitish: 'new-commit',
|
||||||
|
};
|
||||||
|
const createRelease = unexpected('createRelease');
|
||||||
|
const updateRelease = vi.fn().mockResolvedValue({ data: updatedRelease });
|
||||||
|
const releaser = createReleaser({
|
||||||
|
getReleaseByTag: vi.fn().mockRejectedValue({ status: 404 }),
|
||||||
|
allReleases: async function* () {
|
||||||
|
yield { data: [existingRelease] };
|
||||||
|
},
|
||||||
|
createRelease,
|
||||||
|
updateRelease,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await release(
|
||||||
|
{
|
||||||
|
...config,
|
||||||
|
input_name: 'updated nightly',
|
||||||
|
input_body: 'updated nightly body',
|
||||||
|
input_prerelease: true,
|
||||||
|
input_draft: false,
|
||||||
|
input_target_commitish: 'new-commit',
|
||||||
|
},
|
||||||
|
releaser,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toEqual({ release: updatedRelease, created: false });
|
||||||
|
expect(updateRelease).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
release_id: existingRelease.id,
|
||||||
|
tag_name: existingRelease.tag_name,
|
||||||
|
target_commitish: 'new-commit',
|
||||||
|
draft: false,
|
||||||
|
prerelease: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(createRelease).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a replacement after an existing-release update returns a nested response 404', async () => {
|
||||||
|
const existingRelease: Release = {
|
||||||
|
id: 41,
|
||||||
|
upload_url: 'existing-upload',
|
||||||
|
html_url: 'existing-html',
|
||||||
|
tag_name: 'v1.0.0',
|
||||||
|
name: 'existing release',
|
||||||
|
body: 'existing body',
|
||||||
|
target_commitish: 'main',
|
||||||
|
draft: false,
|
||||||
|
prerelease: false,
|
||||||
|
assets: [],
|
||||||
|
};
|
||||||
|
const replacementRelease: Release = {
|
||||||
|
...existingRelease,
|
||||||
|
id: 42,
|
||||||
|
upload_url: 'replacement-upload',
|
||||||
|
html_url: 'replacement-html',
|
||||||
|
name: 'v1.0.0',
|
||||||
|
body: undefined,
|
||||||
|
draft: true,
|
||||||
|
};
|
||||||
|
const updateError = { response: { status: 404 }, message: 'release disappeared' };
|
||||||
|
const getReleaseByTag = vi
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValueOnce({ data: existingRelease })
|
||||||
|
.mockResolvedValue({ data: replacementRelease });
|
||||||
|
const updateRelease = vi.fn().mockRejectedValue(updateError);
|
||||||
|
const createRelease = vi.fn().mockResolvedValue({ data: replacementRelease });
|
||||||
|
const releaser = createReleaser({
|
||||||
|
getReleaseByTag,
|
||||||
|
updateRelease,
|
||||||
|
createRelease,
|
||||||
|
allReleases: async function* () {
|
||||||
|
yield { data: [replacementRelease] };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(release(config, releaser, 1)).resolves.toEqual({
|
||||||
|
release: replacementRelease,
|
||||||
|
created: true,
|
||||||
|
});
|
||||||
|
expect(updateRelease).toHaveBeenCalledOnce();
|
||||||
|
expect(createRelease).toHaveBeenCalledOnce();
|
||||||
|
expect(createRelease).toHaveBeenCalledWith({
|
||||||
|
owner: 'owner',
|
||||||
|
repo: 'repo',
|
||||||
|
tag_name: 'v1.0.0',
|
||||||
|
name: 'v1.0.0',
|
||||||
|
body: undefined,
|
||||||
|
draft: true,
|
||||||
|
prerelease: undefined,
|
||||||
|
target_commitish: undefined,
|
||||||
|
discussion_category_name: undefined,
|
||||||
|
generate_release_notes: false,
|
||||||
|
make_latest: undefined,
|
||||||
|
previous_tag_name: undefined,
|
||||||
|
});
|
||||||
|
expect(getReleaseByTag).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['a nested response 403', { response: { status: 403 }, message: 'forbidden' }],
|
||||||
|
['a nested response 500', { response: { status: 500 }, message: 'server error' }],
|
||||||
|
])(
|
||||||
|
'does not create a replacement after an existing-release update returns %s',
|
||||||
|
async (_name, updateError) => {
|
||||||
|
const existingRelease: Release = {
|
||||||
|
id: 41,
|
||||||
|
upload_url: 'existing-upload',
|
||||||
|
html_url: 'existing-html',
|
||||||
|
tag_name: 'v1.0.0',
|
||||||
|
name: 'existing release',
|
||||||
|
body: 'existing body',
|
||||||
|
target_commitish: 'main',
|
||||||
|
draft: false,
|
||||||
|
prerelease: false,
|
||||||
|
assets: [],
|
||||||
|
};
|
||||||
|
const createRelease = vi.fn();
|
||||||
|
const updateRelease = vi.fn().mockRejectedValue(updateError);
|
||||||
|
const releaser = createReleaser({
|
||||||
|
getReleaseByTag: vi.fn().mockResolvedValue({ data: existingRelease }),
|
||||||
|
updateRelease,
|
||||||
|
createRelease,
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(release(config, releaser, 1)).rejects.toBe(updateError);
|
||||||
|
expect(updateRelease).toHaveBeenCalledOnce();
|
||||||
|
expect(createRelease).not.toHaveBeenCalled();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
['an omitted draft input', undefined],
|
['an omitted draft input', undefined],
|
||||||
['a null-expression draft input', undefined],
|
['a null-expression draft input', undefined],
|
||||||
@@ -1208,19 +1655,37 @@ describe('github', () => {
|
|||||||
expect(uploadReleaseAsset).not.toHaveBeenCalled();
|
expect(uploadReleaseAsset).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('surfaces an actionable immutable-release error for prerelease uploads', async () => {
|
it.each([
|
||||||
|
{
|
||||||
|
name: 'standard release with a nested status',
|
||||||
|
prerelease: undefined,
|
||||||
|
uploadError: {
|
||||||
|
response: { status: 422 },
|
||||||
|
message: 'Cannot upload assets to an immutable release.',
|
||||||
|
},
|
||||||
|
expected:
|
||||||
|
'Cannot upload asset draft-false.txt to an immutable release. GitHub only allows asset uploads before a release is published, so upload assets to a draft release before you publish it.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'prerelease with response data',
|
||||||
|
prerelease: true,
|
||||||
|
uploadError: {
|
||||||
|
status: 422,
|
||||||
|
response: {
|
||||||
|
data: {
|
||||||
|
message: 'Cannot upload assets to an immutable release.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected:
|
||||||
|
'Cannot upload asset draft-false.txt to an immutable release. GitHub only allows asset uploads before a release is published, but draft prereleases publish with the release.published event instead of release.prereleased.',
|
||||||
|
},
|
||||||
|
])('surfaces an actionable immutable-release error for a $name', async (testCase) => {
|
||||||
const tempDir = mkdtempSync(join(tmpdir(), 'gh-release-immutable-'));
|
const tempDir = mkdtempSync(join(tmpdir(), 'gh-release-immutable-'));
|
||||||
const assetPath = join(tempDir, 'draft-false.txt');
|
const assetPath = join(tempDir, 'draft-false.txt');
|
||||||
writeFileSync(assetPath, 'hello');
|
writeFileSync(assetPath, 'hello');
|
||||||
|
|
||||||
const uploadReleaseAsset = vi.fn().mockRejectedValue({
|
const uploadReleaseAsset = vi.fn().mockRejectedValue(testCase.uploadError);
|
||||||
status: 422,
|
|
||||||
response: {
|
|
||||||
data: {
|
|
||||||
message: 'Cannot upload assets to an immutable release.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const mockReleaser: Releaser = {
|
const mockReleaser: Releaser = {
|
||||||
getReleaseByTag: () => Promise.reject('Not implemented'),
|
getReleaseByTag: () => Promise.reject('Not implemented'),
|
||||||
@@ -1237,23 +1702,23 @@ describe('github', () => {
|
|||||||
uploadReleaseAsset,
|
uploadReleaseAsset,
|
||||||
};
|
};
|
||||||
|
|
||||||
await expect(
|
try {
|
||||||
upload(
|
await expect(
|
||||||
{
|
upload(
|
||||||
...config,
|
{
|
||||||
input_prerelease: true,
|
...config,
|
||||||
},
|
input_prerelease: testCase.prerelease,
|
||||||
mockReleaser,
|
},
|
||||||
'https://uploads.github.com/repos/owner/repo/releases/1/assets',
|
mockReleaser,
|
||||||
assetPath,
|
'https://uploads.github.com/repos/owner/repo/releases/1/assets',
|
||||||
[],
|
assetPath,
|
||||||
1,
|
[],
|
||||||
),
|
1,
|
||||||
).rejects.toThrow(
|
),
|
||||||
'Cannot upload asset draft-false.txt to an immutable release. GitHub only allows asset uploads before a release is published, but draft prereleases publish with the release.published event instead of release.prereleased.',
|
).rejects.toThrow(testCase.expected);
|
||||||
);
|
} finally {
|
||||||
|
rmSync(tempDir, { recursive: true, force: true });
|
||||||
rmSync(tempDir, { recursive: true, force: true });
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('retries upload after deleting a conflicting renamed asset matched by label', async () => {
|
it('retries upload after deleting a conflicting renamed asset matched by label', async () => {
|
||||||
@@ -1330,7 +1795,24 @@ describe('github', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles 422 already_exists error gracefully', async () => {
|
it.each([
|
||||||
|
{
|
||||||
|
name: 'top-level status',
|
||||||
|
releaseError: {
|
||||||
|
status: 422,
|
||||||
|
response: { data: { errors: [{ code: 'already_exists' }] } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'nested response status',
|
||||||
|
releaseError: {
|
||||||
|
response: {
|
||||||
|
status: 422,
|
||||||
|
data: { errors: [{ code: 'already_exists' }] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])('retries a 422 already_exists race with $name', async (testCase) => {
|
||||||
vi.useFakeTimers();
|
vi.useFakeTimers();
|
||||||
const existingRelease = {
|
const existingRelease = {
|
||||||
id: 1,
|
id: 1,
|
||||||
@@ -1356,10 +1838,7 @@ describe('github', () => {
|
|||||||
},
|
},
|
||||||
createRelease: () => {
|
createRelease: () => {
|
||||||
createAttempts++;
|
createAttempts++;
|
||||||
return Promise.reject({
|
return Promise.reject(testCase.releaseError);
|
||||||
status: 422,
|
|
||||||
response: { data: { errors: [{ code: 'already_exists' }] } },
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
updateRelease: () =>
|
updateRelease: () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
@@ -1392,6 +1871,7 @@ describe('github', () => {
|
|||||||
assert.ok(result);
|
assert.ok(result);
|
||||||
assert.equal(result.release.id, 1);
|
assert.equal(result.release.id, 1);
|
||||||
assert.equal(result.created, false);
|
assert.equal(result.created, false);
|
||||||
|
expect(createAttempts).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('normalizes refs/tags-prefixed input_tag_name values before reusing an existing release', async () => {
|
it('normalizes refs/tags-prefixed input_tag_name values before reusing an existing release', async () => {
|
||||||
|
|||||||
@@ -286,6 +286,54 @@ describe('run', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('uses the selected moving-tag release ID for upload, finalization, and outputs', async () => {
|
||||||
|
config = {
|
||||||
|
...config,
|
||||||
|
input_tag_name: 'nightly',
|
||||||
|
input_draft: false,
|
||||||
|
input_prerelease: true,
|
||||||
|
input_files: ['nightly.zip'],
|
||||||
|
};
|
||||||
|
const selectedRelease: Release = {
|
||||||
|
...initialRelease,
|
||||||
|
id: 77,
|
||||||
|
upload_url: 'https://uploads.example.test/releases/77/assets{?name,label}',
|
||||||
|
html_url: 'https://example.test/releases/77',
|
||||||
|
tag_name: 'nightly',
|
||||||
|
name: 'nightly',
|
||||||
|
prerelease: true,
|
||||||
|
};
|
||||||
|
const publishedRelease: Release = {
|
||||||
|
...selectedRelease,
|
||||||
|
draft: false,
|
||||||
|
};
|
||||||
|
mocks.release.mockResolvedValue({ release: selectedRelease, created: false });
|
||||||
|
mocks.paths.mockReturnValue(['nightly.zip']);
|
||||||
|
mocks.upload.mockResolvedValue({ id: 9 });
|
||||||
|
mocks.finalizeRelease.mockResolvedValue(publishedRelease);
|
||||||
|
mocks.listReleaseAssets.mockResolvedValue([{ id: 9, name: 'nightly.zip' }]);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(mocks.upload).toHaveBeenCalledWith(
|
||||||
|
config,
|
||||||
|
mocks.releaser,
|
||||||
|
'https://uploads.example.test/releases/77/assets',
|
||||||
|
'nightly.zip',
|
||||||
|
selectedRelease.assets,
|
||||||
|
77,
|
||||||
|
);
|
||||||
|
expect(mocks.finalizeRelease).toHaveBeenCalledWith(
|
||||||
|
config,
|
||||||
|
mocks.releaser,
|
||||||
|
selectedRelease,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
expect(mocks.listReleaseAssets).toHaveBeenCalledWith(config, mocks.releaser, publishedRelease);
|
||||||
|
expect(mocks.setOutput).toHaveBeenCalledWith('id', '77');
|
||||||
|
expect(mocks.setOutput).toHaveBeenCalledWith('url', publishedRelease.html_url);
|
||||||
|
});
|
||||||
|
|
||||||
it('leaves an existing draft recoverable when an upload fails', async () => {
|
it('leaves an existing draft recoverable when an upload fails', async () => {
|
||||||
config = {
|
config = {
|
||||||
...config,
|
...config,
|
||||||
|
|||||||
Vendored
+25
-25
File diff suppressed because one or more lines are too long
Generated
+140
-140
@@ -18,9 +18,9 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/mime-types": "^3.0.1",
|
"@types/mime-types": "^3.0.1",
|
||||||
"@types/node": "^24",
|
"@types/node": "^24",
|
||||||
"@vitest/coverage-v8": "^4.1.9",
|
"@vitest/coverage-v8": "^4.1.10",
|
||||||
"esbuild": "^0.28.1",
|
"esbuild": "^0.28.1",
|
||||||
"prettier": "3.9.0",
|
"prettier": "3.9.5",
|
||||||
"typescript": "^7.0.2",
|
"typescript": "^7.0.2",
|
||||||
"vitest": "^4.0.4"
|
"vitest": "^4.0.4"
|
||||||
},
|
},
|
||||||
@@ -828,9 +828,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@oxc-project/types": {
|
"node_modules/@oxc-project/types": {
|
||||||
"version": "0.138.0",
|
"version": "0.139.0",
|
||||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.138.0.tgz",
|
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz",
|
||||||
"integrity": "sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==",
|
"integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -838,9 +838,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-android-arm64": {
|
"node_modules/@rolldown/binding-android-arm64": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz",
|
||||||
"integrity": "sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw==",
|
"integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -855,9 +855,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz",
|
||||||
"integrity": "sha512-aUi+HBvmYb7j8krl1+qJgkG8C17fO79gk3c+jPw4S8glRFc1DTija9S3EyaTSQUm5GJXYKDAsugBEhFHH2vYiQ==",
|
"integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -872,9 +872,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-darwin-x64": {
|
"node_modules/@rolldown/binding-darwin-x64": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz",
|
||||||
"integrity": "sha512-F7hHC3gwY11+vByKPRWqwGbeXWVgKmL+pTGCinaEhdihzBV2aQ0fvZOch9cXYUOKuKKq429HeYXOqQLc7wFCEg==",
|
"integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -889,9 +889,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz",
|
||||||
"integrity": "sha512-sI5yw+7s92SK6odiEhD5lKCBlWcpjHS5qyqpVQbZAJ0fIzEUXrmbl3DH2ybR3PZogulNJF+COLtmA8hUfvkCCQ==",
|
"integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -906,9 +906,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz",
|
||||||
"integrity": "sha512-mCi0OKgEieFircrtVYmQAFGszRtMnZ6fpZAXrxanXAu7lqZcsK1E1RAaZNG0uKAnxox3B1f4EyQNnoyMfN1vAA==",
|
"integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -923,9 +923,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz",
|
||||||
"integrity": "sha512-B9Ial3Kv5sh0SHnB1g/QWcUQCEvCF6QKGAl4zXypYj65mVI+B4AhFBwPtSN7pDrJeIx8Z7zdy4ntx+wQABom7w==",
|
"integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -943,9 +943,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz",
|
||||||
"integrity": "sha512-lZVym0PuHE1KZ22gmFTC15lAkrg9iTszR617oYRB/iPY1A56ywoJzVKOJBKaot5RiikCObmur6pogpse3gRcng==",
|
"integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -963,9 +963,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz",
|
||||||
"integrity": "sha512-t2DNiLJWNTbnEHyUzTumldML6ET4/g16467LZoDDJ3tSxGvguL5/NyC2lCsNKuyRycg9XeDQF5SSv+TNOhQEXg==",
|
"integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -983,9 +983,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz",
|
||||||
"integrity": "sha512-0WIRnL1Uw4BvTZRLQt+PVgo6ZKTJadlC2btP+/EOXv2f/DWbY0rEgl+y834mIVwP1FkTlWVTrGGJXf12lru7EQ==",
|
"integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -1003,9 +1003,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz",
|
||||||
"integrity": "sha512-JWtGshGfX+oENAKonoNkqEJX+7hC8yfhi9GUyPX1VX4mdh1y5r+ZiJLR5XzAB0aoP6s/PcILsGjKq8O0mm24bw==",
|
"integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1023,9 +1023,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-x64-musl": {
|
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz",
|
||||||
"integrity": "sha512-rT6yQcxUuXs4CnbofqwHRRV0iem349rLMYpTjkgQGLjrY4ado/eDzwPZPTCgTOlF6Nkp8NEv70yLMTn6qkWxsQ==",
|
"integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1043,9 +1043,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-openharmony-arm64": {
|
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz",
|
||||||
"integrity": "sha512-KXMGoboq5cyaCQjDA4GLuRiOwBQ0EyFnJoVViLeZ45/3rFItRODEr+NdsBcVpll40hhNArlm/speWGRvj08LzA==",
|
"integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1060,9 +1060,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-wasm32-wasi": {
|
"node_modules/@rolldown/binding-wasm32-wasi": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz",
|
||||||
"integrity": "sha512-5K83rb36oJiY7BCyE9zLZtGcPV4g5wvq+xwdO0XPIwDVZI8cyB/AUjkNXGb92/rnmezEkjMOpgY61rtwjQtFwg==",
|
"integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"wasm32"
|
"wasm32"
|
||||||
],
|
],
|
||||||
@@ -1079,9 +1079,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz",
|
||||||
"integrity": "sha512-PnWBtw3TV5KOg69HQQDR0mnQuyCmSGR2pAB4DC1rPF808fgKeTUMj2EOEyKATpgiuxuR5APQmiDO7PDgEjTFSA==",
|
"integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1096,9 +1096,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz",
|
||||||
"integrity": "sha512-M1lpniBePobTfsa7Ks9a199e1akxsXn+GYBUKsEzv3YFzOm1HJAMNwKI3qr0Zq+mxwx9gOZoTdP1yXRYsZUocQ==",
|
"integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1170,9 +1170,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "24.13.2",
|
"version": "24.13.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz",
|
||||||
"integrity": "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==",
|
"integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1520,14 +1520,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/coverage-v8": {
|
"node_modules/@vitest/coverage-v8": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz",
|
||||||
"integrity": "sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==",
|
"integrity": "sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bcoe/v8-coverage": "^1.0.2",
|
"@bcoe/v8-coverage": "^1.0.2",
|
||||||
"@vitest/utils": "4.1.9",
|
"@vitest/utils": "4.1.10",
|
||||||
"ast-v8-to-istanbul": "^1.0.0",
|
"ast-v8-to-istanbul": "^1.0.0",
|
||||||
"istanbul-lib-coverage": "^3.2.2",
|
"istanbul-lib-coverage": "^3.2.2",
|
||||||
"istanbul-lib-report": "^3.0.1",
|
"istanbul-lib-report": "^3.0.1",
|
||||||
@@ -1541,8 +1541,8 @@
|
|||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@vitest/browser": "4.1.9",
|
"@vitest/browser": "4.1.10",
|
||||||
"vitest": "4.1.9"
|
"vitest": "4.1.10"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@vitest/browser": {
|
"@vitest/browser": {
|
||||||
@@ -1561,16 +1561,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/expect": {
|
"node_modules/@vitest/expect": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz",
|
||||||
"integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==",
|
"integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@standard-schema/spec": "^1.1.0",
|
"@standard-schema/spec": "^1.1.0",
|
||||||
"@types/chai": "^5.2.2",
|
"@types/chai": "^5.2.2",
|
||||||
"@vitest/spy": "4.1.9",
|
"@vitest/spy": "4.1.10",
|
||||||
"@vitest/utils": "4.1.9",
|
"@vitest/utils": "4.1.10",
|
||||||
"chai": "^6.2.2",
|
"chai": "^6.2.2",
|
||||||
"tinyrainbow": "^3.1.0"
|
"tinyrainbow": "^3.1.0"
|
||||||
},
|
},
|
||||||
@@ -1579,13 +1579,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/mocker": {
|
"node_modules/@vitest/mocker": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz",
|
||||||
"integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==",
|
"integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/spy": "4.1.9",
|
"@vitest/spy": "4.1.10",
|
||||||
"estree-walker": "^3.0.3",
|
"estree-walker": "^3.0.3",
|
||||||
"magic-string": "^0.30.21"
|
"magic-string": "^0.30.21"
|
||||||
},
|
},
|
||||||
@@ -1606,9 +1606,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/pretty-format": {
|
"node_modules/@vitest/pretty-format": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz",
|
||||||
"integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==",
|
"integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1619,13 +1619,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/runner": {
|
"node_modules/@vitest/runner": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz",
|
||||||
"integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==",
|
"integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/utils": "4.1.9",
|
"@vitest/utils": "4.1.10",
|
||||||
"pathe": "^2.0.3"
|
"pathe": "^2.0.3"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -1633,14 +1633,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/snapshot": {
|
"node_modules/@vitest/snapshot": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz",
|
||||||
"integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==",
|
"integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "4.1.9",
|
"@vitest/pretty-format": "4.1.10",
|
||||||
"@vitest/utils": "4.1.9",
|
"@vitest/utils": "4.1.10",
|
||||||
"magic-string": "^0.30.21",
|
"magic-string": "^0.30.21",
|
||||||
"pathe": "^2.0.3"
|
"pathe": "^2.0.3"
|
||||||
},
|
},
|
||||||
@@ -1649,9 +1649,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/spy": {
|
"node_modules/@vitest/spy": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz",
|
||||||
"integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==",
|
"integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -1659,13 +1659,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/utils": {
|
"node_modules/@vitest/utils": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz",
|
||||||
"integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==",
|
"integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "4.1.9",
|
"@vitest/pretty-format": "4.1.10",
|
||||||
"convert-source-map": "^2.0.0",
|
"convert-source-map": "^2.0.0",
|
||||||
"tinyrainbow": "^3.1.0"
|
"tinyrainbow": "^3.1.0"
|
||||||
},
|
},
|
||||||
@@ -1710,9 +1710,9 @@
|
|||||||
"integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="
|
"integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "5.0.6",
|
"version": "5.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
||||||
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^4.0.2"
|
"balanced-match": "^4.0.2"
|
||||||
@@ -2321,9 +2321,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.15",
|
"version": "3.3.16",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
|
||||||
"integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==",
|
"integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2394,9 +2394,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.16",
|
"version": "8.5.19",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz",
|
||||||
"integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==",
|
"integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2423,9 +2423,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "3.9.0",
|
"version": "3.9.5",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.5.tgz",
|
||||||
"integrity": "sha512-LjIqSIC5VYLzs9WedVmJ2ljNAGnU+DteIClbahu4L/DBeWjZ6iT/k1lAYyu9JUh+1xINxWadaPw/Pl63y/agAw==",
|
"integrity": "sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -2439,13 +2439,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rolldown": {
|
"node_modules/rolldown": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz",
|
||||||
"integrity": "sha512-IjZYiLxZwpnhwhdBH2ugdTGVSdhCQUmLxLoqyjiL0JxYjyRst+5a0P3xfrTxJ5F638j4Mvvw5FAX5XE6eHpXbA==",
|
"integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oxc-project/types": "=0.138.0",
|
"@oxc-project/types": "=0.139.0",
|
||||||
"@rolldown/pluginutils": "^1.0.0"
|
"@rolldown/pluginutils": "^1.0.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -2455,21 +2455,21 @@
|
|||||||
"node": "^20.19.0 || >=22.12.0"
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rolldown/binding-android-arm64": "1.1.4",
|
"@rolldown/binding-android-arm64": "1.1.5",
|
||||||
"@rolldown/binding-darwin-arm64": "1.1.4",
|
"@rolldown/binding-darwin-arm64": "1.1.5",
|
||||||
"@rolldown/binding-darwin-x64": "1.1.4",
|
"@rolldown/binding-darwin-x64": "1.1.5",
|
||||||
"@rolldown/binding-freebsd-x64": "1.1.4",
|
"@rolldown/binding-freebsd-x64": "1.1.5",
|
||||||
"@rolldown/binding-linux-arm-gnueabihf": "1.1.4",
|
"@rolldown/binding-linux-arm-gnueabihf": "1.1.5",
|
||||||
"@rolldown/binding-linux-arm64-gnu": "1.1.4",
|
"@rolldown/binding-linux-arm64-gnu": "1.1.5",
|
||||||
"@rolldown/binding-linux-arm64-musl": "1.1.4",
|
"@rolldown/binding-linux-arm64-musl": "1.1.5",
|
||||||
"@rolldown/binding-linux-ppc64-gnu": "1.1.4",
|
"@rolldown/binding-linux-ppc64-gnu": "1.1.5",
|
||||||
"@rolldown/binding-linux-s390x-gnu": "1.1.4",
|
"@rolldown/binding-linux-s390x-gnu": "1.1.5",
|
||||||
"@rolldown/binding-linux-x64-gnu": "1.1.4",
|
"@rolldown/binding-linux-x64-gnu": "1.1.5",
|
||||||
"@rolldown/binding-linux-x64-musl": "1.1.4",
|
"@rolldown/binding-linux-x64-musl": "1.1.5",
|
||||||
"@rolldown/binding-openharmony-arm64": "1.1.4",
|
"@rolldown/binding-openharmony-arm64": "1.1.5",
|
||||||
"@rolldown/binding-wasm32-wasi": "1.1.4",
|
"@rolldown/binding-wasm32-wasi": "1.1.5",
|
||||||
"@rolldown/binding-win32-arm64-msvc": "1.1.4",
|
"@rolldown/binding-win32-arm64-msvc": "1.1.5",
|
||||||
"@rolldown/binding-win32-x64-msvc": "1.1.4"
|
"@rolldown/binding-win32-x64-msvc": "1.1.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
@@ -2647,16 +2647,16 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "8.1.3",
|
"version": "8.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz",
|
||||||
"integrity": "sha512-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA==",
|
"integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lightningcss": "^1.32.0",
|
"lightningcss": "^1.32.0",
|
||||||
"picomatch": "^4.0.4",
|
"picomatch": "^4.0.5",
|
||||||
"postcss": "^8.5.16",
|
"postcss": "^8.5.17",
|
||||||
"rolldown": "~1.1.3",
|
"rolldown": "~1.1.5",
|
||||||
"tinyglobby": "^0.2.17"
|
"tinyglobby": "^0.2.17"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -2725,19 +2725,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vitest": {
|
"node_modules/vitest": {
|
||||||
"version": "4.1.9",
|
"version": "4.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz",
|
||||||
"integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==",
|
"integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/expect": "4.1.9",
|
"@vitest/expect": "4.1.10",
|
||||||
"@vitest/mocker": "4.1.9",
|
"@vitest/mocker": "4.1.10",
|
||||||
"@vitest/pretty-format": "4.1.9",
|
"@vitest/pretty-format": "4.1.10",
|
||||||
"@vitest/runner": "4.1.9",
|
"@vitest/runner": "4.1.10",
|
||||||
"@vitest/snapshot": "4.1.9",
|
"@vitest/snapshot": "4.1.10",
|
||||||
"@vitest/spy": "4.1.9",
|
"@vitest/spy": "4.1.10",
|
||||||
"@vitest/utils": "4.1.9",
|
"@vitest/utils": "4.1.10",
|
||||||
"es-module-lexer": "^2.0.0",
|
"es-module-lexer": "^2.0.0",
|
||||||
"expect-type": "^1.3.0",
|
"expect-type": "^1.3.0",
|
||||||
"magic-string": "^0.30.21",
|
"magic-string": "^0.30.21",
|
||||||
@@ -2765,12 +2765,12 @@
|
|||||||
"@edge-runtime/vm": "*",
|
"@edge-runtime/vm": "*",
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
|
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
|
||||||
"@vitest/browser-playwright": "4.1.9",
|
"@vitest/browser-playwright": "4.1.10",
|
||||||
"@vitest/browser-preview": "4.1.9",
|
"@vitest/browser-preview": "4.1.10",
|
||||||
"@vitest/browser-webdriverio": "4.1.9",
|
"@vitest/browser-webdriverio": "4.1.10",
|
||||||
"@vitest/coverage-istanbul": "4.1.9",
|
"@vitest/coverage-istanbul": "4.1.10",
|
||||||
"@vitest/coverage-v8": "4.1.9",
|
"@vitest/coverage-v8": "4.1.10",
|
||||||
"@vitest/ui": "4.1.9",
|
"@vitest/ui": "4.1.10",
|
||||||
"happy-dom": "*",
|
"happy-dom": "*",
|
||||||
"jsdom": "*",
|
"jsdom": "*",
|
||||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||||
|
|||||||
+2
-2
@@ -35,9 +35,9 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/mime-types": "^3.0.1",
|
"@types/mime-types": "^3.0.1",
|
||||||
"@types/node": "^24",
|
"@types/node": "^24",
|
||||||
"@vitest/coverage-v8": "^4.1.9",
|
"@vitest/coverage-v8": "^4.1.10",
|
||||||
"esbuild": "^0.28.1",
|
"esbuild": "^0.28.1",
|
||||||
"prettier": "3.9.0",
|
"prettier": "3.9.5",
|
||||||
"typescript": "^7.0.2",
|
"typescript": "^7.0.2",
|
||||||
"vitest": "^4.0.4"
|
"vitest": "^4.0.4"
|
||||||
}
|
}
|
||||||
|
|||||||
+69
-39
@@ -9,6 +9,43 @@ type GitHub = InstanceType<typeof GitHub>;
|
|||||||
|
|
||||||
type UploadChunk = ArrayBuffer | Uint8Array<ArrayBufferLike>;
|
type UploadChunk = ArrayBuffer | Uint8Array<ArrayBufferLike>;
|
||||||
type UploadBody = ReadableStream<Uint8Array<ArrayBufferLike>>;
|
type UploadBody = ReadableStream<Uint8Array<ArrayBufferLike>>;
|
||||||
|
type UnknownRecord = Record<string, unknown>;
|
||||||
|
|
||||||
|
const asRecord = (value: unknown): UnknownRecord | undefined =>
|
||||||
|
typeof value === 'object' && value !== null ? (value as UnknownRecord) : undefined;
|
||||||
|
|
||||||
|
const getErrorStatus = (error: unknown): number | undefined => {
|
||||||
|
const errorRecord = asRecord(error);
|
||||||
|
if (typeof errorRecord?.status === 'number') {
|
||||||
|
return errorRecord.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = asRecord(errorRecord?.response);
|
||||||
|
return typeof response?.status === 'number' ? response.status : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getResponseData = (error: unknown): UnknownRecord | undefined => {
|
||||||
|
const response = asRecord(asRecord(error)?.response);
|
||||||
|
return asRecord(response?.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getErrorMessage = (error: unknown): string | undefined => {
|
||||||
|
const message = asRecord(error)?.message;
|
||||||
|
return typeof message === 'string' ? message : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRequestUrl = (error: unknown): string | undefined => {
|
||||||
|
const request = asRecord(asRecord(error)?.request);
|
||||||
|
return typeof request?.url === 'string' ? request.url : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getValidationErrors = (error: unknown): unknown[] => {
|
||||||
|
const errors = getResponseData(error)?.errors;
|
||||||
|
return Array.isArray(errors) ? errors : [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasValidationErrorCode = (error: unknown, code: string): boolean =>
|
||||||
|
asRecord(getValidationErrors(error)[0])?.code === code;
|
||||||
|
|
||||||
const fileUploadStream = (fileHandle: FileHandle): UploadBody => {
|
const fileUploadStream = (fileHandle: FileHandle): UploadBody => {
|
||||||
const source = fileHandle.readableWebStream() as ReadableStream<UploadChunk>;
|
const source = fileHandle.readableWebStream() as ReadableStream<UploadChunk>;
|
||||||
@@ -278,10 +315,7 @@ export class GitHubReleaser implements Releaser {
|
|||||||
try {
|
try {
|
||||||
await this.github.rest.repos.deleteReleaseAsset(githubParams);
|
await this.github.rest.repos.deleteReleaseAsset(githubParams);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
const status =
|
if (getErrorStatus(error) !== 404) {
|
||||||
(error as { status?: number; response?: { status?: number } })?.status ??
|
|
||||||
(error as { response?: { status?: number } })?.response?.status;
|
|
||||||
if (status !== 404) {
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,10 +386,10 @@ const releaseAssetMatchesName = (
|
|||||||
asset: { name: string; label?: string | null },
|
asset: { name: string; label?: string | null },
|
||||||
): boolean => asset.name === name || asset.name === alignAssetName(name) || asset.label === name;
|
): boolean => asset.name === name || asset.name === alignAssetName(name) || asset.label === name;
|
||||||
|
|
||||||
const isReleaseAssetUpdateNotFound = (error: any): boolean => {
|
const isReleaseAssetUpdateNotFound = (error: unknown): boolean => {
|
||||||
const errorStatus = error?.status ?? error?.response?.status;
|
const errorStatus = getErrorStatus(error);
|
||||||
const requestUrl = error?.request?.url;
|
const requestUrl = getRequestUrl(error);
|
||||||
const errorMessage = error?.message;
|
const message = getErrorMessage(error);
|
||||||
const isReleaseAssetRequest =
|
const isReleaseAssetRequest =
|
||||||
typeof requestUrl === 'string' &&
|
typeof requestUrl === 'string' &&
|
||||||
(/\/releases\/assets\//.test(requestUrl) || /\/releases\/\d+\/assets(?:\?|$)/.test(requestUrl));
|
(/\/releases\/assets\//.test(requestUrl) || /\/releases\/\d+\/assets(?:\?|$)/.test(requestUrl));
|
||||||
@@ -363,15 +397,15 @@ const isReleaseAssetUpdateNotFound = (error: any): boolean => {
|
|||||||
return (
|
return (
|
||||||
errorStatus === 404 &&
|
errorStatus === 404 &&
|
||||||
(isReleaseAssetRequest ||
|
(isReleaseAssetRequest ||
|
||||||
(typeof errorMessage === 'string' && errorMessage.includes('update-a-release-asset')))
|
(typeof message === 'string' && message.includes('update-a-release-asset')))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isImmutableReleaseAssetUploadFailure = (error: any): boolean => {
|
const isImmutableReleaseAssetUploadFailure = (error: unknown): boolean => {
|
||||||
const errorStatus = error?.status ?? error?.response?.status;
|
const errorStatus = getErrorStatus(error);
|
||||||
const errorMessage = error?.response?.data?.message ?? error?.message;
|
const message = getResponseData(error)?.message ?? getErrorMessage(error);
|
||||||
|
|
||||||
return errorStatus === 422 && /immutable release/i.test(String(errorMessage));
|
return errorStatus === 422 && /immutable release/i.test(String(message));
|
||||||
};
|
};
|
||||||
|
|
||||||
const immutableReleaseAssetUploadMessage = (
|
const immutableReleaseAssetUploadMessage = (
|
||||||
@@ -480,8 +514,8 @@ export const upload = async (
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
return await updateAssetLabel(uploadedAsset.id);
|
return await updateAssetLabel(uploadedAsset.id);
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
const errorStatus = error?.status ?? error?.response?.status;
|
const errorStatus = getErrorStatus(error);
|
||||||
|
|
||||||
if (errorStatus === 404 && releaseId !== undefined) {
|
if (errorStatus === 404 && releaseId !== undefined) {
|
||||||
try {
|
try {
|
||||||
@@ -518,9 +552,8 @@ export const upload = async (
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
return await handleUploadedAsset(await uploadAsset());
|
return await handleUploadedAsset(await uploadAsset());
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
const errorStatus = error?.status ?? error?.response?.status;
|
const errorStatus = getErrorStatus(error);
|
||||||
const errorData = error?.response?.data;
|
|
||||||
|
|
||||||
if (isImmutableReleaseAssetUploadFailure(error)) {
|
if (isImmutableReleaseAssetUploadFailure(error)) {
|
||||||
throw new Error(immutableReleaseAssetUploadMessage(name, config.input_prerelease));
|
throw new Error(immutableReleaseAssetUploadMessage(name, config.input_prerelease));
|
||||||
@@ -548,7 +581,7 @@ export const upload = async (
|
|||||||
if (
|
if (
|
||||||
config.input_overwrite_files !== false &&
|
config.input_overwrite_files !== false &&
|
||||||
errorStatus === 422 &&
|
errorStatus === 422 &&
|
||||||
errorData?.errors?.[0]?.code === 'already_exists' &&
|
hasValidationErrorCode(error, 'already_exists') &&
|
||||||
releaseId !== undefined
|
releaseId !== undefined
|
||||||
) {
|
) {
|
||||||
console.log(
|
console.log(
|
||||||
@@ -603,7 +636,7 @@ export const release = async (
|
|||||||
try {
|
try {
|
||||||
_release = await findTagFromReleases(releaser, owner, repo, tag, maxRetries);
|
_release = await findTagFromReleases(releaser, owner, repo, tag, maxRetries);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.status === 404) {
|
if (getErrorStatus(error) === 404) {
|
||||||
const diagnostic = releaseLookup404Message(owner, repo, error);
|
const diagnostic = releaseLookup404Message(owner, repo, error);
|
||||||
console.log(`⚠️ ${diagnostic}`);
|
console.log(`⚠️ ${diagnostic}`);
|
||||||
throw new ReleaseAccessError(diagnostic, error);
|
throw new ReleaseAccessError(diagnostic, error);
|
||||||
@@ -689,7 +722,7 @@ export const release = async (
|
|||||||
if (error instanceof ReleaseCreationError) {
|
if (error instanceof ReleaseCreationError) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
if (error.status !== 404) {
|
if (getErrorStatus(error) !== 404) {
|
||||||
console.log(
|
console.log(
|
||||||
`⚠️ Unexpected error fetching GitHub release for tag ${config.github_ref}: ${error}`,
|
`⚠️ Unexpected error fetching GitHub release for tag ${config.github_ref}: ${error}`,
|
||||||
);
|
);
|
||||||
@@ -840,7 +873,7 @@ export async function findTagFromReleases(
|
|||||||
const { data: release } = await releaser.getReleaseByTag({ owner, repo, tag });
|
const { data: release } = await releaser.getReleaseByTag({ owner, repo, tag });
|
||||||
return release;
|
return release;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.status !== 404) {
|
if (getErrorStatus(error) !== 404) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1058,15 +1091,12 @@ async function createRelease(
|
|||||||
created: canonicalRelease.id === createdRelease.data.id,
|
created: canonicalRelease.id === createdRelease.data.id,
|
||||||
};
|
};
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
const githubError = error as {
|
const errorStatus = getErrorStatus(error);
|
||||||
status?: number;
|
|
||||||
response?: { data?: { errors?: Array<{ code?: string }> } };
|
|
||||||
};
|
|
||||||
// presume a race with competing matrix runs
|
// presume a race with competing matrix runs
|
||||||
console.log(`⚠️ GitHub release failed with status: ${githubError.status}`);
|
console.log(`⚠️ GitHub release failed with status: ${errorStatus}`);
|
||||||
console.log(errorMessage(error));
|
console.log(errorMessage(error));
|
||||||
|
|
||||||
switch (githubError.status) {
|
switch (errorStatus) {
|
||||||
case 403:
|
case 403:
|
||||||
console.log(
|
console.log(
|
||||||
'Skip retry — your GitHub token/PAT does not have the required permission to create a release',
|
'Skip retry — your GitHub token/PAT does not have the required permission to create a release',
|
||||||
@@ -1080,8 +1110,7 @@ async function createRelease(
|
|||||||
|
|
||||||
case 422:
|
case 422:
|
||||||
// Check if this is a race condition with "already_exists" error
|
// Check if this is a race condition with "already_exists" error
|
||||||
const errorData = githubError.response?.data;
|
if (hasValidationErrorCode(error, 'already_exists')) {
|
||||||
if (errorData?.errors?.[0]?.code === 'already_exists') {
|
|
||||||
console.log(
|
console.log(
|
||||||
'⚠️ Release already exists (race condition detected), retrying to find and update existing release...',
|
'⚠️ Release already exists (race condition detected), retrying to find and update existing release...',
|
||||||
);
|
);
|
||||||
@@ -1098,16 +1127,17 @@ async function createRelease(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isTagCreationBlockedError(error: any): boolean {
|
function isTagCreationBlockedError(error: unknown): boolean {
|
||||||
const errors = error?.response?.data?.errors;
|
if (getErrorStatus(error) !== 422) {
|
||||||
if (!Array.isArray(errors) || error?.status !== 422) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.some(
|
return getValidationErrors(error).some((validationError) => {
|
||||||
({ field, message }: { field?: string; message?: string }) =>
|
const errorRecord = asRecord(validationError);
|
||||||
field === 'pre_receive' &&
|
return (
|
||||||
typeof message === 'string' &&
|
errorRecord?.field === 'pre_receive' &&
|
||||||
message.includes('creations being restricted'),
|
typeof errorRecord.message === 'string' &&
|
||||||
);
|
errorRecord.message.includes('creations being restricted')
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"useUnknownInCatchVariables": false,
|
"useUnknownInCatchVariables": true,
|
||||||
/* Basic Options */
|
/* Basic Options */
|
||||||
// "incremental": true, /* Enable incremental compilation */
|
// "incremental": true, /* Enable incremental compilation */
|
||||||
"target": "es2022",
|
"target": "es2022",
|
||||||
|
|||||||
+4
-4
@@ -7,10 +7,10 @@ export default defineConfig({
|
|||||||
reporter: ['text', 'json-summary', 'lcov'],
|
reporter: ['text', 'json-summary', 'lcov'],
|
||||||
include: ['src/**/*.ts'],
|
include: ['src/**/*.ts'],
|
||||||
thresholds: {
|
thresholds: {
|
||||||
statements: 88,
|
statements: 94,
|
||||||
branches: 83,
|
branches: 90,
|
||||||
functions: 86,
|
functions: 95,
|
||||||
lines: 88,
|
lines: 94,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
include: ['__tests__/**/*.ts'],
|
include: ['__tests__/**/*.ts'],
|
||||||
|
|||||||
Reference in New Issue
Block a user