From aecd0b1a28610eda4af0d9b9c599be90c5f41be6 Mon Sep 17 00:00:00 2001 From: Sem Van Broekhoven <144097969+dotsem@users.noreply.github.com> Date: Thu, 12 Nov 2026 13:27:52 +0100 Subject: [PATCH 1/7] chore: remove node test fixtures --- tests/fixtures/clean.sarif | 15 --- tests/fixtures/dirty.sarif | 53 -------- tests/fixtures/node/index.js | 5 - tests/fixtures/node/package.json | 22 ---- tests/fixtures/node/pnpm-lock.yaml | 200 ----------------------------- 5 files changed, 295 deletions(-) delete mode 100644 tests/fixtures/clean.sarif delete mode 100644 tests/fixtures/dirty.sarif delete mode 100644 tests/fixtures/node/index.js delete mode 100644 tests/fixtures/node/package.json delete mode 100644 tests/fixtures/node/pnpm-lock.yaml diff --git a/tests/fixtures/clean.sarif b/tests/fixtures/clean.sarif deleted file mode 100644 index 792b929..0000000 --- a/tests/fixtures/clean.sarif +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", - "version": "2.1.0", - "runs": [ - { - "tool": { - "driver": { - "name": "Trivy Test Mock", - "rules": [] - } - }, - "results": [] - } - ] -} diff --git a/tests/fixtures/dirty.sarif b/tests/fixtures/dirty.sarif deleted file mode 100644 index d306a31..0000000 --- a/tests/fixtures/dirty.sarif +++ /dev/null @@ -1,53 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", - "version": "2.1.0", - "runs": [ - { - "tool": { - "driver": { - "name": "Trivy Test Mock", - "rules": [ - { - "id": "CVE-2026-0001", - "name": "MockCriticalVulnerability", - "defaultConfiguration": { "level": "error" } - }, - { - "id": "CVE-2026-0002", - "name": "MockWarningVulnerability", - "defaultConfiguration": { "level": "warning" } - } - ] - } - }, - "results": [ - { - "ruleId": "CVE-2026-0001", - "level": "error", - "message": { "text": "Critical vulnerability detected in dependency" }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { "uri": "package.json" }, - "region": { "startLine": 14 } - } - } - ] - }, - { - "ruleId": "CVE-2026-0002", - "level": "warning", - "message": { "text": "Medium severity configuration warning" }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { "uri": "Dockerfile" }, - "region": { "startLine": 1 } - } - } - ] - } - ] - } - ] -} diff --git a/tests/fixtures/node/index.js b/tests/fixtures/node/index.js deleted file mode 100644 index 11d258d..0000000 --- a/tests/fixtures/node/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import { writeFileSync } from "node:fs"; - -// Record received CLI args so internal-test-node.yml can assert how build-node passes `args` -writeFileSync("build-args.json", JSON.stringify(process.argv.slice(2))); -console.log("Build completed successfully."); diff --git a/tests/fixtures/node/package.json b/tests/fixtures/node/package.json deleted file mode 100644 index 152b301..0000000 --- a/tests/fixtures/node/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "node", - "version": "0.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build": "node index.js" - }, - "keywords": [], - "author": "", - "license": "ISC", - "devEngines": { - "packageManager": { - "name": "pnpm", - "version": "12.15.0", - "onFail": "download" - } - }, - "packageManager": "pnpm@12.15.0", - "type": "module" -} diff --git a/tests/fixtures/node/pnpm-lock.yaml b/tests/fixtures/node/pnpm-lock.yaml deleted file mode 100644 index 929a88d..0000000 --- a/tests/fixtures/node/pnpm-lock.yaml +++ /dev/null @@ -1,200 +0,0 @@ ---- -lockfileVersion: "9.0" - -importers: - .: - configDependencies: {} - packageManagerDependencies: - pnpm: - specifier: 12.15.0 - version: 12.15.0 - -packages: - "@pnpm/exe.android-arm64@12.15.0": - resolution: - { - integrity: sha512-eVCqmscY70LGvgRzu3mi8IBWBFGix+a6mNZoWmXxESsBdeiKEF9xQSuaeel10yn+zI7TXy2jlI63vFLzETk+aQ==, - } - cpu: [arm64] - os: [android] - - "@pnpm/exe.android-x64@12.15.0": - resolution: - { - integrity: sha512-Gx/WbkFcdUFUg8g8AShezcS9EiQxfYvdAm2hfKLO8sfJmJvKOCt5fzlgHWDzmGhoclibgW+tUGZ/KF4F4wOTCA==, - } - cpu: [x64] - os: [android] - - "@pnpm/exe.darwin-arm64@12.15.0": - resolution: - { - integrity: sha512-N0ZykiDWCvqSBJvt71j7cSoBR993FqvYZFtmpCtqvW3pguiI+NAgraLPA5OTwu5mV+/d8hYpVcPktJA3xFDLtA==, - } - cpu: [arm64] - os: [darwin] - - "@pnpm/exe.darwin-x64@12.15.0": - resolution: - { - integrity: sha512-7Ntr2HlBUxzwWOg7NwDxCbRW/3vXuqKO+hcXm+w3SBi9rrHY53li1wLqWVT7Gjxd+lVEHM1QJZo6WJEGFU8C6A==, - } - cpu: [x64] - os: [darwin] - - "@pnpm/exe.freebsd-x64@12.15.0": - resolution: - { - integrity: sha512-btOiyL7fL7W4uQGH0aqFai2TNC3IoKcZ7gNHBVbEFZt2dWrnrD0SufL3iGs3fLlmb4OtuU35uf4Zpr9V9Jh9mA==, - } - cpu: [x64] - os: [freebsd] - - "@pnpm/exe.linux-arm64-musl@12.15.0": - resolution: - { - integrity: sha512-pNC38k+O+90FOjbPNSnw0E7apq1PKTmz3fFe1amn+lys/U5kL++uzir8bRiho8ONcyA3d9EU7wdqcY0DKkj+JA==, - } - cpu: [arm64] - os: [linux] - libc: [musl] - - "@pnpm/exe.linux-arm64@12.15.0": - resolution: - { - integrity: sha512-qML/8aCXslh8td/w4U+HJNqUcZ2+m6EfTbWScIzZRmyPsRFN+xB4LP1U/hymxc1iSJiKtG56HJx2fvUErWFIGw==, - } - cpu: [arm64] - os: [linux] - libc: [glibc] - - "@pnpm/exe.linux-ppc64@12.15.0": - resolution: - { - integrity: sha512-tjRDUUl7RCJ6T/6eoNPxOqkwU4Mzi5RtTXmSAxujyz37OTwwsMU7Ps087Zk4gHcUvRSeyF40ZFsew3vtE+nmUA==, - } - cpu: [ppc64] - os: [linux] - libc: [glibc] - - "@pnpm/exe.linux-riscv64@12.15.0": - resolution: - { - integrity: sha512-bF1uBWEfbwaeryz6lIiaJ8AWD082otZfvzGX5l+6LZRpRwUEpJHUsJrORaYP5/cNMv5ZR2p6TvvcJcQzwtDzmw==, - } - cpu: [riscv64] - os: [linux] - libc: [glibc] - - "@pnpm/exe.linux-s390x@12.15.0": - resolution: - { - integrity: sha512-zKMhIzsexYMToeLmqV5jPsvV/f9vwsJb2kUH2NFYzalbxImeBrlA4kL7GwF8gD4pagV6jYYcJb8RjbZIdjHptg==, - } - cpu: [s390x] - os: [linux] - libc: [glibc] - - "@pnpm/exe.linux-x64-musl@12.15.0": - resolution: - { - integrity: sha512-A9J0Jg0Rdx+qQU4qXtwyST5tto9IOu1zi4fDk35nbc6DWahggbcKtFvZPKFwL2qgUXGXOX//hXv6z/sp6emO8A==, - } - cpu: [x64] - os: [linux] - libc: [musl] - - "@pnpm/exe.linux-x64@12.15.0": - resolution: - { - integrity: sha512-yc+QlZR+9+nr29J4fRzhMoapgxTBDndq/+peyoq1XUufnQFnfbXUMFudDIKzHi545linGqmOmXfn0NoufNgcOg==, - } - cpu: [x64] - os: [linux] - libc: [glibc] - - "@pnpm/exe.win32-arm64@12.15.0": - resolution: - { - integrity: sha512-fPXZxuY1VclkOga9bc53qNCXcegfeoBR8+Ue+AospDwKzuGGSHll0oDxkLfkmpjyRqpe7bH5DyQPYp/xSMaIVA==, - } - cpu: [arm64] - os: [win32] - - "@pnpm/exe.win32-x64@12.15.0": - resolution: - { - integrity: sha512-nrd+Tp1We1DGj4DGG5Bea9lZNh9y1PjlLBdEwdInS4D0h0UIDtM7JaGGGDJhNzIwcHASyihWI7HLV+dtG+fhYg==, - } - cpu: [x64] - os: [win32] - - pnpm@12.15.0: - resolution: - { - integrity: sha512-s5UdbW/0qfE9x89WzSjSuOs328ycaOUuHhGRhz6p+CoBcoerULqtxczElPA2xnwOR+sTYIwvo29HcRth6hKOYQ==, - } - engines: { node: ">=18" } - hasBin: true - -snapshots: - "@pnpm/exe.android-arm64@12.15.0": - optional: true - - "@pnpm/exe.android-x64@12.15.0": - optional: true - - "@pnpm/exe.darwin-arm64@12.15.0": - optional: true - - "@pnpm/exe.darwin-x64@12.15.0": - optional: true - - "@pnpm/exe.freebsd-x64@12.15.0": - optional: true - - "@pnpm/exe.linux-arm64-musl@12.15.0": - optional: true - - "@pnpm/exe.linux-arm64@12.15.0": - optional: true - - "@pnpm/exe.linux-ppc64@12.15.0": - optional: true - - "@pnpm/exe.linux-riscv64@12.15.0": - optional: true - - "@pnpm/exe.linux-s390x@12.15.0": - optional: true - - "@pnpm/exe.linux-x64-musl@12.15.0": - optional: true - - "@pnpm/exe.linux-x64@12.15.0": - optional: true - - "@pnpm/exe.win32-arm64@12.15.0": - optional: true - - "@pnpm/exe.win32-x64@12.15.0": - optional: true - - pnpm@12.15.0: - optionalDependencies: - "@pnpm/exe.android-arm64": 12.15.0 - "@pnpm/exe.android-x64": 12.15.0 - "@pnpm/exe.darwin-arm64": 12.15.0 - "@pnpm/exe.darwin-x64": 12.15.0 - "@pnpm/exe.freebsd-x64": 12.15.0 - "@pnpm/exe.linux-arm64": 12.15.0 - "@pnpm/exe.linux-arm64-musl": 12.15.0 - "@pnpm/exe.linux-ppc64": 12.15.0 - "@pnpm/exe.linux-riscv64": 12.15.0 - "@pnpm/exe.linux-s390x": 12.15.0 - "@pnpm/exe.linux-x64": 12.15.0 - "@pnpm/exe.linux-x64-musl": 12.15.0 - "@pnpm/exe.win32-arm64": 12.15.0 - "@pnpm/exe.win32-x64": 12.15.0 - ---- From 28471855316fb9bb3b2dac2f5df24745615cf862 Mon Sep 17 00:00:00 2001 From: Sem Van Broekhoven <144097969+dotsem@users.noreply.github.com> Date: Thu, 12 Nov 2026 13:30:04 +0100 Subject: [PATCH 2/7] chore: update gitignore --- .gitignore | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 5d1d311..33e8634 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,5 @@ -node_modules/ -.pnpm-store/ *.log .DS_Store Thumbs.db .dart_tool/ -**/build/ -**/ephemeral/ -build-args.json +tests/fixtures/build/ From 71c8f835c38687b987e95f34be6689db72f922a5 Mon Sep 17 00:00:00 2001 From: Sem Van Broekhoven <144097969+dotsem@users.noreply.github.com> Date: Thu, 12 Nov 2026 13:30:22 +0100 Subject: [PATCH 3/7] chore: remove prettierignore --- .prettierignore | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 .prettierignore diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index be0c97c..0000000 --- a/.prettierignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules/ -dist/ -**/dist/** -pnpm-lock.yaml From 45277f8a4e581d4823bab07faab3a44a863646af Mon Sep 17 00:00:00 2001 From: Sem Van Broekhoven <144097969+dotsem@users.noreply.github.com> Date: Thu, 12 Nov 2026 13:31:01 +0100 Subject: [PATCH 4/7] docs(agents): update guidelines about breaking changes --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index a6255fd..0b1a1de 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,7 +55,7 @@ Opinionated composite actions and reusable workflows for building and shipping F ## Public API & Versioning -- Inputs, outputs, input defaults, artifact names and reusable-workflow `with:`/`secrets:` are public API. Renaming, removing or changing the default of any of them is a **breaking change**: flag it explicitly in your response so the human can bump the major version. +- Inputs, outputs, input defaults, artifact names and reusable-workflow `with:`/`secrets:` are public API. Renaming, removing or changing the default of any of them is a **breaking change**: flag it explicitly in your response so the human can bump the minor version (during `0.x`). - Every public input/output change must be reflected in `README.md` (tables + usage examples). - Releases are cut only by the human via the `Release` workflow (`internal-release.yml`). Never create or move tags. From 0915c88b8926ecf368537e5a13ebf82848763fe6 Mon Sep 17 00:00:00 2001 From: Sem Van Broekhoven <144097969+dotsem@users.noreply.github.com> Date: Thu, 12 Nov 2026 13:37:13 +0100 Subject: [PATCH 5/7] refactor(dependabot): remove npm dep --- .github/dependabot.yml | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 29538e3..df7cdaa 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -18,27 +18,3 @@ updates: patterns: - "*" - # Root Monorepo TypeScript & Action dependencies - - package-ecosystem: "npm" - directory: "/" - schedule: - interval: "weekly" - day: "monday" - time: "04:00" - timezone: "UTC" - target-branch: "main" - open-pull-requests-limit: 10 - cooldown: - default-days: 7 - commit-message: - prefix: "chore(deps)" - groups: - tooling: - patterns: - - "typescript*" - - "tsup*" - - "@types/*" - - "prettier*" - actions: - patterns: - - "@actions/*" From ebe4c13db87ca0b55fb64906c38f59c5da40b1a3 Mon Sep 17 00:00:00 2001 From: Sem Van Broekhoven <144097969+dotsem@users.noreply.github.com> Date: Thu, 12 Nov 2026 13:40:00 +0100 Subject: [PATCH 6/7] refactor: change naming to new repo name & structure --- .github/workflows/internal-test.yml | 79 ++++++++++------- build/action.yml | 53 +++++++----- build/scripts/package-android.sh | 7 +- build/scripts/package-ios.sh | 5 +- build/scripts/package-linux.sh | 6 +- build/scripts/package-macos.sh | 5 +- build/scripts/package-windows.ps1 | 11 ++- build/scripts/resolve-metadata.sh | 22 +++-- build/scripts/set-artifact-outputs.sh | 7 +- matrix/scripts/resolve-matrix.sh | 11 ++- tests/fixtures/test/app_test.dart | 7 ++ tests/lib.sh | 118 -------------------------- tests/package-android.test.sh | 20 ++--- tests/package-apple.test.sh | 20 ++--- tests/package-linux.test.sh | 10 +-- tests/package-windows.test.sh | 12 +-- tests/resolve-matrix.test.sh | 13 +-- tests/resolve-metadata.test.sh | 37 +++++--- tests/resolve-version.test.sh | 6 +- tests/set-artifact-outputs.test.sh | 26 +++--- 20 files changed, 221 insertions(+), 254 deletions(-) create mode 100644 tests/fixtures/test/app_test.dart delete mode 100644 tests/lib.sh diff --git a/.github/workflows/internal-test.yml b/.github/workflows/internal-test.yml index 2a14865..6d00027 100644 --- a/.github/workflows/internal-test.yml +++ b/.github/workflows/internal-test.yml @@ -1,34 +1,36 @@ -name: Test Flutter Action +name: Test Flutter Actions on: pull_request: paths: - - "build-flutter/**" - - "setup-flutter/**" - - "flutter-matrix/**" - - ".github/workflows/flutter-*.yml" - - ".github/workflows/internal-test-flutter.yml" - - "tests/fixtures/flutter/**" + - "build/**" + - "setup/**" + - "matrix/**" + - ".github/workflows/ci.yml" + - ".github/workflows/cd.yml" + - ".github/workflows/internal-test.yml" + - "tests/fixtures/**" push: branches: [main] paths: - - "build-flutter/**" - - "setup-flutter/**" - - "flutter-matrix/**" - - ".github/workflows/flutter-*.yml" - - ".github/workflows/internal-test-flutter.yml" - - "tests/fixtures/flutter/**" + - "build/**" + - "setup/**" + - "matrix/**" + - ".github/workflows/ci.yml" + - ".github/workflows/cd.yml" + - ".github/workflows/internal-test.yml" + - "tests/fixtures/**" workflow_dispatch: permissions: contents: read concurrency: - group: test-flutter-${{ github.ref }} + group: internal-test-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: - test-build-flutter: + test-build: runs-on: ubuntu-latest strategy: fail-fast: false @@ -40,50 +42,58 @@ jobs: expected-suffix: "-debug" steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Test build-flutter (${{ matrix.build-mode }}) + - name: Test build (${{ matrix.build-mode }}) id: build-app - uses: ./build-flutter + uses: ./build with: target: "linux" build-mode: ${{ matrix.build-mode }} - working-directory: "tests/fixtures/flutter" + working-directory: "tests/fixtures" - name: Assert build artifacts and outputs shell: bash env: EXPECTED_NAME: "test_app-linux${{ matrix.expected-suffix }}" + ARTIFACT_NAME: ${{ steps.build-app.outputs.artifact-name }} + ARTIFACT_PATH: ${{ steps.build-app.outputs.artifact-path }} run: | - test -f "tests/fixtures/flutter/dist/${EXPECTED_NAME}.tar.gz" - tar -tzf "tests/fixtures/flutter/dist/${EXPECTED_NAME}.tar.gz" > /dev/null - if [ "${{ steps.build-app.outputs.artifact-name }}" != "$EXPECTED_NAME" ]; then - echo "Expected artifact-name '$EXPECTED_NAME', got '${{ steps.build-app.outputs.artifact-name }}'" + DIST_DIR="$RUNNER_TEMP/flutter-dist/linux" + test -f "$DIST_DIR/${EXPECTED_NAME}.tar.gz" + tar -tzf "$DIST_DIR/${EXPECTED_NAME}.tar.gz" > /dev/null + if [ "$ARTIFACT_NAME" != "$EXPECTED_NAME" ]; then + echo "Expected artifact-name '$EXPECTED_NAME', got '$ARTIFACT_NAME'" exit 1 fi - if [ "${{ steps.build-app.outputs.artifact-path }}" != "tests/fixtures/flutter/dist/*" ]; then - echo "Expected artifact-path 'tests/fixtures/flutter/dist/*', got '${{ steps.build-app.outputs.artifact-path }}'" + if [ "$ARTIFACT_PATH" != "$DIST_DIR/*" ]; then + echo "Expected artifact-path '$DIST_DIR/*', got '$ARTIFACT_PATH'" + exit 1 + fi + if [ -e tests/fixtures/dist ]; then + echo "Packaging must not write into the working directory" exit 1 fi - test-setup-flutter-range: + test-setup-range: runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Use a caret constraint in the fixture run: | - sed -i 's/^ flutter: .*/ flutter: ^3.47.1/' tests/fixtures/flutter/pubspec.yaml - - uses: ./setup-flutter + sed -i 's/^ flutter: [0-9].*/ flutter: ^3.47.1/' tests/fixtures/pubspec.yaml + grep -qx ' flutter: ^3.47.1' tests/fixtures/pubspec.yaml + - uses: ./setup with: - working-directory: "tests/fixtures/flutter" + working-directory: "tests/fixtures" - name: Assert lower bound was installed run: flutter --version | grep -F "Flutter 3.47.1 " - test-flutter-matrix: + test-matrix: runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Resolve valid matrix id: valid - uses: ./flutter-matrix + uses: ./matrix with: targets: '["linux", "ios", "windows"]' build-mode: "profile" @@ -107,7 +117,7 @@ jobs: - name: Resolve invalid matrix id: invalid continue-on-error: true - uses: ./flutter-matrix + uses: ./matrix with: targets: '["linux", "bogus"]' @@ -120,3 +130,10 @@ jobs: echo "Expected invalid target to fail, got outcome '$OUTCOME'" exit 1 fi + + # Runs main's actions (ci.yml references them @main), so this covers the workflow's wiring, not this checkout's actions. + test-ci: + uses: ./.github/workflows/ci.yml + with: + targets: '["linux"]' + working-directory: "tests/fixtures" diff --git a/build/action.yml b/build/action.yml index d5fe7ba..738ae85 100644 --- a/build/action.yml +++ b/build/action.yml @@ -31,19 +31,28 @@ outputs: description: "Base name of packaged artifact" value: ${{ steps.package-meta.outputs.artifact-name }} artifact-path: - description: "Path or glob of packaged artifact(s)" + description: "Glob of the packaged artifact(s), in a directory under the runner temp folder" value: ${{ steps.package-meta.outputs.artifact-path }} runs: using: "composite" steps: + - name: Resolve App Metadata + id: meta + shell: bash + working-directory: ${{ inputs.working-directory }} + env: + TARGET: ${{ inputs.target }} + BUILD_MODE: ${{ inputs.build-mode }} + run: bash "${{ github.action_path }}/scripts/resolve-metadata.sh" + - name: Resolve Flutter version id: version shell: bash working-directory: ${{ inputs.working-directory }} env: FLUTTER_VERSION_INPUT: ${{ inputs.flutter-version }} - run: bash "${{ github.action_path }}/../setup-flutter/scripts/resolve-version.sh" + run: bash "${{ github.action_path }}/../setup/scripts/resolve-version.sh" - name: Set up Flutter uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0 @@ -52,21 +61,12 @@ runs: channel: ${{ inputs.channel }} cache: ${{ inputs.cache == 'true' }} - - name: Resolve App Metadata - id: meta - shell: bash - working-directory: ${{ inputs.working-directory }} - env: - TARGET: ${{ inputs.target }} - BUILD_MODE: ${{ inputs.build-mode }} - run: bash "${{ github.action_path }}/scripts/resolve-metadata.sh" - - name: Install Linux dependencies if: inputs.target == 'linux' shell: bash run: | sudo apt-get update -qq - sudo apt-get install -y -qq libgtk-3-dev libblkid-dev liblzma-dev libglib2.0-dev + sudo apt-get install -y -qq clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev libglib2.0-dev - name: Install Dart dependencies shell: bash @@ -134,8 +134,9 @@ runs: working-directory: ${{ inputs.working-directory }} env: BUILD_MODE: ${{ inputs.build-mode }} - APP_NAME: ${{ env.APP_NAME }} - MODE_SUFFIX: ${{ env.MODE_SUFFIX }} + APP_NAME: ${{ steps.meta.outputs.app-name }} + MODE_SUFFIX: ${{ steps.meta.outputs.mode-suffix }} + DIST_DIR: ${{ steps.meta.outputs.dist-dir }} run: bash "${{ github.action_path }}/scripts/package-linux.sh" - name: Package macOS @@ -144,8 +145,9 @@ runs: working-directory: ${{ inputs.working-directory }} env: BUILD_MODE: ${{ inputs.build-mode }} - APP_NAME: ${{ env.APP_NAME }} - MODE_SUFFIX: ${{ env.MODE_SUFFIX }} + APP_NAME: ${{ steps.meta.outputs.app-name }} + MODE_SUFFIX: ${{ steps.meta.outputs.mode-suffix }} + DIST_DIR: ${{ steps.meta.outputs.dist-dir }} run: bash "${{ github.action_path }}/scripts/package-macos.sh" - name: Package Windows @@ -154,8 +156,9 @@ runs: working-directory: ${{ inputs.working-directory }} env: BUILD_MODE: ${{ inputs.build-mode }} - APP_NAME: ${{ env.APP_NAME }} - MODE_SUFFIX: ${{ env.MODE_SUFFIX }} + APP_NAME: ${{ steps.meta.outputs.app-name }} + MODE_SUFFIX: ${{ steps.meta.outputs.mode-suffix }} + DIST_DIR: ${{ steps.meta.outputs.dist-dir }} run: pwsh "${{ github.action_path }}/scripts/package-windows.ps1" - name: Package Android @@ -165,8 +168,9 @@ runs: env: TARGET: ${{ inputs.target }} BUILD_MODE: ${{ inputs.build-mode }} - APP_NAME: ${{ env.APP_NAME }} - MODE_SUFFIX: ${{ env.MODE_SUFFIX }} + APP_NAME: ${{ steps.meta.outputs.app-name }} + MODE_SUFFIX: ${{ steps.meta.outputs.mode-suffix }} + DIST_DIR: ${{ steps.meta.outputs.dist-dir }} run: bash "${{ github.action_path }}/scripts/package-android.sh" - name: Package iOS @@ -174,8 +178,9 @@ runs: shell: bash working-directory: ${{ inputs.working-directory }} env: - APP_NAME: ${{ env.APP_NAME }} - MODE_SUFFIX: ${{ env.MODE_SUFFIX }} + APP_NAME: ${{ steps.meta.outputs.app-name }} + MODE_SUFFIX: ${{ steps.meta.outputs.mode-suffix }} + DIST_DIR: ${{ steps.meta.outputs.dist-dir }} run: bash "${{ github.action_path }}/scripts/package-ios.sh" - name: Set artifact outputs @@ -184,5 +189,7 @@ runs: working-directory: ${{ inputs.working-directory }} env: TARGET: ${{ inputs.target }} - WORKING_DIRECTORY: ${{ inputs.working-directory }} + APP_NAME: ${{ steps.meta.outputs.app-name }} + MODE_SUFFIX: ${{ steps.meta.outputs.mode-suffix }} + DIST_DIR: ${{ steps.meta.outputs.dist-dir }} run: bash "${{ github.action_path }}/scripts/set-artifact-outputs.sh" diff --git a/build/scripts/package-android.sh b/build/scripts/package-android.sh index e24612f..39e62cf 100755 --- a/build/scripts/package-android.sh +++ b/build/scripts/package-android.sh @@ -5,12 +5,13 @@ BUILD_MODE="${BUILD_MODE:-release}" APP_NAME="${APP_NAME:-app}" MODE_SUFFIX="${MODE_SUFFIX:-}" -mkdir -p dist +: "${DIST_DIR:?}" +mkdir -p "$DIST_DIR" if [ "$TARGET" = "android-apk" ] && [ -d "build/app/outputs/flutter-apk" ]; then APK_FILE=$(find build/app/outputs/flutter-apk -maxdepth 1 -name "*${BUILD_MODE}*.apk" | head -n 1) if [ -n "$APK_FILE" ]; then - cp "$APK_FILE" "dist/${APP_NAME}-android${MODE_SUFFIX}.apk" + cp "$APK_FILE" "$DIST_DIR/${APP_NAME}-android${MODE_SUFFIX}.apk" fi fi @@ -20,6 +21,6 @@ if [ "$TARGET" = "android-aab" ] && [ -d "build/app/outputs/bundle" ]; then AAB_FILE=$(find build/app/outputs/bundle -name "*.aab" | head -n 1) fi if [ -n "$AAB_FILE" ]; then - cp "$AAB_FILE" "dist/${APP_NAME}-android${MODE_SUFFIX}.aab" + cp "$AAB_FILE" "$DIST_DIR/${APP_NAME}-android${MODE_SUFFIX}.aab" fi fi \ No newline at end of file diff --git a/build/scripts/package-ios.sh b/build/scripts/package-ios.sh index a592a53..7d22f9b 100755 --- a/build/scripts/package-ios.sh +++ b/build/scripts/package-ios.sh @@ -4,11 +4,12 @@ set -euo pipefail APP_NAME="${APP_NAME:-app}" MODE_SUFFIX="${MODE_SUFFIX:-}" -mkdir -p dist +: "${DIST_DIR:?}" +mkdir -p "$DIST_DIR" if [ ! -d "build/ios/iphoneos/Runner.app" ]; then echo "::error::ios Runner.app bundle not found" exit 1 fi -ditto -c -k --sequesterRsrc "build/ios/iphoneos/Runner.app" "dist/${APP_NAME}-ios-unsigned${MODE_SUFFIX}.zip" +ditto -c -k --sequesterRsrc "build/ios/iphoneos/Runner.app" "$DIST_DIR/${APP_NAME}-ios-unsigned${MODE_SUFFIX}.zip" diff --git a/build/scripts/package-linux.sh b/build/scripts/package-linux.sh index 436d2a1..7da10ea 100755 --- a/build/scripts/package-linux.sh +++ b/build/scripts/package-linux.sh @@ -5,5 +5,7 @@ BUILD_MODE="${BUILD_MODE:-release}" APP_NAME="${APP_NAME:-app}" MODE_SUFFIX="${MODE_SUFFIX:-}" -mkdir -p dist -tar -czf "dist/${APP_NAME}-linux${MODE_SUFFIX}.tar.gz" -C "build/linux/x64/${BUILD_MODE}/bundle" . +: "${DIST_DIR:?}" +mkdir -p "$DIST_DIR" +# ponytail: x64 only; arm64 runners need build/linux/arm64 once targets.json offers them. +tar -czf "$DIST_DIR/${APP_NAME}-linux${MODE_SUFFIX}.tar.gz" -C "build/linux/x64/${BUILD_MODE}/bundle" . diff --git a/build/scripts/package-macos.sh b/build/scripts/package-macos.sh index de26b02..14da994 100755 --- a/build/scripts/package-macos.sh +++ b/build/scripts/package-macos.sh @@ -5,7 +5,8 @@ APP_NAME="${APP_NAME:-app}" BUILD_MODE="${BUILD_MODE:-release}" MODE_SUFFIX="${MODE_SUFFIX:-}" -mkdir -p dist +: "${DIST_DIR:?}" +mkdir -p "$DIST_DIR" APP_PATH="" if [ -d "build/macos/Build/Products" ]; then @@ -17,4 +18,4 @@ if [ -z "$APP_PATH" ]; then exit 1 fi -ditto -c -k --sequesterRsrc "$APP_PATH" "dist/${APP_NAME}-macos${MODE_SUFFIX}.zip" +ditto -c -k --sequesterRsrc "$APP_PATH" "$DIST_DIR/${APP_NAME}-macos${MODE_SUFFIX}.zip" diff --git a/build/scripts/package-windows.ps1 b/build/scripts/package-windows.ps1 index e3f663c..0a10e7f 100644 --- a/build/scripts/package-windows.ps1 +++ b/build/scripts/package-windows.ps1 @@ -4,7 +4,14 @@ $AppName = if ($env:APP_NAME) { $env:APP_NAME } else { "app" } $BuildMode = if ($env:BUILD_MODE) { $env:BUILD_MODE } else { "release" } $ModeSuffix = if ($env:MODE_SUFFIX) { $env:MODE_SUFFIX } else { "" } -New-Item -ItemType Directory -Force -Path dist | Out-Null +$DistDir = $env:DIST_DIR +if (-not $DistDir) { + Write-Error "DIST_DIR is not set" + exit 1 +} +New-Item -ItemType Directory -Force -Path $DistDir | Out-Null + +# ponytail: x64 only; arm64 runners need build/windows/arm64 once targets.json offers them. $runnerDir = Get-ChildItem -Path "build/windows/x64/runner" -Directory | Where-Object { $_.Name -like "*$BuildMode*" } | Select-Object -First 1 if (-not $runnerDir) { @@ -12,4 +19,4 @@ if (-not $runnerDir) { exit 1 } -Compress-Archive -Path "$($runnerDir.FullName)/*" -DestinationPath "dist/${AppName}-windows${ModeSuffix}.zip" -Force +Compress-Archive -Path "$($runnerDir.FullName)/*" -DestinationPath "$DistDir/${AppName}-windows${ModeSuffix}.zip" -Force diff --git a/build/scripts/resolve-metadata.sh b/build/scripts/resolve-metadata.sh index 64b04ec..6f590a4 100755 --- a/build/scripts/resolve-metadata.sh +++ b/build/scripts/resolve-metadata.sh @@ -21,16 +21,26 @@ else MODE_SUFFIX="-${BUILD_MODE}" fi -echo "BUILD_MODE=$BUILD_MODE" >> "$GITHUB_ENV" -echo "MODE_SUFFIX=$MODE_SUFFIX" >> "$GITHUB_ENV" - if [ ! -f pubspec.yaml ]; then echo "::error::pubspec.yaml not found in $PWD" exit 1 fi -NAME=$(sed -n "s/^name:[[:space:]]*//p" pubspec.yaml | head -n 1 | tr -d '\r"'\'' ') +NAME=$(sed -n "s/^name:[[:space:]]*//p" pubspec.yaml | head -n 1 | tr -d '\r' | sed 's/[[:space:]]*#.*$//' | tr -d '"'\'' ') NAME="${NAME:-app}" +# The name ends up in file and artifact names; Dart's own rule keeps it path-safe. +if [[ ! "$NAME" =~ ^[a-z_][a-z0-9_]*$ ]]; then + echo "::error::Invalid pubspec.yaml name '$NAME': must match ^[a-z_][a-z0-9_]*\$" + exit 1 +fi + +# The action owns this directory, so a consumer's own dist/ is never packaged and repeated builds in one job don't mix. +DIST_DIR="${RUNNER_TEMP:?}/flutter-dist/${TARGET}" +rm -rf "$DIST_DIR" +mkdir -p "$DIST_DIR" -echo "APP_NAME=$NAME" >> "$GITHUB_ENV" -echo "app-name=$NAME" >> "$GITHUB_OUTPUT" +{ + echo "app-name=$NAME" + echo "mode-suffix=$MODE_SUFFIX" + echo "dist-dir=$DIST_DIR" +} >> "$GITHUB_OUTPUT" diff --git a/build/scripts/set-artifact-outputs.sh b/build/scripts/set-artifact-outputs.sh index 0c69278..46d1948 100644 --- a/build/scripts/set-artifact-outputs.sh +++ b/build/scripts/set-artifact-outputs.sh @@ -1,9 +1,10 @@ #!/usr/bin/env bash set -euo pipefail -if [ ! -d "dist" ] || [ -z "$(ls -A dist 2>/dev/null)" ]; then - echo "::error::Packaging produced no artifacts in dist/" +: "${DIST_DIR:?}" +if [ ! -d "$DIST_DIR" ] || [ -z "$(ls -A "$DIST_DIR" 2>/dev/null)" ]; then + echo "::error::Packaging produced no artifacts in $DIST_DIR" exit 1 fi echo "artifact-name=${APP_NAME}-${TARGET}${MODE_SUFFIX}" >> "$GITHUB_OUTPUT" -echo "artifact-path=${WORKING_DIRECTORY}/dist/*" >> "$GITHUB_OUTPUT" +echo "artifact-path=${DIST_DIR}/*" >> "$GITHUB_OUTPUT" diff --git a/matrix/scripts/resolve-matrix.sh b/matrix/scripts/resolve-matrix.sh index 47f8afe..ebb7c79 100644 --- a/matrix/scripts/resolve-matrix.sh +++ b/matrix/scripts/resolve-matrix.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash # Resolves the Flutter build matrix and build mode for the current event. -# Single source of truth for valid targets and runner mapping: build-flutter/targets.json +# Single source of truth for valid targets and runner mapping: build/targets.json set -euo pipefail -TARGETS_FILE="${TARGETS_FILE:-${GITHUB_ACTION_PATH}/../build-flutter/targets.json}" +TARGETS_FILE="${TARGETS_FILE:-${GITHUB_ACTION_PATH}/../build/targets.json}" TARGETS_JSON="${TARGETS_INPUT:-}" BUILD_MODE="${BUILD_MODE_INPUT:-}" @@ -28,6 +28,13 @@ if ! echo "$TARGETS_JSON" | jq -e 'type == "array" and length > 0 and all(type = exit 1 fi +# Duplicates would give two matrix jobs uploading the same artifact name, which upload-artifact rejects. +DUPLICATES=$(echo "$TARGETS_JSON" | jq -c 'group_by(.) | map(select(length > 1) | .[0])') +if [ "$DUPLICATES" != "[]" ]; then + echo "::error::Duplicate target(s): $DUPLICATES" + exit 1 +fi + case "$BUILD_MODE" in debug|profile|release) ;; *) diff --git a/tests/fixtures/test/app_test.dart b/tests/fixtures/test/app_test.dart new file mode 100644 index 0000000..1f5c1a7 --- /dev/null +++ b/tests/fixtures/test/app_test.dart @@ -0,0 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('fixture has a test for ci.yml to run', () { + expect(1 + 1, 2); + }); +} diff --git a/tests/lib.sh b/tests/lib.sh deleted file mode 100644 index b70feb7..0000000 --- a/tests/lib.sh +++ /dev/null @@ -1,118 +0,0 @@ -# shellcheck shell=bash -# Shared harness for tests/*.test.sh. Sourced, never executed. -# Must stay bash 3.2 compatible: macOS runners ship it. -# -# Each case: new_case; set up files in "$WORK"; run