fix(ci): prevent signed macOS core archives from polluting cache

This commit is contained in:
lzt404 committed 2026-08-16 02:27:07 +08:00
1 parent 338d04ae5b
commit a1f93630ae
2 files changed
+43 -17

No files matched your search

+8 -1
View File
@@ -121,7 +121,7 @@ jobs:
uses: actions/cache@v5
with:
path: cpa-core
key: core-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles('core-version.txt') }}
key: core-v2-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles('core-version.txt') }}
- name: Apply release version
run: node scripts/set-version.mjs "${{ needs.version.outputs.version }}"
- name: Resolve release package name
@@ -179,6 +179,8 @@ jobs:
keychain_path="$RUNNER_TEMP/apple-signing.keychain-db"
keychain_password="$(uuidgen)"
signed_core_dir="$RUNNER_TEMP/signed-core"
unsigned_core_archive="$RUNNER_TEMP/unsigned-core-${{ matrix.arch }}.tar.gz"
core_archive=""
staging_dir="$RUNNER_TEMP/easycli-dmg-staging"
bundle_dir="src-tauri/target/release/bundle"
output_dmg="${{ steps.package.outputs.stem }}.dmg"
@@ -194,7 +196,11 @@ jobs:
cleanup() {
set +e
if [ -n "$core_archive" ] && [ -f "$unsigned_core_archive" ]; then
cp "$unsigned_core_archive" "$core_archive"
fi
rm -f "$certificate_path"
rm -f "$unsigned_core_archive"
rm -rf "$signed_core_dir" "$staging_dir"
security delete-keychain "$keychain_path"
}
@@ -213,6 +219,7 @@ jobs:
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$keychain_password" "$keychain_path"
core_archive="$(find cpa-core -maxdepth 1 -type f -name 'CLIProxyAPI_*_darwin_${{ matrix.arch }}.tar.gz' -print -quit)"
cp "$core_archive" "$unsigned_core_archive"
mkdir -p "$signed_core_dir"
tar -xzf "$core_archive" -C "$signed_core_dir"
core_binary="$(find "$signed_core_dir" -type f -name 'cli-proxy-api' -print -quit)"
+35 -16
View File
@@ -30,30 +30,49 @@ const assetName = `CLIProxyAPI_${version}_${targetOS}_${targetArch}.${extension}
const sourceDir = join(root, 'cpa-core');
const sourceArchive = join(sourceDir, assetName);
const checksumsPath = join(sourceDir, 'checksums.txt');
const tag = `v${version}`;
const releaseBase = `https://github.com/router-for-me/CLIProxyAPI/releases/download/${tag}`;
const downloadReleaseFile = async (name, destination) => {
const response = await fetch(`${releaseBase}/${name}`);
if (!response.ok) throw new Error(`Download ${name} failed: HTTP ${response.status}`);
await writeFile(destination, Buffer.from(await response.arrayBuffer()));
};
const downloadCoreRelease = () => Promise.all([
downloadReleaseFile(assetName, sourceArchive),
downloadReleaseFile('checksums.txt', checksumsPath),
]);
const verifyCoreArchive = async () => {
if (!existsSync(checksumsPath)) return null;
const checksums = await readFile(checksumsPath, 'utf8');
const expected = checksums.split(/\r?\n/).map((line) => line.trim().split(/\s+/)).find((parts) => parts[1]?.replace(/^\*/, '') === assetName)?.[0]?.toLowerCase();
if (!expected) throw new Error(`checksums.txt does not contain ${assetName}`);
const actual = createHash('sha256').update(await readFile(sourceArchive)).digest('hex');
return { actual, expected };
};
await mkdir(sourceDir, { recursive: true });
if (!existsSync(sourceArchive)) {
if (!shouldDownload) {
throw new Error(`Built-in core archive not found: ${sourceArchive}`);
}
const tag = `v${version}`;
const releaseBase = `https://github.com/router-for-me/CLIProxyAPI/releases/download/${tag}`;
const [archiveResponse, checksumsResponse] = await Promise.all([
fetch(`${releaseBase}/${assetName}`),
fetch(`${releaseBase}/checksums.txt`),
]);
if (!archiveResponse.ok) throw new Error(`Download ${assetName} failed: HTTP ${archiveResponse.status}`);
if (!checksumsResponse.ok) throw new Error(`Download checksums.txt failed: HTTP ${checksumsResponse.status}`);
await writeFile(sourceArchive, Buffer.from(await archiveResponse.arrayBuffer()));
await writeFile(checksumsPath, await checksumsResponse.text());
await downloadCoreRelease();
} else if (shouldDownload && !existsSync(checksumsPath)) {
await downloadReleaseFile('checksums.txt', checksumsPath);
}
if (existsSync(checksumsPath)) {
const checksums = await readFile(checksumsPath, 'utf8');
const expected = checksums.split(/\r?\n/).map((line) => line.trim().split(/\s+/)).find((parts) => parts[1]?.replace(/^\*/, '') === assetName)?.[0]?.toLowerCase();
if (!expected) throw new Error(`checksums.txt does not contain ${assetName}`);
const actual = createHash('sha256').update(await readFile(sourceArchive)).digest('hex');
if (actual !== expected) throw new Error(`SHA-256 mismatch for ${assetName}`);
let verification = await verifyCoreArchive();
if (verification && verification.actual !== verification.expected && shouldDownload) {
console.warn(`Cached ${assetName} failed SHA-256 verification; downloading a fresh copy`);
await downloadCoreRelease();
verification = await verifyCoreArchive();
}
if (verification && verification.actual !== verification.expected) {
throw new Error(
`SHA-256 mismatch for ${assetName}: expected ${verification.expected}, got ${verification.actual}`,
);
}
await mkdir(output, { recursive: true });