Skip to content

Commit 88e903b

Browse files
authored
Merge branch 'main' into vilius-kimchi-cli
2 parents ac36111 + dceac12 commit 88e903b

1,263 files changed

Lines changed: 127291 additions & 21520 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/AGENTS.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ Before changing GitHub automation, read the current versions of:
1212
- `.github/workflows/report.atom.yml`
1313
- `.github/scripts/handoff.py`
1414
- `scripts/scopes.ts`
15+
- `specs/current/ci.md` when changing scope rules, confidence tiers, or guards
1516
- `e2e/tests/packaged-smoke-workflow.test.ts`
16-
- `scripts/approve-fork-pr-workflows.ts` and `scripts/approve-fork-pr-workflows.test.ts` when touching fork PR approval behavior
17+
- `scripts/approve-fork-pr-workflows.ts` and `e2e/tests/scripts/approve-fork-pr-workflows.test.ts` when touching fork PR approval behavior
1718

1819
If the change affects cross-workflow behavior, update the topology tests instead of relying only on workflow YAML review.
1920

@@ -44,7 +45,7 @@ Default rule: do not add a new domain-specific follow-on workflow such as `foo.c
4445
- `.github/workflows/` contains GitHub Actions workflow entrypoints.
4546
- `.github/actions/` contains reusable composite actions for workflow setup steps.
4647
- `.github/scripts/` contains workflow-owned scripts and contracts that are not general repo developer commands.
47-
- `.github/workflow/scripts/` currently contains older release workflow implementation scripts. Treat it as existing release infrastructure, not as the default location for new CI handoff helpers.
48+
- `.github/scripts/release/` contains release workflow implementation helpers. Keep release-only helpers there and CI handoff helpers at `.github/scripts/`.
4849
- Root `scripts/` remains for repo-level developer checks, product scripts, and guard/test logic. Do not move workflow-only handoff glue there just to make it look more general.
4950

5051
New workflow-owned helpers should usually live under `.github/scripts/`. Prefer TypeScript for project-owned scripts in general, but Python is acceptable for small GitHub runner glue when stdlib portability and low setup cost matter. Keep such exceptions narrow and covered by `pnpm guard` policy.
@@ -163,4 +164,4 @@ GitHub artifact behavior is easy to drift: artifact names must be unique per upl
163164

164165
### Where should tests live?
165166

166-
Cross-workflow topology tests belong in `e2e/tests/` when they observe repository-level behavior. Script-specific behavior can stay next to the script's existing tests. Do not add one-off `*.test.ts` files just because a workflow helper exists; prefer existing topology coverage and helper self-checks when that is enough.
167+
Cross-workflow topology tests belong in `e2e/tests/` when they observe repository-level behavior. Root `scripts/` is test-free (enforced by `pnpm guard`); script behavior-contract coverage lives in `e2e/tests/scripts/`. Do not add one-off `*.test.ts` files just because a workflow helper exists; prefer existing topology coverage and helper self-checks when that is enough.

.github/actions/setup-workspace/action.yml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ inputs:
1414
description: JSON array of labels selected for this job's runs-on value
1515
required: false
1616
default: '[]'
17+
save-pnpm-cache:
18+
description: Save a missing pnpm cache after install; trusted main seed jobs only
19+
required: false
20+
default: 'false'
1721

1822
runs:
1923
using: composite
@@ -62,13 +66,21 @@ runs:
6266
shell: bash
6367
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
6468

65-
- name: Cache pnpm store
69+
- name: Restore pnpm store
70+
id: pnpm-cache-restore
6671
if: ${{ steps.persistent-pnpm-store.outputs.enabled != 'true' }}
67-
uses: actions/cache@v5.0.5
72+
uses: actions/cache/restore@v5
6873
with:
6974
path: ${{ steps.pnpm-store.outputs.path }}
7075
key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
7176

7277
- name: Install dependencies
7378
shell: bash
7479
run: pnpm install --frozen-lockfile
80+
81+
- name: Save pnpm store
82+
if: ${{ inputs.save-pnpm-cache == 'true' && steps.persistent-pnpm-store.outputs.enabled != 'true' && steps.pnpm-cache-restore.outputs.cache-hit != 'true' && github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') }}
83+
uses: actions/cache/save@v5
84+
with:
85+
path: ${{ steps.pnpm-store.outputs.path }}
86+
key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}

.github/pull_request_template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Fixes #
3131

3232
- [ ] **UI** — new page / dialog / panel / menu item / setting / empty state in `apps/web` or `apps/desktop` (including Electron menu bar)
3333
- [ ] **Keyboard shortcut** — new or changed
34-
- [ ] **CLI / env var** — new `od` subcommand or flag, new `tools-dev` / `tools-pack` / `tools-pr` flag, or new `OD_*` env var
34+
- [ ] **CLI / env var** — new `od` subcommand or flag, new `tools-dev` / `tools-pack` flag, or new `OD_*` env var
3535
- [ ] **API / contract** — new `/api/*` endpoint, new SSE event, or changed shape in `packages/contracts`
3636
- [ ] **Extension point** — new entry under `skills/`, `design-systems/`, `design-templates/`, or `craft/`, or change to the skills protocol
3737
- [ ] **i18n keys** — added new translation keys (see `TRANSLATIONS.md` for the locale workflow)
49.4 KB
Loading
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: cache-maintenance
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- pnpm-lock.yaml
8+
- .github/actions/setup-workspace/action.yml
9+
- .github/workflows/cache-maintenance.yml
10+
pull_request_target:
11+
types: [closed]
12+
workflow_dispatch:
13+
14+
permissions:
15+
actions: write
16+
contents: read
17+
18+
jobs:
19+
seed-pnpm-windows:
20+
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main')
21+
runs-on: windows-latest
22+
timeout-minutes: 15
23+
steps:
24+
- name: Checkout main
25+
uses: actions/checkout@v6.0.2
26+
27+
- name: Seed Windows pnpm cache
28+
uses: ./.github/actions/setup-workspace
29+
with:
30+
save-pnpm-cache: 'true'
31+
32+
clean-closed-pr-buildkit:
33+
if: github.event_name == 'pull_request_target' && github.repository == 'nexu-io/open-design'
34+
runs-on: ubuntu-24.04
35+
timeout-minutes: 5
36+
steps:
37+
- name: Delete closed PR BuildKit caches
38+
env:
39+
CACHE_REF: refs/pull/${{ github.event.pull_request.number }}/merge
40+
GH_TOKEN: ${{ github.token }}
41+
run: |
42+
set -euo pipefail
43+
44+
cache_rows="$(
45+
gh cache list \
46+
--repo "$GITHUB_REPOSITORY" \
47+
--ref "$CACHE_REF" \
48+
--limit 10000 \
49+
--json id,key,sizeInBytes \
50+
--jq '.[] | select((.key | startswith("buildkit-blob-")) or (.key | startswith("index-buildkit-"))) | [.id, .key, .sizeInBytes] | @tsv'
51+
)"
52+
53+
if [ -z "$cache_rows" ]; then
54+
echo "No closed-PR BuildKit caches found for $CACHE_REF"
55+
exit 0
56+
fi
57+
58+
count=0
59+
bytes=0
60+
while IFS=$'\t' read -r cache_id cache_key cache_size; do
61+
[ -n "$cache_id" ] || continue
62+
echo "Deleting cache id=$cache_id key=$cache_key bytes=$cache_size ref=$CACHE_REF"
63+
gh cache delete "$cache_id" --repo "$GITHUB_REPOSITORY"
64+
count=$((count + 1))
65+
bytes=$((bytes + cache_size))
66+
done <<< "$cache_rows"
67+
68+
echo "Deleted $count closed-PR BuildKit caches ($bytes bytes) for $CACHE_REF"

.github/workflows/ci.yml

Lines changed: 41 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ jobs:
6363
run_playwright_critical: ${{ steps.detect.outputs.run_playwright_critical }}
6464
run_playwright_visual: ${{ steps.detect.outputs.run_playwright_visual }}
6565
run_preflight: ${{ steps.detect.outputs.run_preflight }}
66+
run_preflight_typecheck: ${{ steps.detect.outputs.run_preflight_typecheck }}
6667
run_ui_p0: ${{ steps.detect.outputs.run_ui_p0 }}
6768
run_web_workspace_tests: ${{ steps.detect.outputs.run_web_workspace_tests }}
6869
run_windows_tools_pack_payload_tests: ${{ steps.detect.outputs.run_windows_tools_pack_payload_tests }}
@@ -136,21 +137,31 @@ jobs:
136137
timeout-minutes: 10
137138
env:
138139
ACTIONLINT_VERSION: 1.7.12
140+
SHELLCHECK_VERSION: 0.11.0
139141

140142
steps:
141143
- name: Checkout
142144
uses: actions/checkout@v6.0.2
143145

144-
- name: Install actionlint
146+
- name: Install actionlint and ShellCheck
145147
run: |
146148
case "$(uname -m)" in
147-
x86_64) actionlint_arch=amd64 ;;
148-
aarch64|arm64) actionlint_arch=arm64 ;;
149+
x86_64)
150+
actionlint_arch=amd64
151+
shellcheck_arch=x86_64
152+
;;
153+
aarch64|arm64)
154+
actionlint_arch=arm64
155+
shellcheck_arch=aarch64
156+
;;
149157
*) echo "Unsupported actionlint architecture: $(uname -m)" >&2; exit 1 ;;
150158
esac
151159
curl -fsSL "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_${actionlint_arch}.tar.gz" \
152160
| tar -xz actionlint
161+
curl -fsSL "https://github.com/koalaman/shellcheck/releases/download/v${SHELLCHECK_VERSION}/shellcheck-v${SHELLCHECK_VERSION}.linux.${shellcheck_arch}.tar.gz" \
162+
| tar -xz --strip-components=1 "shellcheck-v${SHELLCHECK_VERSION}/shellcheck"
153163
sudo install -m 0755 actionlint /usr/local/bin/actionlint
164+
sudo install -m 0755 shellcheck /usr/local/bin/shellcheck
154165
155166
- name: Check workflow files
156167
run: actionlint -color
@@ -245,12 +256,14 @@ jobs:
245256
# If postinstall grows a targeted app type-generation phase covering these
246257
# three exports without broad app builds, this CI prebuild can be removed.
247258
- name: Prebuild workspace type declarations
259+
if: ${{ needs.scopes.outputs.run_preflight_typecheck == 'true' }}
248260
run: |
249261
pnpm --filter @open-design/daemon build
250262
pnpm --filter @open-design/desktop build
251263
pnpm --filter @open-design/web build:sidecar
252264
253265
- name: Typecheck workspaces
266+
if: ${{ needs.scopes.outputs.run_preflight_typecheck == 'true' }}
254267
run: |
255268
pnpm -r --filter '!open-design' --filter '!@open-design/landing-page' --workspace-concurrency="${OPEN_DESIGN_WORKSPACE_CONCURRENCY:-1}" --if-present run typecheck
256269
pnpm exec tsc -p scripts/tsconfig.json --noEmit
@@ -291,7 +304,13 @@ jobs:
291304
pnpm --filter @open-design/tools-dev test
292305
fi
293306
if [ "${{ needs.scopes.outputs.tools_pack_tests_required }}" = "true" ]; then
307+
pnpm --filter @open-design/desktop build
308+
pnpm --filter @open-design/desktop test
309+
pnpm --filter @open-design/packaged test
294310
pnpm --filter @open-design/tools-pack test
311+
if [ "${{ needs.scopes.outputs.run_e2e_vitest }}" != "true" ]; then
312+
pnpm --filter @open-design/e2e test tests/packaged-launcher-update-loop.test.ts
313+
fi
295314
fi
296315
297316
- name: Probe watcher environment
@@ -441,12 +460,16 @@ jobs:
441460
OD_PLAYWRIGHT_FULLY_PARALLEL: "1"
442461
run: pnpm -C e2e exec playwright test -c playwright.config.ts ${{ matrix.files }} --grep '@critical'
443462

444-
ui_p0_smoke:
445-
name: UI P0 smoke
463+
ui_p0:
464+
name: UI P0 (${{ matrix.name }})
446465
needs: [scopes, runners]
447466
if: ${{ needs.scopes.outputs.run_ui_p0 == 'true' }}
448467
runs-on: ${{ fromJSON(needs.runners.outputs.runs_on).ui_hot }}
449-
timeout-minutes: 30
468+
timeout-minutes: 45
469+
strategy:
470+
fail-fast: false
471+
matrix:
472+
include: ${{ fromJSON(needs.scopes.outputs.ui_p0_matrix) }}
450473

451474
steps:
452475
- name: Checkout
@@ -476,14 +499,14 @@ jobs:
476499
- name: Clean Playwright state
477500
run: pnpm -C e2e exec tsx scripts/playwright.ts clean
478501

479-
- name: Run UI shell smoke
480-
run: pnpm -C e2e exec tsx scripts/playwright.ts run-ui-group smoke
502+
- name: Run UI P0 domain
503+
run: pnpm -C e2e exec tsx scripts/playwright.ts run-ui-group ${{ matrix.shard }}
481504

482-
- name: Upload Playwright debug artifact
483-
if: ${{ always() }}
505+
- name: Preserve project-runtime domain artifact
506+
if: ${{ success() && matrix.shard == 'project-runtime' }}
484507
uses: actions/upload-artifact@v7
485508
with:
486-
name: ui-p0-ci-${{ github.run_id }}-smoke
509+
name: ui-p0-ci-${{ github.run_id }}-${{ matrix.name }}-domain
487510
path: |
488511
e2e/ui/reports/playwright-html-report
489512
e2e/ui/reports/test-results
@@ -492,47 +515,9 @@ jobs:
492515
if-no-files-found: ignore
493516
retention-days: 7
494517

495-
ui_p0:
496-
name: UI P0 (${{ matrix.name }})
497-
needs: [scopes, runners]
498-
if: ${{ needs.scopes.outputs.run_ui_p0 == 'true' }}
499-
runs-on: ${{ fromJSON(needs.runners.outputs.runs_on).ui_hot }}
500-
timeout-minutes: 45
501-
strategy:
502-
fail-fast: false
503-
matrix:
504-
include: ${{ fromJSON(needs.scopes.outputs.ui_p0_matrix) }}
505-
506-
steps:
507-
- name: Checkout
508-
uses: actions/checkout@v6.0.2
509-
510-
- name: Configure CI parallelism
511-
uses: ./.github/actions/configure-ci-parallelism
512-
513-
- name: Setup workspace
514-
uses: ./.github/actions/setup-workspace
515-
with:
516-
runner-labels: ${{ toJSON(fromJSON(needs.runners.outputs.runs_on).ui_hot) }}
517-
518-
- name: Setup Playwright
519-
uses: ./.github/actions/setup-playwright
520-
with:
521-
package-json-path: e2e/package.json
522-
install-command: pnpm -C e2e exec playwright install --with-deps chromium
523-
runner-labels: ${{ toJSON(fromJSON(needs.runners.outputs.runs_on).ui_hot) }}
524-
525-
- name: Prebuild workspace type declarations
526-
run: |
527-
pnpm --filter @open-design/daemon build
528-
pnpm --filter @open-design/desktop build
529-
pnpm --filter @open-design/web build:sidecar
530-
531-
- name: Clean Playwright state
532-
run: pnpm -C e2e exec tsx scripts/playwright.ts clean
533-
534-
- name: Run UI P0 domain
535-
run: pnpm -C e2e exec tsx scripts/playwright.ts run-ui-group ${{ matrix.shard }}
518+
- name: Run UI critical extras
519+
if: ${{ matrix.shard == 'project-runtime' }}
520+
run: pnpm -C e2e exec tsx scripts/playwright.ts run-ui-group critical-extras
536521

537522
- name: Upload Playwright debug artifact
538523
if: ${{ always() }}
@@ -648,7 +633,6 @@ jobs:
648633
- web_workspace_tests
649634
- e2e_vitest
650635
- playwright_critical
651-
- ui_p0_smoke
652636
- ui_p0
653637
- playwright_visual
654638
if: ${{ always() }}
@@ -695,7 +679,7 @@ jobs:
695679
+ when($out.run_web_workspace_tests == "true"; ["web_workspace_tests"])
696680
+ when($out.run_e2e_vitest == "true"; ["e2e_vitest"])
697681
+ when($out.run_playwright_critical == "true"; ["playwright_critical"])
698-
+ when($out.run_ui_p0 == "true"; ["ui_p0_smoke", "ui_p0"])
682+
+ when($out.run_ui_p0 == "true"; ["ui_p0"])
699683
+ when($out.run_playwright_visual == "true"; ["playwright_visual"])
700684
)[]
701685
| select(($needs[.].result // "missing") != "success")
@@ -758,8 +742,11 @@ jobs:
758742
marker="<!-- merge-queue-needs-validation -->"
759743
{
760744
printf '%s\n' "$marker"
745+
# Markdown code spans are intentional literal text.
746+
# shellcheck disable=SC2016
761747
printf 'Ejected from the merge queue: this PR still carries the `needs-validation` label.\n\n'
762748
printf 'The merge queue gate ([run %s](%s/%s/actions/runs/%s)) blocked the queued group because of the label. That failure runs on the queue'"'"'s transient ref, so it never appears in this PR'"'"'s own checks — they stay green, and this notice is the only visible trace on the PR.\n\n' "$RUN_ID" "$GITHUB_SERVER_URL" "$REPO" "$RUN_ID"
749+
# shellcheck disable=SC2016
763750
printf 'To land this PR: complete the QA pass the label is tracking, remove the `needs-validation` label, then add the PR back to the merge queue.\n'
764751
} > "$handoff_dir/body.md"
765752
jq -n \

.github/workflows/cut-patch-release.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,12 @@ jobs:
100100
# (e.g. version=0.15.1 gates open-design-v0.15.0, not the latest 0.14.0).
101101
vmajor=${V%%.*}; vrest=${V#*.}; vminor=${vrest%%.*}
102102
MINOR_BASE="${vmajor}.${vminor}.0"
103-
echo "version=$V" >> "$GITHUB_OUTPUT"
104-
echo "branch=release/v$V" >> "$GITHUB_OUTPUT"
105-
echo "minor_base=$MINOR_BASE" >> "$GITHUB_OUTPUT"
106-
echo "minor_tag=open-design-v$MINOR_BASE" >> "$GITHUB_OUTPUT"
103+
{
104+
echo "version=$V"
105+
echo "branch=release/v$V"
106+
echo "minor_base=$MINOR_BASE"
107+
echo "minor_tag=open-design-v$MINOR_BASE"
108+
} >> "$GITHUB_OUTPUT"
107109
echo "Cutting patch v$V (branch release/v$V); gating on stable v$MINOR_BASE"
108110
109111
# Guard: the minor this patch sits on (the Tuesday cut) must already be a

.github/workflows/landing-page-ci.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ jobs:
8181
- name: Setup workspace
8282
uses: ./.github/actions/setup-workspace
8383

84-
- name: Cache generated previews
84+
- name: Restore generated previews
8585
id: previews-cache
86-
uses: actions/cache@v5.0.5
86+
uses: actions/cache/restore@v5
8787
with:
8888
path: apps/landing-page/public/previews
8989
key: landing-page-previews-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml', 'package.json', 'apps/landing-page/package.json', 'apps/landing-page/scripts/generate-previews.ts', 'apps/landing-page/scripts/fallback-preview-card.ts', 'skills/**', 'design-templates/**', 'templates/live-artifacts/**', 'plugins/_official/**') }}
@@ -125,6 +125,13 @@ jobs:
125125
OD_LANDING_NOINDEX: '1'
126126
run: pnpm --filter @open-design/landing-page build:static
127127

128+
- name: Save generated previews
129+
if: ${{ steps.previews-cache.outputs.cache-hit != 'true' && github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
130+
uses: actions/cache/save@v5
131+
with:
132+
path: apps/landing-page/public/previews
133+
key: landing-page-previews-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml', 'package.json', 'apps/landing-page/package.json', 'apps/landing-page/scripts/generate-previews.ts', 'apps/landing-page/scripts/fallback-preview-card.ts', 'skills/**', 'design-templates/**', 'templates/live-artifacts/**', 'plugins/_official/**') }}
134+
128135
- name: Lint changed blog SEO
129136
# The build above sets OD_LANDING_NOINDEX=1, so every rendered page in
130137
# `out/` carries `noindex`. Pass the same flag through so the SEO linter

0 commit comments

Comments
 (0)