summaryrefslogtreecommitdiff
path: root/.github/workflows/ci.yml
diff options
context:
space:
mode:
authorMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-04 19:47:58 +0300
committerMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-04 19:47:58 +0300
commit8d0d919fbf43230148da7533519ed0ffdfaa4197 (patch)
tree27e352d6ca09910e577ec27a10659814e88b15b9 /.github/workflows/ci.yml
parentfce202465d4fede9e19d4d057eebbaa702291652 (diff)
downloadProject-Tick-8d0d919fbf43230148da7533519ed0ffdfaa4197.tar.gz
Project-Tick-8d0d919fbf43230148da7533519ed0ffdfaa4197.zip
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 <yongdohyun@projecttick.org>
Diffstat (limited to '.github/workflows/ci.yml')
-rw-r--r--.github/workflows/ci.yml717
1 files changed, 717 insertions, 0 deletions
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