feat: linux_analyze in a workflow (#187889)
fixes #187817
- Will run the same as a LUCI recipe, but in GitHub actions
- Updated composite-flutter-setup to install/cache ktlint.
- Should detect and wait for engine builds before running
Successful run:
https://github.com/flutter/flutter/actions/runs/27382050611/job/80920878955?pr=187889#annotation:15:3
## Pre-launch Checklist
- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.
diff --git a/.github/actions/composite-flutter-setup/action.yml b/.github/actions/composite-flutter-setup/action.yml
index d60e8d1..61c1a81 100644
--- a/.github/actions/composite-flutter-setup/action.yml
+++ b/.github/actions/composite-flutter-setup/action.yml
@@ -70,7 +70,7 @@
run: |
echo "revision=${{inputs.ANDROID_TOOLS_VERSION}};${{inputs.ANDROID_PLATFORM}};build-tools;${{inputs.ANDROID_BUILDTOOLS}}" >> "$GITHUB_OUTPUT"
- name: Android SDK Cache
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684
+ uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae
id: android-sdk-setup
if: env.ACT
with:
@@ -90,6 +90,37 @@
echo "/opt/android/sdk/cmdline-tools/${{ inputs.ANDROID_TOOLS_VERSION }}/bin" >> "$GITHUB_PATH"
echo "/opt/android/sdk/platform-tools" >> "$GITHUB_PATH"
+ # ktlint is needed by tree analyze
+ - name: ktlint cache
+ id: ktlint-cache
+ uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae
+ with:
+ # Cache the ktlint binary in runner.temp to avoid interfering with Flutter's bin folder
+ path: ${{ runner.temp }}/ktlint-bin/ktlint
+ # Hardcode the version in the key since it's hardcoded in the URL
+ key: ${{ runner.os }}-ktlint-1.5.0
+
+ - name: Download and Install ktlint 1.5.0
+ if: steps.ktlint-cache.outputs.cache-hit != 'true'
+ shell: bash
+ run: |
+ mkdir -p ${{ runner.temp }}/ktlint-bin
+
+ # 1. Download directly into the temp directory
+ curl -fsSL -o ${{ runner.temp }}/ktlint-bin/ktlint https://github.com/ktlint/ktlint/releases/download/1.5.0/ktlint
+
+ # 2. Verify the checksum to ensure the binary hasn't been tampered with
+ EXPECTED_SHA="a16be01dcc480aab2f55f444b620142152f66e31564b3b9376506d624c28a2ad"
+ echo "$EXPECTED_SHA ${{ runner.temp }}/ktlint-bin/ktlint" | sha256sum --check -
+
+ # 3. Make the binary executable
+ chmod a+x ${{ runner.temp }}/ktlint-bin/ktlint
+
+ - name: Add ktlint to PATH
+ shell: bash
+ run: |
+ echo "${{ runner.temp }}/ktlint-bin" >> "$GITHUB_PATH"
+
- name: Add `flutter` to the PATH
shell: bash
run: |
@@ -108,7 +139,7 @@
run: |
echo "revision=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Flutter artifacts cache
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684
+ uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae
id: flutter-artifacts
with:
path: ${{ github.workspace }}/bin/cache
@@ -122,7 +153,7 @@
find dev examples packages -name "pubspec.yaml" -print0 | sort -z | xargs -0 cat | sha256sum >> "$RUNNER_TEMP/pub_deps_sha"
echo "revision=$(cat "$RUNNER_TEMP/pub_deps_sha")" >> "$GITHUB_OUTPUT"
- name: pub package cache
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684
+ uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae
id: pub-cache
with:
path: |
diff --git a/.github/scripts/did_engine_change.sh b/.github/scripts/did_engine_change.sh
new file mode 100755
index 0000000..c80256b
--- /dev/null
+++ b/.github/scripts/did_engine_change.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+# Copyright 2014 The Flutter Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# did_engine_change.sh
+#
+# Reads a list of files from stdin and prints "true" if any file matches
+# the engine pattern, or "false" otherwise.
+#
+# Usage:
+# .github/scripts/git_files_changed.sh <hash|ref> | .github/scripts/did_engine_change.sh
+#
+# Test:
+#
+# Pass ("true")
+# ```shell
+# cat <<EOF | .github/scripts/did_engine_change.sh
+# packages/flutter/lib/src/material/button.dart
+# engine/src/flutter/shell/common/shell.cc
+# dev/bots/test.dart
+# EOF
+# echo "DEPS" | .github/scripts/did_engine_change.sh
+# echo "bin/internal/content_aware_hash.sh" | .github/scripts/did_engine_change.sh
+# ```
+#
+# Fail ("false")
+# ```shell
+# cat <<EOF | .github/scripts/did_engine_change.sh
+# packages/flutter/lib/src/material/button.dart
+# dev/bots/test.dart
+# EOF
+# ```
+
+if grep -qE "^(DEPS|engine/.*|bin/internal/content_aware_hash\.(ps1|sh))$"; then
+ echo "true"
+else
+ echo "false"
+fi
diff --git a/.github/scripts/git_files_changed.sh b/.github/scripts/git_files_changed.sh
new file mode 100755
index 0000000..a5ea87b
--- /dev/null
+++ b/.github/scripts/git_files_changed.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+# Copyright 2014 The Flutter Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# git_files_changed.sh <hash|ref>
+# Returns a list of files that change from the given hash or ref compared to HEAD.
+#
+# Example:
+# ./.github/scripts/git_files_changed.sh upstream/master
+# ./.github/scripts/git_files_changed.sh eccbe7d4f7ac6ebf35d342db58e37736ed6c60f9
+if [ -z "$1" ]; then
+ echo "Usage: $0 <hash|ref>" >&2
+ exit 1
+fi
+
+BASE_REF="$1"
+
+# Get the changed files using triple-dot syntax
+# This automatically finds the common ancestor and ignores unrelated changes on the base branch
+git diff --name-only "$BASE_REF"...HEAD
diff --git a/.github/workflows/tree-analyze.yml b/.github/workflows/tree-analyze.yml
new file mode 100644
index 0000000..b2b3801
--- /dev/null
+++ b/.github/workflows/tree-analyze.yml
@@ -0,0 +1,76 @@
+name: Tree Analyze
+
+on:
+ pull_request:
+ branches: [master]
+ merge_group:
+ branches: [master]
+
+jobs:
+ Tree_analyze:
+ permissions:
+ contents: read
+ runs-on: ubuntu-latest
+ timeout-minutes: 130
+
+ steps:
+ # Note: we must check out the tree for the composite action to be available
+ - name: Checkout code
+ if: env.ACT != 'true'
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+ with:
+ fetch-depth: 0
+ ref: ${{ github.event.pull_request.head.sha || github.sha }}
+
+ - name: Checkout code (act local)
+ if: env.ACT
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+
+ - name: Check Engine Changes
+ id: check_engine
+ run: |
+ # 1. Determine local base tracking SHA
+ if [ "${{ github.event_name }}" = "pull_request" ]; then
+ BASE_REF="${{ github.event.pull_request.base.sha }}"
+ elif [ "${{ github.event_name }}" = "merge_group" ]; then
+ BASE_REF="${{ github.event.merge_group.base_sha }}"
+ else
+ BASE_REF="origin/master"
+ fi
+
+ echo "Comparing against base SHA: $BASE_REF"
+
+ # 2. Get the changed files using the helper script
+ CHANGED_FILES=$(.github/scripts/git_files_changed.sh "$BASE_REF")
+ echo "Changed files in this branch:"
+ echo "$CHANGED_FILES"
+
+ # 3. Check if any file matches engine pattern using the helper script:
+ if [ "$(echo "$CHANGED_FILES" | .github/scripts/did_engine_change.sh)" = "true" ]; then
+ echo "Engine file changed - forced to wait for Linux linux_host_engine"
+ echo "changed=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "changed=false" >> "$GITHUB_OUTPUT"
+ fi
+
+ - name: Wait for test check-in
+ if: steps.check_engine.outputs.changed == 'true' && env.ACT != 'true'
+ uses: lewagon/wait-on-check-action@9312864dfbc9fd208e9c0417843430751c042800
+ with:
+ ref: ${{ github.event.pull_request.head.sha || github.sha }}
+ check-name: 'Linux linux_host_engine'
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ wait-interval: 30 # seconds
+ checks-discovery-timeout: 3600 # Wait up to 1 hour for LUCI to register the check
+ allowed-conclusions: success,neutral
+ verbose: false
+
+ - name: Redirect engine.version
+ if: steps.check_engine.outputs.changed == 'true' && github.event_name == 'pull_request'
+ run: echo "FLUTTER_REALM=flutter_archives_v2" >> $GITHUB_ENV
+
+ - uses: ./.github/actions/composite-flutter-setup
+
+ - name: Run Analyze
+ run: |
+ SHARD=analyze LUCI_CI=true dart --enable-asserts dev/bots/test.dart
diff --git a/dev/bots/analyze.dart b/dev/bots/analyze.dart
index 81b77e4..dad6843 100644
--- a/dev/bots/analyze.dart
+++ b/dev/bots/analyze.dart
@@ -2771,6 +2771,8 @@
const Set<String> kExecutableAllowlist = <String>{
'.autoroller-preupload.sh',
'.claude/skills',
+ '.github/scripts/did_engine_change.sh',
+ '.github/scripts/git_files_changed.sh',
'bin/dart',
'bin/flutter',
'bin/flutter-dev',