Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/jsr-consumer-monitor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: JSR Consumer Monitor

on:
workflow_dispatch:
schedule:
- cron: '30 2 * * *'

jobs:
windows-jsr-latest:
name: 'JSR latest consumer build (windows)'
runs-on: windows-latest
timeout-minutes: 20
steps:
- uses: denoland/setup-deno@v2
with:
deno-version: '2'
- name: Create project from JSR latest
shell: pwsh
run: |
$consumerDir = Join-Path $env:RUNNER_TEMP 'lessjs-jsr-consumer-monitor'
if (Test-Path $consumerDir) {
Remove-Item -LiteralPath $consumerDir -Recurse -Force
}
New-Item -ItemType Directory -Path $consumerDir | Out-Null
Set-Location $consumerDir
deno run -A jsr:@lessjs/create test-blog
- name: Build generated project
shell: pwsh
run: |
$projectDir = Join-Path $env:RUNNER_TEMP 'lessjs-jsr-consumer-monitor/test-blog'
Set-Location $projectDir
deno task build
- name: Verify output
shell: pwsh
run: |
$html = Join-Path $env:RUNNER_TEMP 'lessjs-jsr-consumer-monitor/test-blog/dist/index.html'
if (!(Test-Path $html)) {
throw "dist/index.html not found"
}
if ((Get-Content $html -Raw) -notmatch '<html') {
throw "dist/index.html does not contain an html document"
}
2 changes: 2 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ jobs:
# Verifies that freshly published JSR packages form a complete, buildable
# consumer project. This is the final release gate — it catches problems that
# pre-publish testing cannot because the new version does not yet exist on JSR.
# Windows JSR-latest compatibility is monitored separately in
# jsr-consumer-monitor.yml so this release gate stays fast and release-scoped.
consumer-smoke:
runs-on: ubuntu-latest
needs: [publish]
Expand Down
90 changes: 28 additions & 62 deletions .github/workflows/sop-gate.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SOP Gate v0.21.x Hardening CI
# SOP Gate - v0.21.x Hardening CI
#
# Runs the full 11-gate SOP pipeline defined in docs/sop/v0.21.x/README.md.
# Runs the source-backed SOP pipeline defined in docs/sop/v0.21.x/README.md.
# Triggered on push to dev/main and on PRs. Also available via workflow_dispatch.
#
# If any gate fails, v0.22.0 Edge Full-Stack should NOT start.
Expand All @@ -15,7 +15,6 @@ on:
workflow_dispatch:

jobs:
# ─── Gate 1 ───
fmt-check:
name: 'fmt:check'
runs-on: ubuntu-latest
Expand All @@ -26,7 +25,6 @@ jobs:
deno-version: '2'
- run: deno task fmt:check

# ─── Gate 2 ───
lint:
name: 'lint'
runs-on: ubuntu-latest
Expand All @@ -37,7 +35,6 @@ jobs:
deno-version: '2'
- run: deno task lint

# ─── Gate 3 ───
typecheck:
name: 'typecheck'
runs-on: ubuntu-latest
Expand All @@ -49,7 +46,6 @@ jobs:
- run: deno install --node-modules-dir
- run: deno task typecheck

# ─── Gate 4 ───
audit:
name: 'audit'
runs-on: ubuntu-latest
Expand All @@ -60,9 +56,8 @@ jobs:
deno-version: '2'
- run: deno audit

# ─── Gate 5 — Full test suite (includes SSG starter proof) ───
test:
name: 'test (incl. SSG starter proof)'
name: 'test'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -73,9 +68,6 @@ jobs:
- name: Run full test suite
run: deno task test

# ─── Gate 5b — SSG starter proof (isolated) ───
# SOP-010 Q4: Does the SSG blog/docs path build from a generated project?
# This runs ONLY the create package test that scaffolds + builds end-to-end.
test-ssg-starter:
name: 'SSG starter proof (SOP-010 Q4)'
runs-on: ubuntu-latest
Expand All @@ -85,10 +77,9 @@ jobs:
with:
deno-version: '2'
- run: deno install --node-modules-dir
- name: Run SSG starter proof test
- name: Run generated-project starter proof
run: deno test packages/create/__tests__/cli.test.ts --allow-read --allow-write --allow-env --allow-run --allow-ffi --filter "generated project builds"

# ─── Gate 6 — Build ───
build:
name: 'build'
runs-on: ubuntu-latest
Expand All @@ -101,7 +92,6 @@ jobs:
- run: deno install --node-modules-dir
- run: deno task build

# ─── Gate 7 — DSD conformance report ───
dsd-check:
name: 'dsd:check-report'
runs-on: ubuntu-latest
Expand All @@ -112,11 +102,10 @@ jobs:
with:
deno-version: '2'
- run: deno install --node-modules-dir
- name: Build www site (produces dsd-report.json)
- name: Build www site
run: deno task build
- run: deno task dsd:check-report

