From c8c73180052e0aba1373ae5eefde8ff9214fc5c8 Mon Sep 17 00:00:00 2001 From: Tom Davidson Date: Mon, 16 Mar 2026 19:47:18 +0000 Subject: [PATCH 01/16] ci: add CI/CD, release, and publish pipeline - GitHub Actions: ci.yml (moon ci + CodeQL + security), release-please.yml, pr-title.yml (conventional commits), fuzz.yml (manual cargo-fuzz) - Publish workflows: riff (GoReleaser + cargo-zigbuild + multi-arch OCI), npm (slash-javascript, slash-web), crates.io (slash-rust), PyPI (slash-python), WASM (wasm-javascript, wasm-wasi + WIT), Go (slash-go validation) - Review artifacts: workflow_dispatch snapshot builds with PR cleanup - Release-please: manifest mode with cargo-workspace and linked-versions plugins, parser group linking (parser, wasm-javascript, wasm-wasi, spec) - GoReleaser: riff-cli cross-compilation via cargo-zigbuild, nFPM packages, Homebrew tap, multi-arch Docker (ghcr.io/tomdavidson/riff) - Moon tasks: python.yml, go.yml, security.yml, fmt-check for Rust - Project moon.yml: updated language, type, tags, publish tasks - Renovate: dependency update automation - Helper scripts: WASM/WIT artifact attachment, workflow gen placeholder --- .github/workflows/ci.yml | 93 ++++++++++++++++ .github/workflows/fuzz.yml | 36 +++++++ .github/workflows/pr-title.yml | 27 +++++ .github/workflows/publish-crates.yml | 32 ++++++ .github/workflows/publish-go.yml | 21 ++++ .../publish-npm-slash-javascript.yml | 37 +++++++ .github/workflows/publish-npm-slash-web.yml | 37 +++++++ .github/workflows/publish-pypi.yml | 34 ++++++ .github/workflows/publish-riff.yml | 54 ++++++++++ .github/workflows/publish-wasm.yml | 58 ++++++++++ .github/workflows/release-please.yml | 18 ++++ .github/workflows/review.yml | 79 ++++++++++++++ .moon/tasks/go.yml | 36 +++++++ .moon/tasks/python.yml | 42 ++++++++ .moon/tasks/rust.yml | 25 +++++ .moon/tasks/security.yml | 20 ++++ .release-please-manifest.json | 12 +++ release-please-config.json | 70 ++++++++++++ renovate.json | 29 +++++ riff-cli/.goreleaser.yaml | 101 ++++++++++++++++++ riff-cli/Dockerfile | 3 + riff-cli/moon.yml | 19 ++-- scripts/attach-wasm-javascript.sh | 19 ++++ scripts/attach-wasm-wasi.sh | 35 ++++++ scripts/gen-workflows.sh | 14 +++ slash-go/moon.yml | 13 +-- slash-javascript/moon.yml | 18 ++-- slash-python/moon.yml | 18 ++-- slash-rust/moon.yml | 18 ++-- slash-web/moon.yml | 18 ++-- wasm-javascript/moon.yml | 13 +-- wasm-wasi/moon.yml | 13 +-- website/moon.yml | 9 +- 33 files changed, 999 insertions(+), 72 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/fuzz.yml create mode 100644 .github/workflows/pr-title.yml create mode 100644 .github/workflows/publish-crates.yml create mode 100644 .github/workflows/publish-go.yml create mode 100644 .github/workflows/publish-npm-slash-javascript.yml create mode 100644 .github/workflows/publish-npm-slash-web.yml create mode 100644 .github/workflows/publish-pypi.yml create mode 100644 .github/workflows/publish-riff.yml create mode 100644 .github/workflows/publish-wasm.yml create mode 100644 .github/workflows/release-please.yml create mode 100644 .github/workflows/review.yml create mode 100644 .moon/tasks/go.yml create mode 100644 .moon/tasks/python.yml create mode 100644 .moon/tasks/security.yml create mode 100644 .release-please-manifest.json create mode 100644 release-please-config.json create mode 100644 renovate.json create mode 100644 riff-cli/.goreleaser.yaml create mode 100644 riff-cli/Dockerfile create mode 100755 scripts/attach-wasm-javascript.sh create mode 100755 scripts/attach-wasm-wasi.sh create mode 100755 scripts/gen-workflows.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7ad6819 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,93 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +jobs: + ci: + name: Moon CI + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: moonrepo/setup-toolchain@v0 + with: + auto-install: true + + - uses: actions/cache@v4 + with: + path: | + .moon/cache + key: moon-${{ runner.os }}-${{ hashFiles('.moon/workspace.yml', '.moon/tasks/*.yml', '.moon/toolchains.yml') }} + restore-keys: | + moon-${{ runner.os }}- + + - uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + cargo-${{ runner.os }}- + + - uses: actions/cache@v4 + with: + path: | + ~/.local/share/pnpm/store + key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + pnpm-${{ runner.os }}- + + - name: Moon CI + run: moon ci + + codeql: + name: CodeQL + runs-on: ubuntu-latest + permissions: + security-events: write + actions: read + contents: read + steps: + - uses: actions/checkout@v4 + + - uses: github/codeql-action/init@v3 + with: + languages: javascript-typescript + + - uses: github/codeql-action/autobuild@v3 + + - uses: github/codeql-action/analyze@v3 + + security: + name: Security Scans + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: moonrepo/setup-toolchain@v0 + with: + auto-install: true + + - name: Install security tools + run: | + cargo install cargo-audit cargo-deny || true + pip install pip-audit || true + go install golang.org/x/vuln/cmd/govulncheck@latest || true + + - name: Run gitleaks + uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml new file mode 100644 index 0000000..12a9dc2 --- /dev/null +++ b/.github/workflows/fuzz.yml @@ -0,0 +1,36 @@ +name: Fuzz + +on: + workflow_dispatch: + inputs: + duration: + description: "Fuzz duration (e.g., 60s, 10m, 1h)" + required: false + default: "10m" + +jobs: + fuzz: + name: Fuzz Parser + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: moonrepo/setup-toolchain@v0 + with: + auto-install: true + + - name: Install cargo-fuzz + run: cargo install cargo-fuzz + + - name: Run fuzzer + run: | + cd fuzz + cargo fuzz run fuzz_parser -- -max_total_time=${{ github.event.inputs.duration == '' && '600' || github.event.inputs.duration }} + continue-on-error: true + + - name: Upload crash artifacts + if: failure() + uses: actions/upload-artifact@v4 + with: + name: fuzz-crashes + path: fuzz/artifacts/ diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 0000000..96ed416 --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,27 @@ +name: PR Title + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + +jobs: + lint-pr-title: + name: Validate PR Title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + feat + fix + chore + docs + refactor + test + perf + ci + build + revert + requireScope: false diff --git a/.github/workflows/publish-crates.yml b/.github/workflows/publish-crates.yml new file mode 100644 index 0000000..1dca0b2 --- /dev/null +++ b/.github/workflows/publish-crates.yml @@ -0,0 +1,32 @@ +# NOTE: Hand-authored. TODO: Generate from Moon Tera templates (.moon/templates/publish-workflows/) +name: Publish slash-rust to crates.io + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: "Release tag (e.g., slash-rust-v1.0.0)" + required: true + +jobs: + publish: + if: >- + (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'slash-rust-v')) + || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + environment: publish + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + + - uses: moonrepo/setup-toolchain@v0 + with: + auto-install: true + + - name: Publish to crates.io + run: moon run slash-rust:publish + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/publish-go.yml b/.github/workflows/publish-go.yml new file mode 100644 index 0000000..d4a4fb5 --- /dev/null +++ b/.github/workflows/publish-go.yml @@ -0,0 +1,21 @@ +# NOTE: Hand-authored. TODO: Generate from Moon Tera templates (.moon/templates/publish-workflows/) +name: Validate slash-go + +on: + release: + types: [published] + +jobs: + validate: + if: startsWith(github.event.release.tag_name, 'slash-go/') + runs-on: ubuntu-latest + environment: publish + steps: + - uses: actions/checkout@v4 + + - uses: moonrepo/setup-toolchain@v0 + with: + auto-install: true + + - name: Validate Go module + run: moon run slash-go:build diff --git a/.github/workflows/publish-npm-slash-javascript.yml b/.github/workflows/publish-npm-slash-javascript.yml new file mode 100644 index 0000000..62c453f --- /dev/null +++ b/.github/workflows/publish-npm-slash-javascript.yml @@ -0,0 +1,37 @@ +# NOTE: Hand-authored. TODO: Generate from Moon Tera templates (.moon/templates/publish-workflows/) +name: Publish @slasher/slash-javascript + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: "Release tag (e.g., slash-javascript-v1.0.0)" + required: true + +jobs: + publish: + if: >- + (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'slash-javascript-v')) + || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + environment: publish + permissions: + contents: write + id-token: write + steps: + - uses: actions/checkout@v4 + + - uses: moonrepo/setup-toolchain@v0 + with: + auto-install: true + + - name: Build + run: moon run slash-javascript:build + + - name: Publish to npm + run: moon run slash-javascript:publish + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/publish-npm-slash-web.yml b/.github/workflows/publish-npm-slash-web.yml new file mode 100644 index 0000000..55e48a6 --- /dev/null +++ b/.github/workflows/publish-npm-slash-web.yml @@ -0,0 +1,37 @@ +# NOTE: Hand-authored. TODO: Generate from Moon Tera templates (.moon/templates/publish-workflows/) +name: Publish @slasher/slash-web + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: "Release tag (e.g., slash-web-v1.0.0)" + required: true + +jobs: + publish: + if: >- + (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'slash-web-v')) + || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + environment: publish + permissions: + contents: write + id-token: write + steps: + - uses: actions/checkout@v4 + + - uses: moonrepo/setup-toolchain@v0 + with: + auto-install: true + + - name: Build + run: moon run slash-web:build + + - name: Publish to npm + run: moon run slash-web:publish + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml new file mode 100644 index 0000000..9e4e478 --- /dev/null +++ b/.github/workflows/publish-pypi.yml @@ -0,0 +1,34 @@ +# NOTE: Hand-authored. TODO: Generate from Moon Tera templates (.moon/templates/publish-workflows/) +name: Publish slash-python to PyPI + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: "Release tag (e.g., slash-python-v1.0.0)" + required: true + +jobs: + publish: + if: >- + (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'slash-python-v')) + || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + environment: publish + steps: + - uses: actions/checkout@v4 + + - uses: moonrepo/setup-toolchain@v0 + with: + auto-install: true + + - name: Build + run: moon run slash-python:build + + - name: Publish to PyPI + run: moon run slash-python:publish + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/publish-riff.yml b/.github/workflows/publish-riff.yml new file mode 100644 index 0000000..f6a94bc --- /dev/null +++ b/.github/workflows/publish-riff.yml @@ -0,0 +1,54 @@ +# NOTE: Hand-authored. TODO: Generate from Moon Tera templates (.moon/templates/publish-workflows/) +name: Publish riff + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: "Release tag (e.g., riff-v1.0.0)" + required: true + +jobs: + publish: + if: >- + (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'riff-v')) + || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + environment: publish + permissions: + contents: write + packages: write + id-token: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: moonrepo/setup-toolchain@v0 + with: + auto-install: true + + - uses: mlugg/setup-zig@v1 + + - name: Install cargo-zigbuild + run: cargo install --locked cargo-zigbuild + + - uses: docker/setup-qemu-action@v3 + - uses: docker/setup-buildx-action@v3 + + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: "~> v2" + args: release --clean --config riff-cli/.goreleaser.yaml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} diff --git a/.github/workflows/publish-wasm.yml b/.github/workflows/publish-wasm.yml new file mode 100644 index 0000000..0d83805 --- /dev/null +++ b/.github/workflows/publish-wasm.yml @@ -0,0 +1,58 @@ +# NOTE: Hand-authored. TODO: Generate from Moon Tera templates (.moon/templates/publish-workflows/) +name: Publish WASM + WIT + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: "Release tag (e.g., wasm-javascript-v1.0.0 or wasm-wasi-v1.0.0)" + required: true + +jobs: + publish-wasm-javascript: + if: >- + (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'wasm-javascript-v')) + || (github.event_name == 'workflow_dispatch' && startsWith(github.event.inputs.tag, 'wasm-javascript-v')) + runs-on: ubuntu-latest + environment: publish + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - uses: moonrepo/setup-toolchain@v0 + with: + auto-install: true + + - name: Build wasm-javascript + run: moon run wasm-javascript:build + + - name: Attach artifacts to GitHub Release + run: ./scripts/attach-wasm-javascript.sh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish-wasm-wasi: + if: >- + (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'wasm-wasi-v')) + || (github.event_name == 'workflow_dispatch' && startsWith(github.event.inputs.tag, 'wasm-wasi-v')) + runs-on: ubuntu-latest + environment: publish + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - uses: moonrepo/setup-toolchain@v0 + with: + auto-install: true + + - name: Build wasm-wasi + WIT + run: moon run wasm-wasi:build + + - name: Attach wasm-wasi and WIT artifacts to GitHub Release + run: ./scripts/attach-wasm-wasi.sh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..785f956 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,18 @@ +name: Release Please + +on: + push: + branches: [main] + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - uses: googleapis/release-please-action@v4 + id: release + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml new file mode 100644 index 0000000..818b3dc --- /dev/null +++ b/.github/workflows/review.yml @@ -0,0 +1,79 @@ +name: Review Artifacts + +on: + workflow_dispatch: + inputs: + pr_number: + description: "PR number to build review artifacts for" + required: true + + pull_request: + types: [closed] + +jobs: + build-review-artifacts: + if: github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + environment: review-pr-${{ github.event.inputs.pr_number }} + steps: + - uses: actions/checkout@v4 + with: + ref: refs/pull/${{ github.event.inputs.pr_number }}/head + + - uses: moonrepo/setup-toolchain@v0 + with: + auto-install: true + + - uses: mlugg/setup-zig@v1 + + - name: Install cargo-zigbuild + run: cargo install --locked cargo-zigbuild + + - name: Install GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: "~> v2" + install-only: true + + - name: Build riff snapshot + run: goreleaser release --config riff-cli/.goreleaser.yaml --snapshot --clean --skip=publish + continue-on-error: true + + - name: Build SDK snapshots + run: | + moon run slash-rust:build || true + moon run slash-javascript:build || true + moon run slash-web:build || true + moon run slash-python:build || true + moon run wasm-javascript:build || true + moon run wasm-wasi:build || true + + - name: Upload review artifacts + uses: actions/upload-artifact@v4 + with: + name: review-pr-${{ github.event.inputs.pr_number }} + path: | + riff-cli/dist/** + slash-javascript/dist/** + slash-web/dist/** + slash-python/dist/** + retention-days: 14 + + cleanup: + if: github.event_name == 'pull_request' && github.event.action == 'closed' + runs-on: ubuntu-latest + steps: + - name: Mark review environment inactive + run: | + echo "Cleaning up review-pr-${{ github.event.pull_request.number }}" + # Mark deployment as inactive via GitHub API if one exists + DEPLOYMENTS=$(gh api repos/${{ github.repository }}/deployments \ + --jq "[.[] | select(.environment == \"review-pr-${{ github.event.pull_request.number }}\")] | .[].id" 2>/dev/null || true) + for DEPLOY_ID in $DEPLOYMENTS; do + gh api repos/${{ github.repository }}/deployments/$DEPLOY_ID/statuses \ + -f state=inactive \ + -f description="PR closed" 2>/dev/null || true + done + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.moon/tasks/go.yml b/.moon/tasks/go.yml new file mode 100644 index 0000000..b2d73ae --- /dev/null +++ b/.moon/tasks/go.yml @@ -0,0 +1,36 @@ +$schema: "https://moonrepo.dev/schemas/tasks.json" + +fileGroups: + sources: + - "**/*.go" + - "go.mod" + - "go.sum" + tests: + - "**/*_test.go" + +tasks: + lint: + command: "go" + args: ["vet", "./..."] + inputs: + - "@group(sources)" + + fmt-check: + script: "test -z \"$(gofmt -l .)\"" + inputs: + - "@group(sources)" + + test: + command: "go" + args: ["test", "./..."] + inputs: + - "@group(sources)" + - "@group(tests)" + + build: + command: "go" + args: ["build", "./..."] + inputs: + - "@group(sources)" + outputs: + - "bin" diff --git a/.moon/tasks/python.yml b/.moon/tasks/python.yml new file mode 100644 index 0000000..d25903e --- /dev/null +++ b/.moon/tasks/python.yml @@ -0,0 +1,42 @@ +$schema: "https://moonrepo.dev/schemas/tasks.json" + +fileGroups: + sources: + - "**/*.py" + - "pyproject.toml" + tests: + - "tests/**/*.py" + +tasks: + lint: + command: "python" + args: ["-m", "ruff", "check", "."] + inputs: + - "@group(sources)" + + fmt-check: + command: "python" + args: ["-m", "ruff", "format", ".", "--check"] + inputs: + - "@group(sources)" + + test: + command: "python" + args: ["-m", "pytest"] + inputs: + - "@group(sources)" + - "@group(tests)" + + typecheck: + command: "python" + args: ["-m", "mypy", "."] + inputs: + - "@group(sources)" + + build: + command: "python" + args: ["-m", "build"] + inputs: + - "@group(sources)" + outputs: + - "dist" diff --git a/.moon/tasks/rust.yml b/.moon/tasks/rust.yml index cefd935..d749b22 100644 --- a/.moon/tasks/rust.yml +++ b/.moon/tasks/rust.yml @@ -72,3 +72,28 @@ tasks: - "@group(tests)" - "/.config/nextest.*" env: *env + + fmt-check: + command: "cargo fmt --check" + inputs: + - "@group(cargo)" + - "@group(sources)" + env: *env + + cargo-audit: + command: "cargo" + args: ["audit"] + inputs: + - "@group(cargo)" + env: *env + options: + runInCI: false + + cargo-deny: + command: "cargo" + args: ["deny", "check"] + inputs: + - "@group(cargo)" + env: *env + options: + runInCI: false diff --git a/.moon/tasks/security.yml b/.moon/tasks/security.yml new file mode 100644 index 0000000..781922b --- /dev/null +++ b/.moon/tasks/security.yml @@ -0,0 +1,20 @@ +$schema: "https://moonrepo.dev/schemas/tasks.json" + +tasks: + semgrep: + command: "semgrep" + args: ["--config", "auto", "--error", "."] + options: + runInCI: false + + gitleaks: + command: "gitleaks" + args: ["detect", "--source", ".", "-v"] + options: + runInCI: false + + tangleguard: + command: "echo" + args: ["TangleGuard placeholder — not yet implemented"] + options: + runInCI: false diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..11ea174 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,12 @@ +{ + "parser": "0.1.0", + "wasm-javascript": "0.1.0", + "wasm-wasi": "0.1.0", + "spec": "0.1.0", + "riff-cli": "0.1.0", + "slash-rust": "0.1.0", + "slash-javascript": "0.1.0", + "slash-web": "0.1.0", + "slash-python": "0.1.0", + "slash-go": "0.1.0" +} diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..2196351 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,70 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "plugins": [ + { + "type": "cargo-workspace" + }, + { + "type": "linked-versions", + "groupName": "parser-group", + "components": [ + "parser", + "wasm-javascript", + "wasm-wasi", + "spec" + ] + } + ], + "release-type": "rust", + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": true, + "separate-pull-requests": false, + "include-component-in-tag": true, + "include-v-in-tag": true, + "tag-separator": "-", + "packages": { + "parser": { + "release-type": "rust", + "component": "parser" + }, + "wasm-javascript": { + "release-type": "rust", + "component": "wasm-javascript" + }, + "wasm-wasi": { + "release-type": "rust", + "component": "wasm-wasi" + }, + "spec": { + "release-type": "simple", + "component": "spec" + }, + "riff-cli": { + "path": "riff-cli", + "release-type": "rust", + "component": "riff" + }, + "slash-rust": { + "release-type": "rust", + "component": "slash-rust" + }, + "slash-javascript": { + "release-type": "node", + "component": "slash-javascript" + }, + "slash-web": { + "release-type": "node", + "component": "slash-web" + }, + "slash-python": { + "release-type": "python", + "component": "slash-python" + }, + "slash-go": { + "release-type": "go", + "component": "slash-go", + "tag-separator": "/", + "include-v-in-tag": true + } + } +} diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..4a61931 --- /dev/null +++ b/renovate.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ], + "labels": ["dependencies"], + "packageRules": [ + { + "matchManagers": ["cargo"], + "groupName": "rust dependencies" + }, + { + "matchManagers": ["npm"], + "groupName": "npm dependencies" + }, + { + "matchManagers": ["pip_requirements", "poetry"], + "groupName": "python dependencies" + }, + { + "matchManagers": ["gomod"], + "groupName": "go dependencies" + }, + { + "matchManagers": ["github-actions"], + "groupName": "github actions" + } + ] +} diff --git a/riff-cli/.goreleaser.yaml b/riff-cli/.goreleaser.yaml new file mode 100644 index 0000000..fa39f6d --- /dev/null +++ b/riff-cli/.goreleaser.yaml @@ -0,0 +1,101 @@ +# yaml-language-server: $schema=https://goreleaser.com/static/schema.json +version: 2 + +project_name: riff + +release: + mode: keep-existing + github: + owner: tomdavidson + name: slasher + +changelog: + disable: true + +before: + hooks: + - rustup default stable + - cargo install --locked cargo-zigbuild + +builds: + - id: riff + builder: rust + dir: riff-cli + targets: + - x86_64-unknown-linux-gnu + - aarch64-unknown-linux-gnu + - x86_64-apple-darwin + - aarch64-apple-darwin + env: + - RUSTFLAGS=-C target-feature=+crt-static + +archives: + - id: riff-archives + builds: + - riff + format: tar.gz + name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" + files: + - LICENSE* + - README* + +nfpms: + - id: riff-nfpms + package_name: riff + file_name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" + formats: + - deb + - rpm + - apk + builds: + - riff + maintainer: "Tom Davidson " + description: "Slasher riff CLI — slash command parser" + license: MIT + homepage: "https://github.com/tomdavidson/slasher" + +brews: + - name: riff + ids: + - riff-archives + repository: + owner: tomdavidson + name: homebrew-tap + token: "{{ .Env.HOMEBREW_TAP_TOKEN }}" + directory: Formula + homepage: "https://github.com/tomdavidson/slasher" + description: "Slasher riff CLI — slash command parser" + license: MIT + +dockers: + - image_templates: + - "ghcr.io/tomdavidson/riff:{{ .Version }}-amd64" + use: buildx + goos: linux + goarch: amd64 + dockerfile: riff-cli/Dockerfile + build_flag_templates: + - "--platform=linux/amd64" + - image_templates: + - "ghcr.io/tomdavidson/riff:{{ .Version }}-arm64" + use: buildx + goos: linux + goarch: arm64 + dockerfile: riff-cli/Dockerfile + build_flag_templates: + - "--platform=linux/arm64" + +docker_manifests: + - name_template: "ghcr.io/tomdavidson/riff:{{ .Version }}" + image_templates: + - "ghcr.io/tomdavidson/riff:{{ .Version }}-amd64" + - "ghcr.io/tomdavidson/riff:{{ .Version }}-arm64" + - name_template: "ghcr.io/tomdavidson/riff:latest" + image_templates: + - "ghcr.io/tomdavidson/riff:{{ .Version }}-amd64" + - "ghcr.io/tomdavidson/riff:{{ .Version }}-arm64" + +checksum: + name_template: "{{ .ProjectName }}_checksums.txt" + +dist: dist diff --git a/riff-cli/Dockerfile b/riff-cli/Dockerfile new file mode 100644 index 0000000..717fb85 --- /dev/null +++ b/riff-cli/Dockerfile @@ -0,0 +1,3 @@ +FROM gcr.io/distroless/static:nonroot +COPY riff /usr/local/bin/riff +ENTRYPOINT ["/usr/local/bin/riff"] diff --git a/riff-cli/moon.yml b/riff-cli/moon.yml index c9a0d75..26faf0c 100644 --- a/riff-cli/moon.yml +++ b/riff-cli/moon.yml @@ -1,9 +1,12 @@ -# $schema: https://moonrepo.dev/schemas/project.json +$schema: "https://moonrepo.dev/schemas/project.json" +id: "riff-cli" +language: "rust" +type: "application" +tags: ["cli"] -language: "unknown" - -tags: ["core"] - -workspace: - inheritedTasks: - include: [] +tasks: + publish: + command: "goreleaser" + args: ["release", "--clean", "--config", "riff-cli/.goreleaser.yaml"] + options: + runInCI: false diff --git a/scripts/attach-wasm-javascript.sh b/scripts/attach-wasm-javascript.sh new file mode 100755 index 0000000..72147e7 --- /dev/null +++ b/scripts/attach-wasm-javascript.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Attach wasm-javascript build artifacts to the current GitHub Release +# Expects GITHUB_TOKEN and GITHUB_REF_NAME to be set + +artifact_dir="wasm-javascript/pkg" + +if [ ! -d "$artifact_dir" ]; then + echo "ERROR: Build output directory $artifact_dir not found" + exit 1 +fi + +for file in "$artifact_dir"/*; do + [ -f "$file" ] || continue + name="$(basename "$file")" + echo "Uploading $name" + gh release upload "$GITHUB_REF_NAME" "$file" --repo "$GITHUB_REPOSITORY" --clobber +done diff --git a/scripts/attach-wasm-wasi.sh b/scripts/attach-wasm-wasi.sh new file mode 100755 index 0000000..b2819b4 --- /dev/null +++ b/scripts/attach-wasm-wasi.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Attach wasm-wasi + WIT artifacts to the current GitHub Release +# Expects GITHUB_TOKEN and GITHUB_REF_NAME to be set + +wasm_dir="wasm-wasi/target/wasm32-wasip2/release" +wit_dir="wasm-wasi/wit" + +# Upload WASM component if built +if [ -d "$wasm_dir" ]; then + for file in "$wasm_dir"/*.wasm; do + [ -f "$file" ] || continue + echo "Uploading $(basename "$file")" + gh release upload "$GITHUB_REF_NAME" "$file" --repo "$GITHUB_REPOSITORY" --clobber + done +else + echo "WARN: WASM build output directory $wasm_dir not found" +fi + +# Upload WIT definitions +if [ -d "$wit_dir" ]; then + for file in "$wit_dir"/*.wit; do + [ -f "$file" ] || continue + echo "Uploading $(basename "$file")" + gh release upload "$GITHUB_REF_NAME" "$file" --repo "$GITHUB_REPOSITORY" --clobber + done + + # Also create a bundled wit archive + tar -czf wit-definitions.tar.gz -C wasm-wasi wit/ + gh release upload "$GITHUB_REF_NAME" "wit-definitions.tar.gz" --repo "$GITHUB_REPOSITORY" --clobber + rm -f wit-definitions.tar.gz +else + echo "WARN: WIT directory $wit_dir not found" +fi diff --git a/scripts/gen-workflows.sh b/scripts/gen-workflows.sh new file mode 100755 index 0000000..a7e5b30 --- /dev/null +++ b/scripts/gen-workflows.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail + +# TODO: Generate publish workflows from Moon Tera templates +# Once .moon/templates/publish-workflows/ is implemented, this script will: +# moon generate publish-workflows --force +# and place generated files into .github/workflows/ +# +# For now, publish workflows are hand-authored under .github/workflows/publish-*.yml +# To add a new SDK publish workflow, copy an existing one and adjust the tag prefix, +# project name, and publish command. + +echo "Workflow generation not yet implemented — publish workflows are hand-authored." +echo "See .github/workflows/publish-*.yml" diff --git a/slash-go/moon.yml b/slash-go/moon.yml index c9a0d75..4980cfe 100644 --- a/slash-go/moon.yml +++ b/slash-go/moon.yml @@ -1,9 +1,4 @@ -# $schema: https://moonrepo.dev/schemas/project.json - -language: "unknown" - -tags: ["core"] - -workspace: - inheritedTasks: - include: [] +$schema: "https://moonrepo.dev/schemas/project.json" +language: "go" +type: "library" +tags: ["sdk"] diff --git a/slash-javascript/moon.yml b/slash-javascript/moon.yml index c9a0d75..75ba71a 100644 --- a/slash-javascript/moon.yml +++ b/slash-javascript/moon.yml @@ -1,9 +1,11 @@ -# $schema: https://moonrepo.dev/schemas/project.json +$schema: "https://moonrepo.dev/schemas/project.json" +language: "typescript" +type: "library" +tags: ["sdk", "npm"] -language: "unknown" - -tags: ["core"] - -workspace: - inheritedTasks: - include: [] +tasks: + publish: + command: "pnpm" + args: ["publish", "--access", "public", "--no-git-checks"] + options: + runInCI: false diff --git a/slash-python/moon.yml b/slash-python/moon.yml index c9a0d75..c58c0fc 100644 --- a/slash-python/moon.yml +++ b/slash-python/moon.yml @@ -1,9 +1,11 @@ -# $schema: https://moonrepo.dev/schemas/project.json +$schema: "https://moonrepo.dev/schemas/project.json" +language: "python" +type: "library" +tags: ["sdk"] -language: "unknown" - -tags: ["core"] - -workspace: - inheritedTasks: - include: [] +tasks: + publish: + command: "python" + args: ["-m", "twine", "upload", "dist/*"] + options: + runInCI: false diff --git a/slash-rust/moon.yml b/slash-rust/moon.yml index c9a0d75..eb08538 100644 --- a/slash-rust/moon.yml +++ b/slash-rust/moon.yml @@ -1,9 +1,11 @@ -# $schema: https://moonrepo.dev/schemas/project.json +$schema: "https://moonrepo.dev/schemas/project.json" +language: "rust" +type: "library" +tags: ["sdk"] -language: "unknown" - -tags: ["core"] - -workspace: - inheritedTasks: - include: [] +tasks: + publish: + command: "cargo" + args: ["publish", "--allow-dirty"] + options: + runInCI: false diff --git a/slash-web/moon.yml b/slash-web/moon.yml index c9a0d75..75ba71a 100644 --- a/slash-web/moon.yml +++ b/slash-web/moon.yml @@ -1,9 +1,11 @@ -# $schema: https://moonrepo.dev/schemas/project.json +$schema: "https://moonrepo.dev/schemas/project.json" +language: "typescript" +type: "library" +tags: ["sdk", "npm"] -language: "unknown" - -tags: ["core"] - -workspace: - inheritedTasks: - include: [] +tasks: + publish: + command: "pnpm" + args: ["publish", "--access", "public", "--no-git-checks"] + options: + runInCI: false diff --git a/wasm-javascript/moon.yml b/wasm-javascript/moon.yml index c9a0d75..3263f79 100644 --- a/wasm-javascript/moon.yml +++ b/wasm-javascript/moon.yml @@ -1,9 +1,4 @@ -# $schema: https://moonrepo.dev/schemas/project.json - -language: "unknown" - -tags: ["core"] - -workspace: - inheritedTasks: - include: [] +$schema: "https://moonrepo.dev/schemas/project.json" +language: "rust" +type: "library" +tags: ["wasm"] diff --git a/wasm-wasi/moon.yml b/wasm-wasi/moon.yml index c9a0d75..3263f79 100644 --- a/wasm-wasi/moon.yml +++ b/wasm-wasi/moon.yml @@ -1,9 +1,4 @@ -# $schema: https://moonrepo.dev/schemas/project.json - -language: "unknown" - -tags: ["core"] - -workspace: - inheritedTasks: - include: [] +$schema: "https://moonrepo.dev/schemas/project.json" +language: "rust" +type: "library" +tags: ["wasm"] diff --git a/website/moon.yml b/website/moon.yml index c9a0d75..d2ebd6b 100644 --- a/website/moon.yml +++ b/website/moon.yml @@ -1,8 +1,7 @@ -# $schema: https://moonrepo.dev/schemas/project.json - -language: "unknown" - -tags: ["core"] +$schema: "https://moonrepo.dev/schemas/project.json" +language: "typescript" +type: "application" +tags: ["docs"] workspace: inheritedTasks: From af75bc98bce21973405acf9955db780457cbd2d6 Mon Sep 17 00:00:00 2001 From: Tom Davidson Date: Mon, 16 Mar 2026 20:55:38 +0000 Subject: [PATCH 02/16] ci: improve CI pipeline with parallelization, moon-native security, and cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Parallelize moon ci with --job/--job-total matrix (2 shards) - Add moonrepo/run-report-action for PR run reports - Add appthrust/moon-ci-retrospect for readable CI logs - Move OSS security scans into moon tasks (run via moon ci, not separate job) - Move attach-release-assets scripts into their project directories - Thin fuzz workflow — delegate to parser:fuzz moon task - Fix renovate config to use pnpm manager --- .github/workflows/ci.yml | 37 +++++++------------ .github/workflows/fuzz.yml | 21 ++++------- .github/workflows/publish-wasm.yml | 4 +- .moon/tasks/rust.yml | 4 -- .moon/tasks/security.yml | 8 +--- parser/moon.yml | 7 ++++ renovate.json | 27 ++++++++++---- .../scripts/attach-release-assets.sh | 2 +- .../scripts/attach-release-assets.sh | 2 +- 9 files changed, 53 insertions(+), 59 deletions(-) rename scripts/attach-wasm-javascript.sh => wasm-javascript/scripts/attach-release-assets.sh (86%) rename scripts/attach-wasm-wasi.sh => wasm-wasi/scripts/attach-release-assets.sh (93%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ad6819..9b90a4d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,9 @@ jobs: ci: name: Moon CI runs-on: ubuntu-latest + strategy: + matrix: + index: [0, 1] steps: - uses: actions/checkout@v4 with: @@ -49,7 +52,16 @@ jobs: pnpm-${{ runner.os }}- - name: Moon CI - run: moon ci + run: moon ci --color --job ${{ matrix.index }} --job-total 2 + + - uses: moonrepo/run-report-action@v1 + if: success() || failure() + with: + access-token: ${{ secrets.GITHUB_TOKEN }} + matrix: ${{ toJSON(matrix) }} + + - uses: appthrust/moon-ci-retrospect@v1 + if: success() || failure() codeql: name: CodeQL @@ -68,26 +80,3 @@ jobs: - uses: github/codeql-action/autobuild@v3 - uses: github/codeql-action/analyze@v3 - - security: - name: Security Scans - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: moonrepo/setup-toolchain@v0 - with: - auto-install: true - - - name: Install security tools - run: | - cargo install cargo-audit cargo-deny || true - pip install pip-audit || true - go install golang.org/x/vuln/cmd/govulncheck@latest || true - - - name: Run gitleaks - uses: gitleaks/gitleaks-action@v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 12a9dc2..5bf47b3 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -2,11 +2,6 @@ name: Fuzz on: workflow_dispatch: - inputs: - duration: - description: "Fuzz duration (e.g., 60s, 10m, 1h)" - required: false - default: "10m" jobs: fuzz: @@ -14,23 +9,23 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - uses: moonrepo/setup-toolchain@v0 with: auto-install: true - - name: Install cargo-fuzz - run: cargo install cargo-fuzz + - name: Fuzz parser + run: moon run parser:fuzz - - name: Run fuzzer - run: | - cd fuzz - cargo fuzz run fuzz_parser -- -max_total_time=${{ github.event.inputs.duration == '' && '600' || github.event.inputs.duration }} - continue-on-error: true + - uses: appthrust/moon-ci-retrospect@v1 + if: success() || failure() - name: Upload crash artifacts if: failure() uses: actions/upload-artifact@v4 with: name: fuzz-crashes - path: fuzz/artifacts/ + path: | + parser/hfuzz_workspace/ diff --git a/.github/workflows/publish-wasm.yml b/.github/workflows/publish-wasm.yml index 0d83805..c316a43 100644 --- a/.github/workflows/publish-wasm.yml +++ b/.github/workflows/publish-wasm.yml @@ -30,7 +30,7 @@ jobs: run: moon run wasm-javascript:build - name: Attach artifacts to GitHub Release - run: ./scripts/attach-wasm-javascript.sh + run: ./wasm-javascript/scripts/attach-release-assets.sh env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -53,6 +53,6 @@ jobs: run: moon run wasm-wasi:build - name: Attach wasm-wasi and WIT artifacts to GitHub Release - run: ./scripts/attach-wasm-wasi.sh + run: ./wasm-wasi/scripts/attach-release-assets.sh env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.moon/tasks/rust.yml b/.moon/tasks/rust.yml index d749b22..6c14a72 100644 --- a/.moon/tasks/rust.yml +++ b/.moon/tasks/rust.yml @@ -86,8 +86,6 @@ tasks: inputs: - "@group(cargo)" env: *env - options: - runInCI: false cargo-deny: command: "cargo" @@ -95,5 +93,3 @@ tasks: inputs: - "@group(cargo)" env: *env - options: - runInCI: false diff --git a/.moon/tasks/security.yml b/.moon/tasks/security.yml index 781922b..4351730 100644 --- a/.moon/tasks/security.yml +++ b/.moon/tasks/security.yml @@ -4,17 +4,11 @@ tasks: semgrep: command: "semgrep" args: ["--config", "auto", "--error", "."] - options: - runInCI: false gitleaks: command: "gitleaks" args: ["detect", "--source", ".", "-v"] - options: - runInCI: false tangleguard: command: "echo" - args: ["TangleGuard placeholder — not yet implemented"] - options: - runInCI: false + args: ["TangleGuard placeholder \u2014 not yet implemented"] diff --git a/parser/moon.yml b/parser/moon.yml index b98006f..87f3763 100644 --- a/parser/moon.yml +++ b/parser/moon.yml @@ -2,3 +2,10 @@ $schema: "https://moonrepo.dev/schemas/project.json" id: "parser" language: "rust" tags: ["core"] + +tasks: + fuzz: + command: "cargo" + args: ["hfuzz", "run", "parser"] + options: + runInCI: false diff --git a/renovate.json b/renovate.json index 4a61931..8762f16 100644 --- a/renovate.json +++ b/renovate.json @@ -3,26 +3,39 @@ "extends": [ "config:recommended" ], - "labels": ["dependencies"], + "labels": [ + "dependencies" + ], "packageRules": [ { - "matchManagers": ["cargo"], + "matchManagers": [ + "cargo" + ], "groupName": "rust dependencies" }, { - "matchManagers": ["npm"], - "groupName": "npm dependencies" + "matchManagers": [ + "pnpm" + ], + "groupName": "pnpm dependencies" }, { - "matchManagers": ["pip_requirements", "poetry"], + "matchManagers": [ + "pip_requirements", + "poetry" + ], "groupName": "python dependencies" }, { - "matchManagers": ["gomod"], + "matchManagers": [ + "gomod" + ], "groupName": "go dependencies" }, { - "matchManagers": ["github-actions"], + "matchManagers": [ + "github-actions" + ], "groupName": "github actions" } ] diff --git a/scripts/attach-wasm-javascript.sh b/wasm-javascript/scripts/attach-release-assets.sh similarity index 86% rename from scripts/attach-wasm-javascript.sh rename to wasm-javascript/scripts/attach-release-assets.sh index 72147e7..23fa8ea 100755 --- a/scripts/attach-wasm-javascript.sh +++ b/wasm-javascript/scripts/attach-release-assets.sh @@ -2,7 +2,7 @@ set -euo pipefail # Attach wasm-javascript build artifacts to the current GitHub Release -# Expects GITHUB_TOKEN and GITHUB_REF_NAME to be set +# Run from repo root. Expects GITHUB_TOKEN and GITHUB_REF_NAME to be set. artifact_dir="wasm-javascript/pkg" diff --git a/scripts/attach-wasm-wasi.sh b/wasm-wasi/scripts/attach-release-assets.sh similarity index 93% rename from scripts/attach-wasm-wasi.sh rename to wasm-wasi/scripts/attach-release-assets.sh index b2819b4..b7c5b34 100755 --- a/scripts/attach-wasm-wasi.sh +++ b/wasm-wasi/scripts/attach-release-assets.sh @@ -2,7 +2,7 @@ set -euo pipefail # Attach wasm-wasi + WIT artifacts to the current GitHub Release -# Expects GITHUB_TOKEN and GITHUB_REF_NAME to be set +# Run from repo root. Expects GITHUB_TOKEN and GITHUB_REF_NAME to be set. wasm_dir="wasm-wasi/target/wasm32-wasip2/release" wit_dir="wasm-wasi/wit" From f63fb3d451917bb280062b666952ec8a2604f9f8 Mon Sep 17 00:00:00 2001 From: Tom Davidson Date: Mon, 16 Mar 2026 21:41:03 +0000 Subject: [PATCH 03/16] ci: add reusable Slasher setup composite action --- .github/actions/setup/action.yml | 48 ++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/actions/setup/action.yml diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 0000000..00512f2 --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,48 @@ +name: "Slasher Setup" +description: "Checkout, setup Moon/proto (lazy install), restore project caches" + +inputs: + fetch-depth: + default: "0" + cache-cargo: + default: "false" + cache-pnpm: + default: "false" + +runs: + using: composite + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: ${{ inputs.fetch-depth }} + + - uses: moonrepo/setup-toolchain@v0 + with: + auto-install: true + # proto cache is handled internally by this action + + - uses: actions/cache@v4 + with: + path: .moon/cache + key: moon-${{ runner.os }}-${{ hashFiles('.moon/workspace.yml', '.moon/tasks/*.yml', '.moon/toolchains.yml') }} + restore-keys: | + moon-${{ runner.os }}- + + - uses: actions/cache@v4 + if: inputs.cache-cargo == 'true' + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + cargo-${{ runner.os }}- + + - uses: actions/cache@v4 + if: inputs.cache-pnpm == 'true' + with: + path: ~/.local/share/pnpm/store + key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + pnpm-${{ runner.os }}- From 317181fdb7d4a03b2050dba0704c776a2642d14b Mon Sep 17 00:00:00 2001 From: Tom Davidson Date: Mon, 16 Mar 2026 21:44:56 +0000 Subject: [PATCH 04/16] ci: add reusable moon-ci composite action --- .github/actions/moon-ci/action.yml | 42 ++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/actions/moon-ci/action.yml diff --git a/.github/actions/moon-ci/action.yml b/.github/actions/moon-ci/action.yml new file mode 100644 index 0000000..49e5207 --- /dev/null +++ b/.github/actions/moon-ci/action.yml @@ -0,0 +1,42 @@ +# .github/actions/moon-ci/action.yml +# +# Usage: +# - uses: ./.github/actions/moon-ci +# with: +# job-index: ${{ matrix.index }} +# job-total: 2 +# access-token: ${{ secrets.GITHUB_TOKEN }} +# matrix-json: ${{ toJSON(matrix) }} + +name: "Moon CI" +description: "Run moon ci with reporting and retrospect" + +inputs: + job-index: + required: true + description: "Matrix job index" + job-total: + required: true + default: "2" + access-token: + required: true + description: "GITHUB_TOKEN for run report" + matrix-json: + required: true + description: "toJSON(matrix) for run report" + +runs: + using: composite + steps: + - name: Moon CI + shell: bash + run: moon ci --color --job ${{ inputs.job-index }} --job-total ${{ inputs.job-total }} + + - uses: moonrepo/run-report-action@v1 + if: success() || failure() + with: + access-token: ${{ inputs.access-token }} + matrix: ${{ inputs.matrix-json }} + + - uses: appthrust/moon-ci-retrospect@v1 + if: success() || failure() \ No newline at end of file From 8cf183bc8dab9abe1e857fbd71bfc32c6c2ee237 Mon Sep 17 00:00:00 2001 From: Tom Davidson Date: Mon, 16 Mar 2026 21:56:42 +0000 Subject: [PATCH 05/16] ci: add reusable moon-run composite action --- .github/actions/moon-run/action.yml | 53 +++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/actions/moon-run/action.yml diff --git a/.github/actions/moon-run/action.yml b/.github/actions/moon-run/action.yml new file mode 100644 index 0000000..42ea8bc --- /dev/null +++ b/.github/actions/moon-run/action.yml @@ -0,0 +1,53 @@ +# .github/actions/moon-run/action.yml +# +# Usage (affected against remote): +# - uses: ./.github/actions/moon-run +# with: +# task: ":lint" +# affected: "remote" +# +# Usage (affected against default branch): +# - uses: ./.github/actions/moon-run +# with: +# task: ":semgrep" +# affected: "true" +# +# Usage (no affected, run exactly this task): +# - uses: ./.github/actions/moon-run +# with: +# task: "slash-rust:publish" +# +# Usage (multiple tasks): +# - uses: ./.github/actions/moon-run +# with: +# task: ":lint :test :fmt-check" +# affected: "remote" + +name: "Moon Run" +description: "Run a moon task with standard defaults and retrospect" + +inputs: + task: + required: true + description: "Moon task(s) to run (e.g., ':lint' or 'riff-cli:publish')" + affected: + required: false + default: "" + description: "'remote' = compare against remote tracking branch, 'true' = compare against default branch, '' = no affected filtering" + +runs: + using: composite + steps: + - name: Moon Run + shell: bash + run: | + affected_flag="" + if [ "${{ inputs.affected }}" = "remote" ]; then + affected_flag="--affected remote" + elif [ "${{ inputs.affected }}" = "true" ]; then + affected_flag="--affected" + fi + moon run --color ${{ inputs.task }} $affected_flag + + - uses: appthrust/moon-ci-retrospect@v1 + if: success() || failure() From 78251a1fb0b08e16e9a7c77bc5ccfa4b001c17d9 Mon Sep 17 00:00:00 2001 From: Tom Davidson Date: Mon, 16 Mar 2026 22:09:16 +0000 Subject: [PATCH 06/16] ci: add reusable validate-pr-title composite action --- .github/actions/validate-pr-title/action.yml | 62 ++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/actions/validate-pr-title/action.yml diff --git a/.github/actions/validate-pr-title/action.yml b/.github/actions/validate-pr-title/action.yml new file mode 100644 index 0000000..4a579e9 --- /dev/null +++ b/.github/actions/validate-pr-title/action.yml @@ -0,0 +1,62 @@ +# .github/actions/validate-pr-title/action.yml +# +# Usage: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/validate-pr-title +# with: +# github-token: ${{ secrets.GITHUB_TOKEN }} +# +# Usage (with extra scopes): +# - uses: ./.github/actions/validate-pr-title +# with: +# github-token: ${{ secrets.GITHUB_TOKEN }} +# extra-scopes: "infra,release" +# +# Scopes are auto-discovered from .moon/workspace.yml projects list. +# deps, ci, repo are always included. + +name: "Validate PR Title" +description: "Enforce conventional commit format on PR titles with auto-discovered scopes" + +inputs: + github-token: + required: true + description: "GITHUB_TOKEN" + extra-scopes: + required: false + default: "" + description: "Comma-separated additional scopes beyond project names and builtins" + +runs: + using: composite + steps: + - name: Discover project scopes + id: scopes + shell: bash + run: | + projects=$(yq '.workspace.projects // .projects | + if type == "!!seq" then .[] else keys[] end' .moon/workspace.yml 2>/dev/null | sort -u) + + builtins="deps,ci,repo" + extra="${{ inputs.extra-scopes }}" + combined="${builtins}${extra:+,$extra}" + + all_scopes=$(printf "%s\n%s" "$projects" "$(echo "$combined" | tr ',' '\n')" | sort -u) + + echo "scopes<> "$GITHUB_OUTPUT" + echo "$all_scopes" >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ inputs.github-token }} + with: + types: | + feat + fix + chore + docs + refactor + test + perf + scopes: ${{ steps.scopes.outputs.scopes }} From 56cd79569fc8f0298cfeca73be90946c39e042e1 Mon Sep 17 00:00:00 2001 From: Tom Davidson Date: Mon, 16 Mar 2026 23:51:10 +0000 Subject: [PATCH 07/16] ci: overhaul GitHub Actions with composite actions and DRY workflows Replace individual workflow setup with reusable composite actions: - setup: checkout, toolchain, selective caching - moon-ci: sharded CI with reporting and retrospect - moon-run: generic task runner with affected detection - validate-pr-title: auto-discovered conventional commit scopes - raa-open/close/teardown: review app deployment lifecycle Replace workflows with DRY architecture: - pr.yml: unified PR workflow (validate-title + ci + security + codeql + teardown) - main.yml: push-to-main (ci + security + codeql + release-please) - publish-to-registry.yml: reusable publish workflow - Thin per-project publish-*.yml calling the reusable workflow - review.yml: workflow_dispatch with RAA actions - fuzz.yml: thin dispatch delegating to parser:fuzz Delete replaced workflows: ci.yml, pr-title.yml, release-please.yml --- .github/actions/moon-ci/action.yml | 4 +- .github/actions/moon-run/action.yml | 8 +- .github/actions/raa-close/action.yml | 64 +++++++++++ .github/actions/raa-open/action.yml | 87 +++++++++++++++ .github/actions/raa-teardown/action.yml | 104 ++++++++++++++++++ .github/actions/setup/action.yml | 17 ++- .github/actions/validate-pr-title/action.yml | 2 +- .github/workflows/ci.yml | 82 -------------- .github/workflows/fuzz.yml | 15 +-- .github/workflows/main.yml | 66 +++++++++++ .github/workflows/pr-title.yml | 27 ----- .github/workflows/pr.yml | 82 ++++++++++++++ .github/workflows/publish-crates.yml | 33 ++---- .github/workflows/publish-go.yml | 19 +--- .../publish-npm-slash-javascript.yml | 37 ++----- .github/workflows/publish-npm-slash-web.yml | 37 ++----- .github/workflows/publish-pypi.yml | 34 ++---- .github/workflows/publish-riff.yml | 41 +------ .github/workflows/publish-to-registry.yml | 62 +++++++++++ .github/workflows/publish-wasm.yml | 27 ++--- .github/workflows/release-please.yml | 18 --- .github/workflows/review.yml | 81 ++++++-------- 22 files changed, 572 insertions(+), 375 deletions(-) create mode 100644 .github/actions/raa-close/action.yml create mode 100644 .github/actions/raa-open/action.yml create mode 100644 .github/actions/raa-teardown/action.yml delete mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/main.yml delete mode 100644 .github/workflows/pr-title.yml create mode 100644 .github/workflows/pr.yml create mode 100644 .github/workflows/publish-to-registry.yml delete mode 100644 .github/workflows/release-please.yml diff --git a/.github/actions/moon-ci/action.yml b/.github/actions/moon-ci/action.yml index 49e5207..1a14f93 100644 --- a/.github/actions/moon-ci/action.yml +++ b/.github/actions/moon-ci/action.yml @@ -9,7 +9,7 @@ # matrix-json: ${{ toJSON(matrix) }} name: "Moon CI" -description: "Run moon ci with reporting and retrospect" +description: "Run moon ci with sharding, reporting, and retrospect" inputs: job-index: @@ -39,4 +39,4 @@ runs: matrix: ${{ inputs.matrix-json }} - uses: appthrust/moon-ci-retrospect@v1 - if: success() || failure() \ No newline at end of file + if: success() || failure() diff --git a/.github/actions/moon-run/action.yml b/.github/actions/moon-run/action.yml index 42ea8bc..c4412bc 100644 --- a/.github/actions/moon-run/action.yml +++ b/.github/actions/moon-run/action.yml @@ -6,13 +6,7 @@ # task: ":lint" # affected: "remote" # -# Usage (affected against default branch): -# - uses: ./.github/actions/moon-run -# with: -# task: ":semgrep" -# affected: "true" -# -# Usage (no affected, run exactly this task): +# Usage (no affected, exact task): # - uses: ./.github/actions/moon-run # with: # task: "slash-rust:publish" diff --git a/.github/actions/raa-close/action.yml b/.github/actions/raa-close/action.yml new file mode 100644 index 0000000..8e9a70f --- /dev/null +++ b/.github/actions/raa-close/action.yml @@ -0,0 +1,64 @@ +# .github/actions/raa-close/action.yml +# +# Review Apps & Artifacts - Close +# +# Usage (success): +# - uses: ./.github/actions/raa-close +# if: always() +# with: +# pr-number: "42" +# deployment-id: ${{ steps.raa.outputs.deployment-id }} +# status: ${{ job.status == 'success' && 'success' || 'failure' }} +# github-token: ${{ secrets.GITHUB_TOKEN }} +# +# Usage (app with preview URL): +# - uses: ./.github/actions/raa-close +# with: +# pr-number: "42" +# deployment-id: ${{ steps.raa.outputs.deployment-id }} +# status: success +# environment-url: "https://preview-42.example.com" +# github-token: ${{ secrets.GITHUB_TOKEN }} + +name: "RAA Close" +description: "Finalize review deployment status" + +inputs: + pr-number: + required: true + description: "PR number" + deployment-id: + required: true + description: "Deployment ID from raa-open" + status: + required: true + description: "'success' or 'failure'" + environment-url: + required: false + default: "" + description: "URL for deployed app (shown in GitHub Deployments UI)" + github-token: + required: true + description: "GITHUB_TOKEN" + +runs: + using: composite + steps: + - name: Update deployment status + uses: actions/github-script@v7 + with: + github-token: ${{ inputs.github-token }} + script: | + const opts = { + owner: context.repo.owner, + repo: context.repo.repo, + deployment_id: ${{ inputs.deployment-id }}, + state: '${{ inputs.status }}', + }; + const url = '${{ inputs.environment-url }}'; + if (url) { + opts.environment_url = url; + } else { + opts.environment_url = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + } + await github.rest.repos.createDeploymentStatus(opts); diff --git a/.github/actions/raa-open/action.yml b/.github/actions/raa-open/action.yml new file mode 100644 index 0000000..558ba0a --- /dev/null +++ b/.github/actions/raa-open/action.yml @@ -0,0 +1,87 @@ +# .github/actions/raa-open/action.yml +# +# Review Apps & Artifacts - Open +# +# Usage: +# - uses: ./.github/actions/raa-open +# id: raa +# with: +# pr-number: "42" +# type: "artifacts" +# github-token: ${{ secrets.GITHUB_TOKEN }} +# +# Usage (deployable app with teardown metadata): +# - uses: ./.github/actions/raa-open +# id: raa +# with: +# pr-number: "42" +# type: "app" +# teardown-metadata: '{"provider":"cloudflare","project_id":"xyz"}' +# github-token: ${{ secrets.GITHUB_TOKEN }} +# +# Outputs: +# deployment-id: ID of the created deployment +# environment: Name of the environment (review-pr-42) + +name: "RAA Open" +description: "Create review deployment and environment" + +inputs: + pr-number: + required: true + description: "PR number" + type: + required: false + default: "artifacts" + description: "'artifacts' or 'app'" + teardown-metadata: + required: false + default: "{}" + description: "JSON metadata stored in deployment payload, read by raa-teardown" + github-token: + required: true + description: "GITHUB_TOKEN" + +outputs: + deployment-id: + description: "Deployment ID" + value: ${{ steps.deploy.outputs.deployment-id }} + environment: + description: "Environment name" + value: ${{ steps.deploy.outputs.environment }} + +runs: + using: composite + steps: + - name: Create deployment + id: deploy + uses: actions/github-script@v7 + with: + github-token: ${{ inputs.github-token }} + script: | + const envName = `review-pr-${{ inputs.pr-number }}`; + const payload = { + type: '${{ inputs.type }}', + pr_number: '${{ inputs.pr-number }}', + artifact_name: `review-pr-${{ inputs.pr-number }}`, + teardown: ${{ inputs.teardown-metadata }}, + }; + const deployment = await github.rest.repos.createDeployment({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: context.sha, + environment: envName, + auto_merge: false, + required_contexts: [], + description: `Review for PR #${{ inputs.pr-number }}`, + payload: JSON.stringify(payload), + }); + await github.rest.repos.createDeploymentStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + deployment_id: deployment.data.id, + state: 'in_progress', + description: 'Building review artifacts...', + }); + core.setOutput('deployment-id', deployment.data.id); + core.setOutput('environment', envName); diff --git a/.github/actions/raa-teardown/action.yml b/.github/actions/raa-teardown/action.yml new file mode 100644 index 0000000..e39b8ae --- /dev/null +++ b/.github/actions/raa-teardown/action.yml @@ -0,0 +1,104 @@ +# .github/actions/raa-teardown/action.yml +# +# Review Apps & Artifacts - Teardown +# +# Usage (in pr.yml on close): +# - uses: ./.github/actions/raa-teardown +# with: +# pr-number: ${{ github.event.pull_request.number }} +# github-token: ${{ secrets.GITHUB_TOKEN }} +# +# No checkout required. Pure API calls. +# Reads teardown metadata from deployment payloads. + +name: "RAA Teardown" +description: "Deactivate deployments, delete environment, clean up artifacts" + +inputs: + pr-number: + required: true + description: "PR number" + github-token: + required: true + description: "GITHUB_TOKEN" + +runs: + using: composite + steps: + - name: Teardown deployments and environment + uses: actions/github-script@v7 + with: + github-token: ${{ inputs.github-token }} + script: | + const envName = `review-pr-${{ inputs.pr-number }}`; + const owner = context.repo.owner; + const repo = context.repo.repo; + + // List all deployments for this environment + const deployments = await github.rest.repos.listDeployments({ + owner, repo, environment: envName, + }); + + for (const dep of deployments.data) { + // Read payload for any app-specific teardown info + let payload = {}; + try { + payload = JSON.parse(dep.payload || '{}'); + } catch (e) { + core.warning(`Could not parse deployment payload: ${e.message}`); + } + + // Log teardown metadata for debugging + if (payload.teardown && Object.keys(payload.teardown).length > 0) { + core.info(`Deployment ${dep.id} teardown metadata: ${JSON.stringify(payload.teardown)}`); + // Future: use payload.teardown to call provider APIs + // e.g., delete Cloudflare preview, destroy k8s namespace, etc. + } + + // Mark deployment inactive + await github.rest.repos.createDeploymentStatus({ + owner, repo, + deployment_id: dep.id, + state: 'inactive', + }); + + // Delete deployment + await github.rest.repos.deleteDeployment({ + owner, repo, + deployment_id: dep.id, + }); + core.info(`Deleted deployment ${dep.id}`); + } + + // Delete the environment + try { + await github.rest.repos.deleteAnEnvironment({ + owner, repo, + environment_name: envName, + }); + core.info(`Deleted environment ${envName}`); + } catch (e) { + core.warning(`Could not delete environment ${envName}: ${e.message}`); + } + + - name: Delete workflow artifacts + uses: actions/github-script@v7 + with: + github-token: ${{ inputs.github-token }} + script: | + const artifactName = `review-pr-${{ inputs.pr-number }}`; + const owner = context.repo.owner; + const repo = context.repo.repo; + + const artifacts = await github.rest.actions.listArtifactsForRepo({ + owner, repo, + }); + for (const art of artifacts.data.artifacts) { + if (art.name === artifactName && !art.expired) { + await github.rest.actions.deleteArtifact({ + owner, repo, + artifact_id: art.id, + }); + core.info(`Deleted artifact ${art.name} (${art.id})`); + } + } diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 00512f2..70fb22e 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -1,13 +1,27 @@ +# .github/actions/setup/action.yml +# +# Usage: +# - uses: ./.github/actions/setup +# with: +# cache-cargo: "true" +# cache-pnpm: "true" +# +# Usage (minimal, no project caches): +# - uses: ./.github/actions/setup + name: "Slasher Setup" -description: "Checkout, setup Moon/proto (lazy install), restore project caches" +description: "Checkout, setup Moon/proto (lazy install), restore caches" inputs: fetch-depth: default: "0" + description: "Git fetch depth (0 for full history, needed for moon ci affected detection)" cache-cargo: default: "false" + description: "Restore Cargo registry, git, and target caches" cache-pnpm: default: "false" + description: "Restore pnpm store cache" runs: using: composite @@ -19,7 +33,6 @@ runs: - uses: moonrepo/setup-toolchain@v0 with: auto-install: true - # proto cache is handled internally by this action - uses: actions/cache@v4 with: diff --git a/.github/actions/validate-pr-title/action.yml b/.github/actions/validate-pr-title/action.yml index 4a579e9..1b24ae7 100644 --- a/.github/actions/validate-pr-title/action.yml +++ b/.github/actions/validate-pr-title/action.yml @@ -12,7 +12,7 @@ # github-token: ${{ secrets.GITHUB_TOKEN }} # extra-scopes: "infra,release" # -# Scopes are auto-discovered from .moon/workspace.yml projects list. +# Scopes auto-discovered from .moon/workspace.yml projects list. # deps, ci, repo are always included. name: "Validate PR Title" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 9b90a4d..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: CI - -on: - push: - branches: [main] - pull_request: - -concurrency: - group: ci-${{ github.ref }} - cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} - -jobs: - ci: - name: Moon CI - runs-on: ubuntu-latest - strategy: - matrix: - index: [0, 1] - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: moonrepo/setup-toolchain@v0 - with: - auto-install: true - - - uses: actions/cache@v4 - with: - path: | - .moon/cache - key: moon-${{ runner.os }}-${{ hashFiles('.moon/workspace.yml', '.moon/tasks/*.yml', '.moon/toolchains.yml') }} - restore-keys: | - moon-${{ runner.os }}- - - - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - cargo-${{ runner.os }}- - - - uses: actions/cache@v4 - with: - path: | - ~/.local/share/pnpm/store - key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - pnpm-${{ runner.os }}- - - - name: Moon CI - run: moon ci --color --job ${{ matrix.index }} --job-total 2 - - - uses: moonrepo/run-report-action@v1 - if: success() || failure() - with: - access-token: ${{ secrets.GITHUB_TOKEN }} - matrix: ${{ toJSON(matrix) }} - - - uses: appthrust/moon-ci-retrospect@v1 - if: success() || failure() - - codeql: - name: CodeQL - runs-on: ubuntu-latest - permissions: - security-events: write - actions: read - contents: read - steps: - - uses: actions/checkout@v4 - - - uses: github/codeql-action/init@v3 - with: - languages: javascript-typescript - - - uses: github/codeql-action/autobuild@v3 - - - uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 5bf47b3..f86194e 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -1,3 +1,4 @@ +# .github/workflows/fuzz.yml name: Fuzz on: @@ -8,19 +9,13 @@ jobs: name: Fuzz Parser runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: ./.github/actions/setup with: - fetch-depth: 0 + cache-cargo: "true" - - uses: moonrepo/setup-toolchain@v0 + - uses: ./.github/actions/moon-run with: - auto-install: true - - - name: Fuzz parser - run: moon run parser:fuzz - - - uses: appthrust/moon-ci-retrospect@v1 - if: success() || failure() + task: "parser:fuzz" - name: Upload crash artifacts if: failure() diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..ef91deb --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,66 @@ +# .github/workflows/main.yml +name: Main + +on: + push: + branches: [main] + +concurrency: + group: main-${{ github.sha }} + +jobs: + ci: + runs-on: ubuntu-latest + strategy: + matrix: + index: [0, 1] + steps: + - uses: ./.github/actions/setup + with: + cache-cargo: "true" + cache-pnpm: "true" + + - uses: ./.github/actions/moon-ci + with: + job-index: ${{ matrix.index }} + job-total: 2 + access-token: ${{ secrets.GITHUB_TOKEN }} + matrix-json: ${{ toJSON(matrix) }} + + security: + runs-on: ubuntu-latest + needs: ci + steps: + - uses: ./.github/actions/setup + with: + cache-cargo: "true" + cache-pnpm: "true" + + - uses: ./.github/actions/moon-run + with: + task: ":rust-security :npm-security :python-security :go-security :gitleaks" + + codeql: + runs-on: ubuntu-latest + permissions: + security-events: write + actions: read + contents: read + steps: + - uses: actions/checkout@v4 + - uses: github/codeql-action/init@v3 + with: + languages: rust,javascript,python,go + - uses: github/codeql-action/analyze@v3 + + release-please: + needs: [ci, security, codeql] + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: googleapis/release-please-action@v4 + with: + command: manifest + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml deleted file mode 100644 index 96ed416..0000000 --- a/.github/workflows/pr-title.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: PR Title - -on: - pull_request: - types: [opened, edited, synchronize, reopened] - -jobs: - lint-pr-title: - name: Validate PR Title - runs-on: ubuntu-latest - steps: - - uses: amannn/action-semantic-pull-request@v5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - types: | - feat - fix - chore - docs - refactor - test - perf - ci - build - revert - requireScope: false diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..a0ab946 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,82 @@ +# .github/workflows/pr.yml +name: PR + +on: + pull_request: + types: [opened, edited, synchronize, reopened, closed] + +concurrency: + group: pr-${{ github.event.pull_request.number }} + cancel-in-progress: ${{ github.event.action != 'closed' }} + +jobs: + validate-title: + if: github.event.action != 'closed' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + sparse-checkout: .moon/workspace.yml + - uses: ./.github/actions/validate-pr-title + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + ci: + if: github.event.action != 'closed' + runs-on: ubuntu-latest + strategy: + matrix: + index: [0, 1] + steps: + - uses: ./.github/actions/setup + with: + cache-cargo: "true" + cache-pnpm: "true" + + - uses: ./.github/actions/moon-ci + with: + job-index: ${{ matrix.index }} + job-total: 2 + access-token: ${{ secrets.GITHUB_TOKEN }} + matrix-json: ${{ toJSON(matrix) }} + + security: + if: github.event.action != 'closed' + runs-on: ubuntu-latest + needs: ci + steps: + - uses: ./.github/actions/setup + with: + cache-cargo: "true" + cache-pnpm: "true" + + - uses: ./.github/actions/moon-run + with: + task: ":rust-security :npm-security :python-security :go-security :gitleaks" + affected: "remote" + + codeql: + if: github.event.action != 'closed' + runs-on: ubuntu-latest + permissions: + security-events: write + actions: read + contents: read + steps: + - uses: actions/checkout@v4 + - uses: github/codeql-action/init@v3 + with: + languages: rust,javascript,python,go + - uses: github/codeql-action/analyze@v3 + + review-teardown: + if: github.event.action == 'closed' + runs-on: ubuntu-latest + permissions: + deployments: write + actions: write + steps: + - uses: ./.github/actions/raa-teardown + with: + pr-number: ${{ github.event.pull_request.number }} + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-crates.yml b/.github/workflows/publish-crates.yml index 1dca0b2..2934cd9 100644 --- a/.github/workflows/publish-crates.yml +++ b/.github/workflows/publish-crates.yml @@ -1,32 +1,15 @@ -# NOTE: Hand-authored. TODO: Generate from Moon Tera templates (.moon/templates/publish-workflows/) -name: Publish slash-rust to crates.io +# .github/workflows/publish-crates.yml +name: Publish crates on: release: types: [published] - workflow_dispatch: - inputs: - tag: - description: "Release tag (e.g., slash-rust-v1.0.0)" - required: true jobs: publish: - if: >- - (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'slash-rust-v')) - || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - environment: publish - permissions: - contents: read - steps: - - uses: actions/checkout@v4 - - - uses: moonrepo/setup-toolchain@v0 - with: - auto-install: true - - - name: Publish to crates.io - run: moon run slash-rust:publish - env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + uses: ./.github/workflows/publish-to-registry.yml + with: + tag-prefix: "slash-rust-v" + moon-task: "slash-rust:publish" + cache-cargo: true + secrets: inherit diff --git a/.github/workflows/publish-go.yml b/.github/workflows/publish-go.yml index d4a4fb5..da2468c 100644 --- a/.github/workflows/publish-go.yml +++ b/.github/workflows/publish-go.yml @@ -1,4 +1,4 @@ -# NOTE: Hand-authored. TODO: Generate from Moon Tera templates (.moon/templates/publish-workflows/) +# .github/workflows/publish-go.yml name: Validate slash-go on: @@ -7,15 +7,8 @@ on: jobs: validate: - if: startsWith(github.event.release.tag_name, 'slash-go/') - runs-on: ubuntu-latest - environment: publish - steps: - - uses: actions/checkout@v4 - - - uses: moonrepo/setup-toolchain@v0 - with: - auto-install: true - - - name: Validate Go module - run: moon run slash-go:build + uses: ./.github/workflows/publish-to-registry.yml + with: + tag-prefix: "slash-go/" + moon-task: "slash-go:test" + secrets: inherit diff --git a/.github/workflows/publish-npm-slash-javascript.yml b/.github/workflows/publish-npm-slash-javascript.yml index 62c453f..57b7e93 100644 --- a/.github/workflows/publish-npm-slash-javascript.yml +++ b/.github/workflows/publish-npm-slash-javascript.yml @@ -1,37 +1,16 @@ -# NOTE: Hand-authored. TODO: Generate from Moon Tera templates (.moon/templates/publish-workflows/) +# .github/workflows/publish-npm-slash-javascript.yml name: Publish @slasher/slash-javascript on: release: types: [published] - workflow_dispatch: - inputs: - tag: - description: "Release tag (e.g., slash-javascript-v1.0.0)" - required: true jobs: publish: - if: >- - (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'slash-javascript-v')) - || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - environment: publish - permissions: - contents: write - id-token: write - steps: - - uses: actions/checkout@v4 - - - uses: moonrepo/setup-toolchain@v0 - with: - auto-install: true - - - name: Build - run: moon run slash-javascript:build - - - name: Publish to npm - run: moon run slash-javascript:publish - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + uses: ./.github/workflows/publish-to-registry.yml + with: + tag-prefix: "slash-javascript-v" + moon-task: "slash-javascript:publish" + cache-pnpm: true + needs-oidc: true + secrets: inherit diff --git a/.github/workflows/publish-npm-slash-web.yml b/.github/workflows/publish-npm-slash-web.yml index 55e48a6..6684c55 100644 --- a/.github/workflows/publish-npm-slash-web.yml +++ b/.github/workflows/publish-npm-slash-web.yml @@ -1,37 +1,16 @@ -# NOTE: Hand-authored. TODO: Generate from Moon Tera templates (.moon/templates/publish-workflows/) +# .github/workflows/publish-npm-slash-web.yml name: Publish @slasher/slash-web on: release: types: [published] - workflow_dispatch: - inputs: - tag: - description: "Release tag (e.g., slash-web-v1.0.0)" - required: true jobs: publish: - if: >- - (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'slash-web-v')) - || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - environment: publish - permissions: - contents: write - id-token: write - steps: - - uses: actions/checkout@v4 - - - uses: moonrepo/setup-toolchain@v0 - with: - auto-install: true - - - name: Build - run: moon run slash-web:build - - - name: Publish to npm - run: moon run slash-web:publish - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + uses: ./.github/workflows/publish-to-registry.yml + with: + tag-prefix: "slash-web-v" + moon-task: "slash-web:publish" + cache-pnpm: true + needs-oidc: true + secrets: inherit diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 9e4e478..42cd10c 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -1,34 +1,14 @@ -# NOTE: Hand-authored. TODO: Generate from Moon Tera templates (.moon/templates/publish-workflows/) -name: Publish slash-python to PyPI +# .github/workflows/publish-pypi.yml +name: Publish slash-python on: release: types: [published] - workflow_dispatch: - inputs: - tag: - description: "Release tag (e.g., slash-python-v1.0.0)" - required: true jobs: publish: - if: >- - (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'slash-python-v')) - || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - environment: publish - steps: - - uses: actions/checkout@v4 - - - uses: moonrepo/setup-toolchain@v0 - with: - auto-install: true - - - name: Build - run: moon run slash-python:build - - - name: Publish to PyPI - run: moon run slash-python:publish - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + uses: ./.github/workflows/publish-to-registry.yml + with: + tag-prefix: "slash-python-v" + moon-task: "slash-python:publish" + secrets: inherit diff --git a/.github/workflows/publish-riff.yml b/.github/workflows/publish-riff.yml index f6a94bc..8628474 100644 --- a/.github/workflows/publish-riff.yml +++ b/.github/workflows/publish-riff.yml @@ -1,54 +1,25 @@ -# NOTE: Hand-authored. TODO: Generate from Moon Tera templates (.moon/templates/publish-workflows/) +# .github/workflows/publish-riff.yml name: Publish riff on: release: types: [published] - workflow_dispatch: - inputs: - tag: - description: "Release tag (e.g., riff-v1.0.0)" - required: true jobs: publish: - if: >- - (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'riff-v')) - || github.event_name == 'workflow_dispatch' + if: startsWith(github.event.release.tag_name, 'riff-cli-v') runs-on: ubuntu-latest environment: publish permissions: contents: write packages: write - id-token: write steps: - - uses: actions/checkout@v4 + - uses: ./.github/actions/setup with: - fetch-depth: 0 + cache-cargo: "true" - - uses: moonrepo/setup-toolchain@v0 - with: - auto-install: true - - - uses: mlugg/setup-zig@v1 - - - name: Install cargo-zigbuild - run: cargo install --locked cargo-zigbuild - - - uses: docker/setup-qemu-action@v3 - - uses: docker/setup-buildx-action@v3 - - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - uses: goreleaser/goreleaser-action@v6 - with: - distribution: goreleaser - version: "~> v2" - args: release --clean --config riff-cli/.goreleaser.yaml + - name: Run riff publish (GoReleaser) + run: moon run riff-cli:publish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} diff --git a/.github/workflows/publish-to-registry.yml b/.github/workflows/publish-to-registry.yml new file mode 100644 index 0000000..144174d --- /dev/null +++ b/.github/workflows/publish-to-registry.yml @@ -0,0 +1,62 @@ +# .github/workflows/publish-to-registry.yml +# +# Reusable workflow for publishing a project to its registry. +# Called by thin per-project publish-*.yml workflows. +# +# Usage: +# jobs: +# publish: +# uses: ./.github/workflows/publish-to-registry.yml +# with: +# tag-prefix: "slash-javascript-v" +# moon-task: "slash-javascript:publish" +# cache-cargo: true +# needs-oidc: true +# secrets: inherit + +name: Publish to Registry (reusable) + +on: + workflow_call: + inputs: + tag-prefix: + required: true + type: string + description: "Tag prefix to match (e.g., 'slash-javascript-v')" + moon-task: + required: true + type: string + description: "Moon task to run (e.g., 'slash-javascript:publish')" + cache-cargo: + required: false + type: boolean + default: false + cache-pnpm: + required: false + type: boolean + default: false + needs-oidc: + required: false + type: boolean + default: false + description: "Request id-token permission for OIDC trusted publishing" + +jobs: + publish: + if: startsWith(github.event.release.tag_name, inputs.tag-prefix) + runs-on: ubuntu-latest + environment: publish + permissions: + contents: write + packages: write + id-token: ${{ inputs.needs-oidc && 'write' || 'none' }} + + steps: + - uses: ./.github/actions/setup + with: + cache-cargo: ${{ inputs.cache-cargo && 'true' || 'false' }} + cache-pnpm: ${{ inputs.cache-pnpm && 'true' || 'false' }} + + - uses: ./.github/actions/moon-run + with: + task: ${{ inputs.moon-task }} diff --git a/.github/workflows/publish-wasm.yml b/.github/workflows/publish-wasm.yml index c316a43..bd8adaa 100644 --- a/.github/workflows/publish-wasm.yml +++ b/.github/workflows/publish-wasm.yml @@ -1,30 +1,21 @@ -# NOTE: Hand-authored. TODO: Generate from Moon Tera templates (.moon/templates/publish-workflows/) +# .github/workflows/publish-wasm.yml name: Publish WASM + WIT on: release: types: [published] - workflow_dispatch: - inputs: - tag: - description: "Release tag (e.g., wasm-javascript-v1.0.0 or wasm-wasi-v1.0.0)" - required: true jobs: publish-wasm-javascript: - if: >- - (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'wasm-javascript-v')) - || (github.event_name == 'workflow_dispatch' && startsWith(github.event.inputs.tag, 'wasm-javascript-v')) + if: startsWith(github.event.release.tag_name, 'wasm-javascript-v') runs-on: ubuntu-latest environment: publish permissions: contents: write steps: - - uses: actions/checkout@v4 - - - uses: moonrepo/setup-toolchain@v0 + - uses: ./.github/actions/setup with: - auto-install: true + cache-cargo: "true" - name: Build wasm-javascript run: moon run wasm-javascript:build @@ -35,19 +26,15 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} publish-wasm-wasi: - if: >- - (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'wasm-wasi-v')) - || (github.event_name == 'workflow_dispatch' && startsWith(github.event.inputs.tag, 'wasm-wasi-v')) + if: startsWith(github.event.release.tag_name, 'wasm-wasi-v') runs-on: ubuntu-latest environment: publish permissions: contents: write steps: - - uses: actions/checkout@v4 - - - uses: moonrepo/setup-toolchain@v0 + - uses: ./.github/actions/setup with: - auto-install: true + cache-cargo: "true" - name: Build wasm-wasi + WIT run: moon run wasm-wasi:build diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml deleted file mode 100644 index 785f956..0000000 --- a/.github/workflows/release-please.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Release Please - -on: - push: - branches: [main] - -permissions: - contents: write - pull-requests: write - -jobs: - release-please: - runs-on: ubuntu-latest - steps: - - uses: googleapis/release-please-action@v4 - id: release - with: - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml index 818b3dc..e0b7a09 100644 --- a/.github/workflows/review.yml +++ b/.github/workflows/review.yml @@ -1,79 +1,64 @@ -name: Review Artifacts +# .github/workflows/review.yml +name: Review on: workflow_dispatch: inputs: - pr_number: + pr-number: description: "PR number to build review artifacts for" required: true - pull_request: - types: [closed] +permissions: + deployments: write + actions: write + contents: read jobs: - build-review-artifacts: - if: github.event_name == 'workflow_dispatch' + review: runs-on: ubuntu-latest - environment: review-pr-${{ github.event.inputs.pr_number }} steps: - uses: actions/checkout@v4 with: - ref: refs/pull/${{ github.event.inputs.pr_number }}/head + ref: refs/pull/${{ github.event.inputs.pr-number }}/head + fetch-depth: 0 - - uses: moonrepo/setup-toolchain@v0 + - uses: ./.github/actions/setup with: - auto-install: true + cache-cargo: "true" + cache-pnpm: "true" - - uses: mlugg/setup-zig@v1 - - - name: Install cargo-zigbuild - run: cargo install --locked cargo-zigbuild - - - name: Install GoReleaser - uses: goreleaser/goreleaser-action@v6 + - uses: ./.github/actions/raa-open + id: raa with: - distribution: goreleaser - version: "~> v2" - install-only: true + pr-number: ${{ github.event.inputs.pr-number }} + type: "artifacts" + github-token: ${{ secrets.GITHUB_TOKEN }} - name: Build riff snapshot - run: goreleaser release --config riff-cli/.goreleaser.yaml --snapshot --clean --skip=publish - continue-on-error: true + run: goreleaser release --config riff-cli/.goreleaser.yaml --snapshot --skip=publish - name: Build SDK snapshots run: | - moon run slash-rust:build || true - moon run slash-javascript:build || true - moon run slash-web:build || true - moon run slash-python:build || true - moon run wasm-javascript:build || true - moon run wasm-wasi:build || true + moon run slash-rust:build + moon run slash-javascript:build + moon run slash-web:build + moon run slash-python:build - - name: Upload review artifacts + - name: Upload artifacts uses: actions/upload-artifact@v4 with: - name: review-pr-${{ github.event.inputs.pr_number }} + name: review-pr-${{ github.event.inputs.pr-number }} path: | riff-cli/dist/** + slash-rust/target/** slash-javascript/dist/** slash-web/dist/** slash-python/dist/** - retention-days: 14 - cleanup: - if: github.event_name == 'pull_request' && github.event.action == 'closed' - runs-on: ubuntu-latest - steps: - - name: Mark review environment inactive - run: | - echo "Cleaning up review-pr-${{ github.event.pull_request.number }}" - # Mark deployment as inactive via GitHub API if one exists - DEPLOYMENTS=$(gh api repos/${{ github.repository }}/deployments \ - --jq "[.[] | select(.environment == \"review-pr-${{ github.event.pull_request.number }}\")] | .[].id" 2>/dev/null || true) - for DEPLOY_ID in $DEPLOYMENTS; do - gh api repos/${{ github.repository }}/deployments/$DEPLOY_ID/statuses \ - -f state=inactive \ - -f description="PR closed" 2>/dev/null || true - done - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: ./.github/actions/raa-close + if: always() + with: + pr-number: ${{ github.event.inputs.pr-number }} + deployment-id: ${{ steps.raa.outputs.deployment-id }} + status: ${{ job.status == 'success' && 'success' || 'failure' }} + github-token: ${{ secrets.GITHUB_TOKEN }} From eb6ade09733f7d9a0239849be060424f0975372e Mon Sep 17 00:00:00 2001 From: Tom Davidson Date: Tue, 17 Mar 2026 00:48:35 +0000 Subject: [PATCH 08/16] ci: delete all .github/ files (clean slate) --- .github/actions/moon-ci/action.yml | 42 ------- .github/actions/moon-run/action.yml | 47 -------- .github/actions/raa-close/action.yml | 64 ----------- .github/actions/raa-open/action.yml | 87 --------------- .github/actions/raa-teardown/action.yml | 104 ------------------ .github/actions/setup/action.yml | 61 ---------- .github/actions/validate-pr-title/action.yml | 62 ----------- .github/workflows/fuzz.yml | 26 ----- .github/workflows/main.yml | 66 ----------- .github/workflows/pr.yml | 82 -------------- .github/workflows/publish-crates.yml | 15 --- .github/workflows/publish-go.yml | 14 --- .../publish-npm-slash-javascript.yml | 16 --- .github/workflows/publish-npm-slash-web.yml | 16 --- .github/workflows/publish-pypi.yml | 14 --- .github/workflows/publish-riff.yml | 25 ----- .github/workflows/publish-to-registry.yml | 62 ----------- .github/workflows/publish-wasm.yml | 45 -------- .github/workflows/review.yml | 64 ----------- 19 files changed, 912 deletions(-) delete mode 100644 .github/actions/moon-ci/action.yml delete mode 100644 .github/actions/moon-run/action.yml delete mode 100644 .github/actions/raa-close/action.yml delete mode 100644 .github/actions/raa-open/action.yml delete mode 100644 .github/actions/raa-teardown/action.yml delete mode 100644 .github/actions/setup/action.yml delete mode 100644 .github/actions/validate-pr-title/action.yml delete mode 100644 .github/workflows/fuzz.yml delete mode 100644 .github/workflows/main.yml delete mode 100644 .github/workflows/pr.yml delete mode 100644 .github/workflows/publish-crates.yml delete mode 100644 .github/workflows/publish-go.yml delete mode 100644 .github/workflows/publish-npm-slash-javascript.yml delete mode 100644 .github/workflows/publish-npm-slash-web.yml delete mode 100644 .github/workflows/publish-pypi.yml delete mode 100644 .github/workflows/publish-riff.yml delete mode 100644 .github/workflows/publish-to-registry.yml delete mode 100644 .github/workflows/publish-wasm.yml delete mode 100644 .github/workflows/review.yml diff --git a/.github/actions/moon-ci/action.yml b/.github/actions/moon-ci/action.yml deleted file mode 100644 index 1a14f93..0000000 --- a/.github/actions/moon-ci/action.yml +++ /dev/null @@ -1,42 +0,0 @@ -# .github/actions/moon-ci/action.yml -# -# Usage: -# - uses: ./.github/actions/moon-ci -# with: -# job-index: ${{ matrix.index }} -# job-total: 2 -# access-token: ${{ secrets.GITHUB_TOKEN }} -# matrix-json: ${{ toJSON(matrix) }} - -name: "Moon CI" -description: "Run moon ci with sharding, reporting, and retrospect" - -inputs: - job-index: - required: true - description: "Matrix job index" - job-total: - required: true - default: "2" - access-token: - required: true - description: "GITHUB_TOKEN for run report" - matrix-json: - required: true - description: "toJSON(matrix) for run report" - -runs: - using: composite - steps: - - name: Moon CI - shell: bash - run: moon ci --color --job ${{ inputs.job-index }} --job-total ${{ inputs.job-total }} - - - uses: moonrepo/run-report-action@v1 - if: success() || failure() - with: - access-token: ${{ inputs.access-token }} - matrix: ${{ inputs.matrix-json }} - - - uses: appthrust/moon-ci-retrospect@v1 - if: success() || failure() diff --git a/.github/actions/moon-run/action.yml b/.github/actions/moon-run/action.yml deleted file mode 100644 index c4412bc..0000000 --- a/.github/actions/moon-run/action.yml +++ /dev/null @@ -1,47 +0,0 @@ -# .github/actions/moon-run/action.yml -# -# Usage (affected against remote): -# - uses: ./.github/actions/moon-run -# with: -# task: ":lint" -# affected: "remote" -# -# Usage (no affected, exact task): -# - uses: ./.github/actions/moon-run -# with: -# task: "slash-rust:publish" -# -# Usage (multiple tasks): -# - uses: ./.github/actions/moon-run -# with: -# task: ":lint :test :fmt-check" -# affected: "remote" - -name: "Moon Run" -description: "Run a moon task with standard defaults and retrospect" - -inputs: - task: - required: true - description: "Moon task(s) to run (e.g., ':lint' or 'riff-cli:publish')" - affected: - required: false - default: "" - description: "'remote' = compare against remote tracking branch, 'true' = compare against default branch, '' = no affected filtering" - -runs: - using: composite - steps: - - name: Moon Run - shell: bash - run: | - affected_flag="" - if [ "${{ inputs.affected }}" = "remote" ]; then - affected_flag="--affected remote" - elif [ "${{ inputs.affected }}" = "true" ]; then - affected_flag="--affected" - fi - moon run --color ${{ inputs.task }} $affected_flag - - - uses: appthrust/moon-ci-retrospect@v1 - if: success() || failure() diff --git a/.github/actions/raa-close/action.yml b/.github/actions/raa-close/action.yml deleted file mode 100644 index 8e9a70f..0000000 --- a/.github/actions/raa-close/action.yml +++ /dev/null @@ -1,64 +0,0 @@ -# .github/actions/raa-close/action.yml -# -# Review Apps & Artifacts - Close -# -# Usage (success): -# - uses: ./.github/actions/raa-close -# if: always() -# with: -# pr-number: "42" -# deployment-id: ${{ steps.raa.outputs.deployment-id }} -# status: ${{ job.status == 'success' && 'success' || 'failure' }} -# github-token: ${{ secrets.GITHUB_TOKEN }} -# -# Usage (app with preview URL): -# - uses: ./.github/actions/raa-close -# with: -# pr-number: "42" -# deployment-id: ${{ steps.raa.outputs.deployment-id }} -# status: success -# environment-url: "https://preview-42.example.com" -# github-token: ${{ secrets.GITHUB_TOKEN }} - -name: "RAA Close" -description: "Finalize review deployment status" - -inputs: - pr-number: - required: true - description: "PR number" - deployment-id: - required: true - description: "Deployment ID from raa-open" - status: - required: true - description: "'success' or 'failure'" - environment-url: - required: false - default: "" - description: "URL for deployed app (shown in GitHub Deployments UI)" - github-token: - required: true - description: "GITHUB_TOKEN" - -runs: - using: composite - steps: - - name: Update deployment status - uses: actions/github-script@v7 - with: - github-token: ${{ inputs.github-token }} - script: | - const opts = { - owner: context.repo.owner, - repo: context.repo.repo, - deployment_id: ${{ inputs.deployment-id }}, - state: '${{ inputs.status }}', - }; - const url = '${{ inputs.environment-url }}'; - if (url) { - opts.environment_url = url; - } else { - opts.environment_url = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; - } - await github.rest.repos.createDeploymentStatus(opts); diff --git a/.github/actions/raa-open/action.yml b/.github/actions/raa-open/action.yml deleted file mode 100644 index 558ba0a..0000000 --- a/.github/actions/raa-open/action.yml +++ /dev/null @@ -1,87 +0,0 @@ -# .github/actions/raa-open/action.yml -# -# Review Apps & Artifacts - Open -# -# Usage: -# - uses: ./.github/actions/raa-open -# id: raa -# with: -# pr-number: "42" -# type: "artifacts" -# github-token: ${{ secrets.GITHUB_TOKEN }} -# -# Usage (deployable app with teardown metadata): -# - uses: ./.github/actions/raa-open -# id: raa -# with: -# pr-number: "42" -# type: "app" -# teardown-metadata: '{"provider":"cloudflare","project_id":"xyz"}' -# github-token: ${{ secrets.GITHUB_TOKEN }} -# -# Outputs: -# deployment-id: ID of the created deployment -# environment: Name of the environment (review-pr-42) - -name: "RAA Open" -description: "Create review deployment and environment" - -inputs: - pr-number: - required: true - description: "PR number" - type: - required: false - default: "artifacts" - description: "'artifacts' or 'app'" - teardown-metadata: - required: false - default: "{}" - description: "JSON metadata stored in deployment payload, read by raa-teardown" - github-token: - required: true - description: "GITHUB_TOKEN" - -outputs: - deployment-id: - description: "Deployment ID" - value: ${{ steps.deploy.outputs.deployment-id }} - environment: - description: "Environment name" - value: ${{ steps.deploy.outputs.environment }} - -runs: - using: composite - steps: - - name: Create deployment - id: deploy - uses: actions/github-script@v7 - with: - github-token: ${{ inputs.github-token }} - script: | - const envName = `review-pr-${{ inputs.pr-number }}`; - const payload = { - type: '${{ inputs.type }}', - pr_number: '${{ inputs.pr-number }}', - artifact_name: `review-pr-${{ inputs.pr-number }}`, - teardown: ${{ inputs.teardown-metadata }}, - }; - const deployment = await github.rest.repos.createDeployment({ - owner: context.repo.owner, - repo: context.repo.repo, - ref: context.sha, - environment: envName, - auto_merge: false, - required_contexts: [], - description: `Review for PR #${{ inputs.pr-number }}`, - payload: JSON.stringify(payload), - }); - await github.rest.repos.createDeploymentStatus({ - owner: context.repo.owner, - repo: context.repo.repo, - deployment_id: deployment.data.id, - state: 'in_progress', - description: 'Building review artifacts...', - }); - core.setOutput('deployment-id', deployment.data.id); - core.setOutput('environment', envName); diff --git a/.github/actions/raa-teardown/action.yml b/.github/actions/raa-teardown/action.yml deleted file mode 100644 index e39b8ae..0000000 --- a/.github/actions/raa-teardown/action.yml +++ /dev/null @@ -1,104 +0,0 @@ -# .github/actions/raa-teardown/action.yml -# -# Review Apps & Artifacts - Teardown -# -# Usage (in pr.yml on close): -# - uses: ./.github/actions/raa-teardown -# with: -# pr-number: ${{ github.event.pull_request.number }} -# github-token: ${{ secrets.GITHUB_TOKEN }} -# -# No checkout required. Pure API calls. -# Reads teardown metadata from deployment payloads. - -name: "RAA Teardown" -description: "Deactivate deployments, delete environment, clean up artifacts" - -inputs: - pr-number: - required: true - description: "PR number" - github-token: - required: true - description: "GITHUB_TOKEN" - -runs: - using: composite - steps: - - name: Teardown deployments and environment - uses: actions/github-script@v7 - with: - github-token: ${{ inputs.github-token }} - script: | - const envName = `review-pr-${{ inputs.pr-number }}`; - const owner = context.repo.owner; - const repo = context.repo.repo; - - // List all deployments for this environment - const deployments = await github.rest.repos.listDeployments({ - owner, repo, environment: envName, - }); - - for (const dep of deployments.data) { - // Read payload for any app-specific teardown info - let payload = {}; - try { - payload = JSON.parse(dep.payload || '{}'); - } catch (e) { - core.warning(`Could not parse deployment payload: ${e.message}`); - } - - // Log teardown metadata for debugging - if (payload.teardown && Object.keys(payload.teardown).length > 0) { - core.info(`Deployment ${dep.id} teardown metadata: ${JSON.stringify(payload.teardown)}`); - // Future: use payload.teardown to call provider APIs - // e.g., delete Cloudflare preview, destroy k8s namespace, etc. - } - - // Mark deployment inactive - await github.rest.repos.createDeploymentStatus({ - owner, repo, - deployment_id: dep.id, - state: 'inactive', - }); - - // Delete deployment - await github.rest.repos.deleteDeployment({ - owner, repo, - deployment_id: dep.id, - }); - core.info(`Deleted deployment ${dep.id}`); - } - - // Delete the environment - try { - await github.rest.repos.deleteAnEnvironment({ - owner, repo, - environment_name: envName, - }); - core.info(`Deleted environment ${envName}`); - } catch (e) { - core.warning(`Could not delete environment ${envName}: ${e.message}`); - } - - - name: Delete workflow artifacts - uses: actions/github-script@v7 - with: - github-token: ${{ inputs.github-token }} - script: | - const artifactName = `review-pr-${{ inputs.pr-number }}`; - const owner = context.repo.owner; - const repo = context.repo.repo; - - const artifacts = await github.rest.actions.listArtifactsForRepo({ - owner, repo, - }); - for (const art of artifacts.data.artifacts) { - if (art.name === artifactName && !art.expired) { - await github.rest.actions.deleteArtifact({ - owner, repo, - artifact_id: art.id, - }); - core.info(`Deleted artifact ${art.name} (${art.id})`); - } - } diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml deleted file mode 100644 index 70fb22e..0000000 --- a/.github/actions/setup/action.yml +++ /dev/null @@ -1,61 +0,0 @@ -# .github/actions/setup/action.yml -# -# Usage: -# - uses: ./.github/actions/setup -# with: -# cache-cargo: "true" -# cache-pnpm: "true" -# -# Usage (minimal, no project caches): -# - uses: ./.github/actions/setup - -name: "Slasher Setup" -description: "Checkout, setup Moon/proto (lazy install), restore caches" - -inputs: - fetch-depth: - default: "0" - description: "Git fetch depth (0 for full history, needed for moon ci affected detection)" - cache-cargo: - default: "false" - description: "Restore Cargo registry, git, and target caches" - cache-pnpm: - default: "false" - description: "Restore pnpm store cache" - -runs: - using: composite - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: ${{ inputs.fetch-depth }} - - - uses: moonrepo/setup-toolchain@v0 - with: - auto-install: true - - - uses: actions/cache@v4 - with: - path: .moon/cache - key: moon-${{ runner.os }}-${{ hashFiles('.moon/workspace.yml', '.moon/tasks/*.yml', '.moon/toolchains.yml') }} - restore-keys: | - moon-${{ runner.os }}- - - - uses: actions/cache@v4 - if: inputs.cache-cargo == 'true' - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - cargo-${{ runner.os }}- - - - uses: actions/cache@v4 - if: inputs.cache-pnpm == 'true' - with: - path: ~/.local/share/pnpm/store - key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - pnpm-${{ runner.os }}- diff --git a/.github/actions/validate-pr-title/action.yml b/.github/actions/validate-pr-title/action.yml deleted file mode 100644 index 1b24ae7..0000000 --- a/.github/actions/validate-pr-title/action.yml +++ /dev/null @@ -1,62 +0,0 @@ -# .github/actions/validate-pr-title/action.yml -# -# Usage: -# - uses: actions/checkout@v4 -# - uses: ./.github/actions/validate-pr-title -# with: -# github-token: ${{ secrets.GITHUB_TOKEN }} -# -# Usage (with extra scopes): -# - uses: ./.github/actions/validate-pr-title -# with: -# github-token: ${{ secrets.GITHUB_TOKEN }} -# extra-scopes: "infra,release" -# -# Scopes auto-discovered from .moon/workspace.yml projects list. -# deps, ci, repo are always included. - -name: "Validate PR Title" -description: "Enforce conventional commit format on PR titles with auto-discovered scopes" - -inputs: - github-token: - required: true - description: "GITHUB_TOKEN" - extra-scopes: - required: false - default: "" - description: "Comma-separated additional scopes beyond project names and builtins" - -runs: - using: composite - steps: - - name: Discover project scopes - id: scopes - shell: bash - run: | - projects=$(yq '.workspace.projects // .projects | - if type == "!!seq" then .[] else keys[] end' .moon/workspace.yml 2>/dev/null | sort -u) - - builtins="deps,ci,repo" - extra="${{ inputs.extra-scopes }}" - combined="${builtins}${extra:+,$extra}" - - all_scopes=$(printf "%s\n%s" "$projects" "$(echo "$combined" | tr ',' '\n')" | sort -u) - - echo "scopes<> "$GITHUB_OUTPUT" - echo "$all_scopes" >> "$GITHUB_OUTPUT" - echo "EOF" >> "$GITHUB_OUTPUT" - - - uses: amannn/action-semantic-pull-request@v5 - env: - GITHUB_TOKEN: ${{ inputs.github-token }} - with: - types: | - feat - fix - chore - docs - refactor - test - perf - scopes: ${{ steps.scopes.outputs.scopes }} diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml deleted file mode 100644 index f86194e..0000000 --- a/.github/workflows/fuzz.yml +++ /dev/null @@ -1,26 +0,0 @@ -# .github/workflows/fuzz.yml -name: Fuzz - -on: - workflow_dispatch: - -jobs: - fuzz: - name: Fuzz Parser - runs-on: ubuntu-latest - steps: - - uses: ./.github/actions/setup - with: - cache-cargo: "true" - - - uses: ./.github/actions/moon-run - with: - task: "parser:fuzz" - - - name: Upload crash artifacts - if: failure() - uses: actions/upload-artifact@v4 - with: - name: fuzz-crashes - path: | - parser/hfuzz_workspace/ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index ef91deb..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,66 +0,0 @@ -# .github/workflows/main.yml -name: Main - -on: - push: - branches: [main] - -concurrency: - group: main-${{ github.sha }} - -jobs: - ci: - runs-on: ubuntu-latest - strategy: - matrix: - index: [0, 1] - steps: - - uses: ./.github/actions/setup - with: - cache-cargo: "true" - cache-pnpm: "true" - - - uses: ./.github/actions/moon-ci - with: - job-index: ${{ matrix.index }} - job-total: 2 - access-token: ${{ secrets.GITHUB_TOKEN }} - matrix-json: ${{ toJSON(matrix) }} - - security: - runs-on: ubuntu-latest - needs: ci - steps: - - uses: ./.github/actions/setup - with: - cache-cargo: "true" - cache-pnpm: "true" - - - uses: ./.github/actions/moon-run - with: - task: ":rust-security :npm-security :python-security :go-security :gitleaks" - - codeql: - runs-on: ubuntu-latest - permissions: - security-events: write - actions: read - contents: read - steps: - - uses: actions/checkout@v4 - - uses: github/codeql-action/init@v3 - with: - languages: rust,javascript,python,go - - uses: github/codeql-action/analyze@v3 - - release-please: - needs: [ci, security, codeql] - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - steps: - - uses: googleapis/release-please-action@v4 - with: - command: manifest - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml deleted file mode 100644 index a0ab946..0000000 --- a/.github/workflows/pr.yml +++ /dev/null @@ -1,82 +0,0 @@ -# .github/workflows/pr.yml -name: PR - -on: - pull_request: - types: [opened, edited, synchronize, reopened, closed] - -concurrency: - group: pr-${{ github.event.pull_request.number }} - cancel-in-progress: ${{ github.event.action != 'closed' }} - -jobs: - validate-title: - if: github.event.action != 'closed' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - sparse-checkout: .moon/workspace.yml - - uses: ./.github/actions/validate-pr-title - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - ci: - if: github.event.action != 'closed' - runs-on: ubuntu-latest - strategy: - matrix: - index: [0, 1] - steps: - - uses: ./.github/actions/setup - with: - cache-cargo: "true" - cache-pnpm: "true" - - - uses: ./.github/actions/moon-ci - with: - job-index: ${{ matrix.index }} - job-total: 2 - access-token: ${{ secrets.GITHUB_TOKEN }} - matrix-json: ${{ toJSON(matrix) }} - - security: - if: github.event.action != 'closed' - runs-on: ubuntu-latest - needs: ci - steps: - - uses: ./.github/actions/setup - with: - cache-cargo: "true" - cache-pnpm: "true" - - - uses: ./.github/actions/moon-run - with: - task: ":rust-security :npm-security :python-security :go-security :gitleaks" - affected: "remote" - - codeql: - if: github.event.action != 'closed' - runs-on: ubuntu-latest - permissions: - security-events: write - actions: read - contents: read - steps: - - uses: actions/checkout@v4 - - uses: github/codeql-action/init@v3 - with: - languages: rust,javascript,python,go - - uses: github/codeql-action/analyze@v3 - - review-teardown: - if: github.event.action == 'closed' - runs-on: ubuntu-latest - permissions: - deployments: write - actions: write - steps: - - uses: ./.github/actions/raa-teardown - with: - pr-number: ${{ github.event.pull_request.number }} - github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-crates.yml b/.github/workflows/publish-crates.yml deleted file mode 100644 index 2934cd9..0000000 --- a/.github/workflows/publish-crates.yml +++ /dev/null @@ -1,15 +0,0 @@ -# .github/workflows/publish-crates.yml -name: Publish crates - -on: - release: - types: [published] - -jobs: - publish: - uses: ./.github/workflows/publish-to-registry.yml - with: - tag-prefix: "slash-rust-v" - moon-task: "slash-rust:publish" - cache-cargo: true - secrets: inherit diff --git a/.github/workflows/publish-go.yml b/.github/workflows/publish-go.yml deleted file mode 100644 index da2468c..0000000 --- a/.github/workflows/publish-go.yml +++ /dev/null @@ -1,14 +0,0 @@ -# .github/workflows/publish-go.yml -name: Validate slash-go - -on: - release: - types: [published] - -jobs: - validate: - uses: ./.github/workflows/publish-to-registry.yml - with: - tag-prefix: "slash-go/" - moon-task: "slash-go:test" - secrets: inherit diff --git a/.github/workflows/publish-npm-slash-javascript.yml b/.github/workflows/publish-npm-slash-javascript.yml deleted file mode 100644 index 57b7e93..0000000 --- a/.github/workflows/publish-npm-slash-javascript.yml +++ /dev/null @@ -1,16 +0,0 @@ -# .github/workflows/publish-npm-slash-javascript.yml -name: Publish @slasher/slash-javascript - -on: - release: - types: [published] - -jobs: - publish: - uses: ./.github/workflows/publish-to-registry.yml - with: - tag-prefix: "slash-javascript-v" - moon-task: "slash-javascript:publish" - cache-pnpm: true - needs-oidc: true - secrets: inherit diff --git a/.github/workflows/publish-npm-slash-web.yml b/.github/workflows/publish-npm-slash-web.yml deleted file mode 100644 index 6684c55..0000000 --- a/.github/workflows/publish-npm-slash-web.yml +++ /dev/null @@ -1,16 +0,0 @@ -# .github/workflows/publish-npm-slash-web.yml -name: Publish @slasher/slash-web - -on: - release: - types: [published] - -jobs: - publish: - uses: ./.github/workflows/publish-to-registry.yml - with: - tag-prefix: "slash-web-v" - moon-task: "slash-web:publish" - cache-pnpm: true - needs-oidc: true - secrets: inherit diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml deleted file mode 100644 index 42cd10c..0000000 --- a/.github/workflows/publish-pypi.yml +++ /dev/null @@ -1,14 +0,0 @@ -# .github/workflows/publish-pypi.yml -name: Publish slash-python - -on: - release: - types: [published] - -jobs: - publish: - uses: ./.github/workflows/publish-to-registry.yml - with: - tag-prefix: "slash-python-v" - moon-task: "slash-python:publish" - secrets: inherit diff --git a/.github/workflows/publish-riff.yml b/.github/workflows/publish-riff.yml deleted file mode 100644 index 8628474..0000000 --- a/.github/workflows/publish-riff.yml +++ /dev/null @@ -1,25 +0,0 @@ -# .github/workflows/publish-riff.yml -name: Publish riff - -on: - release: - types: [published] - -jobs: - publish: - if: startsWith(github.event.release.tag_name, 'riff-cli-v') - runs-on: ubuntu-latest - environment: publish - permissions: - contents: write - packages: write - steps: - - uses: ./.github/actions/setup - with: - cache-cargo: "true" - - - name: Run riff publish (GoReleaser) - run: moon run riff-cli:publish - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} diff --git a/.github/workflows/publish-to-registry.yml b/.github/workflows/publish-to-registry.yml deleted file mode 100644 index 144174d..0000000 --- a/.github/workflows/publish-to-registry.yml +++ /dev/null @@ -1,62 +0,0 @@ -# .github/workflows/publish-to-registry.yml -# -# Reusable workflow for publishing a project to its registry. -# Called by thin per-project publish-*.yml workflows. -# -# Usage: -# jobs: -# publish: -# uses: ./.github/workflows/publish-to-registry.yml -# with: -# tag-prefix: "slash-javascript-v" -# moon-task: "slash-javascript:publish" -# cache-cargo: true -# needs-oidc: true -# secrets: inherit - -name: Publish to Registry (reusable) - -on: - workflow_call: - inputs: - tag-prefix: - required: true - type: string - description: "Tag prefix to match (e.g., 'slash-javascript-v')" - moon-task: - required: true - type: string - description: "Moon task to run (e.g., 'slash-javascript:publish')" - cache-cargo: - required: false - type: boolean - default: false - cache-pnpm: - required: false - type: boolean - default: false - needs-oidc: - required: false - type: boolean - default: false - description: "Request id-token permission for OIDC trusted publishing" - -jobs: - publish: - if: startsWith(github.event.release.tag_name, inputs.tag-prefix) - runs-on: ubuntu-latest - environment: publish - permissions: - contents: write - packages: write - id-token: ${{ inputs.needs-oidc && 'write' || 'none' }} - - steps: - - uses: ./.github/actions/setup - with: - cache-cargo: ${{ inputs.cache-cargo && 'true' || 'false' }} - cache-pnpm: ${{ inputs.cache-pnpm && 'true' || 'false' }} - - - uses: ./.github/actions/moon-run - with: - task: ${{ inputs.moon-task }} diff --git a/.github/workflows/publish-wasm.yml b/.github/workflows/publish-wasm.yml deleted file mode 100644 index bd8adaa..0000000 --- a/.github/workflows/publish-wasm.yml +++ /dev/null @@ -1,45 +0,0 @@ -# .github/workflows/publish-wasm.yml -name: Publish WASM + WIT - -on: - release: - types: [published] - -jobs: - publish-wasm-javascript: - if: startsWith(github.event.release.tag_name, 'wasm-javascript-v') - runs-on: ubuntu-latest - environment: publish - permissions: - contents: write - steps: - - uses: ./.github/actions/setup - with: - cache-cargo: "true" - - - name: Build wasm-javascript - run: moon run wasm-javascript:build - - - name: Attach artifacts to GitHub Release - run: ./wasm-javascript/scripts/attach-release-assets.sh - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - publish-wasm-wasi: - if: startsWith(github.event.release.tag_name, 'wasm-wasi-v') - runs-on: ubuntu-latest - environment: publish - permissions: - contents: write - steps: - - uses: ./.github/actions/setup - with: - cache-cargo: "true" - - - name: Build wasm-wasi + WIT - run: moon run wasm-wasi:build - - - name: Attach wasm-wasi and WIT artifacts to GitHub Release - run: ./wasm-wasi/scripts/attach-release-assets.sh - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml deleted file mode 100644 index e0b7a09..0000000 --- a/.github/workflows/review.yml +++ /dev/null @@ -1,64 +0,0 @@ -# .github/workflows/review.yml -name: Review - -on: - workflow_dispatch: - inputs: - pr-number: - description: "PR number to build review artifacts for" - required: true - -permissions: - deployments: write - actions: write - contents: read - -jobs: - review: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: refs/pull/${{ github.event.inputs.pr-number }}/head - fetch-depth: 0 - - - uses: ./.github/actions/setup - with: - cache-cargo: "true" - cache-pnpm: "true" - - - uses: ./.github/actions/raa-open - id: raa - with: - pr-number: ${{ github.event.inputs.pr-number }} - type: "artifacts" - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Build riff snapshot - run: goreleaser release --config riff-cli/.goreleaser.yaml --snapshot --skip=publish - - - name: Build SDK snapshots - run: | - moon run slash-rust:build - moon run slash-javascript:build - moon run slash-web:build - moon run slash-python:build - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: review-pr-${{ github.event.inputs.pr-number }} - path: | - riff-cli/dist/** - slash-rust/target/** - slash-javascript/dist/** - slash-web/dist/** - slash-python/dist/** - - - uses: ./.github/actions/raa-close - if: always() - with: - pr-number: ${{ github.event.inputs.pr-number }} - deployment-id: ${{ steps.raa.outputs.deployment-id }} - status: ${{ job.status == 'success' && 'success' || 'failure' }} - github-token: ${{ secrets.GITHUB_TOKEN }} From 9198549682479f9bb1beeb546eee70e11013ad56 Mon Sep 17 00:00:00 2001 From: Tom Davidson Date: Tue, 17 Mar 2026 00:49:51 +0000 Subject: [PATCH 09/16] ci: add GitHub Actions pipeline (clean slate) Actions: - moon-setup: checkout, toolchains (rust/bun/go/python), selective caching - raa-open: provision review environment and deploy snapshots - raa-teardown: deactivate deployments and clean up Workflows: - pr.yml: CI, CodeQL, PR title, review app open/teardown - release.yml: release-please on main push - publish.yml: single publish workflow with moon :publish --affected - deploy.yml: website deploy on main push (path-filtered) - scheduled.yml: weekly semgrep, CodeQL, and fuzz --- .github/actions/moon-setup/action.yml | 72 ++++++++++++++++++++++ .github/actions/raa-open/action.yml | 32 ++++++++++ .github/actions/raa-teardown/action.yml | 36 +++++++++++ .github/workflows/deploy.yml | 18 ++++++ .github/workflows/pr.yml | 82 +++++++++++++++++++++++++ .github/workflows/publish.yml | 33 ++++++++++ .github/workflows/release.yml | 19 ++++++ .github/workflows/scheduled.yml | 38 ++++++++++++ 8 files changed, 330 insertions(+) create mode 100644 .github/actions/moon-setup/action.yml create mode 100644 .github/actions/raa-open/action.yml create mode 100644 .github/actions/raa-teardown/action.yml create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/pr.yml create mode 100644 .github/workflows/publish.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/scheduled.yml diff --git a/.github/actions/moon-setup/action.yml b/.github/actions/moon-setup/action.yml new file mode 100644 index 0000000..23a83fc --- /dev/null +++ b/.github/actions/moon-setup/action.yml @@ -0,0 +1,72 @@ +name: Moon Setup +description: Checkout, install toolchains, restore caches, setup Moon + +inputs: + fetch-depth: + description: Git fetch depth + default: "0" + cache: + description: Enable dependency caching + default: "true" + rust: + description: Install Rust toolchain + default: "true" + bun: + description: Install Bun runtime + default: "true" + go: + description: Install Go toolchain + default: "false" + python: + description: Install Python toolchain + default: "false" + +runs: + using: composite + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: ${{ inputs.fetch-depth }} + + - uses: moonrepo/setup-toolchain@v1 + with: + auto-install: true + + - if: inputs.rust == 'true' + uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown,wasm32-wasip2 + + - if: inputs.go == 'true' + uses: actions/setup-go@v5 + with: + go-version-file: go.work + + - if: inputs.python == 'true' + uses: actions/setup-python@v5 + with: + python-version-file: .python-version + + - if: inputs.cache == 'true' && inputs.rust == 'true' + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target/ + key: cargo-${{ runner.os }}-${{ hashFiles('Cargo.lock') }} + restore-keys: cargo-${{ runner.os }}- + + - if: inputs.cache == 'true' && inputs.bun == 'true' + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: bun-${{ runner.os }}-${{ hashFiles('bun.lockb') }} + restore-keys: bun-${{ runner.os }}- + + - if: inputs.cache == 'true' + uses: actions/cache@v4 + with: + path: .moon/cache + key: moon-${{ runner.os }}-${{ github.sha }} + restore-keys: moon-${{ runner.os }}- diff --git a/.github/actions/raa-open/action.yml b/.github/actions/raa-open/action.yml new file mode 100644 index 0000000..aee8ca3 --- /dev/null +++ b/.github/actions/raa-open/action.yml @@ -0,0 +1,32 @@ +name: Review App Open +description: Provision review environment and deploy snapshot artifacts + +inputs: + pr-number: + description: PR number + required: true + github-token: + description: GitHub token for deployments API + required: true + +runs: + using: composite + steps: + - uses: chrnorm/deployment-action@v2 + id: deployment + with: + token: ${{ inputs.github-token }} + environment: review-pr-${{ inputs.pr-number }} + transient-environment: true + auto-inactive: false + + - shell: bash + run: moon run :publish -- --snapshot + env: + REVIEW_PR: ${{ inputs.pr-number }} + + - uses: chrnorm/deployment-status@v2 + with: + token: ${{ inputs.github-token }} + deployment-id: ${{ steps.deployment.outputs.deployment_id }} + state: success diff --git a/.github/actions/raa-teardown/action.yml b/.github/actions/raa-teardown/action.yml new file mode 100644 index 0000000..83e16f5 --- /dev/null +++ b/.github/actions/raa-teardown/action.yml @@ -0,0 +1,36 @@ +name: Review App Teardown +description: Deactivate deployment and clean up review environment + +inputs: + pr-number: + description: PR number + required: true + github-token: + description: GitHub token for deployments API + required: true + +runs: + using: composite + steps: + - shell: bash + env: + GH_TOKEN: ${{ inputs.github-token }} + PR_NUMBER: ${{ inputs.pr-number }} + run: | + ENV_NAME="review-pr-${PR_NUMBER}" + + # Mark all deployments inactive + DEPLOYMENT_IDS=$(gh api "repos/${{ github.repository }}/deployments?environment=${ENV_NAME}" \ + --jq '.[].id') + + for id in $DEPLOYMENT_IDS; do + gh api "repos/${{ github.repository }}/deployments/${id}/statuses" \ + -f state=inactive -f description="PR closed" + done + + # Delete pre-release assets if they exist + RELEASE_TAG="review-pr-${PR_NUMBER}" + gh release delete "$RELEASE_TAG" --yes --cleanup-tag 2>/dev/null || true + + # Remove the environment + gh api -X DELETE "repos/${{ github.repository }}/environments/${ENV_NAME}" 2>/dev/null || true diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..4a72869 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,18 @@ +name: Deploy + +on: + push: + branches: [main] + paths: + - "website/**" + +jobs: + deploy: + name: Deploy Website + runs-on: ubuntu-latest + environment: production + steps: + - uses: ./.github/actions/moon-setup + with: + rust: "false" + - run: moon run website:deploy diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..1847d10 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,82 @@ +name: PR Pipeline + +on: + pull_request: + types: [opened, synchronize, reopened, closed, labeled] + workflow_dispatch: + inputs: + review-app: + description: Build and deploy review app artifacts + type: boolean + default: false + +concurrency: + group: pr-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + +jobs: + ci: + if: github.event.action != 'closed' + name: Moon CI + runs-on: ubuntu-latest + steps: + - uses: ./.github/actions/moon-setup + - run: moon ci + + codeql: + if: github.event.action != 'closed' + name: CodeQL + runs-on: ubuntu-latest + permissions: + security-events: write + steps: + - uses: ./.github/actions/moon-setup + with: + cache: "false" + - uses: github/codeql-action/init@v3 + with: + languages: javascript,go + - uses: github/codeql-action/autobuild@v3 + - uses: github/codeql-action/analyze@v3 + + pr-title: + if: github.event.action != 'closed' + name: PR Title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + review-open: + if: | + github.event.action != 'closed' && ( + github.event.inputs.review-app == 'true' || + contains(github.event.pull_request.labels.*.name, 'review-app') + ) + name: Review App Deploy + needs: [ci] + runs-on: ubuntu-latest + permissions: + deployments: write + contents: write + steps: + - uses: ./.github/actions/moon-setup + - uses: ./.github/actions/raa-open + with: + pr-number: ${{ github.event.pull_request.number }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + review-teardown: + if: github.event.action == 'closed' + name: Review App Teardown + runs-on: ubuntu-latest + permissions: + deployments: write + contents: write + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/raa-teardown + with: + pr-number: ${{ github.event.pull_request.number }} + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..446dcc6 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,33 @@ +name: Publish + +on: + release: + types: [published] + +jobs: + publish: + name: Publish Affected + runs-on: ubuntu-latest + steps: + - uses: ./.github/actions/moon-setup + with: + go: "true" + python: "true" + + - run: moon run :publish --affected + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GORELEASER_CURRENT_TAG: ${{ github.event.release.tag_name }} + + - name: Attach artifacts to release + shell: bash + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.event.release.tag_name }} + run: | + if [ -d dist/ ]; then + gh release upload "$TAG" dist/* --clobber + fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..8594206 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,19 @@ +name: Release + +on: + push: + branches: [main] + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + name: Release Please + runs-on: ubuntu-latest + steps: + - uses: googleapis/release-please-action@v4 + with: + manifest-file: .release-please-manifest.json + config-file: release-please-config.json diff --git a/.github/workflows/scheduled.yml b/.github/workflows/scheduled.yml new file mode 100644 index 0000000..39fe18c --- /dev/null +++ b/.github/workflows/scheduled.yml @@ -0,0 +1,38 @@ +name: Scheduled Security + +on: + schedule: + - cron: "0 6 * * 1" + workflow_dispatch: + +jobs: + semgrep: + name: Semgrep + runs-on: ubuntu-latest + container: + image: semgrep/semgrep + steps: + - uses: actions/checkout@v4 + - run: semgrep scan --config auto --error + + codeql: + name: CodeQL (scheduled) + runs-on: ubuntu-latest + permissions: + security-events: write + steps: + - uses: actions/checkout@v4 + - uses: github/codeql-action/init@v3 + with: + languages: javascript,go + - uses: github/codeql-action/autobuild@v3 + - uses: github/codeql-action/analyze@v3 + + fuzz: + name: Fuzz (scheduled) + runs-on: ubuntu-latest + steps: + - uses: ./.github/actions/moon-setup + with: + bun: "false" + - run: moon run fuzz:run -- -max_total_time=600 From 775ffa5002c0d480eb07fb3989af1e3487055cd1 Mon Sep 17 00:00:00 2001 From: Tom Davidson Date: Mon, 16 Mar 2026 19:28:17 -0600 Subject: [PATCH 10/16] Add checkout action to deploy workflow --- .github/workflows/deploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4a72869..4b44e6f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -12,6 +12,7 @@ jobs: runs-on: ubuntu-latest environment: production steps: + - uses: actions/checkout@v4 - uses: ./.github/actions/moon-setup with: rust: "false" From 818251030027a0fddc4c6d64c79bcab9d84229d2 Mon Sep 17 00:00:00 2001 From: Tom Davidson Date: Mon, 16 Mar 2026 19:30:44 -0600 Subject: [PATCH 11/16] add checkout action --- .github/workflows/pr.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 1847d10..3adee4f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -20,6 +20,7 @@ jobs: name: Moon CI runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 - uses: ./.github/actions/moon-setup - run: moon ci @@ -30,6 +31,7 @@ jobs: permissions: security-events: write steps: + - uses: actions/checkout@v4 - uses: ./.github/actions/moon-setup with: cache: "false" @@ -61,6 +63,7 @@ jobs: deployments: write contents: write steps: + - uses: actions/checkout@v4 - uses: ./.github/actions/moon-setup - uses: ./.github/actions/raa-open with: From bedb8c5183ef5749c595df1ab7464b8b0530aed2 Mon Sep 17 00:00:00 2001 From: Tom Davidson Date: Mon, 16 Mar 2026 19:31:22 -0600 Subject: [PATCH 12/16] add checkout action to publish workflow --- .github/workflows/publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 446dcc6..689fe95 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,6 +9,7 @@ jobs: name: Publish Affected runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 - uses: ./.github/actions/moon-setup with: go: "true" From 105a0cf3aaa604e217a555943ce6b9c7146dcb7b Mon Sep 17 00:00:00 2001 From: Tom Davidson Date: Mon, 16 Mar 2026 19:33:28 -0600 Subject: [PATCH 13/16] fuzz place holder Updated the fuzz job to echo the command instead of running it directly. --- .github/workflows/scheduled.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/scheduled.yml b/.github/workflows/scheduled.yml index 39fe18c..e006b84 100644 --- a/.github/workflows/scheduled.yml +++ b/.github/workflows/scheduled.yml @@ -32,7 +32,8 @@ jobs: name: Fuzz (scheduled) runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 - uses: ./.github/actions/moon-setup with: bun: "false" - - run: moon run fuzz:run -- -max_total_time=600 + - run: echo "moon run fuzz:run -- -max_total_time=600" From 153724de788afeab52c8ed966eed1eea4204727a Mon Sep 17 00:00:00 2001 From: tomdavidson Date: Wed, 18 Mar 2026 00:19:14 -0600 Subject: [PATCH 14/16] ci: gh actions & review app --- .github/actions/conventional-pr/action.yml | 62 +++++++++ .github/actions/moon-ci-matrix/action.yml | 40 ++++++ .github/actions/moon-ci/action.yml | 32 +++++ .github/actions/moon-raa/action.yml | 57 +++++++++ .github/actions/moon-raa/lib.js | 139 +++++++++++++++++++++ .github/actions/moon-raa/rm.js | 80 ++++++++++++ .github/actions/moon-raa/run.js | 84 +++++++++++++ .github/actions/moon-run/action.yml | 51 ++++++++ .github/actions/setup/action.yml | 61 +++++++++ .github/workflows/main.yml | 108 ++++++++++++++++ .github/workflows/pr-close.yml | 26 ++++ .github/workflows/pr.yml | 130 ++++++++++++------- .github/workflows/publish.yml | 100 ++++++++++++--- 13 files changed, 908 insertions(+), 62 deletions(-) create mode 100644 .github/actions/conventional-pr/action.yml create mode 100644 .github/actions/moon-ci-matrix/action.yml create mode 100644 .github/actions/moon-ci/action.yml create mode 100644 .github/actions/moon-raa/action.yml create mode 100644 .github/actions/moon-raa/lib.js create mode 100644 .github/actions/moon-raa/rm.js create mode 100644 .github/actions/moon-raa/run.js create mode 100644 .github/actions/moon-run/action.yml create mode 100644 .github/actions/setup/action.yml create mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/pr-close.yml diff --git a/.github/actions/conventional-pr/action.yml b/.github/actions/conventional-pr/action.yml new file mode 100644 index 0000000..1b24ae7 --- /dev/null +++ b/.github/actions/conventional-pr/action.yml @@ -0,0 +1,62 @@ +# .github/actions/validate-pr-title/action.yml +# +# Usage: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/validate-pr-title +# with: +# github-token: ${{ secrets.GITHUB_TOKEN }} +# +# Usage (with extra scopes): +# - uses: ./.github/actions/validate-pr-title +# with: +# github-token: ${{ secrets.GITHUB_TOKEN }} +# extra-scopes: "infra,release" +# +# Scopes auto-discovered from .moon/workspace.yml projects list. +# deps, ci, repo are always included. + +name: "Validate PR Title" +description: "Enforce conventional commit format on PR titles with auto-discovered scopes" + +inputs: + github-token: + required: true + description: "GITHUB_TOKEN" + extra-scopes: + required: false + default: "" + description: "Comma-separated additional scopes beyond project names and builtins" + +runs: + using: composite + steps: + - name: Discover project scopes + id: scopes + shell: bash + run: | + projects=$(yq '.workspace.projects // .projects | + if type == "!!seq" then .[] else keys[] end' .moon/workspace.yml 2>/dev/null | sort -u) + + builtins="deps,ci,repo" + extra="${{ inputs.extra-scopes }}" + combined="${builtins}${extra:+,$extra}" + + all_scopes=$(printf "%s\n%s" "$projects" "$(echo "$combined" | tr ',' '\n')" | sort -u) + + echo "scopes<> "$GITHUB_OUTPUT" + echo "$all_scopes" >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ inputs.github-token }} + with: + types: | + feat + fix + chore + docs + refactor + test + perf + scopes: ${{ steps.scopes.outputs.scopes }} diff --git a/.github/actions/moon-ci-matrix/action.yml b/.github/actions/moon-ci-matrix/action.yml new file mode 100644 index 0000000..576c222 --- /dev/null +++ b/.github/actions/moon-ci-matrix/action.yml @@ -0,0 +1,40 @@ +name: "Moon CI Runner Matrix" +description: "Calculates optimal runner count from affected Moon tasks" + +inputs: + tasks-per-runner: + required: false + default: "3" + description: "Number of affected tasks to assign per runner" + max-parallel: + required: false + default: "6" + description: "Maximum number of concurrent runners" + +outputs: + matrix: + description: "JSON array of shard indices, e.g. [0, 1, 2]" + value: ${{ steps.calc.outputs.matrix }} + count: + description: "Total number of runners" + value: ${{ steps.calc.outputs.total }} + +runs: + using: composite + steps: + - id: calc + shell: bash + run: | + TASKS_PER_RUNNER=${{ inputs.tasks-per-runner }} + MAX_RUNNERS=${{ inputs.max-parallel }} + + TASK_COUNT=$(moon query tasks --affected | jq '[.tasks[][] | select(.options.runInCI != false)] | length') + + WANTED=$(( (TASK_COUNT + TASKS_PER_RUNNER - 1) / TASKS_PER_RUNNER )) + TOTAL=$(( WANTED > MAX_RUNNERS ? MAX_RUNNERS : WANTED )) + + MATRIX_JSON=$(jq -n -c --argjson n "$TOTAL" '[range($n)]') + + echo "Affected CI tasks: $TASK_COUNT, Runners: $TOTAL" + echo "count=$TOTAL" >> $GITHUB_OUTPUT + echo "matrix=$MATRIX_JSON" >> $GITHUB_OUTPUT diff --git a/.github/actions/moon-ci/action.yml b/.github/actions/moon-ci/action.yml new file mode 100644 index 0000000..b88b15b --- /dev/null +++ b/.github/actions/moon-ci/action.yml @@ -0,0 +1,32 @@ +# .github/actions/moon-ci/action.yml +# +# Usage: +# - uses: ./.github/actions/moon-ci +# with: +# access-token: ${{ secrets.GITHUB_TOKEN }} + +name: "Moon CI" +description: "Run moon ci with sharding, reporting, and retrospect" + +inputs: + access-token: + required: true + description: "GITHUB_TOKEN for run report" + +runs: + using: composite + steps: + - name: Moon CI + shell: bash + run: | + echo "Runner ${{ strategy.job-index }} of ${{ strategy.job-total }}" + moon ci --color --job ${{ strategy.job-index }} --job-total ${{ strategy.job-total }} + + - uses: moonrepo/run-report-action@v1 + if: success() || failure() + with: + access-token: ${{ inputs.access-token }} + matrix: '{"index": ${{ strategy.job-index }}, "total": ${{ strategy.job-total }}}' + + - uses: appthrust/moon-ci-retrospect@v1 + if: success() || failure() diff --git a/.github/actions/moon-raa/action.yml b/.github/actions/moon-raa/action.yml new file mode 100644 index 0000000..864e3b0 --- /dev/null +++ b/.github/actions/moon-raa/action.yml @@ -0,0 +1,57 @@ +name: "moon-raa" +description: "Manage review apps and artifacts for a Moon monorepo" + +inputs: + command: + required: true + description: "'run' or 'rm'" + pr-number: + required: true + description: "PR number" + prefix: + required: false + default: "raa" + description: "Environment name prefix (prepended to '-pr-{number}')" + github-token: + required: true + description: "GITHUB_TOKEN" + +outputs: + deployment-id: + description: "GitHub Deployment ID (run only)" + value: ${{ steps.raa.outputs.deployment-id }} + environment: + description: "Environment name" + value: ${{ steps.raa.outputs.environment }} + project-count: + description: "Number of projects processed" + value: ${{ steps.raa.outputs.project-count }} + summary: + description: "Markdown summary for GITHUB_STEP_SUMMARY" + value: ${{ steps.raa.outputs.summary }} + manifest: + description: "Full payload as JSON" + value: ${{ steps.raa.outputs.manifest }} + clean: + description: "true if rm completed without warnings (rm only)" + value: ${{ steps.raa.outputs.clean }} + +runs: + using: composite + steps: + - name: moon-raa + id: raa + uses: actions/github-script@v7 + env: + INPUT_COMMAND: ${{ inputs.command }} + INPUT_PR_NUMBER: ${{ inputs.pr-number }} + INPUT_PREFIX: ${{ inputs.prefix }} + with: + github-token: ${{ inputs.github-token }} + script: | + const cmd = process.env.INPUT_COMMAND; + const handler = require(`${{ github.action_path }}/${cmd}.js`); + await handler({ github, context, core, exec, inputs: { + pr_number: process.env.INPUT_PR_NUMBER, + prefix: process.env.INPUT_PREFIX, + }}); diff --git a/.github/actions/moon-raa/lib.js b/.github/actions/moon-raa/lib.js new file mode 100644 index 0000000..a8ac0e2 --- /dev/null +++ b/.github/actions/moon-raa/lib.js @@ -0,0 +1,139 @@ +const environmentName = (prefix, prNumber) => `${prefix}-pr-${prNumber}` + +const artifactName = (prefix, projectId, prNumber) => `${prefix}-${projectId}-pr-${prNumber}` + +const targetName = (projectId, task) => `${projectId}:${task}` + +const buildManifest = (prNumber, projects) => ({ pr_number: prNumber, projects }) + +const buildProjectEntry = (prefix, prNumber) => project => ({ + id: project.id, + artifact_name: artifactName(prefix, project.id, prNumber), + run: project.raa.run, + rm: project.raa.rm, +}) + +const validateProject = project => { + const raa = project.raa + const missing = [!raa && 'raa metadata', raa && !raa.run && 'raa.run', raa && !raa.rm && 'raa.rm'].filter( + Boolean, + ) + + return missing.length ? { valid: false, reason: `missing ${missing.join(', ')}` } : { valid: true } +} + +const partitionProjects = (projects, core) => { + const valid = [] + for (const project of projects) { + const result = validateProject(project) + if (!result.valid) { + core.warning(`Skipping ${project.id}: ${result.reason}`) + continue + } + valid.push(project) + } + return valid +} + +const formatRunSummary = (environment, projects) => { + if (!projects.length) return 'No RAA projects affected.' + + const rows = projects.map(p => `| ${p.id} | \`${p.run}\` | \`${p.artifact_name}\` |`).join('\n') + + return [ + `### RAA Review`, + '', + '| Project | Task | Artifact |', + '|---------|------|----------|', + rows, + '', + `${projects.length} project(s) deployed to \`${environment}\``, + ].join('\n') +} + +const formatRmSummary = (environment, results) => { + if (!results.length) return 'No RAA projects to tear down.' + + const rows = results.map(r => `| ${r.id} | \`${r.rm}\` | ${r.status} |`).join('\n') + + return [ + `### RAA Teardown`, + '', + '| Project | Task | Status |', + '|---------|------|--------|', + rows, + '', + `${results.length} project(s) torn down from \`${environment}\``, + ].join('\n') +} + +const listDeployments = async (github, owner, repo, environment) => { + const { data } = await github.rest.repos.listDeployments({ owner, repo, environment }) + return data +} + +const deactivateDeployments = async (github, owner, repo, environment, core) => { + const deployments = await listDeployments(github, owner, repo, environment) + + for (const deployment of deployments) { + await github.rest.repos.createDeploymentStatus({ + owner, + repo, + deployment_id: deployment.id, + state: 'inactive', + }) + await github.rest.repos.deleteDeployment({ owner, repo, deployment_id: deployment.id }) + core.info(`Deactivated and deleted deployment ${deployment.id}`) + } + + return deployments +} + +const createDeployment = async (github, owner, repo, { ref, environment, description, payload }) => { + const { data: deployment } = await github.rest.repos.createDeployment({ + owner, + repo, + ref, + environment, + auto_merge: false, + required_contexts: [], + description, + payload: JSON.stringify(payload), + }) + return deployment +} + +const setDeploymentStatus = async (github, owner, repo, deploymentId, state, description) => { + await github.rest.repos.createDeploymentStatus({ + owner, + repo, + deployment_id: deploymentId, + state, + description: description ?? '', + }) +} + +const deleteEnvironment = async (github, owner, repo, environment) => { + try { + await github.rest.repos.deleteAnEnvironment({ owner, repo, environment_name: environment }) + } catch (error) { + if (error.status !== 404) throw error + } +} + +module.exports = { + environmentName, + artifactName, + targetName, + buildManifest, + buildProjectEntry, + validateProject, + partitionProjects, + formatRunSummary, + formatRmSummary, + listDeployments, + deactivateDeployments, + createDeployment, + setDeploymentStatus, + deleteEnvironment, +} diff --git a/.github/actions/moon-raa/rm.js b/.github/actions/moon-raa/rm.js new file mode 100644 index 0000000..c1a1b60 --- /dev/null +++ b/.github/actions/moon-raa/rm.js @@ -0,0 +1,80 @@ +const { execSync } = require('child_process') +const lib = require('./lib') + +const collectTargets = deployments => + deployments.map(d => JSON.parse(d.payload || '{}')).flatMap(p => p.projects ?? []).map(( + { id, rm, artifact_name }, + ) => ({ id, rm, artifact_name })) + +const checkAffectedMismatch = (targets, core) => { + let raw + try { + raw = execSync('moon query projects --affected').toString() + } catch (error) { + core.warning(`Could not check affected projects: ${error.message}`) + return false + } + + const { projects } = JSON.parse(raw) + const affectedIds = new Set(projects.map(p => p.id)) + const stale = targets.filter(t => !affectedIds.has(t.id)) + + if (!stale.length) return true + + core.warning(`Teardown targets not in --affected: ${stale.map(t => t.id).join(', ')}`) + return false +} + +const runTeardowns = async (exec, core, targets) => { + const results = [] + for (const target of targets) { + const moonTarget = lib.targetName(target.id, target.rm) + core.info(`Running teardown: ${moonTarget}`) + try { + await exec.exec('moon', ['run', moonTarget]) + results.push({ ...target, status: 'done' }) + } catch (error) { + core.error(`Failed ${moonTarget}: ${error.message}`) + results.push({ ...target, status: 'failed' }) + } + } + return results +} + +module.exports = async ({ github, context, core, exec, inputs }) => { + const { owner, repo } = context.repo + const { pr_number, prefix } = inputs + const environment = lib.environmentName(prefix, pr_number) + + const deployments = await lib.listDeployments(github, owner, repo, environment) + + if (!deployments.length) { + core.info(`No deployments found for ${environment}`) + core.setOutput('environment', environment) + core.setOutput('project-count', '0') + core.setOutput('summary', 'No RAA projects to tear down.') + core.setOutput('manifest', '{}') + core.setOutput('clean', 'true') + return + } + + const targets = collectTargets(deployments) + const affectedClean = checkAffectedMismatch(targets, core) + const results = await runTeardowns(exec, core, targets) + + await lib.deactivateDeployments(github, owner, repo, environment, core) + await lib.deleteEnvironment(github, owner, repo, environment) + core.info(`Cleaned up environment ${environment}`) + + const manifest = { pr_number, projects: targets } + const summary = lib.formatRmSummary(environment, results) + const allPassed = results.every(r => r.status === 'done') + + core.setOutput('environment', environment) + core.setOutput('project-count', String(targets.length)) + core.setOutput('summary', summary) + core.setOutput('manifest', JSON.stringify(manifest)) + core.setOutput('clean', String(affectedClean && allPassed)) + + if (!allPassed) core.setFailed('One or more teardown tasks failed') +} diff --git a/.github/actions/moon-raa/run.js b/.github/actions/moon-raa/run.js new file mode 100644 index 0000000..77c2898 --- /dev/null +++ b/.github/actions/moon-raa/run.js @@ -0,0 +1,84 @@ +const { execSync } = require('child_process') +const lib = require('./lib') + +const discoverProjects = core => { + const { projects } = JSON.parse(execSync('moon query projects --affected').toString()) + + const withRaa = projects.filter(p => p.config?.project?.metadata?.raa).map(p => ({ + id: p.id, + raa: p.config.project.metadata.raa, + })) + + return lib.partitionProjects(withRaa, core) +} + +const runTasks = async (exec, core, projectEntries) => { + const results = [] + for (const project of projectEntries) { + const target = lib.targetName(project.id, project.run) + core.info(`Running ${target}`) + try { + await exec.exec('moon', ['run', target]) + results.push({ ...project, status: 'done' }) + } catch (error) { + core.error(`Failed ${target}: ${error.message}`) + results.push({ ...project, status: 'failed' }) + } + } + return results +} + +module.exports = async ({ github, context, core, exec, inputs }) => { + const { owner, repo } = context.repo + const { pr_number, prefix } = inputs + const environment = lib.environmentName(prefix, pr_number) + + const valid = discoverProjects(core) + + if (!valid.length) { + core.info('No affected projects with valid raa metadata') + core.setOutput('deployment-id', '') + core.setOutput('environment', environment) + core.setOutput('project-count', '0') + core.setOutput('summary', 'No RAA projects affected.') + core.setOutput('manifest', '{}') + return + } + + const projectEntries = valid.map(lib.buildProjectEntry(prefix, pr_number)) + const manifest = lib.buildManifest(pr_number, projectEntries) + + await lib.deactivateDeployments(github, owner, repo, environment, core) + + const deployment = await lib.createDeployment(github, owner, repo, { + ref: context.sha, + environment, + description: `Review for PR #${pr_number} (${valid.length} projects)`, + payload: manifest, + }) + + await lib.setDeploymentStatus( + github, + owner, + repo, + deployment.id, + 'in_progress', + 'Building review environment...', + ) + + const results = await runTasks(exec, core, projectEntries) + const allPassed = results.every(r => r.status === 'done') + + const state = allPassed ? 'success' : 'failure' + const description = allPassed ? 'Review build complete' : 'Review build failed' + await lib.setDeploymentStatus(github, owner, repo, deployment.id, state, description) + + const summary = lib.formatRunSummary(environment, results) + core.setOutput('deployment-id', String(deployment.id)) + core.setOutput('environment', environment) + core.setOutput('project-count', String(projectEntries.length)) + core.setOutput('summary', summary) + core.setOutput('manifest', JSON.stringify(manifest)) + + if (!allPassed) core.setFailed('One or more RAA tasks failed') +} diff --git a/.github/actions/moon-run/action.yml b/.github/actions/moon-run/action.yml new file mode 100644 index 0000000..35b2982 --- /dev/null +++ b/.github/actions/moon-run/action.yml @@ -0,0 +1,51 @@ +# .github/actions/moon-run/action.yml +# +# Usage (affected against remote): +# - uses: ./.github/actions/moon-run +# with: +# task: ":lint" +# affected: "remote" +# +# Usage (no affected, exact task): +# - uses: ./.github/actions/moon-run +# with: +# task: "slash-rust:publish" +# +# Usage (multiple tasks): +# - uses: ./.github/actions/moon-run +# with: +# task: ":lint :test :fmt-check" +# affected: "remote" + +name: "Moon Run" +description: "Run a moon task with standard defaults and retrospect" + +inputs: + task: + required: true + description: "Moon task(s) to run (e.g., ':lint' or 'riff-cli:publish')" + affected: + required: false + default: "" + description: "'remote' = compare against remote tracking branch, 'true' = compare against default branch, '' = no affected filtering" + +runs: + using: composite + steps: + - name: Moon Run + shell: bash + run: | + affected_flag="" + if [ "${{ inputs.affected }}" = "remote" ]; then + affected_flag="--affected remote" + elif [ "${{ inputs.affected }}" = "true" ]; then + affected_flag="--affected" + fi + moon run --color ${{ inputs.task }} $affected_flag + + - uses: appthrust/moon-ci-retrospect@v1 + if: success() || failure() + + - run: echo "${{ steps.raa.outputs.summary }}" >> $GITHUB_STEP_SUMMARY + shell: bash + if: always() diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 0000000..a0a73de --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,61 @@ +# .github/actions/setup/action.yml +# +# Usage: +# - uses: ./.github/actions/setup +# with: +# cache-cargo: "true" +# cache-pnpm: "flase" +# +# Usage (minimal, no project caches): +# - uses: ./.github/actions/setup + +name: "Setup" +description: "Checkout, setup Moon/proto (lazy install), restore caches" + +inputs: + fetch-depth: + default: "0" + description: "Git fetch depth (0 for full history, needed for moon ci affected detection)" + cache-cargo: + default: "true" + description: "Restore Cargo registry, git, and target caches" + cache-pnpm: + default: "true" + description: "Restore pnpm store cache" + +runs: + using: composite + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: ${{ inputs.fetch-depth }} + + - uses: moonrepo/setup-toolchain@v0 + with: + auto-install: true + + - uses: actions/cache@v4 + with: + path: .moon/cache + key: moon-${{ runner.os }}-${{ hashFiles('.moon/workspace.yml', '.moon/tasks/*.yml', '.moon/toolchains.yml') }} + restore-keys: | + moon-${{ runner.os }}- + + - uses: actions/cache@v4 + if: inputs.cache-cargo == 'true' + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + cargo-${{ runner.os }}- + + - uses: actions/cache@v4 + if: inputs.cache-pnpm == 'true' + with: + path: ~/.local/share/pnpm/store + key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + pnpm-${{ runner.os }}- diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..a9ac0a8 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,108 @@ +name: Trunk Pipeline + +on: + push: + branches: [main] + +concurrency: + group: trunk-${{ github.sha }} + cancel-in-progress: false + +jobs: + config: + name: CI Strategy + runs-on: ubuntu-latest + outputs: + ci-matrix: ${{ steps.ci-matrix.outputs.matrix }} + runner-count: ${{ steps.ci-matrix.outputs.count }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: ./.github/actions/setup + - id: ci-matrix + uses: ./.github/actions/moon-ci-matrix + + ci: + name: Moon CI + needs: [config] + if: needs.config.outputs.runner-count > 0 + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + shard: ${{ fromJson(needs.config.outputs.ci-matrix) }} + + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup + - uses: ./.github/actions/moon-ci + with: + access-token: ${{ secrets.GITHUB_TOKEN }} + + audit: + name: Audit & SCA + needs: [ci] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup + - name: Run Moon Audit Tasks (OSV, Cargo, Gitleaks) + run: moon run :audit + + codeql: + name: CodeQL + needs: [ci] + runs-on: ubuntu-latest + permissions: + security-events: write + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup + with: + cache-cargo: "false" + cache-pnpm: "false" + - uses: github/codeql-action/init@v3 + with: + languages: javascript + - uses: github/codeql-action/autobuild@v3 + - uses: github/codeql-action/analyze@v3 + + semgrep: + name: Semgrep + needs: [ci] + runs-on: ubuntu-latest + permissions: + security-events: write + container: + image: returntocorp/semgrep + steps: + - uses: actions/checkout@v4 + - name: Run Semgrep scan + run: | + semgrep ci \ + --sarif --output=semgrep.sarif \ + --config="p/default" \ + --config="p/security-audit" \ + --config="p/secrets" + env: + SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: semgrep.sarif + category: semgrep + + release-please: + name: Release Please + needs: [ci, audit, codeql, semgrep] + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: googleapis/release-please-action@v4 + with: + manifest-file: .release-please-manifest.json + config-file: release-please-config.json diff --git a/.github/workflows/pr-close.yml b/.github/workflows/pr-close.yml new file mode 100644 index 0000000..593ed17 --- /dev/null +++ b/.github/workflows/pr-close.yml @@ -0,0 +1,26 @@ +name: PR Review Teardown + +on: + pull_request_target: + types: [closed] + +jobs: + teardown: + name: Review Teardown + runs-on: ubuntu-latest + permissions: + deployments: write + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup + + - id: raa + uses: ./.github/actions/moon-raa + with: + command: rm + pr-number: ${{ github.event.pull_request.number }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Summary + if: always() + run: echo '${{ steps.raa.outputs.summary }}' >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 3adee4f..285323c 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -1,85 +1,123 @@ -name: PR Pipeline +name: Pull Request Pipeline on: pull_request: - types: [opened, synchronize, reopened, closed, labeled] - workflow_dispatch: - inputs: - review-app: - description: Build and deploy review app artifacts - type: boolean - default: false + types: [opened, synchronize, reopened, ready_for_review] concurrency: - group: pr-${{ github.event.pull_request.number || github.run_id }} + group: pr-${{ github.event.pull_request.number }} cancel-in-progress: true jobs: + config: + name: CI Strategy + runs-on: ubuntu-latest + outputs: + ci-matrix: ${{ steps.ci-matrix.outputs.matrix }} + runner-count: ${{ steps.ci-matrix.outputs.count }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: ./.github/actions/setup + - id: ci-matrix + uses: ./.github/actions/moon-ci-matrix + ci: - if: github.event.action != 'closed' name: Moon CI + needs: [config] + if: needs.config.outputs.runner-count > 0 + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + shard: ${{ fromJson(needs.config.outputs.ci-matrix) }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: ./.github/actions/setup + - uses: ./.github/actions/moon-ci + with: + access-token: ${{ secrets.GITHUB_TOKEN }} + + audit: + name: Audit & SCA + needs: [ci] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/moon-setup - - run: moon ci + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: ./.github/actions/setup + - name: Run Moon Audit Tasks (OSV, Cargo, Gitleaks) + run: moon run :audit codeql: - if: github.event.action != 'closed' name: CodeQL + needs: [ci] runs-on: ubuntu-latest permissions: security-events: write steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/moon-setup + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup with: - cache: "false" + cache-cargo: "false" + cache-pnpm: "false" - uses: github/codeql-action/init@v3 with: - languages: javascript,go + languages: javascript - uses: github/codeql-action/autobuild@v3 - uses: github/codeql-action/analyze@v3 - pr-title: - if: github.event.action != 'closed' - name: PR Title - runs-on: ubuntu-latest - steps: - - uses: amannn/action-semantic-pull-request@v5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - review-open: - if: | - github.event.action != 'closed' && ( - github.event.inputs.review-app == 'true' || - contains(github.event.pull_request.labels.*.name, 'review-app') - ) - name: Review App Deploy + semgrep: + name: Semgrep needs: [ci] runs-on: ubuntu-latest permissions: - deployments: write - contents: write + security-events: write + container: + image: returntocorp/semgrep steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/moon-setup - - uses: ./.github/actions/raa-open + - uses: actions/checkout@v4 + - name: Run Semgrep scan + run: | + semgrep ci \ + --sarif --output=semgrep.sarif \ + --config="p/default" \ + --config="p/security-audit" \ + --config="p/secrets" + env: + SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v3 + if: always() with: - pr-number: ${{ github.event.pull_request.number }} - github-token: ${{ secrets.GITHUB_TOKEN }} + sarif_file: semgrep.sarif + category: semgrep - review-teardown: - if: github.event.action == 'closed' - name: Review App Teardown + # Review App & Artifacts (RAA) + review-open: + name: Review Build + if: contains(github.event.pull_request.labels.*.name, 'review-app') runs-on: ubuntu-latest + needs: [ci] permissions: deployments: write - contents: write steps: - uses: actions/checkout@v4 - - uses: ./.github/actions/raa-teardown with: + fetch-depth: 0 + - uses: ./.github/actions/setup + + - id: raa + uses: ./.github/actions/moon-raa + with: + command: run pr-number: ${{ github.event.pull_request.number }} github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Summary + if: always() + run: echo '${{ steps.raa.outputs.summary }}' >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 689fe95..a252195 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,34 +1,102 @@ -name: Publish +name: Publish Distribution on: release: types: [published] + workflow_dispatch: + inputs: + scope: + description: "What to publish/review" + required: true + type: choice + options: + - affected + - all + snapshot: + description: "Run as a snapshot build (no registry publish)" + required: true + type: boolean + default: true jobs: + # --------------------------------------------------------- + # 1. Routing Job: Determine Task & Environment + # --------------------------------------------------------- + router: + name: Determine Target + runs-on: ubuntu-latest + outputs: + task: ${{ steps.parse.outputs.task }} + target: ${{ steps.parse.outputs.target }} + environment: ${{ steps.parse.outputs.environment }} + steps: + - id: parse + run: | + # Default values for Workflow Dispatch (Manual Trigger) + TASK="publish" + TARGET="" + ENV="" + + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + if [ "${{ inputs.snapshot }}" == "true" ]; then + TASK="review" + fi + + if [ "${{ inputs.scope }}" == "affected" ]; then + TARGET=":$TASK --affected" + else + TARGET=":$TASK" + fi + fi + + # If triggered by an actual GitHub Release event + if [ "${{ github.event_name }}" == "release" ]; then + TAG="${{ github.event.release.tag_name }}" + COMP=$(echo "$TAG" | sed -E 's/-v[0-9].*//') + + # Check if this is a pre-release (review app) + if [ "${{ github.event.release.prerelease }}" == "true" ]; then + TASK="review" + ENV="" # No manual gate needed for review + else + TASK="publish" + ENV="release" # Require production manual approval + fi + + TARGET="$COMP:$TASK" + fi + + echo "task=$TASK" >> $GITHUB_OUTPUT + echo "target=$TARGET" >> $GITHUB_OUTPUT + echo "environment=$ENV" >> $GITHUB_OUTPUT + publish: - name: Publish Affected + name: Moon Run ${{ needs.router.outputs.target }} + needs: router runs-on: ubuntu-latest + environment: ${{ needs.router.outputs.environment }} + permissions: + contents: write + packages: write steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/moon-setup + - uses: actions/checkout@v4 with: - go: "true" - python: "true" + fetch-depth: 0 + - uses: ./.github/actions/setup - - run: moon run :publish --affected + - name: Execute Moon Task + run: moon run ${{ needs.router.outputs.target }} env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GORELEASER_CURRENT_TAG: ${{ github.event.release.tag_name }} - - name: Attach artifacts to release - shell: bash - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAG: ${{ github.event.release.tag_name }} + - name: Attach standalone artifacts to release + if: github.event_name == 'release' run: | if [ -d dist/ ]; then - gh release upload "$TAG" dist/* --clobber + gh release upload "${{ github.event.release.tag_name }}" dist/* --clobber fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 819fcb3f39093d5e6c3ede2fe90e07c6eb7012bc Mon Sep 17 00:00:00 2001 From: tomdavidson Date: Wed, 18 Mar 2026 00:21:35 -0600 Subject: [PATCH 15/16] ci: lint & fmt configs --- .clippy.toml | 12 +++ .editorconfig | 21 +++++ .oxlintrc.json | 221 ++++++++++++++++++++++++++++++++++++++++++++++ .rustfmt.toml | 36 ++++++++ eslint.config.mjs | 187 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 477 insertions(+) create mode 100644 .clippy.toml create mode 100644 .editorconfig create mode 100644 .oxlintrc.json create mode 100644 .rustfmt.toml create mode 100644 eslint.config.mjs diff --git a/.clippy.toml b/.clippy.toml new file mode 100644 index 0000000..995cf22 --- /dev/null +++ b/.clippy.toml @@ -0,0 +1,12 @@ +# Enforce “small functions” (Clean Code: 5–25 lines, allow a bit for Rust formatting) +too-many-lines-threshold = 30 + +# Enforce “2–3 params typical; 4+ suggests a data object” (Clean Code) +too-many-arguments-threshold = 4 + +# Complexity guardrail (closest analogue TS complexity/max-depth posture) +cognitive-complexity-threshold = 8 + +# Optional: approximate “max nesting depth 1–2” +# Note: this is structural nesting +excessive-nesting-threshold = 3 diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..18fae85 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ + + +text +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.rs] +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab \ No newline at end of file diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..397c3f5 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,221 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + // Built-in plugins only (no UI/react/etc). + "plugins": ["typescript", "import", "unicorn", "promise", "node", "oxc"], + "options": { + // Now that you installed tsgolint, keep this on. + // Equivalent to CLI: --type-aware + "typeAware": true, + // Optional (more expensive): TS compiler diagnostics. + // If you want "tsc-like" feedback during lint, turn on. + "typeCheck": true + }, + "categories": { + // Maximize baseline coverage. + "correctness": "error", + "suspicious": "warn", + // Keep these explicit (don't enable wholesale), to avoid noisy/subjective churn: + "perf": "off", + "pedantic": "off", + "style": "off", + "restriction": "off", + "nursery": "off" + }, + "rules": { + // ───────────────────────────────────────────────────────────── + // STRUCTURE / COMPLEXITY + // These look like formatting but enforce cognitive load limits. + // dprint controls how code *looks*; these control how code *thinks*. + // ───────────────────────────────────────────────────────────── + // SEMANTIC: caps branching depth, enforces guard-clause-first style. + // dprint cannot reason about control flow nesting. + "max-depth": ["error", { "max": 2 }], + // SEMANTIC: caps function size as a complexity proxy. + // dprint line-wrapping changes visual lines, but this counts logical lines + // (skipBlankLines + skipComments), which dprint doesn't track. + "max-lines-per-function": ["warn", { "max": 25, "skipBlankLines": true, "skipComments": true }], + // SEMANTIC: caps cyclomatic complexity (branch count). + // Pure control-flow metric, no formatting tool can replicate this. + "complexity": ["warn", { "max": 5 }], + // SEMANTIC: caps parameter count to enforce small function signatures. + // dprint wraps params visually; this limits how many you can have. + "max-params": ["error", { "max": 3 }], + // SEMANTIC: enforces guard-clause pattern (early return, no else after return). + // This is control-flow shape, not whitespace. dprint cannot decide when + // an else block is logically redundant. + "no-else-return": "error", + // SEMANTIC: bans nested ternaries to prevent unreadable conditional chains. + // dprint formats ternaries but cannot judge nesting depth or readability. + "no-nested-ternary": "error", + // DISABLED: dprint handles ternary formatting; this rule's autofix + // rewrites `x ? true : false` to `!!x` which is a style preference + // that overlaps with typescript/strict-boolean-expressions. + // "no-unneeded-ternary": "error", + // ───────────────────────────────────────────────────────────── + // IMMUTABILITY SUPPORT (closest oxlint can get to functional/immutable-data) + // ───────────────────────────────────────────────────────────── + "no-var": "error", + "prefer-const": "error", + "no-param-reassign": "error", + // DISABLED: pure formatting preference. dprint handles spread formatting + // via its own rules. The *semantic* immutability enforcement comes from + // functional/immutable-data in ESLint. + // "prefer-object-spread": "error", + "no-object-constructor": "error", + "no-array-constructor": "error", + // Perf+immutability footguns + "oxc/no-accumulating-spread": "warn", + // Spread over Object.assign + "unicorn/prefer-spread": "warn", + // ───────────────────────────────────────────────────────────── + // NO NULLS / OPTION-ISH STYLE + // ───────────────────────────────────────────────────────────── + "unicorn/no-null": "warn", + "unicorn/no-useless-undefined": "warn", + "typescript/prefer-nullish-coalescing": "warn", + "typescript/prefer-optional-chain": "warn", + // ───────────────────────────────────────────────────────────── + // DECLARATIVE ITERATION (nudges; ESLint functional still bans loops fully) + // ───────────────────────────────────────────────────────────── + "unicorn/no-for-loop": "error", + "unicorn/no-array-for-each": "warn", + "unicorn/prefer-array-flat-map": "error", + "unicorn/prefer-array-find": "error", + "unicorn/prefer-array-some": "error", + "unicorn/prefer-array-index-of": "error", + "unicorn/no-array-reduce": "allow", + "unicorn/no-array-push-push": "warn", + "unicorn/prefer-set-has": "warn", + // ───────────────────────────────────────────────────────────── + // ASYNC / PROMISE SAFETY (type-aware + runtime-ish) + // ───────────────────────────────────────────────────────────── + "typescript/no-floating-promises": ["error", { "ignoreVoid": true }], + "typescript/no-misused-promises": "error", + "typescript/promise-function-async": "off", + "typescript/return-await": "error", + "unicorn/no-await-in-promise-methods": "error", + "unicorn/no-single-promise-in-promise-methods": "error", + "unicorn/no-unnecessary-await": "error", + "unicorn/no-thenable": "error", + "promise/no-new-statics": "error", + "promise/no-multiple-resolved": "warn", + "promise/valid-params": "error", + // Supports ResultAsync-first stance: loops should use ResultAsync.combine + // or similar, not sequential awaits. + "no-await-in-loop": "warn", + // ───────────────────────────────────────────────────────────── + // TYPE SAFETY / UNION EXHAUSTIVENESS + // ───────────────────────────────────────────────────────────── + "typescript/switch-exhaustiveness-check": "error", + "typescript/strict-boolean-expressions": "warn", + "typescript/no-explicit-any": "warn", + "typescript/no-unsafe-return": "error", + "typescript/no-unsafe-argument": "error", + // "Max coverage" extras (may add noise, but reduce unsafe-any creep) + "typescript/no-unsafe-assignment": "warn", + "typescript/no-unsafe-call": "warn", + "typescript/no-unsafe-member-access": "warn", + "typescript/no-non-null-assertion": "warn", + "typescript/no-unnecessary-type-assertion": "warn", + "typescript/no-unnecessary-type-arguments": "warn", + // Discriminated unions over interfaces: enforce `type` keyword + "typescript/consistent-type-definitions": ["error", "type"], + // Keep TS escape hatches honest + "typescript/ban-ts-comment": [ + "warn", + { + "ts-ignore": "allow-with-description", + "ts-expect-error": "allow-with-description", + "ts-nocheck": true, + "minimumDescriptionLength": 5 + } + ], + // ───────────────────────────────────────────────────────────── + // MODULE HYGIENE / IMPORTS + // ───────────────────────────────────────────────────────────── + "import/no-default-export": "error", + "import/no-cycle": "error", + "import/no-self-import": "error", + // DISABLED: dprint handles import deduplication and sorting via + // module.sortImportDeclarations and importDeclaration.sortNamedImports. + // "import/no-duplicates": "warn", + "typescript/consistent-type-imports": "warn", + // ───────────────────────────────────────────────────────────── + // NODE/BUN SERVER HYGIENE + // ───────────────────────────────────────────────────────────── + "node/no-exports-assign": "error", + "node/no-new-require": "error", + "node/no-path-concat": "error", + "node/no-process-env": "warn", + "unicorn/prefer-node-protocol": "error", + // ───────────────────────────────────────────────────────────── + // ERROR QUALITY (even if you avoid throw, shells still throw sometimes) + // ───────────────────────────────────────────────────────────── + "unicorn/error-message": "error", + "unicorn/throw-new-error": "error", + "unicorn/prefer-type-error": "warn", + "no-throw-literal": "error", + "prefer-promise-reject-errors": "error", + // ───────────────────────────────────────────────────────────── + // FP-ADJACENT "NO CLASSES AS NAMESPACES" HELPERS + // (not a full ban; ESLint functional still does the heavy lifting) + // ───────────────────────────────────────────────────────────── + "unicorn/no-static-only-class": "error", + "typescript/no-extraneous-class": "warn", + "class-methods-use-this": "warn", + // ───────────────────────────────────────────────────────────── + // GENERAL HYGIENE + // ───────────────────────────────────────────────────────────── + "no-console": ["warn", { "allow": ["error", "warn"] }], + "no-debugger": "error", + "no-eval": "error", + "eqeqeq": "error", + "no-implicit-coercion": "error", + "no-shadow": "warn", + "no-magic-numbers": ["warn", { "ignore": [0, 1, -1], "enforceConst": true }], + "no-void": ["error", { "allowAsStatement": true }], + "unicorn/no-empty-file": "error", + // Subtle "dead code" / refactor safety + "no-useless-assignment": "warn", + // Small performance-ish correctness + "oxc/approx-constant": "warn", + "no-useless-call": "warn", + // (Opinionated restrictions that typically match FP/clarity) + "no-labels": "error", + "no-with": "error", + "no-sequences": "error", + "no-extend-native": "error", + "no-proto": "error", + "no-iterator": "error", + "no-new-wrappers": "error", + "no-bitwise": "warn", + "arrow-body-style": ["warn", "as-needed"], + "unicorn/prefer-ternary": ["warn", "only-single-line"], + // Positive predicates: prefer `if (ready)` over `if (!notReady)` + "unicorn/no-negated-condition": "warn" + }, + "overrides": [{ + // TESTS: loosen ergonomics + "files": ["*.spec.ts", "*.test.ts", "src/test/**/*.ts"], + "rules": { + "max-params": "off", + "max-depth": "off", + "max-lines-per-function": "off", + "complexity": "off", + "no-magic-numbers": "off", + "no-param-reassign": "off", + "typescript/no-explicit-any": "off", + "typescript/no-unsafe-argument": "off", + "typescript/no-unsafe-return": "off", + "typescript/no-unsafe-assignment": "off", + "typescript/no-unsafe-call": "off", + "typescript/no-unsafe-member-access": "off", + "typescript/no-non-null-assertion": "off", + "unicorn/no-null": "off", + "unicorn/no-array-reduce": "off", + "unicorn/consistent-function-scoping": "off", + "node/no-process-env": "off" + } + }], + "ignorePatterns": ["node_modules", "dist"] +} diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..dc9e5fa --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,36 @@ +# ── Core: match dprint.json + .editorconfig ───────────────────── +# Match dprint lineWidth +max_width = 110 +# Match editorconfig indent_style = space +hard_tabs = false +# Rust community standard (differs from your TS indent of 2, intentionally) +tab_spaces = 4 +# Match editorconfig end_of_line = lf +newline_style = "Unix" +# Like TS shorthand { id } instead of { id: id } +use_field_init_shorthand = true + +# ── Expression density: match dprint preferSingleLine ─────────── +# Rustfmt defaults to breaking things across lines aggressively. +# These keep short expressions on one line (same intent as dprint's preferSingleLine). +# Allow short fn bodies on one line +fn_single_line = true +# Maximize single-line formatting for structs, calls, arrays, chains +use_small_heuristics = "Max" + +# ── Imports: match dprint's sort behavior ──────────────────────── +# Alphabetical sort (matches dprint module.sortImportDeclarations) +reorder_imports = true +# Keep mod declarations sorted too +reorder_modules = true + +# ── Unstable (require nightly rustfmt or cargo +nightly fmt) ───── +# These are high-value options worth running nightly fmt for. +# If you must stay on stable, comment these out; they'll emit warnings but not break. +unstable_features = true +# Merge imports per crate (reduces visual noise) +imports_granularity = "Crate" +# Group: std → external crates → crate (clean architecture readability) +group_imports = "StdExternalCrate" +# Keep doc examples formatted +format_code_in_doc_comments = true diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..4c2075a --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,187 @@ +import boundaries from 'eslint-plugin-boundaries' +import functional from 'eslint-plugin-functional' +import oxlint from 'eslint-plugin-oxlint' +import preferArrowFunctions from 'eslint-plugin-prefer-arrow-functions' +import { defineConfig } from 'eslint/config' +import tseslint from 'typescript-eslint' + +// ── Boundary mode ────────────────────────────────────────────── +// "feature" → src/orders/domain.ts, src/orders/use-cases.ts, ... +// "layer" → src/domain/orders.ts, src/application/orders.ts, ... +// "none" → no boundary enforcement +const BOUNDARY_MODE = 'feature' + +// ── Boundary element definitions per mode ────────────────────── +const boundaryElements = { + feature: [ + { type: 'domain', pattern: ['src/*/domain.ts', 'src/*/domain/**'] }, + { type: 'application', pattern: ['src/*/use-cases.ts', 'src/*/logic.ts', 'src/*/ports.ts'] }, + { type: 'infrastructure', pattern: ['src/*/repo.ts', 'src/*/io.ts', 'src/*/adapter.ts', 'src/cli/**'] }, + { type: 'shared', pattern: ['src/shared/**'] }, + { type: 'composition', pattern: ['src/main.ts'] }, + { type: 'test', pattern: ['src/**/*.spec.ts', 'src/test/**'] }, + ], + layer: [ + { type: 'domain', pattern: ['src/domain/**'] }, + { type: 'application', pattern: ['src/application/**'] }, + { type: 'infrastructure', pattern: ['src/infrastructure/**'] }, + { type: 'shared', pattern: ['src/shared/**'] }, + { type: 'composition', pattern: ['src/main.ts'] }, + { type: 'test', pattern: ['src/**/*.spec.ts', 'src/test/**'] }, + ], +} + +// Dependency inversion: inner layers cannot import outer layers +const boundaryRules = [ + { from: 'domain', allow: ['domain', 'shared'] }, + { from: 'application', allow: ['domain', 'application', 'shared'] }, + { from: 'infrastructure', allow: ['domain', 'application', 'infrastructure', 'shared'] }, + { from: 'composition', allow: ['domain', 'application', 'infrastructure', 'shared', 'composition'] }, + { from: 'test', allow: ['domain', 'application', 'infrastructure', 'shared', 'test'] }, +] + +// Domain file globs (used for purity + FP strictness overrides) +const domainFiles = BOUNDARY_MODE === 'feature' ? + ['src/*/domain.ts', 'src/*/domain/**/*.ts'] : + ['src/domain/**/*.ts'] + +// Infra file globs (relaxed FP rules) +const infraFiles = BOUNDARY_MODE === 'feature' ? + ['src/*/repo.ts', 'src/*/io.ts', 'src/*/adapter.ts', 'src/cli/**/*.ts'] : + ['src/infrastructure/**/*.ts'] + +// ── Functional rules (global defaults) ───────────────────────── +// These are the core FP rules that oxlint cannot replicate. +// ESLint owns all functional/* enforcement. +const functionalRules = { + 'functional/no-loop-statements': 'error', + 'functional/no-try-statements': 'error', + 'functional/no-throw-statements': 'error', + 'functional/no-let': ['error', { allowInForLoopInit: false }], + 'functional/no-classes': 'error', + 'functional/no-this-expressions': 'error', + 'functional/immutable-data': ['warn', { ignoreImmediateMutation: true, ignoreClasses: true }], + 'functional/prefer-readonly-type': 'warn', + 'functional/no-expression-statements': ['error', { + ignoreVoid: true, + ignoreCodePattern: ['^expect', '^assert'], + }], + 'functional/no-return-void': 'error', +} + +// ── AST selector bans (oxlint cannot do ESQuery selectors) ───── +const restrictedSyntax = ['error', { + selector: ":function > Identifier.params[typeAnnotation.typeAnnotation.type='TSBooleanKeyword']", + message: 'Boolean params are banned. Split into separate functions.', +}, { + selector: "CallExpression[callee.property.name='_unsafeUnwrap']", + message: 'Use .map(), .andThen(), or .match() instead.', +}, { + selector: "CallExpression[callee.property.name='_unsafeUnwrapErr']", + message: 'Use .mapErr(), .andThen(), or .match() instead.', +}] + +// ── Domain purity: ban IO imports in domain files ────────────── +const domainImportBans = { + patterns: [{ + group: ['fs', 'fs/*', 'path', 'http', 'https', 'net', 'child_process', 'crypto'], + message: 'Domain must be pure. No Node.js IO.', + }, { + group: ['express', 'fastify', 'koa', 'hono', 'elysia'], + message: 'Domain must be pure. No HTTP frameworks.', + }, { + group: ['pg', 'mysql*', 'redis', 'ioredis', 'mongoose', '@prisma/*', 'kysely'], + message: 'Domain must be pure. No database drivers.', + }, { + group: ['@aws-sdk/*', '@azure/*', '@google-cloud/*'], + message: 'Domain must be pure. No cloud SDKs.', + }], +} + +// ── Build config array ───────────────────────────────────────── +const config = [ + { ignores: ['node_modules/**', 'dist/**', 'eslint.config.mjs'] }, + + // Global: all source files + { + files: ['src/**/*.ts'], + languageOptions: { + parser: tseslint.parser, + parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname }, + }, + plugins: { + functional, + 'prefer-arrow-functions': preferArrowFunctions, + ...(BOUNDARY_MODE !== 'none' && { boundaries }), + }, + ...(BOUNDARY_MODE !== 'none' && { settings: { 'boundaries/elements': boundaryElements[BOUNDARY_MODE] } }), + rules: { + ...functionalRules, + 'no-restricted-syntax': restrictedSyntax, + + // Arrow function enforcement: ESLint owns the *semantic* rule + // (must use arrows). dprint owns arrow *formatting* (parens, wrapping). + 'prefer-arrow-functions/prefer-arrow-functions': ['error', { + allowNamedFunctions: false, + classPropertiesAllowed: false, + disallowPrototype: true, + returnStyle: 'unchanged', + singleReturnOnly: false, + }], + + // Boundaries (only when enabled) + ...(BOUNDARY_MODE !== 'none' && + { 'boundaries/element-types': ['error', { default: 'disallow', rules: boundaryRules }] }), + }, + }, + + // Domain purity: ban IO imports + { + files: domainFiles, + rules: { + 'no-restricted-imports': ['error', domainImportBans], + 'functional/prefer-readonly-type': 'error', + }, + }, + + // Tests: fully relax FP rules + { + files: ['src/**/*.spec.ts', 'src/test/**/*.ts'], + rules: { + 'functional/no-let': 'off', + 'functional/no-loop-statements': 'off', + 'functional/no-try-statements': 'off', + 'functional/no-throw-statements': 'off', + 'functional/no-expression-statements': 'off', + 'functional/no-return-void': 'off', + 'functional/immutable-data': 'off', + 'functional/no-classes': 'off', + 'functional/no-this-expressions': 'off', + 'no-restricted-syntax': 'off', + }, + }, + + // Infrastructure: imperative shell allowances + { + files: infraFiles, + rules: { + 'functional/no-let': 'warn', + 'functional/no-loop-statements': 'warn', + 'functional/no-try-statements': 'off', + 'functional/no-throw-statements': 'off', + 'functional/no-expression-statements': 'off', + 'functional/no-return-void': 'off', + 'functional/immutable-data': 'off', + 'functional/no-classes': ['error', { + ignoreIdentifierPattern: '^.*(Controller|Adapter|Module|Client|Provider|Gateway)$', + }], + 'functional/no-this-expressions': 'off', + }, + }, + + // Deduplicate: turn off anything oxlint already handles + ...oxlint.buildFromOxlintConfigFile('./.oxlintrc.json'), +] + +// eslint-disable no-default-export +export default defineConfig(...config) From 54a4b123ab3f46e19c9ba631b15a06087ca179cc Mon Sep 17 00:00:00 2001 From: tomdavidson Date: Wed, 18 Mar 2026 00:25:51 -0600 Subject: [PATCH 16/16] ci: repo hygien --- .github/actions/moon-setup/action.yml | 72 - .github/actions/raa-open/action.yml | 32 - .github/actions/raa-teardown/action.yml | 36 - .github/workflows/deploy.yml | 19 - .github/workflows/release.yml | 19 - .github/workflows/scheduled.yml | 39 - .moon/tasks/go.yml | 36 - .moon/tasks/python.yml | 42 - .moon/tasks/security.yml | 22 +- moon.yml | 25 + pnpm-lock.yaml | 1839 +++++++++++++++++++++++ pnpm-workspace.yaml | 1 + 12 files changed, 1876 insertions(+), 306 deletions(-) delete mode 100644 .github/actions/moon-setup/action.yml delete mode 100644 .github/actions/raa-open/action.yml delete mode 100644 .github/actions/raa-teardown/action.yml delete mode 100644 .github/workflows/deploy.yml delete mode 100644 .github/workflows/release.yml delete mode 100644 .github/workflows/scheduled.yml delete mode 100644 .moon/tasks/go.yml delete mode 100644 .moon/tasks/python.yml create mode 100644 moon.yml create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml diff --git a/.github/actions/moon-setup/action.yml b/.github/actions/moon-setup/action.yml deleted file mode 100644 index 23a83fc..0000000 --- a/.github/actions/moon-setup/action.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Moon Setup -description: Checkout, install toolchains, restore caches, setup Moon - -inputs: - fetch-depth: - description: Git fetch depth - default: "0" - cache: - description: Enable dependency caching - default: "true" - rust: - description: Install Rust toolchain - default: "true" - bun: - description: Install Bun runtime - default: "true" - go: - description: Install Go toolchain - default: "false" - python: - description: Install Python toolchain - default: "false" - -runs: - using: composite - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: ${{ inputs.fetch-depth }} - - - uses: moonrepo/setup-toolchain@v1 - with: - auto-install: true - - - if: inputs.rust == 'true' - uses: dtolnay/rust-toolchain@stable - with: - targets: wasm32-unknown-unknown,wasm32-wasip2 - - - if: inputs.go == 'true' - uses: actions/setup-go@v5 - with: - go-version-file: go.work - - - if: inputs.python == 'true' - uses: actions/setup-python@v5 - with: - python-version-file: .python-version - - - if: inputs.cache == 'true' && inputs.rust == 'true' - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target/ - key: cargo-${{ runner.os }}-${{ hashFiles('Cargo.lock') }} - restore-keys: cargo-${{ runner.os }}- - - - if: inputs.cache == 'true' && inputs.bun == 'true' - uses: actions/cache@v4 - with: - path: ~/.bun/install/cache - key: bun-${{ runner.os }}-${{ hashFiles('bun.lockb') }} - restore-keys: bun-${{ runner.os }}- - - - if: inputs.cache == 'true' - uses: actions/cache@v4 - with: - path: .moon/cache - key: moon-${{ runner.os }}-${{ github.sha }} - restore-keys: moon-${{ runner.os }}- diff --git a/.github/actions/raa-open/action.yml b/.github/actions/raa-open/action.yml deleted file mode 100644 index aee8ca3..0000000 --- a/.github/actions/raa-open/action.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Review App Open -description: Provision review environment and deploy snapshot artifacts - -inputs: - pr-number: - description: PR number - required: true - github-token: - description: GitHub token for deployments API - required: true - -runs: - using: composite - steps: - - uses: chrnorm/deployment-action@v2 - id: deployment - with: - token: ${{ inputs.github-token }} - environment: review-pr-${{ inputs.pr-number }} - transient-environment: true - auto-inactive: false - - - shell: bash - run: moon run :publish -- --snapshot - env: - REVIEW_PR: ${{ inputs.pr-number }} - - - uses: chrnorm/deployment-status@v2 - with: - token: ${{ inputs.github-token }} - deployment-id: ${{ steps.deployment.outputs.deployment_id }} - state: success diff --git a/.github/actions/raa-teardown/action.yml b/.github/actions/raa-teardown/action.yml deleted file mode 100644 index 83e16f5..0000000 --- a/.github/actions/raa-teardown/action.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Review App Teardown -description: Deactivate deployment and clean up review environment - -inputs: - pr-number: - description: PR number - required: true - github-token: - description: GitHub token for deployments API - required: true - -runs: - using: composite - steps: - - shell: bash - env: - GH_TOKEN: ${{ inputs.github-token }} - PR_NUMBER: ${{ inputs.pr-number }} - run: | - ENV_NAME="review-pr-${PR_NUMBER}" - - # Mark all deployments inactive - DEPLOYMENT_IDS=$(gh api "repos/${{ github.repository }}/deployments?environment=${ENV_NAME}" \ - --jq '.[].id') - - for id in $DEPLOYMENT_IDS; do - gh api "repos/${{ github.repository }}/deployments/${id}/statuses" \ - -f state=inactive -f description="PR closed" - done - - # Delete pre-release assets if they exist - RELEASE_TAG="review-pr-${PR_NUMBER}" - gh release delete "$RELEASE_TAG" --yes --cleanup-tag 2>/dev/null || true - - # Remove the environment - gh api -X DELETE "repos/${{ github.repository }}/environments/${ENV_NAME}" 2>/dev/null || true diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 4b44e6f..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Deploy - -on: - push: - branches: [main] - paths: - - "website/**" - -jobs: - deploy: - name: Deploy Website - runs-on: ubuntu-latest - environment: production - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/moon-setup - with: - rust: "false" - - run: moon run website:deploy diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 8594206..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Release - -on: - push: - branches: [main] - -permissions: - contents: write - pull-requests: write - -jobs: - release-please: - name: Release Please - runs-on: ubuntu-latest - steps: - - uses: googleapis/release-please-action@v4 - with: - manifest-file: .release-please-manifest.json - config-file: release-please-config.json diff --git a/.github/workflows/scheduled.yml b/.github/workflows/scheduled.yml deleted file mode 100644 index e006b84..0000000 --- a/.github/workflows/scheduled.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Scheduled Security - -on: - schedule: - - cron: "0 6 * * 1" - workflow_dispatch: - -jobs: - semgrep: - name: Semgrep - runs-on: ubuntu-latest - container: - image: semgrep/semgrep - steps: - - uses: actions/checkout@v4 - - run: semgrep scan --config auto --error - - codeql: - name: CodeQL (scheduled) - runs-on: ubuntu-latest - permissions: - security-events: write - steps: - - uses: actions/checkout@v4 - - uses: github/codeql-action/init@v3 - with: - languages: javascript,go - - uses: github/codeql-action/autobuild@v3 - - uses: github/codeql-action/analyze@v3 - - fuzz: - name: Fuzz (scheduled) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/moon-setup - with: - bun: "false" - - run: echo "moon run fuzz:run -- -max_total_time=600" diff --git a/.moon/tasks/go.yml b/.moon/tasks/go.yml deleted file mode 100644 index b2d73ae..0000000 --- a/.moon/tasks/go.yml +++ /dev/null @@ -1,36 +0,0 @@ -$schema: "https://moonrepo.dev/schemas/tasks.json" - -fileGroups: - sources: - - "**/*.go" - - "go.mod" - - "go.sum" - tests: - - "**/*_test.go" - -tasks: - lint: - command: "go" - args: ["vet", "./..."] - inputs: - - "@group(sources)" - - fmt-check: - script: "test -z \"$(gofmt -l .)\"" - inputs: - - "@group(sources)" - - test: - command: "go" - args: ["test", "./..."] - inputs: - - "@group(sources)" - - "@group(tests)" - - build: - command: "go" - args: ["build", "./..."] - inputs: - - "@group(sources)" - outputs: - - "bin" diff --git a/.moon/tasks/python.yml b/.moon/tasks/python.yml deleted file mode 100644 index d25903e..0000000 --- a/.moon/tasks/python.yml +++ /dev/null @@ -1,42 +0,0 @@ -$schema: "https://moonrepo.dev/schemas/tasks.json" - -fileGroups: - sources: - - "**/*.py" - - "pyproject.toml" - tests: - - "tests/**/*.py" - -tasks: - lint: - command: "python" - args: ["-m", "ruff", "check", "."] - inputs: - - "@group(sources)" - - fmt-check: - command: "python" - args: ["-m", "ruff", "format", ".", "--check"] - inputs: - - "@group(sources)" - - test: - command: "python" - args: ["-m", "pytest"] - inputs: - - "@group(sources)" - - "@group(tests)" - - typecheck: - command: "python" - args: ["-m", "mypy", "."] - inputs: - - "@group(sources)" - - build: - command: "python" - args: ["-m", "build"] - inputs: - - "@group(sources)" - outputs: - - "dist" diff --git a/.moon/tasks/security.yml b/.moon/tasks/security.yml index 4351730..a59e1f0 100644 --- a/.moon/tasks/security.yml +++ b/.moon/tasks/security.yml @@ -1,14 +1,14 @@ -$schema: "https://moonrepo.dev/schemas/tasks.json" +# $schema: "https://moonrepo.dev/schemas/tasks.json" -tasks: - semgrep: - command: "semgrep" - args: ["--config", "auto", "--error", "."] +# tasks: +# semgrep: +# command: "semgrep" +# args: ["--config", "auto", "--error", "."] - gitleaks: - command: "gitleaks" - args: ["detect", "--source", ".", "-v"] +# gitleaks: +# command: "gitleaks" +# args: ["detect", "--source", ".", "-v"] - tangleguard: - command: "echo" - args: ["TangleGuard placeholder \u2014 not yet implemented"] +# tangleguard: +# command: "echo" +# args: ["TangleGuard placeholder \u2014 not yet implemented"] diff --git a/moon.yml b/moon.yml new file mode 100644 index 0000000..a0d8a60 --- /dev/null +++ b/moon.yml @@ -0,0 +1,25 @@ +$schema: "https://moonrepo.dev/schemas/project.json" + +tags: + - "tooling" + +tasks: + fmt: + script: "rustup toolchain install nightly --component rustfmt -q && dprint fmt" + inputs: + - "**/*.rs" + - "**/*.toml" + - "**/*.yml" + - "**/*.yaml" + - "dprint.json" + options: + runInCI: false + + fmt-check: + script: "rustup toolchain install nightly --component rustfmt -q && dprint check" + inputs: + - "**/*.rs" + - "**/*.toml" + - "**/*.yml" + - "**/*.yaml" + - "dprint.json" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..5f04c6e --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1839 @@ +lockfileVersion: "9.0" + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + slash-parser-js: + devDependencies: + "@types/bun": + specifier: latest + version: 1.3.10 + eslint: + specifier: ^9.0.0 + version: 9.39.4 + eslint-plugin-boundaries: + specifier: ^5.0.0 + version: 5.4.0(eslint@9.39.4) + eslint-plugin-functional: + specifier: ^9.0.0 + version: 9.0.4(eslint@9.39.4)(typescript@5.9.3) + eslint-plugin-oxlint: + specifier: ^1.51.0 + version: 1.55.0 + eslint-plugin-prefer-arrow-functions: + specifier: ^3.6.0 + version: 3.9.1(eslint@9.39.4)(typescript@5.9.3) + fast-check: + specifier: ^3.0.0 + version: 3.23.2 + oxlint: + specifier: ^1.0.0 + version: 1.55.0(oxlint-tsgolint@0.16.0) + oxlint-tsgolint: + specifier: ^0.16.0 + version: 0.16.0 + typescript: + specifier: ^5.5.0 + version: 5.9.3 + +packages: + "@boundaries/elements@1.2.0": + resolution: { + integrity: sha512-W65Gum02liMd3hmNrLmDBX1u5BmRMcunouFjLXyhxHnNY4YlK1kTxsgfflZ5XBGSnPnO0MkiUzAcoGzYrlx0RQ==, + } + engines: { node: ">=18.18" } + + "@eslint-community/eslint-utils@4.9.1": + resolution: { + integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + "@eslint-community/regexpp@4.12.2": + resolution: { + integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==, + } + engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + + "@eslint/config-array@0.21.2": + resolution: { + integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/config-helpers@0.4.2": + resolution: { + integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/core@0.17.0": + resolution: { + integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/eslintrc@3.3.5": + resolution: { + integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/js@9.39.4": + resolution: { + integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/object-schema@2.1.7": + resolution: { + integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@eslint/plugin-kit@0.4.1": + resolution: { + integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@humanfs/core@0.19.1": + resolution: { + integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==, + } + engines: { node: ">=18.18.0" } + + "@humanfs/node@0.16.7": + resolution: { + integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==, + } + engines: { node: ">=18.18.0" } + + "@humanwhocodes/module-importer@1.0.1": + resolution: { + integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, + } + engines: { node: ">=12.22" } + + "@humanwhocodes/retry@0.4.3": + resolution: { + integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==, + } + engines: { node: ">=18.18" } + + "@oxlint-tsgolint/darwin-arm64@0.16.0": + resolution: { + integrity: sha512-WQt5lGwRPJBw7q2KNR0mSPDAaMmZmVvDlEEti96xLO7ONhyomQc6fBZxxwZ4qTFedjJnrHX94sFelZ4OKzS7UQ==, + } + cpu: [arm64] + os: [darwin] + + "@oxlint-tsgolint/darwin-x64@0.16.0": + resolution: { + integrity: sha512-VJo29XOzdkalvCTiE2v6FU3qZlgHaM8x8hUEVJGPU2i5W+FlocPpmn00+Ld2n7Q0pqIjyD5EyvZ5UmoIEJMfqg==, + } + cpu: [x64] + os: [darwin] + + "@oxlint-tsgolint/linux-arm64@0.16.0": + resolution: { + integrity: sha512-MPfqRt1+XRHv9oHomcBMQ3KpTE+CSkZz14wUxDQoqTNdUlV0HWdzwIE9q65I3D9YyxEnqpM7j4qtDQ3apqVvbQ==, + } + cpu: [arm64] + os: [linux] + + "@oxlint-tsgolint/linux-x64@0.16.0": + resolution: { + integrity: sha512-XQSwVUsnwLokMhe1TD6IjgvW5WMTPzOGGkdFDtXWQmlN2YeTw94s/NN0KgDrn2agM1WIgAenEkvnm0u7NgwEyw==, + } + cpu: [x64] + os: [linux] + + "@oxlint-tsgolint/win32-arm64@0.16.0": + resolution: { + integrity: sha512-EWdlspQiiFGsP2AiCYdhg5dTYyAlj6y1nRyNI2dQWq4Q/LITFHiSRVPe+7m7K7lcsZCEz2icN/bCeSkZaORqIg==, + } + cpu: [arm64] + os: [win32] + + "@oxlint-tsgolint/win32-x64@0.16.0": + resolution: { + integrity: sha512-1ufk8cgktXJuJZHKF63zCHAkaLMwZrEXnZ89H2y6NO85PtOXqu4zbdNl0VBpPP3fCUuUBu9RvNqMFiv0VsbXWA==, + } + cpu: [x64] + os: [win32] + + "@oxlint/binding-android-arm-eabi@1.55.0": + resolution: { + integrity: sha512-NhvgAhncTSOhRahQSCnkK/4YIGPjTmhPurQQ2dwt2IvwCMTvZRW5vF2K10UBOxFve4GZDMw6LtXZdC2qeuYIVQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm] + os: [android] + + "@oxlint/binding-android-arm64@1.55.0": + resolution: { + integrity: sha512-P9iWRh+Ugqhg+D7rkc7boHX8o3H2h7YPcZHQIgvVBgnua5tk4LR2L+IBlreZs58/95cd2x3/004p5VsQM9z4SA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [android] + + "@oxlint/binding-darwin-arm64@1.55.0": + resolution: { + integrity: sha512-esakkJIt7WFAhT30P/Qzn96ehFpzdZ1mNuzpOb8SCW7lI4oB8VsyQnkSHREM671jfpuBb/o2ppzBCx5l0jpgMA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [darwin] + + "@oxlint/binding-darwin-x64@1.55.0": + resolution: { + integrity: sha512-xDMFRCCAEK9fOH6As2z8ELsC+VDGSFRHwIKVSilw+xhgLwTDFu37rtmRbmUlx8rRGS6cWKQPTc47AVxAZEVVPQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [darwin] + + "@oxlint/binding-freebsd-x64@1.55.0": + resolution: { + integrity: sha512-mYZqnwUD7ALCRxGenyLd1uuG+rHCL+OTT6S8FcAbVm/ZT2AZMGjvibp3F6k1SKOb2aeqFATmwRykrE41Q0GWVw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [freebsd] + + "@oxlint/binding-linux-arm-gnueabihf@1.55.0": + resolution: { + integrity: sha512-LcX6RYcF9vL9ESGwJW3yyIZ/d/ouzdOKXxCdey1q0XJOW1asrHsIg5MmyKdEBR4plQx+shvYeQne7AzW5f3T1w==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm] + os: [linux] + + "@oxlint/binding-linux-arm-musleabihf@1.55.0": + resolution: { + integrity: sha512-C+8GS1rPtK+dI7mJFkqoRBkDuqbrNihnyYQsJPS9ez+8zF9JzfvU19lawqt4l/Y23o5uQswE/DORa8aiXUih3w==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm] + os: [linux] + + "@oxlint/binding-linux-arm64-gnu@1.55.0": + resolution: { + integrity: sha512-ErLE4XbmcCopA4/CIDiH6J1IAaDOMnf/KSx/aFObs4/OjAAM3sFKWGZ57pNOMxhhyBdcmcXwYymph9GwcpcqgQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [linux] + libc: [glibc] + + "@oxlint/binding-linux-arm64-musl@1.55.0": + resolution: { + integrity: sha512-/kp65avi6zZfqEng56TTuhiy3P/3pgklKIdf38yvYeJ9/PgEeRA2A2AqKAKbZBNAqUzrzHhz9jF6j/PZvhJzTQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [linux] + libc: [musl] + + "@oxlint/binding-linux-ppc64-gnu@1.55.0": + resolution: { + integrity: sha512-A6pTdXwcEEwL/nmz0eUJ6WxmxcoIS+97GbH96gikAyre3s5deC7sts38ZVVowjS2QQFuSWkpA4ZmQC0jZSNvJQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [ppc64] + os: [linux] + libc: [glibc] + + "@oxlint/binding-linux-riscv64-gnu@1.55.0": + resolution: { + integrity: sha512-clj0lnIN+V52G9tdtZl0LbdTSurnZ1NZj92Je5X4lC7gP5jiCSW+Y/oiDiSauBAD4wrHt2S7nN3pA0zfKYK/6Q==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [riscv64] + os: [linux] + libc: [glibc] + + "@oxlint/binding-linux-riscv64-musl@1.55.0": + resolution: { + integrity: sha512-NNu08pllN5x/O94/sgR3DA8lbrGBnTHsINZZR0hcav1sj79ksTiKKm1mRzvZvacwQ0hUnGinFo+JO75ok2PxYg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [riscv64] + os: [linux] + libc: [musl] + + "@oxlint/binding-linux-s390x-gnu@1.55.0": + resolution: { + integrity: sha512-BvfQz3PRlWZRoEZ17dZCqgQsMRdpzGZomJkVATwCIGhHVVeHJMQdmdXPSjcT1DCNUrOjXnVyj1RGDj5+/Je2+Q==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [s390x] + os: [linux] + libc: [glibc] + + "@oxlint/binding-linux-x64-gnu@1.55.0": + resolution: { + integrity: sha512-ngSOoFCSBMKVQd24H8zkbcBNc7EHhjnF1sv3mC9NNXQ/4rRjI/4Dj9+9XoDZeFEkF1SX1COSBXF1b2Pr9rqdEw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [linux] + libc: [glibc] + + "@oxlint/binding-linux-x64-musl@1.55.0": + resolution: { + integrity: sha512-BDpP7W8GlaG7BR6QjGZAleYzxoyKc/D24spZIF2mB3XsfALQJJT/OBmP8YpeTb1rveFSBHzl8T7l0aqwkWNdGA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [linux] + libc: [musl] + + "@oxlint/binding-openharmony-arm64@1.55.0": + resolution: { + integrity: sha512-PS6GFvmde/pc3fCA2Srt51glr8Lcxhpf6WIBFfLphndjRrD34NEcses4TSxQrEcxYo6qVywGfylM0ZhSCF2gGA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [openharmony] + + "@oxlint/binding-win32-arm64-msvc@1.55.0": + resolution: { + integrity: sha512-P6JcLJGs/q1UOvDLzN8otd9JsH4tsuuPDv+p7aHqHM3PrKmYdmUvkNj4K327PTd35AYcznOCN+l4ZOaq76QzSw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [win32] + + "@oxlint/binding-win32-ia32-msvc@1.55.0": + resolution: { + integrity: sha512-gzkk4zE2zsE+WmRxFOiAZHpCpUNDFytEakqNXoNHW+PnYEOTPKDdW6nrzgSeTbGKVPXNAKQnRnMgrh7+n3Xueg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [ia32] + os: [win32] + + "@oxlint/binding-win32-x64-msvc@1.55.0": + resolution: { + integrity: sha512-ZFALNow2/og75gvYzNP7qe+rREQ5xunktwA+lgykoozHZ6hw9bqg4fn5j2UvG4gIn1FXqrZHkOAXuPf5+GOYTQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [win32] + + "@types/bun@1.3.10": + resolution: { + integrity: sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ==, + } + + "@types/estree@1.0.8": + resolution: { + integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, + } + + "@types/json-schema@7.0.15": + resolution: { + integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, + } + + "@types/node@25.5.0": + resolution: { + integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==, + } + + "@typescript-eslint/project-service@8.57.0": + resolution: { + integrity: sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/scope-manager@8.57.0": + resolution: { + integrity: sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@typescript-eslint/tsconfig-utils@8.57.0": + resolution: { + integrity: sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/type-utils@8.57.0": + resolution: { + integrity: sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/types@8.57.0": + resolution: { + integrity: sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@typescript-eslint/typescript-estree@8.57.0": + resolution: { + integrity: sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/utils@8.57.0": + resolution: { + integrity: sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.0.0" + + "@typescript-eslint/visitor-keys@8.57.0": + resolution: { + integrity: sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + acorn-jsx@5.3.2: + resolution: { + integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, + } + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: { + integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==, + } + engines: { node: ">=0.4.0" } + hasBin: true + + ajv@6.14.0: + resolution: { + integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==, + } + + ansi-styles@4.3.0: + resolution: { + integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, + } + engines: { node: ">=8" } + + argparse@2.0.1: + resolution: { + integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, + } + + balanced-match@1.0.2: + resolution: { + integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, + } + + balanced-match@4.0.4: + resolution: { + integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==, + } + engines: { node: 18 || 20 || >=22 } + + brace-expansion@1.1.12: + resolution: { + integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==, + } + + brace-expansion@5.0.4: + resolution: { + integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==, + } + engines: { node: 18 || 20 || >=22 } + + braces@3.0.3: + resolution: { + integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, + } + engines: { node: ">=8" } + + bun-types@1.3.10: + resolution: { + integrity: sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg==, + } + + callsites@3.1.0: + resolution: { + integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, + } + engines: { node: ">=6" } + + chalk@4.1.2: + resolution: { + integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, + } + engines: { node: ">=10" } + + color-convert@2.0.1: + resolution: { + integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, + } + engines: { node: ">=7.0.0" } + + color-name@1.1.4: + resolution: { + integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, + } + + concat-map@0.0.1: + resolution: { + integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, + } + + cross-spawn@7.0.6: + resolution: { + integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, + } + engines: { node: ">= 8" } + + debug@3.2.7: + resolution: { + integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==, + } + peerDependencies: + supports-color: "*" + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: { + integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, + } + engines: { node: ">=6.0" } + peerDependencies: + supports-color: "*" + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: { + integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, + } + + deepmerge-ts@7.1.5: + resolution: { + integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==, + } + engines: { node: ">=16.0.0" } + + escape-string-regexp@4.0.0: + resolution: { + integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, + } + engines: { node: ">=10" } + + escape-string-regexp@5.0.0: + resolution: { + integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==, + } + engines: { node: ">=12" } + + eslint-import-resolver-node@0.3.9: + resolution: { + integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==, + } + + eslint-module-utils@2.12.1: + resolution: { + integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==, + } + engines: { node: ">=4" } + peerDependencies: + "@typescript-eslint/parser": "*" + eslint: "*" + eslint-import-resolver-node: "*" + eslint-import-resolver-typescript: "*" + eslint-import-resolver-webpack: "*" + peerDependenciesMeta: + "@typescript-eslint/parser": + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-boundaries@5.4.0: + resolution: { + integrity: sha512-6SQmEhXCqGrrxm9YiM24SC95CqrVi2MUOm5SDrfquceh/os8MIAvZYsDU69zvtCSb1S6UbNEmdioi1gCDc8+VQ==, + } + engines: { node: ">=18.18" } + peerDependencies: + eslint: ">=6.0.0" + + eslint-plugin-functional@9.0.4: + resolution: { + integrity: sha512-zm4qaoqb2r50V4WXxt0Mj92buXGMECYvMxGQ6sSb+XeJ+Eec6zCHuMY2+AWK1mqiApvUz2tCtp1P3zcEPU0huw==, + } + engines: { node: ">=v18.18.0" } + peerDependencies: + eslint: ^9.0.0 || ^10.0.0 + typescript: ">=4.7.4" + peerDependenciesMeta: + typescript: + optional: true + + eslint-plugin-oxlint@1.55.0: + resolution: { + integrity: sha512-5ng7DOuikSE64e7hX2HBqEWdmql+Q4FWppBoBkxKKflLt1j9LXhab5BN3bYJKyrAihuK1/VH2JvfNefeOZAqpA==, + } + + eslint-plugin-prefer-arrow-functions@3.9.1: + resolution: { + integrity: sha512-Mr9Ia8i5ohfCMcZBRedXXWdDJIo30gdKdPfRQ5DtO62yzogB5ErVGwQWPz+ycRlQPKpCAlBHpJdp1TCeCtt+YA==, + } + engines: { node: ">=18.0.0" } + peerDependencies: + eslint: ">=9.17.0" + + eslint-scope@8.4.0: + resolution: { + integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + eslint-visitor-keys@3.4.3: + resolution: { + integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + eslint-visitor-keys@4.2.1: + resolution: { + integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + eslint-visitor-keys@5.0.1: + resolution: { + integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + eslint@9.39.4: + resolution: { + integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + hasBin: true + peerDependencies: + jiti: "*" + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: { + integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + esquery@1.7.0: + resolution: { + integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==, + } + engines: { node: ">=0.10" } + + esrecurse@4.3.0: + resolution: { + integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, + } + engines: { node: ">=4.0" } + + estraverse@5.3.0: + resolution: { + integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, + } + engines: { node: ">=4.0" } + + esutils@2.0.3: + resolution: { + integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, + } + engines: { node: ">=0.10.0" } + + fast-check@3.23.2: + resolution: { + integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==, + } + engines: { node: ">=8.0.0" } + + fast-deep-equal@3.1.3: + resolution: { + integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, + } + + fast-json-stable-stringify@2.1.0: + resolution: { + integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, + } + + fast-levenshtein@2.0.6: + resolution: { + integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, + } + + fdir@6.5.0: + resolution: { + integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, + } + engines: { node: ">=12.0.0" } + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: { + integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==, + } + engines: { node: ">=16.0.0" } + + fill-range@7.1.1: + resolution: { + integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, + } + engines: { node: ">=8" } + + find-up@5.0.0: + resolution: { + integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, + } + engines: { node: ">=10" } + + flat-cache@4.0.1: + resolution: { + integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==, + } + engines: { node: ">=16" } + + flatted@3.4.1: + resolution: { + integrity: sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==, + } + + function-bind@1.1.2: + resolution: { + integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, + } + + glob-parent@6.0.2: + resolution: { + integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, + } + engines: { node: ">=10.13.0" } + + globals@14.0.0: + resolution: { + integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==, + } + engines: { node: ">=18" } + + handlebars@4.7.8: + resolution: { + integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==, + } + engines: { node: ">=0.4.7" } + hasBin: true + + has-flag@4.0.0: + resolution: { + integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, + } + engines: { node: ">=8" } + + hasown@2.0.2: + resolution: { + integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==, + } + engines: { node: ">= 0.4" } + + ignore@5.3.2: + resolution: { + integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, + } + engines: { node: ">= 4" } + + import-fresh@3.3.1: + resolution: { + integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==, + } + engines: { node: ">=6" } + + imurmurhash@0.1.4: + resolution: { + integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, + } + engines: { node: ">=0.8.19" } + + is-core-module@2.16.1: + resolution: { + integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==, + } + engines: { node: ">= 0.4" } + + is-extglob@2.1.1: + resolution: { + integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, + } + engines: { node: ">=0.10.0" } + + is-glob@4.0.3: + resolution: { + integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, + } + engines: { node: ">=0.10.0" } + + is-immutable-type@5.0.1: + resolution: { + integrity: sha512-LkHEOGVZZXxGl8vDs+10k3DvP++SEoYEAJLRk6buTFi6kD7QekThV7xHS0j6gpnUCQ0zpud/gMDGiV4dQneLTg==, + } + peerDependencies: + eslint: "*" + typescript: ">=4.7.4" + + is-number@7.0.0: + resolution: { + integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, + } + engines: { node: ">=0.12.0" } + + isexe@2.0.0: + resolution: { + integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, + } + + js-yaml@4.1.1: + resolution: { + integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==, + } + hasBin: true + + json-buffer@3.0.1: + resolution: { + integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, + } + + json-schema-traverse@0.4.1: + resolution: { + integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, + } + + json-stable-stringify-without-jsonify@1.0.1: + resolution: { + integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, + } + + jsonc-parser@3.3.1: + resolution: { + integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==, + } + + keyv@4.5.4: + resolution: { + integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, + } + + levn@0.4.1: + resolution: { + integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, + } + engines: { node: ">= 0.8.0" } + + locate-path@6.0.0: + resolution: { + integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, + } + engines: { node: ">=10" } + + lodash.merge@4.6.2: + resolution: { + integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, + } + + micromatch@4.0.8: + resolution: { + integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, + } + engines: { node: ">=8.6" } + + minimatch@10.2.4: + resolution: { + integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==, + } + engines: { node: 18 || 20 || >=22 } + + minimatch@3.1.5: + resolution: { + integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==, + } + + minimist@1.2.8: + resolution: { + integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==, + } + + ms@2.1.3: + resolution: { + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, + } + + natural-compare@1.4.0: + resolution: { + integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, + } + + neo-async@2.6.2: + resolution: { + integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==, + } + + optionator@0.9.4: + resolution: { + integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, + } + engines: { node: ">= 0.8.0" } + + oxlint-tsgolint@0.16.0: + resolution: { + integrity: sha512-4RuJK2jP08XwqtUu+5yhCbxEauCm6tv2MFHKEMsjbosK2+vy5us82oI3VLuHwbNyZG7ekZA26U2LLHnGR4frIA==, + } + hasBin: true + + oxlint@1.55.0: + resolution: { + integrity: sha512-T+FjepiyWpaZMhekqRpH8Z3I4vNM610p6w+Vjfqgj5TZUxHXl7N8N5IPvmOU8U4XdTRxqtNNTh9Y4hLtr7yvFg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + hasBin: true + peerDependencies: + oxlint-tsgolint: ">=0.15.0" + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + + p-limit@3.1.0: + resolution: { + integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, + } + engines: { node: ">=10" } + + p-locate@5.0.0: + resolution: { + integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, + } + engines: { node: ">=10" } + + parent-module@1.0.1: + resolution: { + integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, + } + engines: { node: ">=6" } + + path-exists@4.0.0: + resolution: { + integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, + } + engines: { node: ">=8" } + + path-key@3.1.1: + resolution: { + integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, + } + engines: { node: ">=8" } + + path-parse@1.0.7: + resolution: { + integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, + } + + picomatch@2.3.1: + resolution: { + integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==, + } + engines: { node: ">=8.6" } + + picomatch@4.0.3: + resolution: { + integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==, + } + engines: { node: ">=12" } + + prelude-ls@1.2.1: + resolution: { + integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, + } + engines: { node: ">= 0.8.0" } + + punycode@2.3.1: + resolution: { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, + } + engines: { node: ">=6" } + + pure-rand@6.1.0: + resolution: { + integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==, + } + + resolve-from@4.0.0: + resolution: { + integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, + } + engines: { node: ">=4" } + + resolve@1.22.11: + resolution: { + integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==, + } + engines: { node: ">= 0.4" } + hasBin: true + + semver@7.7.4: + resolution: { + integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==, + } + engines: { node: ">=10" } + hasBin: true + + shebang-command@2.0.0: + resolution: { + integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, + } + engines: { node: ">=8" } + + shebang-regex@3.0.0: + resolution: { + integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, + } + engines: { node: ">=8" } + + source-map@0.6.1: + resolution: { + integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, + } + engines: { node: ">=0.10.0" } + + strip-json-comments@3.1.1: + resolution: { + integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, + } + engines: { node: ">=8" } + + supports-color@7.2.0: + resolution: { + integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, + } + engines: { node: ">=8" } + + supports-preserve-symlinks-flag@1.0.0: + resolution: { + integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, + } + engines: { node: ">= 0.4" } + + tinyglobby@0.2.15: + resolution: { + integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==, + } + engines: { node: ">=12.0.0" } + + to-regex-range@5.0.1: + resolution: { + integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, + } + engines: { node: ">=8.0" } + + ts-api-utils@2.4.0: + resolution: { + integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==, + } + engines: { node: ">=18.12" } + peerDependencies: + typescript: ">=4.8.4" + + ts-declaration-location@1.0.7: + resolution: { + integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==, + } + peerDependencies: + typescript: ">=4.0.0" + + type-check@0.4.0: + resolution: { + integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, + } + engines: { node: ">= 0.8.0" } + + typescript@5.9.3: + resolution: { + integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, + } + engines: { node: ">=14.17" } + hasBin: true + + uglify-js@3.19.3: + resolution: { + integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==, + } + engines: { node: ">=0.8.0" } + hasBin: true + + undici-types@7.18.2: + resolution: { + integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==, + } + + uri-js@4.4.1: + resolution: { + integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, + } + + which@2.0.2: + resolution: { + integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, + } + engines: { node: ">= 8" } + hasBin: true + + word-wrap@1.2.5: + resolution: { + integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, + } + engines: { node: ">=0.10.0" } + + wordwrap@1.0.0: + resolution: { + integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==, + } + + yocto-queue@0.1.0: + resolution: { + integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, + } + engines: { node: ">=10" } + +snapshots: + "@boundaries/elements@1.2.0(eslint@9.39.4)": + dependencies: + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint@9.39.4) + handlebars: 4.7.8 + is-core-module: 2.16.1 + micromatch: 4.0.8 + transitivePeerDependencies: + - "@typescript-eslint/parser" + - eslint + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + "@eslint-community/eslint-utils@4.9.1(eslint@9.39.4)": + dependencies: + eslint: 9.39.4 + eslint-visitor-keys: 3.4.3 + + "@eslint-community/regexpp@4.12.2": {} + + "@eslint/config-array@0.21.2": + dependencies: + "@eslint/object-schema": 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + "@eslint/config-helpers@0.4.2": + dependencies: + "@eslint/core": 0.17.0 + + "@eslint/core@0.17.0": + dependencies: + "@types/json-schema": 7.0.15 + + "@eslint/eslintrc@3.3.5": + dependencies: + ajv: 6.14.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + "@eslint/js@9.39.4": {} + + "@eslint/object-schema@2.1.7": {} + + "@eslint/plugin-kit@0.4.1": + dependencies: + "@eslint/core": 0.17.0 + levn: 0.4.1 + + "@humanfs/core@0.19.1": {} + + "@humanfs/node@0.16.7": + dependencies: + "@humanfs/core": 0.19.1 + "@humanwhocodes/retry": 0.4.3 + + "@humanwhocodes/module-importer@1.0.1": {} + + "@humanwhocodes/retry@0.4.3": {} + + "@oxlint-tsgolint/darwin-arm64@0.16.0": + optional: true + + "@oxlint-tsgolint/darwin-x64@0.16.0": + optional: true + + "@oxlint-tsgolint/linux-arm64@0.16.0": + optional: true + + "@oxlint-tsgolint/linux-x64@0.16.0": + optional: true + + "@oxlint-tsgolint/win32-arm64@0.16.0": + optional: true + + "@oxlint-tsgolint/win32-x64@0.16.0": + optional: true + + "@oxlint/binding-android-arm-eabi@1.55.0": + optional: true + + "@oxlint/binding-android-arm64@1.55.0": + optional: true + + "@oxlint/binding-darwin-arm64@1.55.0": + optional: true + + "@oxlint/binding-darwin-x64@1.55.0": + optional: true + + "@oxlint/binding-freebsd-x64@1.55.0": + optional: true + + "@oxlint/binding-linux-arm-gnueabihf@1.55.0": + optional: true + + "@oxlint/binding-linux-arm-musleabihf@1.55.0": + optional: true + + "@oxlint/binding-linux-arm64-gnu@1.55.0": + optional: true + + "@oxlint/binding-linux-arm64-musl@1.55.0": + optional: true + + "@oxlint/binding-linux-ppc64-gnu@1.55.0": + optional: true + + "@oxlint/binding-linux-riscv64-gnu@1.55.0": + optional: true + + "@oxlint/binding-linux-riscv64-musl@1.55.0": + optional: true + + "@oxlint/binding-linux-s390x-gnu@1.55.0": + optional: true + + "@oxlint/binding-linux-x64-gnu@1.55.0": + optional: true + + "@oxlint/binding-linux-x64-musl@1.55.0": + optional: true + + "@oxlint/binding-openharmony-arm64@1.55.0": + optional: true + + "@oxlint/binding-win32-arm64-msvc@1.55.0": + optional: true + + "@oxlint/binding-win32-ia32-msvc@1.55.0": + optional: true + + "@oxlint/binding-win32-x64-msvc@1.55.0": + optional: true + + "@types/bun@1.3.10": + dependencies: + bun-types: 1.3.10 + + "@types/estree@1.0.8": {} + + "@types/json-schema@7.0.15": {} + + "@types/node@25.5.0": + dependencies: + undici-types: 7.18.2 + + "@typescript-eslint/project-service@8.57.0(typescript@5.9.3)": + dependencies: + "@typescript-eslint/tsconfig-utils": 8.57.0(typescript@5.9.3) + "@typescript-eslint/types": 8.57.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/scope-manager@8.57.0": + dependencies: + "@typescript-eslint/types": 8.57.0 + "@typescript-eslint/visitor-keys": 8.57.0 + + "@typescript-eslint/tsconfig-utils@8.57.0(typescript@5.9.3)": + dependencies: + typescript: 5.9.3 + + "@typescript-eslint/type-utils@8.57.0(eslint@9.39.4)(typescript@5.9.3)": + dependencies: + "@typescript-eslint/types": 8.57.0 + "@typescript-eslint/typescript-estree": 8.57.0(typescript@5.9.3) + "@typescript-eslint/utils": 8.57.0(eslint@9.39.4)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.4 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/types@8.57.0": {} + + "@typescript-eslint/typescript-estree@8.57.0(typescript@5.9.3)": + dependencies: + "@typescript-eslint/project-service": 8.57.0(typescript@5.9.3) + "@typescript-eslint/tsconfig-utils": 8.57.0(typescript@5.9.3) + "@typescript-eslint/types": 8.57.0 + "@typescript-eslint/visitor-keys": 8.57.0 + debug: 4.4.3 + minimatch: 10.2.4 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/utils@8.57.0(eslint@9.39.4)(typescript@5.9.3)": + dependencies: + "@eslint-community/eslint-utils": 4.9.1(eslint@9.39.4) + "@typescript-eslint/scope-manager": 8.57.0 + "@typescript-eslint/types": 8.57.0 + "@typescript-eslint/typescript-estree": 8.57.0(typescript@5.9.3) + eslint: 9.39.4 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/visitor-keys@8.57.0": + dependencies: + "@typescript-eslint/types": 8.57.0 + eslint-visitor-keys: 5.0.1 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + ajv@6.14.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.4: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + bun-types@1.3.10: + dependencies: + "@types/node": 25.5.0 + + callsites@3.1.0: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + deepmerge-ts@7.1.5: {} + + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint@9.39.4): + dependencies: + debug: 3.2.7 + optionalDependencies: + eslint: 9.39.4 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + + eslint-plugin-boundaries@5.4.0(eslint@9.39.4): + dependencies: + "@boundaries/elements": 1.2.0(eslint@9.39.4) + chalk: 4.1.2 + eslint: 9.39.4 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint@9.39.4) + micromatch: 4.0.8 + transitivePeerDependencies: + - "@typescript-eslint/parser" + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-functional@9.0.4(eslint@9.39.4)(typescript@5.9.3): + dependencies: + "@typescript-eslint/utils": 8.57.0(eslint@9.39.4)(typescript@5.9.3) + deepmerge-ts: 7.1.5 + escape-string-regexp: 5.0.0 + eslint: 9.39.4 + is-immutable-type: 5.0.1(eslint@9.39.4)(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) + ts-declaration-location: 1.0.7(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + eslint-plugin-oxlint@1.55.0: + dependencies: + jsonc-parser: 3.3.1 + + eslint-plugin-prefer-arrow-functions@3.9.1(eslint@9.39.4)(typescript@5.9.3): + dependencies: + "@typescript-eslint/types": 8.57.0 + "@typescript-eslint/utils": 8.57.0(eslint@9.39.4)(typescript@5.9.3) + eslint: 9.39.4 + transitivePeerDependencies: + - supports-color + - typescript + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.4: + dependencies: + "@eslint-community/eslint-utils": 4.9.1(eslint@9.39.4) + "@eslint-community/regexpp": 4.12.2 + "@eslint/config-array": 0.21.2 + "@eslint/config-helpers": 0.4.2 + "@eslint/core": 0.17.0 + "@eslint/eslintrc": 3.3.5 + "@eslint/js": 9.39.4 + "@eslint/plugin-kit": 0.4.1 + "@humanfs/node": 0.16.7 + "@humanwhocodes/module-importer": 1.0.1 + "@humanwhocodes/retry": 0.4.3 + "@types/estree": 1.0.8 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-check@3.23.2: + dependencies: + pure-rand: 6.1.0 + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.1 + keyv: 4.5.4 + + flatted@3.4.1: {} + + function-bind@1.1.2: {} + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + handlebars@4.7.8: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + + has-flag@4.0.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + ignore@5.3.2: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-immutable-type@5.0.1(eslint@9.39.4)(typescript@5.9.3): + dependencies: + "@typescript-eslint/type-utils": 8.57.0(eslint@9.39.4)(typescript@5.9.3) + eslint: 9.39.4 + ts-api-utils: 2.4.0(typescript@5.9.3) + ts-declaration-location: 1.0.7(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + is-number@7.0.0: {} + + isexe@2.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + jsonc-parser@3.3.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + minimatch@10.2.4: + dependencies: + brace-expansion: 5.0.4 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.12 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + natural-compare@1.4.0: {} + + neo-async@2.6.2: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + oxlint-tsgolint@0.16.0: + optionalDependencies: + "@oxlint-tsgolint/darwin-arm64": 0.16.0 + "@oxlint-tsgolint/darwin-x64": 0.16.0 + "@oxlint-tsgolint/linux-arm64": 0.16.0 + "@oxlint-tsgolint/linux-x64": 0.16.0 + "@oxlint-tsgolint/win32-arm64": 0.16.0 + "@oxlint-tsgolint/win32-x64": 0.16.0 + + oxlint@1.55.0(oxlint-tsgolint@0.16.0): + optionalDependencies: + "@oxlint/binding-android-arm-eabi": 1.55.0 + "@oxlint/binding-android-arm64": 1.55.0 + "@oxlint/binding-darwin-arm64": 1.55.0 + "@oxlint/binding-darwin-x64": 1.55.0 + "@oxlint/binding-freebsd-x64": 1.55.0 + "@oxlint/binding-linux-arm-gnueabihf": 1.55.0 + "@oxlint/binding-linux-arm-musleabihf": 1.55.0 + "@oxlint/binding-linux-arm64-gnu": 1.55.0 + "@oxlint/binding-linux-arm64-musl": 1.55.0 + "@oxlint/binding-linux-ppc64-gnu": 1.55.0 + "@oxlint/binding-linux-riscv64-gnu": 1.55.0 + "@oxlint/binding-linux-riscv64-musl": 1.55.0 + "@oxlint/binding-linux-s390x-gnu": 1.55.0 + "@oxlint/binding-linux-x64-gnu": 1.55.0 + "@oxlint/binding-linux-x64-musl": 1.55.0 + "@oxlint/binding-openharmony-arm64": 1.55.0 + "@oxlint/binding-win32-arm64-msvc": 1.55.0 + "@oxlint/binding-win32-ia32-msvc": 1.55.0 + "@oxlint/binding-win32-x64-msvc": 1.55.0 + oxlint-tsgolint: 0.16.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + prelude-ls@1.2.1: {} + + punycode@2.3.1: {} + + pure-rand@6.1.0: {} + + resolve-from@4.0.0: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + semver@7.7.4: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + source-map@0.6.1: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.4.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-declaration-location@1.0.7(typescript@5.9.3): + dependencies: + picomatch: 4.0.3 + typescript: 5.9.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript@5.9.3: {} + + uglify-js@3.19.3: + optional: true + + undici-types@7.18.2: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wordwrap@1.0.0: {} + + yocto-queue@0.1.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..3334c0e --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1 @@ +packages: []