mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-10-02 12:54:03 +00:00
OpenInstall 0.1: one idempotent bin/install.sh that puts an app into service (#221)
* OpenInstall 0.1: one idempotent bin/install.sh that puts an app into service A repository carries bin/install.sh and, when the defaults are not right, bin/install.conf. Run on a box from a checkout it installs the runtime and a local Postgres or Redis, builds, writes a systemd unit and an nginx site with TLS, restarts and health-checks the app. Phases setup, build, activate and status; exit 0, 1 or 3; never overwrites a file without its marker line; secrets only in STATE_DIR/app.env. sh1pt's deploy-ssh install.sh is the reference script and `sh1pt ship --target deploy-ssh` the reference deployer. Registered in the process family, with a landing page at /openinstall and a contract test holding the page's tables to the spec. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs(openinstall): describe DATABASE_URL without a credentials-shaped URL ThreatCrush flagged the placeholder postgres URL in the worked example. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f204bc266d
commit
e62c546a1e
6 changed files with 577 additions and 2 deletions
63
apps/logicsrc-web/contract/openinstall.contract.test.ts
Normal file
63
apps/logicsrc-web/contract/openinstall.contract.test.ts
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { readDoc } from "../src/lib/docs";
|
||||
import { CONF, EXIT_CODES, PHASES, SETTINGS } from "../src/app/openinstall/data";
|
||||
|
||||
// The landing page restates the spec's tables. These tests keep the two, and
|
||||
// the spec's own worked example, from drifting apart.
|
||||
|
||||
const doc = readDoc("openinstall") ?? "";
|
||||
|
||||
/** The first-column `code` cells of the table under a `## heading`. */
|
||||
function tableKeys(heading: string): string[] {
|
||||
const start = doc.indexOf(`\n## ${heading}\n`);
|
||||
expect(start, `docs/openinstall.md has a ## ${heading} section`).toBeGreaterThan(-1);
|
||||
const next = doc.indexOf("\n## ", start + 1);
|
||||
const section = doc.slice(start, next === -1 ? undefined : next);
|
||||
return section
|
||||
.split("\n")
|
||||
.map((line) => line.match(/^\| `([^`]+)` \|/))
|
||||
.filter((m): m is RegExpMatchArray => m !== null)
|
||||
.map((m) => m[1]);
|
||||
}
|
||||
|
||||
function confKeys(conf: string): string[] {
|
||||
return conf
|
||||
.split("\n")
|
||||
.filter((line) => line && !line.startsWith("#"))
|
||||
.map((line) => line.slice(0, line.indexOf("=")));
|
||||
}
|
||||
|
||||
describe("OpenInstall: the spec and its landing page agree", () => {
|
||||
it("lists the same settings, in the same order", () => {
|
||||
expect(SETTINGS.map(([key]) => key)).toEqual(tableKeys("Settings"));
|
||||
});
|
||||
|
||||
it("lists the same phases and exit codes", () => {
|
||||
expect(PHASES.map(([name]) => name)).toEqual(tableKeys("Phases"));
|
||||
expect(EXIT_CODES.map(([code]) => code)).toEqual(tableKeys("Exit codes"));
|
||||
expect(tableKeys("Exit codes")).toEqual(["0", "1", "3"]);
|
||||
});
|
||||
|
||||
it("uses only documented settings in the worked example, which the page shows verbatim", () => {
|
||||
const documented = new Set(tableKeys("Settings"));
|
||||
const fence = doc.match(/```\n(# bin\/install\.conf[^`]*?)```/);
|
||||
expect(fence, "the worked example's install.conf").not.toBeNull();
|
||||
const example = fence![1].trim();
|
||||
expect(example).toBe(CONF);
|
||||
for (const key of confKeys(example)) expect(documented.has(key), key).toBe(true);
|
||||
});
|
||||
|
||||
it("numbers its conformance rules without gaps", () => {
|
||||
const numbers = [...doc.matchAll(/^### (\d+)\. /gm)].map((m) => Number(m[1]));
|
||||
expect(numbers.length).toBeGreaterThanOrEqual(10);
|
||||
expect(numbers).toEqual(numbers.map((_, i) => i + 1));
|
||||
});
|
||||
|
||||
it("names the ownership marker the reference script writes", () => {
|
||||
expect(doc).toContain("`# managed by bin/install.sh`");
|
||||
});
|
||||
|
||||
it("has no em dashes", () => {
|
||||
expect(doc).not.toContain(String.fromCharCode(0x2014));
|
||||
});
|
||||
});
|
||||
|
|
@ -18,7 +18,8 @@ describe.each([
|
|||
{ slug: "openwall", name: "OpenWall", family: "people" },
|
||||
{ slug: "openobject", name: "OpenObject", family: "catalogs" },
|
||||
{ slug: "openslice", name: "OpenSlice", family: "catalogs" },
|
||||
{ slug: "openstack", name: "OpenStack.md", family: "catalogs" }
|
||||
{ slug: "openstack", name: "OpenStack.md", family: "catalogs" },
|
||||
{ slug: "openinstall", name: "OpenInstall", family: "process" }
|
||||
])("$name public discovery", ({ slug, name, family }) => {
|
||||
it("serves the specification through its family and docs index", () => {
|
||||
expect(familyOfSpec(slug)?.slug).toBe(family);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue