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
19 changes: 19 additions & 0 deletions .changeset/major-runtime-repository-checkout.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions .github/workflows/smoke-create-cross-repo-pr.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions .github/workflows/smoke-update-cross-repo-pr.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 23 additions & 6 deletions actions/setup/js/build_checkout_manifest.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,21 @@ function readManifestEntriesFromEnv() {
token: process.env[`GH_AW_CHECKOUT_TOKEN_${i}`] || "",
});
}
const dynamicIndexes = process.env.GH_AW_DYNAMIC_CHECKOUT_INDEXES;
if (dynamicIndexes) {
const runnerTemp = process.env.RUNNER_TEMP;
if (!runnerTemp) throw new Error("RUNNER_TEMP is required to read runtime checkout plans");
for (const index of dynamicIndexes.split(",")) {
if (!/^\d+$/.test(index)) throw new Error("Runtime checkout manifest index must be a non-negative integer");
const planPath = path.join(runnerTemp, "gh-aw", "dynamic-checkouts", `${index}.json`);
try {
const plan = parseManifestEntries(fs.readFileSync(planPath, "utf8"));
entries.push(...plan);
} catch (error) {
throw new Error("Unable to read runtime checkouts for the checkout manifest", { cause: error });
}
}
}
return entries;
}

Expand Down Expand Up @@ -136,12 +151,14 @@ function buildCheckoutManifest(entries, options = {}) {
continue;
}
const checkoutPath = String(entry.path || "");
const defaultBranch = resolveDefaultBranch(repository, checkoutPath, {
workspace: options.workspace,
runGit,
runGH,
checkoutToken: entry.token || "",
});
const defaultBranch =
entry.default_branch ||
resolveDefaultBranch(repository, checkoutPath, {
workspace: options.workspace,
runGit,
runGH,
checkoutToken: entry.token || "",
});
manifest[repository.toLowerCase()] = {
repository,
path: checkoutPath,
Expand Down
46 changes: 44 additions & 2 deletions actions/setup/js/build_checkout_manifest.test.cjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { afterEach, describe, expect, it } from "vitest";
import { afterEach, describe, expect, it, vi } from "vitest";
import fs from "fs";
import os from "os";
import path from "path";
import { spawnSync } from "child_process";

import { getSetupTimeoutMs } from "./child_process_timeouts.cjs";
import { buildCheckoutManifest, readManifestEntriesFromEnv, resolveDefaultBranch } from "./build_checkout_manifest.cjs";
import { buildCheckoutManifest, main, readManifestEntriesFromEnv, resolveDefaultBranch } from "./build_checkout_manifest.cjs";
import { _resetCache as resetCheckoutManifestCache, lookupCheckout } from "./checkout_manifest.cjs";
import { findGitDirectories, findRepoCheckout } from "./find_repo_checkout.cjs";

function execGit(args, options = {}) {
const result = spawnSync("git", args, { encoding: "utf8", ...options });
Expand Down Expand Up @@ -157,4 +159,44 @@ describe("build_checkout_manifest.cjs", () => {
const fileContents = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
expect(fileContents).toEqual(manifest);
});

it("resolves runtime-plan checkouts beyond the scan depth through the manifest without scanning", async () => {
const workspace = createTempDir("checkout-manifest-deep-workspace-");
const runnerTemp = createTempDir("checkout-manifest-deep-runner-");
tempDirs.push(workspace, runnerTemp);
const checkoutPath = "one/two/three/four/five/six/targets/owner/repo";
const repoDir = path.join(workspace, checkoutPath);
fs.mkdirSync(path.join(repoDir, ".git"), { recursive: true });
const planDir = path.join(runnerTemp, "gh-aw", "dynamic-checkouts");
fs.mkdirSync(planDir, { recursive: true });
fs.writeFileSync(path.join(planDir, "0.json"), JSON.stringify([{ repository: "Owner/Repo", path: checkoutPath, default_branch: "main" }]));
setEnv("RUNNER_TEMP", runnerTemp);
setEnv("GITHUB_WORKSPACE", workspace);
setEnv("GH_AW_CHECKOUT_MANIFEST", undefined);
setEnv("GH_AW_CHECKOUT_MANIFEST_COUNT", "0");
setEnv("GH_AW_DYNAMIC_CHECKOUT_INDEXES", "0");

expect(findGitDirectories(workspace)).not.toContain(repoDir);
await main();
resetCheckoutManifestCache();
const scan = vi.spyOn(fs, "readdirSync").mockImplementation(() => {
throw new Error("Manifest lookup must not scan the workspace");
});
try {
expect(findRepoCheckout("owner/repo", workspace)).toEqual({
success: true,
path: repoDir,
repoSlug: "owner/repo",
});
expect(lookupCheckout("owner/repo")).toEqual({
repository: "Owner/Repo",
path: checkoutPath,
default_branch: "main",
});
expect(scan).not.toHaveBeenCalled();
} finally {
scan.mockRestore();
resetCheckoutManifestCache();
}
});
});
Loading
Loading