feat: cast github-app create/register — run the App Manifest flow instead of transcribing it #124
2 changed files with 66 additions and 0 deletions
20
src/cli.ts
20
src/cli.ts
|
|
@ -1535,6 +1535,26 @@ async function githubAppCommand(rest: string[]): Promise<number> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// `--port` on the create path has the same defect the ids had, and the same
|
||||||
|
// rule applies: `Number("abc")` is NaN, which reaches `server.listen(NaN)` in
|
||||||
|
// github-app.ts and dies as an uncaught ERR_SOCKET_BAD_PORT stack trace —
|
||||||
|
// after `detectOwnerType` and the org-admin preflight have already gone out.
|
||||||
|
// Nothing is lost when it fails (no App and no secret exist yet), so this is
|
||||||
|
// about the command honouring its own stated rule rather than about damage:
|
||||||
|
// reject before any write or network call, and fail with a sentence instead
|
||||||
|
// of a stack trace.
|
||||||
|
//
|
||||||
|
// Range-checked as well as digits-only, because `--port 99999` is accepted by
|
||||||
|
// every check the ids need and still cannot be listened on.
|
||||||
|
if (values.port !== undefined) {
|
||||||
|
const p = Number(values.port);
|
||||||
|
if (!/^\d+$/.test(values.port) || p < 1 || p > 65535) {
|
||||||
|
console.error(
|
||||||
|
`--port must be a port number between 1 and 65535 (got ${JSON.stringify(values.port)})`,
|
||||||
|
);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
const stateDir = stateDirFrom(values.state);
|
const stateDir = stateDirFrom(values.state);
|
||||||
const bindingsPath = join(stateDir, "environments.yaml");
|
const bindingsPath = join(stateDir, "environments.yaml");
|
||||||
const bindings = loadBindings(bindingsPath);
|
const bindings = loadBindings(bindingsPath);
|
||||||
|
|
|
||||||
|
|
@ -341,6 +341,52 @@ describe("cast github-app register", () => {
|
||||||
expect(readdirSync(f.state).sort()).toEqual(before);
|
expect(readdirSync(f.state).sort()).toEqual(before);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// `--port` belongs to the CREATE path, and had the same defect the ids did:
|
||||||
|
// `Number("abc")` is NaN, which reaches server.listen(NaN) and dies as an
|
||||||
|
// uncaught ERR_SOCKET_BAD_PORT stack trace — after detectOwnerType and the
|
||||||
|
// org-admin preflight have already gone out. Nothing is lost when it fails
|
||||||
|
// (no App and no secret exist yet), so this is about the command honouring
|
||||||
|
// its own rule — reject before any write or network call — and failing with
|
||||||
|
// a sentence rather than a stack trace.
|
||||||
|
//
|
||||||
|
// Driven through `create` because that is the path that reads the flag. The
|
||||||
|
// validation sits in the shared preamble, above openCoolify, so the run ends
|
||||||
|
// before the browser flow this command would otherwise need.
|
||||||
|
for (const [what, port] of [
|
||||||
|
["a non-numeric --port", "abc"],
|
||||||
|
["an out-of-range --port", "99999"],
|
||||||
|
["a zero --port", "0"],
|
||||||
|
["a decimal --port", "80.5"],
|
||||||
|
] as const) {
|
||||||
|
it(`refuses ${what} before touching disk or Coolify`, async () => {
|
||||||
|
const stub = await stubCoolify({ repositories: [] });
|
||||||
|
const f = fixture(
|
||||||
|
stub.url,
|
||||||
|
"github_apps:\n heavy-duty/incubator: hdb-coolify-prod",
|
||||||
|
);
|
||||||
|
const before = readdirSync(f.state).sort();
|
||||||
|
|
||||||
|
const r = await run(
|
||||||
|
[
|
||||||
|
"github-app",
|
||||||
|
"create",
|
||||||
|
"heavy-duty/incubator",
|
||||||
|
"--env",
|
||||||
|
"prod",
|
||||||
|
"--state",
|
||||||
|
f.state,
|
||||||
|
"--port",
|
||||||
|
port,
|
||||||
|
],
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
expect(r.code).toBe(2);
|
||||||
|
expect(r.output).toContain("--port must be a port number");
|
||||||
|
expect(stub.hits).toEqual([]);
|
||||||
|
expect(readdirSync(f.state).sort()).toEqual(before);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
it("prints usage for an unknown subcommand", async () => {
|
it("prints usage for an unknown subcommand", async () => {
|
||||||
const r = await run(["github-app", "wat"], null);
|
const r = await run(["github-app", "wat"], null);
|
||||||
expect(r.code).toBe(2);
|
expect(r.code).toBe(2);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue