Skip to content

Commit 7f4bf81

Browse files
authored
chore(ci): Update concurrency configuration across workflows (#5838)
1 parent f37e3fe commit 7f4bf81

23 files changed

+372
-36
lines changed

.cursor/rules/github-workflow.mdc

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,161 @@ For reusable workflows (workflow_call), use descriptive names that indicate thei
132132
- Ensure names remain meaningful when viewed in GitHub's status check UI
133133
- Test names in the GitHub PR interface before committing changes
134134
- For lint workflows, use simple "Lint" job name since the tool is already specified in the workflow name
135+
136+
---
137+
138+
## GitHub Actions Concurrency Strategy
139+
140+
### Overview
141+
142+
This document outlines the concurrency configuration strategy for all GitHub Actions workflows in the Sentry Cocoa repository. The strategy optimizes CI resource usage while ensuring critical runs (like main branch pushes) are never interrupted.
143+
144+
### Core Principles
145+
146+
#### 1. Resource Optimization
147+
- **Cancel outdated PR runs** - When new commits are pushed to a PR, cancel the previous workflow run since only the latest commit matters for merge decisions
148+
- **Protect critical runs** - Never cancel workflows running on main branch, release branches, or scheduled runs as these are essential for maintaining baseline quality and release integrity
149+
- **Per-branch grouping** - Use `github.ref` for consistent concurrency grouping across all branch types
150+
151+
#### 2. Consistent Patterns
152+
All workflows follow standardized concurrency patterns based on their trigger types and criticality.
153+
154+
### Concurrency Patterns
155+
156+
#### Pattern 1: Conditional Cancellation (Most Common)
157+
**Used by:** Most workflows that run on both main/release branches AND pull requests
158+
159+
```yaml
160+
concurrency:
161+
group: ${{ github.workflow }}-${{ github.ref }}
162+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
163+
```
164+
165+
**Behavior:**
166+
- ✅ Cancels in-progress runs when new commits are pushed to PRs
167+
- ✅ Never cancels runs on main branch pushes
168+
- ✅ Never cancels runs on release branch pushes
169+
- ✅ Never cancels scheduled runs
170+
- ✅ Never cancels manual workflow_dispatch runs
171+
172+
**Examples:** `test.yml`, `build.yml`, `benchmarking.yml`, `ui-tests.yml`, all lint workflows
173+
174+
#### Pattern 2: Always Cancel (PR-Only Workflows)
175+
**Used by:** Workflows that ONLY run on pull requests
176+
177+
```yaml
178+
concurrency:
179+
group: ${{ github.workflow }}-${{ github.ref }}
180+
cancel-in-progress: true
181+
```
182+
183+
**Behavior:**
184+
- ✅ Always cancels in-progress runs (safe since they only run on PRs)
185+
- ✅ Provides immediate feedback on latest changes
186+
187+
**Examples:** `danger.yml`, `api-stability.yml`, `changes-in-high-risk-code.yml`
188+
189+
#### Pattern 3: Fixed Group Name (Special Cases)
190+
**Used by:** Utility workflows with specific requirements
191+
192+
```yaml
193+
concurrency:
194+
group: "auto-update-tools"
195+
cancel-in-progress: true
196+
```
197+
198+
**Example:** `auto-update-tools.yml` (uses fixed group name for global coordination)
199+
200+
### Implementation Details
201+
202+
#### Group Naming Convention
203+
- **Standard:** `${{ github.workflow }}-${{ github.ref }}`
204+
- **Benefits:**
205+
- Unique per workflow and branch/PR
206+
- Consistent across all workflow types
207+
- Works with main, release, and feature branches
208+
- Handles PRs and direct pushes uniformly
209+
210+
#### Why `github.ref` Instead of `github.head_ref || github.run_id`?
211+
- **Simpler logic** - No conditional expressions needed
212+
- **Consistent behavior** - Same pattern works for all trigger types
213+
- **Per-branch grouping** - Natural grouping by branch without special cases
214+
- **Better maintainability** - Single pattern to understand and maintain
215+
216+
#### Cancellation Logic Evolution
217+
**Before:**
218+
```yaml
219+
cancel-in-progress: ${{ !(github.event_name == 'push' && github.ref == 'refs/heads/main') && github.event_name != 'schedule' }}
220+
```
221+
222+
**After:**
223+
```yaml
224+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
225+
```
226+
227+
**Why simplified:**
228+
- ✅ Much more readable and maintainable
229+
- ✅ Functionally identical behavior
230+
- ✅ Clear intent: "only cancel on pull requests"
231+
- ✅ Less prone to errors
232+
233+
### Workflow-Specific Configurations
234+
235+
#### High-Resource Workflows
236+
**Examples:** `benchmarking.yml`, `ui-tests.yml`
237+
- Use conditional cancellation to protect expensive main branch runs
238+
- Include detailed comments explaining resource considerations
239+
- May include special cleanup steps (e.g., SauceLabs job cancellation)
240+
241+
#### Fast Validation Workflows
242+
**Examples:** All lint workflows, `danger.yml`
243+
- Use appropriate cancellation strategy based on trigger scope
244+
- Focus on providing quick feedback on latest changes
245+
246+
#### Critical Infrastructure Workflows
247+
**Examples:** `test.yml`, `build.yml`, `release.yml`
248+
- Never cancel on main/release branches to maintain quality gates
249+
- Ensure complete validation of production-bound code
250+
251+
### Documentation Requirements
252+
253+
Each workflow's concurrency block must include comments explaining:
254+
255+
1. **Purpose** - Why concurrency control is needed for this workflow
256+
2. **Resource considerations** - Any expensive operations (SauceLabs, device time, etc.)
257+
3. **Branch protection logic** - Why main/release branches need complete runs
258+
4. **User experience** - How the configuration improves feedback timing
259+
260+
#### Example Documentation:
261+
```yaml
262+
# Concurrency configuration:
263+
# - We use workflow-specific concurrency groups to prevent multiple benchmark runs on the same code,
264+
# as benchmarks are extremely resource-intensive and require dedicated device time on SauceLabs.
265+
# - For pull requests, we cancel in-progress runs when new commits are pushed to avoid wasting
266+
# expensive external testing resources and provide timely performance feedback.
267+
# - For main branch pushes, we never cancel benchmarks to ensure we have complete performance
268+
# baselines for every main branch commit, which are critical for performance regression detection.
269+
concurrency:
270+
group: ${{ github.workflow }}-${{ github.ref }}
271+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
272+
```
273+
274+
### Maintenance Guidelines
275+
276+
#### When Adding New Workflows
277+
1. **Identify trigger scope** - Does it run on main/release branches?
278+
2. **Choose appropriate pattern** - Conditional vs always cancel
279+
3. **Add documentation** - Explain the resource and timing considerations
280+
4. **Follow naming convention** - Use standard group naming pattern
281+
282+
#### When Modifying Existing Workflows
283+
1. **Preserve protection** - Don't break main/release branch safeguards
284+
2. **Update documentation** - Keep comments accurate and helpful
285+
3. **Test edge cases** - Verify behavior with scheduled/manual triggers
286+
4. **Consider resource impact** - Evaluate cost of additional runs
287+
288+
#### Red Flags to Avoid
289+
- ❌ Never use `cancel-in-progress: true` on workflows that run on main/release branches
290+
- ❌ Don't create complex conditional logic when simple patterns work
291+
- ❌ Avoid custom group names unless absolutely necessary
292+
- ❌ Don't skip documentation - future maintainers need context

.github/workflows/api-stability.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ on:
1616
- "sdk_api.json"
1717
- "sdk_api_v9.json"
1818

19+
# Concurrency configuration:
20+
# - We use workflow-specific concurrency groups to prevent multiple API stability checks on the same code,
21+
# as these analyze public API changes and generate detailed breaking change reports.
22+
# - We always cancel in-progress runs since this workflow only runs on pull requests, and only
23+
# the latest commit's API changes matter for determining if the PR introduces breaking changes.
24+
concurrency:
25+
group: ${{ github.workflow }}-${{ github.ref }}
26+
cancel-in-progress: true
27+
1928
jobs:
2029
api-stability:
2130
name: Check API Stability (${{ matrix.version }})

.github/workflows/benchmarking.yml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,16 @@ on:
2424
- ".github/workflows/build-xcframework-variant-slices.yml"
2525
- ".github/workflows/assemble-xcframework-variant.yml"
2626

27-
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
27+
# Concurrency configuration:
28+
# - We use workflow-specific concurrency groups to prevent multiple benchmark runs on the same code,
29+
# as benchmarks are extremely resource-intensive and require dedicated device time on SauceLabs.
30+
# - For pull requests, we cancel in-progress runs when new commits are pushed to avoid wasting
31+
# expensive external testing resources and provide timely performance feedback.
32+
# - For main branch pushes, we never cancel benchmarks to ensure we have complete performance
33+
# baselines for every main branch commit, which are critical for performance regression detection.
2834
concurrency:
29-
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
30-
cancel-in-progress: true
35+
group: ${{ github.workflow }}-${{ github.ref }}
36+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
3137

3238
jobs:
3339
build-benchmark-test-target:
@@ -129,6 +135,11 @@ jobs:
129135
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
130136
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
131137
with:
138+
# Retry Logic: This step only runs when the benchmark test has failed.
139+
# We determine if the failure was due to SauceLabs infrastructure issues
140+
# (which can be fixed by retrying) or legitimate test failures (which cannot).
141+
# SauceLabs internal errors frequently cause CI failures that can be resolved
142+
# by re-running the test. See scripts/saucelabs-helpers.js for detailed logic.
132143
script: |
133144
const fs = require('fs');
134145
const { execSync } = require('child_process');

.github/workflows/build.yml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,16 @@ on:
2020
- "Makefile" # Make commands used for CI build setup
2121
- "Brewfile*" # Dependency installation affects build environment
2222

23-
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
23+
# Concurrency configuration:
24+
# - We use workflow-specific concurrency groups to prevent multiple build runs of the same code,
25+
# which would waste CI resources without providing additional value.
26+
# - For pull requests, we cancel in-progress builds when new commits are pushed since only the
27+
# latest commit's build results matter for merge decisions.
28+
# - For main branch pushes, we never cancel builds to ensure all release and sample builds complete,
29+
# as broken builds on main could block releases and affect downstream consumers.
2430
concurrency:
25-
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
26-
cancel-in-progress: true
31+
group: ${{ github.workflow }}-${{ github.ref }}
32+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
2733

2834
jobs:
2935
# We had issues that the release build was broken on main.

.github/workflows/changes-in-high-risk-code.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ name: Changes In High Risk Code
22
on:
33
pull_request:
44

5-
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
5+
# Concurrency configuration:
6+
# - We use workflow-specific concurrency groups to prevent multiple high-risk code analysis runs,
7+
# as these detect changes to critical code paths and trigger additional validation workflows.
8+
# - We always cancel in-progress runs since this workflow only runs on pull requests, and only
9+
# the latest commit's high-risk code changes matter for determining which additional tests to run.
610
concurrency:
7-
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
11+
group: ${{ github.workflow }}-${{ github.ref }}
812
cancel-in-progress: true
913

1014
jobs:

.github/workflows/codeql-analysis.yml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,16 @@ on:
99
schedule:
1010
- cron: "40 4 * * 6" # Weekly scheduled run to catch issues regardless of changes
1111

12-
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
12+
# Concurrency configuration:
13+
# - We use workflow-specific concurrency groups to prevent multiple CodeQL security analysis runs,
14+
# as these are comprehensive security scans that shouldn't run simultaneously.
15+
# - For pull requests, we cancel in-progress runs when new commits are pushed since only the
16+
# latest security analysis results matter for identifying potential vulnerabilities.
17+
# - For main branch pushes and scheduled runs, we never cancel security analysis to ensure
18+
# complete security validation and maintain our security baseline with weekly scheduled scans.
1319
concurrency:
14-
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
15-
cancel-in-progress: true
20+
group: ${{ github.workflow }}-${{ github.ref }}
21+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
1622

1723
jobs:
1824
analyze:

.github/workflows/danger.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ on:
44
pull_request:
55
types: [opened, synchronize, reopened, edited, ready_for_review]
66

7+
# Concurrency configuration:
8+
# - We use workflow-specific concurrency groups to prevent multiple Danger runs on the same PR,
9+
# as Danger performs PR validation and comment generation that should be based on the latest code.
10+
# - We always cancel in-progress runs since this workflow only runs on pull requests, and only
11+
# the latest commit's validation results matter for providing accurate PR feedback and reviews.
12+
concurrency:
13+
group: ${{ github.workflow }}-${{ github.ref }}
14+
cancel-in-progress: true
15+
716
jobs:
817
danger:
918
name: Danger

.github/workflows/integration-test.yml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,16 @@ on:
1717
- "Gemfile.lock"
1818
- "Plans/iOS-Cocoapods-Swift6_Base.xctestplan"
1919

20-
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
20+
# Concurrency configuration:
21+
# - We use workflow-specific concurrency groups to prevent multiple CocoaPods integration tests,
22+
# as these tests involve complex dependency resolution and build processes.
23+
# - For pull requests, we cancel in-progress runs when new commits are pushed since only the
24+
# latest integration test results matter for validating CocoaPods compatibility.
25+
# - For main branch pushes, we never cancel integration tests to ensure our CocoaPods
26+
# integration remains functional for all downstream consumers relying on our library.
2127
concurrency:
22-
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
23-
cancel-in-progress: true
28+
group: ${{ github.workflow }}-${{ github.ref }}
29+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
2430

2531
jobs:
2632
test:

.github/workflows/lint-clang-formatting.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ on:
2929
- "Makefile" # Make commands used for formatting setup
3030
- "Brewfile*" # Tools installation affects formatting environment
3131

32+
# Concurrency configuration:
33+
# - We use workflow-specific concurrency groups to prevent multiple lint runs on the same code,
34+
# as linting checks are deterministic and don't require parallel validation.
35+
# - For pull requests, we cancel in-progress runs when new commits are pushed since only the
36+
# latest linting matters for merge decisions.
37+
# - For main branch pushes, we never cancel formatting checks to ensure our code maintains consistent style
38+
# standards across the entire project.
39+
concurrency:
40+
group: ${{ github.workflow }}-${{ github.ref }}
41+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
42+
3243
jobs:
3344
lint:
3445
# While ubuntu runners have clang-format preinstalled, they use an older version. We want to use the most recent one,

.github/workflows/lint-cocoapods-specs.yml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,16 @@ on:
3030
- "Brewfile*" # Tools installation affects linting environment
3131
- ".swiftlint.yml"
3232

33-
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
33+
# Concurrency configuration:
34+
# - We use workflow-specific concurrency groups to prevent multiple lint runs on the same code,
35+
# as linting checks are deterministic and don't require parallel validation.
36+
# - For pull requests, we cancel in-progress runs when new commits are pushed since only the
37+
# latest linting matters for merge decisions.
38+
# - For main branch pushes, we never cancel formatting checks to ensure our code maintains consistent style
39+
# standards across the entire project.
3440
concurrency:
35-
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
36-
cancel-in-progress: true
41+
group: ${{ github.workflow }}-${{ github.ref }}
42+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
3743

3844
jobs:
3945
lint-podspec:

0 commit comments

Comments
 (0)