Skip to content

Commit 9014833

Browse files
[infra] Flutter SDK version checking/updating automation (#8954)
## CI/CD Automation: Automated Flutter SDK Version Bumping & Integrity Verification ### Why This Matters To ensure stable and reproducible builds, our CI/CD scripts are pinned to a specific stable version of the Flutter SDK in `tool/provision_flutter.sh` (see: #8951). Manually monitoring and upgrading this pin is tedious, while a rolling pin risks introducing silent test breakages. This PR automates the maintenance of our pinned Flutter SDK version with safe, presubmit-verified upgrades, backed by compile-time integrity checks to prevent syntax or synchronization bugs. Fixes: #8953 --- ### Proposed Changes #### 1. Automated Version-Pin Bumping * **[NEW] [.github/workflows/update_flutter.yaml](file:///+github/workflows/update_flutter.yaml)**: Added a scheduled weekly GitHub Actions workflow (running every Monday at midnight) that: * Queries the official Flutter GCS manifest API for new stable releases. * Extracts and compares the latest version with our current pin inside `tool/provision_flutter.sh`. * If a newer version is available, it automatically edits the version constant in the script and opens a structured Pull Request so that standard presubmit checks run against the new version prior to merging. #### 2. Compile-Time Integrity Verification * **[NEW] [testSrc/unit/io/flutter/CIIntegrityTest.java](file:///testSrc/unit/io/flutter/CIIntegrityTest.java)**: Added a custom JUnit meta-test to enforce synchronization between the provisioning script and the GitHub Action: * `testFlutterProvisioningScriptIntegrity()`: Asserts `tool/provision_flutter.sh` exists and contains a valid semver-formatted `FLUTTER_VERSION="..."` constant. * `testGitHubWorkflowRegexSync()`: Asserts `.github/workflows/update_flutter.yaml` matches the exact constant pattern used in its search-and-replace step. * *Why this is here*: Prevents silent setup failures if a developer renames or reformats the version constant in the future. --- ### Verification Results 1. **JUnit Integrity Tests**: Ran the integrity suite locally: ```bash ./gradlew test --tests io.flutter.CIIntegrityTest Result: BUILD SUCCESSFUL (Both assertions compiled and passed successfully in 4 seconds). 2. **Local Run**: Until this is landed the action can't be tested on GH but this verifies that it should work: ``` ☁ flutter-intellij [infra_flutterSdkWorkflow] ⚡ # 1. Read the current pinned version from the script (should print 3.41.0) CURRENT_VERSION=$(grep -E 'FLUTTER_VERSION="[0-9.]+"' tool/provision_flutter.sh | head -n 1 | cut -d'"' -f2) echo "Current pinned version: $CURRENT_VERSION" # 2. Query the live Flutter API for the latest stable release version (should print 3.41.9+) LATEST_VERSION=$(curl -s https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json | jq -r '.releases | map(select(.channel == "stable"))[0].version') echo "Latest stable version: $LATEST_VERSION" # 3. Run the comparison and update logic if [ "$CURRENT_VERSION" != "$LATEST_VERSION" ]; then echo "New stable version detected: $LATEST_VERSION. Updating script..." # Perform the replacement (cross-compatible sed for macOS) sed -i "" "s/FLUTTER_VERSION=\"$CURRENT_VERSION\"/FLUTTER_VERSION=\"$LATEST_VERSION\"/g" tool/provision_flutter.sh echo "Successfully updated! Checking file content:" grep "FLUTTER_VERSION=" tool/provision_flutter.sh else echo "Flutter SDK is already up to date." fi Current pinned version: 3.41.0 Latest stable version: 3.41.9 New stable version detected: 3.41.9. Updating script... Successfully updated! Checking file content: FLUTTER_VERSION="3.41.9" ``` --- Review the contribution guidelines below: - [x] I’ve reviewed the contributor guide and applied the relevant portions to this PR. - [x] I've included the required information in the description above. - [x] My up-to-date information is in the `AUTHORS` file. - [x] I've updated `CHANGELOG.md` if appropriate. <details> <summary>Contribution guidelines:</summary><br> - See our [contributor guide](../CONTRIBUTING.md) and the [Flutter organization contributor guide]([https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md) for general expectations for PRs. - Larger or significant changes should be discussed in an issue before creating a PR. - Dart contributions to our repos should follow the [Dart style guide](https://dart.dev/guides/language/effective-dart) and use `dart format`. - Java and Kotlin contributions should strive to follow Java and Kotlin best practices ([discussion](#8098)). </details> --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 574aae1 commit 9014833

3 files changed

Lines changed: 144 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Copyright 2026 The Chromium Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style license that can be
3+
# found in the LICENSE file.
4+
5+
name: Update Flutter SDK Pin
6+
7+
on:
8+
schedule:
9+
- cron: '0 0 * * 1' # Run every Monday at midnight UTC
10+
workflow_dispatch: # Allow manual execution from the Actions tab
11+
12+
permissions:
13+
contents: write
14+
pull-requests: write
15+
16+
jobs:
17+
check-and-update:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Checkout Repository
21+
uses: actions/checkout@v4
22+
23+
- name: Check for New Flutter stable version
24+
id: check-version
25+
run: |
26+
# 1. Retrieve the current pinned version from the shared script
27+
CURRENT_VERSION=$(grep -E 'FLUTTER_VERSION="[0-9.]+"' tool/provision_flutter.sh | head -n 1 | cut -d'"' -f2)
28+
echo "Current pinned version: $CURRENT_VERSION"
29+
30+
# 2. Retrieve the latest stable version from Flutter's official releases manifest
31+
LATEST_VERSION=$(curl -s https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json | jq -r '.releases | map(select(.channel == "stable"))[0].version')
32+
echo "Latest stable version: $LATEST_VERSION"
33+
34+
# 3. Compare and update if a newer version is available
35+
if [ "$CURRENT_VERSION" != "$LATEST_VERSION" ]; then
36+
echo "New Flutter stable version detected: $LATEST_VERSION"
37+
sed -i -e "s/FLUTTER_VERSION=\"$CURRENT_VERSION\"/FLUTTER_VERSION=\"$LATEST_VERSION\"/g" tool/provision_flutter.sh
38+
echo "updated=true" >> $GITHUB_OUTPUT
39+
echo "latest_version=$LATEST_VERSION" >> $GITHUB_OUTPUT
40+
else
41+
echo "Flutter SDK is already up to date."
42+
echo "updated=false" >> $GITHUB_OUTPUT
43+
fi
44+
45+
- name: Create Pull Request for Version Bump
46+
if: steps.check-version.outputs.updated == 'true'
47+
uses: peter-evans/create-pull-request@v6
48+
with:
49+
commit-message: "ci: bump pinned Flutter SDK to version ${{ steps.check-version.outputs.latest_version }}"
50+
title: "ci: bump pinned Flutter SDK to version ${{ steps.check-version.outputs.latest_version }}"
51+
body: |
52+
An automated check has detected a new stable release of the Flutter SDK.
53+
54+
* **New Pinned Version**: `${{ steps.check-version.outputs.latest_version }}`
55+
56+
This Pull Request updates our shared provisioning script (`tool/provision_flutter.sh`) to target this version. All presubmit tests will be run against this version to verify compatibility.
57+
branch: "auto-update-flutter-sdk"
58+
delete-branch: true
59+
labels: |
60+
dependencies
61+
ci
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2026 The Chromium Authors. All rights reserved.
3+
* Use of this source code is governed by a BSD-style license that can be
4+
* found in the LICENSE file.
5+
*/
6+
package io.flutter;
7+
8+
import org.junit.Test;
9+
import java.io.File;
10+
import java.nio.file.Files;
11+
import java.util.regex.Pattern;
12+
import static org.junit.Assert.assertTrue;
13+
14+
/**
15+
* Integrity tests for the CI/CD automation pipeline.
16+
* <p>
17+
* <b>Why this exists:</b>
18+
* Our CI/CD scripts rely on a shared bash script to provision the Flutter SDK, and a
19+
* scheduled GitHub Actions workflow to automatically check for new Flutter stable releases
20+
* and submit version-bumping Pull Requests.
21+
* <p>
22+
* If a developer modifies the provisioning script (e.g., renaming the constant FLUTTER_VERSION)
23+
* or alters the regex search format without updating the GitHub Actions workflow, the automation
24+
* will fail silently in production.
25+
* <p>
26+
* This class acts as a "meta-test" or "integrity-check" that runs during standard project
27+
* compilations (`./gradlew test`) to give developers an immediate early warning of synchronization
28+
* failures before code is merged.
29+
*/
30+
public class CIIntegrityTest {
31+
32+
/**
33+
* Verifies that the Flutter SDK provisioning script exists at the expected path
34+
* and defines the `FLUTTER_VERSION` constant in the exact semver format required.
35+
* <p>
36+
* If this test fails, it means `tool/provision_flutter.sh` was moved, deleted, or
37+
* the `FLUTTER_VERSION` constant definition was formatted incorrectly (or renamed).
38+
*/
39+
@Test
40+
public void testFlutterProvisioningScriptIntegrity() throws Exception {
41+
// The JVM's Current Working Directory (CWD) during unit tests is the repository root.
42+
File scriptFile = new File("tool/provision_flutter.sh");
43+
assertTrue("provision_flutter.sh must exist at the repository root 'tool/' directory", scriptFile.exists());
44+
45+
String content = Files.readString(scriptFile.toPath());
46+
47+
// Ensure the shell script declares the version constant in a predictable pattern:
48+
// E.g. FLUTTER_VERSION="3.41.0"
49+
Pattern versionPattern = Pattern.compile("FLUTTER_VERSION=\"[0-9.]+\"");
50+
assertTrue(
51+
"provision_flutter.sh must define a constant FLUTTER_VERSION matching the expected format (e.g., FLUTTER_VERSION=\"3.41.0\"). " +
52+
"This constant is critical because the automated GitHub Actions updater searches for this exact pattern to bump the version.",
53+
versionPattern.matcher(content).find()
54+
);
55+
}
56+
57+
/**
58+
* Verifies that the automated GitHub Actions updater workflow is synchronized
59+
* with the provisioning script.
60+
* <p>
61+
* Specifically, it ensures the workflow searches for the exact constant pattern
62+
* `FLUTTER_VERSION="[0-9.]+"` to perform its automated string replacement. If a developer
63+
* renames the constant in the script, they MUST also update the workflow, otherwise this test fails.
64+
*/
65+
@Test
66+
public void testGitHubWorkflowRegexSync() throws Exception {
67+
File workflowFile = new File(".github/workflows/update_flutter.yaml");
68+
assertTrue("update_flutter.yaml workflow must exist in the '.github/workflows/' directory", workflowFile.exists());
69+
70+
String content = Files.readString(workflowFile.toPath());
71+
72+
// The workflow must use the constant name followed by a flexible regex query in single or double quotes.
73+
// E.g., matches 'FLUTTER_VERSION="[0-9.]+"' or 'FLUTTER_VERSION="[0-9.]*"' or "FLUTTER_VERSION='[0-9.]+'"
74+
Pattern regexPattern = Pattern.compile("FLUTTER_VERSION=['\"].+?['\"]");
75+
assertTrue(
76+
"update_flutter.yaml must reference the 'FLUTTER_VERSION' constant with a regex pattern (e.g. 'FLUTTER_VERSION=\"[0-9.]+\"') " +
77+
"in its search-and-replace logic.",
78+
regexPattern.matcher(content).find()
79+
);
80+
}
81+
}

tool/provision_flutter.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ set -e
99
# Provision the pinned Flutter SDK if not present
1010
if [ ! -d "../flutter" ]; then
1111
OS_NAME=$(uname -s | tr '[:upper:]' '[:lower:]')
12+
# Pinned Flutter SDK version. This constant is automatically checked and updated weekly
13+
# by the .github/workflows/update_flutter.yaml GitHub Actions workflow.
1214
FLUTTER_VERSION="3.41.0"
1315

1416
echo "Provisioning Flutter SDK version ${FLUTTER_VERSION} for ${OS_NAME}..."

0 commit comments

Comments
 (0)