Publish curated ForgeFlow source from 63022ccf9a37242d573297c8ce7f15db61acc34e
This commit is contained in:
1 parent
bdfcc1585d
commit
04cbfccbdb
19 files changed
+233
-77
No files matched your search
@@ -48,6 +48,20 @@ test("load creates missing config and recovers malformed JSON", async (t) => {
|
||||
assert.ok((await readdir(directory)).some((name) => name.includes(".corrupt-")));
|
||||
});
|
||||
|
||||
test("legacy ForgeFlow update endpoint migrates to the signed public releases", async (t) => {
|
||||
const { directory, store } = await storeFixture(t);
|
||||
assert.equal(store.data.updates.repo, "ForgeFlow-Public");
|
||||
await writeFile(store.filePath, JSON.stringify({
|
||||
updates: { owner: "Jens", repo: "ForgeFlow", branch: "main", autoCheck: true },
|
||||
}), "utf8");
|
||||
await store.load();
|
||||
assert.equal(store.data.updates.repo, "ForgeFlow-Public");
|
||||
assert.equal(JSON.parse(await readFile(store.filePath, "utf8")).updates.repo, "ForgeFlow-Public");
|
||||
assert.equal(store.migrate({ updates: { owner: "Team", repo: "ForgeFlow" } }).updates.repo, "ForgeFlow");
|
||||
assert.equal(store.migrate({ updates: { owner: "Jens", repo: "CustomUpdates" } }).updates.repo, "CustomUpdates");
|
||||
assert.equal(store.migrate({ updates: { owner: "Jens", repo: "ForgeFlow", branch: "custom" } }).updates.repo, "ForgeFlow");
|
||||
});
|
||||
|
||||
test("server normalization rejects unsafe targets and preserves bounded scan configuration", async (t) => {
|
||||
const { store } = await storeFixture(t);
|
||||
assert.throws(() => store.normalizeServer({ host: "bad host", username: "root" }), /hostname/);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import releasePolicy from '../src/shared/release-policy.cjs';
|
||||
|
||||
const { windowsReleaseAssetNames, existingReleaseState } = releasePolicy;
|
||||
const commit = 'a'.repeat(40);
|
||||
const version = '0.10.16';
|
||||
|
||||
test('a draft for another commit cannot receive replacement release assets', () => {
|
||||
assert.throws(() => existingReleaseState({ target_commitish: 'b'.repeat(40), draft: true }, version, commit), /Bump the version/);
|
||||
});
|
||||
|
||||
test('a matching draft can resume while a complete published release is immutable', () => {
|
||||
assert.equal(existingReleaseState({ target_commitish: commit, draft: true }, version, commit), 'draft');
|
||||
assert.equal(existingReleaseState({ target_commitish: commit, draft: false, assets: windowsReleaseAssetNames(version).map((name) => ({ name })) }, version, commit), 'published');
|
||||
});
|
||||
|
||||
test('a published release missing its signature cannot be treated as complete', () => {
|
||||
const assets = windowsReleaseAssetNames(version).filter((name) => !name.endsWith('.sig')).map((name) => ({ name }));
|
||||
assert.throws(() => existingReleaseState({ target_commitish: commit, draft: false, assets }, version, commit), /release-manifest.json.sig/);
|
||||
});
|
||||
@@ -118,6 +118,33 @@ test("update repository parts reject path injection", async () => {
|
||||
await rm(temp, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
test("packaged updates pin the published release commit despite later source-only changes", async (t) => {
|
||||
const temp = await mkdtemp(path.join(os.tmpdir(), "forgeflow-release-check-"));
|
||||
t.after(() => rm(temp, { recursive: true, force: true }));
|
||||
const releaseCommit = "b".repeat(40);
|
||||
const service = new UpdateService({
|
||||
store: { data: { updates: {} }, save: async () => {} },
|
||||
gitea: {
|
||||
async getLatestRelease(owner, repo) {
|
||||
assert.equal(repo, "ForgeFlow-Public");
|
||||
return { tag_name: "v0.10.16", target_commitish: releaseCommit, draft: false, prerelease: false };
|
||||
},
|
||||
async getBranch() { throw new Error("Packaged updates must not follow mutable main."); },
|
||||
async getRepositoryFile(input) {
|
||||
assert.equal(input.ref, releaseCommit);
|
||||
return { decoded: JSON.stringify({ name: "forgeflow", version: "0.10.16" }) };
|
||||
},
|
||||
},
|
||||
appInfo: { version: "0.10.14", packaged: true },
|
||||
sourcePath: temp,
|
||||
userDataPath: temp,
|
||||
});
|
||||
const result = await service.check();
|
||||
assert.equal(result.available, true);
|
||||
assert.equal(result.remoteSha, releaseCommit);
|
||||
assert.equal(result.releaseTag, "v0.10.16");
|
||||
});
|
||||
|
||||
test("source updater refuses an unsigned archive before launching a helper", async () => {
|
||||
const temp = await mkdtemp(
|
||||
path.join(os.tmpdir(), "forgeflow-update-handshake-"),
|
||||
@@ -734,11 +761,13 @@ test("Windows release pipeline emits signed provenance, manifest and SBOM eviden
|
||||
assert.doesNotMatch(releaseWorkflow, /checkout@v\d|setup-node@v\d/);
|
||||
assert.match(releaseWorkflow, /checkout@[a-f0-9]{40}/);
|
||||
assert.match(releaseWorkflow, /setup-node@[a-f0-9]{40}/);
|
||||
assert.ok(
|
||||
releaseWorkflow.indexOf("Validate version bump and build release artifacts") <
|
||||
releaseWorkflow.indexOf("FORGEFLOW_RELEASE_SIGNING_KEY_PEM"),
|
||||
"signing secrets must not be present during dependency installation and quality checks",
|
||||
);
|
||||
const buildStart = releaseWorkflow.indexOf("- name: Build and validate release artifacts");
|
||||
const signingStart = releaseWorkflow.indexOf("- name: Sign and publish validated artifacts");
|
||||
assert.ok(buildStart >= 0 && signingStart > buildStart);
|
||||
assert.doesNotMatch(releaseWorkflow.slice(buildStart, signingStart), /FORGEFLOW_RELEASE_SIGNING_KEY_PEM/);
|
||||
assert.match(releaseWorkflow, /push:\s*\n\s*branches: \[main\]/);
|
||||
assert.match(releaseWorkflow, /should_release=false/);
|
||||
assert.match(releaseWorkflow, /should_release=true/);
|
||||
assert.match(releaseWorkflow, /finally \{/);
|
||||
assert.match(releaseWorkflow, /Remove-Item -LiteralPath \$privateKeyPath -Force/);
|
||||
const publisher = await readFile(new URL("../scripts/publish-binary-release.cjs", import.meta.url), "utf8");
|
||||
|
||||
Reference in new issue
Block a user