mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-14 16:07:52 +00:00
ci: build nightly versions twice a day (#2136)
Adds a nightly channel. Twice a day nightly.yml builds the newest main commit that passed CI and publishes it as a prerelease (OpenIDEA Nightly, ai.l0ng.openidea.nightly, own data and update manifest), or stops when nothing is newer than the last nightly. Platform builds move from release.yml into the reusable release-build.yml so both channels build the same way. Versions are one patch above main with a timestamp suffix; each build gets its own v…-nightly.… tag (remote development downloads the server by version) and the last 14 are kept; a rolling nightly release holds latest.json. No MSI or RPM for nightlies. Nightly builds skip the CNB mirror manifest, and their automatic update prompts (startup and periodic) appear at most once a day; a manual check always answers. Workflow permissions default to contents: read with write granted per job, Apple signing secrets reach only macOS builds, stable release.yml rejects nightly tags, tag pruning only matches -nightly. tags, and nightly runs share one concurrency group.
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
// @vitest-environment node
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { NIGHTLY_ENDPOINT, nightlyVersion, setNightlyIdentity } from "./nightly-prepare.ts";
|
||||
|
||||
describe("nightlyVersion", () => {
|
||||
it("is one patch above the current version, numbered by its stamp", () => {
|
||||
expect(nightlyVersion("0.1.39", "202611081417")).toBe("0.1.40-nightly.202611081417");
|
||||
});
|
||||
|
||||
it.each([undefined, "", "2026", "012611081417", "20261108141a"])("rejects the stamp %s", (stamp) => {
|
||||
expect(() => nightlyVersion("0.1.39", stamp)).toThrow("stamp");
|
||||
});
|
||||
|
||||
it("rejects a current version that is not a release version", () => {
|
||||
expect(() => nightlyVersion("0.1.40-nightly.202611081417", "202611081417")).toThrow("expected a version");
|
||||
});
|
||||
});
|
||||
|
||||
describe("setNightlyIdentity", () => {
|
||||
const conf = JSON.parse(setNightlyIdentity(readFileSync("src-tauri/tauri.conf.json", "utf8")));
|
||||
|
||||
it("is an app of its own that updates from the nightly manifest", () => {
|
||||
expect(conf.productName).toBe("OpenIDEA Nightly");
|
||||
expect(conf.identifier).toBe("ai.l0ng.openidea.nightly");
|
||||
expect(conf.plugins.updater.endpoints).toEqual([NIGHTLY_ENDPOINT]);
|
||||
});
|
||||
|
||||
it("leaves out the bundles that reject prerelease versions", () => {
|
||||
expect(conf.bundle.targets).not.toContain("msi");
|
||||
expect(conf.bundle.targets).not.toContain("rpm");
|
||||
expect(conf.bundle.createUpdaterArtifacts).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Prepares a nightly build's commit: `node scripts/nightly-prepare.ts 202611081417` sets the
|
||||
* version one patch above the current one, with that stamp as its prerelease number, in the
|
||||
* files `release:prepare` covers, and gives tauri.conf.json the nightly app's identity, so it
|
||||
* installs next to the stable app and updates from its own manifest. Prints the version.
|
||||
*/
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { cargoVersion, edit, parseVersion, setCargoVersion, setJsonVersion } from "./release-prepare.ts";
|
||||
|
||||
export const NIGHTLY_ENDPOINT = "https://github.com/l0ng-ai/openidea/releases/download/nightly/latest.json";
|
||||
|
||||
/** `0.1.40-nightly.202611081417` from `0.1.39`: always above the last stable version and the last nightly. */
|
||||
export function nightlyVersion(current: string, stamp: string | undefined): string {
|
||||
if (!stamp || !/^[1-9]\d{11}$/.test(stamp)) throw new Error(`expected a YYYYMMDDHHMM stamp, got ${stamp}`);
|
||||
const [major, minor, patch] = parseVersion(current).split(".").map(Number);
|
||||
return `${major}.${minor}.${patch! + 1}-nightly.${stamp}`;
|
||||
}
|
||||
|
||||
/** MSI and RPM reject prerelease versions, so a nightly build leaves them out of its bundles. */
|
||||
export function setNightlyIdentity(json: string): string {
|
||||
const conf = JSON.parse(json);
|
||||
conf.productName = "OpenIDEA Nightly";
|
||||
conf.identifier = "ai.l0ng.openidea.nightly";
|
||||
conf.bundle.targets = ["app", "dmg", "appimage", "deb", "nsis"];
|
||||
conf.plugins.updater.endpoints = [NIGHTLY_ENDPOINT];
|
||||
return `${JSON.stringify(conf, null, 2)}\n`;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const root = fileURLToPath(new URL("..", import.meta.url));
|
||||
const version = nightlyVersion(cargoVersion(readFileSync(join(root, "Cargo.toml"), "utf8")), process.argv[2]);
|
||||
|
||||
edit(join(root, "Cargo.toml"), (text) => setCargoVersion(text, version));
|
||||
edit(join(root, "package.json"), (text) => setJsonVersion(text, version));
|
||||
edit(join(root, "src-tauri/tauri.conf.json"), (text) => setNightlyIdentity(setJsonVersion(text, version)));
|
||||
const cargo = spawnSync("cargo", ["update", "--workspace"], {
|
||||
cwd: root,
|
||||
stdio: ["ignore", 2, 2],
|
||||
});
|
||||
if (cargo.status !== 0) throw new Error("cargo update --workspace failed");
|
||||
console.log(version);
|
||||
}
|
||||
|
||||
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
||||
try {
|
||||
main();
|
||||
} catch (error) {
|
||||
console.error(`nightly-prepare: ${error instanceof Error ? error.message : String(error)}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ export function syncApiSnapshot(root: string): { from: string; to: string; copie
|
||||
return { from, to, copied: true };
|
||||
}
|
||||
|
||||
function edit(path: string, change: (text: string) => string) {
|
||||
export function edit(path: string, change: (text: string) => string) {
|
||||
const before = readFileSync(path, "utf8");
|
||||
const after = change(before);
|
||||
if (after === before) throw new Error(`${path}: nothing changed`);
|
||||
|
||||
Reference in New Issue
Block a user