# ─── Gate 8 — Hub validation ───
hub-validate:
name: 'hub:validate'
runs-on: ubuntu-latest
Expand All @@ -128,7 +117,6 @@ jobs:
- run: deno install --node-modules-dir
- run: deno task hub:validate -- --strict --json

# ─── Gate 9 — Hub index check ───
hub-check-index:
name: 'hub:check-index'
runs-on: ubuntu-latest
Expand All @@ -139,7 +127,6 @@ jobs:
deno-version: '2'
- run: deno task hub:check-index

# ─── Gate 10 — Strategic docs check ───
docs-check:
name: 'docs:check-strategy'
runs-on: ubuntu-latest
Expand All @@ -150,31 +137,6 @@ jobs:
deno-version: '2'
- run: deno task docs:check-strategy

# ─── Gate 11b — JSR consumer ESM graph build (SOP-011: real JSR, no patching) ───
test-create-jsr:
name: 'JSR consumer build (${{ matrix.os }})'
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- uses: denoland/setup-deno@v2
with:
deno-version: '2'
- name: Create project from JSR
# Track the current JSR release because this gate proves the documented
# user command, not a historical pinned package.
working-directory: ${{ runner.temp }}
run: deno run -A jsr:@lessjs/create test-blog
- name: Build generated project
working-directory: ${{ runner.temp }}/test-blog
run: deno task build
- name: Verify build output
shell: bash
run: test -f "${RUNNER_TEMP}/test-blog/dist/index.html" && grep -q '<html' "${RUNNER_TEMP}/test-blog/dist/index.html"

# ─── Gate 11 — E2E tests ───
test-e2e:
name: 'test:e2e'
runs-on: ubuntu-latest
Expand All @@ -185,14 +147,13 @@ jobs:
with:
deno-version: '2'
- run: deno install --node-modules-dir
- name: Build www site (needed for Playwright webServer)
- name: Build www site for Playwright
run: deno task build
- name: Install Playwright Chromium
run: npx -y playwright@1.59.1 install chromium --with-deps
- name: Run E2E tests
run: deno task test:e2e

# ─── Gate Summary ───
gate-summary:
name: 'SOP Gate Summary'
runs-on: ubuntu-latest
Expand All @@ -209,7 +170,6 @@ jobs:
hub-validate,
hub-check-index,
docs-check,
test-create-jsr,
test-e2e,
]
if: always()
Expand All @@ -218,29 +178,35 @@ jobs:
run: |
ALL_PASS=true

check() { if [ "$1" = "success" ]; then echo "- [x] $2"; else echo "- [ ] $2 (failed)"; ALL_PASS=false; fi; }
check() {
if [ "$1" = "success" ]; then
echo "- [x] $2"
else
echo "- [ ] $2 (failed)"
ALL_PASS=false
fi
}

{
echo "## SOP Gate v0.21.x Results"
echo ""
check "${{ needs.fmt-check.result }}" "fmt:check"
check "${{ needs.lint.result }}" "lint"
check "${{ needs.typecheck.result }}" "typecheck"
check "${{ needs.audit.result }}" "audit"
check "${{ needs.test.result }}" "test (incl. SSG starter proof)"
check "${{ needs.test-ssg-starter.result }}" "SSG starter proof (SOP-010 Q4)"
check "${{ needs.build.result }}" "build"
check "${{ needs.dsd-check.result }}" "dsd:check-report"
check "${{ needs.hub-validate.result }}" "hub:validate --strict"
check "${{ needs.hub-check-index.result }}" "hub:check-index"
check "${{ needs.docs-check.result }}" "docs:check-strategy"
check "${{ needs.test-create-jsr.result }}" "JSR consumer build (ubuntu + windows)"
check "${{ needs.test-e2e.result }}" "test:e2e"
check "${{ needs.fmt-check.result }}" "fmt:check"
check "${{ needs.lint.result }}" "lint"
check "${{ needs.typecheck.result }}" "typecheck"
check "${{ needs.audit.result }}" "audit"
check "${{ needs.test.result }}" "test"
check "${{ needs.test-ssg-starter.result }}" "SSG starter proof (SOP-010 Q4)"
check "${{ needs.build.result }}" "build"
check "${{ needs.dsd-check.result }}" "dsd:check-report"
check "${{ needs.hub-validate.result }}" "hub:validate --strict"
check "${{ needs.hub-check-index.result }}" "hub:check-index"
check "${{ needs.docs-check.result }}" "docs:check-strategy"
check "${{ needs.test-e2e.result }}" "test:e2e"
echo ""
if [ "$ALL_PASS" = "true" ]; then
echo "🎉 **All 13 SOP gates passed. v0.22.0 Edge Full-Stack may proceed.**"
echo "All source-backed SOP gates passed. v0.22.0 Edge Full-Stack may proceed."
else
echo "⚠️ **Some gates failed. v0.22.0 must not start until all gates pass.**"
echo "Some gates failed. v0.22.0 must not start until all gates pass."
exit 1
fi
} >> "$GITHUB_STEP_SUMMARY"
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@
- JSR remote core subpath resolution now fetches `@lessjs/core` source URLs
instead of accidentally deriving paths from `@lessjs/adapter-vite`.

