feat(release): add important flag to nightly feed manifests (#2378)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
52d8c6051a
commit
f92ff1111a
|
|
@ -9,6 +9,11 @@ on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "30 13 * * *" # 13:30 UTC = 19:00 IST daily
|
- cron: "30 13 * * *" # 13:30 UTC = 19:00 IST daily
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
important:
|
||||||
|
description: 'Mark this nightly as an important update (escalates the in-app restart prompt). Retro-flag an already-published nightly by re-running only the publish-feed job with this input set.'
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
guard:
|
guard:
|
||||||
|
|
@ -201,6 +206,7 @@ jobs:
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
NIGHTLY_VERSION: ${{ needs.guard.outputs.version }}
|
NIGHTLY_VERSION: ${{ needs.guard.outputs.version }}
|
||||||
|
NIGHTLY_IMPORTANT: ${{ inputs.important || 'false' }}
|
||||||
run: |
|
run: |
|
||||||
# Forge stamps package.json to VERSION without +build metadata and
|
# Forge stamps package.json to VERSION without +build metadata and
|
||||||
# publishes to v<that>. Match it.
|
# publishes to v<that>. Match it.
|
||||||
|
|
@ -208,7 +214,9 @@ jobs:
|
||||||
tag="v$version"
|
tag="v$version"
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
gh release download "$tag" --dir dist --clobber
|
gh release download "$tag" --dir dist --clobber
|
||||||
node scripts/feed.mjs dist "$version" nightly
|
important_flag=""
|
||||||
|
[ "$NIGHTLY_IMPORTANT" = "true" ] && important_flag="--important"
|
||||||
|
node scripts/feed.mjs dist "$version" nightly $important_flag
|
||||||
shopt -s nullglob
|
shopt -s nullglob
|
||||||
assets=(dist/nightly*.yml dist/*.blockmap)
|
assets=(dist/nightly*.yml dist/*.blockmap)
|
||||||
if [ ${#assets[@]} -eq 0 ]; then echo "no feed assets generated" >&2; exit 1; fi
|
if [ ${#assets[@]} -eq 0 ]; then echo "no feed assets generated" >&2; exit 1; fi
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,10 @@
|
||||||
// ESM (mirrors nightly-version.mjs) so CI runs `node scripts/feed.mjs` directly
|
// ESM (mirrors nightly-version.mjs) so CI runs `node scripts/feed.mjs` directly
|
||||||
// and vitest unit-tests the pure functions. The only non-stdlib reach is the
|
// and vitest unit-tests the pure functions. The only non-stdlib reach is the
|
||||||
// blockmap wrapper (Task 1).
|
// blockmap wrapper (Task 1).
|
||||||
|
// Pass --important to emit `important: true` in each generated yml. An
|
||||||
|
// already-published nightly can be retro-flagged by re-running the feed job
|
||||||
|
// with --important set (or editing the yml and running
|
||||||
|
// `gh release upload TAG nightly*.yml --clobber`).
|
||||||
import { readdirSync, writeFileSync } from "node:fs";
|
import { readdirSync, writeFileSync } from "node:fs";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { writeBlockmap } from "./blockmap.mjs";
|
import { writeBlockmap } from "./blockmap.mjs";
|
||||||
|
|
@ -34,7 +38,9 @@ export function feedFilename(channel, platform) {
|
||||||
// buildYml serializes one platform's feed. files is [{ url, sha512, size }];
|
// buildYml serializes one platform's feed. files is [{ url, sha512, size }];
|
||||||
// for mac the arm64 entry comes first. The deprecated top-level path/sha512
|
// for mac the arm64 entry comes first. The deprecated top-level path/sha512
|
||||||
// point at files[0]. blockMapSize is never written (forces sidecar differential).
|
// point at files[0]. blockMapSize is never written (forces sidecar differential).
|
||||||
export function buildYml(version, files, releaseDate) {
|
// When important is true, emits `important: true` after releaseDate so the
|
||||||
|
// in-app update prompt is escalated.
|
||||||
|
export function buildYml(version, files, releaseDate, important = false) {
|
||||||
const lines = [`version: ${version}`, "files:"];
|
const lines = [`version: ${version}`, "files:"];
|
||||||
for (const f of files) {
|
for (const f of files) {
|
||||||
lines.push(` - url: ${f.url}`);
|
lines.push(` - url: ${f.url}`);
|
||||||
|
|
@ -44,12 +50,13 @@ export function buildYml(version, files, releaseDate) {
|
||||||
lines.push(`path: ${files[0].url}`);
|
lines.push(`path: ${files[0].url}`);
|
||||||
lines.push(`sha512: ${files[0].sha512}`);
|
lines.push(`sha512: ${files[0].sha512}`);
|
||||||
lines.push(`releaseDate: '${releaseDate}'`);
|
lines.push(`releaseDate: '${releaseDate}'`);
|
||||||
|
if (important) lines.push("important: true");
|
||||||
return lines.join("\n") + "\n";
|
return lines.join("\n") + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateFeeds writes the yml + sidecar blockmaps for every platform present in
|
// generateFeeds writes the yml + sidecar blockmaps for every platform present in
|
||||||
// dir. version may carry +build metadata (nightly); strip it for the yml.
|
// dir. version may carry +build metadata (nightly); strip it for the yml.
|
||||||
async function generateFeeds(dir, rawVersion, channel, releaseDate) {
|
async function generateFeeds(dir, rawVersion, channel, releaseDate, important = false) {
|
||||||
const version = rawVersion.split("+")[0];
|
const version = rawVersion.split("+")[0];
|
||||||
const sel = selectInstallers(readdirSync(dir), version);
|
const sel = selectInstallers(readdirSync(dir), version);
|
||||||
const groups = [
|
const groups = [
|
||||||
|
|
@ -64,18 +71,19 @@ async function generateFeeds(dir, rawVersion, channel, releaseDate) {
|
||||||
const { sha512, size } = await writeBlockmap(join(dir, name));
|
const { sha512, size } = await writeBlockmap(join(dir, name));
|
||||||
files.push({ url: name, sha512, size });
|
files.push({ url: name, sha512, size });
|
||||||
}
|
}
|
||||||
writeFileSync(join(dir, feedFilename(channel, platform)), buildYml(version, files, releaseDate));
|
writeFileSync(join(dir, feedFilename(channel, platform)), buildYml(version, files, releaseDate, important));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CLI: node scripts/feed.mjs <dir> <version> <channel>
|
// CLI: node scripts/feed.mjs <dir> <version> <channel> [--important]
|
||||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||||
const [, , dir, version, channel] = process.argv;
|
const [, , dir, version, channel] = process.argv;
|
||||||
if (!dir || !version || !channel) {
|
if (!dir || !version || !channel) {
|
||||||
process.stderr.write("usage: node feed.mjs <dir> <version> <channel>\n");
|
process.stderr.write("usage: node feed.mjs <dir> <version> <channel>\n");
|
||||||
process.exit(2);
|
process.exit(2);
|
||||||
}
|
}
|
||||||
generateFeeds(dir, version, channel, new Date().toISOString()).catch((err) => {
|
const important = process.argv.includes("--important");
|
||||||
|
generateFeeds(dir, version, channel, new Date().toISOString(), important).catch((err) => {
|
||||||
process.stderr.write(`${err.stack || err}\n`);
|
process.stderr.write(`${err.stack || err}\n`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -69,4 +69,27 @@ describe("buildYml", () => {
|
||||||
expect(lines[5]).toBe(" - url: Agent.Orchestrator-darwin-x64-0.10.4.zip");
|
expect(lines[5]).toBe(" - url: Agent.Orchestrator-darwin-x64-0.10.4.zip");
|
||||||
expect(yml).toContain("path: Agent.Orchestrator-darwin-arm64-0.10.4.zip");
|
expect(yml).toContain("path: Agent.Orchestrator-darwin-arm64-0.10.4.zip");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("omits important key when flag is false (byte-identical to old output)", () => {
|
||||||
|
const yml = buildYml(
|
||||||
|
"0.10.4",
|
||||||
|
[{ url: "Agent.Orchestrator.Setup.0.10.4.exe", sha512: "AA/BB+cc==", size: 123 }],
|
||||||
|
"2026-06-27T12:00:00.000Z",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
expect(yml).not.toContain("important");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("emits important: true as top-level key when flag is true", () => {
|
||||||
|
const yml = buildYml(
|
||||||
|
"0.10.4",
|
||||||
|
[{ url: "Agent.Orchestrator.Setup.0.10.4.exe", sha512: "AA/BB+cc==", size: 123 }],
|
||||||
|
"2026-06-27T12:00:00.000Z",
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(yml).toContain("important: true\n");
|
||||||
|
// must still have all existing fields
|
||||||
|
expect(yml).toContain("version: 0.10.4");
|
||||||
|
expect(yml).toContain("releaseDate:");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue