From b15340a0652afc153a2d3e2eef5b90d0bbdea999 Mon Sep 17 00:00:00 2001 From: Daniel Kift Date: Wed, 29 Jul 2026 10:33:27 +0100 Subject: [PATCH] Add timeouts to workflow jobs Assisted-By: devx/4075c309-3611-4ac8-9ea7-26a8974e7963 --- .../workflows/android-protocol-publish.yml | 1 + .github/workflows/android-publish.yml | 1 + .github/workflows/android-test.yml | 3 ++ .github/workflows/breaking-changes.yml | 2 ++ .github/workflows/ci.yml | 4 +++ .github/workflows/cla.yml | 1 + .github/workflows/label-team-prs.yml | 1 + .github/workflows/package-size.yml | 1 + .github/workflows/rn-lint.yml | 2 ++ .github/workflows/stale.yml | 1 + .github/workflows/swift-lint.yml | 3 ++ .github/workflows/swift-publish.yml | 1 + .github/workflows/swift-update-linters.yml | 1 + scripts/test/workflow_timeouts_test.rb | 33 +++++++++++++++++++ 14 files changed, 55 insertions(+) create mode 100644 scripts/test/workflow_timeouts_test.rb diff --git a/.github/workflows/android-protocol-publish.yml b/.github/workflows/android-protocol-publish.yml index ef553a3a5..48dab0e79 100644 --- a/.github/workflows/android-protocol-publish.yml +++ b/.github/workflows/android-protocol-publish.yml @@ -15,6 +15,7 @@ jobs: name: Central Repository Deployment runs-on: ubuntu-latest + timeout-minutes: 30 permissions: contents: read diff --git a/.github/workflows/android-publish.yml b/.github/workflows/android-publish.yml index 0dadb4ec8..52d26aed6 100644 --- a/.github/workflows/android-publish.yml +++ b/.github/workflows/android-publish.yml @@ -15,6 +15,7 @@ jobs: name: Central Repository Deployment runs-on: ubuntu-latest + timeout-minutes: 30 permissions: contents: read diff --git a/.github/workflows/android-test.yml b/.github/workflows/android-test.yml index f9367e51b..4f966a06d 100644 --- a/.github/workflows/android-test.yml +++ b/.github/workflows/android-test.yml @@ -10,6 +10,7 @@ permissions: jobs: test: runs-on: ubuntu-latest + timeout-minutes: 30 defaults: run: working-directory: platforms/android @@ -82,6 +83,7 @@ jobs: build: runs-on: ubuntu-latest + timeout-minutes: 20 steps: - name: Checkout Repository @@ -108,6 +110,7 @@ jobs: lint: runs-on: ubuntu-latest + timeout-minutes: 20 defaults: run: working-directory: platforms/android diff --git a/.github/workflows/breaking-changes.yml b/.github/workflows/breaking-changes.yml index e7dbc5e8d..37f60d58e 100644 --- a/.github/workflows/breaking-changes.yml +++ b/.github/workflows/breaking-changes.yml @@ -35,6 +35,7 @@ jobs: name: Swift if: inputs.swift == 'true' runs-on: ${{ vars.MACOS_RUNNER }} + timeout-minutes: 20 defaults: run: working-directory: platforms/swift @@ -52,6 +53,7 @@ jobs: name: Android if: inputs.android == 'true' runs-on: ubuntu-latest + timeout-minutes: 15 defaults: run: working-directory: platforms/android diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c47d2623d..f176e9ed3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,7 @@ jobs: name: Detect Changed Areas if: github.event_name != 'pull_request' || github.event.pull_request.draft == false runs-on: ubuntu-latest + timeout-minutes: 5 outputs: android: ${{ steps.platform.outputs.android == 'true' || steps.platform.outputs.protocol == 'true' || steps.infra.outputs.android == 'true' }} swift: ${{ steps.platform.outputs.swift == 'true' || steps.platform.outputs.protocol == 'true' || steps.infra.outputs.swift == 'true' }} @@ -142,6 +143,7 @@ jobs: needs: changes if: needs.changes.outputs.swift == 'true' runs-on: ubuntu-latest + timeout-minutes: 5 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - run: platforms/swift/Scripts/check_sample_package_paths @@ -214,6 +216,7 @@ jobs: needs: changes if: needs.changes.outputs.scripts == 'true' runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0 @@ -259,6 +262,7 @@ jobs: - scripts-test - breaking-changes runs-on: ubuntu-latest + timeout-minutes: 5 steps: - name: Verify all upstream jobs succeeded or were skipped env: diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index 92e5867de..5f7833e82 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -9,6 +9,7 @@ on: jobs: cla: runs-on: ubuntu-latest + timeout-minutes: 5 permissions: actions: write pull-requests: write diff --git a/.github/workflows/label-team-prs.yml b/.github/workflows/label-team-prs.yml index 38c6dab70..28cb08fb3 100644 --- a/.github/workflows/label-team-prs.yml +++ b/.github/workflows/label-team-prs.yml @@ -7,6 +7,7 @@ on: jobs: label: runs-on: ubuntu-latest + timeout-minutes: 5 permissions: pull-requests: write if: github.event.pull_request.user.type != 'Bot' diff --git a/.github/workflows/package-size.yml b/.github/workflows/package-size.yml index b6f5599e9..06b0ed326 100644 --- a/.github/workflows/package-size.yml +++ b/.github/workflows/package-size.yml @@ -17,6 +17,7 @@ jobs: name: Detect Changed Packages if: github.event_name == 'pull_request' && github.event.pull_request.draft == false runs-on: ubuntu-latest + timeout-minutes: 5 outputs: android: ${{ steps.package.outputs.android == 'true' || steps.package.outputs.protocol == 'true' || steps.infra.outputs.android == 'true' }} reactNative: ${{ steps.package.outputs.reactNative == 'true' || steps.package.outputs.protocolTypescript == 'true' || steps.infra.outputs.reactNative == 'true' }} diff --git a/.github/workflows/rn-lint.yml b/.github/workflows/rn-lint.yml index 403688567..3029e9c16 100644 --- a/.github/workflows/rn-lint.yml +++ b/.github/workflows/rn-lint.yml @@ -11,6 +11,7 @@ jobs: swiftlint: name: SwiftLint runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: norio-nomura/action-swiftlint@9f4dcd7fd46b4e75d7935cf2f4df406d5cae3684 # 3.2.1 @@ -22,6 +23,7 @@ jobs: swiftformat: name: SwiftFormat & SwiftLint runs-on: ${{ vars.MACOS_RUNNER }} + timeout-minutes: 20 env: MINT_PATH: ${{ github.workspace }}/.mint/lib MINT_LINK_PATH: ${{ github.workspace }}/.mint/bin diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 7f64881aa..292854470 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -12,6 +12,7 @@ on: jobs: stale: runs-on: ubuntu-latest + timeout-minutes: 10 permissions: issues: write diff --git a/.github/workflows/swift-lint.yml b/.github/workflows/swift-lint.yml index c32d7e335..769f59e6c 100644 --- a/.github/workflows/swift-lint.yml +++ b/.github/workflows/swift-lint.yml @@ -12,6 +12,7 @@ jobs: swiftlint: name: SwiftLint runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: norio-nomura/action-swiftlint@9f4dcd7fd46b4e75d7935cf2f4df406d5cae3684 # 3.2.1 @@ -21,6 +22,7 @@ jobs: swiftformat: name: SwiftFormat & SwiftLint runs-on: ${{ vars.MACOS_RUNNER }} + timeout-minutes: 20 env: MINT_PATH: ${{ github.workspace }}/.mint/lib MINT_LINK_PATH: ${{ github.workspace }}/.mint/bin @@ -54,6 +56,7 @@ jobs: lint-podspec: name: CocoaPods runs-on: ${{ vars.MACOS_RUNNER }} + timeout-minutes: 30 env: BUNDLE_GEMFILE: ${{ github.workspace }}/platforms/swift/Gemfile steps: diff --git a/.github/workflows/swift-publish.yml b/.github/workflows/swift-publish.yml index d7b5c4786..576d8004c 100644 --- a/.github/workflows/swift-publish.yml +++ b/.github/workflows/swift-publish.yml @@ -17,6 +17,7 @@ jobs: # workflow doesn't need updating each time a new platform namespace lands. if: ${{ github.event_name == 'workflow_dispatch' || !contains(github.event.release.tag_name, '/') }} runs-on: ${{ vars.MACOS_RUNNER }} + timeout-minutes: 30 env: BUNDLE_GEMFILE: ${{ github.workspace }}/platforms/swift/Gemfile diff --git a/.github/workflows/swift-update-linters.yml b/.github/workflows/swift-update-linters.yml index 4b57a0d60..c87e0caec 100644 --- a/.github/workflows/swift-update-linters.yml +++ b/.github/workflows/swift-update-linters.yml @@ -12,6 +12,7 @@ permissions: jobs: check-updates: runs-on: ${{ vars.MACOS_RUNNER }} + timeout-minutes: 20 env: MINT_PATH: ${{ github.workspace }}/.mint/lib MINT_LINK_PATH: ${{ github.workspace }}/.mint/bin diff --git a/scripts/test/workflow_timeouts_test.rb b/scripts/test/workflow_timeouts_test.rb new file mode 100644 index 000000000..ba5aba172 --- /dev/null +++ b/scripts/test/workflow_timeouts_test.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require "minitest/autorun" +require "yaml" + +class WorkflowTimeoutsTest < Minitest::Test + WORKFLOW_GLOB = File.expand_path("../../.github/workflows/*.{yml,yaml}", __dir__) + + def test_every_runner_job_declares_a_timeout + missing = workflow_jobs.filter_map do |path, name, job| + "#{relative_path(path)}: #{name}" unless reusable_job?(job) || job.key?("timeout-minutes") + end + + assert_empty missing, "Jobs without timeout-minutes:\n#{missing.join("\n")}" + end + + private + + def workflow_jobs + Dir[WORKFLOW_GLOB].sort.flat_map do |path| + workflow = YAML.safe_load_file(path, aliases: true) + (workflow.fetch("jobs", {})).map { |name, job| [path, name, job] } + end + end + + def reusable_job?(job) + job.is_a?(Hash) && job.key?("uses") + end + + def relative_path(path) + path.delete_prefix("#{File.expand_path("../..", __dir__)}/") + end +end