fix(ci): recover from stalled GitCode asset uploads

This commit is contained in:
lzt404 committed 2026-08-05 17:44:19 +08:00
1 parent d679f8983f
commit 5ad07df8ee
3 files changed
+62 -10

No files matched your search

+2 -1
View File
@@ -33,7 +33,7 @@ jobs:
|| github.event.workflow_run.conclusion == 'success'
)
runs-on: ubuntu-24.04
timeout-minutes: 60
timeout-minutes: 35
steps:
- uses: actions/checkout@v7
- name: Resolve release tag
@@ -75,6 +75,7 @@ jobs:
run: gh release download "${{ steps.release.outputs.tag }}" --dir artifacts
- name: Publish GitCode mirror
if: steps.release.outputs.should_mirror == 'true'
timeout-minutes: 30
env:
ARTIFACT_DIRECTORY: artifacts
GITCODE_ACCESS_TOKEN: ${{ secrets.GITCODE_ACCESS_TOKEN }}
+22 -9
View File
@@ -4,6 +4,8 @@ import { basename, join, resolve } from 'node:path';
import { pathToFileURL } from 'node:url';
const API_BASE = 'https://api.gitcode.com/api/v5';
const API_TIMEOUT_MS = 30_000;
const UPLOAD_TIMEOUT_SECONDS = 180;
function parseRepository(value) {
const repository = String(value ?? '').trim();
@@ -36,6 +38,7 @@ async function apiRequest(path, {
method,
headers: body ? { 'Content-Type': 'application/json' } : undefined,
body: body ? JSON.stringify(body) : undefined,
signal: AbortSignal.timeout(API_TIMEOUT_MS),
});
} catch (error) {
throw new Error(`GitCode API request failed for ${path}: ${error instanceof Error ? error.message : error}`);
@@ -53,11 +56,10 @@ export function uploadFileWithCurl({ url, headers, path }) {
'--silent',
'--show-error',
'--location',
'--retry', '3',
'--retry-delay', '5',
'--retry-all-errors',
'--connect-timeout', '30',
'--max-time', '1800',
'--speed-limit', '1',
'--speed-time', '30',
'--max-time', String(UPLOAD_TIMEOUT_SECONDS),
'--request', 'PUT',
'--upload-file', path,
'--output', process.platform === 'win32' ? 'NUL' : '/dev/null',
@@ -147,11 +149,22 @@ export async function publishGitcodeRelease({
const headers = Array.isArray(rawHeaders)
? Object.fromEntries(rawHeaders.map(({ key, name, value }) => [key ?? name, value]))
: rawHeaders && typeof rawHeaders === 'object' ? rawHeaders : {};
await uploadImpl({
url: uploadUrl,
headers,
path: join(directory, entry.name),
});
console.log(`Uploading GitCode asset: ${entry.name}`);
try {
await uploadImpl({
url: uploadUrl,
headers,
path: join(directory, entry.name),
});
} catch (error) {
console.warn(`GitCode upload command did not finish cleanly; verifying asset: ${entry.name}`);
const refreshedRelease = await apiRequest(releaseByTagPath, { token, fetchImpl });
const uploadedDespiteError = (refreshedRelease.assets ?? [])
.some((asset) => asset.name === entry.name);
if (!uploadedDespiteError) throw error;
console.log(`GitCode asset exists despite upload error: ${entry.name}`);
}
existingAssets.add(entry.name);
console.log(`Uploaded GitCode asset: ${entry.name}`);
}
}
+38
View File
@@ -53,4 +53,42 @@ describe('GitCode release mirror', () => {
await rm(directory, { recursive: true, force: true });
}
});
test('continues when GitCode stores an asset but the upload response times out', async () => {
const directory = await mkdtemp(join(tmpdir(), 'easycli-gitcode-release-'));
const artifactName = 'EasyCLIProxyAPI-v1.2.3-Windows-amd64.zip';
let releaseRequests = 0;
try {
await writeFile(join(directory, artifactName), 'artifact');
const fetchImpl = async (input: URL | RequestInfo) => {
const url = String(input);
if (url.includes('/releases/tags/')) {
releaseRequests += 1;
return Response.json({
tag_name: 'v1.2.3',
assets: releaseRequests === 1 ? [] : [{ name: artifactName }],
});
}
if (url.includes('/upload_url?')) {
return Response.json({ upload_url: 'https://file-cdn.gitcode.com/presigned-upload' });
}
return new Response('', { status: 500 });
};
await expect(publishGitcodeRelease({
directory,
repository: 'mirror-owner/EasyCLIProxyAPI',
token: 'secret-token',
tag: 'v1.2.3',
fetchImpl,
uploadImpl: async () => {
throw new Error('upload response timed out');
},
})).resolves.toBeUndefined();
expect(releaseRequests).toBe(2);
} finally {
await rm(directory, { recursive: true, force: true });
}
});
});