From 8d0d919fbf43230148da7533519ed0ffdfaa4197 Mon Sep 17 00:00:00 2001 From: Mehmet Samet Duman Date: Sat, 4 Apr 2026 19:47:58 +0300 Subject: NOISSUE add GitHub Actions scripts for PR preparation and review management - Introduced `prepare.js` to validate PR mergeability and branch targeting. - Added `reviews.js` for automated review dismissal and posting. - Created `run` script to execute actions with GitHub context. - Implemented rate limiting in `withRateLimit.js` to manage API requests. - Added `supportedBranches.js` for branch classification logic. - Created `update-pinned.sh` for updating pinned dependencies. - Added `pinned.json` to manage pinned Nix dependencies. - Updated `libnbtplusplus` version from 2.3 to 3.0 and adjusted README accordingly. Signed-off-by: Mehmet Samet Duman --- .github/actions/change-analysis/action.yml | 289 ++++ .github/workflows/cgit-ci.yml | 9 +- .github/workflows/ci-lint.yml | 122 ++ .github/workflows/ci-schedule.yml | 233 +++ .github/workflows/ci.yml | 717 +++++++++ .github/workflows/cmark-ci.yml | 11 +- .github/workflows/cmark-fuzz.yml | 10 +- .github/workflows/corebinutils-ci.yml | 9 +- .github/workflows/forgewrapper-build.yml | 12 +- .github/workflows/genqrcode-ci.yml | 10 +- .github/workflows/images4docker-build.yml | 8 +- .github/workflows/json4cpp-amalgam.yml | 5 +- .github/workflows/json4cpp-ci.yml | 13 +- .github/workflows/json4cpp-flawfinder.yml | 12 +- .github/workflows/json4cpp-fuzz.yml | 10 +- .github/workflows/json4cpp-publish-docs.yml | 7 +- .github/workflows/json4cpp-semgrep.yml | 15 +- .github/workflows/libnbtplusplus-ci.yml | 9 +- .github/workflows/meshmc-backport.yml | 4 +- .github/workflows/meshmc-build.yml | 14 - .github/workflows/meshmc-codeql.yml | 7 +- .github/workflows/meshmc-container.yml | 13 +- .github/workflows/meshmc-flake-update.yml | 3 +- .github/workflows/meshmc-merge-blocking-pr.yml | 1 + .github/workflows/meshmc-nix.yml | 42 +- .github/workflows/mnv-ci.yml | 13 +- .github/workflows/mnv-codeql.yml | 12 +- .github/workflows/mnv-coverity.yml | 3 +- .github/workflows/mnv-link-check.yml | 3 +- .github/workflows/neozip-ci.yml | 9 +- .github/workflows/neozip-codeql.yml | 2 - .github/workflows/neozip-fuzz.yml | 10 +- .github/workflows/neozip-lint.yml | 4 +- .github/workflows/neozip-release.yml | 5 +- .github/workflows/repo-dependency-review.yml | 3 +- .github/workflows/repo-scorecards.yml | 4 +- .github/workflows/repo-stale.yml | 4 +- .github/workflows/tomlplusplus-ci.yml | 19 +- .github/workflows/tomlplusplus-fuzz.yml | 10 +- .github/workflows/tomlplusplus-gh-pages.yml | 11 +- REUSE.toml | 7 +- ci/OWNERS | 330 +++++ ci/README.md | 76 + ci/codeowners-validator/default.nix | 31 + ci/codeowners-validator/owners-file-name.patch | 15 + ci/codeowners-validator/permissions.patch | 36 + ci/default.nix | 93 ++ ci/github-script/.editorconfig | 3 + ci/github-script/.gitignore | 2 + ci/github-script/.npmrc | 2 + ci/github-script/README.md | 27 + ci/github-script/get-pr-commit-details.js | 101 ++ ci/github-script/lint-commits.js | 177 +++ ci/github-script/package-lock.json | 1856 ++++++++++++++++++++++++ ci/github-script/package.json | 13 + ci/github-script/prepare.js | 181 +++ ci/github-script/reviews.js | 197 +++ ci/github-script/run | 65 + ci/github-script/shell.nix | 25 + ci/github-script/withRateLimit.js | 63 + ci/pinned.json | 31 + ci/supportedBranches.js | 85 ++ ci/update-pinned.sh | 8 + libnbtplusplus/CMakeLists.txt | 2 +- libnbtplusplus/README.md | 10 +- 65 files changed, 4835 insertions(+), 298 deletions(-) create mode 100644 .github/actions/change-analysis/action.yml create mode 100644 .github/workflows/ci-lint.yml create mode 100644 .github/workflows/ci-schedule.yml create mode 100644 .github/workflows/ci.yml create mode 100644 ci/OWNERS create mode 100644 ci/README.md create mode 100644 ci/codeowners-validator/default.nix create mode 100644 ci/codeowners-validator/owners-file-name.patch create mode 100644 ci/codeowners-validator/permissions.patch create mode 100644 ci/default.nix create mode 100644 ci/github-script/.editorconfig create mode 100644 ci/github-script/.gitignore create mode 100644 ci/github-script/.npmrc create mode 100644 ci/github-script/README.md create mode 100644 ci/github-script/get-pr-commit-details.js create mode 100644 ci/github-script/lint-commits.js create mode 100644 ci/github-script/package-lock.json create mode 100644 ci/github-script/package.json create mode 100644 ci/github-script/prepare.js create mode 100644 ci/github-script/reviews.js create mode 100755 ci/github-script/run create mode 100644 ci/github-script/shell.nix create mode 100644 ci/github-script/withRateLimit.js create mode 100644 ci/pinned.json create mode 100755 ci/supportedBranches.js create mode 100755 ci/update-pinned.sh diff --git a/.github/actions/change-analysis/action.yml b/.github/actions/change-analysis/action.yml new file mode 100644 index 0000000000..84494fa821 --- /dev/null +++ b/.github/actions/change-analysis/action.yml @@ -0,0 +1,289 @@ +# Copyright (C) Project Tick Contributors +# SPDX-License-Identifier: MIT +# +# Composite action — analyzes changed directories and parses commit messages. +# Exposes per-project boolean outputs + parsed commit info for the caller. + +name: "Change Analysis" +description: > + Detects which project directories changed in this commit/PR and parses the + commit message (Conventional Commits). Outputs per-project flags and a + summary report. + +# ── Inputs ────────────────────────────────────────────────────── +inputs: + event-name: + description: "github.event_name (push, pull_request, workflow_dispatch)" + required: true + base-sha: + description: "Base SHA for diff (PR base or push before)" + required: false + default: "" + head-sha: + description: "Head SHA for diff" + required: false + default: "" + before-sha: + description: "Push event before SHA" + required: false + default: "" + current-sha: + description: "Current commit SHA (github.sha)" + required: true + pr-title: + description: "Pull request title (for commit message parsing on PRs)" + required: false + default: "" + +# ── Outputs ───────────────────────────────────────────────────── +outputs: + # Per-project change flags + archived_changed: + description: "true if archived/ was modified" + value: ${{ steps.detect.outputs.archived_changed }} + cgit_changed: + description: "true if cgit/ was modified" + value: ${{ steps.detect.outputs.cgit_changed }} + cmark_changed: + description: "true if cmark/ was modified" + value: ${{ steps.detect.outputs.cmark_changed }} + corebinutils_changed: + description: "true if corebinutils/ was modified" + value: ${{ steps.detect.outputs.corebinutils_changed }} + forgewrapper_changed: + description: "true if forgewrapper/ was modified" + value: ${{ steps.detect.outputs.forgewrapper_changed }} + genqrcode_changed: + description: "true if genqrcode/ was modified" + value: ${{ steps.detect.outputs.genqrcode_changed }} + hooks_changed: + description: "true if hooks/ was modified" + value: ${{ steps.detect.outputs.hooks_changed }} + images4docker_changed: + description: "true if images4docker/ was modified" + value: ${{ steps.detect.outputs.images4docker_changed }} + json4cpp_changed: + description: "true if json4cpp/ was modified" + value: ${{ steps.detect.outputs.json4cpp_changed }} + libnbtplusplus_changed: + description: "true if libnbtplusplus/ was modified" + value: ${{ steps.detect.outputs.libnbtplusplus_changed }} + meshmc_changed: + description: "true if meshmc/ was modified" + value: ${{ steps.detect.outputs.meshmc_changed }} + meta_changed: + description: "true if meta/ was modified" + value: ${{ steps.detect.outputs.meta_changed }} + mnv_changed: + description: "true if mnv/ was modified" + value: ${{ steps.detect.outputs.mnv_changed }} + neozip_changed: + description: "true if neozip/ was modified" + value: ${{ steps.detect.outputs.neozip_changed }} + tomlplusplus_changed: + description: "true if tomlplusplus/ was modified" + value: ${{ steps.detect.outputs.tomlplusplus_changed }} + ci_changed: + description: "true if ci/ was modified" + value: ${{ steps.detect.outputs.ci_changed }} + github_changed: + description: "true if .github/ was modified" + value: ${{ steps.detect.outputs.github_changed }} + root_changed: + description: "true if root-level files were modified" + value: ${{ steps.detect.outputs.root_changed }} + # Aggregate + changed_projects: + description: "Comma-separated list of changed project names" + value: ${{ steps.detect.outputs.changed_projects }} + changed_count: + description: "Number of changed projects" + value: ${{ steps.detect.outputs.changed_count }} + # Parsed commit + commit_type: + description: "Conventional commit type (feat, fix, chore, …)" + value: ${{ steps.parse-commit.outputs.type }} + commit_scope: + description: "Conventional commit scope" + value: ${{ steps.parse-commit.outputs.scope }} + commit_subject: + description: "Commit subject line" + value: ${{ steps.parse-commit.outputs.subject }} + commit_breaking: + description: "true if this is a breaking change" + value: ${{ steps.parse-commit.outputs.breaking }} + commit_message: + description: "Full commit message / PR title" + value: ${{ steps.parse-commit.outputs.full_message }} + +# ── Steps ─────────────────────────────────────────────────────── +runs: + using: "composite" + steps: + - name: Detect changed directories + id: detect + shell: bash + run: | + set -euo pipefail + + EVENT_NAME="${{ inputs.event-name }}" + BASE_SHA="${{ inputs.base-sha }}" + HEAD_SHA="${{ inputs.head-sha }}" + BEFORE_SHA="${{ inputs.before-sha }}" + CURRENT_SHA="${{ inputs.current-sha }}" + + # Determine the list of changed files + if [[ "$EVENT_NAME" == "pull_request" ]]; then + CHANGED_FILES=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" --) + elif [[ "$EVENT_NAME" == "push" ]]; then + if [[ "$BEFORE_SHA" == "0000000000000000000000000000000000000000" ]]; then + CHANGED_FILES=$(git diff-tree --no-commit-id --name-only -r HEAD) + else + CHANGED_FILES=$(git diff --name-only "$BEFORE_SHA" "$CURRENT_SHA" --) + fi + else + # workflow_dispatch / other: compare with parent + CHANGED_FILES=$(git diff-tree --no-commit-id --name-only -r HEAD) + fi + + echo "::group::Changed files" + echo "$CHANGED_FILES" + echo "::endgroup::" + + # All projects to detect + PROJECTS=( + archived cgit ci cmark corebinutils forgewrapper genqrcode + hooks images4docker json4cpp libnbtplusplus meshmc meta + mnv neozip tomlplusplus + ) + + CHANGED_PROJECTS=() + + for proj in "${PROJECTS[@]}"; do + if echo "$CHANGED_FILES" | grep -q "^${proj}/"; then + echo "${proj}_changed=true" >> "$GITHUB_OUTPUT" + CHANGED_PROJECTS+=("$proj") + else + echo "${proj}_changed=false" >> "$GITHUB_OUTPUT" + fi + done + + # .github/ changes + if echo "$CHANGED_FILES" | grep -q "^\.github/"; then + echo "github_changed=true" >> "$GITHUB_OUTPUT" + CHANGED_PROJECTS+=(".github") + else + echo "github_changed=false" >> "$GITHUB_OUTPUT" + fi + + # Root-level files (CODEOWNERS, README, SECURITY, etc.) + ROOT_MATCHES=$(echo "$CHANGED_FILES" | grep -vE "^(archived|cgit|ci|cmark|corebinutils|forgewrapper|genqrcode|hooks|images4docker|json4cpp|libnbtplusplus|meshmc|meta|mnv|neozip|tomlplusplus|LICENSES|\.github)/" || true) + if [[ -n "$ROOT_MATCHES" ]]; then + echo "root_changed=true" >> "$GITHUB_OUTPUT" + CHANGED_PROJECTS+=("root") + else + echo "root_changed=false" >> "$GITHUB_OUTPUT" + fi + + # Build comma-separated list + if [[ ${#CHANGED_PROJECTS[@]} -gt 0 ]]; then + PROJECTS_CSV=$(IFS=','; echo "${CHANGED_PROJECTS[*]}") + else + PROJECTS_CSV="" + fi + echo "changed_projects=${PROJECTS_CSV}" >> "$GITHUB_OUTPUT" + echo "changed_count=${#CHANGED_PROJECTS[@]}" >> "$GITHUB_OUTPUT" + + echo "::notice::Changed projects (${#CHANGED_PROJECTS[@]}): ${PROJECTS_CSV}" + + - name: Parse commit message + id: parse-commit + shell: bash + run: | + set -euo pipefail + + EVENT_NAME="${{ inputs.event-name }}" + PR_TITLE="${{ inputs.pr-title }}" + + if [[ "$EVENT_NAME" == "pull_request" && -n "$PR_TITLE" ]]; then + FULL_MSG="$PR_TITLE" + else + FULL_MSG=$(git log -1 --format='%s' HEAD) + fi + + echo "full_message<> "$GITHUB_OUTPUT" + echo "$FULL_MSG" >> "$GITHUB_OUTPUT" + echo "COMMIT_MSG_EOF" >> "$GITHUB_OUTPUT" + + # Parse Conventional Commits: type(scope): subject + # Also handles: type: subject, type!: subject, type(scope)!: subject + if [[ "$FULL_MSG" =~ ^([a-zA-Z]+)(\(([^)]*)\))?(!)?\:\ (.+)$ ]]; then + TYPE="${BASH_REMATCH[1]}" + SCOPE="${BASH_REMATCH[3]:-}" + BREAKING="${BASH_REMATCH[4]:-}" + SUBJECT="${BASH_REMATCH[5]}" + + echo "type=${TYPE}" >> "$GITHUB_OUTPUT" + echo "scope=${SCOPE}" >> "$GITHUB_OUTPUT" + echo "subject=${SUBJECT}" >> "$GITHUB_OUTPUT" + + if [[ "$BREAKING" == "!" ]]; then + echo "breaking=true" >> "$GITHUB_OUTPUT" + else + echo "breaking=false" >> "$GITHUB_OUTPUT" + fi + else + echo "type=" >> "$GITHUB_OUTPUT" + echo "scope=" >> "$GITHUB_OUTPUT" + echo "subject=${FULL_MSG}" >> "$GITHUB_OUTPUT" + echo "breaking=false" >> "$GITHUB_OUTPUT" + fi + + - name: Generate summary report + shell: bash + run: | + set -euo pipefail + + cat >> "$GITHUB_STEP_SUMMARY" <<'HEADER' + # Change Analysis Report + + HEADER + + cat >> "$GITHUB_STEP_SUMMARY" < **Changed:** \`${{ steps.detect.outputs.changed_projects }}\` + EOF diff --git a/.github/workflows/cgit-ci.yml b/.github/workflows/cgit-ci.yml index 9248d2bcef..37241312ef 100644 --- a/.github/workflows/cgit-ci.yml +++ b/.github/workflows/cgit-ci.yml @@ -1,15 +1,8 @@ name: "cgit: CI" on: - push: - paths: - - 'cgit/**' - - '.github/workflows/cgit-ci.yml' - pull_request: - paths: - - 'cgit/**' - - '.github/workflows/cgit-ci.yml' workflow_dispatch: + workflow_call: permissions: contents: read diff --git a/.github/workflows/ci-lint.yml b/.github/workflows/ci-lint.yml new file mode 100644 index 0000000000..0e01b66811 --- /dev/null +++ b/.github/workflows/ci-lint.yml @@ -0,0 +1,122 @@ +# Copyright (C) Project Tick Contributors +# SPDX-License-Identifier: MIT +# +# Fast lint & commit checks — called from ci.yml before builds start. + +name: "Lint & Checks" + +on: + workflow_call: + inputs: + run-level: + description: "minimal | standard | full" + required: true + type: string + changed-projects: + description: "Comma-separated list of changed projects" + required: false + type: string + default: "" + +permissions: + contents: read + +jobs: + # ── Commit message lint (Conventional Commits) ────────────────── + commit-lint: + name: "Commit Messages" + runs-on: ubuntu-latest + steps: + - name: Harden runner + uses: step-security/harden-runner@v2 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 22 + + - name: Install dependencies + working-directory: ci/github-script + run: npm ci + + - name: Lint commit messages + uses: actions/github-script@v7 + with: + script: | + const lint = require('./ci/github-script/lint-commits.js') + await lint({ github, context, core, repoPath: '.' }) + + # ── REUSE / license compliance ────────────────────────────────── + reuse: + name: "REUSE Compliance" + runs-on: ubuntu-latest + steps: + - name: Harden runner + uses: step-security/harden-runner@v2 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@v6 + + - name: Check REUSE compliance + uses: fsfe/reuse-action@v6 + + # ── Whitespace & formatting checks ───────────────────────────── + whitespace: + name: "Whitespace" + runs-on: ubuntu-latest + steps: + - name: Harden runner + uses: step-security/harden-runner@v2 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@v6 + + - name: Check trailing whitespace + run: | + set -euo pipefail + ERRORS=$(git diff --check HEAD~1 HEAD -- \ + ':!*.patch' \ + ':!*/test/data/*' \ + ':!*.ico' \ + ':!*.png' \ + ':!*.jpg' \ + ':!*.gif' \ + ':!*.bin' \ + ':!*.7z' \ + ':!*.zip' \ + ':!*.gz' \ + ':!*.lock' \ + 2>/dev/null || true) + if [[ -n "$ERRORS" ]]; then + echo "::warning::Whitespace issues found:" + echo "$ERRORS" + fi + + # ── Actionlint (validate all workflow YAML) ───────────────────── + actionlint: + name: "Actionlint" + runs-on: ubuntu-latest + steps: + - name: Harden runner + uses: step-security/harden-runner@v2 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@v6 + + - name: Run actionlint + uses: raven-actions/actionlint@v2 + with: + matcher: true diff --git a/.github/workflows/ci-schedule.yml b/.github/workflows/ci-schedule.yml new file mode 100644 index 0000000000..1c933d6b26 --- /dev/null +++ b/.github/workflows/ci-schedule.yml @@ -0,0 +1,233 @@ +# Copyright (C) Project Tick Contributors +# SPDX-License-Identifier: MIT +# +# ╔══════════════════════════════════════════════════════════════════╗ +# ║ Project Tick — Scheduled Maintenance Orchestrator ║ +# ║ ║ +# ║ Consolidates ALL scheduled jobs into one workflow with a ║ +# ║ matrix dispatch. Each cron entry triggers its own subset. ║ +# ╚══════════════════════════════════════════════════════════════════╝ + +name: "Scheduled CI" + +on: + schedule: + # ── Daily (03:00 UTC) ───────────────────────────────────────── + - cron: "0 3 * * *" # [0] daily: stale, coverity, docker + # ── Weekly Sunday (04:00 UTC) ───────────────────────────────── + - cron: "0 4 * * 0" # [1] weekly-sun: link-check, scorecard, flake-update, codeql + # ── Weekly Wednesday (14:00 UTC) ────────────────────────────── + - cron: "0 14 * * 3" # [2] weekly-wed: flawfinder + # ── Weekly Thursday (02:00 UTC) ─────────────────────────────── + - cron: "0 2 * * 4" # [3] weekly-thu: semgrep + workflow_dispatch: + inputs: + schedule-group: + description: "Which schedule group to run" + type: choice + options: [daily, weekly-sun, weekly-wed, weekly-thu, all] + default: all + +permissions: + contents: read + +env: + # Map cron index → group name + SCHEDULE_GROUP: >- + ${{ + github.event.inputs.schedule-group == 'all' && 'all' || + github.event.inputs.schedule-group || + (github.event.schedule == '0 3 * * *' && 'daily') || + (github.event.schedule == '0 4 * * 0' && 'weekly-sun') || + (github.event.schedule == '0 14 * * 3' && 'weekly-wed') || + (github.event.schedule == '0 2 * * 4' && 'weekly-thu') || + 'all' + }} + +jobs: + # ╔════════════════════════════════════════════════════════════════╗ + # ║ Daily Jobs ║ + # ╚════════════════════════════════════════════════════════════════╝ + + stale: + name: "Stale Issues & PRs" + if: contains(fromJSON('["daily","all"]'), env.SCHEDULE_GROUP) + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - name: Harden runner + uses: step-security/harden-runner@v2 + with: + egress-policy: audit + + - name: Mark stale + uses: actions/stale@v10 + with: + stale-issue-message: > + This issue has been automatically marked as stale because it has not had + activity in 90 days. It will be closed in 10 days if no further activity occurs. + stale-pr-message: > + This PR has been automatically marked as stale because it has not had + activity in 30 days. Please rebase or update to keep it alive. + days-before-stale: 90 + days-before-pr-stale: 30 + days-before-close: 10 + days-before-pr-close: -1 + exempt-issue-labels: "pinned,security" + exempt-pr-labels: "pinned,security,status: blocking" + stale-issue-label: "state: stale" + stale-pr-label: "state: stale" + + docker-images: + name: "Docker Image Rebuild" + if: contains(fromJSON('["daily","all"]'), env.SCHEDULE_GROUP) + uses: ./.github/workflows/images4docker-build.yml + permissions: + contents: read + packages: write + secrets: inherit + + mnv-coverity: + name: "MNV Coverity Scan" + if: contains(fromJSON('["daily","all"]'), env.SCHEDULE_GROUP) + uses: ./.github/workflows/mnv-coverity.yml + secrets: inherit + + # ╔════════════════════════════════════════════════════════════════╗ + # ║ Weekly Sunday Jobs ║ + # ╚════════════════════════════════════════════════════════════════╝ + + mnv-link-check: + name: "MNV Link Check" + if: contains(fromJSON('["weekly-sun","all"]'), env.SCHEDULE_GROUP) + uses: ./.github/workflows/mnv-link-check.yml + secrets: inherit + + scorecard: + name: "OpenSSF Scorecard" + if: contains(fromJSON('["weekly-sun","all"]'), env.SCHEDULE_GROUP) + uses: ./.github/workflows/repo-scorecards.yml + permissions: + contents: read + security-events: write + id-token: write + actions: read + issues: read + pull-requests: read + checks: read + secrets: inherit + + meshmc-flake-update: + name: "MeshMC Nix Flake Update" + if: >- + contains(fromJSON('["weekly-sun","all"]'), env.SCHEDULE_GROUP) && + github.repository_owner == 'Project-Tick' + uses: ./.github/workflows/meshmc-flake-update.yml + permissions: + contents: write + pull-requests: write + secrets: inherit + + mnv-codeql: + name: "MNV CodeQL" + if: contains(fromJSON('["weekly-sun","all"]'), env.SCHEDULE_GROUP) + uses: ./.github/workflows/mnv-codeql.yml + permissions: + contents: read + security-events: write + secrets: inherit + + neozip-codeql: + name: "NeoZip CodeQL" + if: contains(fromJSON('["weekly-sun","all"]'), env.SCHEDULE_GROUP) + uses: ./.github/workflows/neozip-codeql.yml + permissions: + contents: read + security-events: write + secrets: inherit + + # ╔════════════════════════════════════════════════════════════════╗ + # ║ Weekly Wednesday Jobs ║ + # ╚════════════════════════════════════════════════════════════════╝ + + json4cpp-flawfinder: + name: "JSON4CPP Flawfinder" + if: contains(fromJSON('["weekly-wed","all"]'), env.SCHEDULE_GROUP) + uses: ./.github/workflows/json4cpp-flawfinder.yml + permissions: + contents: read + security-events: write + actions: read + secrets: inherit + + # ╔════════════════════════════════════════════════════════════════╗ + # ║ Weekly Thursday Jobs ║ + # ╚════════════════════════════════════════════════════════════════╝ + + json4cpp-semgrep: + name: "JSON4CPP Semgrep" + if: contains(fromJSON('["weekly-thu","all"]'), env.SCHEDULE_GROUP) + uses: ./.github/workflows/json4cpp-semgrep.yml + permissions: + contents: read + security-events: write + actions: read + secrets: inherit + + # ╔════════════════════════════════════════════════════════════════╗ + # ║ Schedule Verdict ║ + # ╚════════════════════════════════════════════════════════════════╝ + + verdict: + name: "Schedule Verdict" + if: always() + needs: + - stale + - docker-images + - mnv-coverity + - mnv-link-check + - scorecard + - meshmc-flake-update + - mnv-codeql + - neozip-codeql + - json4cpp-flawfinder + - json4cpp-semgrep + runs-on: ubuntu-latest + steps: + - name: Report + run: | + set -euo pipefail + + echo "## Scheduled CI Report — ${{ env.SCHEDULE_GROUP }}" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "| Job | Result |" >> "$GITHUB_STEP_SUMMARY" + echo "|-----|--------|" >> "$GITHUB_STEP_SUMMARY" + + FAILED=false + report() { + local name="$1" result="$2" icon="⬜" + case "$result" in + success) icon="✅" ;; failure) icon="❌"; FAILED=true ;; + cancelled) icon="⏹️" ;; skipped) icon="⏭️" ;; + esac + echo "| $name | $icon $result |" >> "$GITHUB_STEP_SUMMARY" + } + + report "Stale" "${{ needs.stale.result }}" + report "Docker Images" "${{ needs.docker-images.result }}" + report "MNV Coverity" "${{ needs.mnv-coverity.result }}" + report "MNV Link Check" "${{ needs.mnv-link-check.result }}" + report "Scorecard" "${{ needs.scorecard.result }}" + report "Flake Update" "${{ needs.meshmc-flake-update.result }}" + report "MNV CodeQL" "${{ needs.mnv-codeql.result }}" + report "NeoZip CodeQL" "${{ needs.neozip-codeql.result }}" + report "Flawfinder" "${{ needs.json4cpp-flawfinder.result }}" + report "Semgrep" "${{ needs.json4cpp-semgrep.result }}" + + if [[ "$FAILED" == "true" ]]; then + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "**FAILED** — check individual jobs above." >> "$GITHUB_STEP_SUMMARY" + exit 1 + fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..c0dde03ab0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,717 @@ +# Copyright (C) Project Tick Contributors +# SPDX-License-Identifier: MIT +# +# ╔══════════════════════════════════════════════════════════════════╗ +# ║ Project Tick — Monolithic CI Orchestrator ║ +# ║ ║ +# ║ Every push, pull request, merge queue entry, tag push, and ║ +# ║ manual dispatch flows through this single gate. Nothing runs ║ +# ║ unless this file says so. ║ +# ╚══════════════════════════════════════════════════════════════════╝ + +name: CI + +on: + push: + branches: ["**"] + tags: ["*"] + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + pull_request_target: + types: [closed, labeled] + merge_group: + types: [checks_requested] + workflow_dispatch: + inputs: + force-all: + description: "Force run all project CI pipelines" + type: boolean + default: false + build-type: + description: "Build configuration for meshmc/forgewrapper" + type: choice + options: [Debug, Release] + default: Debug + +permissions: + contents: read + +concurrency: + group: >- + ci-${{ + github.event_name == 'merge_group' && github.event.merge_group.head_ref || + github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || + github.ref + }} + cancel-in-progress: ${{ github.event_name != 'merge_group' }} + +# ════════════════════════════════════════════════════════════════════ +# Environment — shared across all jobs +# ════════════════════════════════════════════════════════════════════ +env: + CI: true + FORCE_ALL: ${{ github.event.inputs.force-all == 'true' || github.event_name == 'merge_group' }} + +jobs: + # ╔════════════════════════════════════════════════════════════════╗ + # ║ STAGE 0 — Gate & Triage ║ + # ╚════════════════════════════════════════════════════════════════╝ + + gate: + name: "Gate" + runs-on: ubuntu-latest + if: >- + !(github.event_name == 'pull_request_target' && !github.event.pull_request.merged) && + !(github.event_name == 'pull_request' && github.event.pull_request.draft) + outputs: + # ── Event classification ──────────────────────────────────── + is_push: ${{ steps.classify.outputs.is_push }} + is_pr: ${{ steps.classify.outputs.is_pr }} + is_merge_queue: ${{ steps.classify.outputs.is_merge_queue }} + is_tag: ${{ steps.classify.outputs.is_tag }} + is_release_tag: ${{ steps.classify.outputs.is_release_tag }} + is_backport: ${{ steps.classify.outputs.is_backport }} + is_dependabot: ${{ steps.classify.outputs.is_dependabot }} + is_master: ${{ steps.classify.outputs.is_master }} + is_scheduled: ${{ steps.classify.outputs.is_scheduled }} + run_level: ${{ steps.classify.outputs.run_level }} + # ── Per-project change flags ──────────────────────────────── + archived_changed: ${{ steps.changes.outputs.archived_changed }} + cgit_changed: ${{ steps.changes.outputs.cgit_changed }} + ci_changed: ${{ steps.changes.outputs.ci_changed }} + cmark_changed: ${{ steps.changes.outputs.cmark_changed }} + corebinutils_changed: ${{ steps.changes.outputs.corebinutils_changed }} + forgewrapper_changed: ${{ steps.changes.outputs.forgewrapper_changed }} + genqrcode_changed: ${{ steps.changes.outputs.genqrcode_changed }} + hooks_changed: ${{ steps.changes.outputs.hooks_changed }} + images4docker_changed: ${{ steps.changes.outputs.images4docker_changed }} + json4cpp_changed: ${{ steps.changes.outputs.json4cpp_changed }} + libnbtplusplus_changed: ${{ steps.changes.outputs.libnbtplusplus_changed }} + meshmc_changed: ${{ steps.changes.outputs.meshmc_changed }} + meta_changed: ${{ steps.changes.outputs.meta_changed }} + mnv_changed: ${{ steps.changes.outputs.mnv_changed }} + neozip_changed: ${{ steps.changes.outputs.neozip_changed }} + tomlplusplus_changed: ${{ steps.changes.outputs.tomlplusplus_changed }} + github_changed: ${{ steps.changes.outputs.github_changed }} + root_changed: ${{ steps.changes.outputs.root_changed }} + changed_projects: ${{ steps.changes.outputs.changed_projects }} + changed_count: ${{ steps.changes.outputs.changed_count }} + # ── Commit parsing ────────────────────────────────────────── + commit_type: ${{ steps.changes.outputs.commit_type }} + commit_scope: ${{ steps.changes.outputs.commit_scope }} + commit_subject: ${{ steps.changes.outputs.commit_subject }} + commit_breaking: ${{ steps.changes.outputs.commit_breaking }} + commit_message: ${{ steps.changes.outputs.commit_message }} + # ── Build config ──────────────────────────────────────────── + build_type: ${{ steps.classify.outputs.build_type }} + + steps: + - name: Harden runner + uses: step-security/harden-runner@v2 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Classify event + id: classify + run: | + set -euo pipefail + + REF="${GITHUB_REF:-}" + EVENT="${{ github.event_name }}" + ACTOR="${{ github.actor }}" + HEAD_REF="${{ github.head_ref || '' }}" + BASE_REF="${{ github.base_ref || '' }}" + + # ── Booleans ────────────────────────────────────────── + echo "is_push=$([[ "$EVENT" == "push" ]] && echo true || echo false)" >> "$GITHUB_OUTPUT" + echo "is_pr=$([[ "$EVENT" == "pull_request" || "$EVENT" == "pull_request_target" ]] && echo true || echo false)" >> "$GITHUB_OUTPUT" + echo "is_merge_queue=$([[ "$EVENT" == "merge_group" ]] && echo true || echo false)" >> "$GITHUB_OUTPUT" + echo "is_tag=$([[ "$REF" == refs/tags/* ]] && echo true || echo false)" >> "$GITHUB_OUTPUT" + echo "is_release_tag=$([[ "$REF" =~ ^refs/tags/(meshmc|neozip|mnv|cmark|forgewrapper)- ]] && echo true || echo false)" >> "$GITHUB_OUTPUT" + echo "is_backport=$([[ "$HEAD_REF" == backport-* || "$HEAD_REF" == backport/* ]] && echo true || echo false)" >> "$GITHUB_OUTPUT" + echo "is_dependabot=$([[ "$ACTOR" == "dependabot[bot]" ]] && echo true || echo false)" >> "$GITHUB_OUTPUT" + echo "is_master=$([[ "$REF" == "refs/heads/master" || "$REF" == "refs/heads/main" ]] && echo true || echo false)" >> "$GITHUB_OUTPUT" + echo "is_scheduled=false" >> "$GITHUB_OUTPUT" + + # ── Run level ───────────────────────────────────────── + # full = merge queue, tags, master push, manual force-all + # standard = normal PR, branch push + # minimal = dependabot, backport, draft + if [[ "$EVENT" == "merge_group" ]] || \ + [[ "$REF" == refs/tags/* ]] || \ + [[ "$REF" == "refs/heads/master" ]] || \ + [[ "${{ env.FORCE_ALL }}" == "true" ]]; then + echo "run_level=full" >> "$GITHUB_OUTPUT" + elif [[ "$ACTOR" == "dependabot[bot]" ]]; then + echo "run_level=minimal" >> "$GITHUB_OUTPUT" + elif [[ "$HEAD_REF" == backport-* ]]; then + echo "run_level=standard" >> "$GITHUB_OUTPUT" + else + echo "run_level=standard" >> "$GITHUB_OUTPUT" + fi + + # ── Build type ──────────────────────────────────────── + if [[ "$REF" == refs/tags/* ]]; then + echo "build_type=Release" >> "$GITHUB_OUTPUT" + elif [[ -n "${{ github.event.inputs.build-type || '' }}" ]]; then + echo "build_type=${{ github.event.inputs.build-type }}" >> "$GITHUB_OUTPUT" + else + echo "build_type=Debug" >> "$GITHUB_OUTPUT" + fi + + - name: Detect changes + id: changes + if: github.event_name != 'pull_request_target' + uses: ./.github/actions/change-analysis + with: + event-name: ${{ github.event_name }} + base-sha: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || '' }} + head-sha: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha || '' }} + before-sha: ${{ github.event.before || '' }} + current-sha: ${{ github.sha }} + pr-title: ${{ github.event.pull_request.title || '' }} + + # ╔════════════════════════════════════════════════════════════════╗ + # ║ STAGE 1 — Lint & Commit Checks (fast, blocks everything) ║ + # ╚════════════════════════════════════════════════════════════════╝ + + lint: + name: "Lint" + needs: gate + if: needs.gate.outputs.is_pr == 'true' + uses: ./.github/workflows/ci-lint.yml + with: + run-level: ${{ needs.gate.outputs.run_level }} + changed-projects: ${{ needs.gate.outputs.changed_projects }} + secrets: inherit + + dependency-review: + name: "Dependency Review" + needs: gate + if: needs.gate.outputs.is_pr == 'true' + uses: ./.github/workflows/repo-dependency-review.yml + secrets: inherit + + # ╔════════════════════════════════════════════════════════════════╗ + # ║ STAGE 2 — Per-Project Build & Test Matrices ║ + # ║ ║ + # ║ Each project runs only when its directory changed, or when ║ + # ║ force-all / merge-queue / master push triggers them all. ║ + # ║ Inner reusable workflows handle their own matrices. ║ + # ╚════════════════════════════════════════════════════════════════╝ + + # ── C / System Projects ───────────────────────────────────────── + mnv: + name: "MNV" + needs: [gate, lint] + if: >- + always() && + !cancelled() && + (needs.lint.result == 'success' || needs.lint.result == 'skipped') && + (needs.gate.outputs.mnv_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/mnv-ci.yml + permissions: + contents: read + secrets: inherit + + cgit: + name: "CGit" + needs: [gate, lint] + if: >- + always() && + !cancelled() && + (needs.lint.result == 'success' || needs.lint.result == 'skipped') && + (needs.gate.outputs.cgit_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/cgit-ci.yml + permissions: + contents: read + secrets: inherit + + cmark: + name: "CMark" + needs: [gate, lint] + if: >- + always() && + !cancelled() && + (needs.lint.result == 'success' || needs.lint.result == 'skipped') && + (needs.gate.outputs.cmark_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/cmark-ci.yml + permissions: + contents: read + secrets: inherit + + corebinutils: + name: "CoreBinutils" + needs: [gate, lint] + if: >- + always() && + !cancelled() && + (needs.lint.result == 'success' || needs.lint.result == 'skipped') && + (needs.gate.outputs.corebinutils_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/corebinutils-ci.yml + permissions: + contents: read + secrets: inherit + + genqrcode: + name: "GenQRCode" + needs: [gate, lint] + if: >- + always() && + !cancelled() && + (needs.lint.result == 'success' || needs.lint.result == 'skipped') && + (needs.gate.outputs.genqrcode_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/genqrcode-ci.yml + permissions: + contents: read + secrets: inherit + + neozip: + name: "NeoZip" + needs: [gate, lint] + if: >- + always() && + !cancelled() && + (needs.lint.result == 'success' || needs.lint.result == 'skipped') && + (needs.gate.outputs.neozip_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/neozip-ci.yml + permissions: + contents: read + secrets: inherit + + # ── C++ / Library Projects ────────────────────────────────────── + json4cpp: + name: "JSON4CPP" + needs: [gate, lint] + if: >- + always() && + !cancelled() && + (needs.lint.result == 'success' || needs.lint.result == 'skipped') && + (needs.gate.outputs.json4cpp_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/json4cpp-ci.yml + permissions: + contents: read + security-events: write + secrets: inherit + + libnbtplusplus: + name: "libNBT++" + needs: [gate, lint] + if: >- + always() && + !cancelled() && + (needs.lint.result == 'success' || needs.lint.result == 'skipped') && + (needs.gate.outputs.libnbtplusplus_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/libnbtplusplus-ci.yml + permissions: + contents: read + secrets: inherit + + tomlplusplus: + name: "TOML++" + needs: [gate, lint] + if: >- + always() && + !cancelled() && + (needs.lint.result == 'success' || needs.lint.result == 'skipped') && + (needs.gate.outputs.tomlplusplus_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/tomlplusplus-ci.yml + permissions: + contents: read + secrets: inherit + + # ── Java / Minecraft Projects ─────────────────────────────────── + meshmc: + name: "MeshMC" + needs: [gate, lint] + if: >- + always() && + !cancelled() && + (needs.lint.result == 'success' || needs.lint.result == 'skipped') && + (needs.gate.outputs.meshmc_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/meshmc-build.yml + with: + build-type: ${{ needs.gate.outputs.build_type }} + permissions: + contents: read + id-token: write + packages: write + secrets: inherit + + forgewrapper: + name: "ForgeWrapper" + needs: [gate, lint] + if: >- + always() && + !cancelled() && + (needs.lint.result == 'success' || needs.lint.result == 'skipped') && + (needs.gate.outputs.forgewrapper_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/forgewrapper-build.yml + permissions: + contents: read + secrets: inherit + + # ── Container & Docker ────────────────────────────────────────── + images4docker: + name: "Docker Images" + needs: [gate, lint] + if: >- + always() && + !cancelled() && + (needs.lint.result == 'success' || needs.lint.result == 'skipped') && + (needs.gate.outputs.images4docker_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/images4docker-build.yml + permissions: + contents: read + packages: write + secrets: inherit + + # ╔════════════════════════════════════════════════════════════════╗ + # ║ STAGE 3 — Extended Analysis (only on full runs) ║ + # ║ ║ + # ║ Fuzz testing, CodeQL, static analysis — expensive jobs that ║ + # ║ run on merge queue, master, or manual dispatch only. ║ + # ╚════════════════════════════════════════════════════════════════╝ + + cmark-fuzz: + name: "CMark Fuzz" + needs: [gate, cmark] + if: >- + always() && + !cancelled() && + needs.cmark.result == 'success' && + needs.gate.outputs.run_level == 'full' && + (needs.gate.outputs.cmark_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/cmark-fuzz.yml + permissions: + contents: read + secrets: inherit + + json4cpp-fuzz: + name: "JSON4CPP Fuzz" + needs: [gate, json4cpp] + if: >- + always() && + !cancelled() && + needs.json4cpp.result == 'success' && + needs.gate.outputs.run_level == 'full' && + (needs.gate.outputs.json4cpp_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/json4cpp-fuzz.yml + permissions: + contents: read + secrets: inherit + + json4cpp-amalgam: + name: "JSON4CPP Amalgamation" + needs: [gate, json4cpp] + if: >- + always() && + !cancelled() && + needs.json4cpp.result == 'success' && + (needs.gate.outputs.json4cpp_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/json4cpp-amalgam.yml + permissions: + contents: read + secrets: inherit + + tomlplusplus-fuzz: + name: "TOML++ Fuzz" + needs: [gate, tomlplusplus] + if: >- + always() && + !cancelled() && + needs.tomlplusplus.result == 'success' && + needs.gate.outputs.run_level == 'full' && + (needs.gate.outputs.tomlplusplus_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/tomlplusplus-fuzz.yml + permissions: + contents: read + security-events: write + secrets: inherit + + neozip-fuzz: + name: "NeoZip Fuzz" + needs: [gate, neozip] + if: >- + always() && + !cancelled() && + needs.neozip.result == 'success' && + needs.gate.outputs.run_level == 'full' && + (needs.gate.outputs.neozip_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/neozip-fuzz.yml + permissions: + contents: read + secrets: inherit + + meshmc-codeql: + name: "MeshMC CodeQL" + needs: [gate, meshmc] + if: >- + always() && + !cancelled() && + needs.meshmc.result == 'success' && + needs.gate.outputs.run_level == 'full' && + (needs.gate.outputs.meshmc_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/meshmc-codeql.yml + permissions: + contents: read + security-events: write + secrets: inherit + + mnv-codeql: + name: "MNV CodeQL" + needs: [gate, mnv] + if: >- + always() && + !cancelled() && + needs.mnv.result == 'success' && + needs.gate.outputs.run_level == 'full' && + (needs.gate.outputs.mnv_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/mnv-codeql.yml + permissions: + contents: read + security-events: write + secrets: inherit + + neozip-codeql: + name: "NeoZip CodeQL" + needs: [gate, neozip] + if: >- + always() && + !cancelled() && + needs.neozip.result == 'success' && + needs.gate.outputs.run_level == 'full' && + (needs.gate.outputs.neozip_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/neozip-codeql.yml + permissions: + contents: read + security-events: write + secrets: inherit + + # ╔════════════════════════════════════════════════════════════════╗ + # ║ STAGE 4 — Containers & Nix (only on full + meshmc changes) ║ + # ╚════════════════════════════════════════════════════════════════╝ + + meshmc-container: + name: "MeshMC Container" + needs: [gate, meshmc] + if: >- + always() && + !cancelled() && + needs.meshmc.result == 'success' && + needs.gate.outputs.run_level == 'full' && + (needs.gate.outputs.meshmc_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/meshmc-container.yml + permissions: + contents: read + packages: write + secrets: inherit + + meshmc-nix: + name: "MeshMC Nix" + needs: [gate, meshmc] + if: >- + always() && + !cancelled() && + needs.meshmc.result == 'success' && + needs.gate.outputs.run_level == 'full' && + (needs.gate.outputs.meshmc_changed == 'true' || needs.gate.outputs.run_level == 'full') + uses: ./.github/workflows/meshmc-nix.yml + permissions: + contents: read + secrets: inherit + + # ╔════════════════════════════════════════════════════════════════╗ + # ║ STAGE 5 — Documentation (master push only) ║ + # ╚════════════════════════════════════════════════════════════════╝ + + json4cpp-docs: + name: "JSON4CPP Docs" + needs: [gate, json4cpp] + if: >- + always() && + !cancelled() && + needs.json4cpp.result == 'success' && + needs.gate.outputs.is_master == 'true' && + needs.gate.outputs.is_push == 'true' && + needs.gate.outputs.json4cpp_changed == 'true' + uses: ./.github/workflows/json4cpp-publish-docs.yml + permissions: + contents: write + secrets: inherit + + tomlplusplus-docs: + name: "TOML++ Docs" + needs: [gate, tomlplusplus] + if: >- + always() && + !cancelled() && + needs.tomlplusplus.result == 'success' && + needs.gate.outputs.is_master == 'true' && + needs.gate.outputs.is_push == 'true' && + needs.gate.outputs.tomlplusplus_changed == 'true' + uses: ./.github/workflows/tomlplusplus-gh-pages.yml + permissions: + contents: write + secrets: inherit + + # ╔════════════════════════════════════════════════════════════════╗ + # ║ STAGE 6 — Backport Automation (PR merge events) ║ + # ╚════════════════════════════════════════════════════════════════╝ + + backport: + name: "Backport" + needs: gate + if: >- + github.event_name == 'pull_request_target' && + github.event.pull_request.merged == true && + contains(toJSON(github.event.pull_request.labels.*.name), 'backport') + uses: ./.github/workflows/meshmc-backport.yml + permissions: + contents: write + pull-requests: write + actions: write + secrets: inherit + + merge-blocking: + name: "Merge Blocking PR" + needs: gate + if: >- + github.event_name == 'pull_request_target' && + github.event.pull_request.merged == true && + contains(toJSON(github.event.pull_request.labels.*.name), 'status: blocking') + uses: ./.github/workflows/meshmc-merge-blocking-pr.yml + secrets: inherit + + # ╔════════════════════════════════════════════════════════════════╗ + # ║ STAGE 7 — Release (tag pushes only) ║ + # ╚════════════════════════════════════════════════════════════════╝ + + meshmc-release: + name: "MeshMC Release" + needs: [gate, meshmc] + if: >- + always() && + !cancelled() && + needs.gate.outputs.is_tag == 'true' && + startsWith(github.ref, 'refs/tags/meshmc-') + uses: ./.github/workflows/meshmc-build.yml + with: + build-type: Release + environment: Release + permissions: + contents: read + id-token: write + packages: write + secrets: inherit + + neozip-release: + name: "NeoZip Release" + needs: gate + if: >- + needs.gate.outputs.is_tag == 'true' && + startsWith(github.ref, 'refs/tags/neozip-') + uses: ./.github/workflows/neozip-release.yml + permissions: + contents: read + secrets: inherit + + # ╔════════════════════════════════════════════════════════════════╗ + # ║ STAGE 8 — Final Verdicts ║ + # ║ ║ + # ║ Merge queue and branch protection rules check this job. ║ + # ║ It collects results from all stages and reports pass/fail. ║ + # ╚════════════════════════════════════════════════════════════════╝ + + verdict: + name: "CI Verdict" + if: always() + needs: + - gate + - lint + - mnv + - cgit + - cmark + - corebinutils + - genqrcode + - neozip + - json4cpp + - libnbtplusplus + - tomlplusplus + - meshmc + - forgewrapper + - images4docker + - cmark-fuzz + - json4cpp-fuzz + - json4cpp-amalgam + - tomlplusplus-fuzz + - neozip-fuzz + - meshmc-codeql + - mnv-codeql + - neozip-codeql + - meshmc-container + - meshmc-nix + runs-on: ubuntu-latest + steps: + - name: Evaluate results + run: | + set -euo pipefail + + echo "## CI Verdict" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "| Job | Result |" >> "$GITHUB_STEP_SUMMARY" + echo "|-----|--------|" >> "$GITHUB_STEP_SUMMARY" + + FAILED=false + + check_job() { + local name="$1" + local result="$2" + local icon="⬜" + case "$result" in + success) icon="✅" ;; + failure) icon="❌"; FAILED=true ;; + cancelled) icon="⏹️" ;; + skipped) icon="⏭️" ;; + esac + echo "| $name | $icon $result |" >> "$GITHUB_STEP_SUMMARY" + } + + check_job "Gate" "${{ needs.gate.result }}" + check_job "Lint" "${{ needs.lint.result }}" + check_job "MNV" "${{ needs.mnv.result }}" + check_job "CGit" "${{ needs.cgit.result }}" + check_job "CMark" "${{ needs.cmark.result }}" + check_job "CoreBinutils" "${{ needs.corebinutils.result }}" + check_job "GenQRCode" "${{ needs.genqrcode.result }}" + check_job "NeoZip" "${{ needs.neozip.result }}" + check_job "JSON4CPP" "${{ needs.json4cpp.result }}" + check_job "libNBT++" "${{ needs.libnbtplusplus.result }}" + check_job "TOML++" "${{ needs.tomlplusplus.result }}" + check_job "MeshMC" "${{ needs.meshmc.result }}" + check_job "ForgeWrapper" "${{ needs.forgewrapper.result }}" + check_job "Docker Images" "${{ needs.images4docker.result }}" + check_job "CMark Fuzz" "${{ needs.cmark-fuzz.result }}" + check_job "JSON4CPP Fuzz" "${{ needs.json4cpp-fuzz.result }}" + check_job "JSON4CPP Amalg" "${{ needs.json4cpp-amalgam.result }}" + check_job "TOML++ Fuzz" "${{ needs.tomlplusplus-fuzz.result }}" + check_job "NeoZip Fuzz" "${{ needs.neozip-fuzz.result }}" + check_job "MeshMC CodeQL" "${{ needs.meshmc-codeql.result }}" + check_job "MNV CodeQL" "${{ needs.mnv-codeql.result }}" + check_job "NeoZip CodeQL" "${{ needs.neozip-codeql.result }}" + check_job "MeshMC Docker" "${{ needs.meshmc-container.result }}" + check_job "MeshMC Nix" "${{ needs.meshmc-nix.result }}" + + echo "" >> "$GITHUB_STEP_SUMMARY" + + if [[ "$FAILED" == "true" ]]; then + echo "**Result: FAILED** — one or more required jobs failed." >> "$GITHUB_STEP_SUMMARY" + exit 1 + else + echo "**Result: PASSED** — all executed jobs succeeded." >> "$GITHUB_STEP_SUMMARY" + fi diff --git a/.github/workflows/cmark-ci.yml b/.github/workflows/cmark-ci.yml index 45daf6082f..4d1fdb2180 100644 --- a/.github/workflows/cmark-ci.yml +++ b/.github/workflows/cmark-ci.yml @@ -1,17 +1,8 @@ name: "cmark: CI" on: - push: - branches-ignore: - - 'dependabot/**' - paths: - - 'cmark/**' - - '.github/workflows/cmark-ci.yml' - pull_request: - paths: - - 'cmark/**' - - '.github/workflows/cmark-ci.yml' workflow_dispatch: + workflow_call: jobs: linter: diff --git a/.github/workflows/cmark-fuzz.yml b/.github/workflows/cmark-fuzz.yml index 28f14b8244..10d2b48ac0 100644 --- a/.github/workflows/cmark-fuzz.yml +++ b/.github/workflows/cmark-fuzz.yml @@ -1,16 +1,8 @@ name: "cmark: Fuzz" on: - push: - branches: [master] - paths: - - 'cmark/**' - - '.github/workflows/cmark-fuzz.yml' - pull_request: - paths: - - 'cmark/**' - - '.github/workflows/cmark-fuzz.yml' workflow_dispatch: + workflow_call: concurrency: group: cmark-fuzz-${{ github.ref }} diff --git a/.github/workflows/corebinutils-ci.yml b/.github/workflows/corebinutils-ci.yml index 34c9b3fcfd..74f49f4a51 100644 --- a/.github/workflows/corebinutils-ci.yml +++ b/.github/workflows/corebinutils-ci.yml @@ -1,15 +1,8 @@ name: "corebinutils: CI" on: - push: - paths: - - 'corebinutils/**' - - '.github/workflows/corebinutils-ci.yml' - pull_request: - paths: - - 'corebinutils/**' - - '.github/workflows/corebinutils-ci.yml' workflow_dispatch: + workflow_call: permissions: contents: read diff --git a/.github/workflows/forgewrapper-build.yml b/.github/workflows/forgewrapper-build.yml index 30010f6e1c..5f9f701965 100644 --- a/.github/workflows/forgewrapper-build.yml +++ b/.github/workflows/forgewrapper-build.yml @@ -1,16 +1,8 @@ name: "ForgeWrapper: Build" on: - push: - branches: [master] - paths: - - 'forgewrapper/**' - - '.github/workflows/forgewrapper-build.yml' - pull_request: - branches: [master] - paths: - - 'forgewrapper/**' - - '.github/workflows/forgewrapper-build.yml' + workflow_dispatch: + workflow_call: jobs: build: diff --git a/.github/workflows/genqrcode-ci.yml b/.github/workflows/genqrcode-ci.yml index 9931f66ec9..202eba4e43 100644 --- a/.github/workflows/genqrcode-ci.yml +++ b/.github/workflows/genqrcode-ci.yml @@ -1,14 +1,8 @@ name: "genqrcode: CI" on: - push: - paths: - - 'genqrcode/**' - - '.github/workflows/genqrcode-ci.yml' - pull_request: - paths: - - 'genqrcode/**' - - '.github/workflows/genqrcode-ci.yml' + workflow_dispatch: + workflow_call: jobs: cmake: diff --git a/.github/workflows/images4docker-build.yml b/.github/workflows/images4docker-build.yml index e91b2a5368..36778194bc 100644 --- a/.github/workflows/images4docker-build.yml +++ b/.github/workflows/images4docker-build.yml @@ -1,14 +1,8 @@ name: "images4docker: Build" on: - push: - branches: ["trunk", "master"] - paths: - - "images4docker/dockerfiles/**" - - ".github/workflows/images4docker-build.yml" - schedule: - - cron: "17 3 * * *" workflow_dispatch: + workflow_call: permissions: contents: read diff --git a/.github/workflows/json4cpp-amalgam.yml b/.github/workflows/json4cpp-amalgam.yml index c25550487c..036532b2d0 100644 --- a/.github/workflows/json4cpp-amalgam.yml +++ b/.github/workflows/json4cpp-amalgam.yml @@ -1,9 +1,8 @@ name: "json4cpp: Check amalgamation" on: - pull_request: - paths: - - 'json4cpp/**' + workflow_dispatch: + workflow_call: permissions: contents: read diff --git a/.github/workflows/json4cpp-ci.yml b/.github/workflows/json4cpp-ci.yml index 2646c1b5c9..21b1e7bfca 100644 --- a/.github/workflows/json4cpp-ci.yml +++ b/.github/workflows/json4cpp-ci.yml @@ -1,19 +1,8 @@ name: "json4cpp: CI" on: - push: - branches: - - develop - - master - - release/* - paths: - - 'json4cpp/**' - - '.github/workflows/json4cpp-ci.yml' - pull_request: - paths: - - 'json4cpp/**' - - '.github/workflows/json4cpp-ci.yml' workflow_dispatch: + workflow_call: permissions: contents: read diff --git a/.github/workflows/json4cpp-flawfinder.yml b/.github/workflows/json4cpp-flawfinder.yml index c496ab8d27..ca78ee817a 100644 --- a/.github/workflows/json4cpp-flawfinder.yml +++ b/.github/workflows/json4cpp-flawfinder.yml @@ -4,16 +4,8 @@ permissions: contents: read on: - push: - branches: ["develop"] - paths: - - 'json4cpp/**' - pull_request: - branches: ["develop"] - paths: - - 'json4cpp/**' - schedule: - - cron: '41 14 * * 3' + workflow_dispatch: + workflow_call: jobs: flawfinder: diff --git a/.github/workflows/json4cpp-fuzz.yml b/.github/workflows/json4cpp-fuzz.yml index 893435c953..dffe6adffc 100644 --- a/.github/workflows/json4cpp-fuzz.yml +++ b/.github/workflows/json4cpp-fuzz.yml @@ -1,16 +1,8 @@ name: "json4cpp: Fuzz" on: - push: - branches: [master, develop] - paths: - - 'json4cpp/**' - - '.github/workflows/json4cpp-fuzz.yml' - pull_request: - paths: - - 'json4cpp/**' - - '.github/workflows/json4cpp-fuzz.yml' workflow_dispatch: + workflow_call: concurrency: group: json4cpp-fuzz-${{ github.ref }} diff --git a/.github/workflows/json4cpp-publish-docs.yml b/.github/workflows/json4cpp-publish-docs.yml index c68dcccbd1..6bbe27d33f 100644 --- a/.github/workflows/json4cpp-publish-docs.yml +++ b/.github/workflows/json4cpp-publish-docs.yml @@ -1,13 +1,8 @@ name: "json4cpp: Publish documentation" on: - push: - branches: - - develop - paths: - - 'json4cpp/docs/mkdocs/**' - - 'json4cpp/docs/examples/**' workflow_dispatch: + workflow_call: concurrency: group: json4cpp-documentation diff --git a/.github/workflows/json4cpp-semgrep.yml b/.github/workflows/json4cpp-semgrep.yml index 6b594e6bab..0e728b3830 100644 --- a/.github/workflows/json4cpp-semgrep.yml +++ b/.github/workflows/json4cpp-semgrep.yml @@ -1,16 +1,8 @@ name: "json4cpp: Semgrep" on: - push: - branches: ["develop"] - paths: - - 'json4cpp/**' - pull_request: - branches: ["develop"] - paths: - - 'json4cpp/**' - schedule: - - cron: '23 2 * * 4' + workflow_dispatch: + workflow_call: permissions: contents: read @@ -31,10 +23,9 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: returntocorp/semgrep-action@713efdd345f3035192eaa63f56867b88e63e4e5d + - uses: semgrep/semgrep@caad1d53f48a40fa55cec9e41bf4820f115889b3 with: publishToken: ${{ secrets.SEMGREP_APP_TOKEN }} - publishDeployment: ${{ secrets.SEMGREP_DEPLOYMENT_ID }} generateSarif: "1" - name: Upload SARIF file diff --git a/.github/workflows/libnbtplusplus-ci.yml b/.github/workflows/libnbtplusplus-ci.yml index ed255a5802..3657d211a3 100644 --- a/.github/workflows/libnbtplusplus-ci.yml +++ b/.github/workflows/libnbtplusplus-ci.yml @@ -1,15 +1,8 @@ name: "libnbtplusplus: CI" on: - push: - paths: - - 'libnbtplusplus/**' - - '.github/workflows/libnbtplusplus-ci.yml' - pull_request: - paths: - - 'libnbtplusplus/**' - - '.github/workflows/libnbtplusplus-ci.yml' workflow_dispatch: + workflow_call: permissions: contents: read diff --git a/.github/workflows/meshmc-backport.yml b/.github/workflows/meshmc-backport.yml index d468fd6986..3c739a4a26 100644 --- a/.github/workflows/meshmc-backport.yml +++ b/.github/workflows/meshmc-backport.yml @@ -1,8 +1,8 @@ name: "MeshMC: Backport" on: - pull_request_target: - types: [closed, labeled] + workflow_dispatch: + workflow_call: permissions: {} diff --git a/.github/workflows/meshmc-build.yml b/.github/workflows/meshmc-build.yml index d7b0a2335e..5468afc409 100644 --- a/.github/workflows/meshmc-build.yml +++ b/.github/workflows/meshmc-build.yml @@ -5,20 +5,6 @@ concurrency: cancel-in-progress: true on: - push: - branches: - - 'master' - paths: - - 'meshmc/**' - - '.github/workflows/meshmc-build.yml' - - '.github/actions/meshmc/**' - merge_group: - types: [checks_requested] - pull_request: - paths: - - 'meshmc/**' - - '.github/workflows/meshmc-build.yml' - - '.github/actions/meshmc/**' workflow_call: inputs: build-type: diff --git a/.github/workflows/meshmc-codeql.yml b/.github/workflows/meshmc-codeql.yml index 6dd764849a..4aff63060b 100644 --- a/.github/workflows/meshmc-codeql.yml +++ b/.github/workflows/meshmc-codeql.yml @@ -5,13 +5,8 @@ concurrency: cancel-in-progress: true on: - merge_group: - types: [checks_requested] - pull_request: - paths: - - 'meshmc/**' - - '.github/workflows/meshmc-codeql.yml' workflow_dispatch: + workflow_call: permissions: {} diff --git a/.github/workflows/meshmc-container.yml b/.github/workflows/meshmc-container.yml index 3a41e15bcd..dc9d3520db 100644 --- a/.github/workflows/meshmc-container.yml +++ b/.github/workflows/meshmc-container.yml @@ -5,19 +5,8 @@ concurrency: cancel-in-progress: true on: - push: - branches: - - 'master' - paths: - - 'meshmc/Containerfile' - - '.github/workflows/meshmc-container.yml' - merge_group: - types: [checks_requested] - pull_request: - paths: - - 'meshmc/Containerfile' - - '.github/workflows/meshmc-container.yml' workflow_dispatch: + workflow_call: permissions: {} diff --git a/.github/workflows/meshmc-flake-update.yml b/.github/workflows/meshmc-flake-update.yml index c0be2756d4..758a471ee4 100644 --- a/.github/workflows/meshmc-flake-update.yml +++ b/.github/workflows/meshmc-flake-update.yml @@ -1,9 +1,8 @@ name: "MeshMC: Update Flake" on: - schedule: - - cron: "0 0 * * 0" workflow_dispatch: + workflow_call: permissions: {} diff --git a/.github/workflows/meshmc-merge-blocking-pr.yml b/.github/workflows/meshmc-merge-blocking-pr.yml index 3542a470e0..978d3e2677 100644 --- a/.github/workflows/meshmc-merge-blocking-pr.yml +++ b/.github/workflows/meshmc-merge-blocking-pr.yml @@ -10,6 +10,7 @@ on: description: Local Pull Request number to work on required: true type: number + workflow_call: permissions: {} diff --git a/.github/workflows/meshmc-nix.yml b/.github/workflows/meshmc-nix.yml index 21af121a34..a764fa70ff 100644 --- a/.github/workflows/meshmc-nix.yml +++ b/.github/workflows/meshmc-nix.yml @@ -5,48 +5,8 @@ concurrency: cancel-in-progress: true on: - push: - branches: - - "master" - - "release-*" - tags: - - "*" - paths: - - "meshmc/**.cpp" - - "meshmc/**.h" - - "meshmc/**.java" - - "meshmc/**.ui" - - "meshmc/**.md" - - "meshmc/**.nix" - - "meshmc/nix/**" - - "meshmc/flake.lock" - - "meshmc/buildconfig/**" - - "meshmc/cmake/**" - - "meshmc/launcher/**" - - "meshmc/libraries/**" - - "meshmc/branding/**" - - "meshmc/tests/**" - - "meshmc/CMakeLists.txt" - - ".github/workflows/meshmc-nix.yml" - pull_request: - paths: - - "meshmc/**.cpp" - - "meshmc/**.h" - - "meshmc/**.java" - - "meshmc/**.ui" - - "meshmc/**.md" - - "meshmc/**.nix" - - "meshmc/nix/**" - - "meshmc/flake.lock" - - "meshmc/buildconfig/**" - - "meshmc/cmake/**" - - "meshmc/launcher/**" - - "meshmc/libraries/**" - - "meshmc/branding/**" - - "meshmc/tests/**" - - "meshmc/CMakeLists.txt" - - ".github/workflows/meshmc-nix.yml" workflow_dispatch: + workflow_call: permissions: {} diff --git a/.github/workflows/mnv-ci.yml b/.github/workflows/mnv-ci.yml index b588d03df2..89d592eec3 100644 --- a/.github/workflows/mnv-ci.yml +++ b/.github/workflows/mnv-ci.yml @@ -1,17 +1,8 @@ name: "mnv: CI" on: - push: - branches: ['**'] - paths: - - 'mnv/**' - - '.github/workflows/mnv-ci.yml' - - '.github/actions/mnv/**' - pull_request: - paths: - - 'mnv/**' - - '.github/workflows/mnv-ci.yml' - - '.github/actions/mnv/**' + workflow_dispatch: + workflow_call: concurrency: group: mnv-${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} diff --git a/.github/workflows/mnv-codeql.yml b/.github/workflows/mnv-codeql.yml index 0cad996fa1..ed3adae7c1 100644 --- a/.github/workflows/mnv-codeql.yml +++ b/.github/workflows/mnv-codeql.yml @@ -1,16 +1,8 @@ name: "mnv: CodeQL" on: - push: - branches: [master] - paths: - - 'mnv/**' - pull_request: - branches: [master] - paths: - - 'mnv/**' - schedule: - - cron: '0 18 * * 1' + workflow_dispatch: + workflow_call: concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} diff --git a/.github/workflows/mnv-coverity.yml b/.github/workflows/mnv-coverity.yml index 6ebb70125b..b0de61694c 100644 --- a/.github/workflows/mnv-coverity.yml +++ b/.github/workflows/mnv-coverity.yml @@ -1,9 +1,8 @@ name: "mnv: Coverity" on: - schedule: - - cron: '42 0 * * *' workflow_dispatch: + workflow_call: permissions: contents: read diff --git a/.github/workflows/mnv-link-check.yml b/.github/workflows/mnv-link-check.yml index 6b9d16ca59..8495c6df32 100644 --- a/.github/workflows/mnv-link-check.yml +++ b/.github/workflows/mnv-link-check.yml @@ -2,8 +2,7 @@ name: "mnv: Check Links" on: workflow_dispatch: - schedule: - - cron: '0 3 * * 0' + workflow_call: jobs: lychee: diff --git a/.github/workflows/neozip-ci.yml b/.github/workflows/neozip-ci.yml index 32c12a2c8d..2a0532fd21 100644 --- a/.github/workflows/neozip-ci.yml +++ b/.github/workflows/neozip-ci.yml @@ -1,15 +1,8 @@ name: "neozip: CI" on: - push: - paths: - - 'neozip/**' - - '.github/workflows/neozip-*.yml' - pull_request: - paths: - - 'neozip/**' - - '.github/workflows/neozip-*.yml' workflow_dispatch: + workflow_call: concurrency: group: neozip-${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/neozip-codeql.yml b/.github/workflows/neozip-codeql.yml index 2bc5f489bd..48bcae3607 100644 --- a/.github/workflows/neozip-codeql.yml +++ b/.github/workflows/neozip-codeql.yml @@ -3,8 +3,6 @@ name: "neozip: CodeQL" on: workflow_call: workflow_dispatch: - schedule: - - cron: "27 17 * * 0" jobs: analyze: diff --git a/.github/workflows/neozip-fuzz.yml b/.github/workflows/neozip-fuzz.yml index 59b74debab..2530386d59 100644 --- a/.github/workflows/neozip-fuzz.yml +++ b/.github/workflows/neozip-fuzz.yml @@ -1,16 +1,8 @@ name: "neozip: Fuzz" on: - push: - branches: [master, develop] - paths: - - 'neozip/**' - - '.github/workflows/neozip-fuzz.yml' - pull_request: - paths: - - 'neozip/**' - - '.github/workflows/neozip-fuzz.yml' workflow_dispatch: + workflow_call: concurrency: group: neozip-fuzz-${{ github.ref }} diff --git a/.github/workflows/neozip-lint.yml b/.github/workflows/neozip-lint.yml index 775856874c..1e25739b2d 100644 --- a/.github/workflows/neozip-lint.yml +++ b/.github/workflows/neozip-lint.yml @@ -1,10 +1,8 @@ name: "neozip: Lint" on: - pull_request: - paths: - - 'neozip/**' workflow_dispatch: + workflow_call: jobs: lint: diff --git a/.github/workflows/neozip-release.yml b/.github/workflows/neozip-release.yml index 6f9610eaf3..77f23d4150 100644 --- a/.github/workflows/neozip-release.yml +++ b/.github/workflows/neozip-release.yml @@ -1,9 +1,8 @@ name: "neozip: Release" on: - push: - tags: - - 'neozip-*' + workflow_dispatch: + workflow_call: defaults: run: diff --git a/.github/workflows/repo-dependency-review.yml b/.github/workflows/repo-dependency-review.yml index 469ecfbdfb..6b65fadd71 100644 --- a/.github/workflows/repo-dependency-review.yml +++ b/.github/workflows/repo-dependency-review.yml @@ -3,7 +3,8 @@ name: "Dependency Review" # Scans dependency manifest changes in every pull request. # Blocks merging of PRs that introduce known-vulnerable packages. on: - pull_request: + workflow_dispatch: + workflow_call: permissions: contents: read diff --git a/.github/workflows/repo-scorecards.yml b/.github/workflows/repo-scorecards.yml index 06291bb004..2dc2ca7a96 100644 --- a/.github/workflows/repo-scorecards.yml +++ b/.github/workflows/repo-scorecards.yml @@ -1,9 +1,9 @@ name: "Scorecard supply-chain security" on: + workflow_dispatch: + workflow_call: branch_protection_rule: - schedule: - - cron: '20 7 * * 2' push: branches: [master, main, develop] diff --git a/.github/workflows/repo-stale.yml b/.github/workflows/repo-stale.yml index 9919ce314a..4973acd172 100644 --- a/.github/workflows/repo-stale.yml +++ b/.github/workflows/repo-stale.yml @@ -1,8 +1,8 @@ name: "Stale: Comment and close stale issues and PRs" on: - schedule: - - cron: '0 0 * * *' + workflow_dispatch: + workflow_call: permissions: contents: read diff --git a/.github/workflows/tomlplusplus-ci.yml b/.github/workflows/tomlplusplus-ci.yml index 974601fdf9..829c985e4d 100644 --- a/.github/workflows/tomlplusplus-ci.yml +++ b/.github/workflows/tomlplusplus-ci.yml @@ -1,25 +1,8 @@ name: "tomlplusplus: CI" on: - push: - paths: - - "tomlplusplus/**.h" - - "tomlplusplus/**.hpp" - - "tomlplusplus/**.cpp" - - "tomlplusplus/**.inl" - - "tomlplusplus/**.py" - - "tomlplusplus/**/meson.build" - - ".github/workflows/tomlplusplus-ci.yml" - pull_request: - paths: - - "tomlplusplus/**.h" - - "tomlplusplus/**.hpp" - - "tomlplusplus/**.cpp" - - "tomlplusplus/**.inl" - - "tomlplusplus/**.py" - - "tomlplusplus/**/meson.build" - - ".github/workflows/tomlplusplus-ci.yml" workflow_dispatch: + workflow_call: concurrency: group: tomlplusplus-${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/tomlplusplus-fuzz.yml b/.github/workflows/tomlplusplus-fuzz.yml index 7d346a172c..f326b6301b 100644 --- a/.github/workflows/tomlplusplus-fuzz.yml +++ b/.github/workflows/tomlplusplus-fuzz.yml @@ -1,16 +1,8 @@ name: "tomlplusplus: Fuzz" on: - push: - branches: [master] - paths: - - 'tomlplusplus/**' - - '.github/workflows/tomlplusplus-fuzz.yml' - pull_request: - paths: - - 'tomlplusplus/**' - - '.github/workflows/tomlplusplus-fuzz.yml' workflow_dispatch: + workflow_call: concurrency: group: tomlplusplus-fuzz-${{ github.ref }} diff --git a/.github/workflows/tomlplusplus-gh-pages.yml b/.github/workflows/tomlplusplus-gh-pages.yml index f25064b6bb..33161dbf2d 100644 --- a/.github/workflows/tomlplusplus-gh-pages.yml +++ b/.github/workflows/tomlplusplus-gh-pages.yml @@ -1,17 +1,8 @@ name: "tomlplusplus: gh-pages" on: - push: - branches: - - master - paths: - - 'tomlplusplus/**.h' - - 'tomlplusplus/**.hpp' - - 'tomlplusplus/**.dox' - - 'tomlplusplus/**.md' - - 'tomlplusplus/docs/**' - - '.github/workflows/tomlplusplus-gh-pages.yml' workflow_dispatch: + workflow_call: jobs: gh-pages: diff --git a/REUSE.toml b/REUSE.toml index cc53e482c1..5ec4afbba4 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -19,7 +19,8 @@ path = [ ".clang-format", ".clang-tidy", "CODEOWNERS", - "hooks/**" + "hooks/**", + "ci/**" ] SPDX-License-Identifier = "CC0-1.0" SPDX-FileCopyrightText = "NONE" @@ -75,8 +76,8 @@ SPDX-License-Identifier = "GPL-3.0-or-later" SPDX-FileCopyrightText = "2026 Project Tick" [[annotations]] -path = ["uvim/**"] -SPDX-License-Identifier = "Vim" +path = ["mnv/**"] +SPDX-License-Identifier = "Vim AND GPL-3.0-or-later" SPDX-FileCopyrightText = "Bram Moolenaar & Vim Contributors & Project Tick" [[annotations]] diff --git a/ci/OWNERS b/ci/OWNERS new file mode 100644 index 0000000000..e2dd4dfb79 --- /dev/null +++ b/ci/OWNERS @@ -0,0 +1,330 @@ +# This file describes who owns what in the Project Tick CI infrastructure. +# Users/teams will get review requests for PRs that change their files. +# +# This file uses the same syntax as the natively supported CODEOWNERS file, +# see https://help.github.com/articles/about-codeowners/ for documentation. +# +# Validated by ci/codeowners-validator. + +/.github/actions/change-analysis/ @YongDo-Hyun +/.github/actions/meshmc/package/ @YongDo-Hyun +/.github/actions/meshmc/setup-dependencies/ @YongDo-Hyun +/.github/actions/mnv/test_artefacts/ @YongDo-Hyun +/.github/codeql/ @YongDo-Hyun +/.github/ISSUE_TEMPLATE/ @YongDo-Hyun +/.github/workflows/ @YongDo-Hyun + +/archived/projt-launcher/ @YongDo-Hyun +/archived/projt-minicraft-modpack/ @YongDo-Hyun +/archived/projt-modpack/ @YongDo-Hyun +/archived/ptlibzippy/ @YongDo-Hyun + +/cgit/* @YongDo-Hyun +/cgit/contrib/* @YongDo-Hyun +/cgit/contrib/hooks/ @YongDo-Hyun +/cgit/filters/ @YongDo-Hyun +/cgit/tests/ @YongDo-Hyun + +/cmark/* @YongDo-Hyun +/cmark/api_test/ @YongDo-Hyun +/cmark/bench/ @YongDo-Hyun +/cmark/cmake/ @YongDo-Hyun +/cmark/data/ @YongDo-Hyun +/cmark/fuzz/ @YongDo-Hyun +/cmark/man/ @YongDo-Hyun +/cmark/src/ @YongDo-Hyun +/cmark/test/ @YongDo-Hyun +/cmark/tools/ @YongDo-Hyun +/cmark/wrappers/ @YongDo-Hyun + +/corebinutils/* @YongDo-Hyun +/corebinutils/cat/ @YongDo-Hyun +/corebinutils/chflags/ @YongDo-Hyun +/corebinutils/chmod/ @YongDo-Hyun +/corebinutils/contrib/* @YongDo-Hyun +/corebinutils/contrib/libc-vis/ @YongDo-Hyun +/corebinutils/contrib/libedit/ @YongDo-Hyun +/corebinutils/contrib/printf/ @YongDo-Hyun +/corebinutils/cp/ @YongDo-Hyun +/corebinutils/cpuset/ @YongDo-Hyun +/corebinutils/csh/ @YongDo-Hyun +/corebinutils/date/ @YongDo-Hyun +/corebinutils/dd/ @YongDo-Hyun +/corebinutils/df/ @YongDo-Hyun +/corebinutils/domainname/ @YongDo-Hyun +/corebinutils/echo/ @YongDo-Hyun +/corebinutils/ed/ @YongDo-Hyun +/corebinutils/expr/ @YongDo-Hyun +/corebinutils/freebsd-version/ @YongDo-Hyun +/corebinutils/getfacl/ @YongDo-Hyun +/corebinutils/hostname/ @YongDo-Hyun +/corebinutils/kill/ @YongDo-Hyun +/corebinutils/ln/ @YongDo-Hyun +/corebinutils/ls/ @YongDo-Hyun +/corebinutils/mkdir/ @YongDo-Hyun +/corebinutils/mv/ @YongDo-Hyun +/corebinutils/nproc/ @YongDo-Hyun +/corebinutils/pax/ @YongDo-Hyun +/corebinutils/pkill/ @YongDo-Hyun +/corebinutils/ps/ @YongDo-Hyun +/corebinutils/pwait/ @YongDo-Hyun +/corebinutils/pwd/ @YongDo-Hyun +/corebinutils/realpath/ @YongDo-Hyun +/corebinutils/rm/ @YongDo-Hyun +/corebinutils/rmail/ @YongDo-Hyun +/corebinutils/rmdir/ @YongDo-Hyun +/corebinutils/setfacl/ @YongDo-Hyun +/corebinutils/sh/ @YongDo-Hyun +/corebinutils/sleep/ @YongDo-Hyun +/corebinutils/stty/ @YongDo-Hyun +/corebinutils/sync/ @YongDo-Hyun +/corebinutils/test/ @YongDo-Hyun +/corebinutils/timeout/ @YongDo-Hyun +/corebinutils/uuidgen/ @YongDo-Hyun + +/forgewrapper/* @YongDo-Hyun +/forgewrapper/gradle/ @YongDo-Hyun +/forgewrapper/jigsaw/ @YongDo-Hyun +/forgewrapper/src/ @YongDo-Hyun + +/genqrcode/* @YongDo-Hyun +/genqrcode/cmake/ @YongDo-Hyun +/genqrcode/tests/ @YongDo-Hyun +/genqrcode/use/ @YongDo-Hyun + +/hooks/ @YongDo-Hyun + +/images4docker/ @YongDo-Hyun + +/json4cpp/* @YongDo-Hyun +/json4cpp/.reuse/ @YongDo-Hyun +/json4cpp/cmake/ @YongDo-Hyun +/json4cpp/docs/ @YongDo-Hyun +/json4cpp/include/* @YongDo-Hyun +/json4cpp/include/nlohmann/* @YongDo-Hyun +/json4cpp/include/nlohmann/detail/* @YongDo-Hyun +/json4cpp/include/nlohmann/detail/conversions/ @YongDo-Hyun +/json4cpp/include/nlohmann/detail/input/ @YongDo-Hyun +/json4cpp/include/nlohmann/detail/iterators/ @YongDo-Hyun +/json4cpp/include/nlohmann/detail/meta/* @YongDo-Hyun +/json4cpp/include/nlohmann/detail/meta/call_std/ @YongDo-Hyun +/json4cpp/include/nlohmann/thirdparty/* @YongDo-Hyun +/json4cpp/include/nlohmann/thirdparty/hedley/ @YongDo-Hyun +/json4cpp/LICENSES/ @YongDo-Hyun +/json4cpp/single_include/* @YongDo-Hyun +/json4cpp/single_include/nlohmann/ @YongDo-Hyun +/json4cpp/src/* @YongDo-Hyun +/json4cpp/src/modules/ @YongDo-Hyun +/json4cpp/tests/ @YongDo-Hyun +/json4cpp/tests/abi/ @YongDo-Hyun +/json4cpp/tests/benchmarks/ @YongDo-Hyun +/json4cpp/tests/cmake_add_subdirectory/ @YongDo-Hyun +/json4cpp/tests/cmake_fetch_content/ @YongDo-Hyun +/json4cpp/tests/cmake_fetch_content2/ @YongDo-Hyun +/json4cpp/tests/cmake_import/ @YongDo-Hyun +/json4cpp/tests/cmake_import_minver/ @YongDo-Hyun +/json4cpp/tests/cmake_target_include_directories/ @YongDo-Hyun +/json4cpp/tests/cuda_example/ @YongDo-Hyun +/json4cpp/tests/module_cpp20/ @YongDo-Hyun +/json4cpp/tests/reports/ @YongDo-Hyun +/json4cpp/tests/src/ @YongDo-Hyun +/json4cpp/tests/thirdparty/* @YongDo-Hyun +/json4cpp/tests/thirdparty/doctest/ @YongDo-Hyun +/json4cpp/tests/thirdparty/fifo_map/ @YongDo-Hyun +/json4cpp/tests/thirdparty/Fuzzer/ @YongDo-Hyun +/json4cpp/tests/thirdparty/imapdl/ @YongDo-Hyun +/json4cpp/tools/* @YongDo-Hyun +/json4cpp/tools/amalgamate/ @YongDo-Hyun +/json4cpp/tools/astyle/ @YongDo-Hyun +/json4cpp/tools/gdb_pretty_printer/ @YongDo-Hyun +/json4cpp/tools/generate_natvis/ @YongDo-Hyun +/json4cpp/tools/macro_builder/ @YongDo-Hyun +/json4cpp/tools/serve_header/ @YongDo-Hyun + +/libnbtplusplus/* @YongDo-Hyun +/libnbtplusplus/include/* @YongDo-Hyun +/libnbtplusplus/include/io/ @YongDo-Hyun +/libnbtplusplus/include/text/ @YongDo-Hyun +/libnbtplusplus/src/* @YongDo-Hyun +/libnbtplusplus/src/io/ @YongDo-Hyun +/libnbtplusplus/src/text/ @YongDo-Hyun +/libnbtplusplus/test/* @YongDo-Hyun +/libnbtplusplus/test/testfiles/ @YongDo-Hyun + +/LICENSES/ @YongDo-Hyun + +/meshmc/* @YongDo-Hyun +/meshmc/branding/ @YongDo-Hyun +/meshmc/buildconfig/ @YongDo-Hyun +/meshmc/cmake/* @YongDo-Hyun +/meshmc/cmake/UnitTest/ @YongDo-Hyun +/meshmc/cmake/vcpkg-ports/ @YongDo-Hyun +/meshmc/cmake/vcpkg-triplets/ @YongDo-Hyun +/meshmc/doc/ @YongDo-Hyun +/meshmc/launcher/* @YongDo-Hyun +/meshmc/launcher/icons/ @YongDo-Hyun +/meshmc/launcher/java/* @YongDo-Hyun +/meshmc/launcher/java/download/ @YongDo-Hyun +/meshmc/launcher/launch/* @YongDo-Hyun +/meshmc/launcher/launch/steps/* @YongDo-Hyun +/meshmc/launcher/meta/ @YongDo-Hyun +/meshmc/launcher/minecraft/* @YongDo-Hyun +/meshmc/launcher/minecraft/auth/* @YongDo-Hyun +/meshmc/launcher/minecraft/auth/flows/ @YongDo-Hyun +/meshmc/launcher/minecraft/auth/steps/ @YongDo-Hyun +/meshmc/launcher/minecraft/gameoptions/ @YongDo-Hyun +/meshmc/launcher/minecraft/launch/ @YongDo-Hyun +/meshmc/launcher/minecraft/legacy/ @YongDo-Hyun +/meshmc/launcher/minecraft/mod/ @YongDo-Hyun +/meshmc/launcher/minecraft/services/ @YongDo-Hyun +/meshmc/launcher/minecraft/testdata/ @YongDo-Hyun +/meshmc/launcher/minecraft/update/ @YongDo-Hyun +/meshmc/launcher/modplatform/* @YongDo-Hyun +/meshmc/launcher/modplatform/atlauncher/ @YongDo-Hyun +/meshmc/launcher/modplatform/flame/ @YongDo-Hyun +/meshmc/launcher/modplatform/legacy_ftb/ @YongDo-Hyun +/meshmc/launcher/modplatform/modpacksch/ @YongDo-Hyun +/meshmc/launcher/modplatform/modrinth/ @YongDo-Hyun +/meshmc/launcher/modplatform/technic/ @YongDo-Hyun +/meshmc/launcher/mojang/* @YongDo-Hyun +/meshmc/launcher/mojang/testdata/ @YongDo-Hyun +/meshmc/launcher/net/ @YongDo-Hyun +/meshmc/launcher/news/ @YongDo-Hyun +/meshmc/launcher/notifications/ @YongDo-Hyun +/meshmc/launcher/pathmatcher/ @YongDo-Hyun +/meshmc/launcher/resources/* @YongDo-Hyun +/meshmc/launcher/resources/assets/ @YongDo-Hyun +/meshmc/launcher/resources/backgrounds/ @YongDo-Hyun +/meshmc/launcher/resources/breeze_dark/ @YongDo-Hyun +/meshmc/launcher/resources/breeze_light/ @YongDo-Hyun +/meshmc/launcher/resources/documents/ @YongDo-Hyun +/meshmc/launcher/resources/flat/ @YongDo-Hyun +/meshmc/launcher/resources/flat_white/ @YongDo-Hyun +/meshmc/launcher/resources/iOS/ @YongDo-Hyun +/meshmc/launcher/resources/multimc/ @YongDo-Hyun +/meshmc/launcher/resources/OSX/ @YongDo-Hyun +/meshmc/launcher/resources/pe_blue/ @YongDo-Hyun +/meshmc/launcher/resources/pe_colored/ @YongDo-Hyun +/meshmc/launcher/resources/pe_dark/ @YongDo-Hyun +/meshmc/launcher/resources/pe_light/ @YongDo-Hyun +/meshmc/launcher/resources/shaders/ @YongDo-Hyun +/meshmc/launcher/resources/sources/ @YongDo-Hyun +/meshmc/launcher/screenshots/ @YongDo-Hyun +/meshmc/launcher/settings/ @YongDo-Hyun +/meshmc/launcher/tasks/ @YongDo-Hyun +/meshmc/launcher/testdata/ @YongDo-Hyun +/meshmc/launcher/tools/ @YongDo-Hyun +/meshmc/launcher/translations/ @YongDo-Hyun +/meshmc/launcher/ui/* @YongDo-Hyun +/meshmc/launcher/ui/dialogs/ @YongDo-Hyun +/meshmc/launcher/ui/instanceview/ @YongDo-Hyun +/meshmc/launcher/ui/pagedialog/ @YongDo-Hyun +/meshmc/launcher/ui/pages/* @YongDo-Hyun +/meshmc/launcher/ui/pages/global/ @YongDo-Hyun +/meshmc/launcher/ui/pages/instance/ @YongDo-Hyun +/meshmc/launcher/ui/pages/modplatform/* @YongDo-Hyun +/meshmc/launcher/ui/pages/modplatform/atlauncher/ @YongDo-Hyun +/meshmc/launcher/ui/pages/modplatform/flame/ @YongDo-Hyun +/meshmc/launcher/ui/pages/modplatform/ftb/ @YongDo-Hyun +/meshmc/launcher/ui/pages/modplatform/legacy_ftb @YongDo-Hyun +/meshmc/launcher/ui/pages/modplatform/modrinth/ @YongDo-Hyun +/meshmc/launcher/ui/pages/modplatform/technic/ @YongDo-Hyun +/meshmc/launcher/ui/setupwizard/ @YongDo-Hyun +/meshmc/launcher/ui/themes/ @YongDo-Hyun +/meshmc/launcher/ui/widgets/ @YongDo-Hyun +/meshmc/launcher/updater/* @YongDo-Hyun +/meshmc/launcher/updater/testdata/ @YongDo-Hyun +/meshmc/libraries/* @YongDo-Hyun +/meshmc/libraries/classparser/ @YongDo-Hyun +/meshmc/libraries/ganalytics/ @YongDo-Hyun +/meshmc/libraries/hoedown/ @YongDo-Hyun +/meshmc/libraries/iconfix/ @YongDo-Hyun +/meshmc/libraries/javacheck/ @YongDo-Hyun +/meshmc/libraries/katabasis/ @YongDo-Hyun +/meshmc/libraries/launcher/ @YongDo-Hyun +/meshmc/libraries/LocalPeer/ @YongDo-Hyun +/meshmc/libraries/optional-bare/ @YongDo-Hyun +/meshmc/libraries/rainbow/ @YongDo-Hyun +/meshmc/libraries/systeminfo/ @YongDo-Hyun +/meshmc/libraries/tomlc99 @YongDo-Hyun +/meshmc/libraries/xz-embedded @YongDo-Hyun +/meshmc/nix/ @YongDo-Hyun +/meshmc/scripts/ @YongDo-Hyun +/meshmc/updater/ @YongDo-Hyun + +/meta/* @YongDo-Hyun +/meta/fuzz/ @YongDo-Hyun +/meta/meta/* @YongDo-Hyun +/meta/meta/common/ @YongDo-Hyun +/meta/meta/model/ @YongDo-Hyun +/meta/meta/run/ @YongDo-Hyun +/meta/nix/ @YongDo-Hyun + +/mnv/* @YongDo-Hyun +/mnv/ci/ @YongDo-Hyun +/mnv/cmake/ @YongDo-Hyun +/mnv/lang/ @YongDo-Hyun +/mnv/nsis/ @YongDo-Hyun +/mnv/pixmaps/ @YongDo-Hyun +/mnv/READMEdir/ @YongDo-Hyun +/mnv/runtime/* @YongDo-Hyun +/mnv/runtime/autoload/* @YongDo-Hyun +/mnv/runtime/autoload/cargo/ @YongDo-Hyun +/mnv/runtime/autoload/dist/ @YongDo-Hyun +/mnv/runtime/autoload/rust/ @YongDo-Hyun +/mnv/runtime/autoload/xml/ @YongDo-Hyun +/mnv/runtime/bitmaps/ @YongDo-Hyun +/mnv/runtime/colors/* @YongDo-Hyun +/mnv/runtime/colors/lists/ @YongDo-Hyun +/mnv/runtime/colors/tools/ @YongDo-Hyun +/mnv/runtime/compiler/ @YongDo-Hyun +/mnv/runtime/doc/ @YongDo-Hyun +/mnv/runtime/ftplugin/ @YongDo-Hyun +/mnv/runtime/icons/ @YongDo-Hyun +/mnv/runtime/import/ @YongDo-Hyun +/mnv/runtime/indent/ @YongDo-Hyun +/mnv/runtime/keymap/ @YongDo-Hyun +/mnv/runtime/lang/ @YongDo-Hyun +/mnv/runtime/macros/ @YongDo-Hyun +/mnv/runtime/pack/ @YongDo-Hyun +/mnv/runtime/plugin/ @YongDo-Hyun +/mnv/runtime/print/ @YongDo-Hyun +/mnv/runtime/spell/ @YongDo-Hyun +/mnv/runtime/syntax/ @YongDo-Hyun +/mnv/runtime/tools/ @YongDo-Hyun +/mnv/runtime/tutor/ @YongDo-Hyun +/mnv/src/ @YongDo-Hyun +/mnv/tools/ @YongDo-Hyun + +/neozip/* @YongDo-Hyun +/neozip/arch/* @YongDo-Hyun +/neozip/arch/arm/ @YongDo-Hyun +/neozip/arch/generic/ @YongDo-Hyun +/neozip/arch/loongarch/ @YongDo-Hyun +/neozip/arch/power/ @YongDo-Hyun +/neozip/arch/riscv/ @YongDo-Hyun +/neozip/arch/s390/ @YongDo-Hyun +/neozip/arch/x86/ @YongDo-Hyun +/neozip/cmake/ @YongDo-Hyun +/neozip/doc/ @YongDo-Hyun +/neozip/test/ @YongDo-Hyun +/neozip/tools/ @YongDo-Hyun +/neozip/win32/ @YongDo-Hyun + +/tomlplusplus/* @YongDo-Hyun +/tomlplusplus/cmake/ @YongDo-Hyun +/tomlplusplus/docs/* @YongDo-Hyun +/tomlplusplus/docs/pages/ @YongDo-Hyun +/tomlplusplus/docs/images/ @YongDo-Hyun +/tomlplusplus/examples/ @YongDo-Hyun +/tomlplusplus/fuzzing/ @YongDo-Hyun +/tomlplusplus/include/* @YongDo-Hyun +/tomlplusplus/include/toml++/* @YongDo-Hyun +/tomlplusplus/include/toml++/impl/ @YongDo-Hyun +/tomlplusplus/src/* @YongDo-Hyun +/tomlplusplus/src/modules/ @YongDo-Hyun +/tomlplusplus/tests/* @YongDo-Hyun +/tomlplusplus/tests/vs/ @YongDo-Hyun +/tomlplusplus/toml-test/ @YongDo-Hyun +/tomlplusplus/tools/ @YongDo-Hyun +/tomlplusplus/vendor/ @YongDo-Hyun diff --git a/ci/README.md b/ci/README.md new file mode 100644 index 0000000000..f1e2becff2 --- /dev/null +++ b/ci/README.md @@ -0,0 +1,76 @@ +# CI Infrastructure — Project Tick + +This directory contains CI support files for the Project Tick monorepo. + +## Structure + +``` +ci/ +├── OWNERS # CI code ownership (CODEOWNERS format) +├── README.md # This file +├── default.nix # Nix CI entry point (treefmt, codeowners-validator) +├── pinned.json # Pinned Nixpkgs revision for reproducibility +├── update-pinned.sh # Update pinned.json via npins +├── supportedBranches.js # Branch classification for CI decisions +├── codeowners-validator/ # Builds codeowners-validator from source +│ ├── default.nix +│ ├── owners-file-name.patch +│ └── permissions.patch +└── github-script/ # GitHub Actions JavaScript helpers + ├── run # CLI entry point for local testing + ├── lint-commits.js # Commit message linter (Conventional Commits) + ├── prepare.js # PR preparation & validation + ├── reviews.js # GitHub review state management + ├── get-pr-commit-details.js # Extract commit details from PRs + ├── withRateLimit.js # GitHub API rate limit helper + ├── package.json # Node.js dependencies + └── shell.nix # Nix dev environment +``` + +## Pinned Nixpkgs + +CI uses a pinned Nixpkgs revision from [`pinned.json`](./pinned.json) to ensure +reproducible builds and formatting. Run [`update-pinned.sh`](./update-pinned.sh) +to update it. + +## GitHub Script + +JavaScript-based CI scripts using [`actions/github-script`](https://github.com/actions/github-script). + +### Local Testing + +```bash +cd ci/github-script +nix-shell # or: nix develop +gh auth login # ensure GitHub CLI is authenticated +./run lint-commits +./run prepare +``` + +## Branch Classification + +`ci/supportedBranches.js` classifies repository branches for CI decisions: + +| Prefix | Type | Description | +|--------------|--------------------------|-------------------------------------| +| `master` | development / primary | Main development branch | +| `release-*` | development / primary | Release branches (e.g. release-1.0) | +| `staging-*` | development / secondary | Pre-release staging | +| `feature-*` | wip | Feature branches | +| `fix-*` | wip | Bug fix branches | +| `backport-*` | wip | Backport branches | + +## Commit Conventions + +Project Tick uses [Conventional Commits](https://www.conventionalcommits.org/): + +``` +type(scope): subject + +feat(mnv): add new keybinding support +fix(meshmc): resolve crash on startup +ci(neozip): update build matrix +docs(cmark): fix API reference +``` + +The commit linter (`ci/github-script/lint-commits.js`) validates this format in PRs. diff --git a/ci/codeowners-validator/default.nix b/ci/codeowners-validator/default.nix new file mode 100644 index 0000000000..76638c6ac1 --- /dev/null +++ b/ci/codeowners-validator/default.nix @@ -0,0 +1,31 @@ +{ + buildGoModule, + fetchFromGitHub, + fetchpatch, +}: +buildGoModule { + name = "codeowners-validator"; + src = fetchFromGitHub { + owner = "mszostok"; + repo = "codeowners-validator"; + rev = "f3651e3810802a37bd965e6a9a7210728179d076"; + hash = "sha256-5aSmmRTsOuPcVLWfDF6EBz+6+/Qpbj66udAmi1CLmWQ="; + }; + patches = [ + # https://github.com/mszostok/codeowners-validator/pull/222 + (fetchpatch { + name = "user-write-access-check"; + url = "https://github.com/mszostok/codeowners-validator/compare/f3651e3810802a37bd965e6a9a7210728179d076...840eeb88b4da92bda3e13c838f67f6540b9e8529.patch"; + hash = "sha256-t3Dtt8SP9nbO3gBrM0nRE7+G6N/ZIaczDyVHYAG/6mU="; + }) + # Undoes part of the above PR: We don't want to require write access + # to the repository, that's only needed for GitHub's native CODEOWNERS. + # Furthermore, it removes an unnecessary check from the code + # that breaks tokens generated for GitHub Apps. + ./permissions.patch + # Allows setting a custom CODEOWNERS path using the OWNERS_FILE env var + ./owners-file-name.patch + ]; + postPatch = "rm -r docs/investigation"; + vendorHash = "sha256-R+pW3xcfpkTRqfS2ETVOwG8PZr0iH5ewroiF7u8hcYI="; +} diff --git a/ci/codeowners-validator/owners-file-name.patch b/ci/codeowners-validator/owners-file-name.patch new file mode 100644 index 0000000000..d8b87ba2f8 --- /dev/null +++ b/ci/codeowners-validator/owners-file-name.patch @@ -0,0 +1,15 @@ +diff --git a/pkg/codeowners/owners.go b/pkg/codeowners/owners.go +index 6910bd2..e0c95e9 100644 +--- a/pkg/codeowners/owners.go ++++ b/pkg/codeowners/owners.go +@@ -39,6 +39,10 @@ func NewFromPath(repoPath string) ([]Entry, error) { + // openCodeownersFile finds a CODEOWNERS file and returns content. + // see: https://help.github.com/articles/about-code-owners/#codeowners-file-location + func openCodeownersFile(dir string) (io.Reader, error) { ++ if file, ok := os.LookupEnv("OWNERS_FILE"); ok { ++ return fs.Open(file) ++ } ++ + var detectedFiles []string + for _, p := range []string{".", "docs", ".github"} { + pth := path.Join(dir, p) diff --git a/ci/codeowners-validator/permissions.patch b/ci/codeowners-validator/permissions.patch new file mode 100644 index 0000000000..38f42f4839 --- /dev/null +++ b/ci/codeowners-validator/permissions.patch @@ -0,0 +1,36 @@ +diff --git a/internal/check/valid_owner.go b/internal/check/valid_owner.go +index a264bcc..610eda8 100644 +--- a/internal/check/valid_owner.go ++++ b/internal/check/valid_owner.go +@@ -16,7 +16,6 @@ import ( + const scopeHeader = "X-OAuth-Scopes" + + var reqScopes = map[github.Scope]struct{}{ +- github.ScopeReadOrg: {}, + } + + type ValidOwnerConfig struct { +@@ -223,10 +222,7 @@ func (v *ValidOwner) validateTeam(ctx context.Context, name string) *validateErr + for _, t := range v.repoTeams { + // GitHub normalizes name before comparison + if strings.EqualFold(t.GetSlug(), team) { +- if t.Permissions["push"] { +- return nil +- } +- return newValidateError("Team %q cannot review PRs on %q as neither it nor any parent team has write permissions.", team, v.orgRepoName) ++ return nil + } + } + +@@ -245,10 +241,7 @@ func (v *ValidOwner) validateGitHubUser(ctx context.Context, name string) *valid + for _, u := range v.repoUsers { + // GitHub normalizes name before comparison + if strings.EqualFold(u.GetLogin(), userName) { +- if u.Permissions["push"] { +- return nil +- } +- return newValidateError("User %q cannot review PRs on %q as they don't have write permissions.", userName, v.orgRepoName) ++ return nil + } + } + diff --git a/ci/default.nix b/ci/default.nix new file mode 100644 index 0000000000..3badcac036 --- /dev/null +++ b/ci/default.nix @@ -0,0 +1,93 @@ +let + pinned = (builtins.fromJSON (builtins.readFile ./pinned.json)).pins; +in +{ + system ? builtins.currentSystem, + nixpkgs ? null, +}: +let + nixpkgs' = + if nixpkgs == null then + fetchTarball { + inherit (pinned.nixpkgs) url; + sha256 = pinned.nixpkgs.hash; + } + else + nixpkgs; + + pkgs = import nixpkgs' { + inherit system; + config = { }; + overlays = [ ]; + }; + + fmt = + let + treefmtNixSrc = fetchTarball { + inherit (pinned.treefmt-nix) url; + sha256 = pinned.treefmt-nix.hash; + }; + treefmtEval = (import treefmtNixSrc).evalModule pkgs { + projectRootFile = ".git/config"; + + settings.verbose = 1; + settings.on-unmatched = "debug"; + + programs.actionlint.enable = true; + + programs.biome = { + enable = true; + validate.enable = false; + settings.formatter = { + useEditorconfig = true; + }; + settings.javascript.formatter = { + quoteStyle = "single"; + semicolons = "asNeeded"; + }; + settings.json.formatter.enabled = false; + }; + settings.formatter.biome.excludes = [ + "*.min.js" + ]; + + programs.keep-sorted.enable = true; + + programs.nixfmt = { + enable = true; + package = pkgs.nixfmt; + }; + + programs.yamlfmt = { + enable = true; + settings.formatter = { + retain_line_breaks = true; + }; + }; + + programs.zizmor.enable = true; + }; + fs = pkgs.lib.fileset; + src = fs.toSource { + root = ../.; + fileset = fs.difference ../. (fs.maybeMissing ../.git); + }; + in + { + shell = treefmtEval.config.build.devShell; + pkg = treefmtEval.config.build.wrapper; + check = treefmtEval.config.build.check src; + }; + +in +rec { + inherit pkgs fmt; + codeownersValidator = pkgs.callPackage ./codeowners-validator { }; + + shell = pkgs.mkShell { + packages = [ + fmt.pkg + codeownersValidator + ]; + }; +} diff --git a/ci/github-script/.editorconfig b/ci/github-script/.editorconfig new file mode 100644 index 0000000000..67d678ef17 --- /dev/null +++ b/ci/github-script/.editorconfig @@ -0,0 +1,3 @@ +[run] +indent_style = space +indent_size = 2 diff --git a/ci/github-script/.gitignore b/ci/github-script/.gitignore new file mode 100644 index 0000000000..6b8a37657b --- /dev/null +++ b/ci/github-script/.gitignore @@ -0,0 +1,2 @@ +node_modules +step-summary.md diff --git a/ci/github-script/.npmrc b/ci/github-script/.npmrc new file mode 100644 index 0000000000..fb41d64f46 --- /dev/null +++ b/ci/github-script/.npmrc @@ -0,0 +1,2 @@ +package-lock-only = true +save-exact = true diff --git a/ci/github-script/README.md b/ci/github-script/README.md new file mode 100644 index 0000000000..f0965d693c --- /dev/null +++ b/ci/github-script/README.md @@ -0,0 +1,27 @@ +# GitHub CI Scripts — Project Tick + +JavaScript-based CI scripts using [`actions/github-script`](https://github.com/actions/github-script). + +## Local Testing + +```bash +cd ci/github-script +nix-shell # sets up Node.js + dependencies +gh auth login # ensure GitHub CLI is authenticated +``` + +### Available Commands + +#### Lint Commits +Validates commit messages follow [Conventional Commits](https://www.conventionalcommits.org/) format. + +```bash +./run lint-commits YongDo-Hyun Project-Tick 123 +``` + +#### Prepare +Checks PR mergeability and validates branch targeting. + +```bash +./run prepare YongDo-Hyun Project-Tick 123 +``` diff --git a/ci/github-script/get-pr-commit-details.js b/ci/github-script/get-pr-commit-details.js new file mode 100644 index 0000000000..fcccfeacd7 --- /dev/null +++ b/ci/github-script/get-pr-commit-details.js @@ -0,0 +1,101 @@ +// @ts-check +const { promisify } = require('node:util') +const execFile = promisify(require('node:child_process').execFile) + +/** + * @param {{ + * args: string[] + * core: import('@actions/core'), + * quiet?: boolean, + * repoPath?: string, + * }} RunGitProps + */ +async function runGit({ args, repoPath, core, quiet }) { + if (repoPath) { + args = ['-C', repoPath, ...args] + } + + if (!quiet) { + core.info(`About to run \`git ${args.map((s) => `'${s}'`).join(' ')}\``) + } + + return await execFile('git', args) +} + +/** + * Gets the SHA, subject and changed files for each commit in the given PR. + * + * Don't use GitHub API at all: the "list commits on PR" endpoint has a limit + * of 250 commits and doesn't return the changed files. + * + * @param {{ + * core: import('@actions/core'), + * pr: Awaited["rest"]["pulls"]["get"]>>["data"] + * repoPath?: string, + * }} GetCommitMessagesForPRProps + * + * @returns {Promise<{ + * subject: string, + * sha: string, + * changedPaths: string[], + * changedPathSegments: Set, + * }[]>} + */ +async function getCommitDetailsForPR({ core, pr, repoPath }) { + await runGit({ + args: ['fetch', `--depth=1`, 'origin', pr.base.sha], + repoPath, + core, + }) + await runGit({ + args: ['fetch', `--depth=${pr.commits + 1}`, 'origin', pr.head.sha], + repoPath, + core, + }) + + const shas = ( + await runGit({ + args: [ + 'rev-list', + `--max-count=${pr.commits}`, + `${pr.base.sha}..${pr.head.sha}`, + ], + repoPath, + core, + }) + ).stdout + .split('\n') + .map((s) => s.trim()) + .filter(Boolean) + + return Promise.all( + shas.map(async (sha) => { + // Subject first, then a blank line, then filenames. + const result = ( + await runGit({ + args: ['log', '--format=%s', '--name-only', '-1', sha], + repoPath, + core, + quiet: true, + }) + ).stdout.split('\n') + + const subject = result[0] + + const changedPaths = result.slice(2, -1) + + const changedPathSegments = new Set( + changedPaths.flatMap((path) => path.split('/')), + ) + + return { + sha, + subject, + changedPaths, + changedPathSegments, + } + }), + ) +} + +module.exports = { getCommitDetailsForPR } diff --git a/ci/github-script/lint-commits.js b/ci/github-script/lint-commits.js new file mode 100644 index 0000000000..ad8f1c63ac --- /dev/null +++ b/ci/github-script/lint-commits.js @@ -0,0 +1,177 @@ +// @ts-check +const { classify } = require('../supportedBranches.js') +const { getCommitDetailsForPR } = require('./get-pr-commit-details.js') + +// Supported Conventional Commit types for Project Tick +const CONVENTIONAL_TYPES = [ + 'build', + 'chore', + 'ci', + 'docs', + 'feat', + 'fix', + 'perf', + 'refactor', + 'revert', + 'style', + 'test', +] + +// Known project scopes in the monorepo +const KNOWN_SCOPES = [ + 'archived', + 'cgit', + 'ci', + 'cmark', + 'corebinutils', + 'forgewrapper', + 'genqrcode', + 'hooks', + 'images4docker', + 'json4cpp', + 'libnbtplusplus', + 'meshmc', + 'meta', + 'mnv', + 'neozip', + 'tomlplusplus', + 'repo', + 'deps', +] + +/** + * Validates commit messages against Project Tick Conventional Commits conventions. + * + * Format: type(scope): subject + * type — one of: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert + * scope — optional, should match a project directory or be a known scope + * subject — imperative, no trailing period, no uppercase first letter + * + * @param {{ + * github: InstanceType, + * context: import('@actions/github/lib/context').Context, + * core: import('@actions/core'), + * repoPath?: string, + * }} CheckCommitMessagesProps + */ +async function checkCommitMessages({ github, context, core, repoPath }) { + const pull_number = context.payload.pull_request?.number + if (!pull_number) { + core.info('This is not a pull request. Skipping checks.') + return + } + + const pr = ( + await github.rest.pulls.get({ + ...context.repo, + pull_number, + }) + ).data + + const baseBranchType = classify( + pr.base.ref.replace(/^refs\/heads\//, ''), + ).type + const headBranchType = classify( + pr.head.ref.replace(/^refs\/heads\//, ''), + ).type + + if ( + baseBranchType.includes('development') && + headBranchType.includes('development') && + pr.base.repo.id === pr.head.repo?.id + ) { + core.info( + 'This PR is from one development branch to another. Skipping checks.', + ) + return + } + + const commits = await getCommitDetailsForPR({ core, pr, repoPath }) + + const failures = new Set() + const warnings = new Set() + + const conventionalRegex = new RegExp( + `^(${CONVENTIONAL_TYPES.join('|')})(\\(([^)]+)\\))?(!)?: .+$`, + ) + + for (const commit of commits) { + const msg = commit.subject + const logPrefix = `Commit ${commit.sha.slice(0, 12)}` + + // Check: fixup/squash commits + const fixups = ['amend!', 'fixup!', 'squash!'] + if (fixups.some((s) => msg.startsWith(s))) { + core.error( + `${logPrefix}: starts with "${fixups.find((s) => msg.startsWith(s))}". ` + + 'Did you forget to run `git rebase -i --autosquash`?', + ) + failures.add(commit.sha) + continue + } + + // Check: Conventional Commit format + if (!conventionalRegex.test(msg)) { + core.error( + `${logPrefix}: "${msg}" does not follow Conventional Commits format. ` + + 'Expected: type(scope): subject (e.g. "feat(mnv): add keybinding")', + ) + failures.add(commit.sha) + continue + } + + // Extract parts + const match = msg.match(conventionalRegex) + const type = match[1] + const scope = match[3] || null + + // Check: trailing period + if (msg.endsWith('.')) { + core.error( + `${logPrefix}: subject should not end with a period.`, + ) + failures.add(commit.sha) + } + + // Warning: unknown scope + if (scope && !KNOWN_SCOPES.includes(scope)) { + core.warning( + `${logPrefix}: scope "${scope}" is not a known project. ` + + `Known scopes: ${KNOWN_SCOPES.join(', ')}`, + ) + warnings.add(commit.sha) + } + + // Check: subject should not start with uppercase (after type(scope): ) + const subjectStart = msg.indexOf(': ') + 2 + if (subjectStart < msg.length) { + const firstChar = msg[subjectStart] + if (firstChar === firstChar.toUpperCase() && firstChar !== firstChar.toLowerCase()) { + core.warning( + `${logPrefix}: subject should start with lowercase letter.`, + ) + warnings.add(commit.sha) + } + } + + if (!failures.has(commit.sha)) { + core.info(`${logPrefix}: "${msg}" — passed.`) + } + } + + if (failures.size !== 0) { + core.error( + 'Please review the Conventional Commits guidelines at ' + + ' and the project CONTRIBUTING.md.', + ) + core.setFailed( + `${failures.size} commit(s) do not follow commit conventions.`, + ) + } else if (warnings.size !== 0) { + core.warning( + `${warnings.size} commit(s) have minor issues (see warnings above).`, + ) + } +} + +module.exports = checkCommitMessages diff --git a/ci/github-script/package-lock.json b/ci/github-script/package-lock.json new file mode 100644 index 0000000000..ce766a5550 --- /dev/null +++ b/ci/github-script/package-lock.json @@ -0,0 +1,1856 @@ +{ + "name": "github-script", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@actions/artifact": "5.0.3", + "@actions/core": "1.11.1", + "@actions/github": "6.0.1", + "bottleneck": "2.19.5", + "commander": "14.0.3" + } + }, + "node_modules/@actions/artifact": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-5.0.3.tgz", + "integrity": "sha512-FIEG8Kum0wABZnktJvFi1xuVPc31xrunhZwLCvjrCGISQOm0ifyo7cjqf6PHiEeqoWMa5HIGOsB+lGM4aKCseA==", + "license": "MIT", + "dependencies": { + "@actions/core": "^2.0.0", + "@actions/github": "^6.0.1", + "@actions/http-client": "^3.0.2", + "@azure/storage-blob": "^12.29.1", + "@octokit/core": "^5.2.1", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-retry": "^3.0.9", + "@octokit/request": "^8.4.1", + "@octokit/request-error": "^5.1.1", + "@protobuf-ts/plugin": "^2.2.3-alpha.1", + "archiver": "^7.0.1", + "jwt-decode": "^3.1.2", + "unzip-stream": "^0.3.1" + } + }, + "node_modules/@actions/artifact/node_modules/@actions/core": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-2.0.3.tgz", + "integrity": "sha512-Od9Thc3T1mQJYddvVPM4QGiLUewdh+3txmDYHHxoNdkqysR1MbCT+rFOtNUxYAz+7+6RIsqipVahY2GJqGPyxA==", + "license": "MIT", + "dependencies": { + "@actions/exec": "^2.0.0", + "@actions/http-client": "^3.0.2" + } + }, + "node_modules/@actions/artifact/node_modules/@actions/exec": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-2.0.0.tgz", + "integrity": "sha512-k8ngrX2voJ/RIN6r9xB82NVqKpnMRtxDoiO+g3olkIUpQNqjArXrCQceduQZCQj3P3xm32pChRLqRrtXTlqhIw==", + "license": "MIT", + "dependencies": { + "@actions/io": "^2.0.0" + } + }, + "node_modules/@actions/artifact/node_modules/@actions/http-client": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.2.tgz", + "integrity": "sha512-JP38FYYpyqvUsz+Igqlc/JG6YO9PaKuvqjM3iGvaLqFnJ7TFmcLyy2IDrY0bI0qCQug8E9K+elv5ZNfw62ZJzA==", + "license": "MIT", + "dependencies": { + "tunnel": "^0.0.6", + "undici": "^6.23.0" + } + }, + "node_modules/@actions/artifact/node_modules/@actions/io": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-2.0.0.tgz", + "integrity": "sha512-Jv33IN09XLO+0HS79aaODsvIRyduiF7NY/F6LYeK5oeUmrsz7aFdRphQjFoESF4jS7lMauDOttKALcpapVDIAg==", + "license": "MIT" + }, + "node_modules/@actions/artifact/node_modules/undici": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", + "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/@actions/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz", + "integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==", + "license": "MIT", + "dependencies": { + "@actions/exec": "^1.1.1", + "@actions/http-client": "^2.0.1" + } + }, + "node_modules/@actions/exec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "license": "MIT", + "dependencies": { + "@actions/io": "^1.0.1" + } + }, + "node_modules/@actions/github": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-6.0.1.tgz", + "integrity": "sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==", + "license": "MIT", + "dependencies": { + "@actions/http-client": "^2.2.0", + "@octokit/core": "^5.0.1", + "@octokit/plugin-paginate-rest": "^9.2.2", + "@octokit/plugin-rest-endpoint-methods": "^10.4.0", + "@octokit/request": "^8.4.1", + "@octokit/request-error": "^5.1.1", + "undici": "^5.28.5" + } + }, + "node_modules/@actions/github/node_modules/@octokit/plugin-paginate-rest": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz", + "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@actions/github/node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", + "license": "MIT" + }, + "node_modules/@actions/github/node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, + "node_modules/@actions/github/node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz", + "integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@actions/github/node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", + "license": "MIT" + }, + "node_modules/@actions/github/node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, + "node_modules/@actions/http-client": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz", + "integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==", + "license": "MIT", + "dependencies": { + "tunnel": "^0.0.6", + "undici": "^5.25.4" + } + }, + "node_modules/@actions/io": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", + "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==", + "license": "MIT" + }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz", + "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-http-compat": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.2.tgz", + "integrity": "sha512-Tf6ltdKzOJEgxZeWLCjMxrxbodB/ZeCbzzA1A2qHbhzAjzjHoBVSUeSl/baT/oHAxhc4qdqVaDKnc2+iE932gw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@azure/core-client": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0" + } + }, + "node_modules/@azure/core-lro": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", + "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-paging": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", + "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.2.tgz", + "integrity": "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", + "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-xml": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.5.0.tgz", + "integrity": "sha512-D/sdlJBMJfx7gqoj66PKVmhDDaU6TKA49ptcolxdas29X7AfvLTmfAGLjAcIMBK7UZ2o4lygHIqVckOlQU3xWw==", + "license": "MIT", + "dependencies": { + "fast-xml-parser": "^5.0.7", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/storage-blob": { + "version": "12.31.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.31.0.tgz", + "integrity": "sha512-DBgNv10aCSxopt92DkTDD0o9xScXeBqPKGmR50FPZQaEcH4JLQ+GEOGEDv19V5BMkB7kxr+m4h6il/cCDPvmHg==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.3", + "@azure/core-http-compat": "^2.2.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.6.2", + "@azure/core-rest-pipeline": "^1.19.1", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/core-xml": "^1.4.5", + "@azure/logger": "^1.1.4", + "@azure/storage-common": "^12.3.0", + "events": "^3.0.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/storage-common": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/@azure/storage-common/-/storage-common-12.3.0.tgz", + "integrity": "sha512-/OFHhy86aG5Pe8dP5tsp+BuJ25JOAl9yaMU3WZbkeoiFMHFtJ7tu5ili7qEdBXNW9G5lDB19trwyI6V49F/8iQ==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-http-compat": "^2.2.0", + "@azure/core-rest-pipeline": "^1.19.1", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.1.4", + "events": "^3.3.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.6.0.tgz", + "integrity": "sha512-6cuonJVNOIL7lTj5zgo/Rc2bKAo4/GvN+rKCrUj7GdEHRzCk8zKOfFwUsL9nAVk5rSIsRmlgcpLzTRysopEeeg==", + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/@bufbuild/protoplugin": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-2.6.0.tgz", + "integrity": "sha512-mfAwI+4GqUtbw/ddfyolEHaAL86ozRIVlOg2A+SVRbjx1CjsMc1YJO+hBSkt/pqfpR+PmWBbZLstHbXP8KGtMQ==", + "license": "Apache-2.0", + "dependencies": { + "@bufbuild/protobuf": "2.6.0", + "@typescript/vfs": "^1.5.2", + "typescript": "5.4.5" + } + }, + "node_modules/@bufbuild/protoplugin/node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz", + "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.4.1", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "license": "MIT" + }, + "node_modules/@octokit/core/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", + "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "license": "MIT" + }, + "node_modules/@octokit/endpoint/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", + "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", + "license": "MIT", + "dependencies": { + "@octokit/request": "^8.4.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "license": "MIT" + }, + "node_modules/@octokit/graphql/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==", + "license": "MIT" + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "license": "MIT", + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-retry": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-3.0.9.tgz", + "integrity": "sha512-r+fArdP5+TG6l1Rv/C9hVoty6tldw6cE2pRHNGmFPdyfrc696R6JjrQ3d7HdVqGwuzfyrcaLAKD7K8TX8aehUQ==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.0.3", + "bottleneck": "^2.15.3" + } + }, + "node_modules/@octokit/request": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", + "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^9.0.6", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", + "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "license": "MIT" + }, + "node_modules/@octokit/request-error/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/request/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "license": "MIT" + }, + "node_modules/@octokit/request/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobuf-ts/plugin": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/plugin/-/plugin-2.11.1.tgz", + "integrity": "sha512-HyuprDcw0bEEJqkOWe1rnXUP0gwYLij8YhPuZyZk6cJbIgc/Q0IFgoHQxOXNIXAcXM4Sbehh6kjVnCzasElw1A==", + "license": "Apache-2.0", + "dependencies": { + "@bufbuild/protobuf": "^2.4.0", + "@bufbuild/protoplugin": "^2.4.0", + "@protobuf-ts/protoc": "^2.11.1", + "@protobuf-ts/runtime": "^2.11.1", + "@protobuf-ts/runtime-rpc": "^2.11.1", + "typescript": "^3.9" + }, + "bin": { + "protoc-gen-dump": "bin/protoc-gen-dump", + "protoc-gen-ts": "bin/protoc-gen-ts" + } + }, + "node_modules/@protobuf-ts/protoc": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/protoc/-/protoc-2.11.1.tgz", + "integrity": "sha512-mUZJaV0daGO6HUX90o/atzQ6A7bbN2RSuHtdwo8SSF2Qoe3zHwa4IHyCN1evftTeHfLmdz+45qo47sL+5P8nyg==", + "license": "Apache-2.0", + "bin": { + "protoc": "protoc.js" + } + }, + "node_modules/@protobuf-ts/runtime": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime/-/runtime-2.11.1.tgz", + "integrity": "sha512-KuDaT1IfHkugM2pyz+FwiY80ejWrkH1pAtOBOZFuR6SXEFTsnb/jiQWQ1rCIrcKx2BtyxnxW6BWwsVSA/Ie+WQ==", + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/@protobuf-ts/runtime-rpc": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.11.1.tgz", + "integrity": "sha512-4CqqUmNA+/uMz00+d3CYKgElXO9VrEbucjnBFEjqI4GuDrEQ32MaI3q+9qPBvIGOlL4PmHXrzM32vBPWRhQKWQ==", + "license": "Apache-2.0", + "dependencies": { + "@protobuf-ts/runtime": "^2.11.1" + } + }, + "node_modules/@typescript/vfs": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.1.tgz", + "integrity": "sha512-JwoxboBh7Oz1v38tPbkrZ62ZXNHAk9bJ7c9x0eI5zBfBnBYGhURdbnh7Z4smN/MV48Y5OCcZb58n972UtbazsA==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.3.tgz", + "integrity": "sha512-91fp6CAAJSRtH5ja95T1FHSKa8aPW9/Zw6cta81jlZTUw/+Vq8jM/AfF/14h2b71wwR84JUTW/3Y8QPhDAawFA==", + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", + "license": "Apache-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", + "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "license": "Apache-2.0" + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "license": "MIT", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "license": "MIT/X11", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "license": "ISC" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, + "node_modules/fast-xml-parser": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.6.tgz", + "integrity": "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.2" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jwt-decode": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", + "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==", + "license": "MIT" + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/streamx": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", + "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strnum": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", + "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "license": "MIT/X11", + "engines": { + "node": "*" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "license": "ISC" + }, + "node_modules/unzip-stream": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/unzip-stream/-/unzip-stream-0.3.4.tgz", + "integrity": "sha512-PyofABPVv+d7fL7GOpusx7eRT9YETY2X04PhwbSipdj6bMxVCFJrr+nm0Mxqbf9hUiTin/UsnuFWBXlDZFy0Cw==", + "license": "MIT", + "dependencies": { + "binary": "^0.3.0", + "mkdirp": "^0.5.1" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + } + } +} diff --git a/ci/github-script/package.json b/ci/github-script/package.json new file mode 100644 index 0000000000..1517886232 --- /dev/null +++ b/ci/github-script/package.json @@ -0,0 +1,13 @@ +{ + "private": true, + "//": [ + "Keep @actions/core and @actions/github in sync with", + "https://github.com/actions/github-script/blob/main/package.json." + ], + "dependencies": { + "@actions/core": "1.11.1", + "@actions/github": "6.0.1", + "bottleneck": "2.19.5", + "commander": "14.0.3" + } +} diff --git a/ci/github-script/prepare.js b/ci/github-script/prepare.js new file mode 100644 index 0000000000..9f98e8d392 --- /dev/null +++ b/ci/github-script/prepare.js @@ -0,0 +1,181 @@ +// @ts-check +const { classify } = require('../supportedBranches.js') +const { postReview, dismissReviews } = require('./reviews.js') +const reviewKey = 'prepare' + +/** + * Prepares a PR for CI by checking mergeability and branch targeting. + * + * Outputs: + * - base: base branch classification + * - head: head branch classification + * - mergedSha: the merge commit SHA (or head SHA if conflict) + * - targetSha: the target comparison SHA + * + * @param {{ + * github: InstanceType, + * context: import('@actions/github/lib/context').Context, + * core: import('@actions/core'), + * dry?: boolean, + * }} PrepareProps + */ +module.exports = async ({ github, context, core, dry }) => { + const pull_number = context.payload.pull_request.number + + for (const retryInterval of [5, 10, 20, 40, 80]) { + core.info('Checking whether the pull request can be merged...') + const prInfo = ( + await github.rest.pulls.get({ + ...context.repo, + pull_number, + }) + ).data + + if (prInfo.state !== 'open') throw new Error('PR is not open anymore.') + + if (prInfo.mergeable == null) { + core.info( + `GitHub is still computing mergeability, waiting ${retryInterval}s...`, + ) + await new Promise((resolve) => setTimeout(resolve, retryInterval * 1000)) + continue + } + + const { base, head } = prInfo + + const baseClassification = classify(base.ref) + core.setOutput('base', baseClassification) + console.log('base classification:', baseClassification) + + const headClassification = + base.repo.full_name === head.repo.full_name + ? classify(head.ref) + : { type: ['wip'] } + core.setOutput('head', headClassification) + console.log('head classification:', headClassification) + + // Warn if targeting a release branch with a non-backport/fix branch + if ( + baseClassification.stable && + baseClassification.type.includes('primary') + ) { + const headPrefix = head.ref.split('-')[0] + if (!['backport', 'fix', 'revert'].includes(headPrefix)) { + core.warning( + `This PR targets release branch \`${base.ref}\`. ` + + 'New features should typically target \`master\`.', + ) + } + } + + // Check base branch targeting + if (headClassification.type.includes('wip')) { + // Determine the best base branch candidate + const branches = ( + await github.paginate(github.rest.repos.listBranches, { + ...context.repo, + per_page: 100, + }) + ).map(({ name }) => classify(name)) + + const releases = branches + .filter(({ stable, type }) => type.includes('primary') && stable) + .sort((a, b) => b.version.localeCompare(a.version)) + + async function mergeBase({ branch, order, version }) { + const { data } = await github.rest.repos.compareCommitsWithBasehead({ + ...context.repo, + basehead: `${branch}...${head.sha}`, + per_page: 1, + page: 2, + }) + return { + branch, + order, + version, + commits: data.total_commits, + sha: data.merge_base_commit.sha, + } + } + + let candidates = [await mergeBase(classify('master'))] + for (const release of releases) { + const nextCandidate = await mergeBase(release) + if (candidates[0].commits === nextCandidate.commits) + candidates.push(nextCandidate) + if (candidates[0].commits > nextCandidate.commits) + candidates = [nextCandidate] + if (candidates[0].commits < 10000) break + } + + const best = candidates.sort((a, b) => a.order - b.order).at(0) + + core.info(`Best base branch candidate: ${best.branch}`) + + if (best.branch !== base.ref) { + const current = await mergeBase(classify(base.ref)) + const body = [ + `This PR targets \`${current.branch}\`, but based on the commit history ` + + `\`${best.branch}\` appears to be a better fit ` + + `(${current.commits - best.commits} fewer commits ahead).`, + '', + `If this is intentional, you can ignore this message. Otherwise:`, + `- [Change the base branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-base-branch-of-a-pull-request) to \`${best.branch}\`.`, + ].join('\n') + + await postReview({ github, context, core, dry, body, reviewKey }) + core.warning(`PR may target the wrong base branch.`) + } else { + await dismissReviews({ github, context, core, dry, reviewKey }) + } + } else { + await dismissReviews({ github, context, core, dry, reviewKey }) + } + + let mergedSha, targetSha + + if (prInfo.mergeable) { + core.info('The PR can be merged.') + mergedSha = prInfo.merge_commit_sha + targetSha = ( + await github.rest.repos.getCommit({ + ...context.repo, + ref: prInfo.merge_commit_sha, + }) + ).data.parents[0].sha + } else { + core.warning('The PR has a merge conflict.') + mergedSha = head.sha + targetSha = ( + await github.rest.repos.compareCommitsWithBasehead({ + ...context.repo, + basehead: `${base.sha}...${head.sha}`, + }) + ).data.merge_base_commit.sha + } + + core.info(`merged: ${mergedSha}\ntarget: ${targetSha}`) + core.setOutput('mergedSha', mergedSha) + core.setOutput('targetSha', targetSha) + + // Detect touched CI-relevant files + const files = ( + await github.paginate(github.rest.pulls.listFiles, { + ...context.repo, + pull_number, + per_page: 100, + }) + ).map((file) => file.filename) + + const touched = [] + if (files.some((f) => f.startsWith('ci/'))) touched.push('ci') + if (files.includes('ci/pinned.json')) touched.push('pinned') + if (files.some((f) => f.startsWith('.github/'))) touched.push('github') + core.setOutput('touched', touched) + + return + } + throw new Error( + 'Timed out waiting for GitHub to compute mergeability. Check https://www.githubstatus.com.', + ) +} diff --git a/ci/github-script/reviews.js b/ci/github-script/reviews.js new file mode 100644 index 0000000000..b26615fae2 --- /dev/null +++ b/ci/github-script/reviews.js @@ -0,0 +1,197 @@ +// @ts-check + +const eventToState = { + COMMENT: 'COMMENTED', + REQUEST_CHANGES: 'CHANGES_REQUESTED', +} + +/** + * @param {{ + * github: InstanceType, + * context: import('@actions/github/lib/context').Context, + * core: import('@actions/core'), + * dry: boolean, + * reviewKey?: string, + * }} DismissReviewsProps + */ +async function dismissReviews({ github, context, core, dry, reviewKey }) { + const pull_number = context.payload.pull_request?.number + if (!pull_number) { + core.warning('dismissReviews called outside of pull_request context') + return + } + + if (dry) return + + const reviews = ( + await github.paginate(github.rest.pulls.listReviews, { + ...context.repo, + pull_number, + }) + ).filter( + (review) => + review.user?.login === 'github-actions[bot]' && + review.state !== 'DISMISSED', + ) + const changesRequestedReviews = reviews.filter( + (review) => review.state === 'CHANGES_REQUESTED', + ) + + const commentRegex = // + const reviewKeyRegex = new RegExp( + ``, + ) + const commentResolvedRegex = + // + + let reviewsToMinimize = reviews + let /** @type {typeof reviews} */ reviewsToDismiss = [] + let /** @type {typeof reviews} */ reviewsToResolve = [] + + if (reviewKey && reviews.every((review) => commentRegex.test(review.body))) { + reviewsToMinimize = reviews.filter((review) => + reviewKeyRegex.test(review.body), + ) + } + + if ( + changesRequestedReviews.every( + (review) => + commentResolvedRegex.test(review.body) || + (reviewKey && reviewKeyRegex.test(review.body)), + ) + ) { + reviewsToDismiss = changesRequestedReviews + } else if (reviewsToMinimize.length) { + reviewsToResolve = reviewsToMinimize.filter( + (review) => + review.state === 'CHANGES_REQUESTED' && + !commentResolvedRegex.test(review.body), + ) + } + + await Promise.all([ + ...reviewsToMinimize.map(async (review) => + github.graphql( + `mutation($node_id:ID!) { + minimizeComment(input: { + classifier: OUTDATED, + subjectId: $node_id + }) + { clientMutationId } + }`, + { node_id: review.node_id }, + ), + ), + ...reviewsToDismiss.map(async (review) => + github.rest.pulls.dismissReview({ + ...context.repo, + pull_number, + review_id: review.id, + message: 'Review dismissed automatically', + }), + ), + ...reviewsToResolve.map(async (review) => + github.rest.pulls.updateReview({ + ...context.repo, + pull_number, + review_id: review.id, + body: review.body.replace( + reviewKeyRegex, + ``, + ), + }), + ), + ]) +} + +/** + * @param {{ + * github: InstanceType, + * context: import('@actions/github/lib/context').Context + * core: import('@actions/core'), + * dry: boolean, + * body: string, + * event?: keyof eventToState, + * reviewKey: string, + * }} PostReviewProps + */ +async function postReview({ + github, + context, + core, + dry, + body, + event = 'REQUEST_CHANGES', + reviewKey, +}) { + const pull_number = context.payload.pull_request?.number + if (!pull_number) { + core.warning('postReview called outside of pull_request context') + return + } + + const reviewKeyRegex = new RegExp( + ``, + ) + const reviewKeyComment = `` + body = body + '\n\n' + reviewKeyComment + + const reviews = ( + await github.paginate(github.rest.pulls.listReviews, { + ...context.repo, + pull_number, + }) + ).filter( + (review) => + review.user?.login === 'github-actions[bot]' && + review.state !== 'DISMISSED', + ) + + /** @type {null | typeof reviews[number]} */ + let pendingReview + const matchingReviews = reviews.filter((review) => + reviewKeyRegex.test(review.body), + ) + + if (matchingReviews.length === 0) { + pendingReview = null + } else if ( + matchingReviews.length === 1 && + matchingReviews[0].state === eventToState[event] + ) { + pendingReview = matchingReviews[0] + } else { + await dismissReviews({ github, context, core, dry, reviewKey }) + pendingReview = null + } + + if (pendingReview !== null) { + if (pendingReview.body === body) { + core.info('Review is already up to date') + return + } + + core.info('Updating existing review') + if (!dry) { + await github.rest.pulls.updateReview({ + ...context.repo, + pull_number, + review_id: pendingReview.id, + body, + }) + } + } else { + core.info(`Posting review (event: ${event})`) + if (!dry) { + await github.rest.pulls.createReview({ + ...context.repo, + pull_number, + event, + body, + }) + } + } +} + +module.exports = { postReview, dismissReviews } diff --git a/ci/github-script/run b/ci/github-script/run new file mode 100755 index 0000000000..b523cc2dbc --- /dev/null +++ b/ci/github-script/run @@ -0,0 +1,65 @@ +#!/usr/bin/env -S node --import ./run +import { execSync } from 'node:child_process' +import { closeSync, openSync } from 'node:fs' +import { program } from 'commander' +import * as core from '@actions/core' +import { getOctokit } from '@actions/github' + +async function run(action, owner, repo, pull_number, options = {}) { + const token = execSync('gh auth token', { encoding: 'utf-8' }).trim() + + const github = getOctokit(token) + + const payload = !pull_number + ? {} + : { + pull_request: ( + await github.rest.pulls.get({ + owner, + repo, + pull_number, + }) + ).data, + } + + process.env['INPUT_GITHUB-TOKEN'] = token + + closeSync(openSync('step-summary.md', 'w')) + process.env.GITHUB_STEP_SUMMARY = 'step-summary.md' + + await action({ + github, + context: { + payload, + repo: { owner, repo }, + }, + core, + dry: true, + ...options, + }) +} + +program + .command('prepare') + .description('Validate PR mergeability and branch targeting.') + .argument('', 'Repository owner (e.g. YongDo-Hyun)') + .argument('', 'Repository name (e.g. Project-Tick)') + .argument('', 'Pull Request number') + .option('--no-dry', 'Make actual modifications') + .action(async (owner, repo, pr, options) => { + const prepare = (await import('./prepare.js')).default + await run(prepare, owner, repo, pr, options) + }) + +program + .command('lint-commits') + .description('Lint commit messages for Conventional Commits compliance.') + .argument('', 'Repository owner (e.g. YongDo-Hyun)') + .argument('', 'Repository name (e.g. Project-Tick)') + .argument('', 'Pull Request number') + .action(async (owner, repo, pr) => { + const lint = (await import('./lint-commits.js')).default + await run(lint, owner, repo, pr) + }) + +program.parse() diff --git a/ci/github-script/shell.nix b/ci/github-script/shell.nix new file mode 100644 index 0000000000..ce8c719ffe --- /dev/null +++ b/ci/github-script/shell.nix @@ -0,0 +1,25 @@ +{ + system ? builtins.currentSystem, + pkgs ? (import ../../ci { inherit system; }).pkgs, +}: + +pkgs.callPackage ( + { + gh, + importNpmLock, + mkShell, + nodejs, + }: + mkShell { + packages = [ + gh + importNpmLock.hooks.linkNodeModulesHook + nodejs + ]; + + npmDeps = importNpmLock.buildNodeModules { + npmRoot = ./.; + inherit nodejs; + }; + } +) { } diff --git a/ci/github-script/withRateLimit.js b/ci/github-script/withRateLimit.js new file mode 100644 index 0000000000..d1b1c756ea --- /dev/null +++ b/ci/github-script/withRateLimit.js @@ -0,0 +1,63 @@ +module.exports = async ({ github, core, maxConcurrent = 1 }, callback) => { + const Bottleneck = require('bottleneck') + + const stats = { + issues: 0, + prs: 0, + requests: 0, + artifacts: 0, + } + + // Rate-Limiting and Throttling, see for details: + // https://github.com/octokit/octokit.js/issues/1069#throttling + // https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api + const allLimits = new Bottleneck({ + // Avoid concurrent requests + maxConcurrent, + // Will be updated with first `updateReservoir()` call below. + reservoir: 0, + }) + // Pause between mutative requests + const writeLimits = new Bottleneck({ minTime: 1000 }).chain(allLimits) + github.hook.wrap('request', async (request, options) => { + // Requests to a different host do not count against the rate limit. + if (options.url.startsWith('https://github.com')) return request(options) + // Requests to the /rate_limit endpoint do not count against the rate limit. + if (options.url === '/rate_limit') return request(options) + // Search requests are in a different resource group, which allows 30 requests / minute. + // We do less than a handful each run, so not implementing throttling for now. + if (options.url.startsWith('/search/')) return request(options) + stats.requests++ + if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(options.method)) + return writeLimits.schedule(request.bind(null, options)) + else return allLimits.schedule(request.bind(null, options)) + }) + + async function updateReservoir() { + let response + try { + response = await github.rest.rateLimit.get() + } catch (err) { + core.error(`Failed updating reservoir:\n${err}`) + // Keep retrying on failed rate limit requests instead of exiting the script early. + return + } + // Always keep 1000 spare requests for other jobs to do their regular duty. + // They normally use below 100, so 1000 is *plenty* of room to work with. + const reservoir = Math.max(0, response.data.resources.core.remaining - 1000) + core.info(`Updating reservoir to: ${reservoir}`) + allLimits.updateSettings({ reservoir }) + } + await updateReservoir() + // Update remaining requests every minute to account for other jobs running in parallel. + const reservoirUpdater = setInterval(updateReservoir, 60 * 1000) + + try { + await callback(stats) + } finally { + clearInterval(reservoirUpdater) + core.notice( + `Processed ${stats.prs} PRs, ${stats.issues} Issues, made ${stats.requests + stats.artifacts} API requests and downloaded ${stats.artifacts} artifacts.`, + ) + } +} diff --git a/ci/pinned.json b/ci/pinned.json new file mode 100644 index 0000000000..9e039136a1 --- /dev/null +++ b/ci/pinned.json @@ -0,0 +1,31 @@ +{ + "pins": { + "nixpkgs": { + "type": "Git", + "repository": { + "type": "GitHub", + "owner": "NixOS", + "repo": "nixpkgs" + }, + "branch": "nixpkgs-unstable", + "submodules": false, + "revision": "bde09022887110deb780067364a0818e89258968", + "url": "https://github.com/NixOS/nixpkgs/archive/bde09022887110deb780067364a0818e89258968.tar.gz", + "hash": "13mi187zpa4rw680qbwp7pmykjia8cra3nwvjqmsjba3qhlzif5l" + }, + "treefmt-nix": { + "type": "Git", + "repository": { + "type": "GitHub", + "owner": "numtide", + "repo": "treefmt-nix" + }, + "branch": "main", + "submodules": false, + "revision": "e96d59dff5c0d7fddb9d113ba108f03c3ef99eca", + "url": "https://github.com/numtide/treefmt-nix/archive/e96d59dff5c0d7fddb9d113ba108f03c3ef99eca.tar.gz", + "hash": "02gqyxila3ghw8gifq3mns639x86jcq079kvfvjm42mibx7z5fzb" + } + }, + "version": 5 +} diff --git a/ci/supportedBranches.js b/ci/supportedBranches.js new file mode 100755 index 0000000000..5d7a9904d5 --- /dev/null +++ b/ci/supportedBranches.js @@ -0,0 +1,85 @@ +#!/usr/bin/env nix-shell +/* +#!nix-shell -i node -p nodejs +*/ + +// Branch classification for the Project Tick monorepo. +// Used by CI scripts to determine branch types and skip/run certain jobs. + +const typeConfig = { + master: ['development', 'primary'], + release: ['development', 'primary'], + staging: ['development', 'secondary'], + 'staging-next': ['development', 'secondary'], + feature: ['wip'], + fix: ['wip'], + backport: ['wip'], + revert: ['wip'], + wip: ['wip'], + dependabot: ['wip'], +} + +// "order" ranks the development branches by how likely they are the intended +// base branch when they are an otherwise equally good fit. +const orderConfig = { + master: 0, + release: 1, + staging: 2, + 'staging-next': 3, +} + +function split(branch) { + return { + ...branch.match( + /(?[a-zA-Z-]+?)(-(?\d+\.\d+(?:\.\d+)?)(?:-(?.*))?)?$/, + ).groups, + } +} + +function classify(branch) { + const { prefix, version } = split(branch) + return { + branch, + order: orderConfig[prefix] ?? Infinity, + stable: version != null, + type: typeConfig[prefix] ?? ['wip'], + version: version ?? 'dev', + } +} + +module.exports = { classify, split } + +// If called directly via CLI, runs the following tests: +if (!module.parent) { + console.log('split(branch)') + function testSplit(branch) { + console.log(branch, split(branch)) + } + testSplit('master') + testSplit('release-1.0') + testSplit('release-2.5.1') + testSplit('staging-1.0') + testSplit('staging-next-1.0') + testSplit('feature-new-ui') + testSplit('fix-crash-on-start') + testSplit('backport-123-to-release-1.0') + testSplit('dependabot-npm') + testSplit('wip-experiment') + + console.log('') + + console.log('classify(branch)') + function testClassify(branch) { + console.log(branch, classify(branch)) + } + testClassify('master') + testClassify('release-1.0') + testClassify('release-2.5.1') + testClassify('staging-1.0') + testClassify('staging-next-1.0') + testClassify('feature-new-ui') + testClassify('fix-crash-on-start') + testClassify('backport-123-to-release-1.0') + testClassify('dependabot-npm') + testClassify('wip-experiment') +} diff --git a/ci/update-pinned.sh b/ci/update-pinned.sh new file mode 100755 index 0000000000..7e3a26695f --- /dev/null +++ b/ci/update-pinned.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i bash -p npins + +set -euo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")" + +npins --lock-file pinned.json update diff --git a/libnbtplusplus/CMakeLists.txt b/libnbtplusplus/CMakeLists.txt index cb3bff5baa..ea4efeccd1 100644 --- a/libnbtplusplus/CMakeLists.txt +++ b/libnbtplusplus/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.15) project(libnbt++ - VERSION 2.3) + VERSION 3.0) # supported configure options option(NBT_BUILD_SHARED "Build shared libraries" OFF) diff --git a/libnbtplusplus/README.md b/libnbtplusplus/README.md index fdc96df533..139872eeff 100644 --- a/libnbtplusplus/README.md +++ b/libnbtplusplus/README.md @@ -4,7 +4,7 @@ SPDX-FileCopyrightText: 2013, 2015 ljfa-ag SPDX-License-Identifier: LGPL-3.0-or-later --> -# libnbt++ 2 +# libnbt++ 3 libnbt++ is a free C++ library for Minecraft's file format Named Binary Tag (NBT). It can read and write compressed and uncompressed NBT files and @@ -12,8 +12,8 @@ provides a code interface for working with NBT data. ---------- -libnbt++2 is a remake of the old libnbt++ library with the goal of making it -more easily usable and fixing some problems. The old libnbt++ especially +libnbt++3 is a remake of the old libnbt++2 library with the goal of making it +more easily usable and fixing some problems. The old libnbt++2 especially suffered from a very convoluted syntax and boilerplate code needed to work with NBT data. @@ -29,8 +29,8 @@ This project uses CMake for building. Ensure you have CMake installed. ### Build Steps 1. Clone the repository: ``` - git clone https://github.com/PrismLauncher/libnbtplusplus.git - cd libnbtplusplus + git clone https://github.com/Project-Tick/Project-Tick.git + cd Project-Tick/libnbtplusplus/ ``` 2. Create a build directory: -- cgit 0.0.5-2-1-g0f52