1a88ab3199
The v0.8 milestone: installable on any Debian-based server via docker (Gitea registry image, honest sandbox degrade), deb (amd64+arm64, advisory postinst), and arch-aware binary — with release consistency enforced by pre-commit hooks + Gitea Actions (3-runner fleet on CT 113, machine-made releases with the full 8-asset matrix + registry image), the CLI carrying the full suite (guide, channel-aware doctor), and the admin runbook (deploy/README.md, evidence-backed recipes). ---ci--- phase: 6 milestone: v0.8 status: complete requirements: covered: [REQ-8-001, REQ-8-002, REQ-8-003, REQ-8-004, REQ-8-005] partial: [] ---/ci---
40 lines
1.6 KiB
TypeScript
40 lines
1.6 KiB
TypeScript
// In-container semantics (v0.8 P2, D-066): the docker image embeds the
|
|
// CLI; doctor/verify/guide work in-container, the repo/daemon commands
|
|
// (bootstrap/deploy/dev/stop/log) refuse honestly — the image is
|
|
// immutable, upgrades are `docker pull` + recreate (D-071).
|
|
//
|
|
// Detection: NC_CONTAINER=1 (the image sets it) OR /.dockerenv exists —
|
|
// belt and suspenders. The refusal text names the remediation; it is the
|
|
// single source shared by every refused command (they cannot drift).
|
|
|
|
const REFUSAL = [
|
|
"✗ this command manages a nextcraft clone, but this is the docker image —",
|
|
" the image is immutable (no repo clone, no daemons inside).",
|
|
" hint: upgrades = docker pull + recreate the container (see deploy guide);",
|
|
" in-container: nextcraft doctor / nextcraft verify / nextcraft guide",
|
|
].join("\n");
|
|
|
|
export interface ContainerMode {
|
|
inContainer: boolean;
|
|
}
|
|
|
|
export function detectContainer(
|
|
env: Record<string, string | undefined>,
|
|
): ContainerMode {
|
|
// NC_CONTAINER=1 is the image's definitive marker. /.dockerenv ALONE is
|
|
// NOT a trigger (rc2 finding: CI job containers, dev boxes-in-docker all
|
|
// carry /.dockerenv — the host-path CLI must keep working there).
|
|
const marker = env.NC_CONTAINER === "1";
|
|
return { inContainer: marker };
|
|
}
|
|
|
|
export function refuseIfContainer(
|
|
ctx: { env: Record<string, string | undefined>; exists: (p: string) => boolean; stderr: { write(s: string): void } },
|
|
command: string,
|
|
): boolean {
|
|
const { inContainer } = detectContainer(ctx.env);
|
|
if (!inContainer) return false;
|
|
ctx.stderr.write(`✗ \`${command}\` is unavailable in the docker image\n${REFUSAL}\n`);
|
|
return true;
|
|
}
|