源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { basename, join } from 'node:path';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
prepareDesktopNightlyRelease,
|
||||
publishDesktopNightlyRelease,
|
||||
} from './desktop-nightly-release.mjs';
|
||||
import {
|
||||
addDesktopNightlyAttestation,
|
||||
desktopNightlyReleaseAssetNames,
|
||||
stageDesktopNightly,
|
||||
} from './desktop-nightly.mjs';
|
||||
import { productReleaseArtifactRecords } from './product-release-artifacts.mjs';
|
||||
|
||||
async function writeUpdateSet(directory, version, platform) {
|
||||
const isMac = platform === 'mac';
|
||||
const artifact = isMac ? `Maka-${version}-mac-arm64.zip` : `Maka-${version}-win-x64.exe`;
|
||||
const metadata = isMac ? 'dev-mac.yml' : 'dev.yml';
|
||||
const bytes = Buffer.from(`${platform} nightly bytes`);
|
||||
const sha512 = createHash('sha512').update(bytes).digest('base64');
|
||||
await Promise.all([
|
||||
writeFile(join(directory, artifact), bytes),
|
||||
writeFile(join(directory, `${artifact}.blockmap`), `${platform} blockmap`),
|
||||
writeFile(
|
||||
join(directory, metadata),
|
||||
`version: ${version}\nfiles:\n - url: ${artifact}\n sha512: ${sha512}\n size: ${bytes.length}\npath: ${artifact}\nsha512: ${sha512}\n`,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
async function stageRelease(root, version) {
|
||||
const input = join(root, 'input');
|
||||
const output = join(root, 'output');
|
||||
await mkdir(input);
|
||||
await Promise.all([
|
||||
writeUpdateSet(input, version, 'mac'),
|
||||
writeUpdateSet(input, version, 'win'),
|
||||
writeFile(join(input, `Maka-${version}-mac-arm64.dmg`), 'dmg'),
|
||||
writeFile(join(input, `Maka-${version}-win-x64.zip`), 'windows zip'),
|
||||
]);
|
||||
await stageDesktopNightly({
|
||||
inputDirectory: input,
|
||||
outputDirectory: output,
|
||||
version,
|
||||
});
|
||||
const bundle = join(root, 'bundle.json');
|
||||
await writeFile(bundle, 'sigstore bundle');
|
||||
await addDesktopNightlyAttestation({ outputDirectory: output, version, bundlePath: bundle });
|
||||
return join(output, 'release');
|
||||
}
|
||||
|
||||
test('Nightly publication verifies the exact draft before one Prerelease/non-Latest mutation', async (t) => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'maka-nightly-release-'));
|
||||
t.after(() => rm(root, { recursive: true, force: true }));
|
||||
const version = '0.2.0-dev.42.20260829';
|
||||
const tag = `v${version}`;
|
||||
const sourceCommit = 'a'.repeat(40);
|
||||
const directory = await stageRelease(root, version);
|
||||
const records = await productReleaseArtifactRecords(
|
||||
directory,
|
||||
desktopNightlyReleaseAssetNames(version),
|
||||
);
|
||||
const calls = [];
|
||||
let remoteAssets = records.slice(0, 1);
|
||||
let draft = true;
|
||||
let tampered = false;
|
||||
const release = () => ({
|
||||
databaseId: 42,
|
||||
tagName: tag,
|
||||
isDraft: draft,
|
||||
isPrerelease: true,
|
||||
assets: remoteAssets.map((record, index) =>
|
||||
tampered && index === 0 ? { ...record, digest: `sha256:${'0'.repeat(64)}` } : record,
|
||||
),
|
||||
});
|
||||
const run = async (command, args) => {
|
||||
calls.push([command, args]);
|
||||
if (command === 'git') return { stdout: `${sourceCommit}\trefs/tags/${tag}\n` };
|
||||
if (command === 'gh' && args[0] === 'release' && args[1] === 'upload') {
|
||||
remoteAssets = records;
|
||||
return { stdout: '' };
|
||||
}
|
||||
if (command === 'gh' && args[0] === 'release' && args[1] === 'edit') return { stdout: '' };
|
||||
if (command === 'gh' && args[0] === 'release' && args[1] === 'view') {
|
||||
return { stdout: JSON.stringify(release()) };
|
||||
}
|
||||
if (command === 'gh' && args.includes('PATCH')) {
|
||||
draft = false;
|
||||
return {
|
||||
stdout: JSON.stringify({
|
||||
id: 42,
|
||||
tag_name: tag,
|
||||
draft: false,
|
||||
prerelease: true,
|
||||
assets: records,
|
||||
}),
|
||||
};
|
||||
}
|
||||
throw new Error(`unexpected command: ${command} ${args.join(' ')}`);
|
||||
};
|
||||
|
||||
await prepareDesktopNightlyRelease({
|
||||
artifactDirectory: directory,
|
||||
repository: 'apache/maka',
|
||||
run,
|
||||
sourceCommit,
|
||||
version,
|
||||
});
|
||||
tampered = true;
|
||||
await assert.rejects(
|
||||
publishDesktopNightlyRelease({
|
||||
artifactDirectory: directory,
|
||||
repository: 'apache/maka',
|
||||
run,
|
||||
sourceCommit,
|
||||
version,
|
||||
}),
|
||||
/assets do not match local bytes/u,
|
||||
);
|
||||
assert.equal(draft, true);
|
||||
assert.equal(
|
||||
calls.some(([, args]) => args.includes('PATCH')),
|
||||
false,
|
||||
);
|
||||
tampered = false;
|
||||
await publishDesktopNightlyRelease({
|
||||
artifactDirectory: directory,
|
||||
repository: 'apache/maka',
|
||||
run,
|
||||
sourceCommit,
|
||||
version,
|
||||
});
|
||||
|
||||
const upload = calls.find(([, args]) => args[0] === 'release' && args[1] === 'upload');
|
||||
const patchCall = calls.find(([, args]) => args.includes('PATCH'));
|
||||
assert.ok(upload);
|
||||
assert.deepEqual(
|
||||
upload[1].slice(3, upload[1].indexOf('--repo')).map((path) => basename(path)),
|
||||
records.slice(1).map(({ name }) => name),
|
||||
);
|
||||
assert.ok(patchCall);
|
||||
assert.ok(calls.indexOf(upload) < calls.indexOf(patchCall));
|
||||
assert.ok(patchCall[1].includes('draft=false'));
|
||||
assert.ok(patchCall[1].includes('prerelease=true'));
|
||||
assert.ok(patchCall[1].includes('make_latest=false'));
|
||||
const edit = calls.find(([, args]) => args[0] === 'release' && args[1] === 'edit');
|
||||
assert.match(edit[1].at(-1), /Developer Snapshot/u);
|
||||
assert.match(edit[1].at(-1), /not an Apache Release/u);
|
||||
assert.match(edit[1].at(-1), /DISCLAIMER-WIP/u);
|
||||
assert.match(edit[1].at(-1), /Apache License 2\.0/u);
|
||||
});
|
||||
|
||||
test('a missing Nightly release is created only as a draft prerelease with Latest disabled', async (t) => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'maka-nightly-create-'));
|
||||
t.after(() => rm(root, { recursive: true, force: true }));
|
||||
const version = '0.2.0-dev.42.20260829';
|
||||
const tag = `v${version}`;
|
||||
const sourceCommit = 'a'.repeat(40);
|
||||
const directory = await stageRelease(root, version);
|
||||
const records = await productReleaseArtifactRecords(
|
||||
directory,
|
||||
desktopNightlyReleaseAssetNames(version),
|
||||
);
|
||||
const calls = [];
|
||||
let created = false;
|
||||
let uploaded = false;
|
||||
const run = async (command, args) => {
|
||||
calls.push([command, args]);
|
||||
if (command === 'git') return { stdout: `${sourceCommit}\trefs/tags/${tag}\n` };
|
||||
if (command === 'gh' && args[0] === 'release' && args[1] === 'view') {
|
||||
if (!created) {
|
||||
const error = new Error('release not found');
|
||||
error.stderr = 'release not found';
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
stdout: JSON.stringify({
|
||||
databaseId: 42,
|
||||
tagName: tag,
|
||||
isDraft: true,
|
||||
isPrerelease: true,
|
||||
assets: uploaded ? records : [],
|
||||
}),
|
||||
};
|
||||
}
|
||||
if (command === 'gh' && args[0] === 'release' && args[1] === 'create') {
|
||||
created = true;
|
||||
return { stdout: '' };
|
||||
}
|
||||
if (command === 'gh' && args[0] === 'release' && args[1] === 'edit') return { stdout: '' };
|
||||
if (command === 'gh' && args[0] === 'release' && args[1] === 'upload') {
|
||||
uploaded = true;
|
||||
return { stdout: '' };
|
||||
}
|
||||
throw new Error(`unexpected command: ${command} ${args.join(' ')}`);
|
||||
};
|
||||
|
||||
await prepareDesktopNightlyRelease({
|
||||
artifactDirectory: directory,
|
||||
repository: 'apache/maka',
|
||||
run,
|
||||
sourceCommit,
|
||||
version,
|
||||
});
|
||||
|
||||
const create = calls.find(([, args]) => args[0] === 'release' && args[1] === 'create');
|
||||
for (const flag of ['--draft', '--verify-tag', '--prerelease', '--latest=false']) {
|
||||
assert.ok(create[1].includes(flag), flag);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user