diff --git a/.github/scripts/pr_touches_paths.py b/.github/scripts/pr_touches_paths.py new file mode 100644 index 0000000000..315f270338 --- /dev/null +++ b/.github/scripts/pr_touches_paths.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +"""Exit 0 if any changed file matches the calling workflow's on.push.paths +globs (honoring ! negations), else exit 1. + +The `gate` job uses this to restore the path filtering that `pull_request` +supported but `pull_request_review` does not, so an approval only launches +CI for workflows whose relevant files actually changed. +""" +import argparse +import re +import sys + +import yaml + + +def glob_to_regex(pat): + # GitHub Actions path glob -> regex. `**` spans directories, `*` does not + # cross `/`, `?` matches a single non-`/` char. + i, n, out = 0, len(pat), ["^"] + while i < n: + c = pat[i] + if c == "*": + if pat[i + 1 : i + 2] == "*": + out.append(".*") + i += 2 + if pat[i : i + 1] == "/": + i += 1 # `**/` also matches zero directories + else: + out.append("[^/]*") + i += 1 + elif c == "?": + out.append("[^/]") + i += 1 + else: + out.append(re.escape(c)) + i += 1 + out.append("$") + return "".join(out) + + +def workflow_path(ref): + # "owner/repo/.github/workflows/x.yml@refs/heads/main" -> ".github/workflows/x.yml" + path = ref.split("@", 1)[0] + parts = path.split("/", 2) + return parts[2] if len(parts) == 3 else path + + +def read_patterns(paths_file, workflow_ref): + # An explicit paths file wins: it lets a caller gate on a narrower set than + # its own push.paths (e.g. run only when the pipeline definition changed). + if paths_file: + with open(paths_file) as f: + return [ln.strip() for ln in f if ln.strip()] + with open(workflow_path(workflow_ref)) as f: + spec = yaml.safe_load(f) + # YAML 1.1 parses the bare `on:` key as the boolean True. + on = spec.get("on", spec.get(True, {})) or {} + push = on.get("push") or {} + return push.get("paths") + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--workflow-ref") + ap.add_argument("--paths-file") + ap.add_argument("--changed-files", required=True) + args = ap.parse_args() + + if not args.paths_file and not args.workflow_ref: + ap.error("one of --paths-file or --workflow-ref is required") + + patterns = read_patterns(args.paths_file, args.workflow_ref) + if not patterns: + return 0 # no path filter -> always relevant + + pos = [re.compile(glob_to_regex(p)) for p in patterns if not p.startswith("!")] + neg = [re.compile(glob_to_regex(p[1:])) for p in patterns if p.startswith("!")] + + with open(args.changed_files) as f: + files = [ln.strip() for ln in f if ln.strip()] + + for path in files: + if any(r.match(path) for r in pos) and not any(r.match(path) for r in neg): + return 0 + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0c66e5a716..d0a2efea19 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -4,40 +4,84 @@ name: "CodeQL" on: - # run on every push to the feature-development branch - # the main branch is covered by below cron plan + # Fork CI: runs on the fork's own Actions minutes when a contributor + # pushes a branch to their fork. push: - branches: - - dev/** - # scan pull requests so findings surface on the PR instead of only post-merge - pull_request: + # Skip pushes to the maintainer branches (main, release/**, dev/**); + # upstream coverage comes from the nightly cron below. Fork feature + # branches still run. + # NOTE: do not name a local/feature branch main, release/*, or dev/* - + # its push CI would be silently skipped by this filter. + branches-ignore: + - "main" + - "release/**" + - "dev/**" + # Only the code CodeQL actually compiles (see codeql_buildscript.sh: + # wamr-compiler + product-mini/platforms/linux) plus the scan recipe and + # config. samples/** and tests/** are never built here, so scanning them + # on push would change nothing. + paths: + - ".github/workflows/codeql.yml" + - ".github/scripts/codeql_buildscript.sh" + - ".github/codeql/**" + - "build-scripts/**" + - "core/**" + - "!core/deps/**" + - "product-mini/**" + - "wamr-compiler/**" + # Upstream CI: on the first approval, re-run once - but only when the PR + # changed the CodeQL pipeline itself (gate.paths below). Regular source + # changes are covered by the nightly cron, not per-PR. + pull_request_review: + types: [submitted] # midnight UTC on the latest commit on the main branch schedule: - cron: "0 0 * * *" # allow to be triggered manually workflow_dispatch: -# Serialize CodeQL runs per PR / branch. Only a pull_request cancels its own -# superseded run - once a PR head is replaced the old analysis is throwaway. -# Pushes to dev/** and the nightly cron are left to finish, so branch and -# scheduled scans are never dropped. +# Serialize CodeQL runs per PR / branch. Only an approval-gated review cancels +# its superseded run; feature-branch pushes and the nightly cron are left to +# finish, so branch and scheduled scans are never dropped. concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request_review' }} + +env: + # Merge ref to check out on approval-gated review events; empty elsewhere + # so checkout falls back to the event's default ref. + PR_REF: ${{ github.event_name == 'pull_request_review' && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }} jobs: + # Gate the upstream approval re-run: only for a PR's first approval AND only + # when the PR touches the CodeQL pipeline definition (not general source). + gate: + permissions: + contents: read + pull-requests: read + uses: ./.github/workflows/gate.yml + with: + paths: | + .github/workflows/codeql.yml + .github/scripts/codeql_buildscript.sh + .github/scripts/codeql_fail_on_error.py + .github/codeql/** + analyze: - # Pull requests: only scan when the head branch lives in this same - # repository. A pull request from a fork runs with a read-only GITHUB_TOKEN - # (no `security-events: write`), so uploading results and reading - # code-scanning alerts is not permitted and the job would fail; skip it - # cleanly instead. Other events (push to dev/**, the nightly cron, manual - # runs) keep the original behavior of running only on the upstream repo. + # Where each trigger is meant to run: + # - push: fork CI only (a contributor's fork), on the fork's minutes. + # - schedule: upstream only (the nightly scan of the canonical repo). + # - review: upstream, first approval, only if the pipeline changed. + # - dispatch: anywhere, for manual runs on either repo. + needs: gate if: >- - (github.event_name == 'pull_request' && - github.event.pull_request.head.repo.full_name == github.repository) - || (github.event_name != 'pull_request' && - github.repository == 'bytecodealliance/wasm-micro-runtime') + !cancelled() && + ( + github.event_name == 'workflow_dispatch' || + (github.event_name == 'push' && github.repository != 'bytecodealliance/wasm-micro-runtime') || + (github.event_name == 'schedule' && github.repository == 'bytecodealliance/wasm-micro-runtime') || + (github.event_name == 'pull_request_review' && needs.gate.outputs.run == 'true') + ) name: Analyze # Runner size impacts CodeQL analysis time. To learn more, please see: # - https://gh.io/recommended-hardware-resources-for-running-codeql @@ -63,6 +107,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v7.0.0 with: + ref: ${{ env.PR_REF }} submodules: recursive # Initializes the CodeQL tools for scanning. diff --git a/.github/workflows/coding_guidelines.yml b/.github/workflows/coding_guidelines.yml index 0db0a9b0ef..8c91cf1044 100644 --- a/.github/workflows/coding_guidelines.yml +++ b/.github/workflows/coding_guidelines.yml @@ -3,27 +3,59 @@ name: Coding Guidelines on: - # will be triggered on PR events - pull_request: + # Fork CI: runs on the fork's own Actions minutes when a contributor + # pushes a branch to their fork. + push: + # Never run upstream CI on the maintainer branches: pushes to main, + # release/** and dev/** are skipped here (they are covered by the + # approval-gated PR review below). Fork feature branches still run. + # NOTE: do not name a local/feature branch main, release/*, or dev/* - + # its push CI would be silently skipped by this filter. + branches-ignore: + - "main" + - "release/**" + - "dev/**" + # Upstream CI: only spend upstream minutes once a PR is approved + # (first approval only, enforced by the gate job below). + pull_request_review: + types: [submitted] # allow to be triggered manually workflow_dispatch: # Cancel any in-flight jobs for the same PR/branch so there's only one active # at a time concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true permissions: contents: read +env: + # Merge ref to check out on approval-gated review events; empty elsewhere + # so checkout falls back to the event's default ref. + PR_REF: ${{ github.event_name == 'pull_request_review' && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }} + jobs: + # Gate upstream minutes: always runs (a few-second no-op on push/dispatch) + # and outputs `run`, "true" only for a PR's first approval. It must NOT be + # skipped: a skipped job propagates the skip down the needs chain to every + # descendant, even ones that broke the chain with their own `if`. + gate: + permissions: + contents: read + pull-requests: read + uses: ./.github/workflows/gate.yml + compliance_job: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} runs-on: ubuntu-24.04 steps: - name: checkout uses: actions/checkout@v7.0.0 with: + ref: ${{ env.PR_REF }} fetch-depth: 0 - name: install clang-format-21 @@ -44,4 +76,19 @@ jobs: # github.event.pull_request.base.label = ${{github.repository}}/${{github.base_ref}} - name: Run Coding Guidelines Checks - run: /usr/bin/env python3 ./ci/coding_guidelines_check.py --commits ${{ github.event.pull_request.base.sha }}..HEAD + run: | + # pull_request_review: base.sha is the PR base commit. + # push: github.event.before is the previous tip, but it is the zero + # SHA on a branch's first push / after a force-push, which would make + # `..HEAD` fail. Fall back to the merge-base with origin/main. + base="${{ github.event.pull_request.base.sha }}" + if [ -z "$base" ]; then + before="${{ github.event.before }}" + if [ -z "$before" ] || [ "$before" = "0000000000000000000000000000000000000000" ]; then + git fetch --quiet origin main + base=$(git merge-base origin/main HEAD) + else + base="$before" + fi + fi + /usr/bin/env python3 ./ci/coding_guidelines_check.py --commits "$base..HEAD" diff --git a/.github/workflows/compilation_on_android_ubuntu.yml b/.github/workflows/compilation_on_android_ubuntu.yml index d9397ec857..f5488efa8a 100644 --- a/.github/workflows/compilation_on_android_ubuntu.yml +++ b/.github/workflows/compilation_on_android_ubuntu.yml @@ -4,28 +4,17 @@ name: compilation on android, ubuntu-22.04 on: - # will be triggered on PR events - pull_request: - types: - - opened - - synchronize - paths: - - ".github/workflows/build_llvm_libraries.yml" - - ".github/workflows/compilation_on_android_ubuntu.yml" - - "build-scripts/**" - - "core/**" - - "!core/deps/**" - - "product-mini/**" - - "samples/**" - - "!samples/workload/**" - - "tests/wamr-test-suites/**" - - "tests/unit/**" - - "wamr-compiler/**" - - "test-tools/wamr-ide/**" - # will be triggered on push events + # Fork CI: runs on the fork's own Actions minutes when a contributor + # pushes a branch to their fork. push: - branches: - - main + # Never run upstream CI on the maintainer branches: pushes to main, + # release/** and dev/** are skipped here (they are covered by the + # approval-gated PR review below). Fork feature branches still run. + # NOTE: do not name a local/feature branch main, release/*, or dev/* - + # its push CI would be silently skipped by this filter. + branches-ignore: + - "main" + - "release/**" - "dev/**" paths: - ".github/workflows/build_llvm_libraries.yml" @@ -40,16 +29,23 @@ on: - "tests/unit/**" - "wamr-compiler/**" - "test-tools/wamr-ide/**" + # Upstream CI: only spend upstream minutes once a PR is approved + # (first approval only, enforced by the gate job below). + pull_request_review: + types: [submitted] # allow to be triggered manually workflow_dispatch: # Cancel any in-flight jobs for the same PR/branch so there's only one active # at a time concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true env: + # Merge ref to check out on approval-gated review events; empty elsewhere + # so checkout falls back to the event's default ref. + PR_REF: ${{ github.event_name == 'pull_request_review' && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }} # For BUILD AOT_BUILD_OPTIONS: " -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" CLASSIC_INTERP_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=0 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" @@ -75,13 +71,27 @@ permissions: contents: read jobs: + # Gate upstream minutes: always runs (a few-second no-op on push/dispatch) + # and outputs `run`, "true" only for a PR's first approval. It must NOT be + # skipped: a skipped job propagates the skip down the needs chain to every + # descendant, even ones that broke the chain with their own `if`. + gate: + permissions: + contents: read + pull-requests: read + uses: ./.github/workflows/gate.yml + check_version_h: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} permissions: contents: read actions: write uses: ./.github/workflows/check_version_h.yml build_llvm_libraries_on_ubuntu_2204: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} permissions: contents: read actions: write @@ -101,6 +111,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} # since jobs.id can't contain the dot character # it is hard to use `format` to assemble the cache key @@ -272,6 +284,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} # only download llvm cache when needed - name: Get LLVM libraries @@ -324,6 +338,7 @@ jobs: - name: checkout uses: actions/checkout@v7.0.0 with: + ref: ${{ env.PR_REF }} submodules: recursive - name: Get LLVM libraries @@ -382,6 +397,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: Get LLVM libraries id: retrieve_llvm_libs @@ -447,6 +464,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: Get LLVM libraries id: retrieve_llvm_libs @@ -504,6 +523,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: Get LLVM libraries id: retrieve_llvm_libs @@ -698,6 +719,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: Set-up OCaml uses: ocaml/setup-ocaml@v3 diff --git a/.github/workflows/compilation_on_macos.yml b/.github/workflows/compilation_on_macos.yml index 4c5063cbbe..e1d232327e 100644 --- a/.github/workflows/compilation_on_macos.yml +++ b/.github/workflows/compilation_on_macos.yml @@ -4,26 +4,17 @@ name: compilation on macos on: - # will be triggered on PR events - pull_request: - types: - - opened - - synchronize - paths: - - ".github/workflows/build_llvm_libraries.yml" - - ".github/workflows/compilation_on_macos.yml" - - "build-scripts/**" - - "core/**" - - "!core/deps/**" - - "product-mini/**" - - "samples/**" - - "!samples/workload/**" - - "tests/wamr-test-suites/**" - - "wamr-compiler/**" - # will be triggered on push events + # Fork CI: runs on the fork's own Actions minutes when a contributor + # pushes a branch to their fork. push: - branches: - - main + # Never run upstream CI on the maintainer branches: pushes to main, + # release/** and dev/** are skipped here (they are covered by the + # approval-gated PR review below). Fork feature branches still run. + # NOTE: do not name a local/feature branch main, release/*, or dev/* - + # its push CI would be silently skipped by this filter. + branches-ignore: + - "main" + - "release/**" - "dev/**" paths: - ".github/workflows/build_llvm_libraries.yml" @@ -36,16 +27,23 @@ on: - "!samples/workload/**" - "tests/wamr-test-suites/**" - "wamr-compiler/**" + # Upstream CI: only spend upstream minutes once a PR is approved + # (first approval only, enforced by the gate job below). + pull_request_review: + types: [submitted] # allow to be triggered manually workflow_dispatch: # Cancel any in-flight jobs for the same PR/branch so there's only one active # at a time concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true env: + # Merge ref to check out on approval-gated review events; empty elsewhere + # so checkout falls back to the event's default ref. + PR_REF: ${{ github.event_name == 'pull_request_review' && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }} # For BUILD AOT_BUILD_OPTIONS: " -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" CLASSIC_INTERP_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=0 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" @@ -59,7 +57,18 @@ permissions: contents: read jobs: + # Gate upstream minutes: always runs (a few-second no-op on push/dispatch) + # and outputs `run`, "true" only for a PR's first approval. It must NOT be + # skipped: a skipped job propagates the skip down the needs chain to every + # descendant, even ones that broke the chain with their own `if`. + gate: + permissions: + contents: read + pull-requests: read + uses: ./.github/workflows/gate.yml build_llvm_libraries_on_intel_macos: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} permissions: contents: read actions: write @@ -68,6 +77,8 @@ jobs: os: "macos-15-intel" arch: "X86" build_llvm_libraries_on_arm_macos: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} permissions: contents: read actions: write @@ -90,6 +101,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: Get LLVM libraries id: retrieve_llvm_libs @@ -192,6 +205,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} # only download llvm cache when needed - name: Get LLVM libraries @@ -254,6 +269,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: Get LLVM libraries id: retrieve_llvm_libs @@ -312,6 +329,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: install-wasi-sdk-wabt uses: ./.github/actions/install-wasi-sdk-wabt diff --git a/.github/workflows/compilation_on_nuttx.yml b/.github/workflows/compilation_on_nuttx.yml index 50fa0bbb0c..2c5d3eb108 100644 --- a/.github/workflows/compilation_on_nuttx.yml +++ b/.github/workflows/compilation_on_nuttx.yml @@ -4,25 +4,17 @@ name: compilation on nuttx on: - # will be triggered on PR events - pull_request: - types: - - opened - - synchronize - paths: - - ".github/workflows/compilation_on_nuttx.yml" - - "build-scripts/**" - - "core/**" - - "!core/deps/**" - - "product-mini/**" - - "samples/**" - - "!samples/workload/**" - - "tests/wamr-test-suites/**" - - "wamr-compiler/**" - # will be triggered on push events + # Fork CI: runs on the fork's own Actions minutes when a contributor + # pushes a branch to their fork. push: - branches: - - main + # Never run upstream CI on the maintainer branches: pushes to main, + # release/** and dev/** are skipped here (they are covered by the + # approval-gated PR review below). Fork feature branches still run. + # NOTE: do not name a local/feature branch main, release/*, or dev/* - + # its push CI would be silently skipped by this filter. + branches-ignore: + - "main" + - "release/**" - "dev/**" paths: - ".github/workflows/compilation_on_nuttx.yml" @@ -34,16 +26,23 @@ on: - "!samples/workload/**" - "tests/wamr-test-suites/**" - "wamr-compiler/**" + # Upstream CI: only spend upstream minutes once a PR is approved + # (first approval only, enforced by the gate job below). + pull_request_review: + types: [submitted] # allow to be triggered manually workflow_dispatch: # Cancel any in-flight jobs for the same PR/branch so there's only one active # at a time concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true env: + # Merge ref to check out on approval-gated review events; empty elsewhere + # so checkout falls back to the event's default ref. + PR_REF: ${{ github.event_name == 'pull_request_review' && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }} WASI_SDK_PATH: "/opt/wasi-sdk" NUTTX_IMAGE: "ghcr.io/apache/nuttx/apache-nuttx-ci-linux@sha256:d9261eacf6c6ebe656c571757751c803e8f04c3ae9b820320a5ea5dd57b7205a" # Bump BLOATY_REF to update bloaty. The size-report cache below is keyed on @@ -54,10 +53,22 @@ permissions: contents: read jobs: + # Gate upstream minutes: always runs (a few-second no-op on push/dispatch) + # and outputs `run`, "true" only for a PR's first approval. It must NOT be + # skipped: a skipped job propagates the skip down the needs chain to every + # descendant, even ones that broke the chain with their own `if`. + gate: + permissions: + contents: read + pull-requests: read + uses: ./.github/workflows/gate.yml + # Build bloaty once and cache it, keyed on BLOATY_REF (a new ref -> cache # miss -> rebuild + refresh). The matrix below restores the prebuilt binary # instead of every leg compiling bloaty and its protobuf/abseil submodules. build_bloaty: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} runs-on: ubuntu-latest steps: - name: Restore or seed the bloaty cache @@ -159,6 +170,7 @@ jobs: uses: actions/checkout@v7.0.0 with: repository: ${{ github.repository }} + ref: ${{ env.PR_REF }} path: apps/interpreters/wamr/wamr - name: Restore prebuilt bloaty diff --git a/.github/workflows/compilation_on_sgx.yml b/.github/workflows/compilation_on_sgx.yml index 5f37bc6266..99857af853 100644 --- a/.github/workflows/compilation_on_sgx.yml +++ b/.github/workflows/compilation_on_sgx.yml @@ -4,26 +4,17 @@ name: compilation on SGX on: - # will be triggered on PR events - pull_request: - types: - - opened - - synchronize - paths: - - ".github/workflows/build_llvm_libraries.yml" - - ".github/workflows/compilation_on_sgx.yml" - - "build-scripts/**" - - "core/**" - - "!core/deps/**" - - "product-mini/**" - - "samples/**" - - "!samples/workload/**" - - "tests/wamr-test-suites/**" - - "wamr-compiler/**" - # will be triggered on push events + # Fork CI: runs on the fork's own Actions minutes when a contributor + # pushes a branch to their fork. push: - branches: - - main + # Never run upstream CI on the maintainer branches: pushes to main, + # release/** and dev/** are skipped here (they are covered by the + # approval-gated PR review below). Fork feature branches still run. + # NOTE: do not name a local/feature branch main, release/*, or dev/* - + # its push CI would be silently skipped by this filter. + branches-ignore: + - "main" + - "release/**" - "dev/**" paths: - ".github/workflows/build_llvm_libraries.yml" @@ -36,16 +27,23 @@ on: - "!samples/workload/**" - "tests/wamr-test-suites/**" - "wamr-compiler/**" + # Upstream CI: only spend upstream minutes once a PR is approved + # (first approval only, enforced by the gate job below). + pull_request_review: + types: [submitted] # allow to be triggered manually workflow_dispatch: # Cancel any in-flight jobs for the same PR/branch so there's only one active # at a time concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true env: + # Merge ref to check out on approval-gated review events; empty elsewhere + # so checkout falls back to the event's default ref. + PR_REF: ${{ github.event_name == 'pull_request_review' && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }} # ref types enabled in wamrc by default, so we need to enable it for iwasm in AOT mode AOT_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0 -DWAMR_BUILD_REF_TYPES=1" CLASSIC_INTERP_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=0 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" @@ -62,7 +60,18 @@ permissions: contents: read jobs: + # Gate upstream minutes: always runs (a few-second no-op on push/dispatch) + # and outputs `run`, "true" only for a PR's first approval. It must NOT be + # skipped: a skipped job propagates the skip down the needs chain to every + # descendant, even ones that broke the chain with their own `if`. + gate: + permissions: + contents: read + pull-requests: read + uses: ./.github/workflows/gate.yml build_llvm_libraries: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} permissions: contents: read actions: write @@ -72,6 +81,8 @@ jobs: arch: "X86" build_iwasm: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} runs-on: ${{ matrix.os }} strategy: matrix: @@ -124,6 +135,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: install SGX SDK and necessary libraries uses: ./.github/actions/install-linux-sgx @@ -167,6 +180,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: install-wasi-sdk-wabt uses: ./.github/actions/install-wasi-sdk-wabt @@ -263,6 +278,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: Get LLVM libraries if: matrix.running_mode == 'aot' diff --git a/.github/workflows/compilation_on_windows.yml b/.github/workflows/compilation_on_windows.yml index d1bfbfd3ab..9d413f996b 100644 --- a/.github/workflows/compilation_on_windows.yml +++ b/.github/workflows/compilation_on_windows.yml @@ -4,25 +4,17 @@ name: compilation on windows-2022 on: - # will be triggered on PR events - pull_request: - types: - - opened - - synchronize - paths: - - ".github/workflows/compilation_on_windows.yml" - - "build-scripts/**" - - "core/**" - - "!core/deps/**" - - "product-mini/**" - - "samples/**" - - "!samples/workload/**" - - "tests/wamr-test-suites/**" - - "wamr-compiler/**" - # will be triggered on push events + # Fork CI: runs on the fork's own Actions minutes when a contributor + # pushes a branch to their fork. push: - branches: - - main + # Never run upstream CI on the maintainer branches: pushes to main, + # release/** and dev/** are skipped here (they are covered by the + # approval-gated PR review below). Fork feature branches still run. + # NOTE: do not name a local/feature branch main, release/*, or dev/* - + # its push CI would be silently skipped by this filter. + branches-ignore: + - "main" + - "release/**" - "dev/**" paths: - ".github/workflows/compilation_on_windows.yml" @@ -34,10 +26,17 @@ on: - "!samples/workload/**" - "tests/wamr-test-suites/**" - "wamr-compiler/**" + # Upstream CI: only spend upstream minutes once a PR is approved + # (first approval only, enforced by the gate job below). + pull_request_review: + types: [submitted] # allow to be triggered manually workflow_dispatch: env: + # Merge ref to check out on approval-gated review events; empty elsewhere + # so checkout falls back to the event's default ref. + PR_REF: ${{ github.event_name == 'pull_request_review' && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }} # For Spec Test DEFAULT_TEST_OPTIONS: "-s spec -b" MULTI_MODULES_TEST_OPTIONS: "-s spec -b -M" @@ -50,14 +49,25 @@ env: # Cancel any in-flight jobs for the same PR/branch so there's only one active # at a time concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true permissions: contents: read jobs: + # Gate upstream minutes: always runs (a few-second no-op on push/dispatch) + # and outputs `run`, "true" only for a PR's first approval. It must NOT be + # skipped: a skipped job propagates the skip down the needs chain to every + # descendant, even ones that broke the chain with their own `if`. + gate: + permissions: + contents: read + pull-requests: read + uses: ./.github/workflows/gate.yml build_llvm_libraries_on_windows: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} permissions: contents: read actions: write @@ -67,6 +77,8 @@ jobs: arch: "AArch64 ARM Mips RISCV X86" build_iwasm: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} runs-on: windows-2022 strategy: matrix: @@ -85,6 +97,8 @@ jobs: ] steps: - uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: clone uvwasi library if: ${{ !contains(matrix.build_options, '-DWAMR_BUILD_LIBC_UVWASI=0') }} @@ -109,6 +123,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} # since jobs.id can't contain the dot character # it is hard to use `format` to assemble the cache key @@ -151,6 +167,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: download and install wasi-sdk if: matrix.test_option == '$WASI_TEST_OPTIONS' diff --git a/.github/workflows/compilation_on_zephyr.yml b/.github/workflows/compilation_on_zephyr.yml index a01ef33bd9..885828732b 100644 --- a/.github/workflows/compilation_on_zephyr.yml +++ b/.github/workflows/compilation_on_zephyr.yml @@ -4,26 +4,17 @@ name: compilation on zephyr on: - # will be triggered on PR events - pull_request: - types: - - opened - - synchronize - paths: - - ".github/workflows/compilation_on_zephyr.yml" - - "build-scripts/**" - - "core/**" - - "!core/deps/**" - - "product-mini/platforms/common/**" - - "product-mini/platforms/zephyr/**" - - "samples/**" - - "!samples/workload/**" - - "tests/wamr-test-suites/**" - - "wamr-compiler/**" - # will be triggered on push events + # Fork CI: runs on the fork's own Actions minutes when a contributor + # pushes a branch to their fork. push: - branches: - - main + # Never run upstream CI on the maintainer branches: pushes to main, + # release/** and dev/** are skipped here (they are covered by the + # approval-gated PR review below). Fork feature branches still run. + # NOTE: do not name a local/feature branch main, release/*, or dev/* - + # its push CI would be silently skipped by this filter. + branches-ignore: + - "main" + - "release/**" - "dev/**" paths: - ".github/workflows/compilation_on_zephyr.yml" @@ -36,16 +27,23 @@ on: - "!samples/workload/**" - "tests/wamr-test-suites/**" - "wamr-compiler/**" + # Upstream CI: only spend upstream minutes once a PR is approved + # (first approval only, enforced by the gate job below). + pull_request_review: + types: [submitted] # allow to be triggered manually workflow_dispatch: # Cancel any in-flight jobs for the same PR/branch so there's only one active # at a time concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true env: + # Merge ref to check out on approval-gated review events; empty elsewhere + # so checkout falls back to the event's default ref. + PR_REF: ${{ github.event_name == 'pull_request_review' && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }} # FOR SETUP ZEPHYR_SDK_VERSION: "0.16.9" ZEPHYR_VERSION: "v3.7.0" @@ -55,7 +53,18 @@ permissions: contents: read jobs: + # Gate upstream minutes: always runs (a few-second no-op on push/dispatch) + # and outputs `run`, "true" only for a PR's first approval. It must NOT be + # skipped: a skipped job propagates the skip down the needs chain to every + # descendant, even ones that broke the chain with their own `if`. + gate: + permissions: + contents: read + pull-requests: read + uses: ./.github/workflows/gate.yml smoke_test: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} runs-on: ubuntu-22.04 strategy: fail-fast: false @@ -108,6 +117,7 @@ jobs: - name: Checkout code uses: actions/checkout@v7.0.0 with: + ref: ${{ env.PR_REF }} path: modules/wasm-micro-runtime - name: Prepare Zephyr environment diff --git a/.github/workflows/gate.yml b/.github/workflows/gate.yml new file mode 100644 index 0000000000..512ea3834d --- /dev/null +++ b/.github/workflows/gate.yml @@ -0,0 +1,89 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Reusable gate shared by every upstream CI workflow. Callers invoke it via +# `uses:` on a pull_request_review event and gate their real jobs on +# `needs.gate.outputs.run`. It runs only for the FIRST approval of a PR and +# only when the PR touches the caller workflow's push.paths (the path filter +# pull_request_review lacks, re-applied via pr_touches_paths.py). +name: gate + +on: + workflow_call: + inputs: + paths: + description: >- + Optional newline-separated path globs. When set, the approval gate + fires only if the PR touches these paths, instead of the caller's + own on.push.paths. Use it to run heavy pipelines on approval only + when their own definition changed. + type: string + required: false + default: "" + outputs: + run: + description: '"true" if downstream jobs should run, else "false".' + value: ${{ jobs.gate.outputs.run }} + +permissions: + contents: read + pull-requests: read + +jobs: + gate: + runs-on: ubuntu-22.04 + outputs: + run: ${{ steps.g.outputs.run }} + steps: + # A local composite action needs the repo checked out before it can be + # referenced; fetch just .github (also carries the path-filter helper). + # The path-filter helper lives under .github; fetch just that. + - name: checkout workflow metadata + uses: actions/checkout@v7.0.0 + with: + sparse-checkout: .github + sparse-checkout-cone-mode: false + - id: g + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + set -e + if [ "${{ github.event.review.state }}" != "approved" ]; then + echo "run=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + # Count APPROVED reviews to run only on the FIRST approval (>1 -> skip). + # This relies on the branch rule "Dismiss stale pull request approvals + # when new commits are pushed": a new push must flip prior approvals to + # DISMISSED so they stop counting here. Without that setting a + # re-approval accumulates a second APPROVED and is silently skipped - + # keep the rule enabled in repo settings. + n=$(gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews" \ + --jq '[.[]|select(.state=="APPROVED")]|length') + if [ "$n" -gt 1 ]; then + echo "run=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + # Restore the path filter pull_request had but pull_request_review + # lacks: only run when the PR actually touches relevant files. + gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" \ + --paginate --jq '.[].filename' > /tmp/changed_files.txt + # An explicit `paths` input narrows the filter (e.g. a heavy pipeline + # that should only run on approval when its own definition changed); + # otherwise fall back to the caller workflow's own on.push.paths. + if [ -n "${{ inputs.paths }}" ]; then + printf '%s\n' "${{ inputs.paths }}" > /tmp/gate_paths.txt + filter_arg=(--paths-file /tmp/gate_paths.txt) + else + filter_arg=(--workflow-ref "${{ github.workflow_ref }}") + fi + python3 -c 'import yaml' 2>/dev/null \ + || python3 -m pip install --quiet --disable-pip-version-check pyyaml + if python3 .github/scripts/pr_touches_paths.py \ + "${filter_arg[@]}" \ + --changed-files /tmp/changed_files.txt; then + echo "run=true" >> "$GITHUB_OUTPUT" + else + echo "run=false" >> "$GITHUB_OUTPUT" + fi diff --git a/.github/workflows/hadolint_dockerfiles.yml b/.github/workflows/hadolint_dockerfiles.yml index c36ebace41..3ad1f8d421 100644 --- a/.github/workflows/hadolint_dockerfiles.yml +++ b/.github/workflows/hadolint_dockerfiles.yml @@ -4,40 +4,62 @@ name: hadolint dockerfiles on: - # will be triggered on PR events - pull_request: - types: - - opened - - synchronize - paths: - - "**/Dockerfile*" - - ".github/workflows/hadolint_dockerfiles.yml" + # Fork CI: runs on the fork's own Actions minutes when a contributor + # pushes a branch to their fork. push: - branches: - - main + # Never run upstream CI on the maintainer branches: pushes to main, + # release/** and dev/** are skipped here (they are covered by the + # approval-gated PR review below). Fork feature branches still run. + # NOTE: do not name a local/feature branch main, release/*, or dev/* - + # its push CI would be silently skipped by this filter. + branches-ignore: + - "main" + - "release/**" - "dev/**" paths: - "**/Dockerfile*" - ".github/workflows/hadolint_dockerfiles.yml" + # Upstream CI: only spend upstream minutes once a PR is approved + # (first approval only, enforced by the gate job below). + pull_request_review: + types: [submitted] # allow to be triggered manually workflow_dispatch: # Cancel any in-flight jobs for the same PR/branch so there's only one active # at a time concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true permissions: contents: read +env: + # Merge ref to check out on approval-gated review events; empty elsewhere + # so checkout falls back to the event's default ref. + PR_REF: ${{ github.event_name == 'pull_request_review' && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }} + jobs: + # Gate upstream minutes: always runs (a few-second no-op on push/dispatch) + # and outputs `run`, "true" only for a PR's first approval. It must NOT be + # skipped: a skipped job propagates the skip down the needs chain to every + # descendant, even ones that broke the chain with their own `if`. + gate: + permissions: + contents: read + pull-requests: read + uses: ./.github/workflows/gate.yml run-hadolint-on-dockerfiles: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} runs-on: ubuntu-22.04 steps: - name: Checkout repository uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} # on default, hadolint will fail on warnings and errors - name: Run hadolint on dockerfiles diff --git a/.github/workflows/nightly_run.yml b/.github/workflows/nightly_run.yml index 6548ebdaca..0daa98c2c4 100644 --- a/.github/workflows/nightly_run.yml +++ b/.github/workflows/nightly_run.yml @@ -4,15 +4,28 @@ name: nightly_run on: - pull_request: - types: - - opened - - synchronize + # Fork CI: runs on the fork's own Actions minutes when a contributor + # pushes a branch to their fork. + push: + # Skip pushes to the maintainer branches (main, release/**, dev/**); + # upstream coverage comes from the nightly cron below. Fork feature + # branches still run. + # NOTE: do not name a local/feature branch main, release/*, or dev/* - + # its push CI would be silently skipped by this filter. + branches-ignore: + - "main" + - "release/**" + - "dev/**" # running nightly pipeline if you're changing it # stress tests are run only in nightly at the moment, so running them in they are changed paths: - ".github/workflows/nightly_run.yml" - "core/iwasm/libraries/lib-wasi-threads/stress-test/**" + # Upstream CI: on the first approval, re-run once - but only when the PR + # changed the nightly pipeline itself (gate.paths below). Regular source + # changes are covered by the nightly cron, not per-PR. + pull_request_review: + types: [submitted] # midnight UTC schedule: @@ -23,10 +36,13 @@ on: # Cancel any in-flight jobs for the same PR/branch so there's only one active # at a time concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true env: + # Merge ref to check out on approval-gated review events; empty elsewhere + # so checkout falls back to the event's default ref. + PR_REF: ${{ github.event_name == 'pull_request_review' && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }} # For BUILD AOT_BUILD_OPTIONS: " -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" CLASSIC_INTERP_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=0 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" @@ -48,7 +64,21 @@ permissions: contents: read jobs: + # Gate the upstream approval re-run: only for a PR's first approval AND only + # when the PR touches the nightly pipeline definition (not general source). + gate: + permissions: + contents: read + pull-requests: read + uses: ./.github/workflows/gate.yml + with: + paths: | + .github/workflows/nightly_run.yml + core/iwasm/libraries/lib-wasi-threads/stress-test/** + build_llvm_libraries_on_ubuntu: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} permissions: contents: read actions: write @@ -68,6 +98,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} # since jobs.id can't contain the dot character # it is hard to use `format` to assemble the cache key @@ -236,6 +268,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} # only download llvm cache when needed - name: Get LLVM libraries @@ -288,6 +322,7 @@ jobs: - name: checkout uses: actions/checkout@v7.0.0 with: + ref: ${{ env.PR_REF }} submodules: recursive - name: Get LLVM libraries @@ -334,7 +369,9 @@ jobs: working-directory: tests/unit build_iwasm_linux_gcc4_8: - runs-on: ubuntu-latest + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} + runs-on: ubuntu-22.04 container: image: ubuntu:14.04 strategy: @@ -471,6 +508,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: Get LLVM libraries id: retrieve_llvm_libs @@ -524,6 +563,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: install-wasi-sdk-wabt uses: ./.github/actions/install-wasi-sdk-wabt @@ -668,10 +709,14 @@ jobs: ./run.sh --aot addr2line_tests_multi_sdk: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} runs-on: ubuntu-22.04 steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: Install wasi-sdk 29.0 run: | @@ -763,6 +808,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: install-wasi-sdk-wabt if: matrix.test_option == '$WASI_TEST_OPTIONS' diff --git a/.github/workflows/spec_test_on_nuttx.yml b/.github/workflows/spec_test_on_nuttx.yml index 25d3d9c5c0..8b81485cd8 100644 --- a/.github/workflows/spec_test_on_nuttx.yml +++ b/.github/workflows/spec_test_on_nuttx.yml @@ -4,10 +4,18 @@ name: spec test on nuttx on: - pull_request: - types: - - opened - - synchronize + # Fork CI: runs on the fork's own Actions minutes when a contributor + # pushes a branch to their fork. + push: + # Never run upstream CI on the maintainer branches: pushes to main, + # release/** and dev/** are skipped here (they are covered by the + # approval-gated PR review below). Fork feature branches still run. + # NOTE: do not name a local/feature branch main, release/*, or dev/* - + # its push CI would be silently skipped by this filter. + branches-ignore: + - "main" + - "release/**" + - "dev/**" paths: - ".github/workflows/spec_test_on_nuttx.yml" - "core/**" @@ -17,6 +25,10 @@ on: - "tests/wamr-test-suites/**" - "wamr-compiler/**" - "wamr-sdk/**" + # Upstream CI: only spend upstream minutes once a PR is approved + # (first approval only, enforced by the gate job below). + pull_request_review: + types: [submitted] schedule: - cron: '0 0 * * *' @@ -26,6 +38,9 @@ on: # https://github.com/apache/nuttx-apps/pull/2241 is not included in # releases/12.4 branch as of writing this. env: + # Merge ref to check out on approval-gated review events; empty elsewhere + # so checkout falls back to the event's default ref. + PR_REF: ${{ github.event_name == 'pull_request_review' && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }} LLVM_CACHE_SUFFIX: "build-llvm_libraries_ex" WASI_SDK_PATH: "/opt/wasi-sdk" @@ -33,7 +48,18 @@ permissions: contents: read jobs: + # Gate upstream minutes: always runs (a few-second no-op on push/dispatch) + # and outputs `run`, "true" only for a PR's first approval. It must NOT be + # skipped: a skipped job propagates the skip down the needs chain to every + # descendant, even ones that broke the chain with their own `if`. + gate: + permissions: + contents: read + pull-requests: read + uses: ./.github/workflows/gate.yml build_llvm_libraries: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} permissions: contents: read actions: write @@ -44,6 +70,8 @@ jobs: container_image: ghcr.io/no1wudi/nuttx/apache-nuttx-ci-linux@sha256:8c4e00b607d4d6d66ba8f51c4544819a616eac69d3a2ac669e2af2150e2eb0f9 build_llvm_libraries_xtensa: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} permissions: contents: read actions: write @@ -56,7 +84,7 @@ jobs: container_image: ghcr.io/no1wudi/nuttx/apache-nuttx-ci-linux@sha256:8c4e00b607d4d6d66ba8f51c4544819a616eac69d3a2ac669e2af2150e2eb0f9 spec_test_on_qemu: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: [build_llvm_libraries, build_llvm_libraries_xtensa] container: image: ghcr.io/no1wudi/nuttx/apache-nuttx-ci-linux@sha256:8c4e00b607d4d6d66ba8f51c4544819a616eac69d3a2ac669e2af2150e2eb0f9 @@ -160,6 +188,7 @@ jobs: uses: actions/checkout@v7.0.0 with: repository: ${{ github.repository }} + ref: ${{ env.PR_REF }} path: apps/interpreters/wamr/wamr - name: Get LLVM libraries diff --git a/.github/workflows/wamr_wasi_extensions.yml b/.github/workflows/wamr_wasi_extensions.yml index b16bf0399c..71352da82b 100644 --- a/.github/workflows/wamr_wasi_extensions.yml +++ b/.github/workflows/wamr_wasi_extensions.yml @@ -4,26 +4,54 @@ name: wamr_wasi_extensions on: - pull_request: - types: - - opened - - synchronize + # Fork CI: runs on the fork's own Actions minutes when a contributor + # pushes a branch to their fork. + push: + # Never run upstream CI on the maintainer branches: pushes to main, + # release/** and dev/** are skipped here (they are covered by the + # approval-gated PR review below). Fork feature branches still run. + # NOTE: do not name a local/feature branch main, release/*, or dev/* - + # its push CI would be silently skipped by this filter. + branches-ignore: + - "main" + - "release/**" + - "dev/**" paths: - ".github/workflows/wamr_wasi_extensions.yml" - "wamr_wasi_extensios/**" - "core/iwasm/libraries/wasi-nn/include/**" - "core/iwasm/libraries/lib-socket/**" + # Upstream CI: only spend upstream minutes once a PR is approved + # (first approval only, enforced by the gate job below). + pull_request_review: + types: [submitted] # allow to be triggered manually workflow_dispatch: # Cancel any in-flight jobs for the same PR/branch so there's only one active # at a time concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true +env: + # Merge ref to check out on approval-gated review events; empty elsewhere + # so checkout falls back to the event's default ref. + PR_REF: ${{ github.event_name == 'pull_request_review' && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }} + jobs: + # Gate upstream minutes: always runs (a few-second no-op on push/dispatch) + # and outputs `run`, "true" only for a PR's first approval. It must NOT be + # skipped: a skipped job propagates the skip down the needs chain to every + # descendant, even ones that broke the chain with their own `if`. + gate: + permissions: + contents: read + pull-requests: read + uses: ./.github/workflows/gate.yml build_wamr_wasi_extensions: + needs: gate + if: ${{ !cancelled() && (github.event_name != 'pull_request_review' || needs.gate.outputs.run == 'true') }} runs-on: ${{ matrix.os }} strategy: matrix: @@ -31,6 +59,8 @@ jobs: steps: - name: checkout uses: actions/checkout@v7.0.0 + with: + ref: ${{ env.PR_REF }} - name: install-wasi-sdk-wabt uses: ./.github/actions/install-wasi-sdk-wabt diff --git a/README.md b/README.md index 3a3782453e..773dc527d1 100644 --- a/README.md +++ b/README.md @@ -139,3 +139,5 @@ Any contributions you make will be under the same license. - [WAMR Blogs](https://bytecodealliance.github.io/wamr.dev/blog/) - [Community news and events](https://bytecodealliance.github.io/wamr.dev/events/) - [WAMR TSC meetings](https://github.com/bytecodealliance/wasm-micro-runtime/wiki/TSC-meeting-notes) + +