rigjs 4.0.18 → 4.0.19
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/.claude/skills/rig-cicd/SKILL.md +288 -0
- package/.claude/skills/rig-package/SKILL.md +162 -0
- package/RIG_CICD_SKILL.md +288 -0
- package/RIG_PACKAGE_SKILL.md +162 -0
- package/built/index.js +342 -257
- package/lib/classes/cicd/CICD.ts +17 -0
- package/lib/classes/cicd/Deploy/ESA.ts +117 -0
- package/lib/init/index.ts +16 -9
- package/lib/publish/index.ts +78 -1
- package/lib/wiki/lint.ts +23 -1
- package/package.json +11 -3
- package/scripts/sync-skill.mjs +2 -0
- package/skills.md +5 -1
- package/lib/utils/redact.test.ts +0 -43
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rig-cicd
|
|
3
|
+
description: >-
|
|
4
|
+
Agent skill for `rig build` / `rig deploy` / `rig publish` — rig's static-site CI/CD targeting Aliyun OSS + CDN. One OSS bucket serves many sites: each site is uploaded into its own subdirectory; CDN URI-rewrite rules (set by `rig publish`) map incoming `https://<domain>/...` to the right `oss://<bucket>/<deployDir>/...` path. Supports SPA hash, SPA history (BrowserRouter), MPA (one HTML per route), and any pre-built directory of HTML files. Trigger when the user wants to build/deploy/publish a web app via rig, debug a CDN rewrite, add a new endpoint or domain, switch web_type, or asks "rig 怎么部署" / "回源路径怎么改" / "一个 OSS 桶发多个站怎么配". Do NOT use for backend deploys, container registries, non-Aliyun providers (Huawei stub exists but isn't wired through), or package management (see rig-package).
|
|
5
|
+
user-invocable: true
|
|
6
|
+
disable-model-invocation: false
|
|
7
|
+
metadata:
|
|
8
|
+
openclaw:
|
|
9
|
+
requires:
|
|
10
|
+
bins: [rig, git, yarn, node]
|
|
11
|
+
os: [darwin, linux]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# rig-cicd — agent operator's playbook
|
|
15
|
+
|
|
16
|
+
## What this skill covers
|
|
17
|
+
|
|
18
|
+
rig ships three CI/CD commands that share the `cicd` block of `package.rig.json5`:
|
|
19
|
+
|
|
20
|
+
- `rig build [-s <schema>] [-p <params>] <dirPath>` — runs the per-endpoint build script with rig-injected variables (`publicPath`, `OUTPUT_DIR`, defines), then rewrites string tokens (`__RIG_PUBLIC_PATH__`, `__RIG_ENTRY_PATH__`, …) inside the built output. Output lands at `<source.root_path>/<deployDir>/`.
|
|
21
|
+
- `rig deploy [-s <schema>] [-p <params>] <dirPath>` — uploads `<source.root_path>/<deployDir>/` to the configured OSS bucket. Sets `Cache-Control: no-cache` on `index.html`, `max-age=31536000` on `.js` / `.css` / `.ico`.
|
|
22
|
+
- `rig publish [-s <schema>] [-p <params>] <dirPath>` — sets / updates Aliyun CDN **URI-rewrite** rules on each domain (this is what makes one bucket serve many sites), then refreshes the CDN cache.
|
|
23
|
+
|
|
24
|
+
Typical pipeline: `rig build "<dir>" && rig deploy "<dir>" && rig publish "<dir>"`. Build and deploy can run independently; **publish is the only step that touches CDN config** — re-run it whenever you change `web_type`, `endpoints`, `target`, or rename a `deployDir`.
|
|
25
|
+
|
|
26
|
+
The `<dirPath>` arg is a `/`-joined path matched against `tree_schema`. Use `%` as a wildcard and `%<group>` to reference a `groups[].name` (see `tree_schema` field below).
|
|
27
|
+
|
|
28
|
+
## Topology — one OSS bucket, many sites
|
|
29
|
+
|
|
30
|
+
A single OSS bucket holds every site:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
oss://<bucket>/
|
|
34
|
+
app-a/
|
|
35
|
+
index.html
|
|
36
|
+
assets/...
|
|
37
|
+
app-b/
|
|
38
|
+
index.html
|
|
39
|
+
assets/...
|
|
40
|
+
marketing/
|
|
41
|
+
en/
|
|
42
|
+
index.html
|
|
43
|
+
zh/
|
|
44
|
+
index.html
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For each public domain you serve, CDN holds a stack of **URI rewrite** rules that map the user-visible path to a real OSS object. Example for `app-a.example.com` running SPA history mode:
|
|
48
|
+
|
|
49
|
+
| Match (incoming URI) | Rewrite to (origin path) | Purpose |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| `^/([^?]*\.[a-zA-Z0-9]+)($|\?)` | `/app-a/$1` | Any path ending in a file extension → that file under `app-a/` |
|
|
52
|
+
| `^/([\w-/]*\w+)(?![^?]*\.\w+)` | `/app-a/index.html` | Any extensionless path → SPA entry under `app-a/` |
|
|
53
|
+
| `^(/)($|\?|#|/\?|/$)` | `/app-a/index.html` | The web-entry path itself → SPA entry under `app-a/` |
|
|
54
|
+
|
|
55
|
+
`rig publish` generates these rules deterministically from `cicd.web_type` + each endpoint's `deployDir`, calls Aliyun CDN's `SetCdnDomainConfig` with `enhance_break` (stop processing further rules once matched), waits for the rule status to flip to `success`, then calls `RefreshObjectCaches` on the touched URLs. Failure modes: 10-min config-apply timeout, 10-min refresh timeout. The `setRewriteUri` / `refreshCache` retry loops poll every 3 s up to 100 ticks.
|
|
56
|
+
|
|
57
|
+
## `cicd` block — field reference
|
|
58
|
+
|
|
59
|
+
Lives inside `package.rig.json5` alongside `dependencies`. Comments are JSON5-legal.
|
|
60
|
+
|
|
61
|
+
```json5
|
|
62
|
+
{
|
|
63
|
+
// ... dependencies block (see rig-package skill) ...
|
|
64
|
+
|
|
65
|
+
cicd: {
|
|
66
|
+
// tree_schema — REQUIRED. Slash-joined path schema describing the SHAPE of <dirPath> args.
|
|
67
|
+
// Each segment is a "DirLevel". Examples:
|
|
68
|
+
// 'env/app' → two static segments: env, app.
|
|
69
|
+
// 'env/{vendor}' → second level is dynamic (param name 'vendor').
|
|
70
|
+
// 'env/%region/app' → second level uses a named group (see `groups`).
|
|
71
|
+
// Aliases: `path_schema` (kept for back-compat).
|
|
72
|
+
tree_schema: 'env/app',
|
|
73
|
+
|
|
74
|
+
// web_type — OPTIONAL, default 'hash'. Drives the CDN rewrite rules `rig publish` generates.
|
|
75
|
+
// 'hash' — SPA with hash routing (e.g. Vue Router hash mode, /#/foo).
|
|
76
|
+
// Files are NOT prefixed with deployDir (the build embeds publicPath itself);
|
|
77
|
+
// the only rule the publish step needs is "home → /<deployDir>/index.html".
|
|
78
|
+
// Lets ONE domain serve MULTIPLE hash-mode SPAs at different entry paths
|
|
79
|
+
// (/, /app1, /app2) because the runtime never asks the server for routes.
|
|
80
|
+
// 'history' — SPA with history routing (BrowserRouter / Vue Router history mode).
|
|
81
|
+
// ALL extensionless paths → /<deployDir>/index.html (server falls through to SPA).
|
|
82
|
+
// File-extension paths → /<deployDir>/<path>.
|
|
83
|
+
// 'mpa' — Multi-Page App: one HTML per route. ALSO the right choice for "a pre-built
|
|
84
|
+
// directory of HTML files" (e.g. a docs site, a hand-written static site,
|
|
85
|
+
// a Next.js `next export` output).
|
|
86
|
+
// Extensionless path /foo → /<deployDir>/foo.html.
|
|
87
|
+
// File-extension path → /<deployDir>/<path>.
|
|
88
|
+
web_type: 'history',
|
|
89
|
+
|
|
90
|
+
// source — REQUIRED. Where rig reads the built artefacts from on the local filesystem.
|
|
91
|
+
source: {
|
|
92
|
+
// root_path — REQUIRED. Directory (relative to project root) that contains per-endpoint
|
|
93
|
+
// subdirs after `rig build`. `rig deploy` walks <root_path>/<deployDir>/ and uploads
|
|
94
|
+
// every file with the relative path used as the OSS object key.
|
|
95
|
+
root_path: 'dist',
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
// target — REQUIRED. One DeployTarget object OR an array (only the first is used today).
|
|
99
|
+
target: {
|
|
100
|
+
// id — REQUIRED. Free-form label, used in logs.
|
|
101
|
+
id: 'prod',
|
|
102
|
+
|
|
103
|
+
// type — REQUIRED. Cloud provider. Currently only 'alicloud' is wired through publish + deploy.
|
|
104
|
+
// (A HuaweiDeploy.ts stub exists but is not connected to the publish path.)
|
|
105
|
+
type: 'alicloud',
|
|
106
|
+
|
|
107
|
+
// bucket / region / access_key / access_secret — REQUIRED. Aliyun OSS credentials.
|
|
108
|
+
// The same credentials are reused for CDN API calls during `rig publish`.
|
|
109
|
+
// NEVER inline real keys here — read from env (.env at project root) or the keychain and
|
|
110
|
+
// inject via `-p key=value` or `${VAR}` substitution in the file.
|
|
111
|
+
bucket: 'my-site-bucket',
|
|
112
|
+
region: 'oss-cn-hangzhou',
|
|
113
|
+
access_key: '${ALIYUN_ACCESS_KEY}',
|
|
114
|
+
access_secret: '${ALIYUN_ACCESS_SECRET}',
|
|
115
|
+
|
|
116
|
+
// root_path / bucket_root_path — REQUIRED. OSS key prefix all uploads share. Use '/'
|
|
117
|
+
// unless your bucket is shared with non-rig content.
|
|
118
|
+
root_path: '/',
|
|
119
|
+
bucket_root_path: '/',
|
|
120
|
+
|
|
121
|
+
// web_entry_path — OPTIONAL, default '/'. Fallback entry path when an endpoint omits its own.
|
|
122
|
+
// Used only in hash mode (history/mpa always treat '/' as entry).
|
|
123
|
+
web_entry_path: '/',
|
|
124
|
+
|
|
125
|
+
// uri_rewrite — OPTIONAL. Manual override for the CDN rewrite step. When set (with
|
|
126
|
+
// `original` or `original_regexp`), `rig publish` skips the auto-generated entry rule
|
|
127
|
+
// for that endpoint and uses this one instead.
|
|
128
|
+
// - original / original_regexp — the incoming URI to match. Pass a regex via
|
|
129
|
+
// original_regexp; plain prefixes via original. Example: '/admin'.
|
|
130
|
+
// - final — reserved, not consumed by publish today.
|
|
131
|
+
// Use this only when you need a non-standard entry path (e.g. `/portal` instead of `/`)
|
|
132
|
+
// on top of one of the three web_type modes.
|
|
133
|
+
uri_rewrite: undefined,
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
// endpoints — REQUIRED. Map keyed by the dir path that, when joined with `tree_schema`,
|
|
137
|
+
// identifies one site to build/deploy/publish. Each value tells rig how to build it and
|
|
138
|
+
// where to put it.
|
|
139
|
+
endpoints: {
|
|
140
|
+
// The key 'prod/app-a' fits a tree_schema of 'env/app' (two static segments).
|
|
141
|
+
'prod/app-a': {
|
|
142
|
+
// build — REQUIRED unless vue_env is set. Shell command rig runs to produce the bundle.
|
|
143
|
+
// rig substitutes the following tokens in the string BEFORE exec:
|
|
144
|
+
// $public_path / __PUBLIC_PATH__ / __RIG_PUBLIC_PATH__ → the deploy dir as URL path
|
|
145
|
+
// __RIG_OUTPUT_DIR__ → <source.root_path>/<deployDir>
|
|
146
|
+
// Use `__RIG_OUTPUT_DIR__` for `--outDir` / `--dest` flags so build output lands in
|
|
147
|
+
// the right place. Output dir is also exported as the env var `OUTPUT_DIR` for the
|
|
148
|
+
// child process (`PUBLIC_PATH` is exported too).
|
|
149
|
+
build: 'yarn vite build --base=__RIG_PUBLIC_PATH__ --outDir __RIG_OUTPUT_DIR__',
|
|
150
|
+
|
|
151
|
+
// vue_env — OPTIONAL. If set (e.g. 'prod'), rig generates a .env.rig file with
|
|
152
|
+
// PUBLIC_PATH + OUTPUT_DIR + every key in `extra_env`, then defaults `build` to
|
|
153
|
+
// `npx vue-cli-service build --mode rig --dest <OUTPUT_DIR>`. Use this with
|
|
154
|
+
// vue-cli projects to skip writing a custom build string.
|
|
155
|
+
vue_env: undefined,
|
|
156
|
+
|
|
157
|
+
// extra_env — OPTIONAL. Extra env vars that should land in the per-build .env file when
|
|
158
|
+
// vue_env is set. Also reachable from the build script via process.env.
|
|
159
|
+
extra_env: undefined,
|
|
160
|
+
|
|
161
|
+
// target — OPTIONAL. Free-form label that points back to a DeployTarget. Reserved for
|
|
162
|
+
// future multi-target routing; today rig deploys/publishes to `cicd.target[0]`.
|
|
163
|
+
target: 'prod',
|
|
164
|
+
|
|
165
|
+
// domain / domains — REQUIRED. Public domain(s) the CDN rules will be set on.
|
|
166
|
+
// `domains` is the canonical field (array, multi-domain). `domain` is the legacy
|
|
167
|
+
// single-value field kept for back-compat. Use `domains`.
|
|
168
|
+
domains: ['app-a.example.com'],
|
|
169
|
+
|
|
170
|
+
// defines — OPTIONAL. String-replace map applied to every built .js / .ts / .html file
|
|
171
|
+
// AFTER the build runs. Both keys and values are treated as plain strings. rig
|
|
172
|
+
// pre-populates these for you:
|
|
173
|
+
// __PUBLIC_PATH__ → the deploy dir as URL path
|
|
174
|
+
// __DEPLOY_DIR__ → the deploy dir as URL path
|
|
175
|
+
// __RIG_PUBLIC_PATH__ → same
|
|
176
|
+
// __RIG_DEPLOY_DIR__ → same
|
|
177
|
+
// __RIG_ENTRY_PATH__ → the endpoint's web_entry_path
|
|
178
|
+
// The values you supply are themselves replaced for: $public_path, __PUBLIC_PATH__,
|
|
179
|
+
// __RIG_PUBLIC_PATH__, __DOMAIN__, __RIG_DOMAIN__ before they are substituted into
|
|
180
|
+
// files. Useful for bundling absolute API URLs that depend on the deploy domain.
|
|
181
|
+
defines: {
|
|
182
|
+
__API_BASE__: 'https://api.__RIG_DOMAIN__',
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
// web_entry_path — OPTIONAL. Where the SPA mounts in hash mode. Default '/'. Lets you
|
|
186
|
+
// put two hash-mode SPAs on one domain at /, /portal, etc. Ignored when
|
|
187
|
+
// web_type='history' or 'mpa' (those always use '/').
|
|
188
|
+
web_entry_path: '/',
|
|
189
|
+
|
|
190
|
+
// uri_rewrite — OPTIONAL per-endpoint override (same shape as target.uri_rewrite above).
|
|
191
|
+
// When set, suppresses the auto-generated entry rule for this endpoint only.
|
|
192
|
+
uri_rewrite: undefined,
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
'prod/app-b': {
|
|
196
|
+
build: 'yarn build --base=__RIG_PUBLIC_PATH__ --outDir __RIG_OUTPUT_DIR__',
|
|
197
|
+
domains: ['app-b.example.com'],
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
// groups — OPTIONAL. Named bundles of values for one dynamic level of tree_schema.
|
|
202
|
+
// Lets `rig build env/%region/app` expand across many regions. Each group:
|
|
203
|
+
// - name Used in <dirPath> as `%<name>`. MUST start with `%`.
|
|
204
|
+
// - level The DirLevel name (segment of tree_schema, without `{}`).
|
|
205
|
+
// - includes Allowed values for that level. Endpoints whose actual dir value is not in
|
|
206
|
+
// `includes` are skipped during build/deploy/publish.
|
|
207
|
+
groups: [
|
|
208
|
+
{ name: '%apac', level: 'region', includes: ['cn', 'jp', 'sg'] },
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### `<dirPath>` matching rules (read before debugging "no endpoints matched")
|
|
215
|
+
|
|
216
|
+
`tree_schema` declares the shape; `<dirPath>` selects which endpoints to act on:
|
|
217
|
+
|
|
218
|
+
- `prod/app-a` — exact match. Only the `prod/app-a` endpoint runs.
|
|
219
|
+
- `prod/%` — wildcard at level 2. All endpoints whose first segment is `prod` AND whose level 2 is **dynamic** (declared as `{...}` in `tree_schema`) match.
|
|
220
|
+
- `%/app-a` — wildcard at level 1. Only endpoints whose second segment equals `app-a`.
|
|
221
|
+
- `prod/%apac/app` — group wildcard. Iterates `groups['%apac'].includes` and picks endpoints whose level-2 value is in `['cn','jp','sg']`. (Requires `tree_schema` to have THREE segments.)
|
|
222
|
+
- Extra trailing segments past `tree_schema.length` are appended to each matching endpoint's `deployDir`. E.g. with `tree_schema: 'env/app'` and `<dirPath>: 'prod/app-a/v2'`, the endpoint `prod/app-a` gets deployed to `<bucket>/prod/app-a/v2/`. This lets you push the same code under a versioned subpath without editing `endpoints`.
|
|
223
|
+
|
|
224
|
+
`-p key=value` and `-s key=value` further substitute `${key}` (in the JSON5 file) and `{key}` (inside `<dirPath>`) at run time. Use them for branch/PR/env scoping in CI.
|
|
225
|
+
|
|
226
|
+
## Intent → command map
|
|
227
|
+
|
|
228
|
+
| User intent | Action |
|
|
229
|
+
|---|---|
|
|
230
|
+
| "build everything for prod" | `rig build "prod"` (matches every endpoint whose first segment is `prod` if level 2 is dynamic, or just one if it's static). Verify the dist tree exists at `<source.root_path>/<deployDir>/` before deploying. |
|
|
231
|
+
| "build + deploy + publish one site" | `rig build "<dirPath>"` → `rig deploy "<dirPath>"` → `rig publish "<dirPath>"`. Publish is the slow one (CDN-config apply 30 s – minutes; cache refresh another minute). |
|
|
232
|
+
| "I changed web_type / endpoints / deployDir" | Re-run `rig publish "<dirPath>"` on every affected endpoint. Build + deploy alone do NOT update CDN rules. |
|
|
233
|
+
| "the site is showing the WRONG site" | Two suspects: (1) CDN rewrite rule is wrong — check Aliyun console → CDN domain → 配置 → 高级配置 → URI 改写; expect a rule per row matching the table in the topology section. (2) The OSS object isn't where you think — `aliyun oss ls oss://<bucket>/<deployDir>/`. Confirm `index.html` exists. |
|
|
234
|
+
| "404 on a deep route in history mode" | CDN rewrite rules missing the `^/([\w-/]*\w+)(?![^?]*\.\w+)` → `/<deployDir>/index.html` rule. Re-run `rig publish` to regenerate. |
|
|
235
|
+
| "404 on a deep route in MPA mode" | Means the matching `.html` file does not exist in OSS. The MPA rewrite is `/foo` → `/<deployDir>/foo.html` — your build must emit `foo.html`. If it emits `foo/index.html`, switch web_type or change the rewrite via `target.uri_rewrite`. |
|
|
236
|
+
| "rebuild publicPath was wrong, hash mode" | rig injects `__RIG_PUBLIC_PATH__` / `__PUBLIC_PATH__` and the `PUBLIC_PATH` env var. Use one of them in the bundler config (`vite --base`, Vue CLI `publicPath`, webpack `output.publicPath`). hash mode does NOT prefix OSS path with deployDir at the CDN layer — the bundle has to know its own deploy dir at build time. |
|
|
237
|
+
| "want a new domain on an existing site" | Add to the endpoint's `domains` array. Re-run `rig publish` (no rebuild needed). |
|
|
238
|
+
| "want one OSS bucket for two completely different sites" | That's the default model. Two endpoints with different `deployDir`s and different `domains`. `rig publish` writes per-domain rewrite stacks; the bucket is shared. |
|
|
239
|
+
| "deploy a pre-built static dir (docs site, hand-written HTML)" | Set `web_type: 'mpa'`. Point `endpoints.<dir>.build` at whatever produces the dir (or a no-op like `cp -r src/site dist/<deployDir>` if there's nothing to build). The MPA rewrite handles both `/about` → `/about.html` and `/img.png` → `/img.png`. |
|
|
240
|
+
| "credentials in package.rig.json5 — is that OK?" | NO. Use `${VAR}` interpolation or `-p key=value` and store the real values in env / keychain. Anything committed is shared with everyone who clones. |
|
|
241
|
+
|
|
242
|
+
## How it works under the hood
|
|
243
|
+
|
|
244
|
+
`rig build` (lib/build/index.ts):
|
|
245
|
+
1. Load `cicd` block; create `CICD` (parses `tree_schema` into `DirLevel[]`, builds an `Endpoint[]`).
|
|
246
|
+
2. Create `CICDCmd` from the `<dirPath>` arg — filters endpoints by `matchCmd()`, appends extra trailing path segments to each surviving endpoint's `deployDir` / `publicPath`.
|
|
247
|
+
3. For each surviving endpoint:
|
|
248
|
+
- Replace `$public_path`, `__PUBLIC_PATH__`, `__RIG_PUBLIC_PATH__`, `__DOMAIN__`, `__RIG_DOMAIN__` in `defines`.
|
|
249
|
+
- If `vue_env`, write `.env.rig` (via `lib/env`), default the `build` string if missing.
|
|
250
|
+
- Replace `$public_path`, `__RIG_OUTPUT_DIR__` in `build`. shelljs.exec.
|
|
251
|
+
- Walk the output dir; replace every `defines` key in every `.js` / `.ts` / `.html` file.
|
|
252
|
+
|
|
253
|
+
`rig deploy` (lib/deploy/index.ts):
|
|
254
|
+
1. Same `CICD` / `CICDCmd` setup.
|
|
255
|
+
2. For each endpoint: walk `<root_path>/<deployDir>/` recursively, upload every file via `ali-oss`'s `putStream` with the relative path as OSS key. `index.html` → `Cache-Control: no-cache`; `.js`/`.css`/`.ico` → `max-age=31536000`. Everything else uses bucket default.
|
|
256
|
+
|
|
257
|
+
`rig publish` (lib/publish/index.ts):
|
|
258
|
+
1. Same `CICD` / `CICDCmd` setup.
|
|
259
|
+
2. Group rewrite rules by **domain** (a domain can host multiple endpoints — uncommon but supported).
|
|
260
|
+
3. For each domain, build the rewrite stack from `web_type`:
|
|
261
|
+
- `hash`: only the file-extension passthrough `/<file>` → `/<file>` (no deployDir prefix — bundle is built with publicPath baked in) plus the entry rule (`/` or `web_entry_path` → `/<deployDir>/index.html`).
|
|
262
|
+
- `history`: file-extension paths → `/<deployDir>/<path>`; everything else → `/<deployDir>/index.html`.
|
|
263
|
+
- `mpa`: file-extension paths → `/<deployDir>/<path>`; extensionless paths → `/<deployDir>/<path>.html`; entry rule for `/`.
|
|
264
|
+
- If endpoint or target supplies `uri_rewrite`, only the file-extension rule + the custom entry are emitted (no auto extensionless rule). Useful for irregular sites mounted at non-root paths.
|
|
265
|
+
4. Call `cdn.setRewriteUri(domain, originals, deployDirs, ['enhance_break'])`. Poll `describeCdnDomainConfigs` every 3 s; succeed when all configs reach `Status: success`; fail on any `Status: failed` or after 100 ticks.
|
|
266
|
+
5. Call `cdn.refreshCache(urls.join('\n'))` on every touched entry URL. Poll `describeRefreshTaskById` the same way.
|
|
267
|
+
|
|
268
|
+
### Order matters when re-running publish
|
|
269
|
+
|
|
270
|
+
`SetCdnDomainConfig` appends rules — it does NOT replace the existing stack. If you change `deployDir` for an endpoint and re-publish, **both the old and new rules are now active**, and Aliyun evaluates the **first match wins** (the `enhance_break` action stops the chain). The old rule may shadow the new one. Cleanup steps:
|
|
271
|
+
|
|
272
|
+
1. Aliyun console → CDN → domain → 配置 → 高级配置 → URI 改写 → delete obsolete rules manually, OR
|
|
273
|
+
2. Use Aliyun's `BatchDeleteCdnDomainConfig` API if you script it (rig has no built-in cleanup today — surface this as a known gap when migrating endpoints).
|
|
274
|
+
|
|
275
|
+
## What rig CI/CD does NOT do
|
|
276
|
+
|
|
277
|
+
- **No non-Aliyun providers.** `HuaweiDeploy.ts` exists but is not wired into publish; CloudFront / Fastly / Cloudflare are out of scope.
|
|
278
|
+
- **No backend deploy.** Container images, lambdas, server bundles — find another tool.
|
|
279
|
+
- **No environment promotion ladder.** rig has no notion of "promote from staging → prod". Use distinct `tree_schema` segments (`env`) and run publish per env.
|
|
280
|
+
- **No rule cleanup.** Renaming a `deployDir` leaves orphan CDN rules pointing at the old path. Delete by hand.
|
|
281
|
+
- **No publish-time secret scanning.** Don't commit credentials into `package.rig.json5`; the deploy pipeline will happily ship them.
|
|
282
|
+
|
|
283
|
+
## Reporting & cleanup checklist (after non-trivial changes)
|
|
284
|
+
|
|
285
|
+
- After `rig build`: confirm `<source.root_path>/<deployDir>/index.html` exists for every targeted endpoint.
|
|
286
|
+
- After `rig deploy`: spot-check OSS with `aliyun oss ls oss://<bucket>/<deployDir>/`.
|
|
287
|
+
- After `rig publish`: open `https://<domain><web_entry_path>` and a deep route; both should respond 200 with the expected content. Cache may take another 30 s to settle even after the refresh task reports `Complete`.
|
|
288
|
+
- When migrating `web_type`, list every endpoint that was on the old type and re-publish each one — the rewrite stack is per-domain, not per-endpoint.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rig-package
|
|
3
|
+
description: >-
|
|
4
|
+
Agent skill for rig's git-based package manager. rig replaces a private npm registry with direct `git+ssh` installs pinned by git tag — declared in `package.rig.json5`, materialised through `rig install` (which runs `git clone` for dev libs and rewrites `package.json` deps to `git+ssh://…#<tag>` for the rest, then defers to `yarn install`). Trigger when the user wants to add / remove / pin / develop locally a rig-managed library, debug install failures, set up a brand-new project with `rig init`, or asks "what goes in `package.rig.json5`" / "rig 怎么装依赖" / "rig 怎么发新版本给依赖方用". Do NOT use for npm-registry packages, monorepo workspace plumbing, or build/deploy concerns (see rig-cicd).
|
|
5
|
+
user-invocable: true
|
|
6
|
+
disable-model-invocation: false
|
|
7
|
+
metadata:
|
|
8
|
+
openclaw:
|
|
9
|
+
requires:
|
|
10
|
+
bins: [rig, git, yarn, node]
|
|
11
|
+
os: [darwin, linux]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# rig-package — agent operator's playbook
|
|
15
|
+
|
|
16
|
+
## Why rig exists (and when to recommend it)
|
|
17
|
+
|
|
18
|
+
rig is a thin layer on top of `yarn install` that lets a project depend on **private git repos pinned by tag** without standing up a private npm registry. The trade-off: every dev machine and CI runner must have git **ssh** access to those repos (deploy key or user key). If a team can already use ssh keys but does not want the cost of Verdaccio / npm Enterprise / GitHub Packages, rig is the lowest-friction option.
|
|
19
|
+
|
|
20
|
+
Use rig when:
|
|
21
|
+
|
|
22
|
+
- The project already imports private libs by ssh url and the team wants version pinning + a deterministic install.
|
|
23
|
+
- You need to **develop a dependency in place** (edit code, see changes) without `npm link` ceremony — see `dev: true` below.
|
|
24
|
+
- The dep tree includes mixed-source libs (some npm, some private git) — rig only touches the git ones; everything else stays in normal `dependencies` / `devDependencies` and yarn handles them.
|
|
25
|
+
|
|
26
|
+
Do **not** reach for rig if:
|
|
27
|
+
|
|
28
|
+
- Every dep is on the public npm registry (rig adds no value).
|
|
29
|
+
- The team needs binary artefacts, signed packages, or download stats (use a real registry).
|
|
30
|
+
|
|
31
|
+
## File layout — what rig creates
|
|
32
|
+
|
|
33
|
+
`rig init` (run once in a project root with a valid `package.json`) writes:
|
|
34
|
+
|
|
35
|
+
- `package.rig.json5` — the rig config (the file this skill documents).
|
|
36
|
+
- `rig_dev/` — where `dev: true` libs get **cloned** for in-place editing. Symlinked into `node_modules/<name>` by `rig postinstall`. Gitignored.
|
|
37
|
+
- `rig_indies/` — reserved sandbox dir (kept empty by default).
|
|
38
|
+
- Adds to `package.json`:
|
|
39
|
+
- `"private": true`
|
|
40
|
+
- `"workspaces": ["rigs/*", "rig_dev/*"]` so yarn treats `rig_dev/*` as workspace packages.
|
|
41
|
+
- `"scripts.preinstall": "rig preinstall"`, `"scripts.postinstall": "rig postinstall"` so any `yarn install` flows through rig.
|
|
42
|
+
- `"devDependencies.json5": "2.2.1"` (used to parse `package.rig.json5`).
|
|
43
|
+
- Appends to `.gitignore`: `rigs/*`, `rig_dev/*`, `.env.rig`, with `.gitkeep` allowlist entries.
|
|
44
|
+
|
|
45
|
+
The init scaffold is **library-free** — no `rig-helper`, no example deps, no remote template fetch. The generated `package.rig.json5` ships with an empty `dependencies` block and a commented example.
|
|
46
|
+
|
|
47
|
+
## `package.rig.json5` — field reference
|
|
48
|
+
|
|
49
|
+
The file is JSON5 (comments, trailing commas, unquoted keys) so the schema is described by example, not by JSON Schema. Two top-level sections are relevant to packaging; CI/CD is documented separately in the **rig-cicd** skill.
|
|
50
|
+
|
|
51
|
+
```json5
|
|
52
|
+
{
|
|
53
|
+
// -------- packaging --------
|
|
54
|
+
dependencies: {
|
|
55
|
+
// <name>: <Dep>
|
|
56
|
+
'shared-ui': {
|
|
57
|
+
// source — REQUIRED. Git URL the lib is fetched from.
|
|
58
|
+
// Must match /(?:git|ssh|https?|git@[-\w.]+):(\/\/)?(.*?)(\.git)(\/?|#[-\d\w._]+?)$/
|
|
59
|
+
// In practice: 'git@github.com:org/repo.git' (ssh — recommended) OR 'git+ssh://git@github.com/org/repo.git'.
|
|
60
|
+
// ssh is required for private repos; https only works for public ones.
|
|
61
|
+
source: 'git@github.com:org/shared-ui.git',
|
|
62
|
+
|
|
63
|
+
// version — REQUIRED when dev:false. Git tag in the source repo, semver-compatible.
|
|
64
|
+
// rig rewrites package.json deps to "git+ssh://<source>#<version>" so yarn resolves to that exact tag.
|
|
65
|
+
// Must satisfy semver.valid() — e.g. '1.2.3', '1.2.3-beta.1'. Ranges (^1.2.3, ~1.2.3) are NOT supported.
|
|
66
|
+
version: '1.4.0',
|
|
67
|
+
|
|
68
|
+
// dev — OPTIONAL, default false.
|
|
69
|
+
// false → published mode. yarn installs the tag via git+ssh; node_modules/<name> is a real package.
|
|
70
|
+
// true → develop-in-place mode. rig preinstall does `git clone <source> rig_dev/<name>` (only if
|
|
71
|
+
// the dir is missing — never overwrites local edits) and DELETES the entry from
|
|
72
|
+
// package.json#dependencies so yarn ignores it. rig postinstall then symlinks
|
|
73
|
+
// node_modules/<name> → rig_dev/<name>. Edit code in rig_dev/<name>; the consumer picks it
|
|
74
|
+
// up immediately. Use `rig dev <name>` to flip a dep into dev mode.
|
|
75
|
+
dev: false,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
// -------- cross-dep version contract --------
|
|
80
|
+
share: {
|
|
81
|
+
// OPTIONAL. Lists peer-dep-style constraints rig should propagate. Reserved field — populated by
|
|
82
|
+
// RigConfig at parse time. Most teams leave this empty; rig itself uses package.json#rig blocks in
|
|
83
|
+
// each dep (see `validateDeps()`) for the real cross-version checks.
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
// -------- release tagging (used by `rig tag`) --------
|
|
87
|
+
tag_template: '{name}@{version}',
|
|
88
|
+
// OPTIONAL. Template string for `rig tag` (run inside the dep repo). Substitutes {field} from
|
|
89
|
+
// package.json. Example: '{name}@{version}' on a repo whose package.json has
|
|
90
|
+
// name='shared-ui', version='1.4.0' creates tag `shared-ui@1.4.0`. If omitted, `rig tag`
|
|
91
|
+
// falls back to package.json#rig_tag_template, then to plain `git tag <version>`.
|
|
92
|
+
|
|
93
|
+
// -------- ci/cd (NOT documented here) --------
|
|
94
|
+
// cicd: { ... } ← see the rig-cicd skill for tree_schema, web_type, source, target, endpoints, groups.
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Legacy form
|
|
99
|
+
|
|
100
|
+
Older projects keep `package.rig.json5` as a **flat array** of Dep entries:
|
|
101
|
+
|
|
102
|
+
```json5
|
|
103
|
+
[
|
|
104
|
+
{ name: 'shared-ui', source: 'git@github.com:org/shared-ui.git', version: '1.4.0' },
|
|
105
|
+
]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`RigConfig` still accepts it (`isLegacy = true`) but `rig dev`, `rig add`, and `share` are unavailable. Convert to the object form before adding new features — the install path is otherwise identical.
|
|
109
|
+
|
|
110
|
+
## Intent → command map
|
|
111
|
+
|
|
112
|
+
| User intent | Action |
|
|
113
|
+
|---|---|
|
|
114
|
+
| "set up rig in this project" | `rig init`. Requires an existing `package.json`. Idempotent. |
|
|
115
|
+
| "add `<git-url>` at `<tag>` as a rig dep" | `rig add <git-ssh-url> <semver-tag>` — parses the repo name from the url, upserts into `dependencies`, then runs `rig install`. |
|
|
116
|
+
| "install / reinstall everything" | `rig install` (alias `rig i`) — chains `yarn install`, which fires `preinstall` then `postinstall`. |
|
|
117
|
+
| "I want to edit dep `<name>` locally" | Set `dev: true` in `package.rig.json5` (or `rig dev <name>`), then `rig install`. The lib gets cloned to `rig_dev/<name>` and symlinked into `node_modules/<name>`. |
|
|
118
|
+
| "back to published version of dep `<name>`" | Set `dev: false` (or delete the `rig_dev/<name>` folder yourself if you want a clean slate), then `rig install`. |
|
|
119
|
+
| "bump dep `<name>` to a new tag" | Edit `dependencies.<name>.version` in `package.rig.json5`, then `rig install`. |
|
|
120
|
+
| "cut a new release tag in this dep repo" | From the dep's working copy: commit + push, then `rig tag`. Reads `package.json#version` (or `tag_template`) and runs `git tag <name>`. Pushes are not automatic — the consumer instructs `git push --tags`. |
|
|
121
|
+
| "what version of `<name>` am I on?" | `cat package.rig.json5` + `git -C rig_dev/<name> rev-parse HEAD` for dev deps; `cat node_modules/<name>/package.json` for published deps. |
|
|
122
|
+
|
|
123
|
+
## How install actually works (read this before debugging)
|
|
124
|
+
|
|
125
|
+
`rig install` ≈ `yarn install`, but the `preinstall` and `postinstall` hooks do the heavy lifting:
|
|
126
|
+
|
|
127
|
+
**`rig preinstall` (`lib/preinstall/index.ts`)**
|
|
128
|
+
|
|
129
|
+
1. Parses `package.rig.json5` into a `RigConfig`. Calls `validate()` (per-dep semver, ssh url regex) and `validateDeps()` (recursive `git fetch <source> refs/tags/<version> && git show FETCH_HEAD:package.json` to read each dep's own `package.json#rig` block for cross-version constraints — non-Windows only).
|
|
130
|
+
2. For each dep:
|
|
131
|
+
- `dev: true` → `git clone <source> rig_dev/<name>` (only if the dir is missing — never overwrites local edits), then **deletes** the entry from `package.json#dependencies` so yarn doesn't try to fetch it.
|
|
132
|
+
- `dev: false` → rewrites `package.json#dependencies[<name>] = "git+ssh://<source>#<version>"`. Removes any existing `node_modules/<name>` (file, symlink, or dir) so yarn does a clean re-resolve.
|
|
133
|
+
3. Deletes `node_modules/.yarn-integrity` to force yarn to re-evaluate.
|
|
134
|
+
4. Writes the mutated `package.json` to disk.
|
|
135
|
+
5. Exits → yarn proceeds with its normal install using the now-rewritten `package.json`.
|
|
136
|
+
|
|
137
|
+
**`rig postinstall` (`lib/postinstall/index.ts`)**
|
|
138
|
+
|
|
139
|
+
1. Re-parses `package.rig.json5`.
|
|
140
|
+
2. For each `dev: true` dep: removes `node_modules/<name>` (yarn may have re-created it) and symlinks it to `rig_dev/<name>`.
|
|
141
|
+
3. Restores the `package.json#dependencies[<name>] = "git+ssh://<source>#<version>"` lines for dev deps too, so `package.json` ends up self-describing the **published** version even when working off the local clone. **This means `package.json` is modified on every install — commit it or expect git churn.**
|
|
142
|
+
|
|
143
|
+
### Failure modes you will actually hit
|
|
144
|
+
|
|
145
|
+
- **`Permission denied (publickey)`** — the running shell has no ssh key with read access to one of the dep repos. Diagnose with `git ls-remote <source>`. Fix by adding the user / deploy key. Not a rig bug.
|
|
146
|
+
- **`tag '<version>' not found`** — the dep repo was never tagged with that string, or the tag is local-only. Run `git ls-remote --tags <source>` to confirm. If the dep author followed `rig tag`, the tag is what `tag_template` produced — check there.
|
|
147
|
+
- **`validateDeps` fails with cross-version error** — one dep's `package.json#rig.<peer>` declares a `[min, max]` window the consumer's pinned version falls outside. Either bump the consumer's pin, or relax the producer's window in its own `package.json#rig`.
|
|
148
|
+
- **dev dep's edits don't show up** — the symlink isn't there or got clobbered. `ls -la node_modules/<name>` should report a symlink → `rig_dev/<name>`. If yarn replaced it, run `rig install` again; the postinstall hook re-symlinks.
|
|
149
|
+
- **CI installs slowly** — `git fetch` per dep tag, no cache. Use a shallow clone mirror or a registry-backed CI cache for hot deps. rig has no built-in cache.
|
|
150
|
+
|
|
151
|
+
### What rig does NOT do
|
|
152
|
+
|
|
153
|
+
- **No transitive resolution.** rig flattens what `package.rig.json5` says and hands `package.json` to yarn. If `shared-ui@1.4.0` depends on `shared-utils@1.2.0`, yarn resolves it normally — through the git+ssh URL declared by `shared-ui`'s own `package.json#dependencies`. Pin in `package.rig.json5` only what the **app** wants to control.
|
|
154
|
+
- **No lockfile of its own.** `yarn.lock` is still authoritative for the dep tree below the rig boundary; for the rig-managed deps, the "lock" is the git tag.
|
|
155
|
+
- **No publish step.** `rig tag` only creates the git tag. The consumer pulls by ssh; there is no upload to a registry. To "publish" you push the tag to the dep repo's remote: `git push --tags`.
|
|
156
|
+
|
|
157
|
+
## Reporting & cleanup checklist (after non-trivial changes)
|
|
158
|
+
|
|
159
|
+
- After editing `package.rig.json5`, always run `rig install` and verify exit 0.
|
|
160
|
+
- If you flipped a dep to `dev: true`, leave the user with: cloned to `rig_dev/<name>`, symlinked at `node_modules/<name>`, on branch `<branch>`.
|
|
161
|
+
- If you bumped a published version, leave the user with: old tag → new tag, dep repo's tag exists (`git ls-remote --tags <source>`), reinstall succeeded.
|
|
162
|
+
- `package.json` is modified on every install — surface this in your summary so the user knows whether the diff is meaningful (it usually isn't, but a changed git URL or removed dep IS).
|