@kici-dev/compiler 0.1.22 → 0.1.23
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/dist/cli.js +20 -6
- package/dist/commands/compile.d.ts +6 -0
- package/dist/commands/compile.js +6 -3
- package/dist/commands/docs.d.ts +8 -8
- package/dist/commands/docs.js +35 -16
- package/dist/commands/org.js +2 -2
- package/dist/commands/run.d.ts +16 -1
- package/dist/commands/run.js +86 -14
- package/dist/commands/test.d.ts +4 -0
- package/dist/commands/types.d.ts +2 -0
- package/dist/commands/types.js +1 -1
- package/dist/fixtures/describe-event.d.ts +6 -0
- package/dist/fixtures/describe-event.js +18 -0
- package/dist/fixtures/picker.d.ts +19 -0
- package/dist/fixtures/picker.js +64 -0
- package/dist/llm-context/llms-architecture.txt +1440 -0
- package/dist/llm-context/llms-cli.txt +2386 -0
- package/dist/llm-context/llms-features.txt +2389 -0
- package/dist/llm-context/llms-full.txt +976 -317
- package/dist/llm-context/llms-getting-started.txt +519 -0
- package/dist/llm-context/llms-patterns.txt +1324 -0
- package/dist/llm-context/llms-providers.txt +805 -0
- package/dist/llm-context/llms-sdk.txt +3725 -0
- package/dist/llm-context/llms.txt +13 -0
- package/dist/local-executor/index.js +40 -3
- package/dist/local-executor/job-runner.d.ts +2 -0
- package/dist/local-executor/job-runner.js +36 -4
- package/dist/local-executor/types.d.ts +2 -0
- package/dist/lockfile/generator.js +13 -4
- package/dist/remote/platform-client.d.ts +6 -0
- package/dist/remote/uploader.js +1 -0
- package/dist/templates/package-json.js +1 -1
- package/dist/test-runner/rule-evaluator.d.ts +1 -1
- package/dist/test-runner/rule-evaluator.js +2 -1
- package/dist/test-runner/step-context.d.ts +1 -1
- package/dist/test-runner/step-context.js +7 -2
- package/dist/types.d.ts +6 -2
- package/package.json +4 -4
- package/sbom.spdx.json +35 -35
|
@@ -0,0 +1,2386 @@
|
|
|
1
|
+
# KiCI CLI and authoring
|
|
2
|
+
|
|
3
|
+
This bundle covers: Running the CLI: compile, test, run local/remote, auth, hooks, lock-file drift.
|
|
4
|
+
|
|
5
|
+
## CLI authentication
|
|
6
|
+
|
|
7
|
+
Source: https://docs.kici.dev/user/cli-auth/
|
|
8
|
+
|
|
9
|
+
The KiCI CLI supports three authentication methods: browser-based OAuth (default), device authorization flow (for headless environments), and API key paste (for CI/CD pipelines).
|
|
10
|
+
|
|
11
|
+
## Authentication methods
|
|
12
|
+
|
|
13
|
+
### Browser OAuth (default)
|
|
14
|
+
|
|
15
|
+
The default `kici login` flow:
|
|
16
|
+
|
|
17
|
+
1. Opens your default browser to the KiCI identity provider
|
|
18
|
+
2. You authenticate in the browser
|
|
19
|
+
3. The CLI receives a token via localhost callback
|
|
20
|
+
4. A personal access token (PAT) is created and stored locally
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
kici login
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The CLI auto-detects headless environments (SSH sessions, CI runners) and switches to device flow automatically.
|
|
27
|
+
|
|
28
|
+
### Device flow (headless)
|
|
29
|
+
|
|
30
|
+
For environments without a browser (SSH, remote servers):
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
kici login --device
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
This displays a URL and a code. Open the URL on any device, enter the code, and authenticate. The CLI polls for completion.
|
|
37
|
+
|
|
38
|
+
### API key paste
|
|
39
|
+
|
|
40
|
+
For CI/CD pipelines and automated environments, paste an API key directly:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
kici login --token kici_sk_abc123...
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The API key (starts with `kici_sk_`) is passed directly as the flag value and stored in your local config file.
|
|
47
|
+
|
|
48
|
+
## kici logout
|
|
49
|
+
|
|
50
|
+
Revoke your PAT and clear local authentication:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
kici logout
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This:
|
|
57
|
+
|
|
58
|
+
1. Revokes the PAT on the server (preventing further use)
|
|
59
|
+
2. Clears auth fields from the local config file
|
|
60
|
+
3. Preserves non-auth settings (active org, default clusters, Platform endpoint)
|
|
61
|
+
|
|
62
|
+
## Organization management
|
|
63
|
+
|
|
64
|
+
### List organizations
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
kici org list
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Shows all organizations you belong to, with your role in each. The active organization is marked with an asterisk.
|
|
71
|
+
|
|
72
|
+
### Switch active organization
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
kici org use <name-or-id>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Name matching is case-insensitive. You can also use the organization ID directly.
|
|
79
|
+
|
|
80
|
+
The active organization is both the scope for org-scoped commands (`kici runs list`, `kici diagnostics`, `kici secrets list`, …) **and** the default target for `kici run remote`. After `kici login` and `kici org use <org>`, `kici run remote` dispatches to that org through the Platform — that is the complete path to a remote run. Override the target for a single run with `kici run remote --org <id>`.
|
|
81
|
+
|
|
82
|
+
If an organization has more than one connected orchestrator cluster, set its default cluster once with `kici orchestrators use <name>` (list them with `kici orchestrators list`). `kici run remote` then targets that cluster unless you pass `--orchestrator <name>`. With a single connected orchestrator the cluster is selected automatically.
|
|
83
|
+
|
|
84
|
+
### Show current organization
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
kici org current
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Displays the currently active organization name and ID.
|
|
91
|
+
|
|
92
|
+
## Auth status
|
|
93
|
+
|
|
94
|
+
`kici org current` shows your current login state and active organization:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
kici org current
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
It reports whether you are logged in and which organization is active. PAT
|
|
101
|
+
expiry and the full list of your tokens are managed from the dashboard (see
|
|
102
|
+
"Dashboard management" below).
|
|
103
|
+
|
|
104
|
+
## Personal access tokens
|
|
105
|
+
|
|
106
|
+
Personal access tokens (PATs) are created automatically when you log in via OAuth. You can also create and manage PATs through the dashboard.
|
|
107
|
+
|
|
108
|
+
### How PATs work
|
|
109
|
+
|
|
110
|
+
- **User-scoped**: PATs work across all organizations you belong to
|
|
111
|
+
- **120-day default expiry**: Configurable when creating from the dashboard
|
|
112
|
+
- **Named per machine**: Each login creates a PAT named after the machine hostname
|
|
113
|
+
- **Permission inheritance**: PATs inherit your effective role permissions in each org
|
|
114
|
+
|
|
115
|
+
### PATs vs API keys
|
|
116
|
+
|
|
117
|
+
| | Personal access tokens | API keys |
|
|
118
|
+
| ---------- | ---------------------- | --------------- |
|
|
119
|
+
| Scope | User (cross-org) | Organization |
|
|
120
|
+
| Prefix | `kici_pat_` | `kici_sk_` |
|
|
121
|
+
| Created by | CLI login or dashboard | Dashboard |
|
|
122
|
+
| Expiry | 120 days (default) | No expiry |
|
|
123
|
+
| Use case | Developer CLI access | CI/CD pipelines |
|
|
124
|
+
|
|
125
|
+
### Dashboard management
|
|
126
|
+
|
|
127
|
+
Create, view, and revoke PATs from the dashboard:
|
|
128
|
+
|
|
129
|
+
1. Click your avatar in the sidebar
|
|
130
|
+
2. Select **Account settings**
|
|
131
|
+
3. Navigate to the **Personal access tokens** tab
|
|
132
|
+
|
|
133
|
+
From here you can:
|
|
134
|
+
|
|
135
|
+
- Create PATs with custom names and expiry periods
|
|
136
|
+
- View active PATs with their prefixes and expiry dates
|
|
137
|
+
- Revoke PATs that are no longer needed
|
|
138
|
+
|
|
139
|
+
## Reaching the Platform API directly
|
|
140
|
+
|
|
141
|
+
The Platform exposes a versioned REST API under `/api/v1/*`. The same endpoints back the dashboard SPA, the `kici` CLI, and any third-party automation. There is no separate "public" surface — the dashboard's API is the API.
|
|
142
|
+
|
|
143
|
+
### Base URL
|
|
144
|
+
|
|
145
|
+
| Deployment | Base URL pattern |
|
|
146
|
+
| ----------- | ------------------------------------------------------------------------- |
|
|
147
|
+
| KiCI Cloud | `https://<your-platform-host>/api/v1/` |
|
|
148
|
+
| Self-hosted | `https://<orchestrator-host>/<deployment-slug>/api/v1/` (slug is optional |
|
|
149
|
+
| | — `KICI_BASE_PATH` may add a prefix when the Platform is reverse-proxied) |
|
|
150
|
+
|
|
151
|
+
`/api/v1/*` requires authentication (see below). `/health`, `/metrics`, and `/ws` (WebSocket) sit outside that prefix and have their own access posture (`/metrics` is meant for Prometheus scrape, not public exposure).
|
|
152
|
+
|
|
153
|
+
### Authentication
|
|
154
|
+
|
|
155
|
+
Every request to `/api/v1/*` carries an `Authorization: Bearer <token>` header. The Platform routes on the prefix:
|
|
156
|
+
|
|
157
|
+
| Prefix | Token type | Created via | Scope |
|
|
158
|
+
| ----------- | ----------------------------- | --------------------------------------- | ---------------- |
|
|
159
|
+
| `kici_pat_` | Personal access token | `kici login` or dashboard | User (cross-org) |
|
|
160
|
+
| `kici_sk_` | User API key | Dashboard → Settings → API keys | Org |
|
|
161
|
+
| `kici_sa_` | Service account key | Dashboard → Settings → Service accounts | Org |
|
|
162
|
+
| (other) | OIDC JWT or opaque OIDC token | OIDC login (browser SPA) | User (cross-org) |
|
|
163
|
+
|
|
164
|
+
JWT and opaque OIDC tokens are validated against the configured OIDC issuer (JWKS for JWTs, the issuer's UserInfo endpoint for opaque ones). All `kici_*` tokens are validated by SHA-256 hash lookup against the Platform DB. See [RBAC: authentication methods](https://docs.kici.dev/architecture/security/rbac/#authentication-methods) for the full model.
|
|
165
|
+
|
|
166
|
+
> **Note:** `kici_ok_` keys are **not** for the HTTP API — they authenticate orchestrator-to-Platform WebSocket connections only. Use `kici_sk_` (or `kici_pat_`) for HTTP calls.
|
|
167
|
+
|
|
168
|
+
### Permissions
|
|
169
|
+
|
|
170
|
+
Tokens authenticate; RBAC authorizes. Every org-scoped route runs `orgContextMiddleware` (verifies you are a member of the target org) followed by `requirePermission(resource, level)`. The 17 resources and 5 levels are documented in [RBAC](https://docs.kici.dev/architecture/security/rbac/#permission-model). User API keys carry their own permission matrix bounded above by the creator's effective permissions; PATs inherit the user's role permissions (or are capped further by their `scopes` field).
|
|
171
|
+
|
|
172
|
+
### Configurable surfaces
|
|
173
|
+
|
|
174
|
+
The dashboard is a browser SPA on top of the same `/api/v1/*` surface, so anything you can configure in the dashboard you can configure over HTTP. The mounted route groups include:
|
|
175
|
+
|
|
176
|
+
- **Auth & identity:** `/cli/exchange-token`, `/pats`, `/user`, `/identity-links`, `/github-oauth`, `/invites`, `/invites/pending`, `/invites/:inviteId/{accept,decline}`
|
|
177
|
+
- **Org & membership:** `/orgs`, `/orgs/:customerId`, `/orgs/:customerId/{members,roles,api-keys,orchestrator-keys,service-accounts,billing,trust-policies}`
|
|
178
|
+
- **Workflows & runs:** `/orgs/:customerId/{runs,registrations,workflows,held-runs,environments,secrets,global-workflows}`
|
|
179
|
+
- **Webhooks & event log:** `/orgs/:customerId/{sources,webhook-endpoints,event-log}`
|
|
180
|
+
- **Diagnostics & activity:** `/orgs/:customerId/{diagnostics,activity,access-log}`
|
|
181
|
+
|
|
182
|
+
The full route tree is the source of truth — every method, request schema, and response schema is enumerated server-side. There is currently no auto-generated OpenAPI spec; the typed `DashboardApiType` export is the canonical contract for TypeScript clients.
|
|
183
|
+
|
|
184
|
+
### Calling the API
|
|
185
|
+
|
|
186
|
+
Two short examples — adapt the base URL and token to your deployment.
|
|
187
|
+
|
|
188
|
+
**curl (PAT or API key):**
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
TOKEN="$(grep -E '^pat=' ~/.kici/config | cut -d= -f2)" # or paste a kici_sk_…
|
|
192
|
+
ORG="<your-org-id>"
|
|
193
|
+
curl -sS \
|
|
194
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
195
|
+
"https://<orchestrator-host>/<deployment-slug>/api/v1/orgs/$ORG/runs?limit=5" | jq
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Browser console (after dashboard login):**
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
const ns = Object.keys(localStorage).find((k) => k.startsWith('oidc.user:'));
|
|
202
|
+
const { access_token } = JSON.parse(localStorage.getItem(ns));
|
|
203
|
+
const res = await fetch('/<deployment-slug>/api/v1/orgs/<your-org-id>/runs?limit=5', {
|
|
204
|
+
headers: { Authorization: `Bearer ${access_token}` },
|
|
205
|
+
});
|
|
206
|
+
console.log(await res.json());
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Rate limits and body size
|
|
210
|
+
|
|
211
|
+
There is currently no per-token rate limit on `/api/v1/*`. A single global body-size cap applies to webhook ingress and dashboard API requests alike.
|
|
212
|
+
|
|
213
|
+
### Audit trail
|
|
214
|
+
|
|
215
|
+
Every `/api/v1/*` mutation that touches tenant-plane data is recorded in the upstream tenant-plane audit log, stamped with the actor (user, API key, service account, or upstream operator on a break-glass support read). Reads on customer data go through the orchestrator over the WebSocket proxy and land in the orchestrator's `access_log` table. See [Audit log](https://docs.kici.dev/operator/security/audit-log/) for the orchestrator schema and the dashboard's "Activity" page for the federated view.
|
|
216
|
+
|
|
217
|
+
## Token storage
|
|
218
|
+
|
|
219
|
+
The CLI stores authentication data in `~/.kici/config` with `0600` permissions (owner read/write only). The config file contains:
|
|
220
|
+
|
|
221
|
+
- PAT token
|
|
222
|
+
- PAT expiry date
|
|
223
|
+
- Active organization ID
|
|
224
|
+
- Per-org default orchestrator clusters
|
|
225
|
+
- Platform endpoint URL
|
|
226
|
+
|
|
227
|
+
## Troubleshooting
|
|
228
|
+
|
|
229
|
+
### Browser doesn't open
|
|
230
|
+
|
|
231
|
+
If `kici login` can't open a browser:
|
|
232
|
+
|
|
233
|
+
- Use `kici login --device` for the device flow
|
|
234
|
+
- Or set the `KICI_BROWSER_CMD` environment variable to your browser command (e.g., `KICI_BROWSER_CMD='firefox {url}'`)
|
|
235
|
+
|
|
236
|
+
### Device flow timeout
|
|
237
|
+
|
|
238
|
+
The device flow has a 5-minute timeout. If it expires:
|
|
239
|
+
|
|
240
|
+
- Run `kici login --device` again to get a new code
|
|
241
|
+
- Ensure you're using the correct URL displayed by the CLI
|
|
242
|
+
|
|
243
|
+
### Expired PAT
|
|
244
|
+
|
|
245
|
+
If you see "Personal access token has expired":
|
|
246
|
+
|
|
247
|
+
- Run `kici login` to create a new PAT
|
|
248
|
+
- The old expired PAT is automatically superseded
|
|
249
|
+
|
|
250
|
+
### "Not a member" errors
|
|
251
|
+
|
|
252
|
+
If authenticated commands return 403:
|
|
253
|
+
|
|
254
|
+
- Check your active org: `kici org current`
|
|
255
|
+
- List available orgs: `kici org list`
|
|
256
|
+
- Switch to the correct org: `kici org use <name>`
|
|
257
|
+
|
|
258
|
+
### Connection refused
|
|
259
|
+
|
|
260
|
+
If the CLI can't reach the server:
|
|
261
|
+
|
|
262
|
+
- Verify the endpoint: check `~/.kici/config` for the correct URL
|
|
263
|
+
- Test connectivity: `curl <your-platform-url>/health`
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## CLI reference
|
|
268
|
+
|
|
269
|
+
Source: https://docs.kici.dev/user/cli-reference/
|
|
270
|
+
|
|
271
|
+
The `@kici-dev/compiler` package provides the `kici` CLI for compiling, testing, and managing workflows.
|
|
272
|
+
|
|
273
|
+
## Installation
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
pnpm add -D @kici-dev/compiler
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
The examples use pnpm, but npm and yarn work too — `npm install -D @kici-dev/compiler` or `yarn add -D @kici-dev/compiler`.
|
|
280
|
+
|
|
281
|
+
Run commands with `npx kici` or add scripts to your `package.json`:
|
|
282
|
+
|
|
283
|
+
```json
|
|
284
|
+
{
|
|
285
|
+
"scripts": {
|
|
286
|
+
"kici:compile": "kici compile",
|
|
287
|
+
"kici:test": "kici test"
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Commands
|
|
293
|
+
|
|
294
|
+
### kici compile
|
|
295
|
+
|
|
296
|
+
Compile workflows from `.kici/workflows/` to `kici.lock.json`.
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
kici compile [options]
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**Options:**
|
|
303
|
+
|
|
304
|
+
| Option | Default | Description |
|
|
305
|
+
| ------------------- | ------- | -------------------------------------------- |
|
|
306
|
+
| `--check` | `false` | Validate workflows without writing lock file |
|
|
307
|
+
| `--watch` | `false` | Watch for changes and recompile |
|
|
308
|
+
| `--kici-dir <path>` | `.kici` | Path to .kici directory |
|
|
309
|
+
| `--verbose` | `false` | Detailed output |
|
|
310
|
+
|
|
311
|
+
**Examples:**
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
# Compile all workflows
|
|
315
|
+
kici compile
|
|
316
|
+
|
|
317
|
+
# Validate only (CI-friendly, no file writes)
|
|
318
|
+
kici compile --check
|
|
319
|
+
|
|
320
|
+
# Watch mode for development
|
|
321
|
+
kici compile --watch
|
|
322
|
+
|
|
323
|
+
# Custom .kici directory location
|
|
324
|
+
kici compile --kici-dir packages/app/.kici
|
|
325
|
+
|
|
326
|
+
# Verbose output for debugging
|
|
327
|
+
kici compile --verbose
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Exit codes:**
|
|
331
|
+
|
|
332
|
+
| Code | Meaning |
|
|
333
|
+
| ---- | --------------------------- |
|
|
334
|
+
| 0 | Compilation successful |
|
|
335
|
+
| 1 | Compilation failed (errors) |
|
|
336
|
+
|
|
337
|
+
The `--check` flag is useful in CI pipelines and pre-commit hooks. It validates that workflows are syntactically and semantically correct without writing the lock file or any other files.
|
|
338
|
+
|
|
339
|
+
**Auto-type regeneration:** When authenticated (via `kici login`), `kici compile` automatically refreshes `.kici/types/secrets.d.ts` after each successful compilation. This keeps type declarations in sync with your orchestrator's secret contexts. The type regeneration is non-blocking -- if the orchestrator is unreachable, compilation still succeeds with a warning. The `--check` flag skips type regeneration since no files are written.
|
|
340
|
+
|
|
341
|
+
### kici run
|
|
342
|
+
|
|
343
|
+
Execute workflows locally or remotely. The `run` command has two subcommands: `local` for direct execution without infrastructure, and `remote` for fixture-based execution through an orchestrator.
|
|
344
|
+
|
|
345
|
+
#### kici run local
|
|
346
|
+
|
|
347
|
+
Execute workflows locally without orchestrator infrastructure. Compiles workflows, matches triggers against the specified event, expands matrices, and runs jobs with DAG-based parallel scheduling.
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
kici run local [event] [options]
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**Arguments:**
|
|
354
|
+
|
|
355
|
+
| Argument | Required | Description |
|
|
356
|
+
| -------- | ---------------------- | ------------------------------------------------ |
|
|
357
|
+
| `event` | when `--pick` is unset | Event type (e.g., `push`, `pr:open`, `schedule`) |
|
|
358
|
+
|
|
359
|
+
**Options:**
|
|
360
|
+
|
|
361
|
+
| Option | Default | Description |
|
|
362
|
+
| --------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
363
|
+
| `-p, --pick` | `false` | Interactively pick a workflow + trigger (see below) |
|
|
364
|
+
| `--workflow <name>` | none | Run only the specified workflow (mutex with `--pick`) |
|
|
365
|
+
| `--job <name>` | none | Run only the specified job (and its dependencies) |
|
|
366
|
+
| `--branch <name>` | detected | Override detected git branch |
|
|
367
|
+
| `--sha <hash>` | detected | Override detected git SHA |
|
|
368
|
+
| `--payload <path>` | none | Path to explicit event payload JSON file |
|
|
369
|
+
| `--concurrency <n>` | CPU cores | Max parallel jobs **within one run** (job-level only). Cross-run [concurrency groups](https://docs.kici.dev/user/concurrency/) declared in `workflow({ concurrency: ... })` are enforced separately — see "Concurrency enforcement" below. |
|
|
370
|
+
| `--keep-going` | `false` | Continue after job failure |
|
|
371
|
+
| `--container` | `false` | Use Podman container isolation |
|
|
372
|
+
| `--env <KEY=VALUE>` | none | Environment variable override (repeatable) |
|
|
373
|
+
| `--input <KEY=VALUE>` | none | Typed workflow-dispatch input (repeatable) — coerced + validated against the workflow's `dispatch({ inputs })` schema, exposed as `ctx.dispatchInputs` (see [triggers → typed dispatch inputs](https://docs.kici.dev/user/sdk/triggers/#typed-dispatch-inputs)) |
|
|
374
|
+
| `--files <path>` | git diff | Override changed file paths (repeatable, default: git diff) |
|
|
375
|
+
| `--quiet` | `false` | Suppress streaming output (summary only) |
|
|
376
|
+
| `--json` | `false` | Output structured JSON result |
|
|
377
|
+
| `--junit <path>` | none | Output JUnit XML result to file |
|
|
378
|
+
| `--debug` | `false` | Verbose internals |
|
|
379
|
+
| `--kici-dir <path>` | `.kici` | Path to .kici directory |
|
|
380
|
+
| `--in-place` | `false` | Run against the real working directory instead of an isolated tmp checkout (see "Execution isolation" below) |
|
|
381
|
+
| `--keep` | `false` | Always retain the isolated tmp checkout (default: keep only on failure) |
|
|
382
|
+
|
|
383
|
+
**Interactive workflow selection (`--pick` / `-p`):**
|
|
384
|
+
|
|
385
|
+
When you do not remember the event arg for a workflow, pass `--pick` (or `-p`) to open an interactive picker. It lists every workflow with a compact summary of its triggers, lets you choose one, and (for multi-trigger workflows) prompts again for which trigger to simulate. The selected trigger is converted back into an event arg and fed through the normal pipeline.
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
# Open the picker across all triggerable workflows
|
|
389
|
+
kici run local --pick
|
|
390
|
+
|
|
391
|
+
# Scope the picker to a trigger family (e.g. only workflows that react to pr:*)
|
|
392
|
+
kici run local pr:open --pick
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Rules:
|
|
396
|
+
|
|
397
|
+
- `--pick` is mutually exclusive with `--workflow`. Passing both exits with code 2.
|
|
398
|
+
- When `stdin` is not a TTY, `--pick` prints the available workflows and exits without running anything — fall back to `kici run local <event> --workflow <name>` in scripts.
|
|
399
|
+
- Passing an event arg together with `--pick` narrows the picker to workflows that declare at least one trigger in that event family (e.g. `schedule --pick` shows only scheduled workflows).
|
|
400
|
+
|
|
401
|
+
**Concurrency enforcement:**
|
|
402
|
+
|
|
403
|
+
When the workflow declares a `concurrency` block, `kici run local` enforces it across concurrent local invocations on the same machine and user account. The behavior mirrors the orchestrator:
|
|
404
|
+
|
|
405
|
+
- The `group` callback is evaluated against the simulated event (same `{ branch, event }` context that the agent sees), and the resulting key is used as the lock identity. Throwing from `group` aborts the workflow run with a clear error — there is no fallback to the workflow name.
|
|
406
|
+
- `cancelInProgress: true` interrupts the holder via `SIGTERM`, then escalates to `SIGKILL` after a grace window if the holder does not exit, and proceeds with the new run.
|
|
407
|
+
- Otherwise the new invocation waits in FIFO order. A status line is printed when the wait starts and roughly every five seconds thereafter.
|
|
408
|
+
- Locks live under `$XDG_RUNTIME_DIR/kici-local-locks/` on Linux, falling back to `os.tmpdir()/kici-local-locks-<uid>/` on platforms without a per-user runtime dir. Each lock file records the holder PID, hostname, workflow name, group key, and start timestamp so concurrent invocations can describe what they are waiting for.
|
|
409
|
+
- Stale locks (the recorded holder PID is gone, per `process.kill(pid, 0)`) are reclaimed automatically.
|
|
410
|
+
|
|
411
|
+
Coordination is local only — running the same workflow on two different machines does not serialize across them. That requires the orchestrator.
|
|
412
|
+
|
|
413
|
+
The `SIGTERM`-to-`SIGKILL` grace window defaults to 30 000 ms. Override it with the `KICI_LOCAL_LOCK_KILL_GRACE_MS` environment variable (positive integer, milliseconds) when iterating on workflows that need longer to clean up on cancellation.
|
|
414
|
+
|
|
415
|
+
**Execution isolation:**
|
|
416
|
+
|
|
417
|
+
By default, `kici run local` executes steps inside an **isolated tmp checkout** rather than against your real working directory. Any file a step writes, builds, or deletes — and any `git` mutation a step performs — lands in that throwaway copy, so casual local runs never touch your tree.
|
|
418
|
+
|
|
419
|
+
What gets materialized into the isolated checkout has full parity with what `kici run remote` reconstructs: your current working tree minus gitignored files, with `.kiciignore` applied to local changes, over a real `.git` directory. Concretely, the checkout is built from a clone pinned to your current `HEAD`, with your local overlay (modified, staged, and untracked-but-not-ignored files) copied on top and locally-deleted files removed. Workflows that read git metadata work because the `.git` directory is present and pinned to your `HEAD`.
|
|
420
|
+
|
|
421
|
+
The path is logged at run start (for example, `running in /tmp/kici-run-ab12cd`) so you can inspect it.
|
|
422
|
+
|
|
423
|
+
Cleanup policy:
|
|
424
|
+
|
|
425
|
+
- On a fully successful run, the isolated checkout is removed.
|
|
426
|
+
- On failure, it is retained and its path is logged so you can inspect the failed state.
|
|
427
|
+
- `--keep` always retains it, even on success.
|
|
428
|
+
- Retained checkouts are garbage-collected after 72 hours by the next `kici run local` invocation — copy a checkout elsewhere if you need it longer.
|
|
429
|
+
|
|
430
|
+
Set the `KICI_RUN_DIR` environment variable to place the isolated checkout under a base directory other than the system temp directory.
|
|
431
|
+
|
|
432
|
+
Secrets are always sourced from your real `.kici/` directory, not from the isolated checkout. Gitignored secret files (such as `.kici/.env.local` and `.kici/secrets.yaml`) are never copied into the checkout, so a step that reads a secret still gets it from the original location.
|
|
433
|
+
|
|
434
|
+
Pass `--in-place` to run against the real working directory instead — useful when you explicitly want in-tree execution. `--in-place` requires no git repository; the default isolated mode does, and fails with an actionable error pointing at `--in-place` when the directory is not a git repository.
|
|
435
|
+
|
|
436
|
+
**Examples:**
|
|
437
|
+
|
|
438
|
+
```bash
|
|
439
|
+
# Run workflows matching a push event
|
|
440
|
+
kici run local push
|
|
441
|
+
|
|
442
|
+
# Run only a specific workflow
|
|
443
|
+
kici run local push --workflow ci
|
|
444
|
+
|
|
445
|
+
# Run only a specific job (and its dependencies)
|
|
446
|
+
kici run local push --job test
|
|
447
|
+
|
|
448
|
+
# JSON output for CI scripting
|
|
449
|
+
kici run local push --json
|
|
450
|
+
|
|
451
|
+
# JUnit XML for CI integration
|
|
452
|
+
kici run local push --junit results.xml
|
|
453
|
+
|
|
454
|
+
# Quiet mode (summary only, no streaming)
|
|
455
|
+
kici run local push --quiet
|
|
456
|
+
|
|
457
|
+
# Override branch and SHA
|
|
458
|
+
kici run local push --branch main --sha abc1234
|
|
459
|
+
|
|
460
|
+
# Environment variable overrides
|
|
461
|
+
kici run local push --env NODE_ENV=test --env CI=true
|
|
462
|
+
|
|
463
|
+
# Continue running other jobs after one fails
|
|
464
|
+
kici run local push --keep-going
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
**Exit codes:**
|
|
468
|
+
|
|
469
|
+
| Code | Meaning |
|
|
470
|
+
| ---- | ----------------------- |
|
|
471
|
+
| 0 | All workflows succeeded |
|
|
472
|
+
| 1 | One or more jobs failed |
|
|
473
|
+
|
|
474
|
+
**Output formats:**
|
|
475
|
+
|
|
476
|
+
- **Default:** Streaming job output during execution, followed by a tree-format summary with per-step timing
|
|
477
|
+
- **`--json`:** Structured JSON with workflows, jobs, steps, timing, and matrix values
|
|
478
|
+
- **`--junit <path>`:** Standard JUnit XML for CI integration (Jenkins, GitLab, etc.)
|
|
479
|
+
- **`--quiet`:** Summary only, no streaming output during execution
|
|
480
|
+
|
|
481
|
+
#### kici run remote
|
|
482
|
+
|
|
483
|
+
Execute fixtures remotely through the full CI pipeline. Fixtures are defined in `.kici/tests/*.ts` using the `fixture()` factory function. Without arguments, lists available fixtures.
|
|
484
|
+
|
|
485
|
+
Remote runs route through the Platform. Authenticate with a personal access token (`kici login`), then target an organization with `kici org use <org>` or the `--org` flag. The Platform relays the run to the org's orchestrator, while your working-tree overlay uploads directly to object storage — see [How the run is routed](https://docs.kici.dev/user/cli-reference/#how-the-run-is-routed) and [The two planes](https://docs.kici.dev/user/cli-reference/#the-two-planes) below.
|
|
486
|
+
|
|
487
|
+
Like `kici run local`, `kici run remote` recompiles your workflows (`.kici/workflows` → `kici.lock.json`) before dispatching, so the orchestrator matches and dispatches against your current workflow definitions — a brand-new or edited workflow takes effect without a separate `kici compile`. A compile or validation error aborts the run before anything is uploaded.
|
|
488
|
+
|
|
489
|
+
The orchestrator must have **cache storage configured** (`KICI_STORAGE_TYPE` = `s3` or `filesystem`) with a dev-reachable upload endpoint so the CLI's direct upload succeeds; see the [testing guide](https://docs.kici.dev/user/testing-guide/) and [Storage layout](https://docs.kici.dev/operator/orchestrator/storage-layout/) for setup.
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
kici run remote [fixture] [options]
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**Arguments:**
|
|
496
|
+
|
|
497
|
+
| Argument | Required | Description |
|
|
498
|
+
| --------- | -------- | ----------------------------------------------- |
|
|
499
|
+
| `fixture` | no | Fixture name or glob pattern (omit to list all) |
|
|
500
|
+
|
|
501
|
+
**Options:**
|
|
502
|
+
|
|
503
|
+
| Option | Default | Description |
|
|
504
|
+
| --------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
505
|
+
| `--org <id>` | active | Target organization for this run (overrides `kici org use`) |
|
|
506
|
+
| `--orchestrator <name>` | default | Target orchestrator cluster within the org (overrides the per-org default) |
|
|
507
|
+
| `--all` | `false` | Run all fixtures |
|
|
508
|
+
| `-p, --pick` | `false` | Interactively pick fixtures to run (multi-select; mutex with a fixture arg, `--all`, and `--workflow`) |
|
|
509
|
+
| `--workflow <name>` | none | Run a specific workflow directly (bypass triggers) |
|
|
510
|
+
| `--parallel` | `false` | Run multiple fixtures concurrently |
|
|
511
|
+
| `--no-wait` | - | Fire and forget (print runIds, don't stream) |
|
|
512
|
+
| `--quiet` | `false` | Minimal output (only final result) |
|
|
513
|
+
| `--json` | `false` | Machine-readable JSON output |
|
|
514
|
+
| `--junit <path>` | none | JUnit XML output to file for CI integration |
|
|
515
|
+
| `--history` | `false` | Show table of recent test runs |
|
|
516
|
+
| `--context <ctx.key=value>` | none | Inject a namespaced context secret, uploaded encrypted (repeatable) |
|
|
517
|
+
| `--env <KEY=VALUE>` | none | Provide a per-run secret, uploaded encrypted (repeatable) — see [testing guide](https://docs.kici.dev/user/testing-guide/) |
|
|
518
|
+
| `--input <KEY=VALUE>` | none | Typed workflow-dispatch input (repeatable) — validated + coerced + defaulted on the orchestrator from the lock descriptor, exposed as `ctx.dispatchInputs` (see [triggers → typed dispatch inputs](https://docs.kici.dev/user/sdk/triggers/#typed-dispatch-inputs)) |
|
|
519
|
+
| `--target <selector>` | none | Narrow `runsOnAll` jobs to hosts matching this label selector (repeatable, AND-combined) |
|
|
520
|
+
| `--target-allow-empty` | `false` | A `--target` that narrows a `runsOnAll` job to zero hosts skips it instead of failing |
|
|
521
|
+
| `--debug` | `false` | Verbose internals |
|
|
522
|
+
| `--kici-dir <path>` | `.kici` | Path to .kici directory |
|
|
523
|
+
|
|
524
|
+
**Examples:**
|
|
525
|
+
|
|
526
|
+
```bash
|
|
527
|
+
# List available fixtures
|
|
528
|
+
kici run remote
|
|
529
|
+
|
|
530
|
+
# Run a single fixture against the active org
|
|
531
|
+
kici run remote push-main
|
|
532
|
+
|
|
533
|
+
# Target a specific org for this run
|
|
534
|
+
kici run remote push-main --org xyz789ghi012
|
|
535
|
+
|
|
536
|
+
# Target a specific orchestrator cluster within the org
|
|
537
|
+
kici run remote push-main --orchestrator us-east
|
|
538
|
+
|
|
539
|
+
# Run all push-related fixtures
|
|
540
|
+
kici run remote 'push-*'
|
|
541
|
+
|
|
542
|
+
# Run everything
|
|
543
|
+
kici run remote --all
|
|
544
|
+
|
|
545
|
+
# Run a specific workflow directly (bypass trigger matching)
|
|
546
|
+
kici run remote --workflow ci
|
|
547
|
+
|
|
548
|
+
# Quiet mode -- just pass/fail
|
|
549
|
+
kici run remote push-main --quiet
|
|
550
|
+
|
|
551
|
+
# JSON output for scripting
|
|
552
|
+
kici run remote push-main --json
|
|
553
|
+
|
|
554
|
+
# Fire and forget
|
|
555
|
+
kici run remote push-main --no-wait
|
|
556
|
+
|
|
557
|
+
# View recent test run history
|
|
558
|
+
kici run remote --history
|
|
559
|
+
|
|
560
|
+
# Interactively pick which fixtures to run (multi-select)
|
|
561
|
+
kici run remote --pick
|
|
562
|
+
|
|
563
|
+
# Narrow runsOnAll jobs to a subset of the host roster
|
|
564
|
+
kici run remote deploy --target role:web
|
|
565
|
+
|
|
566
|
+
# AND-combine repeated --target values (hosts must match every selector)
|
|
567
|
+
kici run remote deploy --target role:web --target dc:eu
|
|
568
|
+
|
|
569
|
+
# Skip a runsOnAll job instead of failing it when the target matches no host
|
|
570
|
+
kici run remote deploy --target role:gpu --target-allow-empty
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
**Interactive fixture selection (`--pick` / `-p`):**
|
|
574
|
+
|
|
575
|
+
Pass `--pick` (or `-p`) to open an interactive checkbox menu of the available
|
|
576
|
+
fixtures. Toggle one or more with space, confirm with enter, and the selected
|
|
577
|
+
fixtures run through the normal remote pipeline (honoring `--parallel`,
|
|
578
|
+
`--no-wait`, and the other run flags). Notes:
|
|
579
|
+
|
|
580
|
+
- `--pick` is mutually exclusive with a fixture argument, `--all`, and
|
|
581
|
+
`--workflow`. Passing any together exits with code 2.
|
|
582
|
+
- When `stdin` is not a TTY, `--pick` prints the available fixtures and exits
|
|
583
|
+
without running anything — pass a fixture name (or `--all`) in scripts.
|
|
584
|
+
|
|
585
|
+
#### Host narrowing with `--target`
|
|
586
|
+
|
|
587
|
+
`--target <selector>` is a runtime narrowing for `runsOnAll` jobs, analogous to
|
|
588
|
+
Ansible's `--limit`. A `runsOnAll` job normally fans out to **every** roster host
|
|
589
|
+
matching its predicate, one pinned execution per host. `--target` intersects that
|
|
590
|
+
matched roster with a label selector, so the effective host set is
|
|
591
|
+
`runsOnAll ∩ target`:
|
|
592
|
+
|
|
593
|
+
- **Narrow-only.** `--target` can only _remove_ hosts from the matched set, never
|
|
594
|
+
add them. The widening dimension (OR across host groups) lives in the workflow's
|
|
595
|
+
`runsOnAll`; `--target` only subtracts.
|
|
596
|
+
- **Run-global, `runsOnAll`-only.** A single `--target` applies to every
|
|
597
|
+
`runsOnAll` job in the run. Jobs pinned to a single host with `runsOn` are
|
|
598
|
+
untouched.
|
|
599
|
+
- **Repeatable and AND-combined.** Each `--target` value is its own selector; a
|
|
600
|
+
host must satisfy **all** of them to survive the narrowing. Use a single value
|
|
601
|
+
for an OR-style match within one selector and repeated values for AND.
|
|
602
|
+
- **Selector syntax** matches `runsOn`: an exact label (`role:web`), a glob
|
|
603
|
+
(`role:*`), or a regex (`/^box-0[1-3]$/`).
|
|
604
|
+
|
|
605
|
+
When `--target` narrows a `runsOnAll` job to zero hosts, the default is to **fail**
|
|
606
|
+
the run (fail-loud — a typo in the selector shouldn't silently skip work). Pass
|
|
607
|
+
`--target-allow-empty` to **skip** the zeroed job instead; the job records a
|
|
608
|
+
`skipped` status, and any downstream job that needs it with `when: 'on-skip'` (or
|
|
609
|
+
`when: 'always'`) still runs. See [Job dependencies](https://docs.kici.dev/user/sdk/core/#job-dependencies-needs)
|
|
610
|
+
for the `when` gating model.
|
|
611
|
+
|
|
612
|
+
**Exit codes:**
|
|
613
|
+
|
|
614
|
+
| Code | Meaning |
|
|
615
|
+
| ---- | ---------------------------- |
|
|
616
|
+
| 0 | All matched workflows passed |
|
|
617
|
+
| 1 | One or more workflows failed |
|
|
618
|
+
|
|
619
|
+
#### How the run is routed
|
|
620
|
+
|
|
621
|
+
A remote run is dispatched to your **active organization** — the one set with `kici org use <org>`, or overridden per-run with `--org <id>`. The org is resolved in this order:
|
|
622
|
+
|
|
623
|
+
1. The `--org <id>` flag, if provided.
|
|
624
|
+
2. Otherwise the active org saved in your global config by `kici org use <org>`.
|
|
625
|
+
3. If neither is set, the command errors and asks you to select an org with `kici org use` or pass `--org`.
|
|
626
|
+
|
|
627
|
+
The orchestrator anchors the org without any manual webhook source: it auto-provisions a system-managed **remote source** (routing key `remote:<orgId>`) that maps to its bound organization, so even a zero-source org is immediately routable for remote runs. You never set a routing key for a remote run — selecting the org is enough.
|
|
628
|
+
|
|
629
|
+
When an org has more than one connected orchestrator cluster, the CLI picks the target cluster in this order:
|
|
630
|
+
|
|
631
|
+
1. The `--orchestrator <name>` flag, if provided.
|
|
632
|
+
2. Otherwise the per-org default cluster, set with `kici orchestrators use <name>`.
|
|
633
|
+
3. If the org has exactly **one** connected orchestrator, it is auto-selected.
|
|
634
|
+
4. Otherwise the run errors with the list of connected clusters, and you pass `--orchestrator <name>` to choose one. Run `kici orchestrators list` to see the available cluster names.
|
|
635
|
+
|
|
636
|
+
#### The two planes
|
|
637
|
+
|
|
638
|
+
`kici run remote` uses two independent paths:
|
|
639
|
+
|
|
640
|
+
- **Control plane** — run initiation, trigger, status, log retrieval, and cancellation flow from your machine through the Platform, which relays them over a WebSocket connection to the org's orchestrator. Logs are delivered by the CLI polling the Platform for log chunks (tracked by a monotonic line cursor) and run status until the run reaches a terminal state; there is no direct streaming socket to the orchestrator.
|
|
641
|
+
- **Data plane** — your working-tree overlay tarball uploads **directly** from your machine to the orchestrator's object store via a presigned PUT URL. The overlay never passes through the Platform. This is why the orchestrator's object-store upload endpoint must be reachable from your machine; see [Storage layout](https://docs.kici.dev/operator/orchestrator/storage-layout/).
|
|
642
|
+
|
|
643
|
+
An orchestrator with no Platform connection cannot serve remote runs — the Platform is the service that offers them. For executing workflow steps on your own machine without an orchestrator (no scaler, agents, or environments), use [`kici run local`](https://docs.kici.dev/user/cli-reference/#kici-run-local).
|
|
644
|
+
|
|
645
|
+
#### Fresh repos (no GitHub remote)
|
|
646
|
+
|
|
647
|
+
`kici run remote` works even if the repo has never been pushed to GitHub. When no remote is detected:
|
|
648
|
+
|
|
649
|
+
- The entire repo content is uploaded (not just a diff overlay)
|
|
650
|
+
- The lock file is sent inline (no GitHub API fetch)
|
|
651
|
+
- Steps that use git commands will fail (no `.git` directory in the remote workspace)
|
|
652
|
+
- Build cache (`__build__` jobs) is skipped for local repos
|
|
653
|
+
- Environments must have `allowLocalExecution: true` to be accessible from local runs (default is `false`)
|
|
654
|
+
|
|
655
|
+
Destination routing is unchanged for fresh repos: the run still goes to your active org through the Platform.
|
|
656
|
+
|
|
657
|
+
For a detailed guide on writing fixtures, configuring secrets, and understanding the upload flow, see [Testing guide](https://docs.kici.dev/user/testing-guide/).
|
|
658
|
+
|
|
659
|
+
#### kici orchestrators
|
|
660
|
+
|
|
661
|
+
List the orchestrator clusters connected to an organization, and set the per-org default cluster used by `kici run remote`. Requires `kici login` and an active org (or pass `--org`).
|
|
662
|
+
|
|
663
|
+
```bash
|
|
664
|
+
kici orchestrators list [--org <id>]
|
|
665
|
+
kici orchestrators use <clusterName> [--org <id>]
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
**`kici orchestrators list`** prints the org's connected orchestrator clusters, so you know what to pass to `--orchestrator` (or to `kici orchestrators use`).
|
|
669
|
+
|
|
670
|
+
**`kici orchestrators use <clusterName>`** sets the default orchestrator cluster for the org, stored per-org in your global config. Subsequent `kici run remote` invocations target that cluster unless overridden with `--orchestrator`.
|
|
671
|
+
|
|
672
|
+
**Examples:**
|
|
673
|
+
|
|
674
|
+
```bash
|
|
675
|
+
# List the active org's connected clusters
|
|
676
|
+
kici orchestrators list
|
|
677
|
+
|
|
678
|
+
# List a specific org's clusters
|
|
679
|
+
kici orchestrators list --org xyz789ghi012
|
|
680
|
+
|
|
681
|
+
# Set the default cluster for the active org
|
|
682
|
+
kici orchestrators use us-east
|
|
683
|
+
|
|
684
|
+
# Set the default cluster for a specific org
|
|
685
|
+
kici orchestrators use us-east --org xyz789ghi012
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
### kici test
|
|
689
|
+
|
|
690
|
+
Preview which workflows match a trigger event (dry-run, no execution). Useful for verifying trigger configurations during development.
|
|
691
|
+
|
|
692
|
+
```bash
|
|
693
|
+
kici test [event] [options]
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
**Arguments:**
|
|
697
|
+
|
|
698
|
+
| Argument | Required | Description |
|
|
699
|
+
| -------- | -------- | ----------------------------------------------------------- |
|
|
700
|
+
| `event` | no | Event type to preview (e.g., `push`, `pr:open`, `schedule`) |
|
|
701
|
+
|
|
702
|
+
**Options:**
|
|
703
|
+
|
|
704
|
+
| Option | Default | Description |
|
|
705
|
+
| --------------------------- | ------- | ------------------------------------------------------------ |
|
|
706
|
+
| `--workflow <name>` | none | Filter to specific workflow |
|
|
707
|
+
| `--job <name>` | none | Filter to specific job |
|
|
708
|
+
| `--branch <name>` | `main` | Override target branch for trigger matching |
|
|
709
|
+
| `--sha <hash>` | none | Override commit SHA |
|
|
710
|
+
| `--files <path>` | none | Simulate changed file path for trigger matching (repeatable) |
|
|
711
|
+
| `--secret <key=value>` | none | Inject flat secret (repeatable) |
|
|
712
|
+
| `--context <ctx.key=value>` | none | Inject context secret (repeatable) |
|
|
713
|
+
| `--debug` | `false` | Verbose internals |
|
|
714
|
+
| `--kici-dir <path>` | `.kici` | Path to .kici directory |
|
|
715
|
+
|
|
716
|
+
**Examples:**
|
|
717
|
+
|
|
718
|
+
```bash
|
|
719
|
+
# Preview which workflows match a push event
|
|
720
|
+
kici test push
|
|
721
|
+
|
|
722
|
+
# Preview PR trigger matching
|
|
723
|
+
kici test pr:open
|
|
724
|
+
|
|
725
|
+
# Preview with branch override
|
|
726
|
+
kici test push --branch develop
|
|
727
|
+
|
|
728
|
+
# Filter to specific workflow
|
|
729
|
+
kici test push --workflow ci
|
|
730
|
+
|
|
731
|
+
# Simulate changed files for path-filtered triggers
|
|
732
|
+
kici test push --files src/index.ts --files README.md
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
**Exit codes:**
|
|
736
|
+
|
|
737
|
+
| Code | Meaning |
|
|
738
|
+
| ---- | ------------------------------------------ |
|
|
739
|
+
| 0 | Preview completed (including zero matches) |
|
|
740
|
+
| 1 | Error |
|
|
741
|
+
|
|
742
|
+
**Migration from old `kici test <fixture>`:** If you were using `kici test <fixture-name>` for remote fixture execution, use `kici run remote <fixture-name>` instead. For local workflow execution, use `kici run local <event>`.
|
|
743
|
+
|
|
744
|
+
### kici login
|
|
745
|
+
|
|
746
|
+
Authenticate with KiCI via browser-based OAuth (default) or API key (`--token`).
|
|
747
|
+
|
|
748
|
+
By default, `kici login` opens your browser for OIDC authentication using PKCE. In headless environments (SSH, CI, containers), it automatically switches to the RFC 8628 device authorization flow where you visit a URL and enter a code.
|
|
749
|
+
|
|
750
|
+
After OAuth, the CLI exchanges the OIDC token for a personal access token (PAT) stored in the config directory (`~/.kici/config` by default, overridable with `KICI_CONFIG_DIR`).
|
|
751
|
+
|
|
752
|
+
`kici login` targets the hosted KiCI Platform by default. To authenticate against another environment (a self-hosted Platform, for example), pass `--platform-endpoint` / `--oidc-issuer` or set `KICI_PLATFORM_URL` / `KICI_OIDC_ISSUER`. Login persists the platform endpoint and OIDC issuer it authenticated against alongside the PAT, so a saved PAT always matches its endpoint. Because the config describes one environment at a time, **switching the endpoint resets the active organization and default clusters** — re-run `kici org use <name>` after switching environments.
|
|
753
|
+
|
|
754
|
+
```bash
|
|
755
|
+
kici login [options]
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
**Options:**
|
|
759
|
+
|
|
760
|
+
| Option | Default | Description |
|
|
761
|
+
| --------------------------- | ------- | --------------------------------------------------- |
|
|
762
|
+
| `--token <key>` | none | API key for direct authentication (legacy) |
|
|
763
|
+
| `--device` | false | Force device authorization flow (headless/SSH) |
|
|
764
|
+
| `--platform-endpoint <url>` | none | Platform relay URL |
|
|
765
|
+
| `--oidc-issuer <url>` | none | OIDC issuer URL (selects a non-default environment) |
|
|
766
|
+
| `--routing-key <key>` | none | Routing key for webhook source identification |
|
|
767
|
+
|
|
768
|
+
**Environment variables:**
|
|
769
|
+
|
|
770
|
+
| Variable | Default | Description |
|
|
771
|
+
| --------------------- | -------------------------------------------- | ---------------------------------------------------------------------- |
|
|
772
|
+
| `KICI_PLATFORM_URL` | `https://api.kici.dev` | Platform API base URL (override for a self-hosted Platform) |
|
|
773
|
+
| `KICI_OIDC_ISSUER` | `https://auth.kici.dev/realms/kici-internal` | OIDC issuer URL (override for a self-hosted Platform) |
|
|
774
|
+
| `KICI_OIDC_CLIENT_ID` | `kici-cli` | OIDC client ID (override for a self-hosted Platform) |
|
|
775
|
+
| `KICI_BROWSER_CMD` | uses `open` package | Custom browser command with `{url}` placeholder, or `none` to suppress |
|
|
776
|
+
| `KICI_CALLBACK_PORT` | random | Fixed port for OAuth PKCE callback server |
|
|
777
|
+
| `KICI_CONFIG_DIR` | `~/.kici` | Override config directory |
|
|
778
|
+
|
|
779
|
+
**Examples:**
|
|
780
|
+
|
|
781
|
+
```bash
|
|
782
|
+
# Browser-based OAuth login (default)
|
|
783
|
+
kici login
|
|
784
|
+
|
|
785
|
+
# Force device flow (for SSH/headless)
|
|
786
|
+
kici login --device
|
|
787
|
+
|
|
788
|
+
# Legacy API key login
|
|
789
|
+
kici login --token kici_sk_abc123...
|
|
790
|
+
|
|
791
|
+
# Log in against a self-hosted Platform
|
|
792
|
+
kici login --platform-endpoint https://platform.example.com \
|
|
793
|
+
--oidc-issuer https://auth.example.com/realms/kici-internal
|
|
794
|
+
|
|
795
|
+
# Suppress browser opening (print authorize URL to stdout)
|
|
796
|
+
KICI_BROWSER_CMD=none kici login
|
|
797
|
+
|
|
798
|
+
# Use custom browser command
|
|
799
|
+
KICI_BROWSER_CMD='firefox {url}' kici login
|
|
800
|
+
|
|
801
|
+
# Fixed callback port and custom config directory
|
|
802
|
+
KICI_CALLBACK_PORT=19876 KICI_CONFIG_DIR=/tmp/kici-test kici login
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
**Headless detection:** The CLI automatically detects headless environments by checking for `SSH_CLIENT`, `SSH_TTY`, `CI`, `GITHUB_ACTIONS`, `GITLAB_CI`, `container`, or `DOCKER_CONTAINER` env vars, and on Linux, the absence of `DISPLAY` and `WAYLAND_DISPLAY`.
|
|
806
|
+
|
|
807
|
+
### kici logout
|
|
808
|
+
|
|
809
|
+
Revoke your personal access token on the server and clear local credentials.
|
|
810
|
+
|
|
811
|
+
If the server is unreachable, local credentials are still cleared (the PAT will expire automatically). Non-auth config fields (active org, default clusters, etc.) are preserved.
|
|
812
|
+
|
|
813
|
+
```bash
|
|
814
|
+
kici logout
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
**Examples:**
|
|
818
|
+
|
|
819
|
+
```bash
|
|
820
|
+
# Log out and revoke PAT
|
|
821
|
+
kici logout
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
### kici org
|
|
825
|
+
|
|
826
|
+
Manage organization context. Requires a PAT (run `kici login` first).
|
|
827
|
+
|
|
828
|
+
#### kici org list
|
|
829
|
+
|
|
830
|
+
List organizations you belong to. The active org is marked with a star (`*`).
|
|
831
|
+
|
|
832
|
+
```bash
|
|
833
|
+
kici org list
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
**Example output:**
|
|
837
|
+
|
|
838
|
+
```
|
|
839
|
+
Organizations:
|
|
840
|
+
|
|
841
|
+
* Personal (owner) abc123def456
|
|
842
|
+
My team (admin) xyz789ghi012
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
#### kici org use
|
|
846
|
+
|
|
847
|
+
Switch the active organization by name (case-insensitive) or ID.
|
|
848
|
+
|
|
849
|
+
```bash
|
|
850
|
+
kici org use <name>
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
**Arguments:**
|
|
854
|
+
|
|
855
|
+
| Argument | Required | Description |
|
|
856
|
+
| -------- | -------- | ----------------------- |
|
|
857
|
+
| `name` | yes | Organization name or ID |
|
|
858
|
+
|
|
859
|
+
**Examples:**
|
|
860
|
+
|
|
861
|
+
```bash
|
|
862
|
+
# Switch by name
|
|
863
|
+
kici org use "My team"
|
|
864
|
+
|
|
865
|
+
# Switch by ID
|
|
866
|
+
kici org use xyz789ghi012
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
#### kici org current
|
|
870
|
+
|
|
871
|
+
Show the current active organization.
|
|
872
|
+
|
|
873
|
+
```bash
|
|
874
|
+
kici org current
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
### kici diagnostics
|
|
878
|
+
|
|
879
|
+
Show the orchestrators, scalers, and agents serving your organization — the
|
|
880
|
+
terminal equivalent of the dashboard Diagnostics page. Reads the same
|
|
881
|
+
org-scoped data the dashboard does, so it needs `kici login` and an active org
|
|
882
|
+
(`kici org use <name>`).
|
|
883
|
+
|
|
884
|
+
The output has three parts: a one-line header (runs in the last 24h, success
|
|
885
|
+
rate, average duration, queued/running job counts), any infrastructure alerts
|
|
886
|
+
(only shown when present), and a tree of each orchestrator with its scalers and
|
|
887
|
+
agents. Each agent line shows its labels, platform/architecture, active/maximum
|
|
888
|
+
concurrency, and heartbeat age.
|
|
889
|
+
|
|
890
|
+
```bash
|
|
891
|
+
kici diagnostics [options]
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
**Options:**
|
|
895
|
+
|
|
896
|
+
| Option | Default | Description |
|
|
897
|
+
| --------------------- | ------- | --------------------------------------------------- |
|
|
898
|
+
| `--json` | `false` | Machine-readable JSON output |
|
|
899
|
+
| `--verbose` | `false` | Show extended per-agent fields (host, node, memory) |
|
|
900
|
+
| `--orchestrator <id>` | all | Scope the tree to one orchestrator connection id |
|
|
901
|
+
|
|
902
|
+
**Examples:**
|
|
903
|
+
|
|
904
|
+
```bash
|
|
905
|
+
# Show the full infrastructure tree
|
|
906
|
+
kici diagnostics
|
|
907
|
+
|
|
908
|
+
# Extended per-agent detail
|
|
909
|
+
kici diagnostics --verbose
|
|
910
|
+
|
|
911
|
+
# Only one orchestrator's scalers and agents
|
|
912
|
+
kici diagnostics --orchestrator conn-abc123
|
|
913
|
+
|
|
914
|
+
# Machine-readable output
|
|
915
|
+
kici diagnostics --json
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
### kici runs
|
|
919
|
+
|
|
920
|
+
Inspect and manage execution runs from the terminal — the equivalent of the
|
|
921
|
+
dashboard Runs page. All `kici runs` subcommands read/write the same org-scoped
|
|
922
|
+
data as the dashboard, so they require `kici login` and an active org
|
|
923
|
+
(`kici org use <name>`).
|
|
924
|
+
|
|
925
|
+
#### kici runs list
|
|
926
|
+
|
|
927
|
+
List runs with optional filters. Output is a table (run id, workflow, status,
|
|
928
|
+
branch, trigger, started, duration); pagination is reported at the bottom.
|
|
929
|
+
|
|
930
|
+
```bash
|
|
931
|
+
kici runs list [options]
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
**Options:**
|
|
935
|
+
|
|
936
|
+
| Option | Default | Description |
|
|
937
|
+
| ----------------------- | ------- | --------------------------------------------- |
|
|
938
|
+
| `--status <s>` | all | Filter by run status |
|
|
939
|
+
| `--workflow <w>` | all | Filter by workflow name |
|
|
940
|
+
| `--branch <b>` | all | Filter by branch/ref |
|
|
941
|
+
| `--repo <r>` | all | Filter by repository |
|
|
942
|
+
| `--trigger <t>` | all | Filter by trigger type |
|
|
943
|
+
| `--source <routingKey>` | all | Filter by source routing key |
|
|
944
|
+
| `--since <ts>` | none | Only runs since this ISO-8601 or epoch ms |
|
|
945
|
+
| `--page <n>` | `1` | Page number (server page size is fixed at 20) |
|
|
946
|
+
| `--json` | `false` | Machine-readable JSON output |
|
|
947
|
+
|
|
948
|
+
```bash
|
|
949
|
+
kici runs list
|
|
950
|
+
kici runs list --status running
|
|
951
|
+
kici runs list --workflow ci --branch main
|
|
952
|
+
kici runs list --json | jq '.runs[].runId'
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
#### kici runs show
|
|
956
|
+
|
|
957
|
+
Show a run's summary header plus its jobs-and-steps tree (name, status,
|
|
958
|
+
duration, exit code). If the run id is not on the Platform but exists in your
|
|
959
|
+
local run history (from `kici run local`), the local record is shown instead.
|
|
960
|
+
|
|
961
|
+
```bash
|
|
962
|
+
kici runs show <run-id> [options]
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
| Option | Default | Description |
|
|
966
|
+
| -------- | ------- | ---------------------------- |
|
|
967
|
+
| `--json` | `false` | Machine-readable JSON output |
|
|
968
|
+
|
|
969
|
+
```bash
|
|
970
|
+
kici runs show abc123
|
|
971
|
+
kici runs show abc123 --json
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
#### kici runs logs
|
|
975
|
+
|
|
976
|
+
Print each job/step's log lines in order, with headers.
|
|
977
|
+
|
|
978
|
+
```bash
|
|
979
|
+
kici runs logs <run-id> [options]
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
| Option | Default | Description |
|
|
983
|
+
| -------------- | ------- | -------------------------------------- |
|
|
984
|
+
| `--job <name>` | all | Only print logs for this job |
|
|
985
|
+
| `-f, --follow` | `false` | Tail logs for a live run until it ends |
|
|
986
|
+
| `--json` | `false` | Machine-readable JSON output |
|
|
987
|
+
|
|
988
|
+
```bash
|
|
989
|
+
kici runs logs abc123
|
|
990
|
+
kici runs logs abc123 --job build
|
|
991
|
+
kici runs logs abc123 --follow
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
#### kici runs rerun
|
|
995
|
+
|
|
996
|
+
Re-trigger a completed run. Prints the new run id. The server enforces a short
|
|
997
|
+
cooldown between reruns of the same run.
|
|
998
|
+
|
|
999
|
+
```bash
|
|
1000
|
+
kici runs rerun <run-id> [options]
|
|
1001
|
+
```
|
|
1002
|
+
|
|
1003
|
+
| Option | Default | Description |
|
|
1004
|
+
| -------- | ------- | ---------------------------- |
|
|
1005
|
+
| `--json` | `false` | Machine-readable JSON output |
|
|
1006
|
+
|
|
1007
|
+
```bash
|
|
1008
|
+
kici runs rerun abc123
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
#### kici runs cancel
|
|
1012
|
+
|
|
1013
|
+
Cancel a single run, or all in-progress runs on a branch.
|
|
1014
|
+
|
|
1015
|
+
```bash
|
|
1016
|
+
kici runs cancel [run-id] [options]
|
|
1017
|
+
```
|
|
1018
|
+
|
|
1019
|
+
| Argument | Required | Description |
|
|
1020
|
+
| -------- | -------- | ---------------- |
|
|
1021
|
+
| `run-id` | no | Run ID to cancel |
|
|
1022
|
+
|
|
1023
|
+
| Option | Default | Description |
|
|
1024
|
+
| ----------------- | ------- | ------------------------------------------- |
|
|
1025
|
+
| `--force` | `false` | Force cancel (kill immediately, skip hooks) |
|
|
1026
|
+
| `--branch <name>` | none | Cancel all in-progress runs on this branch |
|
|
1027
|
+
|
|
1028
|
+
```bash
|
|
1029
|
+
kici runs cancel abc123
|
|
1030
|
+
kici runs cancel abc123 --force
|
|
1031
|
+
kici runs cancel --branch feature/wip
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
When `--json` is set on any of these commands, `kici` emits only the JSON
|
|
1035
|
+
document on stdout — the `kici v<version>` banner is suppressed — so the output
|
|
1036
|
+
is safe to pipe into `jq` or `JSON.parse`. The same holds for the other
|
|
1037
|
+
`--json` commands (`kici run remote --json`, `kici workflows list --json`) and
|
|
1038
|
+
for `--quiet`.
|
|
1039
|
+
|
|
1040
|
+
### kici approve
|
|
1041
|
+
|
|
1042
|
+
Approve a held [approval gate](https://docs.kici.dev/user/approvals/) so the run resumes. Identify the held element by run ID, optionally narrowed to a job and step.
|
|
1043
|
+
|
|
1044
|
+
```bash
|
|
1045
|
+
kici approve <run-id> [options]
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
**Arguments:**
|
|
1049
|
+
|
|
1050
|
+
| Argument | Required | Description |
|
|
1051
|
+
| -------- | -------- | ---------------------------------- |
|
|
1052
|
+
| `run-id` | yes | Run ID holding the gate to approve |
|
|
1053
|
+
|
|
1054
|
+
**Options:**
|
|
1055
|
+
|
|
1056
|
+
| Option | Default | Description |
|
|
1057
|
+
| ---------------- | ------- | ---------------------------------------------------- |
|
|
1058
|
+
| `--job <name>` | none | Approve a held job (omit for a workflow-level hold) |
|
|
1059
|
+
| `--step <index>` | none | Approve a held step by its index (used with `--job`) |
|
|
1060
|
+
|
|
1061
|
+
**Examples:**
|
|
1062
|
+
|
|
1063
|
+
```bash
|
|
1064
|
+
# Approve a workflow-level hold
|
|
1065
|
+
kici approve abc123
|
|
1066
|
+
|
|
1067
|
+
# Approve a held job
|
|
1068
|
+
kici approve abc123 --job deploy-production
|
|
1069
|
+
|
|
1070
|
+
# Approve a held step (steps are addressed by index)
|
|
1071
|
+
kici approve abc123 --job migrate-and-deploy --step 1
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
You must be eligible for at least one unsatisfied clause (a member of a named team, or a named user) and hold the `environments:write` or `ci_trust:write` permission. The command reports whether the element was released, how many clauses remain, or that it was rejected.
|
|
1075
|
+
|
|
1076
|
+
### kici reject
|
|
1077
|
+
|
|
1078
|
+
Reject a held [approval gate](https://docs.kici.dev/user/approvals/). A rejection fails the held element and the run. A reason is required.
|
|
1079
|
+
|
|
1080
|
+
```bash
|
|
1081
|
+
kici reject <run-id> --reason <text> [options]
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
**Arguments:**
|
|
1085
|
+
|
|
1086
|
+
| Argument | Required | Description |
|
|
1087
|
+
| -------- | -------- | --------------------------------- |
|
|
1088
|
+
| `run-id` | yes | Run ID holding the gate to reject |
|
|
1089
|
+
|
|
1090
|
+
**Options:**
|
|
1091
|
+
|
|
1092
|
+
| Option | Default | Description |
|
|
1093
|
+
| ----------------- | ------- | --------------------------------------------------- |
|
|
1094
|
+
| `--reason <text>` | none | Required. Reason recorded with the rejection |
|
|
1095
|
+
| `--job <name>` | none | Reject a held job (omit for a workflow-level hold) |
|
|
1096
|
+
| `--step <index>` | none | Reject a held step by its index (used with `--job`) |
|
|
1097
|
+
|
|
1098
|
+
**Examples:**
|
|
1099
|
+
|
|
1100
|
+
```bash
|
|
1101
|
+
# Reject a held job with a reason
|
|
1102
|
+
kici reject abc123 --job deploy-production --reason "Wrong release branch"
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
### kici secrets list
|
|
1106
|
+
|
|
1107
|
+
List secret contexts available for test runs. Shows context names and key names (not values).
|
|
1108
|
+
|
|
1109
|
+
```bash
|
|
1110
|
+
kici secrets list
|
|
1111
|
+
```
|
|
1112
|
+
|
|
1113
|
+
Each "context" corresponds to an environment configured on the orchestrator. The output lists every environment whose `allowLocalExecution` flag is `true` (the gate that lets CLI-initiated test runs resolve secrets through that environment), along with the secret key names reachable from the environment's bound scopes.
|
|
1114
|
+
|
|
1115
|
+
Only key names are shown — secret values are never returned over this endpoint.
|
|
1116
|
+
|
|
1117
|
+
**Prerequisites:** authenticate via `kici login` and select an active organization with `kici org use <name>`.
|
|
1118
|
+
|
|
1119
|
+
### kici types
|
|
1120
|
+
|
|
1121
|
+
Generate TypeScript declaration files from orchestrator environment metadata. The generated `.d.ts` file augments the SDK's `KnownSecretKeys` and `EnvironmentSecrets` interfaces, providing compile-time autocomplete and type checking for secret key names.
|
|
1122
|
+
|
|
1123
|
+
```bash
|
|
1124
|
+
kici types [options]
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
**Options:**
|
|
1128
|
+
|
|
1129
|
+
| Option | Default | Description |
|
|
1130
|
+
| ------------------- | ------- | ----------------------- |
|
|
1131
|
+
| `--kici-dir <path>` | `.kici` | Path to .kici directory |
|
|
1132
|
+
|
|
1133
|
+
**Prerequisites:** Must be authenticated via `kici login`.
|
|
1134
|
+
|
|
1135
|
+
**Output:** `.kici/types/secrets.d.ts`
|
|
1136
|
+
|
|
1137
|
+
**Examples:**
|
|
1138
|
+
|
|
1139
|
+
```bash
|
|
1140
|
+
# Generate types from orchestrator
|
|
1141
|
+
kici types
|
|
1142
|
+
|
|
1143
|
+
# Use custom .kici directory
|
|
1144
|
+
kici types --kici-dir packages/app/.kici
|
|
1145
|
+
```
|
|
1146
|
+
|
|
1147
|
+
**How it works:**
|
|
1148
|
+
|
|
1149
|
+
1. Fetches all environment metadata (environment names and secret key names) from the orchestrator
|
|
1150
|
+
2. Generates a `.d.ts` file that augments `@kici-dev/sdk`'s `KnownSecretKeys` and `EnvironmentSecrets` interfaces
|
|
1151
|
+
3. Writes the file to `.kici/types/secrets.d.ts`
|
|
1152
|
+
|
|
1153
|
+
After generating types, `ctx.secrets.get('MY_KEY')` and `ctx.secrets.expose('DB_HOST')` gain autocomplete and type checking in your IDE.
|
|
1154
|
+
|
|
1155
|
+
**Git workflow:** Commit the generated `.kici/types/secrets.d.ts` so team members get type checking without needing orchestrator access. Run `kici types` to refresh when environments change.
|
|
1156
|
+
|
|
1157
|
+
**Auto-regeneration:** `kici compile` automatically runs `kici types` after successful compilation when authenticated. See the [kici compile](https://docs.kici.dev/user/cli-reference/#kici-compile) section for details.
|
|
1158
|
+
|
|
1159
|
+
**Escape hatch:** For dynamic keys not in the generated types, use a cast: `(ctx.secrets as any).DYNAMIC_KEY`.
|
|
1160
|
+
|
|
1161
|
+
### kici fixture
|
|
1162
|
+
|
|
1163
|
+
Generate a fixture template for an event type. Useful for creating custom test payloads.
|
|
1164
|
+
|
|
1165
|
+
```bash
|
|
1166
|
+
kici fixture <event> [options]
|
|
1167
|
+
```
|
|
1168
|
+
|
|
1169
|
+
**Arguments:**
|
|
1170
|
+
|
|
1171
|
+
| Argument | Required | Description |
|
|
1172
|
+
| -------- | -------- | ----------------------------- |
|
|
1173
|
+
| `event` | yes | Event to generate fixture for |
|
|
1174
|
+
|
|
1175
|
+
**Valid events:** `pr:open`, `pr:sync`, `pr:close`, `pr:reopen`, `push`, `tag`, `comment`, `review`, `review_comment`, `release`, `dispatch`, `create`, `delete`, `status`, `workflow_run`, `fork`, `star`, `watch`, `kici_event`, `workflow_complete`, `job_complete`, `generic_webhook`, `schedule`, `lifecycle` (many support `:action` suffixes, e.g. `comment:edited`, `release:published`, `lifecycle:workflow_complete`). `webhook:<source>` is a shorthand alias for `generic_webhook:<source>`.
|
|
1176
|
+
|
|
1177
|
+
**Options:**
|
|
1178
|
+
|
|
1179
|
+
| Option | Default | Description |
|
|
1180
|
+
| ----------------- | ------- | ------------------------------- |
|
|
1181
|
+
| `--output <path>` | stdout | Write to file instead of stdout |
|
|
1182
|
+
|
|
1183
|
+
**Examples:**
|
|
1184
|
+
|
|
1185
|
+
```bash
|
|
1186
|
+
# Print fixture to stdout
|
|
1187
|
+
kici fixture pr:open
|
|
1188
|
+
|
|
1189
|
+
# Write fixture to file
|
|
1190
|
+
kici fixture pr:open --output fixtures/pr-open.json
|
|
1191
|
+
|
|
1192
|
+
# Generate push fixture
|
|
1193
|
+
kici fixture push --output fixtures/push.json
|
|
1194
|
+
```
|
|
1195
|
+
|
|
1196
|
+
Use generated fixtures as reference when writing test fixture files in `.kici/tests/`:
|
|
1197
|
+
|
|
1198
|
+
```bash
|
|
1199
|
+
kici fixture pr:open --output fixtures/pr-open-reference.json
|
|
1200
|
+
# Use the generated JSON as reference when writing .kici/tests/pr-open.ts
|
|
1201
|
+
```
|
|
1202
|
+
|
|
1203
|
+
### kici init
|
|
1204
|
+
|
|
1205
|
+
Initialize a `.kici/` directory with default workflow templates.
|
|
1206
|
+
|
|
1207
|
+
```bash
|
|
1208
|
+
kici init [options]
|
|
1209
|
+
```
|
|
1210
|
+
|
|
1211
|
+
**Options:**
|
|
1212
|
+
|
|
1213
|
+
| Option | Default | Description |
|
|
1214
|
+
| ------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
1215
|
+
| `--force` | `false` | Overwrite existing `.kici/` directory |
|
|
1216
|
+
| `--skip-install` | `false` | Create files without installing dependencies |
|
|
1217
|
+
| `--package-manager <npm\|pnpm\|yarn>` | auto-detect | Force a package manager for the install step (default: detect from your repo) |
|
|
1218
|
+
| `--mjs` | `false` | JavaScript-only mode (no TypeScript, no deps) |
|
|
1219
|
+
| `--no-agents-md` | writes `AGENTS.md` | Skip writing `.kici/AGENTS.md` (the LLM authoring context file) |
|
|
1220
|
+
| `--private-registry <url>` | none | Scaffold a workflow `registries:` entry pointing at `<url>` (e.g. CodeArtifact, GH Packages, Verdaccio) |
|
|
1221
|
+
| `--private-registry-scope <scope>` | none | Optional npm package scope (e.g. `@my-org`) for the private registry |
|
|
1222
|
+
| `--private-registry-secret <ref>` | `production:NPM_TOKEN` | Qualified secret reference (`env:NAME`) the private registry token comes from |
|
|
1223
|
+
|
|
1224
|
+
**Examples:**
|
|
1225
|
+
|
|
1226
|
+
```bash
|
|
1227
|
+
# Interactive initialization
|
|
1228
|
+
kici init
|
|
1229
|
+
|
|
1230
|
+
# Overwrite existing setup
|
|
1231
|
+
kici init --force
|
|
1232
|
+
|
|
1233
|
+
# Skip dependency install (faster, install manually later)
|
|
1234
|
+
kici init --skip-install
|
|
1235
|
+
|
|
1236
|
+
# Force a specific package manager (default: detect from your repo)
|
|
1237
|
+
kici init --package-manager pnpm
|
|
1238
|
+
|
|
1239
|
+
# JavaScript mode (no TypeScript)
|
|
1240
|
+
kici init --mjs
|
|
1241
|
+
|
|
1242
|
+
# Skip writing the AGENTS.md LLM authoring context file
|
|
1243
|
+
kici init --no-agents-md
|
|
1244
|
+
|
|
1245
|
+
# Scaffold a workflow registries entry for a private npm registry
|
|
1246
|
+
kici init --private-registry https://npm.pkg.github.com/ \
|
|
1247
|
+
--private-registry-scope @my-org \
|
|
1248
|
+
--private-registry-secret production:GITHUB_PACKAGES_TOKEN
|
|
1249
|
+
```
|
|
1250
|
+
|
|
1251
|
+
**What it creates:**
|
|
1252
|
+
|
|
1253
|
+
```
|
|
1254
|
+
.kici/
|
|
1255
|
+
workflows/
|
|
1256
|
+
hello-world.ts # Minimal push workflow
|
|
1257
|
+
pr-checks.ts # Comprehensive PR workflow
|
|
1258
|
+
tests/
|
|
1259
|
+
push-test.ts # Sample test fixture
|
|
1260
|
+
types/ # Directory for generated type declarations (kici types)
|
|
1261
|
+
package.json # Dependencies (@kici-dev/sdk)
|
|
1262
|
+
tsconfig.json # TypeScript configuration (includes types/**/*.d.ts)
|
|
1263
|
+
.kiciignore # Default exclusion patterns for test uploads
|
|
1264
|
+
```
|
|
1265
|
+
|
|
1266
|
+
In interactive mode (TTY), `kici init` prompts you to:
|
|
1267
|
+
|
|
1268
|
+
1. Select which workflow templates to include
|
|
1269
|
+
2. Optionally install a pre-commit hook
|
|
1270
|
+
|
|
1271
|
+
**Package manager:** the dependency install step uses the package manager detected for your repo — the `packageManager` field in the nearest `package.json` (Corepack convention), then a lockfile in the project root (`pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, `package-lock.json` → npm), then the package manager that invoked `kici` (`pnpm dlx` / `yarn dlx` / `npx`), defaulting to npm. Pass `--package-manager <npm|pnpm|yarn>` to override detection, or `--skip-install` to set up the files and install later yourself.
|
|
1272
|
+
|
|
1273
|
+
**Development mode:** When `KICI_DEV=true` or `package.json` has `"kici": { "development": true }`, the generated `package.json` uses prerelease-compatible version ranges (`>=0.0.1-0`) so npm resolves Verdaccio's prerelease builds.
|
|
1274
|
+
|
|
1275
|
+
### kici hook install
|
|
1276
|
+
|
|
1277
|
+
Install a pre-commit hook that runs `kici compile` before each commit.
|
|
1278
|
+
|
|
1279
|
+
```bash
|
|
1280
|
+
kici hook install [options]
|
|
1281
|
+
```
|
|
1282
|
+
|
|
1283
|
+
**Options:**
|
|
1284
|
+
|
|
1285
|
+
| Option | Default | Description |
|
|
1286
|
+
| ------- | ------- | ------------------------------------------ |
|
|
1287
|
+
| `--git` | `false` | Use raw git hook (`.git/hooks/pre-commit`) |
|
|
1288
|
+
|
|
1289
|
+
**Examples:**
|
|
1290
|
+
|
|
1291
|
+
```bash
|
|
1292
|
+
# Auto-detect hook tool (husky, lint-staged, etc.)
|
|
1293
|
+
kici hook install
|
|
1294
|
+
|
|
1295
|
+
# Force raw git hook
|
|
1296
|
+
kici hook install --git
|
|
1297
|
+
```
|
|
1298
|
+
|
|
1299
|
+
The command auto-detects existing hook tools in your project:
|
|
1300
|
+
|
|
1301
|
+
- **Husky**: Adds to `.husky/pre-commit`
|
|
1302
|
+
- **lint-staged**: Adds to lint-staged configuration
|
|
1303
|
+
- **Raw git**: Writes `.git/hooks/pre-commit`
|
|
1304
|
+
|
|
1305
|
+
If multiple tools are detected, you are prompted to choose.
|
|
1306
|
+
|
|
1307
|
+
### kici endpoints
|
|
1308
|
+
|
|
1309
|
+
List all webhook entrypoints for the current project. Reads the compiled lock file and displays webhook URLs grouped by type (git provider, generic webhooks, scheduled, event-driven).
|
|
1310
|
+
|
|
1311
|
+
```bash
|
|
1312
|
+
kici endpoints [options]
|
|
1313
|
+
```
|
|
1314
|
+
|
|
1315
|
+
**Options:**
|
|
1316
|
+
|
|
1317
|
+
| Option | Default | Description |
|
|
1318
|
+
| ------------------- | ------- | ----------------------- |
|
|
1319
|
+
| `--kici-dir <path>` | `.kici` | Path to .kici directory |
|
|
1320
|
+
|
|
1321
|
+
**Prerequisites:** Run `kici compile` first to generate the lock file.
|
|
1322
|
+
|
|
1323
|
+
**Examples:**
|
|
1324
|
+
|
|
1325
|
+
```bash
|
|
1326
|
+
# List all webhook entrypoints
|
|
1327
|
+
kici endpoints
|
|
1328
|
+
|
|
1329
|
+
# Custom .kici directory
|
|
1330
|
+
kici endpoints --kici-dir packages/app/.kici
|
|
1331
|
+
```
|
|
1332
|
+
|
|
1333
|
+
### kici workflows list
|
|
1334
|
+
|
|
1335
|
+
List permanently registered workflows on the orchestrator.
|
|
1336
|
+
|
|
1337
|
+
```bash
|
|
1338
|
+
kici workflows list [options]
|
|
1339
|
+
```
|
|
1340
|
+
|
|
1341
|
+
**Options:**
|
|
1342
|
+
|
|
1343
|
+
| Option | Default | Description |
|
|
1344
|
+
| ----------------------- | ------- | ---------------------------------------------- |
|
|
1345
|
+
| `--json` | `false` | Output as JSON |
|
|
1346
|
+
| `--stale <duration>` | none | Filter stale registrations (e.g., `30d`, `7d`) |
|
|
1347
|
+
| `--trigger-type <type>` | none | Filter by trigger type |
|
|
1348
|
+
| `--repo <repo>` | none | Filter by repository |
|
|
1349
|
+
|
|
1350
|
+
**Examples:**
|
|
1351
|
+
|
|
1352
|
+
```bash
|
|
1353
|
+
# List all registered workflows
|
|
1354
|
+
kici workflows list
|
|
1355
|
+
|
|
1356
|
+
# JSON output for scripting
|
|
1357
|
+
kici workflows list --json
|
|
1358
|
+
|
|
1359
|
+
# Show workflows not updated in 30 days
|
|
1360
|
+
kici workflows list --stale 30d
|
|
1361
|
+
|
|
1362
|
+
# Filter by trigger type
|
|
1363
|
+
kici workflows list --trigger-type push
|
|
1364
|
+
|
|
1365
|
+
# Filter by repository
|
|
1366
|
+
kici workflows list --repo my-org/my-repo
|
|
1367
|
+
```
|
|
1368
|
+
|
|
1369
|
+
### kici docs
|
|
1370
|
+
|
|
1371
|
+
Open the KiCI documentation site in the default browser. With the `llm` subcommand, print the LLM-friendly documentation bundle that ships with `@kici-dev/compiler` — pipe it into a coding agent's context buffer to brief the agent on authoring conventions without an internet round-trip.
|
|
1372
|
+
|
|
1373
|
+
```bash
|
|
1374
|
+
kici docs # open https://kici.dev/docs/
|
|
1375
|
+
kici docs --no-open # print the URL instead of opening a browser
|
|
1376
|
+
kici docs llm # print the llms.txt index (a router over the task bundles)
|
|
1377
|
+
kici docs llm sdk # print the SDK task bundle
|
|
1378
|
+
kici docs llm full # print llms-full.txt (every page in one file)
|
|
1379
|
+
kici docs llm sdk --out sdk-context.md # write a bundle to a file
|
|
1380
|
+
```
|
|
1381
|
+
|
|
1382
|
+
**Examples:**
|
|
1383
|
+
|
|
1384
|
+
```bash
|
|
1385
|
+
# Open the docs site in your browser
|
|
1386
|
+
kici docs
|
|
1387
|
+
|
|
1388
|
+
# Pipe just the SDK bundle into a coding agent (small, task-scoped context)
|
|
1389
|
+
kici docs llm sdk | claude -- "Read this and help me author a deploy workflow"
|
|
1390
|
+
|
|
1391
|
+
# Save the router index for offline reference
|
|
1392
|
+
kici docs llm --out kici-llms-index.txt
|
|
1393
|
+
```
|
|
1394
|
+
|
|
1395
|
+
Bundles are regenerated from `docs/` every time `@kici-dev/compiler` is built, so they always match your installed CLI version. The index lists each task bundle — `getting-started`, `sdk`, `cli`, `patterns`, `features`, `providers`, `architecture` — with its size and a one-line purpose; pass the bundle id as the topic. Every cross-reference link inside a bundle is an absolute `docs.kici.dev` URL. The same files are published online following the [llms.txt convention](https://llmstxt.org/).
|
|
1396
|
+
|
|
1397
|
+
### kici admin
|
|
1398
|
+
|
|
1399
|
+
Operator-facing commands for running instances.
|
|
1400
|
+
|
|
1401
|
+
#### kici admin drain-worker
|
|
1402
|
+
|
|
1403
|
+
Trigger graceful drain on a worker instance. Sends a POST request to the worker's `/drain` endpoint.
|
|
1404
|
+
|
|
1405
|
+
```bash
|
|
1406
|
+
kici admin drain-worker [options]
|
|
1407
|
+
```
|
|
1408
|
+
|
|
1409
|
+
**Options:**
|
|
1410
|
+
|
|
1411
|
+
| Option | Required | Description |
|
|
1412
|
+
| ------------- | -------- | --------------------------------------------- |
|
|
1413
|
+
| `--url <url>` | yes | Worker URL (e.g., `http://worker-host:10143`) |
|
|
1414
|
+
|
|
1415
|
+
**Examples:**
|
|
1416
|
+
|
|
1417
|
+
```bash
|
|
1418
|
+
# Drain a local worker
|
|
1419
|
+
kici admin drain-worker --url http://localhost:10143
|
|
1420
|
+
|
|
1421
|
+
# Drain a remote worker
|
|
1422
|
+
kici admin drain-worker --url http://worker-2.internal:10143
|
|
1423
|
+
```
|
|
1424
|
+
|
|
1425
|
+
**Exit codes:**
|
|
1426
|
+
|
|
1427
|
+
| Code | Meaning |
|
|
1428
|
+
| ---- | ----------------------------------- |
|
|
1429
|
+
| 0 | Drain request accepted |
|
|
1430
|
+
| 1 | Error (unreachable or request fail) |
|
|
1431
|
+
|
|
1432
|
+
### kici verify-attestation
|
|
1433
|
+
|
|
1434
|
+
Verify a KiCI build-provenance attestation bundle offline. A bundle is the signed package a workflow step produces via `ctx.attestProvenance(...)`: a DSSE-wrapped SLSA in-toto statement, the ephemeral public key that signed it, and the KiCI identity token that anchors the build context. For the end-to-end attest → verify → view journey, see the [build provenance guide](https://docs.kici.dev/user/provenance/). Verification establishes the full chain — the identity token verifies against the trusted issuer's JWKS, the DSSE signature verifies against the bundled key, and the statement's build context must match the token's claims (a mismatch is a hard failure). When an `[artifact]` is given, its SHA-256 digest is also matched against the attestation subject.
|
|
1435
|
+
|
|
1436
|
+
```bash
|
|
1437
|
+
kici verify-attestation [artifact] --bundle <path-or-url> --trust-root <url-or-file> [options]
|
|
1438
|
+
```
|
|
1439
|
+
|
|
1440
|
+
**Arguments:**
|
|
1441
|
+
|
|
1442
|
+
| Argument | Required | Description |
|
|
1443
|
+
| ------------ | -------- | --------------------------------------------------------------------- |
|
|
1444
|
+
| `[artifact]` | no | Artifact path to digest-check against the attestation subject digest. |
|
|
1445
|
+
|
|
1446
|
+
**Options:**
|
|
1447
|
+
|
|
1448
|
+
| Option | Required | Description |
|
|
1449
|
+
| ---------------------------- | -------- | ----------------------------------------------------------------------------------------- |
|
|
1450
|
+
| `--bundle <path-or-url>` | yes | Path or `http(s)` URL to the attestation bundle JSON. |
|
|
1451
|
+
| `--trust-root <url-or-file>` | yes | Trusted issuer (see below). The token issuer is pinned to it, never taken from the token. |
|
|
1452
|
+
| `--audience <aud>` | no | Expected token audience (defaults to the KiCI provenance audience). |
|
|
1453
|
+
| `--json` | no | Print the structured verification result as JSON instead of human-readable output. |
|
|
1454
|
+
|
|
1455
|
+
**Trust root:** the verifier never trusts the issuer named inside the token — you supply the trusted issuer out-of-band via `--trust-root`, in one of two forms:
|
|
1456
|
+
|
|
1457
|
+
- **Online — an HTTPS issuer URL.** The verifier fetches `<url>/.well-known/openid-configuration`, reads its `issuer` and `jwks_uri`, and fetches the JWKS. The token's `iss` is pinned to the discovery document's `issuer`.
|
|
1458
|
+
- **Offline — a self-contained trust-root file.** A local JSON file with the issuer and JWKS inlined, so no network access is needed (air-gapped verification):
|
|
1459
|
+
|
|
1460
|
+
```json
|
|
1461
|
+
{
|
|
1462
|
+
"issuer": "https://platform.example/issuer",
|
|
1463
|
+
"jwks": {
|
|
1464
|
+
"keys": [
|
|
1465
|
+
{ "kty": "EC", "crv": "P-256", "x": "...", "y": "...", "alg": "ES256", "kid": "..." }
|
|
1466
|
+
]
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
```
|
|
1470
|
+
|
|
1471
|
+
**Examples:**
|
|
1472
|
+
|
|
1473
|
+
```bash
|
|
1474
|
+
# Online: verify a bundle against a deployed issuer, digest-checking the artifact
|
|
1475
|
+
kici verify-attestation ./dist/app.tgz \
|
|
1476
|
+
--bundle ./app.tgz.kici.json \
|
|
1477
|
+
--trust-root https://platform.example/issuer
|
|
1478
|
+
|
|
1479
|
+
# Offline / air-gapped: verify against a self-contained trust-root file
|
|
1480
|
+
kici verify-attestation ./dist/app.tgz \
|
|
1481
|
+
--bundle ./app.tgz.kici.json \
|
|
1482
|
+
--trust-root ./kici-trust-root.json
|
|
1483
|
+
|
|
1484
|
+
# Machine-readable result for scripting
|
|
1485
|
+
kici verify-attestation --bundle ./app.tgz.kici.json \
|
|
1486
|
+
--trust-root https://platform.example/issuer --json
|
|
1487
|
+
```
|
|
1488
|
+
|
|
1489
|
+
**Exit codes:**
|
|
1490
|
+
|
|
1491
|
+
| Code | Meaning |
|
|
1492
|
+
| ---- | ----------------------------------------------------------------------------------- |
|
|
1493
|
+
| 0 | Verified — signature, identity, build context (and digest, if checked) all pass |
|
|
1494
|
+
| 1 | Not verified, or an error (missing flag, unreadable bundle, unreachable trust root) |
|
|
1495
|
+
|
|
1496
|
+
## Workflow discovery
|
|
1497
|
+
|
|
1498
|
+
The CLI discovers workflows by scanning `.kici/workflows/*.ts` (or `.mjs` in MJS mode). Each file should `export default` a single workflow:
|
|
1499
|
+
|
|
1500
|
+
```typescript
|
|
1501
|
+
// .kici/workflows/ci.ts
|
|
1502
|
+
import { workflow, job, step, pr } from '@kici-dev/sdk';
|
|
1503
|
+
|
|
1504
|
+
export default workflow('ci', {
|
|
1505
|
+
on: pr(),
|
|
1506
|
+
jobs: [
|
|
1507
|
+
/* ... */
|
|
1508
|
+
],
|
|
1509
|
+
});
|
|
1510
|
+
```
|
|
1511
|
+
|
|
1512
|
+
Multiple workflow files are supported -- each becomes a separate workflow in `kici.lock.json`.
|
|
1513
|
+
|
|
1514
|
+
## Lock file
|
|
1515
|
+
|
|
1516
|
+
The `kici compile` command produces `.kici/kici.lock.json` inside the `.kici` directory. This file:
|
|
1517
|
+
|
|
1518
|
+
- Contains all workflow definitions in a portable JSON format
|
|
1519
|
+
- Is used by the orchestrator to evaluate triggers without code checkout
|
|
1520
|
+
- Should be committed to version control
|
|
1521
|
+
- Is regenerated on every `kici compile` run
|
|
1522
|
+
|
|
1523
|
+
Use `kici compile --check` in CI to validate that workflows are correct without writing files. For the full story on drift, pre-commit/CI, and agent-side verification, see [Lock file and workflow drift](https://docs.kici.dev/user/lock-file-and-drift/).
|
|
1524
|
+
|
|
1525
|
+
## Exit codes
|
|
1526
|
+
|
|
1527
|
+
All commands follow a consistent exit code convention:
|
|
1528
|
+
|
|
1529
|
+
| Code | Meaning |
|
|
1530
|
+
| ---- | -------------------- |
|
|
1531
|
+
| 0 | Success |
|
|
1532
|
+
| 1 | Failure (see output) |
|
|
1533
|
+
|
|
1534
|
+
## Debug output
|
|
1535
|
+
|
|
1536
|
+
Use `--debug` (on `kici run local`, `kici run remote`, `kici test`) or `--verbose` (on `kici compile`) for detailed output:
|
|
1537
|
+
|
|
1538
|
+
```bash
|
|
1539
|
+
# Shows trigger matching, rule evaluation, decision traces
|
|
1540
|
+
kici run local push --debug
|
|
1541
|
+
|
|
1542
|
+
# Shows detailed compilation steps
|
|
1543
|
+
kici compile --verbose
|
|
1544
|
+
|
|
1545
|
+
# Shows trigger matching preview
|
|
1546
|
+
kici test pr:open --debug
|
|
1547
|
+
```
|
|
1548
|
+
|
|
1549
|
+
Set `KICI_DEBUG=true` for additional internal debug output across all commands.
|
|
1550
|
+
|
|
1551
|
+
## Environment variables
|
|
1552
|
+
|
|
1553
|
+
| Variable | Description |
|
|
1554
|
+
| ------------ | ----------------------------------------- |
|
|
1555
|
+
| `KICI_DEV` | Set to `true` for development mode |
|
|
1556
|
+
| `KICI_DEBUG` | Set to `true` for verbose internal output |
|
|
1557
|
+
| `CI` | When `true`, disables interactive prompts |
|
|
1558
|
+
|
|
1559
|
+
## See also
|
|
1560
|
+
|
|
1561
|
+
- [Getting started](https://docs.kici.dev/user/getting-started/) -- install the SDK and write your first workflow
|
|
1562
|
+
- [Testing guide](https://docs.kici.dev/user/testing-guide/) -- writing fixtures, remote test runs, secret contexts, and repo state transfer
|
|
1563
|
+
- [SDK reference](https://docs.kici.dev/user/sdk-reference/) -- complete API for the workflow definitions that the CLI compiles
|
|
1564
|
+
- [Workflow patterns](https://docs.kici.dev/user/workflow-patterns/) -- example workflows to compile and test with these commands
|
|
1565
|
+
|
|
1566
|
+
---
|
|
1567
|
+
|
|
1568
|
+
## Lifecycle hooks
|
|
1569
|
+
|
|
1570
|
+
Source: https://docs.kici.dev/user/hooks/
|
|
1571
|
+
|
|
1572
|
+
Hooks are callbacks that run at specific points in the execution lifecycle. They let you react to outcomes (cancellation, success, failure) and perform cleanup without affecting the execution flow.
|
|
1573
|
+
|
|
1574
|
+
## Hook types
|
|
1575
|
+
|
|
1576
|
+
KiCI supports six hook types at three levels (step, job, workflow):
|
|
1577
|
+
|
|
1578
|
+
| Hook | When it runs | Available on |
|
|
1579
|
+
| ------------ | ------------------------------------ | ------------------- |
|
|
1580
|
+
| `onCancel` | After step/job/workflow is cancelled | Step, Job, Workflow |
|
|
1581
|
+
| `cleanup` | Always (success, failure, or cancel) | Step, Job, Workflow |
|
|
1582
|
+
| `onSuccess` | After job/workflow succeeds | Job, Workflow |
|
|
1583
|
+
| `onFailure` | After job/workflow fails | Job, Workflow |
|
|
1584
|
+
| `beforeStep` | Before each step in a job | Job |
|
|
1585
|
+
| `afterStep` | After each step in a job | Job |
|
|
1586
|
+
|
|
1587
|
+
## Basic usage
|
|
1588
|
+
|
|
1589
|
+
### Job-level hooks
|
|
1590
|
+
|
|
1591
|
+
```typescript
|
|
1592
|
+
import { workflow, job, step, push } from '@kici-dev/sdk';
|
|
1593
|
+
|
|
1594
|
+
export default workflow('deploy', {
|
|
1595
|
+
on: push({ branches: ['main'] }),
|
|
1596
|
+
jobs: [
|
|
1597
|
+
job('deploy-prod', {
|
|
1598
|
+
runsOn: 'linux',
|
|
1599
|
+
steps: [
|
|
1600
|
+
step('deploy', async ({ $ }) => {
|
|
1601
|
+
await $`kubectl apply -f manifests/`;
|
|
1602
|
+
}),
|
|
1603
|
+
],
|
|
1604
|
+
onCancel: async (ctx) => {
|
|
1605
|
+
console.log(`Deploy cancelled: ${ctx.outcome.reason}`);
|
|
1606
|
+
await ctx.$`kubectl rollout undo deployment/app`;
|
|
1607
|
+
},
|
|
1608
|
+
cleanup: async (ctx) => {
|
|
1609
|
+
// Always runs -- release lock, notify team, etc.
|
|
1610
|
+
await ctx.$`curl -X POST https://slack.com/webhook -d '{"text": "Deploy ${ctx.outcome.status}"}'`;
|
|
1611
|
+
},
|
|
1612
|
+
onSuccess: async (ctx) => {
|
|
1613
|
+
console.log(`Deploy succeeded in ${ctx.outcome.duration}ms`);
|
|
1614
|
+
},
|
|
1615
|
+
onFailure: async (ctx) => {
|
|
1616
|
+
console.log(`Deploy failed at step: ${ctx.outcome.failedStep}`);
|
|
1617
|
+
},
|
|
1618
|
+
gracePeriod: 60, // 60 seconds before SIGKILL on cancel
|
|
1619
|
+
}),
|
|
1620
|
+
],
|
|
1621
|
+
});
|
|
1622
|
+
```
|
|
1623
|
+
|
|
1624
|
+
### Step-level hooks
|
|
1625
|
+
|
|
1626
|
+
```typescript
|
|
1627
|
+
step('download-artifacts', {
|
|
1628
|
+
run: async ({ $ }) => {
|
|
1629
|
+
await $`wget https://artifacts.example.com/build.tar.gz`;
|
|
1630
|
+
},
|
|
1631
|
+
onCancel: async (ctx) => {
|
|
1632
|
+
// Clean up partial downloads
|
|
1633
|
+
await ctx.$`rm -f build.tar.gz`;
|
|
1634
|
+
},
|
|
1635
|
+
cleanup: async (ctx) => {
|
|
1636
|
+
await ctx.$`rm -rf /tmp/staging`;
|
|
1637
|
+
},
|
|
1638
|
+
});
|
|
1639
|
+
```
|
|
1640
|
+
|
|
1641
|
+
### Workflow-level hooks
|
|
1642
|
+
|
|
1643
|
+
```typescript
|
|
1644
|
+
workflow('ci', {
|
|
1645
|
+
on: push({ branches: ['main'] }),
|
|
1646
|
+
jobs: [
|
|
1647
|
+
/* ... */
|
|
1648
|
+
],
|
|
1649
|
+
onCancel: async (ctx) => {
|
|
1650
|
+
// Notify when any job in the workflow is cancelled
|
|
1651
|
+
console.log('CI workflow cancelled');
|
|
1652
|
+
},
|
|
1653
|
+
cleanup: async (ctx) => {
|
|
1654
|
+
// Always runs after all jobs complete
|
|
1655
|
+
console.log(`CI workflow finished with status: ${ctx.outcome.status}`);
|
|
1656
|
+
},
|
|
1657
|
+
});
|
|
1658
|
+
```
|
|
1659
|
+
|
|
1660
|
+
## Hook context
|
|
1661
|
+
|
|
1662
|
+
Hook functions receive the same `StepContext` as regular steps (`$`, `ctx`, `log`, `env`), plus an `outcome` object with metadata about the execution result.
|
|
1663
|
+
|
|
1664
|
+
### ctx.outcome
|
|
1665
|
+
|
|
1666
|
+
```typescript
|
|
1667
|
+
interface OutcomeMetadata {
|
|
1668
|
+
/** Final status of the job/workflow. */
|
|
1669
|
+
status: 'cancelled' | 'success' | 'failed';
|
|
1670
|
+
/** Reason for cancellation (e.g., "User requested", "Superseded by run #42"). */
|
|
1671
|
+
reason?: string;
|
|
1672
|
+
/** Name of the step that caused failure (for onFailure hooks). */
|
|
1673
|
+
failedStep?: string;
|
|
1674
|
+
/** Outputs from all completed steps. */
|
|
1675
|
+
stepOutputs: Record<string, unknown>;
|
|
1676
|
+
/** Total execution duration in milliseconds. */
|
|
1677
|
+
duration: number;
|
|
1678
|
+
}
|
|
1679
|
+
```
|
|
1680
|
+
|
|
1681
|
+
### Capabilities
|
|
1682
|
+
|
|
1683
|
+
Hooks can do everything regular steps can:
|
|
1684
|
+
|
|
1685
|
+
- Run shell commands via `$`
|
|
1686
|
+
- Set environment variables via `ctx.setEnv()` and prepend to `PATH` via `ctx.addPath()`
|
|
1687
|
+
- Access previous step outputs via `ctx.outputsOf()` and `ctx.jobOutputs()`
|
|
1688
|
+
- Publish encrypted secret outputs via `ctx.setSecretOutput()`
|
|
1689
|
+
- Log via `log.info()`, `log.error()`, etc.
|
|
1690
|
+
|
|
1691
|
+
## Hook timeout
|
|
1692
|
+
|
|
1693
|
+
Each hook has a timeout (default: 5 minutes). You can customize it per-hook:
|
|
1694
|
+
|
|
1695
|
+
```typescript
|
|
1696
|
+
job('deploy', {
|
|
1697
|
+
runsOn: 'linux',
|
|
1698
|
+
steps: [
|
|
1699
|
+
/* ... */
|
|
1700
|
+
],
|
|
1701
|
+
cleanup: {
|
|
1702
|
+
run: async (ctx) => {
|
|
1703
|
+
await ctx.$`./lengthy-cleanup.sh`;
|
|
1704
|
+
},
|
|
1705
|
+
timeout: 10 * 60 * 1000, // 10 minutes in ms
|
|
1706
|
+
},
|
|
1707
|
+
});
|
|
1708
|
+
```
|
|
1709
|
+
|
|
1710
|
+
## Hook execution order
|
|
1711
|
+
|
|
1712
|
+
Hooks execute inside-out on cancellation (like stack unwinding):
|
|
1713
|
+
|
|
1714
|
+
1. **Step-level** cleanup (on the cancelled step)
|
|
1715
|
+
2. **Job-level** onCancel, then cleanup
|
|
1716
|
+
3. **Workflow-level** onCancel, then cleanup
|
|
1717
|
+
|
|
1718
|
+
On success: step afterStep (after each step), then job onSuccess + cleanup, then workflow onSuccess + cleanup.
|
|
1719
|
+
|
|
1720
|
+
On failure: job onFailure + cleanup, then workflow onFailure + cleanup.
|
|
1721
|
+
|
|
1722
|
+
**cleanup always runs** -- regardless of whether the outcome was success, failure, or cancel.
|
|
1723
|
+
|
|
1724
|
+
## Hooks are observers
|
|
1725
|
+
|
|
1726
|
+
Hooks follow the "one mechanism per concern" principle:
|
|
1727
|
+
|
|
1728
|
+
- **Rules** control whether a step/job executes (conditional logic)
|
|
1729
|
+
- **Hooks** react to execution outcomes (lifecycle callbacks)
|
|
1730
|
+
|
|
1731
|
+
Hooks cannot short-circuit step execution or change the execution flow. They observe and respond.
|
|
1732
|
+
|
|
1733
|
+
## beforeStep and afterStep
|
|
1734
|
+
|
|
1735
|
+
These job-level hooks run around every step in the job:
|
|
1736
|
+
|
|
1737
|
+
```typescript
|
|
1738
|
+
job('test', {
|
|
1739
|
+
runsOn: 'linux',
|
|
1740
|
+
beforeStep: async (ctx) => {
|
|
1741
|
+
console.log(`Starting step at ${new Date().toISOString()}`);
|
|
1742
|
+
},
|
|
1743
|
+
afterStep: async (ctx) => {
|
|
1744
|
+
console.log(`Step completed with status: ${ctx.outcome.status}`);
|
|
1745
|
+
},
|
|
1746
|
+
steps: [
|
|
1747
|
+
step('lint', async ({ $ }) => {
|
|
1748
|
+
await $`pnpm lint`;
|
|
1749
|
+
}),
|
|
1750
|
+
step('test', async ({ $ }) => {
|
|
1751
|
+
await $`pnpm test`;
|
|
1752
|
+
}),
|
|
1753
|
+
],
|
|
1754
|
+
});
|
|
1755
|
+
```
|
|
1756
|
+
|
|
1757
|
+
`afterStep` runs immediately after its step, before the next step starts (not deferred to the end of the job).
|
|
1758
|
+
|
|
1759
|
+
## Step-level rules
|
|
1760
|
+
|
|
1761
|
+
Step-level rules control whether a step executes, evaluated at runtime by the agent:
|
|
1762
|
+
|
|
1763
|
+
```typescript
|
|
1764
|
+
import { step, rule, skip, isEventType } from '@kici-dev/sdk';
|
|
1765
|
+
|
|
1766
|
+
step('deploy', {
|
|
1767
|
+
run: async ({ $ }) => {
|
|
1768
|
+
await $`kubectl apply -f manifests/`;
|
|
1769
|
+
},
|
|
1770
|
+
rules: [
|
|
1771
|
+
rule('only on main pushes', (ctx) => {
|
|
1772
|
+
if (!isEventType(ctx.event, 'push')) return false;
|
|
1773
|
+
return ctx.event.payload.ref === 'refs/heads/main';
|
|
1774
|
+
}),
|
|
1775
|
+
],
|
|
1776
|
+
});
|
|
1777
|
+
|
|
1778
|
+
// Or use skip() for explicit skip with a reason
|
|
1779
|
+
step('optional-check', {
|
|
1780
|
+
run: async ({ $ }) => {
|
|
1781
|
+
await $`./optional-check.sh`;
|
|
1782
|
+
},
|
|
1783
|
+
rules: [skip('not needed in CI', () => true)],
|
|
1784
|
+
});
|
|
1785
|
+
```
|
|
1786
|
+
|
|
1787
|
+
When a rule returns `false`, the step is reported as `skipped` and subsequent steps continue normally. Skipped steps don't cause the job to fail.
|
|
1788
|
+
|
|
1789
|
+
Step rules have access to runtime context via `RuleContext`: `event` (typed discriminated union), `changedFiles`, `env`, and `$`. They evaluate agent-side (unlike job-level rules which evaluate at the orchestrator during trigger matching).
|
|
1790
|
+
|
|
1791
|
+
## Hook failure behavior
|
|
1792
|
+
|
|
1793
|
+
If a hook throws an error or times out:
|
|
1794
|
+
|
|
1795
|
+
- The job status changes to `failed` with a compound reason (e.g., "cancelled (onCancel hook failed: Connection timeout)")
|
|
1796
|
+
- Remaining hooks for that level are skipped
|
|
1797
|
+
- The failure is visible in the dashboard as a failed hook step
|
|
1798
|
+
- Force cancel kills running hooks immediately via SIGKILL
|
|
1799
|
+
|
|
1800
|
+
This behavior is consistent across all hook types.
|
|
1801
|
+
|
|
1802
|
+
---
|
|
1803
|
+
|
|
1804
|
+
_Source: `packages/sdk/src/hooks/`, `packages/sdk/src/types.ts`_
|
|
1805
|
+
|
|
1806
|
+
---
|
|
1807
|
+
|
|
1808
|
+
## Lock file and workflow drift
|
|
1809
|
+
|
|
1810
|
+
Source: https://docs.kici.dev/user/lock-file-and-drift/
|
|
1811
|
+
|
|
1812
|
+
KiCI uses a **two-artifact model**: TypeScript workflows are the source of truth; the lock file (`kici.lock.json`) is the execution contract. The orchestrator reads only the lock file to match triggers and decide cache vs build. Keeping these in sync is important.
|
|
1813
|
+
|
|
1814
|
+
## Why the lock file matters
|
|
1815
|
+
|
|
1816
|
+
- **Orchestrator** fetches the lock file at the commit SHA and uses it to evaluate triggers and to look up the cached `.kici/` source tarball + `node_modules` tarball. It never runs your TypeScript.
|
|
1817
|
+
- **Agents** download the cached source tarball (or, on cold cache, the build agent clones + packs it), register the shared TypeScript loader hook, and dynamic-`import()` the workflow `.ts` directly. The lock file's per-workflow `contentHash` identifies the expected raw-source bytes and is verified against the extracted source before any step runs.
|
|
1818
|
+
|
|
1819
|
+
If you change a workflow file (`.ts`) but do **not** regenerate and commit the lock file, the repo at that commit has **drift**: the lock file no longer matches the source. Triggers and cache keys can be wrong, and runs can fail with a clear “stale lock file” error once the agent verifies the hash.
|
|
1820
|
+
|
|
1821
|
+
## Lock file structure
|
|
1822
|
+
|
|
1823
|
+
The lock file (`kici.lock.json`) is a JSON file with the following top-level fields:
|
|
1824
|
+
|
|
1825
|
+
| Field | Description |
|
|
1826
|
+
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1827
|
+
| `schemaVersion` | Lock file schema version (currently 20). Incremented on breaking format changes. |
|
|
1828
|
+
| `source` | Reference to the source file and export (e.g., `{ file: “.kici/workflows/ci.ts”, export: “#default” }`). |
|
|
1829
|
+
| `contentHash` | SHA-256 of the serialized lock file content (excluding itself). Changes when any workflow, trigger, or job changes. |
|
|
1830
|
+
| `lockfileHash` | SHA-256 of the detected package manager's lockfile, used as the dependency cache key. The lockfile is `.kici/package-lock.json` for npm, or the repo-root `pnpm-lock.yaml` / `yarn.lock` for a pnpm/yarn workspace; the hash input is prefixed with the manager name so a manager change is a guaranteed cache miss. Omitted when no lockfile exists. |
|
|
1831
|
+
| `workflows` | Array of workflow entries, each with its own `contentHash`, `compileSchemaVersion`, triggers, and jobs. |
|
|
1832
|
+
|
|
1833
|
+
Each workflow entry includes:
|
|
1834
|
+
|
|
1835
|
+
| Field | Description |
|
|
1836
|
+
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1837
|
+
| `name` | Workflow name. |
|
|
1838
|
+
| `source` | Per-workflow source file and export reference. |
|
|
1839
|
+
| `contentHash` | SHA-256 of the raw workflow source mixed with `compileSchemaVersion` (and an `assetDigest` of declared `hashFiles` when present): `SHA-256(compileSchemaVersion + ":" + rawSource [+ "\0" + assetDigest])`. The orchestrator uses this as the source-tarball cache key and the agent re-computes it against the extracted source to detect drift. |
|
|
1840
|
+
| `compileSchemaVersion` | Compiler schema version used when computing `contentHash` (currently `5`). The hash input is line-ending-normalized (CRLF → LF) so a lock file produced on Linux matches the agent's hash on Windows where Git's `core.autocrlf=true` rewrites checked-out text to CRLF. Bumping the schema version invalidates every existing source cache entry even if source is unchanged, which is the correct behavior when the compile-time or runtime contract changes. |
|
|
1841
|
+
| `triggers` | Trigger definitions extracted from the workflow (used by the orchestrator for event matching). |
|
|
1842
|
+
| `jobs` | Job definitions with scheduling metadata (runsOn, needs, matrix, environment, concurrency, container, checkout, gracePeriod, label routing, dynamic fields, etc.). |
|
|
1843
|
+
| `rules` | Workflow-level conditional rules (optional). Stored as dynamic references since rule functions cannot be serialized. |
|
|
1844
|
+
| `description` | Optional workflow description. |
|
|
1845
|
+
| `hashFiles` | Declared glob patterns for extra files included in the content hash (optional). See [extra files in the content hash](https://docs.kici.dev/user/lock-file-and-drift/#extra-files-in-the-content-hash-hashfiles). |
|
|
1846
|
+
| `resolvedHashFiles` | Resolved file paths from `hashFiles` at compile time (optional). Recorded so the agent can verify without re-discovering. |
|
|
1847
|
+
| `contexts` | Secret contexts declared by the workflow (optional). The orchestrator validates access to each context before dispatch. |
|
|
1848
|
+
| `registries` | Private npm registry declarations the agent authenticates against before install (optional): `url`, `scope`, `tokenSecret` reference, `alwaysAuth`. Resolved token bytes never appear in the lock file. See [private registries](https://docs.kici.dev/user/private-registries/). |
|
|
1849
|
+
| `installEnv` | Extra qualified secret refs (`<environment>:<secret-name>`) projected as env vars on the install subprocess for use with a committed `.kici/.npmrc` (optional). See [private registries](https://docs.kici.dev/user/private-registries/). |
|
|
1850
|
+
| `concurrency` | Workflow-level concurrency config: `hasGroup`, `cancelInProgress`, `max` (optional). See [concurrency groups](https://docs.kici.dev/user/concurrency/). |
|
|
1851
|
+
| `timeout` | Whole-run wall-clock timeout in milliseconds (optional). The orchestrator reads this at run creation to set the run deadline. |
|
|
1852
|
+
| Hook flags | Boolean flags (`hasOnCancel`, `hasCleanup`, `hasOnSuccess`, `hasOnFailure`) indicating which lifecycle hooks are defined. Job entries additionally have `hasBeforeStep` and `hasAfterStep`. |
|
|
1853
|
+
|
|
1854
|
+
## Rule: commit both together
|
|
1855
|
+
|
|
1856
|
+
**Always commit `.kici/kici.lock.json` in the same commit as the workflow source files it was generated from.**
|
|
1857
|
+
|
|
1858
|
+
1. After editing `.kici/workflows/*.ts`, run:
|
|
1859
|
+
```bash
|
|
1860
|
+
npx kici compile
|
|
1861
|
+
```
|
|
1862
|
+
2. Stage both the workflow file(s) and `.kici/kici.lock.json`.
|
|
1863
|
+
3. Commit them together.
|
|
1864
|
+
|
|
1865
|
+
That way the lock file at every commit SHA matches the workflow source at that SHA.
|
|
1866
|
+
|
|
1867
|
+
## Catch drift early: pre-commit and CI
|
|
1868
|
+
|
|
1869
|
+
Use automation so drift is caught before it reaches the repo.
|
|
1870
|
+
|
|
1871
|
+
### Pre-commit hook
|
|
1872
|
+
|
|
1873
|
+
Install a hook that compiles and stages the lock file before each commit:
|
|
1874
|
+
|
|
1875
|
+
```bash
|
|
1876
|
+
npx kici hook install
|
|
1877
|
+
```
|
|
1878
|
+
|
|
1879
|
+
This runs `kici compile && git add .kici/kici.lock.json` before each commit: if compilation fails the commit is blocked; if it succeeds the updated lock file is automatically staged. See [CLI Reference — kici hook](https://docs.kici.dev/user/cli-reference/#kici-hook) for options (husky, lefthook, pre-commit, prek, raw git).
|
|
1880
|
+
|
|
1881
|
+
### CI check
|
|
1882
|
+
|
|
1883
|
+
In your CI pipeline, verify that the workflow source compiles without errors:
|
|
1884
|
+
|
|
1885
|
+
```bash
|
|
1886
|
+
kici compile --check
|
|
1887
|
+
```
|
|
1888
|
+
|
|
1889
|
+
This validates all workflows and generates the lock file in memory without writing it. If any workflow has syntax errors or invalid configuration, the command exits non-zero. Pair this with the agent-side hash verification (below) for full drift detection -- `--check` catches broken source, while the agent catches source-lock-file mismatches at run time.
|
|
1890
|
+
|
|
1891
|
+
## Extra files in the content hash (`hashFiles`)
|
|
1892
|
+
|
|
1893
|
+
By default, the per-workflow content hash is `SHA-256(compileSchemaVersion + ":" + rawSource)` where `rawSource` is the TypeScript text of the workflow entry file. If your workflow depends on files outside `.kici/workflows/` -- configuration files, scripts, Dockerfiles, etc. -- changes to those files will **not** invalidate the cache unless you declare them.
|
|
1894
|
+
|
|
1895
|
+
Use the `hashFiles` option on a workflow to include additional paths or glob patterns (relative to the repo root) in the content hash:
|
|
1896
|
+
|
|
1897
|
+
```typescript
|
|
1898
|
+
export default workflow('deploy', {
|
|
1899
|
+
hashFiles: ['config.json', 'scripts/*.sh'],
|
|
1900
|
+
jobs: [
|
|
1901
|
+
/* ... */
|
|
1902
|
+
],
|
|
1903
|
+
});
|
|
1904
|
+
```
|
|
1905
|
+
|
|
1906
|
+
When any of the matched files change, the content hash formula becomes `SHA-256(compileSchemaVersion + ":" + rawSource + "\0" + assetDigest)` where `assetDigest` is a deterministic encoding of the resolved file paths and their contents. This busts the source-tarball cache and forces the build agent to pack and upload a fresh `source/{contentHash}.tar.gz`. The resolved file paths are recorded in the lock file under `resolvedHashFiles` so the agent can verify without re-discovering the workflow.
|
|
1907
|
+
|
|
1908
|
+
## Agent-side safety net
|
|
1909
|
+
|
|
1910
|
+
If drift still occurs (e.g. someone committed only the `.ts` change), the agent detects it at run time before any step runs:
|
|
1911
|
+
|
|
1912
|
+
- After extracting the `.kici/` source tarball (or loading source from a `git clone` on the build path), the agent reads the workflow entry file and re-computes `contentHash = SHA-256(compileSchemaVersion + ":" + rawSource [+ "\0" + assetDigest])` using the same formula as the compiler.
|
|
1913
|
+
- If the orchestrator sent a `contentHash` (from the lock file) and the computed hash does **not** match, the agent fails the run with an error like: **lock file is out of date** (workflow source changed without regenerating the lock file). The error includes the baked agent `@kici-dev/sdk` version + bundle hash so operators can debug cross-host compile mismatches.
|
|
1914
|
+
|
|
1915
|
+
So even without a pre-commit or CI check, a stale lock file will cause the run to fail with a clear message instead of running with the wrong workflow.
|
|
1916
|
+
|
|
1917
|
+
## Summary
|
|
1918
|
+
|
|
1919
|
+
| Goal | What to do |
|
|
1920
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------ |
|
|
1921
|
+
| Keep lock file in sync | Commit `kici.lock.json` with the workflow `.ts` changes; run `kici compile` before commit. |
|
|
1922
|
+
| Catch drift before commit | Install a pre-commit hook with `kici hook install`. |
|
|
1923
|
+
| Catch broken source in CI | Run `kici compile --check` in CI. |
|
|
1924
|
+
| Bust cache on external files | Add `hashFiles: [‘config.json’]` to include non-workflow files in the content hash. |
|
|
1925
|
+
| Fail fast when drift remains | Rely on the agent’s hash verification when it compiles from source. |
|
|
1926
|
+
|
|
1927
|
+
## See also
|
|
1928
|
+
|
|
1929
|
+
- [Getting Started](https://docs.kici.dev/user/getting-started/) — compile and commit the lock file
|
|
1930
|
+
- [CLI Reference](https://docs.kici.dev/user/cli-reference/) — `kici compile`, `kici compile --check`, `kici hook`
|
|
1931
|
+
- [Architecture — Data flows](https://docs.kici.dev/architecture/data-flows/) — how the lock file is used in the pipeline
|
|
1932
|
+
|
|
1933
|
+
---
|
|
1934
|
+
|
|
1935
|
+
## Testing guide
|
|
1936
|
+
|
|
1937
|
+
Source: https://docs.kici.dev/user/testing-guide/
|
|
1938
|
+
|
|
1939
|
+
Test your workflows remotely against the full CI pipeline from your local machine. `kici run remote` uploads your current repo state (including uncommitted changes), triggers the pipeline, and streams execution logs back in real time.
|
|
1940
|
+
|
|
1941
|
+
## Overview
|
|
1942
|
+
|
|
1943
|
+
`kici run remote` connects your local development environment to the remote orchestrator/agent pipeline. Instead of pushing a commit and waiting for CI, you can:
|
|
1944
|
+
|
|
1945
|
+
- Run any workflow against your current working tree (including unstaged changes)
|
|
1946
|
+
- Get real-time log output streamed back to your terminal
|
|
1947
|
+
- Give test runs test-scoped secrets — your local secret files and `--env` values (uploaded encrypted) plus any environment flagged `allowLocalExecution: true` — while production environments stay unreachable
|
|
1948
|
+
- Detect test mode in workflow code via `ctx.isTestRun`
|
|
1949
|
+
|
|
1950
|
+
The command is remote-only -- all execution happens on the orchestrator and agent. For local-only trigger matching previews, use `kici test <event>`.
|
|
1951
|
+
|
|
1952
|
+
:::note[Orchestrator prerequisite: cache storage]
|
|
1953
|
+
`kici run remote` uploads your working-tree overlay to the orchestrator's **cache storage** via a pre-signed URL, and the agent fetches it from there (see [Repo state transfer](https://docs.kici.dev/user/testing-guide/#repo-state-transfer)). The target orchestrator must therefore have cache storage enabled (`KICI_STORAGE_TYPE` = `s3` or `filesystem`).
|
|
1954
|
+
|
|
1955
|
+
- **Both quickstarts wire this up for you** — the [Docker / Podman quickstart](https://docs.kici.dev/user/quickstart/compose/) and the [bare-metal quickstart](https://docs.kici.dev/user/quickstart/bare-metal/) each ship a SeaweedFS object store and pre-fill the orchestrator's `KICI_STORAGE_*` block, so `kici run remote` works out of the box (see each guide's "run a workflow without pushing" step).
|
|
1956
|
+
- **A hand-rolled orchestrator deploy does not configure storage by default** — enable a backend before using `kici run remote`:
|
|
1957
|
+
- **`filesystem`** — simplest for a single-host orchestrator: set `KICI_STORAGE_TYPE=filesystem` and `KICI_STORAGE_FS_PATH=/var/lib/kici/cache`. No external service needed; blobs are served through the orchestrator's own HMAC-signed HTTP route.
|
|
1958
|
+
- **`s3`** — any S3-compatible bucket. **A non-public / self-hosted endpoint works**: set `KICI_STORAGE_TYPE=s3`, `KICI_STORAGE_BUCKET`, `KICI_STORAGE_ENDPOINT=https://your-endpoint` and (for most self-hosted services) `KICI_STORAGE_FORCE_PATH_STYLE=true`. If the developer machine running `kici run remote` reaches the bucket at a different address than the orchestrator, set `KICI_STORAGE_UPLOAD_ENDPOINT` to the developer-reachable address; if agents reach it at yet another address (e.g. agents in containers), set `KICI_STORAGE_EXTERNAL_ENDPOINT` to the agent-routable URL.
|
|
1959
|
+
|
|
1960
|
+
See [Storage layout](https://docs.kici.dev/operator/orchestrator/storage-layout/) for the full env-var reference.
|
|
1961
|
+
:::
|
|
1962
|
+
|
|
1963
|
+
## Getting started
|
|
1964
|
+
|
|
1965
|
+
### 1. Authenticate
|
|
1966
|
+
|
|
1967
|
+
```bash
|
|
1968
|
+
kici login
|
|
1969
|
+
```
|
|
1970
|
+
|
|
1971
|
+
This opens your browser for OAuth authentication and stores a personal access token in `~/.kici/config`. For CI/CD pipelines or headless environments, use `kici login --token <your-api-key>` or `kici login --device` instead. See [CLI authentication](https://docs.kici.dev/user/cli-auth/) for details.
|
|
1972
|
+
|
|
1973
|
+
### 2. Write a test fixture
|
|
1974
|
+
|
|
1975
|
+
Fixtures define the events you want to simulate. They live in `.kici/tests/*.ts` and use the same SDK trigger functions as workflows.
|
|
1976
|
+
|
|
1977
|
+
```typescript
|
|
1978
|
+
// .kici/tests/push-tests.ts
|
|
1979
|
+
import { fixture, push } from '@kici-dev/sdk';
|
|
1980
|
+
|
|
1981
|
+
export const pushMain = fixture('push-main', {
|
|
1982
|
+
event: push({ branches: ['main'] }),
|
|
1983
|
+
});
|
|
1984
|
+
|
|
1985
|
+
export const pushDevelop = fixture('push-develop', {
|
|
1986
|
+
event: push({ branches: ['develop'] }),
|
|
1987
|
+
});
|
|
1988
|
+
```
|
|
1989
|
+
|
|
1990
|
+
Each file can export multiple fixtures. The `fixture()` factory takes an ID (used on the command line) and options including the event to simulate.
|
|
1991
|
+
|
|
1992
|
+
### 3. Run a fixture
|
|
1993
|
+
|
|
1994
|
+
```bash
|
|
1995
|
+
# List available fixtures
|
|
1996
|
+
kici run remote
|
|
1997
|
+
|
|
1998
|
+
# Run a specific fixture
|
|
1999
|
+
kici run remote push-main
|
|
2000
|
+
|
|
2001
|
+
# Run all fixtures matching a glob
|
|
2002
|
+
kici run remote 'push-*'
|
|
2003
|
+
|
|
2004
|
+
# Run everything
|
|
2005
|
+
kici run remote --all
|
|
2006
|
+
```
|
|
2007
|
+
|
|
2008
|
+
The single quotes keep your shell from expanding `push-*` against local files, so the pattern reaches KiCI intact for its own fixture-glob matching.
|
|
2009
|
+
|
|
2010
|
+
## Fixture reference
|
|
2011
|
+
|
|
2012
|
+
### Event types
|
|
2013
|
+
|
|
2014
|
+
Fixtures accept any SDK trigger function as their event:
|
|
2015
|
+
|
|
2016
|
+
```typescript
|
|
2017
|
+
import { fixture, push, pr, comment, tag, release } from '@kici-dev/sdk';
|
|
2018
|
+
|
|
2019
|
+
// Push event
|
|
2020
|
+
export const pushMain = fixture('push-main', {
|
|
2021
|
+
event: push({ branches: ['main'] }),
|
|
2022
|
+
});
|
|
2023
|
+
|
|
2024
|
+
// PR event
|
|
2025
|
+
export const prOpen = fixture('pr-open', {
|
|
2026
|
+
event: pr({ branches: ['main'], actions: ['opened'] }),
|
|
2027
|
+
});
|
|
2028
|
+
|
|
2029
|
+
// Comment event
|
|
2030
|
+
export const prComment = fixture('pr-comment', {
|
|
2031
|
+
event: comment({ actions: ['created'] }),
|
|
2032
|
+
});
|
|
2033
|
+
|
|
2034
|
+
// Tag event
|
|
2035
|
+
export const tagRelease = fixture('tag-release', {
|
|
2036
|
+
event: tag({ tags: ['v*'] }),
|
|
2037
|
+
});
|
|
2038
|
+
|
|
2039
|
+
// Release event
|
|
2040
|
+
export const published = fixture('release-published', {
|
|
2041
|
+
event: release({ actions: ['published'] }),
|
|
2042
|
+
});
|
|
2043
|
+
```
|
|
2044
|
+
|
|
2045
|
+
### Overrides
|
|
2046
|
+
|
|
2047
|
+
Override default payload values per fixture:
|
|
2048
|
+
|
|
2049
|
+
```typescript
|
|
2050
|
+
export const pushFeature = fixture('push-feature', {
|
|
2051
|
+
event: push({ branches: ['feature/*'] }),
|
|
2052
|
+
branch: 'feature/auth', // Override branch name
|
|
2053
|
+
sha: 'abc123def456', // Override commit SHA
|
|
2054
|
+
repo: 'myorg/myrepo', // Override repository
|
|
2055
|
+
pr: 42, // Override PR number (for PR events)
|
|
2056
|
+
});
|
|
2057
|
+
```
|
|
2058
|
+
|
|
2059
|
+
When not specified, these default to values detected from your local git repo (current branch, HEAD SHA, remote URL).
|
|
2060
|
+
|
|
2061
|
+
### Secret context mappings
|
|
2062
|
+
|
|
2063
|
+
Map secret contexts to your fixture:
|
|
2064
|
+
|
|
2065
|
+
```typescript
|
|
2066
|
+
export const pushWithSecrets = fixture('push-with-secrets', {
|
|
2067
|
+
event: push({ branches: ['main'] }),
|
|
2068
|
+
secrets: {
|
|
2069
|
+
db: 'test-database',
|
|
2070
|
+
api: 'test-api-keys',
|
|
2071
|
+
},
|
|
2072
|
+
});
|
|
2073
|
+
```
|
|
2074
|
+
|
|
2075
|
+
This maps the `db` secret context to the `test-database` context, and `api` to `test-api-keys`.
|
|
2076
|
+
|
|
2077
|
+
This mapping is honored by **both** `kici run local` and `kici run remote`:
|
|
2078
|
+
|
|
2079
|
+
- For **`kici run local`** (see [`kici run local`](https://docs.kici.dev/user/cli-reference/#kici-run-local)), each named context is resolved from your local secret files (`.kici/.secrets`, `.env.local`, `secrets.yaml`, and `--env` flags).
|
|
2080
|
+
- For **`kici run remote`**, each named context maps to an orchestrator **environment**, and the orchestrator resolves that environment's secrets for the run. The target environment must be flagged `allowLocalExecution: true` — mapping a context to a missing or non-test environment rejects the run (see [Secret contexts for testing](https://docs.kici.dev/user/testing-guide/#secret-contexts-for-testing) below).
|
|
2081
|
+
|
|
2082
|
+
### Async fixtures
|
|
2083
|
+
|
|
2084
|
+
For dynamic fixture configuration, export an async function:
|
|
2085
|
+
|
|
2086
|
+
```typescript
|
|
2087
|
+
export const dynamicFixture = fixture('dynamic', async () => ({
|
|
2088
|
+
event: push({ branches: ['main'] }),
|
|
2089
|
+
sha: await getCurrentSha(),
|
|
2090
|
+
}));
|
|
2091
|
+
```
|
|
2092
|
+
|
|
2093
|
+
## Running tests
|
|
2094
|
+
|
|
2095
|
+
### Basic commands
|
|
2096
|
+
|
|
2097
|
+
```bash
|
|
2098
|
+
# List all available fixtures (discovers .kici/tests/*.ts)
|
|
2099
|
+
kici run remote
|
|
2100
|
+
|
|
2101
|
+
# Run a single fixture by ID
|
|
2102
|
+
kici run remote push-main
|
|
2103
|
+
|
|
2104
|
+
# Glob matching -- run all push-related fixtures
|
|
2105
|
+
kici run remote 'push-*'
|
|
2106
|
+
|
|
2107
|
+
# Run all fixtures sequentially
|
|
2108
|
+
kici run remote --all
|
|
2109
|
+
|
|
2110
|
+
# Run all fixtures in parallel
|
|
2111
|
+
kici run remote --all --parallel
|
|
2112
|
+
```
|
|
2113
|
+
|
|
2114
|
+
### Direct workflow run
|
|
2115
|
+
|
|
2116
|
+
Bypass trigger matching and run a specific workflow directly:
|
|
2117
|
+
|
|
2118
|
+
```bash
|
|
2119
|
+
kici run remote --workflow ci
|
|
2120
|
+
```
|
|
2121
|
+
|
|
2122
|
+
This skips the trigger evaluation step and runs all jobs in the named workflow.
|
|
2123
|
+
|
|
2124
|
+
### Output modes
|
|
2125
|
+
|
|
2126
|
+
```bash
|
|
2127
|
+
# Default: full log streaming with colored job prefixes
|
|
2128
|
+
kici run remote push-main
|
|
2129
|
+
|
|
2130
|
+
# Quiet: minimal output (just pass/fail result)
|
|
2131
|
+
kici run remote push-main --quiet
|
|
2132
|
+
|
|
2133
|
+
# JSON: machine-readable structured output
|
|
2134
|
+
kici run remote push-main --json
|
|
2135
|
+
|
|
2136
|
+
# JUnit XML: for CI integration
|
|
2137
|
+
kici run remote push-main --junit results.xml
|
|
2138
|
+
```
|
|
2139
|
+
|
|
2140
|
+
### Non-blocking execution
|
|
2141
|
+
|
|
2142
|
+
```bash
|
|
2143
|
+
# Fire and forget -- returns immediately with run ID
|
|
2144
|
+
kici run remote push-main --no-wait
|
|
2145
|
+
|
|
2146
|
+
# Check status later
|
|
2147
|
+
kici runs show <run-id>
|
|
2148
|
+
```
|
|
2149
|
+
|
|
2150
|
+
### Cancellation
|
|
2151
|
+
|
|
2152
|
+
Press Ctrl+C during a running test to send a cancel signal to the orchestrator. The agent job will be terminated gracefully.
|
|
2153
|
+
|
|
2154
|
+
## Repo state transfer
|
|
2155
|
+
|
|
2156
|
+
When you run `kici run remote`, the CLI:
|
|
2157
|
+
|
|
2158
|
+
1. Detects all files differing from HEAD (staged, unstaged, and untracked)
|
|
2159
|
+
2. Creates a compressed tarball of changed files
|
|
2160
|
+
3. Encrypts the tarball using X25519 ECDH key exchange
|
|
2161
|
+
4. Uploads the encrypted tarball to storage via a signed URL
|
|
2162
|
+
5. Triggers the pipeline with a reference to the upload
|
|
2163
|
+
|
|
2164
|
+
The agent clones your repo at HEAD, then applies the overlay tarball on top -- giving you the exact same file state as your local working tree.
|
|
2165
|
+
|
|
2166
|
+
### What gets included
|
|
2167
|
+
|
|
2168
|
+
- Modified tracked files (staged and unstaged)
|
|
2169
|
+
- New untracked files (not in `.gitignore`)
|
|
2170
|
+
- File deletions (tracked files you deleted locally)
|
|
2171
|
+
|
|
2172
|
+
### What gets excluded
|
|
2173
|
+
|
|
2174
|
+
- Files matching `.gitignore` patterns
|
|
2175
|
+
- Files matching `.kiciignore` patterns (additional exclusions)
|
|
2176
|
+
- The `.git` directory itself
|
|
2177
|
+
|
|
2178
|
+
### `.kiciignore`
|
|
2179
|
+
|
|
2180
|
+
Create a `.kiciignore` file in your repo root to exclude additional files from the upload:
|
|
2181
|
+
|
|
2182
|
+
```
|
|
2183
|
+
# Large binaries
|
|
2184
|
+
*.bin
|
|
2185
|
+
*.iso
|
|
2186
|
+
data/fixtures/large-dataset.csv
|
|
2187
|
+
|
|
2188
|
+
# Local-only configs
|
|
2189
|
+
.env.local
|
|
2190
|
+
docker-compose.override.yml
|
|
2191
|
+
```
|
|
2192
|
+
|
|
2193
|
+
The format is the same as `.gitignore` -- one glob pattern per line, `#` for comments.
|
|
2194
|
+
|
|
2195
|
+
### Size limits
|
|
2196
|
+
|
|
2197
|
+
| Threshold | Behavior |
|
|
2198
|
+
| --------- | --------------------------------------------------------------------------------- |
|
|
2199
|
+
| < 50 MB | Normal upload |
|
|
2200
|
+
| 50-500 MB | Warning displayed, upload proceeds |
|
|
2201
|
+
| > 500 MB | Error -- reduce bundle size via `.kiciignore` or check for unintended large files |
|
|
2202
|
+
|
|
2203
|
+
The CLI always shows a pre-upload summary before transferring:
|
|
2204
|
+
|
|
2205
|
+
```
|
|
2206
|
+
12 files changed, 3 new, 1 deleted (2.3 MB compressed)
|
|
2207
|
+
```
|
|
2208
|
+
|
|
2209
|
+
## Secret contexts for testing
|
|
2210
|
+
|
|
2211
|
+
The goal of the test-secret model is to let test runs reach **test-only credentials** while keeping production credentials out of reach. `kici run remote` combines two sources of secrets for a test run, then merges them with a clear precedence and a fail-closed gate.
|
|
2212
|
+
|
|
2213
|
+
### CLI-uploaded local secrets
|
|
2214
|
+
|
|
2215
|
+
`kici run remote` collects the same local secret values that `kici run local` reads — `.kici/.secrets`, `.kici/.env.local`, `.kici/secrets.yaml`, and any `--env KEY=VALUE` flags — and uploads them **encrypted** to the orchestrator alongside the run. The orchestrator decrypts them only to inject them into the agent for that run; the control plane never sees the values.
|
|
2216
|
+
|
|
2217
|
+
```bash
|
|
2218
|
+
# Provide an ad-hoc test value for a single remote run
|
|
2219
|
+
kici run remote push-main --env KICI_DATABASE_URL=postgresql://localhost/test
|
|
2220
|
+
```
|
|
2221
|
+
|
|
2222
|
+
`--env` provides a **flat** per-run override; `--context <ctx>.<KEY>=<value>` is its sibling for a **namespaced** per-run override, placing the value under the named context `ctx`. Both are uploaded **encrypted** and follow the same precedence rule below — a CLI-supplied value wins over the orchestrator test-environment secret on a key collision.
|
|
2223
|
+
|
|
2224
|
+
```bash
|
|
2225
|
+
# Provide a namespaced per-run value under the 'db' context
|
|
2226
|
+
kici run remote push-db --context db.KICI_DATABASE_URL=postgresql://localhost/test
|
|
2227
|
+
```
|
|
2228
|
+
|
|
2229
|
+
Because these values originate on your machine, they are the natural place to put throwaway test credentials without touching any orchestrator-stored secret.
|
|
2230
|
+
|
|
2231
|
+
### Orchestrator test-environment secrets
|
|
2232
|
+
|
|
2233
|
+
In addition to your uploaded values, the orchestrator resolves test-scoped secrets from its own store for a remote test run:
|
|
2234
|
+
|
|
2235
|
+
- The job's own declared `environment` contributes its resolved secrets (flat). Static strings and **pure dynamic functions** both participate: a pure `environment:` function (see [Dynamic values](https://docs.kici.dev/user/dynamic-values/)) is evaluated against the fixture's simulated event, and the resolved name is gated and resolved like a static one. Impure dynamic functions (those requiring an init job) are not evaluated for test runs — use a fixture `secrets:` mapping (or `--context`) to supply such a job's secrets.
|
|
2236
|
+
- Each fixture `secrets: { ctx: envName }` mapping resolves the named environment's secrets under the namespaced context `ctx`.
|
|
2237
|
+
|
|
2238
|
+
Both paths are restricted to environments flagged `allowLocalExecution: true`. A production environment left at the default `false` is never resolvable for a test run.
|
|
2239
|
+
|
|
2240
|
+
```typescript
|
|
2241
|
+
export const pushWithDb = fixture('push-db', {
|
|
2242
|
+
event: push({ branches: ['main'] }),
|
|
2243
|
+
secrets: { db: 'test-database' }, // 'test-database' must be allowLocalExecution: true
|
|
2244
|
+
});
|
|
2245
|
+
```
|
|
2246
|
+
|
|
2247
|
+
```typescript
|
|
2248
|
+
step('migrate', async (ctx) => {
|
|
2249
|
+
const dbUrl = await ctx.secrets.get('KICI_DATABASE_URL');
|
|
2250
|
+
await ctx.$`npx prisma migrate deploy`;
|
|
2251
|
+
});
|
|
2252
|
+
```
|
|
2253
|
+
|
|
2254
|
+
### Precedence: CLI values win
|
|
2255
|
+
|
|
2256
|
+
When a key exists in both sources, the **CLI-uploaded local value wins** over the orchestrator test-environment value. This makes a local override a per-run knob: set `--env KICI_DATABASE_URL=...` (or put it in `.kici/.secrets`) to shadow the test environment's value for just that run, without changing anything on the orchestrator.
|
|
2257
|
+
|
|
2258
|
+
### Fail-closed on non-test environments
|
|
2259
|
+
|
|
2260
|
+
Test-run secret resolution is fail-closed:
|
|
2261
|
+
|
|
2262
|
+
- If a fixture maps a context to an environment that does not exist, the run is **rejected**.
|
|
2263
|
+
- If a fixture maps a context to an environment whose `allowLocalExecution` is `false`, the run is **rejected**.
|
|
2264
|
+
- The `allowLocalExecution` gate applies to **all** remote test runs: a run whose matched workflow targets an environment with the flag off is rejected, so a test run can never resolve production secrets.
|
|
2265
|
+
|
|
2266
|
+
### The `allowLocalExecution` environment flag
|
|
2267
|
+
|
|
2268
|
+
Each environment carries an `allowLocalExecution` flag (default `false`) that controls test-run access to that environment and to its secrets. Production environments should leave it at `false`; create a dedicated test environment with `allowLocalExecution: true` that binds only test-only secret scopes for the contexts you want test runs to use.
|
|
2269
|
+
|
|
2270
|
+
The flag is set by the orchestrator operator, either via the CLI:
|
|
2271
|
+
|
|
2272
|
+
```bash
|
|
2273
|
+
kici-admin environment set-policy --env test-database --allow-local-execution true
|
|
2274
|
+
```
|
|
2275
|
+
|
|
2276
|
+
or via the dashboard's "Test runs" toggle on the environment detail page. `kici secrets list` only surfaces contexts whose owning environment has `allowLocalExecution: true`, so production environments are never advertised as test-accessible.
|
|
2277
|
+
|
|
2278
|
+
### Local execution as an alternative
|
|
2279
|
+
|
|
2280
|
+
`kici run local` resolves the same local secret files entirely on your machine and honors the fixture `secrets: { ... }` mapping to pick which local context backs each name (see [`kici run local`](https://docs.kici.dev/user/cli-reference/#kici-run-local)). Because the values never leave your machine, it's a good fit when you want to exercise secret-dependent steps without involving the orchestrator at all.
|
|
2281
|
+
|
|
2282
|
+
### Discovering available contexts
|
|
2283
|
+
|
|
2284
|
+
```bash
|
|
2285
|
+
# List test-accessible secret contexts and their key names (not values)
|
|
2286
|
+
kici secrets list
|
|
2287
|
+
```
|
|
2288
|
+
|
|
2289
|
+
## Detecting test mode in workflows
|
|
2290
|
+
|
|
2291
|
+
Use `ctx.isTestRun` to conditionally skip destructive operations:
|
|
2292
|
+
|
|
2293
|
+
```typescript
|
|
2294
|
+
step('deploy', async (ctx) => {
|
|
2295
|
+
if (ctx.isTestRun) {
|
|
2296
|
+
ctx.log.info('Skipping deployment in test mode');
|
|
2297
|
+
return;
|
|
2298
|
+
}
|
|
2299
|
+
await ctx.$`kubectl apply -f k8s/`;
|
|
2300
|
+
});
|
|
2301
|
+
```
|
|
2302
|
+
|
|
2303
|
+
## Run history
|
|
2304
|
+
|
|
2305
|
+
### Viewing history
|
|
2306
|
+
|
|
2307
|
+
```bash
|
|
2308
|
+
# Show recent test runs (from local history)
|
|
2309
|
+
kici run remote --history
|
|
2310
|
+
```
|
|
2311
|
+
|
|
2312
|
+
### Run details
|
|
2313
|
+
|
|
2314
|
+
```bash
|
|
2315
|
+
# Show run summary (reads the Platform, falls back to local history)
|
|
2316
|
+
kici runs show <run-id>
|
|
2317
|
+
|
|
2318
|
+
# Show full logs
|
|
2319
|
+
kici runs logs <run-id>
|
|
2320
|
+
|
|
2321
|
+
# Show logs for a specific job
|
|
2322
|
+
kici runs logs <run-id> --job build
|
|
2323
|
+
|
|
2324
|
+
# Machine-readable output
|
|
2325
|
+
kici runs show <run-id> --json
|
|
2326
|
+
```
|
|
2327
|
+
|
|
2328
|
+
## Scaffolding with kici init
|
|
2329
|
+
|
|
2330
|
+
Running `kici init` in a new project scaffolds a sample test fixture alongside the workflow templates:
|
|
2331
|
+
|
|
2332
|
+
```
|
|
2333
|
+
.kici/
|
|
2334
|
+
workflows/
|
|
2335
|
+
hello-world.ts # Sample workflow
|
|
2336
|
+
pr-checks.ts # Sample PR workflow
|
|
2337
|
+
tests/
|
|
2338
|
+
push-test.ts # Sample push fixture
|
|
2339
|
+
package.json
|
|
2340
|
+
tsconfig.json
|
|
2341
|
+
.kiciignore # Default exclusion patterns
|
|
2342
|
+
```
|
|
2343
|
+
|
|
2344
|
+
The generated fixture uses the detected default branch:
|
|
2345
|
+
|
|
2346
|
+
```typescript
|
|
2347
|
+
// .kici/tests/push-test.ts
|
|
2348
|
+
import { fixture, push } from '@kici-dev/sdk';
|
|
2349
|
+
|
|
2350
|
+
export const pushMain = fixture('push-main', {
|
|
2351
|
+
event: push({ branches: ['main'] }),
|
|
2352
|
+
});
|
|
2353
|
+
```
|
|
2354
|
+
|
|
2355
|
+
## See also
|
|
2356
|
+
|
|
2357
|
+
- [CLI reference](https://docs.kici.dev/user/cli-reference/) -- complete command reference for all `kici` commands
|
|
2358
|
+
- [SDK reference](https://docs.kici.dev/user/sdk-reference/) -- trigger functions, step context, and workflow API
|
|
2359
|
+
- [Workflow patterns](https://docs.kici.dev/user/workflow-patterns/) -- example workflows to test against
|
|
2360
|
+
|
|
2361
|
+
---
|
|
2362
|
+
|
|
2363
|
+
## Workflow patterns
|
|
2364
|
+
|
|
2365
|
+
Source: https://docs.kici.dev/user/workflow-patterns/
|
|
2366
|
+
|
|
2367
|
+
Practical patterns for building real-world KiCI workflows in TypeScript. The patterns are organised across five pages -- start with [Basic CI](https://docs.kici.dev/user/patterns/basic/) if you're new, or jump to [Integrations](https://docs.kici.dev/user/patterns/integrations/) if you're wiring up a non-GitHub forge or a generic webhook.
|
|
2368
|
+
|
|
2369
|
+
| Page | Covers |
|
|
2370
|
+
| ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
2371
|
+
| [Basic CI](https://docs.kici.dev/user/patterns/basic/) | Single-job CI, PR-only / push-only filters, multiple triggers on one workflow, manual-only workflows. |
|
|
2372
|
+
| [Conditionals & matrix](https://docs.kici.dev/user/patterns/conditionals-matrix/) | Conditional execution with rules, matrix builds (static + dynamic), and dynamic job generation. |
|
|
2373
|
+
| [Integrations](https://docs.kici.dev/user/patterns/integrations/) | Workflow chaining, generic webhooks, Stripe handlers, self-hosted git forges (Forgejo / Gitea / Gogs), plain GitHub repo webhooks (no GitHub App). |
|
|
2374
|
+
| [Scheduling & events](https://docs.kici.dev/user/patterns/scheduling-and-events/) | Nightly cron, workflow-complete-triggered deploys, custom event chaining. |
|
|
2375
|
+
| [Pattern reference](https://docs.kici.dev/user/patterns/reference/) | Step context, the examples repository, and GitHub check run output -- cross-cutting reference shared by every pattern above. |
|
|
2376
|
+
|
|
2377
|
+
## See also
|
|
2378
|
+
|
|
2379
|
+
- [Event system](https://docs.kici.dev/user/events/) -- event model concepts, registration model, circuit breaker
|
|
2380
|
+
- [SDK reference](https://docs.kici.dev/user/sdk-reference/) -- complete API reference for all functions used in these patterns
|
|
2381
|
+
- [CLI reference](https://docs.kici.dev/user/cli-reference/) -- how to compile and test these workflows locally
|
|
2382
|
+
- [Getting started](https://docs.kici.dev/user/getting-started/) -- installation and first workflow setup
|
|
2383
|
+
- [Job execution lifecycle](https://docs.kici.dev/architecture/execution/job-execution/) -- how agents execute the jobs defined in these patterns
|
|
2384
|
+
- [GitHub checks architecture](https://docs.kici.dev/architecture/webhooks/github-checks/) -- deep dive into the check run system
|
|
2385
|
+
|
|
2386
|
+
---
|