Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions scripts/check-upstream-release.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
import { execFileSync } from "node:child_process";
import { appendFileSync, readFileSync } from "node:fs";

const UPSTREAM_REMOTE = "upstream";
const UPSTREAM_REPO = "badlogic/pi-mono";
const UPSTREAM_REMOTE_URL = `https://github.com/${UPSTREAM_REPO}.git`;
const UPSTREAM_MAIN_REF = "refs/remotes/pi-mono/main";
const PIN_PATH = ".github/upstream.json";

function log(message) {
Expand Down Expand Up @@ -55,7 +56,7 @@ function latestUpstreamReleaseTag() {
log("gh releases/latest unavailable; falling back to remote tags");
}

const lsRemote = run("git", ["ls-remote", "--tags", "--refs", UPSTREAM_REMOTE, "v*"]);
const lsRemote = run("git", ["ls-remote", "--tags", "--refs", UPSTREAM_REMOTE_URL, "v*"]);
const tags = lsRemote
.split("\n")
.filter(Boolean)
Expand All @@ -81,10 +82,16 @@ function compareSemver(a, b) {

function resolveTagSha(tag) {
// Fetch the tag so the commit object exists locally for the ancestry check.
const fetch = tryRun("git", ["fetch", "--quiet", UPSTREAM_REMOTE, "--no-tags", `+refs/tags/${tag}:refs/upstream-tags/${tag}`]);
const fetch = tryRun("git", [
"fetch",
"--quiet",
UPSTREAM_REMOTE_URL,
"--no-tags",
`+refs/tags/${tag}:refs/upstream-tags/${tag}`,
]);
if (!fetch.ok) {
// Fall back to a full tag fetch.
tryRun("git", ["fetch", "--quiet", "--tags", UPSTREAM_REMOTE]);
tryRun("git", ["fetch", "--quiet", "--tags", UPSTREAM_REMOTE_URL]);
}
const peeled = tryRun("git", ["rev-parse", `refs/upstream-tags/${tag}^{commit}`]);
if (peeled.ok && peeled.stdout) {
Expand All @@ -94,7 +101,8 @@ function resolveTagSha(tag) {
}

function resolveUpstreamHeadSha() {
return run("git", ["rev-parse", `${UPSTREAM_REMOTE}/main`]);
run("git", ["fetch", "--quiet", UPSTREAM_REMOTE_URL, `+refs/heads/main:${UPSTREAM_MAIN_REF}`]);
return run("git", ["rev-parse", UPSTREAM_MAIN_REF]);
}

function currentPinTag() {
Expand Down Expand Up @@ -129,7 +137,7 @@ function main() {
const sha = resolveTagSha(tag);
log(`resolved ${tag} -> ${sha}`);
const upstreamHeadSha = resolveUpstreamHeadSha();
log(`resolved ${UPSTREAM_REMOTE}/main -> ${upstreamHeadSha}`);
log(`resolved ${UPSTREAM_REPO} main -> ${upstreamHeadSha}`);

if (force) {
log("--force set; proceeding regardless of merge state");
Expand Down
48 changes: 36 additions & 12 deletions scripts/check-upstream-release.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { after, before, describe, it } from "node:test";

const UPSTREAM_REMOTE_URL = "https://github.com/badlogic/pi-mono.git";
const CONTRIBUTOR_UPSTREAM_URL = "https://github.com/code-yeongyu/senpi.git";
const DETECTOR_UPSTREAM_URL = "https://github.com/badlogic/pi-mono.git";

let addedUpstreamRemote = false;
let originalUpstreamUrl = "";

function git(args) {
return execFileSync("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
Expand All @@ -18,28 +20,39 @@ function tryGit(args) {
}
}

function tryGitCommand(args) {
try {
execFileSync("git", args, { stdio: "ignore" });
return true;
} catch {
return false;
}
}

describe("upstream release detector outputs", () => {
let upstreamAvailable = false;

before(() => {
if (!tryGit(["remote", "get-url", "upstream"])) {
if (!tryGit(["remote", "add", "upstream", UPSTREAM_REMOTE_URL])) return;
originalUpstreamUrl = tryGit(["remote", "get-url", "upstream"]);
if (originalUpstreamUrl) {
if (!tryGitCommand(["remote", "set-url", "upstream", CONTRIBUTOR_UPSTREAM_URL])) return;
} else {
if (!tryGitCommand(["remote", "add", "upstream", CONTRIBUTOR_UPSTREAM_URL])) return;
addedUpstreamRemote = true;
}
// A real fetch of the upstream GitHub repo. On credential-less/offline
// Probe the repository the detector actually reads. On credential-less/offline
// runners (e.g. the release publish job checks out with
// persist-credentials:false) this cannot authenticate — skip rather than
// persist-credentials:false) this can be unreachable — skip rather than
// hard-fail the whole `test:scripts` suite, since this test inherently
// requires the external upstream repo.
if (tryGit(["fetch", "--quiet", "upstream", "+refs/heads/main:refs/remotes/upstream/main"]) === "" && !tryGit(["rev-parse", "upstream/main"])) {
return;
}
upstreamAvailable = tryGit(["rev-parse", "upstream/main"]) !== "";
upstreamAvailable = Boolean(tryGit(["ls-remote", DETECTOR_UPSTREAM_URL, "refs/heads/main"]));
});

after(() => {
if (addedUpstreamRemote) {
git(["remote", "remove", "upstream"]);
} else if (originalUpstreamUrl) {
git(["remote", "set-url", "upstream", originalUpstreamUrl]);
}
});

Expand All @@ -48,16 +61,27 @@ describe("upstream release detector outputs", () => {
t.skip("upstream remote unreachable (offline or no git credentials)");
return;
}
const stdout = execFileSync("node", ["scripts/check-upstream-release.mjs", "--force"], { encoding: "utf8" });
const stdout = execFileSync("node", ["scripts/check-upstream-release.mjs", "--force"], {
encoding: "utf8",
env: { ...process.env, GITHUB_OUTPUT: "" },
});
const output = Object.fromEntries(
stdout
.trim()
.split("\n")
.map((line) => line.split("=", 2)),
);
const upstreamMain = git(["rev-parse", "upstream/main"]);
const upstreamMain = git(["ls-remote", DETECTOR_UPSTREAM_URL, "refs/heads/main"]).split(/\s+/, 1)[0];
const releaseTag = output.tag;
const releaseSha = tryGit(["rev-parse", `refs/upstream-tags/${releaseTag}^{commit}`]) || git(["rev-parse", `${releaseTag}^{commit}`]);
const remoteTagRefs = git([
"ls-remote",
"--tags",
DETECTOR_UPSTREAM_URL,
`refs/tags/${releaseTag}`,
`refs/tags/${releaseTag}^{}`,
]).split("\n");
const peeledTag = remoteTagRefs.find((line) => line.endsWith(`refs/tags/${releaseTag}^{}`));
const releaseSha = (peeledTag ?? remoteTagRefs[0]).split(/\s+/, 1)[0];

assert.equal(output.proceed, "true");
assert.equal(output.sha, releaseSha);
Expand Down