@theholocron/cli 5.0.0-alpha.11 → 5.0.0-alpha.12
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 +30 -0
- package/dist/cli.mjs +118 -0
- package/dist/cli.mjs.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -112,6 +112,36 @@ Additional `repo` fields recognised by `holocron setup`:
|
|
|
112
112
|
| `repo.protection` | `"balanced" \| "strict" \| "none"` | Branch-protection preset applied by `holocron setup`. For `"strict"`, the required status checks are derived from the task manifest — see below. |
|
|
113
113
|
| `repo.properties` | `RepoProperties` | Org-level custom property values synced to the GitHub dashboard. |
|
|
114
114
|
|
|
115
|
+
### Custom properties synced to GitHub
|
|
116
|
+
|
|
117
|
+
`holocron setup` and `holocron sync` both call `syncProperties()` with two
|
|
118
|
+
kinds of fields — always one-way (`holocron.config.ts` → resolved →
|
|
119
|
+
properties; properties are never a second editable source of truth):
|
|
120
|
+
|
|
121
|
+
**Manual** — `repo.properties` states these explicitly:
|
|
122
|
+
|
|
123
|
+
| Property | Value |
|
|
124
|
+
| ------------------------ | ---------------------------------------------- |
|
|
125
|
+
| `lifecycle` | `"active" \| "experimental" \| "deprecated"` |
|
|
126
|
+
| `open_source` | `boolean` |
|
|
127
|
+
| `runtime_environment` | `"node" \| "browser" \| "universal" \| "none"` |
|
|
128
|
+
| `uses_external_packages` | `boolean` |
|
|
129
|
+
|
|
130
|
+
**Derived** — computed from resolved config + `package.json`, not a config field:
|
|
131
|
+
|
|
132
|
+
| Property | Value |
|
|
133
|
+
| ------------------------- | --------------------------------------------------------------------------------------- |
|
|
134
|
+
| `monorepo` | `boolean` — whether `pnpm-workspace.yaml` exists |
|
|
135
|
+
| `branch_protection_level` | the active `repo.protection` preset |
|
|
136
|
+
| `holocron_profile` | repo archetype: `library` / `cli` / `plugin` / `template` / `app` / `docs` / `platform` |
|
|
137
|
+
| `holocron_capabilities` | comma-joined provider capability keys actually wired in `providers: {}` |
|
|
138
|
+
| `holocron_stack` | comma-joined detected build/framework tooling (`next`, `vite`, `astro`, `tsdown`, …) |
|
|
139
|
+
| `holocron_compliance` | `"compliant" \| "non-compliant"` against a minimal `source` + `ci` baseline |
|
|
140
|
+
|
|
141
|
+
Field definitions and derivation heuristics:
|
|
142
|
+
`.notes/tech-holocron-platform.spec.md` → "Custom-properties sync — field
|
|
143
|
+
definitions (#677)".
|
|
144
|
+
|
|
115
145
|
### Required status checks (`protection: "strict"`)
|
|
116
146
|
|
|
117
147
|
`holocron setup` builds the branch-protection required-check list from the
|
package/dist/cli.mjs
CHANGED
|
@@ -3796,6 +3796,94 @@ function vaultProviderName(loader) {
|
|
|
3796
3796
|
return loader.get("vault").providerName;
|
|
3797
3797
|
}
|
|
3798
3798
|
//#endregion
|
|
3799
|
+
//#region src/commands/setup/derived-properties.ts
|
|
3800
|
+
/**
|
|
3801
|
+
* Repo archetype, in priority order:
|
|
3802
|
+
* 1. `*-template` repo name → `template` (independent of package shape).
|
|
3803
|
+
* 2. Monorepo whose primary published artifact is a CLI (some workspace
|
|
3804
|
+
* package has `bin`) → `platform` — matches `holocron` itself: the CLI is
|
|
3805
|
+
* the deliverable, the plugin packages are supporting infrastructure for
|
|
3806
|
+
* it, not separate products.
|
|
3807
|
+
* 3. Single-package repo with `package.json#bin` → `cli`.
|
|
3808
|
+
* 4. Docs-only site (astro/starlight present, root package private, no
|
|
3809
|
+
* publishable exports) → `docs`.
|
|
3810
|
+
* 5. Private, non-publishable, non-docs → `app`.
|
|
3811
|
+
* 6. Otherwise, a single publishable package → `library`.
|
|
3812
|
+
*/
|
|
3813
|
+
function deriveProfile(input) {
|
|
3814
|
+
if (/-template$/.test(input.repoName)) return "template";
|
|
3815
|
+
if (input.isMonorepo) {
|
|
3816
|
+
if (input.workspacePackageJsons.some((pkg) => Boolean(pkg.bin))) return "platform";
|
|
3817
|
+
}
|
|
3818
|
+
const pkg = input.rootPackageJson;
|
|
3819
|
+
if (pkg?.bin) return "cli";
|
|
3820
|
+
const deps = {
|
|
3821
|
+
...pkg?.dependencies,
|
|
3822
|
+
...pkg?.devDependencies
|
|
3823
|
+
};
|
|
3824
|
+
const isDocsSite = Boolean(deps["@theholocron/astro-config"] || deps["astro"]);
|
|
3825
|
+
if (pkg?.private && isDocsSite) return "docs";
|
|
3826
|
+
if (pkg?.private) return "app";
|
|
3827
|
+
return "library";
|
|
3828
|
+
}
|
|
3829
|
+
const KNOWN_STACK_DEPS = [
|
|
3830
|
+
"next",
|
|
3831
|
+
"vite",
|
|
3832
|
+
"astro",
|
|
3833
|
+
"tsdown",
|
|
3834
|
+
"rollup",
|
|
3835
|
+
"webpack",
|
|
3836
|
+
"storybook",
|
|
3837
|
+
"vitest",
|
|
3838
|
+
"playwright",
|
|
3839
|
+
"turbo"
|
|
3840
|
+
];
|
|
3841
|
+
/**
|
|
3842
|
+
* Detected build/framework tooling — the literal "does this repo need
|
|
3843
|
+
* next/vite/tsdown" signal `holocron.config.ts` has no field for today
|
|
3844
|
+
* (framework choice isn't a config concept, unlike `providers`/`tasks`).
|
|
3845
|
+
* Curated table, not exhaustive by design — add to `KNOWN_STACK_DEPS` as new
|
|
3846
|
+
* frameworks show up rather than trying to detect anything on npm.
|
|
3847
|
+
*/
|
|
3848
|
+
function deriveStack(pkg) {
|
|
3849
|
+
const deps = {
|
|
3850
|
+
...pkg?.dependencies,
|
|
3851
|
+
...pkg?.devDependencies
|
|
3852
|
+
};
|
|
3853
|
+
return KNOWN_STACK_DEPS.filter((name) => name in deps);
|
|
3854
|
+
}
|
|
3855
|
+
/** The provider capability keys actually wired in `providers: {}` — zero heuristics. */
|
|
3856
|
+
function deriveCapabilities(providers) {
|
|
3857
|
+
return Object.keys(providers ?? {}).sort();
|
|
3858
|
+
}
|
|
3859
|
+
/**
|
|
3860
|
+
* `source` + `ci` are the minimal baseline every repo `holocron setup`
|
|
3861
|
+
* touches wires — every `holocron.config.ts` in this org already satisfies
|
|
3862
|
+
* this, so `non-compliant` here means drift (a provider manually removed
|
|
3863
|
+
* after setup), not an unmet aspirational policy. Deliberately not a richer
|
|
3864
|
+
* per-profile policy yet (e.g. "a `library` must have `deployment`") — that's
|
|
3865
|
+
* a Phase B (GitHub App) refinement once it can post *why* on a check run.
|
|
3866
|
+
*/
|
|
3867
|
+
const REQUIRED_BASELINE = ["source", "ci"];
|
|
3868
|
+
function deriveCompliance(capabilities) {
|
|
3869
|
+
return REQUIRED_BASELINE.every((key) => capabilities.includes(key)) ? "compliant" : "non-compliant";
|
|
3870
|
+
}
|
|
3871
|
+
/**
|
|
3872
|
+
* Reads each workspace package's actual `package.json` content (not just
|
|
3873
|
+
* `readWorkspacePackages()`'s `{ slug, name, dir }` — `deriveProfile()`'s
|
|
3874
|
+
* `platform` detection needs `bin`, which that helper doesn't carry).
|
|
3875
|
+
* Missing/invalid files are skipped, matching `readWorkspacePackages()`'s
|
|
3876
|
+
* own soft-fail behavior.
|
|
3877
|
+
*/
|
|
3878
|
+
async function readWorkspacePackageJsons(repoRoot, workspacePackages) {
|
|
3879
|
+
const results = [];
|
|
3880
|
+
for (const { slug, dir = "packages" } of workspacePackages) try {
|
|
3881
|
+
const raw = await readFile(join(repoRoot, dir, slug, "package.json"), "utf8");
|
|
3882
|
+
results.push(JSON.parse(raw));
|
|
3883
|
+
} catch {}
|
|
3884
|
+
return results;
|
|
3885
|
+
}
|
|
3886
|
+
//#endregion
|
|
3799
3887
|
//#region src/commands/setup/labels.ts
|
|
3800
3888
|
const CANONICAL_LABELS = [
|
|
3801
3889
|
{
|
|
@@ -5160,6 +5248,21 @@ async function runSetup(input) {
|
|
|
5160
5248
|
if (manual.open_source !== void 0) properties["open_source"] = String(manual.open_source);
|
|
5161
5249
|
if (manual.runtime_environment) properties["runtime_environment"] = manual.runtime_environment;
|
|
5162
5250
|
if (manual.uses_external_packages !== void 0) properties["uses_external_packages"] = String(manual.uses_external_packages);
|
|
5251
|
+
{
|
|
5252
|
+
const rootPackageJson = await readFile(join(input.context.repoRoot, "package.json"), "utf8").then((raw) => JSON.parse(raw)).catch(() => null);
|
|
5253
|
+
const workspacePackages = readWorkspacePackages(input.context.repoRoot);
|
|
5254
|
+
const workspacePackageJsons = await readWorkspacePackageJsons(input.context.repoRoot, workspacePackages);
|
|
5255
|
+
properties["holocron_profile"] = deriveProfile({
|
|
5256
|
+
rootPackageJson,
|
|
5257
|
+
repoName: basename(input.context.repoRoot),
|
|
5258
|
+
isMonorepo,
|
|
5259
|
+
workspacePackageJsons
|
|
5260
|
+
});
|
|
5261
|
+
properties["holocron_stack"] = deriveStack(rootPackageJson).join(", ");
|
|
5262
|
+
const capabilities = deriveCapabilities(config.providers);
|
|
5263
|
+
properties["holocron_capabilities"] = capabilities.join(", ");
|
|
5264
|
+
properties["holocron_compliance"] = deriveCompliance(capabilities);
|
|
5265
|
+
}
|
|
5163
5266
|
if (source.syncProperties) {
|
|
5164
5267
|
steps.push(await runStep("source", "sync properties", dryRun, () => source.syncProperties(properties)));
|
|
5165
5268
|
print(formatStep(steps[steps.length - 1]));
|
|
@@ -6086,6 +6189,21 @@ async function runSync(input) {
|
|
|
6086
6189
|
if (manual.open_source !== void 0) properties["open_source"] = String(manual.open_source);
|
|
6087
6190
|
if (manual.runtime_environment) properties["runtime_environment"] = manual.runtime_environment;
|
|
6088
6191
|
if (manual.uses_external_packages !== void 0) properties["uses_external_packages"] = String(manual.uses_external_packages);
|
|
6192
|
+
{
|
|
6193
|
+
const rootPackageJson = await readFile(join(input.context.repoRoot, "package.json"), "utf8").then((raw) => JSON.parse(raw)).catch(() => null);
|
|
6194
|
+
const workspacePackages = readWorkspacePackages(input.context.repoRoot);
|
|
6195
|
+
const workspacePackageJsons = await readWorkspacePackageJsons(input.context.repoRoot, workspacePackages);
|
|
6196
|
+
properties["holocron_profile"] = deriveProfile({
|
|
6197
|
+
rootPackageJson,
|
|
6198
|
+
repoName: basename(input.context.repoRoot),
|
|
6199
|
+
isMonorepo,
|
|
6200
|
+
workspacePackageJsons
|
|
6201
|
+
});
|
|
6202
|
+
properties["holocron_stack"] = deriveStack(rootPackageJson).join(", ");
|
|
6203
|
+
const capabilities = deriveCapabilities(config.providers);
|
|
6204
|
+
properties["holocron_capabilities"] = capabilities.join(", ");
|
|
6205
|
+
properties["holocron_compliance"] = deriveCompliance(capabilities);
|
|
6206
|
+
}
|
|
6089
6207
|
steps.push(await runSyncStep("source", "sync properties", dryRun, () => source.syncProperties(properties)));
|
|
6090
6208
|
print(formatSyncStep(steps[steps.length - 1]));
|
|
6091
6209
|
} else {
|