diff --git a/.github/scripts/detect-android-changes.cjs b/.github/scripts/detect-android-changes.cjs index bb6f6ac..2ff45e0 100644 --- a/.github/scripts/detect-android-changes.cjs +++ b/.github/scripts/detect-android-changes.cjs @@ -42,10 +42,12 @@ function isAppPath(filePath) { function isNativeBuildPath(filePath) { const p = normalizePath(filePath); if (!p) return false; - // Native build requires: Android code, CI changes, or build config + // Native build requires: Android code, CI-critical files, or build config. + // Only CI files that affect build behavior trigger release builds; doc/comment changes do not. return ( p.startsWith('android/') || - p.startsWith('.github/') || + p === '.github/workflows/android-build.yml' || + p.startsWith('.github/scripts/') || p === 'pubspec.yaml' || p === 'pubspec.lock' || p === 'analysis_options.yaml' || diff --git a/.github/scripts/detect-android-changes.test.cjs b/.github/scripts/detect-android-changes.test.cjs index fdf22f4..c82dc79 100644 --- a/.github/scripts/detect-android-changes.test.cjs +++ b/.github/scripts/detect-android-changes.test.cjs @@ -681,3 +681,106 @@ describe('Release Job Gating (native_build_changed output)', () => { }); }); +describe('GitHub Actions Workflow Output Wiring and Release Job Conditions', () => { + const baseSha = '03446911949ff8b27543280bfe226d9babd6fd7a'; + const headSha = '247ccde71e4378dacd45121c891f45aad5ed2424'; + + it('detector outputs native_build_changed for GitHub Actions output file', () => { + // Simulate the detector writing to GITHUB_OUTPUT + const result = detectAndroidChanges({ + eventName: 'push', + baseSha, + headSha + }, { getChangedFilesFn: () => ['lib/main.dart'] }); + + // The detector MUST set native_build_changed=false for Dart-only changes + assert.strictEqual(result.native_build_changed, false, 'Dart-only should set native_build_changed=false'); + assert.strictEqual(result.android_changed, true, 'But android_changed=true still drives heavy build'); + }); + + it('workflow release condition evaluates correctly: Dart-only changes skip release', () => { + // Simulate GitHub Actions condition: if: needs.detect.outputs.native_build_changed == 'true' && github.ref == 'refs/heads/master' + const result = detectAndroidChanges({ + eventName: 'push', + baseSha, + headSha + }, { getChangedFilesFn: () => ['lib/main.dart', 'test/widget_test.dart'] }); + + // Release job condition evaluates to false + const nativeBuildChanged = result.native_build_changed; + const onMaster = true; // github.ref == 'refs/heads/master' + const releaseJobShouldRun = nativeBuildChanged && onMaster; + + assert.strictEqual(nativeBuildChanged, false, 'Dart-only should output false'); + assert.strictEqual(releaseJobShouldRun, false, 'Release job should NOT run for Dart-only'); + }); + + it('workflow release condition evaluates correctly: Android changes run release', () => { + // Simulate GitHub Actions condition: if: needs.detect.outputs.native_build_changed == 'true' && github.ref == 'refs/heads/master' + const result = detectAndroidChanges({ + eventName: 'push', + baseSha, + headSha + }, { getChangedFilesFn: () => ['android/app/build.gradle'] }); + + // Release job condition evaluates to true + const nativeBuildChanged = result.native_build_changed; + const onMaster = true; // github.ref == 'refs/heads/master' + const releaseJobShouldRun = nativeBuildChanged && onMaster; + + assert.strictEqual(nativeBuildChanged, true, 'Android changes should output true'); + assert.strictEqual(releaseJobShouldRun, true, 'Release job SHOULD run for Android changes'); + }); + + it('workflow release condition evaluates correctly: CI-critical changes run release', () => { + // Simulate GitHub Actions condition: if: needs.detect.outputs.native_build_changed == 'true' && github.ref == 'refs/heads/master' + const result = detectAndroidChanges({ + eventName: 'push', + baseSha, + headSha + }, { getChangedFilesFn: () => ['.github/workflows/android-build.yml'] }); + + // Release job condition evaluates to true + const nativeBuildChanged = result.native_build_changed; + const onMaster = true; // github.ref == 'refs/heads/master' + const releaseJobShouldRun = nativeBuildChanged && onMaster; + + assert.strictEqual(nativeBuildChanged, true, 'CI-critical changes should output true'); + assert.strictEqual(releaseJobShouldRun, true, 'Release job SHOULD run for CI-critical changes'); + }); + + it('workflow release condition evaluates correctly: Config changes run release', () => { + // Simulate GitHub Actions condition: if: needs.detect.outputs.native_build_changed == 'true' && github.ref == 'refs/heads/master' + const result = detectAndroidChanges({ + eventName: 'push', + baseSha, + headSha + }, { getChangedFilesFn: () => ['pubspec.yaml'] }); + + // Release job condition evaluates to true + const nativeBuildChanged = result.native_build_changed; + const onMaster = true; // github.ref == 'refs/heads/master' + const releaseJobShouldRun = nativeBuildChanged && onMaster; + + assert.strictEqual(nativeBuildChanged, true, 'Config changes should output true'); + assert.strictEqual(releaseJobShouldRun, true, 'Release job SHOULD run for config changes'); + }); + + it('workflow release condition evaluates correctly: Unrelated changes skip release', () => { + // Simulate GitHub Actions condition: if: needs.detect.outputs.native_build_changed == 'true' && github.ref == 'refs/heads/master' + const result = detectAndroidChanges({ + eventName: 'push', + baseSha, + headSha + }, { getChangedFilesFn: () => ['docs/readme.md', 'website/app.tsx'] }); + + // Release job condition evaluates to false + const nativeBuildChanged = result.native_build_changed; + const onMaster = true; // github.ref == 'refs/heads/master' + const releaseJobShouldRun = nativeBuildChanged && onMaster; + + assert.strictEqual(nativeBuildChanged, false, 'Unrelated changes should output false'); + assert.strictEqual(releaseJobShouldRun, false, 'Release job should NOT run for unrelated changes'); + }); +}); + diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml index 405bada..424ba2d 100644 --- a/.github/workflows/android-build.yml +++ b/.github/workflows/android-build.yml @@ -30,6 +30,7 @@ jobs: outputs: android_changed: ${{ steps.detector.outputs.android_changed }} app_changed: ${{ steps.detector.outputs.app_changed }} + native_build_changed: ${{ steps.detector.outputs.native_build_changed }} steps: - name: Checkout uses: actions/checkout@v6