### CI / Documentation

- SOP Gate now stays source-backed and no longer runs the JSR latest consumer
matrix as a required branch gate.
- Post-publish Ubuntu consumer smoke remains the authoritative release gate for
freshly published immutable JSR packages.
- Added a scheduled/manual Windows JSR latest monitor for platform
compatibility without blocking source branch gates.
- Added ADR-0048 and SOP-018 to document the CI/release gate split.

## 0.21.15 (2026-05-25)

### Fixed
Expand Down
62 changes: 62 additions & 0 deletions docs/adr/ADR-0048-ci-release-gate-separation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# ADR-0048: CI and Release Gate Separation

- Status: ACCEPTED
- Date: 2026-05-25

## Context

The v0.21.x JSR consumer hardening line exposed a gap between three different
checks that had been treated as one release signal:

- source-backed checks that prove the current branch can build;
- post-publish checks that prove the immutable JSR package set works after it
exists; and
- platform compatibility checks that monitor JSR latest on non-Linux runners.

The old SOP Gate included a `JSR consumer build (ubuntu + windows)` job that
ran `deno run -A jsr:@lessjs/create test-blog`. That job used the already
published latest JSR version, not the current commit. It was useful as a public
path monitor, but it could not validate unpublished release candidates. The
Ubuntu half also duplicated the post-publish consumer smoke in `publish.yml`,
while the Windows half could be canceled or delayed by hosted-runner capacity
and block unrelated source-gate feedback.

## Decision

LessJS CI is split into three explicit layers:

1. **SOP Gate**
Runs source-backed validation for the current branch. It keeps local package
tests, generated-project starter proof, build, DSD, Hub, docs, and e2e gates.
It does not run JSR latest as a required branch gate.

2. **Publish Gate**
Runs after publishing missing JSR package versions. It generates a fresh
consumer project from `jsr:@lessjs/create` on Ubuntu and builds it. This is
the authoritative release gate for immutable JSR packages.

3. **JSR Consumer Monitor**
Runs manually and on schedule against JSR latest on Windows. It proves the
public command on a second operating system without blocking every source
branch or duplicating the Ubuntu post-publish release gate.

## Consequences

- Release failures caused by immutable package publication remain caught by the
post-publish consumer smoke.
- Source branches get faster, more relevant feedback because JSR latest no
longer masquerades as a release-candidate check.
- Windows compatibility remains visible, but hosted-runner cancellation does
not block the main SOP Gate.
- If the Windows monitor fails, the fix should be handled as a compatibility
issue against JSR latest or the next patch release, not as evidence that the
current source branch failed to build.

## Acceptance

- `.github/workflows/sop-gate.yml` contains only source-backed branch gates.
- `.github/workflows/publish.yml` keeps the Ubuntu post-publish consumer smoke.
- `.github/workflows/jsr-consumer-monitor.yml` runs JSR latest on Windows by
`workflow_dispatch` and schedule.
- `docs/sop/v0.21.x/` documents the separation so future release work does not
reintroduce duplicate JSR consumer gates.
1 change: 1 addition & 0 deletions docs/adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ architectural decision, its context, and consequences.
| 0039 | DsdElement + Signals Reactive Architecture | Accepted |
| 0040 | Streaming DSD | Accepted |
| 0041 | ESM Module Graph First for JSR Consumer Builds | Accepted |
| 0048 | CI and Release Gate Separation | Accepted |

## Superseded / Historical

Expand Down
23 changes: 23 additions & 0 deletions docs/changelog/v0.21.x.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,29 @@ deno task publish
Do not treat the release as complete until the generated consumer smoke passes
outside the LessJS workspace.

## v0.21.13 to v0.21.16

### Post-publish JSR Consumer Hardening

- v0.21.13 guarded JSR remote execution paths before calling `fileURLToPath()`.
- v0.21.14 reused the LessJS JSR package resolver in Phase 2 client island
builds, fixing package island imports such as `@lessjs/ui/less-card`.
- v0.21.15 gave `content.nav` a default `routesDir: 'app/routes'` and wrote the
same value into generated projects.
- v0.21.16 replaced the outer Vite Hono-entry bundle with a no-op build trigger
and fixed remote `@lessjs/core/*` URL derivation.

### CI Gate Separation

- `sop-gate.yml` is now source-backed and no longer requires a JSR latest
consumer matrix.
- `publish.yml` keeps the Ubuntu post-publish consumer smoke as the
authoritative release validation for freshly published package sets.
- `jsr-consumer-monitor.yml` monitors JSR latest on Windows by schedule or
manual dispatch.
- See [ADR-0048](../adr/ADR-0048-ci-release-gate-separation.md) and
[SOP-018](../sop/v0.21.x/SOP-018-ci-release-gate-separation.md).

## v0.21.12

### Fixed
Expand Down
Loading
Loading