fix(ci): prevent signed macOS core archives from polluting cache
This commit is contained in:
1 parent
338d04ae5b
commit
a1f93630ae
2 files changed
+43
-17
No files matched your search
@@ -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
@@ -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 });
|
||||
|
||||
Reference in new issue
Block a user