@decohere/ci-tools 0.0.1-dev.4 → 0.0.1-dev.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/audit/summarize.mjs +1 -1
- package/src/bench/bench.mjs +2 -2
- package/src/bench/trace.mjs +1 -1
- package/src/coverage/badge.mjs +2 -2
- package/src/pages/restore-bench-history.mjs +1 -2
- package/src/release/push-prerelease-commit.mjs +57 -15
- package/src/release/sync-dependencies.mjs +1 -1
- package/tests/lib/lcov.test.mjs +7 -7
package/package.json
CHANGED
package/src/audit/summarize.mjs
CHANGED
package/src/bench/bench.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* bench.mjs — run the
|
|
4
|
+
* bench.mjs — run the forge benchmark and compare against the last saved baseline.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
7
|
* node bench.mjs — run and compare, do not save
|
|
@@ -34,7 +34,7 @@ const HISTORY_FILE = join(
|
|
|
34
34
|
"projects/ci-tools/dist/ci/bench/bench-history.json",
|
|
35
35
|
);
|
|
36
36
|
const VITEST_BIN = join(WORKSPACE_ROOT, "node_modules/.bin/vitest");
|
|
37
|
-
const BENCH_FILE = "projects/
|
|
37
|
+
const BENCH_FILE = "projects/terminal/e2e/build.benchmark.bench.ts";
|
|
38
38
|
const CONFIG = join(WORKSPACE_ROOT, "vitest.e2e.config.ts");
|
|
39
39
|
const TRACE_LOG_DIR = join(WORKSPACE_ROOT, ".decohere", "logs");
|
|
40
40
|
const MAX_RUNS = 10;
|
package/src/bench/trace.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { join } from "node:path";
|
|
|
7
7
|
const WORKSPACE_ROOT = process.cwd();
|
|
8
8
|
const VITEST_BIN = join(WORKSPACE_ROOT, "node_modules/.bin/vitest");
|
|
9
9
|
const CONFIG = join(WORKSPACE_ROOT, "vitest.e2e.config.ts");
|
|
10
|
-
const TRACE_SPEC = "projects/
|
|
10
|
+
const TRACE_SPEC = "projects/terminal/e2e/build.trace.e2e.spec.ts";
|
|
11
11
|
const TRACE_LOG_DIR = join(WORKSPACE_ROOT, ".decohere", "logs");
|
|
12
12
|
|
|
13
13
|
function truthy(value, defaultValue = false) {
|
package/src/coverage/badge.mjs
CHANGED
|
@@ -10,8 +10,8 @@ import path from "node:path";
|
|
|
10
10
|
import { parseLcovByProject } from "../lib/lcov.mjs";
|
|
11
11
|
import { createBadgeSvg, getColor } from "../lib/svg.mjs";
|
|
12
12
|
|
|
13
|
-
const summaryPath = path.resolve("
|
|
14
|
-
const lcovPath = path.resolve("
|
|
13
|
+
const summaryPath = path.resolve("dist/coverage/coverage-summary.json");
|
|
14
|
+
const lcovPath = path.resolve("dist/coverage/lcov.info");
|
|
15
15
|
const badgesDirectory = path.resolve("projects/ci-tools/dist/ci/badges");
|
|
16
16
|
const globalBadgePath = path.join(badgesDirectory, "coverage.svg");
|
|
17
17
|
const projectBadgesDirectory = path.join(badgesDirectory, "projects");
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
// Restore benchmark history from Pages, or initialize empty if not found
|
|
4
4
|
|
|
5
5
|
import { execSync } from "node:child_process";
|
|
6
|
-
import {
|
|
6
|
+
import { writeFileSync } from "node:fs";
|
|
7
7
|
import { mkdir } from "node:fs/promises";
|
|
8
|
-
import { dirname } from "node:path";
|
|
9
8
|
|
|
10
9
|
const HISTORY_DIR = "projects/ci-tools/dist/ci/bench";
|
|
11
10
|
const HISTORY_FILE = `${HISTORY_DIR}/bench-history.json`;
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* Validates working tree
|
|
3
|
+
* Publish prerelease version commit via PR
|
|
4
|
+
* Validates working tree, pushes prerelease commit to a release branch,
|
|
5
|
+
* and creates/updates a PR targeting the protected base branch.
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
import { execSync } from "node:child_process";
|
|
8
9
|
|
|
10
|
+
function run(command) {
|
|
11
|
+
return execSync(command, { encoding: "utf8" }).trim();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function runInherit(command, env) {
|
|
15
|
+
execSync(command, { stdio: "inherit", env });
|
|
16
|
+
}
|
|
17
|
+
|
|
9
18
|
try {
|
|
10
19
|
// Check if working tree is dirty
|
|
11
|
-
const status =
|
|
20
|
+
const status = run("git status --porcelain");
|
|
12
21
|
if (status.trim().length > 0) {
|
|
13
22
|
console.error(
|
|
14
23
|
"::error::Working tree is dirty after version/dependency sync; refusing to publish with uncommitted changes.",
|
|
@@ -17,25 +26,58 @@ try {
|
|
|
17
26
|
process.exit(1);
|
|
18
27
|
}
|
|
19
28
|
|
|
20
|
-
// Compare HEAD with origin
|
|
21
|
-
const head =
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
// Compare HEAD with origin and create a PR branch targeting an explicit base branch.
|
|
30
|
+
const head = run("git rev-parse HEAD");
|
|
31
|
+
const sourceBranch = process.env.GITHUB_REF_NAME || run("git rev-parse --abbrev-ref HEAD");
|
|
32
|
+
const channel = process.env.RELEASE_CHANNEL ?? "beta";
|
|
33
|
+
const baseBranch = process.env.RELEASE_BASE_BRANCH || sourceBranch;
|
|
34
|
+
const releasePrBranch = `release/${channel}-${sourceBranch}-to-${baseBranch}`;
|
|
35
|
+
const token = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;
|
|
27
36
|
|
|
28
37
|
try {
|
|
29
|
-
const origin =
|
|
38
|
+
const origin = run(`git rev-parse origin/${sourceBranch}`);
|
|
30
39
|
|
|
31
40
|
if (head !== origin) {
|
|
32
|
-
console.log(
|
|
33
|
-
|
|
41
|
+
console.log(
|
|
42
|
+
`Publishing release commit via PR branch origin/${releasePrBranch} (base: origin/${baseBranch})`,
|
|
43
|
+
);
|
|
44
|
+
runInherit(`git push origin HEAD:${releasePrBranch} --force`, process.env);
|
|
45
|
+
|
|
46
|
+
if (token == null || token.length === 0) {
|
|
47
|
+
throw new Error("Missing GITHUB_TOKEN/GH_TOKEN for PR upsert");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const version = process.env.RELEASE_VERSION ?? "(unknown)";
|
|
51
|
+
const ownerRepo = run("git config --get remote.origin.url")
|
|
52
|
+
.replace(/^.*github\.com[:/]/, "")
|
|
53
|
+
.replace(/\.git$/, "");
|
|
54
|
+
|
|
55
|
+
runInherit(
|
|
56
|
+
[
|
|
57
|
+
"node projects/ci-tools/src/github/create-or-update-pr.mjs",
|
|
58
|
+
`--title "chore(release): ${channel} ${version}"`,
|
|
59
|
+
`--base "${baseBranch}"`,
|
|
60
|
+
`--head "${releasePrBranch}"`,
|
|
61
|
+
`--body "Automated ${channel} version bump and dependency sync.\\n\\n- Channel: ${channel}\\n- Version: ${version}\\n- Source branch: ${sourceBranch}\\n- Base branch: ${baseBranch}\\n- Source commit: ${head}\\n\\nGenerated by pull-request workflow."`,
|
|
62
|
+
`--owner "${ownerRepo.split("/")[0]}"`,
|
|
63
|
+
`--repo "${ownerRepo.split("/")[1]}"`,
|
|
64
|
+
].join(" "),
|
|
65
|
+
{ ...process.env, GITHUB_TOKEN: token },
|
|
66
|
+
);
|
|
34
67
|
} else {
|
|
35
68
|
console.log("No prerelease version commit to push.");
|
|
36
69
|
}
|
|
37
|
-
} catch {
|
|
38
|
-
|
|
70
|
+
} catch (err) {
|
|
71
|
+
const message = err?.message ?? "";
|
|
72
|
+
if (
|
|
73
|
+
message.includes(`origin/${sourceBranch}`) &&
|
|
74
|
+
message.includes("Needed a single revision")
|
|
75
|
+
) {
|
|
76
|
+
console.log(`Remote branch origin/${sourceBranch} not found, skipping push.`);
|
|
77
|
+
} else {
|
|
78
|
+
console.error("::error::Failed to publish release commit via PR branch.");
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
39
81
|
}
|
|
40
82
|
} catch (e) {
|
|
41
83
|
console.error(`Push failed: ${e.message}`);
|
|
@@ -10,7 +10,7 @@ const DRY_RUN = process.env.DRY_RUN === "true";
|
|
|
10
10
|
const TERMINAL_CLI = process.env.TERMINAL_CLI || "dist/debug/projects/terminal/src/cli.js";
|
|
11
11
|
const DECOHERE_CONFIG = process.env.DECOHERE_CONFIG || ".decohere.js";
|
|
12
12
|
|
|
13
|
-
const baseCmd = `node "${TERMINAL_CLI}" --log-level
|
|
13
|
+
const baseCmd = `node "${TERMINAL_CLI}" --log-level info --config-file "${DECOHERE_CONFIG}" forge dependencies --configuration release sync`;
|
|
14
14
|
|
|
15
15
|
try {
|
|
16
16
|
if (DRY_RUN) {
|
package/tests/lib/lcov.test.mjs
CHANGED
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
parseLcovFiles,
|
|
6
6
|
} from "../../src/lib/lcov.mjs";
|
|
7
7
|
|
|
8
|
-
const sampleLcov = `SF:projects/
|
|
8
|
+
const sampleLcov = `SF:projects/forge/src/index.ts
|
|
9
9
|
LF:10
|
|
10
10
|
LH:9
|
|
11
11
|
end_of_record
|
|
12
|
-
SF:projects/
|
|
12
|
+
SF:projects/forge/src/build/index.ts
|
|
13
13
|
LF:20
|
|
14
14
|
LH:15
|
|
15
15
|
end_of_record
|
|
@@ -32,7 +32,7 @@ end_of_record
|
|
|
32
32
|
|
|
33
33
|
describe("getProjectFromPath", () => {
|
|
34
34
|
it("extracts project name from projects/ path", () => {
|
|
35
|
-
expect(getProjectFromPath("projects/
|
|
35
|
+
expect(getProjectFromPath("projects/forge/src/index.ts")).toBe("forge");
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
it("returns undefined for unrecognized paths", () => {
|
|
@@ -43,16 +43,16 @@ describe("getProjectFromPath", () => {
|
|
|
43
43
|
describe("parseLcovByProject", () => {
|
|
44
44
|
it("groups files by project and sums coverage", () => {
|
|
45
45
|
const result = parseLcovByProject(sampleLcov);
|
|
46
|
-
expect(result.get("
|
|
47
|
-
expect(result.get("
|
|
48
|
-
expect(result.get("
|
|
46
|
+
expect(result.get("forge")).toBeDefined();
|
|
47
|
+
expect(result.get("forge").totalLines).toBe(30);
|
|
48
|
+
expect(result.get("forge").coveredLines).toBe(24);
|
|
49
49
|
expect(result.get("nexus")).toBeDefined();
|
|
50
50
|
expect(result.get("nexus").totalLines).toBe(5);
|
|
51
51
|
expect(result.get("nexus").coveredLines).toBe(5);
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
it("ignores records with LF:0", () => {
|
|
55
|
-
const lcov = `SF:projects/
|
|
55
|
+
const lcov = `SF:projects/forge/src/empty.ts\nLF:0\nLH:0\nend_of_record\n`;
|
|
56
56
|
const result = parseLcovByProject(lcov);
|
|
57
57
|
expect(result.size).toBe(0);
|
|
58
58
|
});
|