@stll/oxlint-config 0.11.0 → 0.12.0
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/README.md +103 -1
- package/dist/cloud-setup-cli.cjs +63 -0
- package/dist/cloud-setup-cli.cjs.map +1 -0
- package/dist/cloud-setup-cli.d.cts +1 -0
- package/dist/cloud-setup-cli.d.mts +1 -0
- package/dist/cloud-setup-cli.mjs +63 -0
- package/dist/cloud-setup-cli.mjs.map +1 -0
- package/dist/toolchain-check-cli.cjs +3 -2287
- package/dist/toolchain-check-cli.cjs.map +1 -1
- package/dist/toolchain-check-cli.mjs +2 -2262
- package/dist/toolchain-check-cli.mjs.map +1 -1
- package/dist/toolchain-guard.cjs +3302 -0
- package/dist/toolchain-guard.cjs.map +1 -0
- package/dist/toolchain-guard.mjs +3243 -0
- package/dist/toolchain-guard.mjs.map +1 -0
- package/package.json +2 -1
- package/toolchain.json +27 -10
package/README.md
CHANGED
|
@@ -95,6 +95,30 @@ The series policy accepts that patch without weakening release validation.
|
|
|
95
95
|
GitHub JavaScript action host runtimes (`runs.using`) are platform-managed
|
|
96
96
|
and do not select the project toolchain Node version.
|
|
97
97
|
|
|
98
|
+
A published package can test the minimum Node major in its `engines.node`
|
|
99
|
+
support range in one declared workflow job. Add a scoped `engineFloors` entry
|
|
100
|
+
to tracked root `stll-toolchain.json`:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"engineFloors": [
|
|
105
|
+
{
|
|
106
|
+
"package": "packages/library",
|
|
107
|
+
"workflow": ".github/workflows/ci.yml",
|
|
108
|
+
"job": "node-floor"
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
That job may use a literal exact stable patch in setup-node's `node-version`
|
|
115
|
+
whose major matches the range's minimum major and whose release satisfies
|
|
116
|
+
the range (for example, `20.10.0` for `>=20.10.0`). The approved action SHA, version comment and input validation
|
|
117
|
+
also apply. Other jobs and runtime files follow the shared Node series.
|
|
118
|
+
Missing packages or jobs, private packages, absent setup-node selectors and
|
|
119
|
+
floor mismatches fail configuration validation. `optOuts` is optional when
|
|
120
|
+
only `engineFloors` is declared.
|
|
121
|
+
|
|
98
122
|
Run the installed checker from the repository root in CI:
|
|
99
123
|
|
|
100
124
|
```sh
|
|
@@ -120,7 +144,21 @@ rule and a nonempty reason:
|
|
|
120
144
|
}
|
|
121
145
|
```
|
|
122
146
|
|
|
123
|
-
|
|
147
|
+
Workspace TypeScript toolchains combine the root and declared members' devDependencies.
|
|
148
|
+
An included member may declare the exact TypeScript 6 compatibility API across
|
|
149
|
+
dependencies, devDependencies, and peerDependencies when that shared install uses
|
|
150
|
+
the complete split layout. Ambiguous or partial layouts do not grant this allowance.
|
|
151
|
+
Peer ranges declare support: they must include the policy compiler release and the
|
|
152
|
+
compatibility release when the selected layout uses it. Ranges do not contribute
|
|
153
|
+
to the installed compiler inventory.
|
|
154
|
+
|
|
155
|
+
JavaScript update roots come from the repository root, declared workspace membership,
|
|
156
|
+
and directories with their own `bun.lock`, `package-lock.json`, `yarn.lock`, or
|
|
157
|
+
`pnpm-lock.yaml`. Other package manifests do not create update roots; directory names
|
|
158
|
+
have no special meaning. Workspace members share their owner's update entry.
|
|
159
|
+
|
|
160
|
+
`node-engine` and `cloud-setup-drift` are mandatory: opt-outs cannot bypass the
|
|
161
|
+
shared Node support range or generated cloud setup requirements.
|
|
124
162
|
|
|
125
163
|
Opt-outs apply to the entire repository for that rule. Unknown or duplicate
|
|
126
164
|
rules, empty reasons, and malformed configuration fail validation. See the
|
|
@@ -144,3 +182,67 @@ is unnecessary. Their original reference metadata is preserved without
|
|
|
144
182
|
discovering or building referenced projects. Missing, unreadable, and zero-input
|
|
145
183
|
selected configs fail the check. `--help` prints usage and exits successfully;
|
|
146
184
|
unknown arguments fail.
|
|
185
|
+
|
|
186
|
+
## Shared cloud setup
|
|
187
|
+
|
|
188
|
+
Add an explicit declaration to tracked root `stll-toolchain.json`:
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"optOuts": [],
|
|
193
|
+
"cloud": {
|
|
194
|
+
"services": ["postgres", "valkey"],
|
|
195
|
+
"install": "bun install --frozen-lockfile",
|
|
196
|
+
"envFile": ".env.cloud"
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Services are optional within the declared list: use `[]` for runtimes and
|
|
202
|
+
dependencies only. No `cloud` declaration means no cloud setup script.
|
|
203
|
+
`envFile` must be a canonical repository-relative path; its existing parent
|
|
204
|
+
must be safe at runtime. Track an exact stable Node patch in root
|
|
205
|
+
`.node-version` within the shared Node series. Generate and commit the script:
|
|
206
|
+
|
|
207
|
+
```sh
|
|
208
|
+
bunx --no-install stll-cloud-setup
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Generation uses the installed policy's Bun, Node, PostgreSQL, and Valkey pins.
|
|
212
|
+
PostgreSQL uses the PGDG major-version packages; minor updates within the pinned
|
|
213
|
+
major are accepted for the disposable database. Before adding PGDG, provisioning
|
|
214
|
+
reuses an existing source only after verifying its signing-key fingerprint;
|
|
215
|
+
conflicts identify the source file. Existing matching-major binaries are reused. Valkey uses official versioned
|
|
216
|
+
binary DEBs for the declared release and architecture, verifies the policy's
|
|
217
|
+
SHA256 pins, and installs those local files with apt. It does not resolve Valkey
|
|
218
|
+
from a mutable package index.
|
|
219
|
+
The command writes `.agents/cloud-setup.sh` with executable permissions and
|
|
220
|
+
refuses symlink parents or destinations and hard-linked destinations. `stll-toolchain-check` rejects missing,
|
|
221
|
+
drifted, or undeclared tracked scripts with mandatory `cloud-setup-drift`.
|
|
222
|
+
Regenerate after changing the policy or declaration. `stll-cloud-setup --help`
|
|
223
|
+
prints usage without reading repository configuration.
|
|
224
|
+
|
|
225
|
+
| Environment phase | Command |
|
|
226
|
+
| ----------------------- | ------------------------------------- |
|
|
227
|
+
| Networked install | `bash .agents/cloud-setup.sh install` |
|
|
228
|
+
| Offline session startup | `bash .agents/cloud-setup.sh start` |
|
|
229
|
+
|
|
230
|
+
Both commands can be rerun. Missing or unknown subcommands print usage and
|
|
231
|
+
exit 2; there is no implicit lifecycle mode. The script supports Ubuntu 24.04
|
|
232
|
+
with root access or passwordless sudo and fails when required capabilities
|
|
233
|
+
or pinned installations are unavailable. `install` provisions runtimes,
|
|
234
|
+
dependencies, and declared services; `start` restores local services after
|
|
235
|
+
cached environments lose running processes. Services bind only to localhost.
|
|
236
|
+
System provisioning runs as root; frozen dependencies run as the invoking user
|
|
237
|
+
with that user's home and a private, user-owned Bun cache. Package installation
|
|
238
|
+
does not permit automatic downgrades.
|
|
239
|
+
If a newer Valkey package is already installed, provisioning fails rather than
|
|
240
|
+
downgrading it; use an environment compatible with the pinned release.
|
|
241
|
+
The environment file contains `NODE_ENV=test` and declared local connection
|
|
242
|
+
URLs, carries a generated ownership marker, and refuses an unowned file or
|
|
243
|
+
symlink. Configure the repository's test runner to load that file; migration
|
|
244
|
+
commands and repository adoption are separate configuration steps.
|
|
245
|
+
|
|
246
|
+
Environments may cache filesystems without preserving service processes.
|
|
247
|
+
Run `start` at every session start, including sessions that reuse installed files.
|
|
248
|
+
The supported OS, privileges, and pinned runtime requirements are checked explicitly.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const require_toolchain_guard = require("./toolchain-guard.cjs");
|
|
4
|
+
let node_fs = require("node:fs");
|
|
5
|
+
let node_path = require("node:path");
|
|
6
|
+
let node_fs_promises = require("node:fs/promises");
|
|
7
|
+
//#region src/cloud-setup-cli.ts
|
|
8
|
+
const usage = "Usage: stll-cloud-setup (run from repository root)\n stll-cloud-setup --help";
|
|
9
|
+
const optionalStat = async (path) => {
|
|
10
|
+
try {
|
|
11
|
+
return await (0, node_fs_promises.lstat)(path);
|
|
12
|
+
} catch (error) {
|
|
13
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const main = async () => {
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
if (args.length === 1 && args.at(0) === "--help") {
|
|
20
|
+
process.stdout.write(`${usage}\n`);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (args.length !== 0) throw new Error(usage);
|
|
24
|
+
const root = await (0, node_fs_promises.realpath)(process.cwd());
|
|
25
|
+
const { files, diagnostics } = require_toolchain_guard.readToolchainInputs(root);
|
|
26
|
+
if (diagnostics.length > 0) throw new Error(diagnostics.map(({ path, message }) => `${path}: ${message}`).join("\n"));
|
|
27
|
+
const text = files["stll-toolchain.json"];
|
|
28
|
+
if (text === void 0) throw new Error("requires tracked stll-toolchain.json");
|
|
29
|
+
const { cloud } = require_toolchain_guard.parseToolchainConfiguration(JSON.parse(text));
|
|
30
|
+
if (cloud === void 0) throw new Error("requires an explicit cloud declaration");
|
|
31
|
+
const nodeVersion = files[".node-version"];
|
|
32
|
+
if (nodeVersion === void 0) throw new Error("requires tracked root .node-version");
|
|
33
|
+
const script = require_toolchain_guard.generateCloudSetup({
|
|
34
|
+
policy: require_toolchain_guard.parseToolchainPolicy(JSON.parse(await (0, node_fs_promises.readFile)(new URL("../toolchain.json", require("url").pathToFileURL(__filename).href), "utf8"))),
|
|
35
|
+
cloud,
|
|
36
|
+
nodeVersion: nodeVersion.trim()
|
|
37
|
+
});
|
|
38
|
+
const destination = (0, node_path.join)(root, require_toolchain_guard.cloudSetupPath);
|
|
39
|
+
const parent = (0, node_path.dirname)(destination);
|
|
40
|
+
const parentStat = await optionalStat(parent);
|
|
41
|
+
if (parentStat === void 0) await (0, node_fs_promises.mkdir)(parent);
|
|
42
|
+
else if (parentStat.isSymbolicLink() || !parentStat.isDirectory()) throw new Error("cloud setup parent must be a real repository directory");
|
|
43
|
+
const destinationStat = await optionalStat(destination);
|
|
44
|
+
if (destinationStat !== void 0 && (destinationStat.isSymbolicLink() || !destinationStat.isFile() || destinationStat.nlink !== 1)) throw new Error("cloud setup destination must be a single-link regular repository file");
|
|
45
|
+
const output = await (0, node_fs_promises.open)(destination, node_fs.constants.O_WRONLY | node_fs.constants.O_CREAT | node_fs.constants.O_NOFOLLOW, 493);
|
|
46
|
+
try {
|
|
47
|
+
const opened = await output.stat();
|
|
48
|
+
if (!opened.isFile() || opened.nlink !== 1) throw new Error("cloud setup destination must be a single-link regular repository file");
|
|
49
|
+
await output.truncate(0);
|
|
50
|
+
await output.writeFile(script, "utf8");
|
|
51
|
+
await output.chmod(493);
|
|
52
|
+
} finally {
|
|
53
|
+
await output.close();
|
|
54
|
+
}
|
|
55
|
+
process.stdout.write(`Generated ${require_toolchain_guard.cloudSetupPath}\n`);
|
|
56
|
+
};
|
|
57
|
+
main().catch((error) => {
|
|
58
|
+
process.stderr.write(`cloud-setup: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
59
|
+
process.exitCode = 1;
|
|
60
|
+
});
|
|
61
|
+
//#endregion
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=cloud-setup-cli.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-setup-cli.cjs","names":["readToolchainInputs","parseToolchainConfiguration","generateCloudSetup","parseToolchainPolicy","cloudSetupPath","constants"],"sources":["../src/cloud-setup-cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { constants } from \"node:fs\";\nimport { lstat, mkdir, open, readFile, realpath } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { cloudSetupPath, generateCloudSetup } from \"./cloud-setup\";\nimport {\n parseToolchainConfiguration,\n readToolchainInputs,\n} from \"./toolchain-guard\";\nimport { parseToolchainPolicy } from \"./toolchain-schema\";\n\nconst usage =\n \"Usage: stll-cloud-setup (run from repository root)\\n stll-cloud-setup --help\";\nconst optionalStat = async (path: string) => {\n try {\n return await lstat(path);\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\")\n return undefined;\n throw error;\n }\n};\n\nconst main = async () => {\n const args = process.argv.slice(2);\n if (args.length === 1 && args.at(0) === \"--help\") {\n process.stdout.write(`${usage}\\n`);\n return;\n }\n if (args.length !== 0) throw new Error(usage);\n const root = await realpath(process.cwd());\n const { files, diagnostics } = readToolchainInputs(root);\n if (diagnostics.length > 0)\n throw new Error(\n diagnostics.map(({ path, message }) => `${path}: ${message}`).join(\"\\n\"),\n );\n const text = files[\"stll-toolchain.json\"];\n if (text === undefined)\n throw new Error(\"requires tracked stll-toolchain.json\");\n const { cloud } = parseToolchainConfiguration(JSON.parse(text));\n if (cloud === undefined)\n throw new Error(\"requires an explicit cloud declaration\");\n const nodeVersion = files[\".node-version\"];\n if (nodeVersion === undefined)\n throw new Error(\"requires tracked root .node-version\");\n const policy = parseToolchainPolicy(\n JSON.parse(\n await readFile(new URL(\"../toolchain.json\", import.meta.url), \"utf8\"),\n ),\n );\n const script = generateCloudSetup({\n policy,\n cloud,\n nodeVersion: nodeVersion.trim(),\n });\n const destination = join(root, cloudSetupPath);\n const parent = dirname(destination);\n const parentStat = await optionalStat(parent);\n if (parentStat === undefined) await mkdir(parent);\n else if (parentStat.isSymbolicLink() || !parentStat.isDirectory())\n throw new Error(\"cloud setup parent must be a real repository directory\");\n const destinationStat = await optionalStat(destination);\n if (\n destinationStat !== undefined &&\n (destinationStat.isSymbolicLink() ||\n !destinationStat.isFile() ||\n destinationStat.nlink !== 1)\n )\n throw new Error(\n \"cloud setup destination must be a single-link regular repository file\",\n );\n const output = await open(\n destination,\n constants.O_WRONLY | constants.O_CREAT | constants.O_NOFOLLOW,\n 0o755,\n );\n try {\n const opened = await output.stat();\n if (!opened.isFile() || opened.nlink !== 1)\n throw new Error(\n \"cloud setup destination must be a single-link regular repository file\",\n );\n await output.truncate(0);\n await output.writeFile(script, \"utf8\");\n await output.chmod(0o755);\n } finally {\n await output.close();\n }\n process.stdout.write(`Generated ${cloudSetupPath}\\n`);\n};\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `cloud-setup: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;AAYA,MAAM,QACJ;AACF,MAAM,eAAe,OAAO,SAAiB;CAC3C,IAAI;EACF,OAAO,OAAA,GAAA,iBAAA,MAAA,CAAY,IAAI;CACzB,SAAS,OAAO;EACd,IAAI,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,UAC9D,OAAO,KAAA;EACT,MAAM;CACR;AACF;AAEA,MAAM,OAAO,YAAY;CACvB,MAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;CACjC,IAAI,KAAK,WAAW,KAAK,KAAK,GAAG,CAAC,MAAM,UAAU;EAChD,QAAQ,OAAO,MAAM,GAAG,MAAM,GAAG;EACjC;CACF;CACA,IAAI,KAAK,WAAW,GAAG,MAAM,IAAI,MAAM,KAAK;CAC5C,MAAM,OAAO,OAAA,GAAA,iBAAA,SAAA,CAAe,QAAQ,IAAI,CAAC;CACzC,MAAM,EAAE,OAAO,gBAAgBA,wBAAAA,oBAAoB,IAAI;CACvD,IAAI,YAAY,SAAS,GACvB,MAAM,IAAI,MACR,YAAY,KAAK,EAAE,MAAM,cAAc,GAAG,KAAK,IAAI,SAAS,CAAC,CAAC,KAAK,IAAI,CACzE;CACF,MAAM,OAAO,MAAM;CACnB,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,sCAAsC;CACxD,MAAM,EAAE,UAAUC,wBAAAA,4BAA4B,KAAK,MAAM,IAAI,CAAC;CAC9D,IAAI,UAAU,KAAA,GACZ,MAAM,IAAI,MAAM,wCAAwC;CAC1D,MAAM,cAAc,MAAM;CAC1B,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,MAAM,qCAAqC;CAMvD,MAAM,SAASC,wBAAAA,mBAAmB;EAChC,QANaC,wBAAAA,qBACb,KAAK,MACH,OAAA,GAAA,iBAAA,SAAA,CAAe,IAAI,IAAI,qBAAA,QAAA,KAAA,CAAA,CAAA,cAAA,UAAA,CAAA,CAAA,IAAoC,GAAG,MAAM,CACtE,CAGK;EACL;EACA,aAAa,YAAY,KAAK;CAChC,CAAC;CACD,MAAM,eAAA,GAAA,UAAA,KAAA,CAAmB,MAAMC,wBAAAA,cAAc;CAC7C,MAAM,UAAA,GAAA,UAAA,QAAA,CAAiB,WAAW;CAClC,MAAM,aAAa,MAAM,aAAa,MAAM;CAC5C,IAAI,eAAe,KAAA,GAAW,OAAA,GAAA,iBAAA,MAAA,CAAY,MAAM;MAC3C,IAAI,WAAW,eAAe,KAAK,CAAC,WAAW,YAAY,GAC9D,MAAM,IAAI,MAAM,wDAAwD;CAC1E,MAAM,kBAAkB,MAAM,aAAa,WAAW;CACtD,IACE,oBAAoB,KAAA,MACnB,gBAAgB,eAAe,KAC9B,CAAC,gBAAgB,OAAO,KACxB,gBAAgB,UAAU,IAE5B,MAAM,IAAI,MACR,uEACF;CACF,MAAM,SAAS,OAAA,GAAA,iBAAA,KAAA,CACb,aACAC,QAAAA,UAAU,WAAWA,QAAAA,UAAU,UAAUA,QAAAA,UAAU,YACnD,GACF;CACA,IAAI;EACF,MAAM,SAAS,MAAM,OAAO,KAAK;EACjC,IAAI,CAAC,OAAO,OAAO,KAAK,OAAO,UAAU,GACvC,MAAM,IAAI,MACR,uEACF;EACF,MAAM,OAAO,SAAS,CAAC;EACvB,MAAM,OAAO,UAAU,QAAQ,MAAM;EACrC,MAAM,OAAO,MAAM,GAAK;CAC1B,UAAU;EACR,MAAM,OAAO,MAAM;CACrB;CACA,QAAQ,OAAO,MAAM,aAAaD,wBAAAA,eAAe,GAAG;AACtD;AAEA,KAAK,CAAC,CAAC,OAAO,UAAmB;CAC/B,QAAQ,OAAO,MACb,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GACzE;CACA,QAAQ,WAAW;AACrB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { a as cloudSetupPath, i as parseToolchainPolicy, n as parseToolchainConfiguration, o as generateCloudSetup, r as readToolchainInputs } from "./toolchain-guard.mjs";
|
|
3
|
+
import { constants } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { lstat, mkdir, open, readFile, realpath } from "node:fs/promises";
|
|
6
|
+
//#region src/cloud-setup-cli.ts
|
|
7
|
+
const usage = "Usage: stll-cloud-setup (run from repository root)\n stll-cloud-setup --help";
|
|
8
|
+
const optionalStat = async (path) => {
|
|
9
|
+
try {
|
|
10
|
+
return await lstat(path);
|
|
11
|
+
} catch (error) {
|
|
12
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
|
|
13
|
+
throw error;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const main = async () => {
|
|
17
|
+
const args = process.argv.slice(2);
|
|
18
|
+
if (args.length === 1 && args.at(0) === "--help") {
|
|
19
|
+
process.stdout.write(`${usage}\n`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (args.length !== 0) throw new Error(usage);
|
|
23
|
+
const root = await realpath(process.cwd());
|
|
24
|
+
const { files, diagnostics } = readToolchainInputs(root);
|
|
25
|
+
if (diagnostics.length > 0) throw new Error(diagnostics.map(({ path, message }) => `${path}: ${message}`).join("\n"));
|
|
26
|
+
const text = files["stll-toolchain.json"];
|
|
27
|
+
if (text === void 0) throw new Error("requires tracked stll-toolchain.json");
|
|
28
|
+
const { cloud } = parseToolchainConfiguration(JSON.parse(text));
|
|
29
|
+
if (cloud === void 0) throw new Error("requires an explicit cloud declaration");
|
|
30
|
+
const nodeVersion = files[".node-version"];
|
|
31
|
+
if (nodeVersion === void 0) throw new Error("requires tracked root .node-version");
|
|
32
|
+
const script = generateCloudSetup({
|
|
33
|
+
policy: parseToolchainPolicy(JSON.parse(await readFile(new URL("../toolchain.json", import.meta.url), "utf8"))),
|
|
34
|
+
cloud,
|
|
35
|
+
nodeVersion: nodeVersion.trim()
|
|
36
|
+
});
|
|
37
|
+
const destination = join(root, cloudSetupPath);
|
|
38
|
+
const parent = dirname(destination);
|
|
39
|
+
const parentStat = await optionalStat(parent);
|
|
40
|
+
if (parentStat === void 0) await mkdir(parent);
|
|
41
|
+
else if (parentStat.isSymbolicLink() || !parentStat.isDirectory()) throw new Error("cloud setup parent must be a real repository directory");
|
|
42
|
+
const destinationStat = await optionalStat(destination);
|
|
43
|
+
if (destinationStat !== void 0 && (destinationStat.isSymbolicLink() || !destinationStat.isFile() || destinationStat.nlink !== 1)) throw new Error("cloud setup destination must be a single-link regular repository file");
|
|
44
|
+
const output = await open(destination, constants.O_WRONLY | constants.O_CREAT | constants.O_NOFOLLOW, 493);
|
|
45
|
+
try {
|
|
46
|
+
const opened = await output.stat();
|
|
47
|
+
if (!opened.isFile() || opened.nlink !== 1) throw new Error("cloud setup destination must be a single-link regular repository file");
|
|
48
|
+
await output.truncate(0);
|
|
49
|
+
await output.writeFile(script, "utf8");
|
|
50
|
+
await output.chmod(493);
|
|
51
|
+
} finally {
|
|
52
|
+
await output.close();
|
|
53
|
+
}
|
|
54
|
+
process.stdout.write(`Generated ${cloudSetupPath}\n`);
|
|
55
|
+
};
|
|
56
|
+
main().catch((error) => {
|
|
57
|
+
process.stderr.write(`cloud-setup: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
});
|
|
60
|
+
//#endregion
|
|
61
|
+
export {};
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=cloud-setup-cli.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-setup-cli.mjs","names":[],"sources":["../src/cloud-setup-cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { constants } from \"node:fs\";\nimport { lstat, mkdir, open, readFile, realpath } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { cloudSetupPath, generateCloudSetup } from \"./cloud-setup\";\nimport {\n parseToolchainConfiguration,\n readToolchainInputs,\n} from \"./toolchain-guard\";\nimport { parseToolchainPolicy } from \"./toolchain-schema\";\n\nconst usage =\n \"Usage: stll-cloud-setup (run from repository root)\\n stll-cloud-setup --help\";\nconst optionalStat = async (path: string) => {\n try {\n return await lstat(path);\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\")\n return undefined;\n throw error;\n }\n};\n\nconst main = async () => {\n const args = process.argv.slice(2);\n if (args.length === 1 && args.at(0) === \"--help\") {\n process.stdout.write(`${usage}\\n`);\n return;\n }\n if (args.length !== 0) throw new Error(usage);\n const root = await realpath(process.cwd());\n const { files, diagnostics } = readToolchainInputs(root);\n if (diagnostics.length > 0)\n throw new Error(\n diagnostics.map(({ path, message }) => `${path}: ${message}`).join(\"\\n\"),\n );\n const text = files[\"stll-toolchain.json\"];\n if (text === undefined)\n throw new Error(\"requires tracked stll-toolchain.json\");\n const { cloud } = parseToolchainConfiguration(JSON.parse(text));\n if (cloud === undefined)\n throw new Error(\"requires an explicit cloud declaration\");\n const nodeVersion = files[\".node-version\"];\n if (nodeVersion === undefined)\n throw new Error(\"requires tracked root .node-version\");\n const policy = parseToolchainPolicy(\n JSON.parse(\n await readFile(new URL(\"../toolchain.json\", import.meta.url), \"utf8\"),\n ),\n );\n const script = generateCloudSetup({\n policy,\n cloud,\n nodeVersion: nodeVersion.trim(),\n });\n const destination = join(root, cloudSetupPath);\n const parent = dirname(destination);\n const parentStat = await optionalStat(parent);\n if (parentStat === undefined) await mkdir(parent);\n else if (parentStat.isSymbolicLink() || !parentStat.isDirectory())\n throw new Error(\"cloud setup parent must be a real repository directory\");\n const destinationStat = await optionalStat(destination);\n if (\n destinationStat !== undefined &&\n (destinationStat.isSymbolicLink() ||\n !destinationStat.isFile() ||\n destinationStat.nlink !== 1)\n )\n throw new Error(\n \"cloud setup destination must be a single-link regular repository file\",\n );\n const output = await open(\n destination,\n constants.O_WRONLY | constants.O_CREAT | constants.O_NOFOLLOW,\n 0o755,\n );\n try {\n const opened = await output.stat();\n if (!opened.isFile() || opened.nlink !== 1)\n throw new Error(\n \"cloud setup destination must be a single-link regular repository file\",\n );\n await output.truncate(0);\n await output.writeFile(script, \"utf8\");\n await output.chmod(0o755);\n } finally {\n await output.close();\n }\n process.stdout.write(`Generated ${cloudSetupPath}\\n`);\n};\n\nmain().catch((error: unknown) => {\n process.stderr.write(\n `cloud-setup: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;AAYA,MAAM,QACJ;AACF,MAAM,eAAe,OAAO,SAAiB;CAC3C,IAAI;EACF,OAAO,MAAM,MAAM,IAAI;CACzB,SAAS,OAAO;EACd,IAAI,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,UAC9D,OAAO,KAAA;EACT,MAAM;CACR;AACF;AAEA,MAAM,OAAO,YAAY;CACvB,MAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;CACjC,IAAI,KAAK,WAAW,KAAK,KAAK,GAAG,CAAC,MAAM,UAAU;EAChD,QAAQ,OAAO,MAAM,GAAG,MAAM,GAAG;EACjC;CACF;CACA,IAAI,KAAK,WAAW,GAAG,MAAM,IAAI,MAAM,KAAK;CAC5C,MAAM,OAAO,MAAM,SAAS,QAAQ,IAAI,CAAC;CACzC,MAAM,EAAE,OAAO,gBAAgB,oBAAoB,IAAI;CACvD,IAAI,YAAY,SAAS,GACvB,MAAM,IAAI,MACR,YAAY,KAAK,EAAE,MAAM,cAAc,GAAG,KAAK,IAAI,SAAS,CAAC,CAAC,KAAK,IAAI,CACzE;CACF,MAAM,OAAO,MAAM;CACnB,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,sCAAsC;CACxD,MAAM,EAAE,UAAU,4BAA4B,KAAK,MAAM,IAAI,CAAC;CAC9D,IAAI,UAAU,KAAA,GACZ,MAAM,IAAI,MAAM,wCAAwC;CAC1D,MAAM,cAAc,MAAM;CAC1B,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,MAAM,qCAAqC;CAMvD,MAAM,SAAS,mBAAmB;EAChC,QANa,qBACb,KAAK,MACH,MAAM,SAAS,IAAI,IAAI,qBAAqB,OAAO,KAAK,GAAG,GAAG,MAAM,CACtE,CAGK;EACL;EACA,aAAa,YAAY,KAAK;CAChC,CAAC;CACD,MAAM,cAAc,KAAK,MAAM,cAAc;CAC7C,MAAM,SAAS,QAAQ,WAAW;CAClC,MAAM,aAAa,MAAM,aAAa,MAAM;CAC5C,IAAI,eAAe,KAAA,GAAW,MAAM,MAAM,MAAM;MAC3C,IAAI,WAAW,eAAe,KAAK,CAAC,WAAW,YAAY,GAC9D,MAAM,IAAI,MAAM,wDAAwD;CAC1E,MAAM,kBAAkB,MAAM,aAAa,WAAW;CACtD,IACE,oBAAoB,KAAA,MACnB,gBAAgB,eAAe,KAC9B,CAAC,gBAAgB,OAAO,KACxB,gBAAgB,UAAU,IAE5B,MAAM,IAAI,MACR,uEACF;CACF,MAAM,SAAS,MAAM,KACnB,aACA,UAAU,WAAW,UAAU,UAAU,UAAU,YACnD,GACF;CACA,IAAI;EACF,MAAM,SAAS,MAAM,OAAO,KAAK;EACjC,IAAI,CAAC,OAAO,OAAO,KAAK,OAAO,UAAU,GACvC,MAAM,IAAI,MACR,uEACF;EACF,MAAM,OAAO,SAAS,CAAC;EACvB,MAAM,OAAO,UAAU,QAAQ,MAAM;EACrC,MAAM,OAAO,MAAM,GAAK;CAC1B,UAAU;EACR,MAAM,OAAO,MAAM;CACrB;CACA,QAAQ,OAAO,MAAM,aAAa,eAAe,GAAG;AACtD;AAEA,KAAK,CAAC,CAAC,OAAO,UAAmB;CAC/B,QAAQ,OAAO,MACb,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GACzE;CACA,QAAQ,WAAW;AACrB,CAAC"}
|