@beastmode-develeap/beastmode 0.1.149 → 0.1.151
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/dist/index.js +157 -4
- package/dist/index.js.map +1 -1
- package/dist/web/board.html +1 -1
- package/dist/web/build-commit.txt +1 -1
- package/dist/web/build-stamp.txt +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12473,6 +12473,10 @@ async function listRunners(config) {
|
|
|
12473
12473
|
const url = runnersUrl(config.owner, config.repo);
|
|
12474
12474
|
return githubFetch(url, config.token);
|
|
12475
12475
|
}
|
|
12476
|
+
async function deleteRunner(config, runnerId) {
|
|
12477
|
+
const url = `${runnersUrl(config.owner, config.repo)}/${runnerId}`;
|
|
12478
|
+
await githubFetch(url, config.token, "DELETE");
|
|
12479
|
+
}
|
|
12476
12480
|
function resolveGitHubConfig() {
|
|
12477
12481
|
const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
12478
12482
|
if (!token) {
|
|
@@ -12617,6 +12621,20 @@ async function startRunnerContainer(opts) {
|
|
|
12617
12621
|
);
|
|
12618
12622
|
}
|
|
12619
12623
|
}
|
|
12624
|
+
async function removeEnvEntries(keys, envPath = ".env") {
|
|
12625
|
+
let existing = "";
|
|
12626
|
+
try {
|
|
12627
|
+
existing = await fs.readFile(envPath, "utf-8");
|
|
12628
|
+
} catch (err) {
|
|
12629
|
+
if (err.code !== "ENOENT") throw err;
|
|
12630
|
+
return;
|
|
12631
|
+
}
|
|
12632
|
+
const lines = existing.split("\n");
|
|
12633
|
+
const filtered = lines.filter(
|
|
12634
|
+
(line) => !keys.some((key) => line.startsWith(`${key}=`))
|
|
12635
|
+
);
|
|
12636
|
+
await fs.writeFile(envPath, filtered.join("\n"), "utf-8");
|
|
12637
|
+
}
|
|
12620
12638
|
async function writeEnvEntries(entries, envPath = ".env") {
|
|
12621
12639
|
let existing = "";
|
|
12622
12640
|
try {
|
|
@@ -12839,11 +12857,146 @@ async function composeUpRunner() {
|
|
|
12839
12857
|
runnerCommand.command("setup").description("Set up a self-hosted GitHub Actions runner").option("--repo <owner/repo>", "GitHub repo for runner registration").option("--name <name>", "Container + runner name", "beastmode-runner").option("--label <label>", "Additional runner label", "beastmode").option("--dry-run", "Print what would happen without mutating state").option("--native", "Use native install instead of Docker").action(async (opts) => {
|
|
12840
12858
|
await runnerSetupAction(opts);
|
|
12841
12859
|
});
|
|
12842
|
-
|
|
12843
|
-
|
|
12860
|
+
async function runnerStatusAction(opts) {
|
|
12861
|
+
const container = await findContainerByName(opts.name);
|
|
12862
|
+
const containerStatus = container ? container.status : null;
|
|
12863
|
+
let ghRunner = null;
|
|
12864
|
+
let ghError = null;
|
|
12865
|
+
try {
|
|
12866
|
+
const ghConfig = resolveGitHubConfig();
|
|
12867
|
+
const list = await listRunners(ghConfig);
|
|
12868
|
+
ghRunner = list.runners.find((r) => r.name === opts.name) ?? null;
|
|
12869
|
+
} catch (err) {
|
|
12870
|
+
ghError = err?.message ? String(err.message) : String(err);
|
|
12871
|
+
}
|
|
12872
|
+
if (opts.json) {
|
|
12873
|
+
const result = {
|
|
12874
|
+
name: opts.name,
|
|
12875
|
+
container: { found: !!container, status: containerStatus }
|
|
12876
|
+
};
|
|
12877
|
+
if (ghRunner) {
|
|
12878
|
+
result.github = {
|
|
12879
|
+
registered: true,
|
|
12880
|
+
id: ghRunner.id,
|
|
12881
|
+
status: ghRunner.status,
|
|
12882
|
+
busy: ghRunner.busy,
|
|
12883
|
+
os: ghRunner.os,
|
|
12884
|
+
labels: ghRunner.labels.map((l) => l.name)
|
|
12885
|
+
};
|
|
12886
|
+
} else if (ghError) {
|
|
12887
|
+
result.github = { registered: null, error: ghError };
|
|
12888
|
+
} else {
|
|
12889
|
+
result.github = {
|
|
12890
|
+
registered: false,
|
|
12891
|
+
id: null,
|
|
12892
|
+
status: null,
|
|
12893
|
+
busy: null,
|
|
12894
|
+
os: null,
|
|
12895
|
+
labels: []
|
|
12896
|
+
};
|
|
12897
|
+
}
|
|
12898
|
+
console.log(JSON.stringify(result, null, 2));
|
|
12899
|
+
return;
|
|
12900
|
+
}
|
|
12901
|
+
header(`Runner Status: ${opts.name}`);
|
|
12902
|
+
if (container && containerIsHealthy(container)) {
|
|
12903
|
+
success(`Container: ${container.status}`);
|
|
12904
|
+
} else if (container) {
|
|
12905
|
+
error(`Container: ${container.status}`);
|
|
12906
|
+
} else {
|
|
12907
|
+
error("Container: not found");
|
|
12908
|
+
}
|
|
12909
|
+
if (ghRunner) {
|
|
12910
|
+
const busySuffix = ghRunner.busy ? " \u2014 busy" : "";
|
|
12911
|
+
if (ghRunner.status === "online") {
|
|
12912
|
+
success(`GitHub: online (id: ${ghRunner.id})${busySuffix}`);
|
|
12913
|
+
} else {
|
|
12914
|
+
warn(`GitHub: ${ghRunner.status} (id: ${ghRunner.id})${busySuffix}`);
|
|
12915
|
+
}
|
|
12916
|
+
info(`Labels: ${ghRunner.labels.map((l) => l.name).join(", ")}`);
|
|
12917
|
+
info(`OS: ${ghRunner.os}`);
|
|
12918
|
+
info(`Busy: ${ghRunner.busy ? "yes" : "no"}`);
|
|
12919
|
+
} else if (ghError) {
|
|
12920
|
+
warn(`GitHub: could not reach API (${ghError})`);
|
|
12921
|
+
} else {
|
|
12922
|
+
error("GitHub: not registered");
|
|
12923
|
+
}
|
|
12924
|
+
}
|
|
12925
|
+
async function runnerRemoveAction(opts) {
|
|
12926
|
+
header(`Removing runner: ${opts.name}`);
|
|
12927
|
+
let ghDeregistrationFailed = false;
|
|
12928
|
+
let ghWarning = null;
|
|
12929
|
+
try {
|
|
12930
|
+
const ghConfig = resolveGitHubConfig();
|
|
12931
|
+
const list = await listRunners(ghConfig);
|
|
12932
|
+
const runner = list.runners.find((r) => r.name === opts.name);
|
|
12933
|
+
if (!runner) {
|
|
12934
|
+
ghWarning = "not registered (nothing to deregister)";
|
|
12935
|
+
} else {
|
|
12936
|
+
if (runner.busy && !opts.force) {
|
|
12937
|
+
warn(`Runner '${opts.name}' is currently busy (running a job).`);
|
|
12938
|
+
info("Use --force to remove anyway.");
|
|
12939
|
+
return;
|
|
12940
|
+
}
|
|
12941
|
+
try {
|
|
12942
|
+
await deleteRunner(ghConfig, runner.id);
|
|
12943
|
+
success(`Deregistered from GitHub (runner id: ${runner.id})`);
|
|
12944
|
+
} catch (err) {
|
|
12945
|
+
ghDeregistrationFailed = true;
|
|
12946
|
+
ghWarning = `deregistration failed: ${err?.message ?? String(err)}`;
|
|
12947
|
+
}
|
|
12948
|
+
}
|
|
12949
|
+
} catch (err) {
|
|
12950
|
+
ghDeregistrationFailed = true;
|
|
12951
|
+
ghWarning = err?.message ? String(err.message) : String(err);
|
|
12952
|
+
}
|
|
12953
|
+
if (ghWarning) {
|
|
12954
|
+
warn(`GitHub: ${ghWarning}`);
|
|
12955
|
+
}
|
|
12956
|
+
let containerWarning = null;
|
|
12957
|
+
try {
|
|
12958
|
+
const container = await findContainerByName(opts.name);
|
|
12959
|
+
if (!container) {
|
|
12960
|
+
containerWarning = "not found (nothing to remove)";
|
|
12961
|
+
} else {
|
|
12962
|
+
try {
|
|
12963
|
+
await removeContainer(opts.name);
|
|
12964
|
+
success("Container removed");
|
|
12965
|
+
} catch (err) {
|
|
12966
|
+
containerWarning = err?.message ? String(err.message) : String(err);
|
|
12967
|
+
}
|
|
12968
|
+
}
|
|
12969
|
+
} catch (err) {
|
|
12970
|
+
containerWarning = err?.message ? String(err.message) : String(err);
|
|
12971
|
+
}
|
|
12972
|
+
if (containerWarning) {
|
|
12973
|
+
warn(`Container: ${containerWarning}`);
|
|
12974
|
+
}
|
|
12975
|
+
await removeEnvEntries(["RUNNER_REPO_URL", "RUNNER_TOKEN", "RUNNER_NAME"]);
|
|
12976
|
+
success(".env entries cleaned (RUNNER_REPO_URL, RUNNER_TOKEN, RUNNER_NAME)");
|
|
12977
|
+
if (ghDeregistrationFailed) {
|
|
12978
|
+
warn(
|
|
12979
|
+
`Runner '${opts.name}' partially removed (GitHub deregistration failed).`
|
|
12980
|
+
);
|
|
12981
|
+
} else {
|
|
12982
|
+
success(`Runner '${opts.name}' fully removed.`);
|
|
12983
|
+
}
|
|
12984
|
+
}
|
|
12985
|
+
runnerCommand.command("status").description("Show runner status (container + GitHub registration)").option("--name <name>", "Container + runner name", "beastmode-runner").option("--json", "Output machine-readable JSON").action(async (opts) => {
|
|
12986
|
+
try {
|
|
12987
|
+
await runnerStatusAction(opts);
|
|
12988
|
+
} catch (err) {
|
|
12989
|
+
error(err.message);
|
|
12990
|
+
process.exitCode = 1;
|
|
12991
|
+
}
|
|
12844
12992
|
});
|
|
12845
|
-
runnerCommand.command("remove").description("Remove the runner (container + GitHub deregistration)").action(() => {
|
|
12846
|
-
|
|
12993
|
+
runnerCommand.command("remove").description("Remove the runner (container + GitHub deregistration)").option("--name <name>", "Container + runner name", "beastmode-runner").option("--force", "Remove even if the runner is busy").action(async (opts) => {
|
|
12994
|
+
try {
|
|
12995
|
+
await runnerRemoveAction(opts);
|
|
12996
|
+
} catch (err) {
|
|
12997
|
+
error(err.message);
|
|
12998
|
+
process.exitCode = 1;
|
|
12999
|
+
}
|
|
12847
13000
|
});
|
|
12848
13001
|
runnerCommand.command("switch-workflows").description("Switch workflow runs-on to self-hosted runner").option("--project-dir <path>", "Project root directory", process.cwd()).action(async (opts) => {
|
|
12849
13002
|
const projectDir = resolve20(opts.projectDir);
|