chore(release): stop publishing legacy Windows update packages (#159)
This commit is contained in:
1 parent
b15773b0f4
commit
e60b47fd7c
3 files changed
+69
-101
No files matched your search
@@ -154,14 +154,6 @@ jobs:
|
||||
--arch "${{ matrix.arch }}"
|
||||
--download true
|
||||
shell: bash
|
||||
- name: Prepare Windows legacy updater payload
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$updateDir = "release/update-${{ matrix.arch }}"
|
||||
New-Item -ItemType Directory -Force -Path $updateDir | Out-Null
|
||||
Copy-Item -LiteralPath "release/${{ steps.package.outputs.stem }}/EasyCLIProxyAPI.exe" -Destination $updateDir
|
||||
Copy-Item -LiteralPath "release/${{ steps.package.outputs.stem }}/portable-app.json" -Destination $updateDir
|
||||
- name: Build signed macOS DMG
|
||||
if: matrix.os == 'darwin'
|
||||
shell: bash
|
||||
@@ -267,21 +259,12 @@ jobs:
|
||||
shell: pwsh
|
||||
run: |
|
||||
Compress-Archive -Path "release/${{ steps.package.outputs.stem }}" -DestinationPath "${{ steps.package.outputs.stem }}.zip"
|
||||
$updateArchive = "EasyCLIProxyAPI-update-v${{ steps.package.outputs.version }}-Windows-${{ matrix.arch }}.zip"
|
||||
Compress-Archive -Path "release/update-${{ matrix.arch }}/*" -DestinationPath $updateArchive
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ steps.package.outputs.stem }}
|
||||
path: ${{ steps.package.outputs.stem }}.${{ matrix.suffix }}
|
||||
if-no-files-found: error
|
||||
retention-days: 14
|
||||
- uses: actions/upload-artifact@v7
|
||||
if: matrix.os == 'windows'
|
||||
with:
|
||||
name: portable-updater-windows-${{ matrix.arch }}
|
||||
path: EasyCLIProxyAPI-update-v${{ steps.package.outputs.version }}-Windows-${{ matrix.arch }}.zip
|
||||
if-no-files-found: error
|
||||
retention-days: 14
|
||||
publish:
|
||||
name: Publish GitHub release
|
||||
if: >-
|
||||
|
||||
+26
-32
@@ -34,9 +34,9 @@ export async function generatePortableUpdateManifest({
|
||||
}) {
|
||||
const resolvedDirectory = resolve(directory ?? 'artifacts');
|
||||
const platformSpecs = {
|
||||
windows: { display: 'Windows', suffix: 'zip', legacy: true },
|
||||
linux: { display: 'Linux', suffix: 'tar.gz', legacy: false },
|
||||
darwin: { display: 'Darwin', suffix: 'dmg', legacy: false },
|
||||
windows: { display: 'Windows', suffix: 'zip' },
|
||||
linux: { display: 'Linux', suffix: 'tar.gz' },
|
||||
darwin: { display: 'Darwin', suffix: 'dmg' },
|
||||
};
|
||||
const normalizedPlatform = String(platform).trim().toLowerCase();
|
||||
const platformSpec = platformSpecs[normalizedPlatform];
|
||||
@@ -64,35 +64,26 @@ export async function generatePortableUpdateManifest({
|
||||
}
|
||||
|
||||
const assets = {};
|
||||
const fullAssets = {};
|
||||
for (const arch of ['amd64', 'aarch64']) {
|
||||
const fullFilename = `EasyCLIProxyAPI-${tag}-${platformSpec.display}-${arch}.${platformSpec.suffix}`;
|
||||
const candidates = platformSpec.legacy
|
||||
? [
|
||||
[assets, `EasyCLIProxyAPI-update-${tag}-Windows-${arch}.zip`],
|
||||
[fullAssets, fullFilename],
|
||||
]
|
||||
: [[assets, fullFilename]];
|
||||
for (const [collection, filename] of candidates) {
|
||||
const path = join(resolvedDirectory, filename);
|
||||
const [contents, metadata] = await Promise.all([readFile(path), stat(path)]);
|
||||
if (!metadata.isFile() || metadata.size === 0) {
|
||||
throw new Error(`Portable release asset is empty or not a file: ${filename}`);
|
||||
}
|
||||
const asset = {
|
||||
url: `https://github.com/${resolvedRepository}/releases/download/${tag}/${filename}`,
|
||||
sha256: createHash('sha256').update(contents).digest('hex'),
|
||||
sizeBytes: metadata.size,
|
||||
};
|
||||
if (resolvedGitcodeRepository) {
|
||||
asset.fallbackUrls = [gitcodeReleaseAttachmentUrl(
|
||||
resolvedGitcodeRepository,
|
||||
tag,
|
||||
filename,
|
||||
)];
|
||||
}
|
||||
collection[`${normalizedPlatform}-${arch}`] = asset;
|
||||
const filename = `EasyCLIProxyAPI-${tag}-${platformSpec.display}-${arch}.${platformSpec.suffix}`;
|
||||
const path = join(resolvedDirectory, filename);
|
||||
const [contents, metadata] = await Promise.all([readFile(path), stat(path)]);
|
||||
if (!metadata.isFile() || metadata.size === 0) {
|
||||
throw new Error(`Portable release asset is empty or not a file: ${filename}`);
|
||||
}
|
||||
const asset = {
|
||||
url: `https://github.com/${resolvedRepository}/releases/download/${tag}/${filename}`,
|
||||
sha256: createHash('sha256').update(contents).digest('hex'),
|
||||
sizeBytes: metadata.size,
|
||||
};
|
||||
if (resolvedGitcodeRepository) {
|
||||
asset.fallbackUrls = [gitcodeReleaseAttachmentUrl(
|
||||
resolvedGitcodeRepository,
|
||||
tag,
|
||||
filename,
|
||||
)];
|
||||
}
|
||||
assets[`${normalizedPlatform}-${arch}`] = asset;
|
||||
}
|
||||
|
||||
const manifest = {
|
||||
@@ -101,7 +92,6 @@ export async function generatePortableUpdateManifest({
|
||||
publishedAt,
|
||||
releaseUrl: `https://github.com/${resolvedRepository}/releases/tag/${tag}`,
|
||||
assets,
|
||||
...(platformSpec.legacy ? { fullAssets } : {}),
|
||||
};
|
||||
|
||||
await writeFile(resolvedOutput, `${JSON.stringify(manifest, null, 2)}\n`);
|
||||
@@ -125,6 +115,7 @@ async function main() {
|
||||
const outputs = requestedOutput
|
||||
? [resolve(requestedOutput)]
|
||||
: portableUpdateManifestNames(platform).map((name) => join(directory, name));
|
||||
const publishedAt = new Date().toISOString();
|
||||
let manifest;
|
||||
for (const output of outputs) {
|
||||
manifest = await generatePortableUpdateManifest({
|
||||
@@ -134,9 +125,12 @@ async function main() {
|
||||
repository: args.get('--repository'),
|
||||
gitcodeRepository: args.get('--gitcode-repository'),
|
||||
tag: args.get('--tag'),
|
||||
publishedAt,
|
||||
});
|
||||
}
|
||||
console.log(`Generated ${outputs.map(basename).join(', ')} for v${manifest.version}`);
|
||||
console.log(
|
||||
`Generated ${outputs.map((output) => basename(output)).join(', ')} for v${manifest.version}`,
|
||||
);
|
||||
}
|
||||
|
||||
const entryPoint = process.argv[1] ? pathToFileURL(resolve(process.argv[1])).href : '';
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import {
|
||||
generatePortableUpdateManifest,
|
||||
@@ -20,6 +22,42 @@ test('macOS uses a new primary channel and keeps the legacy manifest alias', ()
|
||||
expect(portableUpdateManifestNames('linux')).toEqual(['portable-update-linux.json']);
|
||||
});
|
||||
|
||||
test('macOS manifest CLI publishes both update channels', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'easycli-darwin-manifest-cli-'));
|
||||
try {
|
||||
for (const arch of ['amd64', 'aarch64']) {
|
||||
await writeFile(
|
||||
join(root, `EasyCLIProxyAPI-v1.2.3-Darwin-${arch}.dmg`),
|
||||
`darwin ${arch} release`,
|
||||
);
|
||||
}
|
||||
const script = fileURLToPath(new URL('../scripts/manifest.mjs', import.meta.url));
|
||||
const result = spawnSync('node', [
|
||||
script,
|
||||
'--directory',
|
||||
root,
|
||||
'--platform',
|
||||
'darwin',
|
||||
'--repository',
|
||||
'router-for-me/EasyCLIProxyAPI',
|
||||
'--tag',
|
||||
'v1.2.3',
|
||||
], { encoding: 'utf8' });
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stderr).toBe('');
|
||||
const primary = JSON.parse(
|
||||
await readFile(join(root, 'portable-update-darwin-v2.json'), 'utf8'),
|
||||
);
|
||||
const legacy = JSON.parse(
|
||||
await readFile(join(root, 'portable-update-darwin.json'), 'utf8'),
|
||||
);
|
||||
expect(legacy).toEqual(primary);
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
describe('Windows 便携更新清单', () => {
|
||||
test('URL、大小和哈希与实际上传资产一致', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'easycli-manifest-test-'));
|
||||
@@ -29,10 +67,6 @@ describe('Windows 便携更新清单', () => {
|
||||
aarch64: Buffer.from('aarch64 full portable package'),
|
||||
};
|
||||
for (const [arch, contents] of Object.entries(payloads)) {
|
||||
await writeFile(
|
||||
join(root, `EasyCLIProxyAPI-update-v1.2.3-Windows-${arch}.zip`),
|
||||
Buffer.from(`${arch} legacy update package`),
|
||||
);
|
||||
await writeFile(
|
||||
join(root, `EasyCLIProxyAPI-v1.2.3-Windows-${arch}.zip`),
|
||||
contents,
|
||||
@@ -54,21 +88,15 @@ describe('Windows 便携更新清单', () => {
|
||||
for (const arch of ['amd64', 'aarch64'] as const) {
|
||||
const asset = manifest.assets[`windows-${arch}`];
|
||||
expect(asset.url).toBe(
|
||||
`https://github.com/router-for-me/EasyCLIProxyAPI/releases/download/v1.2.3/EasyCLIProxyAPI-update-v1.2.3-Windows-${arch}.zip`,
|
||||
);
|
||||
expect(asset.fallbackUrls).toEqual([
|
||||
`https://api.gitcode.com/api/v5/repos/mirror-owner/EasyCLIProxyAPI/releases/v1.2.3/attach_files/EasyCLIProxyAPI-update-v1.2.3-Windows-${arch}.zip/download`,
|
||||
]);
|
||||
const fullAsset = manifest.fullAssets[`windows-${arch}`];
|
||||
expect(fullAsset.url).toBe(
|
||||
`https://github.com/router-for-me/EasyCLIProxyAPI/releases/download/v1.2.3/EasyCLIProxyAPI-v1.2.3-Windows-${arch}.zip`,
|
||||
);
|
||||
expect(fullAsset.fallbackUrls).toEqual([
|
||||
expect(asset.fallbackUrls).toEqual([
|
||||
`https://api.gitcode.com/api/v5/repos/mirror-owner/EasyCLIProxyAPI/releases/v1.2.3/attach_files/EasyCLIProxyAPI-v1.2.3-Windows-${arch}.zip/download`,
|
||||
]);
|
||||
expect(fullAsset.sizeBytes).toBe(payloads[arch].byteLength);
|
||||
expect(fullAsset.sha256).toBe(createHash('sha256').update(payloads[arch]).digest('hex'));
|
||||
expect(asset.sizeBytes).toBe(payloads[arch].byteLength);
|
||||
expect(asset.sha256).toBe(createHash('sha256').update(payloads[arch]).digest('hex'));
|
||||
}
|
||||
expect(manifest.fullAssets).toBeUndefined();
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
@@ -78,7 +106,7 @@ describe('Windows 便携更新清单', () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'easycli-manifest-missing-'));
|
||||
try {
|
||||
await writeFile(
|
||||
join(root, 'EasyCLIProxyAPI-update-v1.2.3-Windows-amd64.zip'),
|
||||
join(root, 'EasyCLIProxyAPI-v1.2.3-Windows-amd64.zip'),
|
||||
'amd64',
|
||||
);
|
||||
await expect(generatePortableUpdateManifest({
|
||||
@@ -92,43 +120,6 @@ describe('Windows 便携更新清单', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('所有版本同时保留旧版更新包和完整包', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'easycli-manifest-dual-'));
|
||||
try {
|
||||
for (const arch of ['amd64', 'aarch64']) {
|
||||
await writeFile(
|
||||
join(root, `EasyCLIProxyAPI-update-v1.2.3-Windows-${arch}.zip`),
|
||||
`legacy update ${arch}`,
|
||||
);
|
||||
await writeFile(
|
||||
join(root, `EasyCLIProxyAPI-v1.2.3-Windows-${arch}.zip`),
|
||||
`full package ${arch}`,
|
||||
);
|
||||
}
|
||||
const manifest = await generatePortableUpdateManifest({
|
||||
directory: root,
|
||||
output: join(root, 'portable-update-windows.json'),
|
||||
repository: 'router-for-me/EasyCLIProxyAPI',
|
||||
tag: 'v1.2.3',
|
||||
publishedAt: '2026-08-02T00:00:00.000Z',
|
||||
});
|
||||
|
||||
expect(manifest.assets['windows-amd64'].url).toEndWith(
|
||||
'/EasyCLIProxyAPI-update-v1.2.3-Windows-amd64.zip',
|
||||
);
|
||||
expect(manifest.assets['windows-aarch64'].url).toEndWith(
|
||||
'/EasyCLIProxyAPI-update-v1.2.3-Windows-aarch64.zip',
|
||||
);
|
||||
expect(manifest.fullAssets['windows-amd64'].url).toEndWith(
|
||||
'/EasyCLIProxyAPI-v1.2.3-Windows-amd64.zip',
|
||||
);
|
||||
expect(manifest.fullAssets['windows-aarch64'].url).toEndWith(
|
||||
'/EasyCLIProxyAPI-v1.2.3-Windows-aarch64.zip',
|
||||
);
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('跨平台便携更新清单', () => {
|
||||
|
||||
Reference in new issue
Block a user