@icebreakers/monorepo-templates 2.1.0 → 2.2.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/assets/.agents/skills/repoctl/SKILL.md +2 -0
- package/assets/.agents/skills/repoctl/references/commands.md +224 -13
- package/assets/.agents/skills/repoctl/references/config.md +70 -6
- package/assets/.agents/skills/repoctl/references/dependencies.md +37 -0
- package/assets/.agents/skills/repoctl/references/knip.md +34 -0
- package/assets/.agents/skills/repoctl/references/templates.md +23 -0
- package/assets/.github/workflows/ci.yml +10 -10
- package/assets/package.json +5 -9
- package/assets/pnpm-workspace.yaml +2 -1
- package/assets/repoctl.config.ts +1 -1
- package/assets/turbo.json +2 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +281 -1
- package/dist/index.mjs +1137 -74
- package/dist/{prepare-2VvFwYDz.mjs → prepare-BYNSxvoB.mjs} +76 -4
- package/package.json +2 -1
- package/template-data.mjs +24 -0
- package/templates/client/package.json +1 -1
- package/templates/next/README.md +31 -0
- package/templates/next/eslint.config.js +5 -0
- package/templates/next/gitignore +7 -0
- package/templates/next/next.config.ts +5 -0
- package/templates/next/package.json +36 -0
- package/templates/next/src/app/api/health/route.ts +5 -0
- package/templates/next/src/app/counter.tsx +19 -0
- package/templates/next/src/app/favicon.ico +0 -0
- package/templates/next/src/app/globals.css +109 -0
- package/templates/next/src/app/layout.tsx +17 -0
- package/templates/next/src/app/page.tsx +16 -0
- package/templates/next/stylelint.config.js +3 -0
- package/templates/next/test/counter.test.tsx +22 -0
- package/templates/next/tsconfig.json +40 -0
- package/templates/next/turbo.json +24 -0
- package/templates/next/vitest.config.js +9 -0
- package/templates/react-lib/LICENSE +21 -0
- package/templates/react-lib/README.md +52 -0
- package/templates/react-lib/eslint.config.js +5 -0
- package/templates/react-lib/gitignore +4 -0
- package/templates/react-lib/package.json +48 -0
- package/templates/react-lib/src/counter.tsx +28 -0
- package/templates/react-lib/src/index.ts +2 -0
- package/templates/react-lib/src/style.css +36 -0
- package/templates/react-lib/stylelint.config.js +3 -0
- package/templates/react-lib/test/counter.test.tsx +28 -0
- package/templates/react-lib/test-d/index.test-d.ts +11 -0
- package/templates/react-lib/tsconfig.app.json +15 -0
- package/templates/react-lib/tsconfig.build.json +12 -0
- package/templates/react-lib/tsconfig.json +4 -0
- package/templates/react-lib/tsconfig.node.json +11 -0
- package/templates/react-lib/vite.config.ts +22 -0
- package/templates/react-lib/vitest.config.ts +5 -0
- package/templates/react-vite/README.md +27 -0
- package/templates/react-vite/eslint.config.js +5 -0
- package/templates/react-vite/gitignore +6 -0
- package/templates/react-vite/index.html +13 -0
- package/templates/react-vite/package.json +36 -0
- package/templates/react-vite/src/app.tsx +22 -0
- package/templates/react-vite/src/main.tsx +15 -0
- package/templates/react-vite/src/style.css +95 -0
- package/templates/react-vite/stylelint.config.js +3 -0
- package/templates/react-vite/test/app.test.tsx +22 -0
- package/templates/react-vite/tsconfig.app.json +26 -0
- package/templates/react-vite/tsconfig.json +11 -0
- package/templates/react-vite/tsconfig.node.json +18 -0
- package/templates/react-vite/vite.config.ts +6 -0
- package/templates/react-vite/vitest.config.ts +9 -0
- package/templates/vitepress/.vitepress/navigation/routes.ts +7 -0
- package/templates/vitepress/reference/artifacts.md +51 -0
- package/templates/vitepress/reference/commands.md +149 -0
- package/templates/vitepress/reference/config.md +101 -7
- package/templates/vitepress/reference/dependencies.md +116 -0
- package/templates/vitepress/reference/dependency-admission.md +67 -0
- package/templates/vitepress/reference/devcontainer.md +25 -0
- package/templates/vitepress/reference/index.md +1 -0
- package/templates/vitepress/reference/install-security.md +51 -0
- package/templates/vitepress/reference/knip.md +55 -0
- package/templates/vitepress/reference/move.md +39 -0
- package/templates/vitepress/reference/organization-presets.md +89 -0
- package/templates/vitepress/reference/removal.md +42 -0
- package/templates/vitepress/reference/template-assets.md +39 -0
- package/templates/vitepress/reference/templates.md +372 -9
- package/templates/vitepress/start/diagnose.md +19 -2
- package/templates/vitepress/tasks/checks.md +149 -1
- package/templates/vitepress/tasks/ci.md +93 -1
- package/templates/vitepress/tasks/release.md +72 -6
- package/templates/vitepress/zh/reference/artifacts.md +51 -0
- package/templates/vitepress/zh/reference/commands.md +148 -0
- package/templates/vitepress/zh/reference/config.md +94 -2
- package/templates/vitepress/zh/reference/dependencies.md +116 -0
- package/templates/vitepress/zh/reference/dependency-admission.md +67 -0
- package/templates/vitepress/zh/reference/devcontainer.md +25 -0
- package/templates/vitepress/zh/reference/index.md +1 -0
- package/templates/vitepress/zh/reference/install-security.md +51 -0
- package/templates/vitepress/zh/reference/knip.md +55 -0
- package/templates/vitepress/zh/reference/move.md +39 -0
- package/templates/vitepress/zh/reference/organization-presets.md +89 -0
- package/templates/vitepress/zh/reference/removal.md +42 -0
- package/templates/vitepress/zh/reference/template-assets.md +19 -4
- package/templates/vitepress/zh/reference/templates.md +352 -9
- package/templates/vitepress/zh/start/diagnose.md +24 -13
- package/templates/vitepress/zh/tasks/checks.md +150 -0
- package/templates/vitepress/zh/tasks/ci.md +93 -1
- package/templates/vitepress/zh/tasks/release.md +72 -6
- package/templates/vue-lib/package.json +1 -1
|
@@ -53,7 +53,11 @@ Use `--dry-run`, `--json`, or `--markdown` to inspect the plan without running i
|
|
|
53
53
|
|
|
54
54
|
## Pre-Push
|
|
55
55
|
|
|
56
|
-
`repo verify pre-push`
|
|
56
|
+
`repo verify pre-push` discovers packages from `pnpm-workspace.yaml`, including private applications and respecting excluded patterns. It runs the owning package's available `build`, `test`, and `tsd` scripts for changed files, then always runs root `lint` and `typecheck`. Nested packages use the deepest matching directory; the workspace root is not also run as a child package.
|
|
57
|
+
|
|
58
|
+
Deleted files still trigger their former package. Moving a file between packages checks both directories. Root configuration changes and removed package manifests without a current owner also trigger root `build`, `test`, and `tsd`. This is file ownership selection, not a reverse dependency closure. A newly pushed ref is compared with the empty tree; deleting a ref adds no package tasks.
|
|
59
|
+
|
|
60
|
+
Programmatic callers can still pass `workspaces` to override discovery, including an empty array. Entries are directories relative to `cwd`, and their order does not affect ownership.
|
|
57
61
|
|
|
58
62
|
## Automation
|
|
59
63
|
|
|
@@ -63,3 +67,147 @@ repo check --staged --markdown --redact --out reports/check-plan.md
|
|
|
63
67
|
```
|
|
64
68
|
|
|
65
69
|
JSON field names and command IDs do not change with `--lang`; only human-readable descriptions do.
|
|
70
|
+
|
|
71
|
+
## Affected Checks
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
repo check --affected --base origin/main --head HEAD --json
|
|
75
|
+
repo check --affected --base HEAD~1 --filter @acme/web --dry-run
|
|
76
|
+
repo check --affected --global-input 'shared-config/**'
|
|
77
|
+
repo check --affected --base origin/main --report reports/affected.json
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Affected mode selects changed workspace packages and their direct/transitive consumers, including private apps. JSON schema version `1` records the Git range, file ownership, dependency paths, fallback reasons, package selection and skipped tasks. Repeatable `--filter` accepts exact names or workspace-relative directories and intersects the affected set. Empty intersections and missing scripts are explained; they never execute unfiltered recursive commands.
|
|
81
|
+
|
|
82
|
+
The default range is the merge base of `origin/main` and `HEAD`, through `HEAD`, plus staged, unstaged and untracked files. `--head` must resolve to the current checkout. Missing refs, shallow/incomplete history, a non-current head, unavailable Git or a workspace nested below the Git root explicitly fall back to full checks. Unsafe workspace discovery fails instead of returning an empty plan. Deleted files and both sides of renames retain their owners; package manifest changes, including new/deleted packages, trigger full checks because dependencies may have changed.
|
|
83
|
+
|
|
84
|
+
Default global inputs cover root package/workspace/lock/Turbo files, TypeScript/lint/test/commit configs, `.npmrc`, `.pnpmfile.*`, Node version files, `.github/**`, `.husky/**`, `scripts/**` and `patches/**`. Turbo `globalDependencies` and repeatable `--global-input` globs are additive. Unknown unowned files trigger full checks. Root Markdown, `docs/**` and license/notice documents are ignored unless a global rule matches; files inside a workspace remain package inputs. An unreadable Turbo config or unresolved graph diagnostics (including catalogs) also trigger full fallback when there are changes. Inspect `globalInputs` and `fallback` in JSON for the exact rules and reasons.
|
|
85
|
+
|
|
86
|
+
Execution follows `build → lint → typecheck → tsd → test`, skipping unavailable scripts. pnpm recursive commands receive the exact planned filters and handle dependency ordering. Build also includes dependencies outside the affected/filter set, recorded in `prerequisiteTargets`, so tests can use built artifacts. Full fallback uses each existing root script; when a root script is absent, it uses that script across all selected packages. Explicit filters limit checks even during fallback; build prerequisites may still lie outside them. Root scripts are responsible for their own Turbo/pnpm delegation.
|
|
87
|
+
|
|
88
|
+
`--json`, `--markdown`, `--dry-run` and `--out` remain previews. `--report` executes the same model and embeds it as `affectedPlan`. `--affected` cannot combine with `--full`, `--staged` or `--edit-file`; base/head/filter/global-input options require affected mode. Programmatic callers use `resolveAffectedCheckPlan({ cwd, base, head, filters, globalInputs })` or `runCheckWithReport({ cwd, affected: true, ... })`.
|
|
89
|
+
|
|
90
|
+
Export this same plan as a GitHub Actions matrix with `repo check --affected --matrix`; use `--shards 16` to group workspaces. See [CI matrix setup](/tasks/ci#generate-an-affected-github-actions-matrix) for empty-result handling and safe argument-array execution.
|
|
91
|
+
|
|
92
|
+
## Execution Reports
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
repo check --full --report reports/check-result.json --redact
|
|
96
|
+
repo check --full --report reports/check-result.md --report-format markdown
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`--report <file>` runs checks and writes the result separately from live terminal logs. It cannot be combined with `--dry-run`, `--json`, `--markdown`, or `--out`, which continue to produce plans only. The default report format is JSON.
|
|
100
|
+
|
|
101
|
+
JSON schema version `1` records the mode, directory, start/end timestamps, duration in milliseconds, exit code, and each task's executable and argument array. Stable statuses are `success`, `failed`, `skipped`, and `interrupted`. Failed commands stop the sequence; later tasks retain null timestamps/exit codes and a skip reason. The CLI keeps the failing exit code and saves the report on SIGINT/SIGTERM where the operating system allows graceful handling. SIGKILL, abrupt power loss, and an unwritable report destination cannot guarantee a saved report.
|
|
102
|
+
|
|
103
|
+
Reports capture root script or verification stage results, not the internals of Turbo or lint-staged. In staged mode, configured typechecks run inside pre-commit; the separate staged-typecheck stage has no explicit file arguments and is reported as skipped. Reports do not collect environment values or child output. `--redact` replaces workspace and home path prefixes, including those in command arguments.
|
|
104
|
+
|
|
105
|
+
The programmatic `runCheckWithReport({ cwd, full: true, signal })` API returns the same report without exiting the caller. An optional `AbortSignal` cancels the active command.
|
|
106
|
+
|
|
107
|
+
## Find available workspace tasks
|
|
108
|
+
|
|
109
|
+
`repo workspace tasks` lists actual package scripts, including root tasks and private applications. A package without scripts is shown explicitly. Search text matches names, paths, descriptions and task names literally; `--script` requires an exact script name. Neither discovery nor location runs scripts or writes files.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
repo workspace tasks client
|
|
113
|
+
repo workspace tasks --script test --json
|
|
114
|
+
repo workspace tasks --no-private --no-root
|
|
115
|
+
repo workspace locate @scope/client
|
|
116
|
+
repo workspace locate client --json
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`locate` accepts an absolute directory path (including directory symlinks) as an exact query; an absolute path that is not a workspace root never falls back to text search. Other queries prefer an exact package name or workspace-relative path, then search names, paths and descriptions. Relative paths and `.` refer to the workspace root even when invoked from a child package. A unique match prints only its absolute path. Missing or ambiguous matches exit with code 1; ambiguity lists candidates instead of choosing one. `--interactive` enables a choice only in a TTY, with the prompt on stderr; JSON and CI always report candidates. Quote the resulting path when passing it to a shell command.
|
|
120
|
+
|
|
121
|
+
The public `getWorkspaceTaskCatalog(cwd, options)` and `locateWorkspace(cwd, query)` APIs return `schemaVersion: 1` data. The catalog includes stable exclusion reasons and each task's existing script plus a pnpm executable/argument array (`--dir`, directory, `run`, task). Callers may explicitly execute that array to preserve pnpm and Turbo behavior; searching never launches it.
|
|
122
|
+
|
|
123
|
+
## Per-Package Manifest Health
|
|
124
|
+
|
|
125
|
+
`repo doctor` discovers packages using pnpm workspace patterns (including private and root packages) and reads each manifest independently. An unreadable package.json produces a diagnostic while other packages are still checked. pnpm package.yaml and package.json5 are supported; multiple manifests in one directory require review. Discovery reads fresh files for each API call.
|
|
126
|
+
|
|
127
|
+
Manifest findings reuse the existing doctor JSON, Markdown, `--strict`, and `--redact` output. Each finding includes a stable `id`, workspace-relative `path`, and `field`. Rules cover missing/invalid/duplicate names, invalid versions and dependency sections, missing/ambiguous workspace targets, self-dependencies, and duplicate/conflicting declarations. Normal peer/dev and peer/runtime pairs are retained; peer compatibility is a separate check. An optional dependency overriding a regular dependency is a warning, since npm permits this explicitly.
|
|
128
|
+
|
|
129
|
+
Unpublished applications should declare `private: true`. Public package license, repository, repository.directory and publishConfig recommendations are warnings, not mandatory publishing policy; strict mode also fails on warnings. repository.directory is compared against the workspace root, so a workspace embedded in another repository or a separately hosted package requires manual review. Values from invalid JSON and registry credentials are never included in these findings. Doctor neither executes package scripts nor changes manifests. Actual tarball contents and type consumption remain separate package delivery checks.
|
|
130
|
+
|
|
131
|
+
## Internal dependency boundaries
|
|
132
|
+
|
|
133
|
+
`repo workspace boundaries --json` inspects the shared manifest graph, including root and private packages. Configure `boundaries` in repoctl.config; configured policies also appear in `repo doctor` JSON/Markdown and its existing CI exit policy. The standalone command exits 1 on failures; `--strict` includes warnings. It never runs tasks, scans source imports, changes dependencies or enforces third-party admission.
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
export default {
|
|
137
|
+
boundaries: {
|
|
138
|
+
tags: { shared: { paths: ['packages/**'] }, app: { paths: ['apps/**'] } },
|
|
139
|
+
rules: [
|
|
140
|
+
{ id: 'shared-layer', from: { tags: ['shared'] }, allow: [{ tags: ['shared'] }] },
|
|
141
|
+
{ id: 'public-packages', from: { private: false }, allow: [{ private: false }] },
|
|
142
|
+
],
|
|
143
|
+
cycles: { dependencyTypes: ['dependencies', 'optionalDependencies'], severity: 'fail' },
|
|
144
|
+
exceptions: [
|
|
145
|
+
{ rule: 'shared-layer', source: 'packages/adapter', target: 'apps/web', type: 'peerDependencies', reason: 'Temporary adapter during migration' },
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Selectors combine fields with AND and values within one field with OR. `packages` means exact names; `paths` supports exact workspace directories (root is `.`) or `directory/**` for descendants. `./**` includes the entire workspace. Other glob syntax is rejected. Tags are named selectors, cannot reference other tags, and may overlap. A rule's `allow` array is a union; an empty array denies all internal dependencies. Every matching rule applies. Boundary rules include all four dependency fields by default, while cycle detection defaults to dependencies and optionalDependencies only. Configure dependencyTypes to include dev or peer edges; `cycles: false` disables cycles. Semver edges mean possible local relationships, not lockfile installation proof.
|
|
152
|
+
|
|
153
|
+
Reports contain stable rule IDs, the violated manifest field, endpoints and exact edges. Each cyclic strongly connected component produces one deterministic closed representative chain and its full member list, rather than enumerating every cycle. A cycle exception removes only the exact typed edge; remaining cycles are still checked. Reasons and waived edges remain visible. Unknown fields/tags, invalid paths, duplicate IDs and missing exception reasons fail validation. Unmatched selector alternatives and unused exceptions warn; incomplete internal graph resolution fails so missing relationships are not reported as healthy.
|
|
154
|
+
|
|
155
|
+
The public `checkWorkspaceBoundaries(cwd, { config? })` returns schemaVersion 1 with findings, exceptions and summary. Explicit API config replaces project configuration for that call. Relative IDs and rule IDs do not change with output language. Put the CLI or `repo doctor --strict` into an existing pnpm CI script; repoctl adds no separate task runner.
|
|
156
|
+
|
|
157
|
+
## Workspace ownership and CODEOWNERS
|
|
158
|
+
|
|
159
|
+
Declare exact package names or workspace-relative paths in `repoctl.config.ts`:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
export default {
|
|
163
|
+
codeowners: { owners: { '@acme/web': ['@acme/frontend'], 'libraries/sdk': ['@maintainer'] } },
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`repoctl workspace owners [workspace] --json` reports private and public packages, owners, configuration sources, and missing ownership. Owners accept GitHub users, teams, or email addresses. No network calls verify membership; actual access and review permissions remain managed by GitHub.
|
|
168
|
+
|
|
169
|
+
```sh
|
|
170
|
+
repoctl workspace owners --file .github/CODEOWNERS --dry-run
|
|
171
|
+
repoctl workspace owners --file .github/CODEOWNERS --sync
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
A file is always explicit: `.github/CODEOWNERS`, `CODEOWNERS`, or `docs/CODEOWNERS`, relative to the workspace root. Map `.` or the root package name to create a `*` default rule before the more specific child package rules. The default is read-only. `--dry-run` also prevents writes with `--sync`. A marked block preserves outside rules/comments byte for byte. The last matching GitHub rule wins: later rules that may shadow generated paths are reported, including ownerless rules. An existing higher-priority CODEOWNERS file is reported. Unsupported literal directory characters and invalid mappings block sync.
|
|
175
|
+
|
|
176
|
+
The public `planCodeowners()` / `applyCodeownersPlan()` APIs expose before/after content and a diff, revalidate configuration and workspace inputs, and reject stale plans. Writes replace one file atomically, refuse symlink/hardlink targets, and are idempotent. No messages, review requests, permissions, or branch protection are changed.
|
|
177
|
+
Installation policy: see [pnpm installation security](../reference/install-security.md) for release waiting, trust and build approval diagnostics and optional reviewed presets.
|
|
178
|
+
|
|
179
|
+
## Managed asset upgrade PRs
|
|
180
|
+
|
|
181
|
+
After a trusted default-branch dependency update, `repo maintenance upgrade --base <full-before-sha> --head <full-current-sha> --out <empty-external-directory>` compares the resolved **root** `repoctl` version in the pnpm lockfile. It understands pnpm 12's separate package-manager document, ignores unrelated dependency and peer-context changes, and blocks unsupported or ambiguous resolutions. The target version must already be installed from the frozen lockfile. Exactly pinned, directly configured [organization presets](../reference/organization-presets.md#maintenance-pull-requests) can also trigger maintenance for existing owned assets. Exported workflows embed fixed package/source/target authorization; newly declared files require explicit adoption and workflow review.
|
|
182
|
+
|
|
183
|
+
Run preparation in a disposable clean checkout: it applies the complete root upgrade plan and three-way merges, refreshes the lockfile through pnpm, reinstalls with `--frozen-lockfile --ignore-scripts`, then runs declared build, lint, typecheck, tsd and test scripts in that order. The checkout retains the generated changes. Conflicts, changed source inputs, unsupported paths and failed checks leave `report.json` and logs with `blocked` status. No asset diff produces `unchanged`; neither state can create a PR. The report contains every planned file, skips/conflicts, source and target versions, validation results, exact file hashes/modes and the patch digest.
|
|
184
|
+
|
|
185
|
+
Report file hashes identify exact Git blob bytes. Preparation still checks the planned working-file bytes; publication verifies the patch index and Git-equivalent checkout contents, allowing built-in line-ending conversions. The isolated publisher disables hooks, executable clean/smudge/process filters and filesystem monitors before inspecting files, and retains those settings for PR creation. Preset reports also carry raw preconditions and restricted checkout settings, which Git uses to bind planned bytes to committed and patched blobs while preserving fixed source and ownership checks. Before bytes use the original attributes and after bytes use the authorized patched attributes, so a preset can update .gitattributes. Patch/report path mismatches fail before apply; final plan verification can reject the isolated checkout afterward, still before obtaining write credentials.
|
|
186
|
+
|
|
187
|
+
Automatic maintenance can complete the built-in Changesets migration and update only its exact `.repoctl/migrations/ledger.json` metadata path. Preparation and publication share the same fixed migration identity, committed legacy-source, journal and completed-history checks; the target template version comes from the committed root repoctl → monorepo → templates lockfile dependency chain, not the repoctl version number. The publisher binds every migration output to the verified Git patch and checks public-package prerelease lanes against committed workspace membership. Interrupted journals, ambiguous lockfiles, noncanonical workspace patterns, and YAML/JSON5 workspace manifests require a reviewed manual `repo upgrade`; no broader `.repoctl` path is authorized. Refresh the trusted exported workflow to adopt this migration policy.
|
|
188
|
+
|
|
189
|
+
Automatic maintenance can complete the built-in Changesets migration and update only its exact `.repoctl/migrations/ledger.json` metadata path. Preparation and publication share the same fixed migration identity, committed legacy-source, journal and completed-history checks; the target template version comes from the committed root repoctl → monorepo → templates lockfile dependency chain, not the repoctl version number. The publisher binds every migration output to the verified Git patch and checks public-package prerelease lanes against committed workspace membership. Interrupted journals, ambiguous lockfiles, noncanonical workspace patterns, and YAML/JSON5 workspace manifests require a reviewed manual `repo upgrade`; no broader `.repoctl` path is authorized. Refresh the trusted exported workflow to adopt this migration policy.
|
|
190
|
+
|
|
191
|
+
Export the opt-in recipe with `repo maintenance workflow --out .github/workflows/repoctl-upgrade.yml`; existing files are never overwritten. Configure a repository GitHub App through `REPOCTL_APP_CLIENT_ID` and `REPOCTL_APP_PRIVATE_KEY`, with **Contents, Pull requests and Workflows: write** permissions, then commit the workflow. The Workflows permission is needed for managed workflow updates; an App token also lets the proposed PR trigger normal CI. No registry token is required.
|
|
192
|
+
|
|
193
|
+
The first job only has read permissions and can run the trusted checkout's configuration, dependencies and validation scripts. It uploads an immutable artifact. The second job downloads that exact artifact ID, verifies its digest, originating run/attempt/repository, source SHA, allowed root paths, Git modes and before/after hashes, and applies only the reviewed patch. It executes no project scripts or installed dependencies. It then obtains the App token and uses a pinned `create-pull-request` action on the dedicated `repoctl/managed-assets` branch. Repeated events update that branch and PR; reserve it for automation. A moved default branch or missing/insufficient App permissions stops publication. Reports remain in the workflow artifacts for review.
|
|
194
|
+
|
|
195
|
+
Only default-branch pushes touching `pnpm-lock.yaml` and explicit default-branch manual runs are supported. For a manual retry, provide the full trusted ancestor SHA before the dependency update. Concurrency is serialized per repository/default branch. Fork PR events, automatic merges, registry publication, arbitrary dependency updates, organization asset providers and template instance upgrades are outside this recipe.
|
|
196
|
+
|
|
197
|
+
## Diagnose Turbo cache misses and slow tasks
|
|
198
|
+
|
|
199
|
+
Capture evidence with Turbo's existing `pnpm exec turbo run build --summarize`, then analyze an explicit saved summary. The analyzer never invokes tasks, deletes caches or edits configuration:
|
|
200
|
+
|
|
201
|
+
```sh
|
|
202
|
+
repo check cache .turbo/runs/current.json
|
|
203
|
+
repo check cache .turbo/runs/current.json .turbo/runs/previous.json --json
|
|
204
|
+
repo check cache current.json previous.json --markdown > cache-analysis.md
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`--slowest 10` selects up to 1–100 tasks. JSON schema version 1 includes known hits/misses, unknown cache outcomes, a hit rate whose denominator excludes unknowns, actual task durations, duration deltas and comparisons keyed by stable task ID. Added and removed tasks are explicit. Missing/duplicate IDs, missing fields and unsupported summary schemas produce limitations; absent evidence never proves an input was removed. Native Turbo summary schema 1 is supported, including a real Turbo 2.11.6 fixture. Future schemas expose coarse recorded data only, with unknown comparisons.
|
|
208
|
+
|
|
209
|
+
Shared global differences are stored once in `globalEvidence`; task evidence contains only task-specific differences, so report size does not multiply global file count by task count. Comparison evidence covers files, global inputs, dependency task hashes, environment declarations/values, task commands/arguments and resolved task configuration. Every value, including untrusted input hashes, becomes a SHA-256 digest before output; only task IDs, filenames, environment names and allowlisted field names are retained as labels. Raw environment values, commands and arbitrary configuration are never printed. Digest equality is evidence comparison, not proof of a unique cache-miss cause. Unchanged hashes with a miss and opaque changed hashes have explicit limitations. Reports do not include the input summary path.
|
|
210
|
+
|
|
211
|
+
The dependency critical path is available only with unique tasks, complete dependencies, valid timings, an acyclic graph and no overlapping dependency execution. It reports summed task time separately from the observed span; scheduling gaps and contention prevent treating this as an optimization estimate. Inputs are limited to JSON files of 20 MiB and nesting depth 64. Analysis is read-only; redirect stdout to retain an artifact. Parent execution and plan-writing options (`--full`, `--affected`, `--report`, `--out`, etc.) are rejected; `--json`, `--markdown` and `--dry-run` work before or after `cache`. Normal `check --json` remains a check-plan preview.
|
|
212
|
+
|
|
213
|
+
Public API: `analyzeTurboRuns(currentPath, { previous?: string, slowest?: number })` returns `TurboRunAnalysis` without changing files. JSON fields and limitation codes remain stable across languages.
|
|
@@ -24,7 +24,7 @@ CI receives a stable plan and can upload the JSON or Markdown report as an artif
|
|
|
24
24
|
|
|
25
25
|
- Pull requests need a fast gate: use `repo check --staged` or the repository's pre-commit mode.
|
|
26
26
|
- Main branch needs a delivery gate: use `repo check --full`.
|
|
27
|
-
- A release job needs a version plan: run `repo release --
|
|
27
|
+
- A release job needs a version plan: run `repo release plan --json` before publishing.
|
|
28
28
|
|
|
29
29
|
repoctl commands have two audiences: humans using short daily commands, and automation consuming stable reports.
|
|
30
30
|
|
|
@@ -108,6 +108,98 @@ repo verify pre-push
|
|
|
108
108
|
| Output for scripts | `--json --out <file>` |
|
|
109
109
|
| Share a redacted report | `--markdown --redact --out <file>` |
|
|
110
110
|
|
|
111
|
+
## Generate an affected GitHub Actions matrix
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pnpm exec repo check --affected --base origin/main --matrix
|
|
115
|
+
pnpm exec repo check --affected --base origin/main --filter @scope/web --matrix
|
|
116
|
+
pnpm exec repo check --affected --base origin/main --matrix --shards 16 --out reports/matrix.json
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`--matrix` always previews JSON. It runs no checks and does not create or change a workflow; only explicit `--out` writes a report. It requires `--affected` and accepts the same `--base`, `--head`, repeatable `--filter` and `--global-input`. It cannot be combined with `--markdown` or execution-report options. `--json` and `--dry-run` are optional because matrix output already previews JSON.
|
|
120
|
+
|
|
121
|
+
The public `resolveAffectedCheckMatrix(options)` API returns `schemaVersion: 1`, `provider: "github-actions"`, `hasWork`, `grouping`, `summary`, `matrix: { include: [...] }`, and the complete `affectedPlan`. That embedded plan is identical to `resolveAffectedCheckPlan` for the same inputs, including selection reasons, Git diagnostics and skipped stages. Pass **only `matrix`** to `fromJSON`. Gate the job on `hasWork` before strategy expansion; no changes or no runnable stages produce `hasWork: false` and `include: []`.
|
|
122
|
+
|
|
123
|
+
Each item has a stable `id`, workspace-relative `packages`, and ordered `commands` containing `executable`/`args`, `targets`, `prerequisiteTargets` and `skipReason`. Execute these arrays from the new checkout's root. The descriptive `command` string is for display, not shell evaluation. Every job keeps **build → lint → typecheck → tsd → test** together. Its build also includes the required internal dependencies, so consumers never wait for artifacts from a different matrix job. Missing scripts retain their reasons; groups with no runnable command are listed in `summary.skippedPackages`.
|
|
124
|
+
|
|
125
|
+
By default each selected workspace gets one job. `--shards N` (1–256) sorts package directories and distributes them round-robin into at most N groups; it never drops packages. Above GitHub's 256-job limit, generation fails with a request to use `--shards 256` or fewer. The serialized matrix is also checked against the 1 MB job-output limit (estimated as UTF-16), with a small allowance for metadata. Oversized output fails instead of truncating targets; use fewer shards to reduce repeated builds. Every full fallback remains **one complete job**, even with `--shards`: this preserves root scripts and conservatively handles uncertain dependency graphs. Missing or invalid Git refs follow affected mode's full fallback and retain explicit diagnostic codes rather than becoming an empty successful matrix.
|
|
126
|
+
|
|
127
|
+
This example uses full checkout history and the pull request's base SHA. Each job checks out the same event commit and installs the same lockfile. It requires only `contents: read`; pnpm's store cache does not replace per-job dependency builds. Existing pnpm/Turbo scripts continue to own task ordering and caching. The example targets Linux and sends matrix data through an environment variable into argument-array execution, so paths with spaces, quotes or shell syntax remain data.
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
name: Affected checks
|
|
131
|
+
on: pull_request
|
|
132
|
+
permissions:
|
|
133
|
+
contents: read
|
|
134
|
+
jobs:
|
|
135
|
+
plan:
|
|
136
|
+
runs-on: ubuntu-latest
|
|
137
|
+
outputs:
|
|
138
|
+
matrix: ${{ steps.matrix.outputs.matrix }}
|
|
139
|
+
has-work: ${{ steps.matrix.outputs.has-work }}
|
|
140
|
+
steps:
|
|
141
|
+
- uses: actions/checkout@v7
|
|
142
|
+
with:
|
|
143
|
+
fetch-depth: 0
|
|
144
|
+
- uses: pnpm/action-setup@v6
|
|
145
|
+
- uses: actions/setup-node@v7
|
|
146
|
+
with:
|
|
147
|
+
node-version: 22
|
|
148
|
+
cache: pnpm
|
|
149
|
+
- run: pnpm install --frozen-lockfile
|
|
150
|
+
- id: matrix
|
|
151
|
+
env:
|
|
152
|
+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
153
|
+
run: |
|
|
154
|
+
node --input-type=module <<'JS'
|
|
155
|
+
import { appendFileSync } from 'node:fs'
|
|
156
|
+
import { resolveAffectedCheckMatrix } from 'repoctl'
|
|
157
|
+
const result = await resolveAffectedCheckMatrix({
|
|
158
|
+
cwd: process.cwd(), base: process.env.BASE_SHA, head: 'HEAD', shards: 16,
|
|
159
|
+
})
|
|
160
|
+
appendFileSync(process.env.GITHUB_OUTPUT,
|
|
161
|
+
`has-work=${result.hasWork}\nmatrix=${JSON.stringify(result.matrix)}\n`)
|
|
162
|
+
if (result.affectedPlan.fallback.length) {
|
|
163
|
+
console.error('Full fallback:', JSON.stringify(result.affectedPlan.fallback))
|
|
164
|
+
}
|
|
165
|
+
JS
|
|
166
|
+
checks:
|
|
167
|
+
needs: plan
|
|
168
|
+
if: ${{ needs.plan.outputs.has-work == 'true' }}
|
|
169
|
+
runs-on: ubuntu-latest
|
|
170
|
+
strategy:
|
|
171
|
+
fail-fast: false
|
|
172
|
+
matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
|
|
173
|
+
steps:
|
|
174
|
+
- uses: actions/checkout@v7
|
|
175
|
+
with:
|
|
176
|
+
fetch-depth: 0
|
|
177
|
+
- uses: pnpm/action-setup@v6
|
|
178
|
+
- uses: actions/setup-node@v7
|
|
179
|
+
with:
|
|
180
|
+
node-version: 22
|
|
181
|
+
cache: pnpm
|
|
182
|
+
- run: pnpm install --frozen-lockfile
|
|
183
|
+
- name: Run this job's planned stages
|
|
184
|
+
env:
|
|
185
|
+
MATRIX_JOB: ${{ toJSON(matrix) }}
|
|
186
|
+
run: |
|
|
187
|
+
node --input-type=module <<'JS'
|
|
188
|
+
import { spawnSync } from 'node:child_process'
|
|
189
|
+
const job = JSON.parse(process.env.MATRIX_JOB)
|
|
190
|
+
for (const command of job.commands) {
|
|
191
|
+
if (command.skipReason) continue
|
|
192
|
+
const result = spawnSync(command.executable, command.args, {
|
|
193
|
+
stdio: 'inherit', shell: false,
|
|
194
|
+
})
|
|
195
|
+
if (result.error) throw result.error
|
|
196
|
+
if (result.status !== 0) process.exit(result.status ?? 1)
|
|
197
|
+
}
|
|
198
|
+
JS
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The planning step uses the API to avoid creating an untracked report before selection. If you use `--out`, write into an ignored report directory. No remote workflow is modified or triggered by matrix generation. See [GitHub matrix syntax](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/run-job-variations) and [Turborepo task execution](https://turborepo.dev/docs/reference/run).
|
|
202
|
+
|
|
111
203
|
## Documentation Worker
|
|
112
204
|
|
|
113
205
|
The repoctl documentation is deployed as the `repoctl-docs` Cloudflare Worker. VitePress produces static assets, and Workers Static Assets serves them without an application handler or an `ASSETS` binding.
|
|
@@ -11,14 +11,57 @@ Use this task when a change is ready to version and publish. Keep the release pl
|
|
|
11
11
|
## Smallest command
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
repo release --
|
|
14
|
+
repo release plan --markdown
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
Review
|
|
17
|
+
Review current and planned versions, direct intents, native dependency/fixed-group reasons, lanes, private packages, publish candidates, and release-note previews. Use `repo release plan --json` for the versioned machine interface (`schemaVersion: 1`); status is `ready`, `empty`, or `blocked`. Blocked plans exit with code 1 and stable blocker IDs.
|
|
18
|
+
|
|
19
|
+
The plan delegates version decisions to pnpm recursive dry-run (tested with pnpm 12.8.1). It never consumes intents, changes manifests/changelogs/ledger/Git state, installs or activates pnpm, runs version/release/pnpmfile hooks, or writes to registries. Install a supported pnpm version explicitly if it is unavailable. Hooks that would change preparation inputs are intentionally excluded; rerun the plan after making those changes explicitly.
|
|
20
|
+
|
|
21
|
+
After review, use the separate preparation entry `repo release ci --mode=prepare`. Preview already prepared publication or recovery with `repo release ci --mode=publish --dry-run`. A version plan describes pending intents and does not check registry authentication or prove that publishing can succeed.
|
|
18
22
|
|
|
19
23
|
## Expected result
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
JSON and Markdown describe the same native version decisions. Notes use the existing release renderer and exclude already consumed intent entries. Private packages may change version but are marked as non-publish candidates. The public `createReleasePlan({ cwd })` API returns the same report without writing it to disk.
|
|
26
|
+
|
|
27
|
+
Planning, automatic triggers, and version preparation recognize the same root and private-package intents. Native pnpm controls version propagation across the complete workspace. Release PR notes include only publishable child packages and require their version-specific changelogs; missing root/private changelogs do not block preparation. Root packages are not publication candidates, even when they are not marked private. Configure pnpm's `versioning.ignore` when a workspace package should not participate in versioning, rather than changing workspace discovery only for the apply step.
|
|
28
|
+
|
|
29
|
+
## Stable and maintenance branches
|
|
30
|
+
|
|
31
|
+
Configure `commands.release.branches` in `repoctl.config.*`:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
export default {
|
|
35
|
+
commands: {
|
|
36
|
+
release: {
|
|
37
|
+
branches: {
|
|
38
|
+
stable: 'master',
|
|
39
|
+
maintenance: [{ branch: '1.x', range: '1.x', tag: 'legacy-1' }],
|
|
40
|
+
prerelease: [
|
|
41
|
+
{ branch: 'preview/1.x', lane: 'beta', tag: 'legacy-beta', target: '1.x' },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Omitting this configuration preserves `main` plus `alpha`, `beta`, `rc`, and `next`. `stable` defaults to `main`; omitting `prerelease` keeps those four lanes targeting the stable branch, while `prerelease: []` disables them. Stable and maintenance branches both use pnpm's native `main` lane. Git branch names and npm tags are separate values.
|
|
50
|
+
|
|
51
|
+
Maintenance ranges must have finite upper bounds and cannot overlap. The primary stable line owns versions outside all maintenance ranges. These ranges apply to every publishable package in the repository; packages with independent major versions must fit the chosen line together. Private packages remain native versioning participants but are not publish candidates. Branches, npm tags, and prerelease lanes must be unique. Semver-like tags such as `1.x` are invalid npm tags; use `legacy-1`. The `snapshot-` tag prefix is reserved for temporary packages.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
repo release plan --branch 1.x --markdown
|
|
55
|
+
repo release plan --branch master --json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Plans expose `branchRule` with `branch`, `kind`, `lane`, `range`, `excludedRanges`, `distTag`, and `target`. An explicit `--branch` (or API `branch`) takes precedence; a matching `GITHUB_REF_NAME` is used next; otherwise a read-only plan targets the primary stable branch. Execution uses the actual Git branch or CI ref and rejects unconfigured branches. `resolveReleaseBranches(config)` exposes the same validated mapping to API callers.
|
|
59
|
+
|
|
60
|
+
Preparation checks native pnpm's dry-run before release hooks or version writes, then checks again after verification hooks. Invalid ranges block without consuming intents. Native applied manifests and final publication candidates are rechecked; a hook that changes versions must leave them within the selected line. Arbitrary hook side effects cannot be rolled back by repoctl. The existing pnpm ledger and release checkpoints govern continuation after an interrupted preparation or publication.
|
|
61
|
+
|
|
62
|
+
Release PRs target the selected branch. The primary head remains `release/pnpm-version`; maintenance heads use `release/pnpm-version-<encoded-branch>` so multiple lines can coexist. Maintenance publication uses its own dist-tag and stable GitHub Release metadata. `repo release pre enter beta` selects a declared native lane. `repo release pre exit` returns to the target's native `main` lane and prints the stable/maintenance target; it does not switch Git branches.
|
|
63
|
+
|
|
64
|
+
After changing the mapping, preview `repo upgrade --dry-run --json` and apply the reviewed upgrade to synchronize managed Release workflow push branches. Saved plans detect changed configuration, repeated upgrades are unchanged, and custom workflows remain protected unless explicitly replaced. Workflow dispatch and source recovery must run on the intended configured branch. Keep publish workflows serialized and retain their checkpoints.
|
|
22
65
|
|
|
23
66
|
## Common branches
|
|
24
67
|
|
|
@@ -47,6 +90,8 @@ The managed Release workflow uploads both files as `npm-publish-progress-<run_id
|
|
|
47
90
|
|
|
48
91
|
Reruns query the registry and GitHub and finish missing stages without reuploading accepted versions or repeating completed hooks. Prereleases push the version commit before uploading. Source discovery requires complete Git history (`fetch-depth: 0`), and recovery checks the original manifest and changelog. If a later commit changed the changelog, check out the reported original commit before recovery.
|
|
49
92
|
|
|
93
|
+
When `.changeset/ledger.yaml` records a package version, its first appearance along Git's first-parent history identifies the prepared release, including a first release that keeps the package's initial version. A release merged without squashing belongs to the mainline merge commit. Versions without ledger entries, such as dependency-propagated bumps, fall back to manifest history on the same first-parent chain. Uncommitted ledger entries cannot establish a release source. Recovery also rejects adding or deleting a changelog relative to that source; it permits a missing changelog only when both the source and current checkout omit it.
|
|
94
|
+
|
|
50
95
|
Legacy releases without a checkpoint use npm `gitHead`; if absent, verify the source manually and set `REPO_RELEASE_SOURCE_SHA`. Never substitute the rerun HEAD. Historical targets with existing Releases remain no-ops. Hooks for legacy targets with missing metadata have unknown outcomes.
|
|
51
96
|
|
|
52
97
|
```bash
|
|
@@ -73,15 +118,36 @@ Release PRs use the applied package list returned by pnpm, not arbitrary Git cha
|
|
|
73
118
|
If an existing prepared version has not completed publication, new version preparation stops before consuming intents and prints its source commit and recovery command. Finish that release first, then run preparation again:
|
|
74
119
|
|
|
75
120
|
```bash
|
|
76
|
-
repo release ci --mode publish --source-sha <full-
|
|
77
|
-
repo release ci --mode publish --source-sha <full-
|
|
121
|
+
repo release ci --mode publish --source-sha <full-release-line-commit-sha> --dry-run
|
|
122
|
+
repo release ci --mode publish --source-sha <full-release-line-commit-sha>
|
|
78
123
|
repo release ci --mode prepare
|
|
79
124
|
```
|
|
80
125
|
|
|
81
|
-
The managed Release workflow exposes the same `source-sha` input for `publish` and `publish-unpublished`. Current tooling checks out the original commit into an isolated directory, installs its locked dependencies, builds and verifies that source, and recovers the full set of versions introduced by its manifests or ledger, including dependency propagation. Package/version inputs validate membership; they do not split the original release. The SHA must belong to `origin
|
|
126
|
+
The managed Release workflow exposes the same `source-sha` input for `publish` and `publish-unpublished`. Current tooling checks out the original commit into an isolated directory, installs its locked dependencies, builds and verifies that source, and recovers the full set of versions introduced by its manifests or ledger, including dependency propagation. Package/version inputs validate membership; they do not split the original release. The SHA must belong to `origin/<selected-branch>`; the dispatch configuration determines the line, including when the historical source had a different configuration. Dry-run only inspects the source and remote release state; it does not install, upload, run hooks, or write checkpoints. Later changes stay pending for a new version. The original source must already include all intended version changes; no versions are changed during recovery.
|
|
82
127
|
|
|
83
128
|
Recovery preserves GitHub's workflow environment for trusted publishing and provenance: its signed identity refers to the workflow run's commit. The isolated checkout, release checkpoint, Git tags, and Release targets refer to `source-sha`. Do not overwrite `GITHUB_SHA` to recover an older source; npm rejects provenance that disagrees with the signed workflow identity.
|
|
84
129
|
|
|
130
|
+
## PR and nightly snapshots
|
|
131
|
+
|
|
132
|
+
Use a snapshot for downstream validation before a stable release. Commit the source first and preview the complete set of public workspace packages:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
repo release snapshot --kind pr --pr 123 --commit <full-HEAD-sha> --build-id <run-id>-<attempt> --dry-run --json
|
|
136
|
+
repo release snapshot --kind nightly --commit <full-HEAD-sha> --build-id <run-id>-<attempt> --output /tmp/repoctl-artifacts
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The command reuses the native pnpm release plan for candidate explanations and base versions, then snapshots every public package together. Versions include the full source SHA and a deterministic digest of the PR/nightly build identity. The only tags are `snapshot-pr-<number>` and `snapshot-nightly`; there is no option to target a stable tag. Reuse a build identity only to recover the same source and artifacts. The report contains exact `pnpm add` instructions.
|
|
140
|
+
|
|
141
|
+
Snapshot planning through the CLI and API does not load or execute `repoctl.config.*`. It uses committed pnpm metadata independently of stable release configuration; ordinary release commands still validate their configuration before running.
|
|
142
|
+
|
|
143
|
+
Without `--publish`, the command prepares installable tarballs. It extracts committed HEAD outside the repository, installs the frozen lockfile, rewrites internal references to exact snapshot versions, runs the root `build` script, and checks the packed files with publint, Are the Types Wrong, and isolated consumers. Original manifests, intents, ledger, changelogs and Git refs stay unchanged on success, failure or interruption. Failed and completed runs retain `snapshot-report.json`, tarballs, and diagnostic consumers under the reported directory. A hard interruption can leave partial output; rerun the same identity to reconcile registry evidence. Source symlinks, Git submodules and `publishConfig.directory` are currently rejected; the root build script and frozen lockfile are required.
|
|
144
|
+
|
|
145
|
+
Add `--publish` only in an explicitly authorized GitHub Actions job with `REPOCTL_SNAPSHOT_PUBLISH=1`. `GITHUB_SHA` must match the requested commit and the event repository must match `GITHUB_REPOSITORY`. PR publication requires a same-repository `pull_request` event; forks and `pull_request_target` cannot publish. Nightly publication accepts `schedule` or `workflow_dispatch`. Keep fork jobs on preparation only and scope credentials to the trusted publishing job. Existing stable and prerelease workflows are not changed.
|
|
146
|
+
|
|
147
|
+
Publication defaults to the public npm registry; `--registry <https-url>` explicitly selects another registry (HTTP is supported only for a local test registry). Uploads reuse the bounded publication/recovery adapter and never create GitHub Releases or Git tags. Repeated identities compare exact package metadata and SHA-512 tarball integrity before skipping an upload. Unknown registry state or conflicting bytes stop publication. A changed or non-reproducible build needs a new `--build-id`.
|
|
148
|
+
|
|
149
|
+
The public APIs are `createSnapshotPlan(options)` and `releaseSnapshot(options)`. Both expose `schemaVersion: 1`, the native plan, snapshot identity, candidates, exact versions, target tag and install instructions. `releaseSnapshot` adds retained artifacts and validation results; preparation failures return `status: failed` with `error`, and the CLI exits with code 1.
|
|
150
|
+
|
|
85
151
|
## Next
|
|
86
152
|
|
|
87
153
|
Read [publishing and changelogs](/learn/monorepo/publish) for repository policy and [reports and output](/tasks/reports) for CI artifacts.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# 准备构建上下文与生产产物
|
|
2
|
+
|
|
3
|
+
`repo workspace prepare` 预览一个明确的工作区包。`prune` 调用本地 Turbo 2,生成部分 monorepo 与裁剪后的锁文件;`deploy` 调用根 `packageManager` 锁定的 pnpm,生成生产目录。命令只准备本地文件,不运行应用、不构建镜像,也不连接部署平台。
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
# 先显式构建所需产物。
|
|
7
|
+
pnpm build
|
|
8
|
+
repo workspace prepare @acme/service --mode prune --out ../service-build --json > ../prune-plan.json
|
|
9
|
+
repo workspace prepare --apply ../prune-plan.json
|
|
10
|
+
|
|
11
|
+
# 保留 Turbo 原生的 Docker full/json 缓存层。
|
|
12
|
+
repo workspace prepare ./apps/service --mode prune --docker --out ../docker-context --json
|
|
13
|
+
|
|
14
|
+
# 只使用本地存储,生成包含生产 workspace 依赖的目录。
|
|
15
|
+
repo workspace prepare @acme/service --mode deploy --entry dist/server.js --offline --out ../service-runtime --json > ../deploy-plan.json
|
|
16
|
+
repo workspace prepare --apply ../deploy-plan.json
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
预览始终只读,`--dry-run` 用于明确默认行为。原生版本、能力与路径设置探测不会安装工具。JSON 计划必须保存在源工作区外,审查后另行 `--apply`;应用不能混用目标、预览选项或 `--dry-run`。
|
|
20
|
+
|
|
21
|
+
## 原生行为与前置条件
|
|
22
|
+
|
|
23
|
+
两种模式都要求已有 pnpm 工作区和 `pnpm-lock.yaml`。使用准确包名或 `./目录` 选择唯一、具名的非根包,不支持 glob、多包匹配或依赖选择表达式。输出必须位于源工作区之外,父目录必须已存在;链接目标、重叠目录和无关的非空目录会被拒绝,没有强制覆盖选项。
|
|
24
|
+
|
|
25
|
+
Prune 支持本地已安装的稳定 Turbo 2,保留原生构建依赖选择规则。`manifestCandidates` 仅解释清单关系,实际保留哪些包由 Turbo 决定。任务依赖、global 文件是否纳入,遵从对应版本和配置。`--docker` 保留原生 `full`、`json` 与锁文件布局。
|
|
26
|
+
|
|
27
|
+
Deploy 支持明确锁定的稳定 pnpm 10、11、12,且原生命令必须提供所需选项。执行使用 `--prod`、`--ignore-scripts`、`--frozen-lockfile`,并禁用 pnpmfile hooks。`--legacy` 显式选择原生旧模式;该模式的生产目录可能没有根锁文件,只有 prune 要求产物包含该文件。pnpm 12.2 起支持非注入的 workspace 依赖;更早版本可能需要 `injectWorkspacePackages` 或 legacy 模式。peer 歧义和其他原生错误保留诊断代码并导致失败,repoctl 不自行解析或重写依赖图、锁文件。
|
|
28
|
+
|
|
29
|
+
部署前会验证已构建的运行入口:显式 `--entry`、`package.main` 或唯一 `bin`。如果原生 `files`/ignore 规则未包含入口,准备失败。不会解释或执行任意 `start` 脚本。依赖生命周期脚本保持关闭;需要的原生二进制应事先准备。准备产物不等于验证应用全部运行行为。
|
|
30
|
+
|
|
31
|
+
## 源目录与产物边界
|
|
32
|
+
|
|
33
|
+
原生工具运行在隔离的源码副本中,不复制源 `node_modules`、`.git`、`.turbo` 和 `.repoctl` 存储。复制前排除真实 `.env`/`.env.*`、`.npmrc`、`.netrc`、`_netrc`、Yarn 认证配置及 `.aws`、`.ssh`、`.gnupg` 路径,保留公开的 `.env.example`、`.env.sample`。产物再次检查这些路径。这是明确文件名规则,不是通用秘密检测器;示例文件应使用公开占位值,部署凭据另行提供。
|
|
34
|
+
|
|
35
|
+
源码副本保留被 Git 忽略的构建文件,最终是否纳入仍由原生 prune/打包规则决定。源码链接和特殊文件会被拒绝。输入和输出清单限制为 100,000 项、1 GiB;需要其他复制或存储策略的工作区可直接使用原生命令。
|
|
36
|
+
|
|
37
|
+
命令读取 pnpm 有效 store/cache/state/module/lockfile 路径,不输出含凭据的完整配置。会写回源工作区的配置在 deploy 前被拒绝,module/lockfile 存储必须保持产物内的相对路径。暂存目录也必须在源工作区之外;工作区外的原生全局存储仍由 pnpm 管理。
|
|
38
|
+
|
|
39
|
+
产物链接只能指向产物内部。相对链接保留;Windows 目录 junction 会针对最终产物目录重新创建。传输 Windows 产物时,使用会复制 junction 内容的复制/归档方式。集成验收会复制目录、移除源码和原产物,再独立执行生产入口。
|
|
40
|
+
|
|
41
|
+
## 审查、恢复与重复应用
|
|
42
|
+
|
|
43
|
+
计划包含源文件、权限、工具/版本、相关路径设置与选项的指纹。应用重新生成计划并拒绝漂移,在复制后、原生执行后、发布中及完成前复核源输入,并保留并发编辑。
|
|
44
|
+
|
|
45
|
+
原生产物先进入暂存目录。发布以独占方式创建文件,记录归属和已写内容,最后提交 `.repoctl-artifact.json`。提交前文件可能已经可见,不应消费不完整目录。失败只移除本操作写入且未被修改的内容,保留并发文件并报告准确路径。成功后仅清理失败时,通过 `cleanupPending` 报告,不谎报回滚。
|
|
46
|
+
|
|
47
|
+
重复应用只有在完整 receipt 与文件清单仍匹配时才返回 `unchanged`。未知或修改过的非空目录从不覆盖。`.repoctl/workspace-artifacts.lock` 覆盖重复检查、原生执行、发布与清理。进程中断后,先确认没有活动写入进程,检查并恢复不完整产物与保留暂存路径,再移除锁。
|
|
48
|
+
|
|
49
|
+
公共 API:`planWorkspaceArtifact(cwd, options)`、`applyWorkspaceArtifactPlan(cwd, plan, { signal, timeoutMs })`;JSON schema 为 1。原生执行默认两分钟超时,API 可配置至十分钟。
|
|
50
|
+
|
|
51
|
+
参考:[Turbo prune](https://turborepo.com/docs/reference/prune)、[pnpm deploy](https://pnpm.io/cli/deploy)。
|