From 42d9b9e422ef4844ce92d3ef899bdce9b301faf1 Mon Sep 17 00:00:00 2001 From: Adam Cohen Hillel Date: Wed, 1 Jul 2026 18:01:33 -0700 Subject: [PATCH] Harden GitHub Actions workflows Pin all actions to commit SHAs, gate the gcp-lab label trigger on the labeling actor repo permission, validate the release version input before shell interpolation, add concurrency groups to ci/eval/release, add a CI job timeout, and drop the dead pull_request guard in emulator.yml. Co-Authored-By: Claude Opus 4.7 --- .github/workflows/ci.yml | 13 +++++--- .github/workflows/emulator.yml | 5 ++- .github/workflows/eval.yml | 10 ++++-- .github/workflows/gcp-lab.yml | 32 ++++++++++++++---- .github/workflows/release.yml | 60 ++++++++++++++++++++++++++-------- 5 files changed, 90 insertions(+), 30 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bdd7242..928ddae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,27 +10,32 @@ on: permissions: contents: read +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/master' }} + jobs: repo-checks: name: Repository checks runs-on: ubuntu-latest + timeout-minutes: 60 steps: - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Set up JDK 21 - uses: actions/setup-java@v4 + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0 with: distribution: temurin java-version: '21' - name: Set up Android SDK - uses: android-actions/setup-android@v3 + uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 with: packages: 'platforms;android-35' - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '24' cache: npm diff --git a/.github/workflows/emulator.yml b/.github/workflows/emulator.yml index f60ed28..0e4dfe0 100644 --- a/.github/workflows/emulator.yml +++ b/.github/workflows/emulator.yml @@ -54,7 +54,6 @@ concurrency: jobs: emulator-smoke: name: Boot emulator and run OpenPhone smoke - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} runs-on: [self-hosted, openphone-emulator] timeout-minutes: 180 env: @@ -63,7 +62,7 @@ jobs: OPENPHONE_EMULATOR_ASSISTANT_SMOKE: ${{ inputs.assistant_smoke == 'false' && '0' || '1' }} steps: - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: clean: false @@ -90,7 +89,7 @@ jobs: - name: Upload emulator smoke artifacts if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: openphone-emulator-smoke path: .worktree/lab/*/artifacts/emulator-smoke/ diff --git a/.github/workflows/eval.yml b/.github/workflows/eval.yml index f8c0bb3..af11484 100644 --- a/.github/workflows/eval.yml +++ b/.github/workflows/eval.yml @@ -22,6 +22,10 @@ on: permissions: contents: read +concurrency: + group: eval-device-runner + cancel-in-progress: false + jobs: trajectory-smokes: name: Trajectory smokes @@ -29,7 +33,7 @@ jobs: timeout-minutes: 45 steps: - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Validate repository run: | @@ -70,7 +74,7 @@ jobs: - name: Upload trajectories if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: openphone-trajectories path: .worktree/eval-trajectories/ @@ -78,7 +82,7 @@ jobs: - name: Upload benchmark results if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: openphone-benchmark-results path: .worktree/evals/ diff --git a/.github/workflows/gcp-lab.yml b/.github/workflows/gcp-lab.yml index d47caeb..eea62fc 100644 --- a/.github/workflows/gcp-lab.yml +++ b/.github/workflows/gcp-lab.yml @@ -116,24 +116,42 @@ jobs: OPENPHONE_GCP_BOOT_DISK_TYPE: ${{ vars.OPENPHONE_GCP_BOOT_DISK_TYPE || 'pd-ssd' }} OPENPHONE_GCP_CACHE_SOURCE_SNAPSHOT: ${{ vars.OPENPHONE_GCP_CACHE_SOURCE_SNAPSHOT || '' }} steps: + - name: Require maintainer to apply the label + if: ${{ github.event_name == 'pull_request_target' }} + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + permission="$(gh api "repos/${{ github.repository }}/collaborators/${{ github.actor }}/permission" --jq '.permission')" + case "$permission" in + admin|maintain|write) + echo "Label applied by ${{ github.actor }} with $permission permission" + ;; + *) + echo "Label applied by ${{ github.actor }} with insufficient permission: $permission" >&2 + exit 1 + ;; + esac + - name: Check out trusted workflow repository - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: ref: ${{ github.event.pull_request.base.sha || github.sha }} - name: Set up JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0 with: distribution: temurin java-version: "21" - name: Set up Android SDK - uses: android-actions/setup-android@v3 + uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 with: packages: "platforms;android-35" - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: "24" @@ -144,13 +162,13 @@ jobs: git diff --check - name: Authenticate to Google Cloud - uses: google-github-actions/auth@v2 + uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2.1.13 with: workload_identity_provider: ${{ vars.OPENPHONE_GCP_WORKLOAD_IDENTITY_PROVIDER }} service_account: ${{ vars.OPENPHONE_GCP_SERVICE_ACCOUNT }} - name: Set up gcloud - uses: google-github-actions/setup-gcloud@v2 + uses: google-github-actions/setup-gcloud@e427ad8a34f8676edf47cf7d7925499adf3eb74f # v2.2.1 - name: Select target id: target @@ -252,7 +270,7 @@ jobs: - name: Upload GCP lab artifacts if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: openphone-gcp-lab path: .worktree/gcp-lab/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9d95c5f..a7fda54 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,6 +64,10 @@ permissions: contents: write id-token: write +concurrency: + group: release-${{ inputs.version }} + cancel-in-progress: false + jobs: gcp-lab-smoke: name: Release GCP lab smoke @@ -79,22 +83,32 @@ jobs: OPENPHONE_GCP_BOOT_DISK_TYPE: ${{ vars.OPENPHONE_GCP_BOOT_DISK_TYPE || 'pd-ssd' }} OPENPHONE_GCP_CACHE_SOURCE_SNAPSHOT: ${{ vars.OPENPHONE_GCP_CACHE_SOURCE_SNAPSHOT || '' }} steps: + - name: Validate version input + env: + RELEASE_VERSION: ${{ inputs.version }} + run: | + set -euo pipefail + if [[ ! "$RELEASE_VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+([-.][A-Za-z0-9.-]+)?$ ]]; then + echo "Invalid version input: $RELEASE_VERSION" >&2 + exit 1 + fi + - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Set up JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0 with: distribution: temurin java-version: "21" - name: Set up Android SDK - uses: android-actions/setup-android@v3 + uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 with: packages: "platforms;android-35" - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: "24" @@ -107,13 +121,13 @@ jobs: test -f "${{ inputs.release_notes }}" - name: Authenticate to Google Cloud - uses: google-github-actions/auth@v2 + uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2.1.13 with: workload_identity_provider: ${{ vars.OPENPHONE_GCP_WORKLOAD_IDENTITY_PROVIDER }} service_account: ${{ vars.OPENPHONE_GCP_SERVICE_ACCOUNT }} - name: Set up gcloud - uses: google-github-actions/setup-gcloud@v2 + uses: google-github-actions/setup-gcloud@e427ad8a34f8676edf47cf7d7925499adf3eb74f # v2.2.1 - name: Run release GCP lab smoke run: | @@ -143,7 +157,7 @@ jobs: - name: Upload GCP lab artifacts if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: openphone-release-gcp-lab path: .worktree/gcp-lab/ @@ -156,22 +170,32 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 20 steps: + - name: Validate version input + env: + RELEASE_VERSION: ${{ inputs.version }} + run: | + set -euo pipefail + if [[ ! "$RELEASE_VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+([-.][A-Za-z0-9.-]+)?$ ]]; then + echo "Invalid version input: $RELEASE_VERSION" >&2 + exit 1 + fi + - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Set up JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0 with: distribution: temurin java-version: "21" - name: Set up Android SDK - uses: android-actions/setup-android@v3 + uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 with: packages: "platforms;android-35" - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: "24" @@ -207,8 +231,18 @@ jobs: OPENPHONE_RELEASE: bp4a OPENPHONE_BUILD_GOAL: otapackage steps: + - name: Validate version input + env: + RELEASE_VERSION: ${{ inputs.version }} + run: | + set -euo pipefail + if [[ ! "$RELEASE_VERSION" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+([-.][A-Za-z0-9.-]+)?$ ]]; then + echo "Invalid version input: $RELEASE_VERSION" >&2 + exit 1 + fi + - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Validate repository run: | @@ -243,7 +277,7 @@ jobs: echo "artifacts_path=${staging}/ARTIFACTS.md" >> "$GITHUB_OUTPUT" - name: Publish GitHub Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2 with: tag_name: ${{ inputs.version }} name: ${{ inputs.version }}