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/*" 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/.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/ 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 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. diff --git a/README.md b/README.md index 1afff76..9650355 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,37 @@ -# Shared actions +# dotactions/flutter -## Versioning +Composite actions and reusable workflows to set up, test, build and package Flutter apps for Linux, macOS, Windows, Android and iOS. -Pin a **full commit SHA** of a release tag (most secure, Semgrep standard), with the version in a comment: +## Versioning -```yaml -uses: dotsem/actions/.github/workflows/flutter-ci.yml@<40-char-sha> # v0.5.0 -``` +This repo is on **0.x** until release artifacts can be signed (see [Limitations](#limitations)). During `0.x`, a **minor** release may contain breaking changes. -Or pin a **minor** tag (convenient, receives patch fixes): +Pin the **full commit SHA** of a release tag, with the version in a comment, or a floating minor tag: ```yaml -uses: dotsem/actions/.github/workflows/flutter-ci.yml@v0.5 +uses: dotactions/flutter/.github/workflows/ci.yml@<40-char-sha> # v0.1.0 +uses: dotactions/flutter/.github/workflows/ci.yml@v0.1 ``` -Resolve a SHA with `git ls-remote https://github.com/dotsem/actions 'refs/tags/v0.5.0^{}'`. Only SHAs of **release tags** pin the whole tree; a SHA from `main` still contains `@main` self-references. +Resolve a SHA with `git ls-remote https://github.com/dotactions/flutter 'refs/tags/v0.1.0^{}'`. Only SHAs of **release tags** pin the whole tree; a SHA from `main` still contains `@main` self-references. + +| Ref | Moves on | Use | +| :-- | :-- | :-- | +| `@` | never | **Recommended**: immutable, full reproducibility | +| `@v0.1` | patch releases of `0.1` | **Recommended**: bug fixes, no breaking changes | +| `@v0.1.0` | never (tag can be moved) | Readable, but tags are mutable | +| `@v0` | every `0.x` release | Not recommended: during `0.x`, minor releases may break you | -| Ref | Moves on | Use | -| :-------- | :----------------------- | :------------------------------------------------------------------------ | -| `@` | never | **Recommended** (recommended by SemGrep): immutable, full reproducibility | -| `@v0.5` | patch releases of `0.5` | **Recommended**: bug fixes, no breaking changes | -| `@v0.5.0` | never (tag can be moved) | Readable, but tags are mutable | -| `@v0` | every `0.x` release | Not recommended: during `0.x`, minor releases may break you | +Release notes: [GitHub Releases](https://github.com/dotactions/flutter/releases). -Every release tag pins the **whole tree**. Reusable workflows and actions that call other actions in this repo get those references rewritten to the same release tag, so `flutter-ci.yml@v0.5.0` always runs `build-flutter@v0.5.0`. On `main`, self-references are `@main`. +## Limitations -Release notes: [GitHub Releases](https://github.com/dotsem/actions/releases). +- **Artifacts are unsigned.** iOS builds use `--no-codesign` and can't be installed. macOS apps are neither signed nor notarized, so Gatekeeper blocks them. Android release builds use your project's own signing config, which in the Flutter template is the debug key, so the Play Store rejects them. Windows executables aren't signed. v1.0.0 follows once signing is supported. +- **Desktop builds are x64 only** (`ubuntu-latest`, `windows-latest`); arm64 runners aren't supported yet. ## Permissions -Examples assume a **public** repository. In a **private** repository, `actions/checkout` needs `contents: read`, so every job using these actions (or `flutter-ci.yml`) must grant it: +Examples assume a **public** repository. In a **private** repository, `actions/checkout` needs `contents: read`, so every job using these actions (or `ci.yml`) must grant it: ```yaml permissions: @@ -40,99 +42,7 @@ Reusable workflows can only narrow the caller's token permissions, never widen t ## Actions -### Setup Node & pnpm (`setup-node`) - -Configures PNPM, sets up the Node.js runtime, restores cached dependencies, and runs `pnpm install --frozen-lockfile`. - -> **Prerequisite**: Run `actions/checkout` before calling this action. - -#### Inputs - -| Name | Description | Required | Default | -| :---------------------- | :------------------------------------------------------------------------------------------ | :------- | :------ | -| `node-version` | Target Node.js version | No | `"24"` | -| `pnpm-version` | Specific PNPM version (empty string reads from `package.json`) | No | `""` | -| `working-directory` | Directory containing the target `package.json` | No | `"."` | -| `cache-dependency-path` | Explicit lockfile path for cache hashing (defaults to `/pnpm-lock.yaml`) | No | `""` | - -#### Usage - -```yaml -- uses: dotsem/actions/setup-node@v0.5 - with: - node-version: "24" - working-directory: "frontend" -``` - -### Build Node (`build-node`) - -Runs a production build command using PNPM in the specified directory. - -#### Inputs - -| Name | Description | Required | Default | -| :------------------ | :-------------------------------------------------------- | :------- | :-------- | -| `working-directory` | Directory to execute the build command in | No | `"."` | -| `script` | Lifecycle script defined in package.json | No | `"build"` | -| `args` | Additional CLI flags passed to the script (e.g. --filter) | No | `""` | - -#### Usage - -```yaml -- uses: dotsem/actions/build-node@v0.5 - with: - working-directory: "frontend" - script: "build" - args: "--filter=web" -``` - -### Security Report (`security-report`) - -Parses SARIF output, appends a detailed markdown summary with badge to the GitHub Actions Job Summary, upserts a sticky PR comment, and manages automated issue tracking. - -#### Permissions Required - -```yaml -permissions: - contents: read - pull-requests: write # for sticky PR comments - issues: write # for automated issue management -``` - -#### Inputs - -| Name | Description | Required | Default | -| :----------------- | :---------------------------------------------------- | :------- | :--------------------------------------------------- | -| `sarif-file` | Path to SARIF output file | Yes | - | -| `title` | Header title in report | No | `"Security Scan Report"` | -| `comment-tag` | Identifier tag for sticky PR comments & issue markers | No | `"security-report"` | -| `badge-label` | Label text for shields.io badge | No | `"Security"` | -| `github-token` | Token with PR and issues write permissions | No | `${{ github.token }}` | -| `fail-on-findings` | Fail step if findings are detected | No | `"false"` | -| `manage-issue` | Create/update/close tracked GitHub issue | No | `"false"` | -| `issue-title` | Title of automated GitHub issue | No | `"[Security Audit] Active Vulnerabilities Detected"` | - -#### Outputs - -| Name | Description | -| :--------------- | :----------------------------------------------- | -| `total-findings` | Total number of non-suppressed findings detected | -| `report-file` | Path to the generated Markdown report file | - -#### Usage - -```yaml -- name: Report Trivy Results - uses: dotsem/actions/security-report@v0.5 - if: always() && hashFiles('trivy.sarif') != '' - with: - title: "Trivy Vulnerability Report" - sarif-file: "trivy.sarif" - comment-tag: "trivy" - fail-on-findings: "true" -``` - -### Build Flutter (`build-flutter`) +### Build Flutter (`build`) Sets up Flutter, automatically resolves app metadata from `pubspec.yaml`, installs target dependencies, builds binaries in release, debug, or profile mode, and packages artifacts for Linux, macOS, Windows, Android (android-apk/android-aab), or iOS. @@ -162,18 +72,18 @@ Sets up Flutter, automatically resolves app metadata from `pubspec.yaml`, instal | Name | Description | | :-------------- | :-------------------------------------------- | | `artifact-name` | Base name for the generated artifact | -| `artifact-path` | Path glob pointing to the packaged archive(s) | +| `artifact-path` | Glob of the packaged archive(s), in a directory under the runner temp folder that the action owns | #### Usage ```yaml - name: Build Flutter App - uses: dotsem/actions/build-flutter@v0.5 + uses: dotactions/flutter/build@v0.1 with: target: "linux" ``` -### Setup Flutter (`setup-flutter`) +### Setup Flutter (`setup`) Installs the Flutter SDK and runs `flutter pub get`. @@ -199,13 +109,13 @@ Installs the Flutter SDK and runs `flutter pub get`. #### Usage ```yaml -- uses: dotsem/actions/setup-flutter@v0.5 +- uses: dotactions/flutter/setup@v0.1 - run: flutter analyze ``` -### Flutter Matrix (`flutter-matrix`) +### Flutter Matrix (`matrix`) -Validates Flutter targets and resolves a `strategy.matrix` (target + runner OS) and build mode for the current event. Used internally by `flutter-ci.yml` and `flutter-cd.yml`. The list of valid targets and their runners is defined in [`build-flutter/targets.json`](build-flutter/targets.json). +Validates Flutter targets and resolves a `strategy.matrix` (target + runner OS) and build mode for the current event. Used internally by `ci.yml` and `cd.yml`. The list of valid targets and their runners is defined in [`build/targets.json`](build/targets.json). #### Inputs @@ -218,7 +128,7 @@ Validates Flutter targets and resolves a `strategy.matrix` (target + runner OS) | `pr-build-mode` | Build mode on `pull_request` (falls back to `"debug"`) | No | `""` | | `push-build-mode` | Build mode on push / other events (falls back to `"release"`) | No | `""` | -_Provide either `targets` or both `pr-targets` and `push-targets`._ +_Provide either `targets` or both `pr-targets` and `push-targets`. Duplicate targets are rejected._ #### Outputs @@ -237,7 +147,7 @@ jobs: matrix: ${{ steps.m.outputs.matrix }} steps: - id: m - uses: dotsem/actions/flutter-matrix@v0.5 + uses: dotactions/flutter/matrix@v0.1 with: targets: '["linux", "windows"]' build: @@ -248,81 +158,16 @@ jobs: include: ${{ fromJson(needs.matrix.outputs.matrix) }} steps: - uses: actions/checkout@v7 - - uses: dotsem/actions/build-flutter@v0.5 + - uses: dotactions/flutter/build@v0.1 with: target: ${{ matrix.target }} ``` ## Workflows -### Trivy Security Audit (`trivy-audit.yml`) - -Runs Trivy vulnerability and misconfiguration scanning on the caller repository, parses findings, publishes sticky PR comments / job summaries via `security-report`, and optionally uploads SARIF artifacts. - -#### Permissions Required - -```yaml -permissions: - contents: read - issues: write # PR comments + issue tracking - pull-requests: write # optional: only if you replace the issues API with the pulls API -``` - -#### Inputs - -| Name | Description | Required | Default | -| :----------------- | :----------------------------------------- | :------- | :----------------------- | -| `scan-type` | Trivy scan target type (e.g. `fs`) | No | `"fs"` | -| `severity` | Comma-separated severity levels to include | No | `"CRITICAL,HIGH,MEDIUM"` | -| `fail-on-findings` | Fail the workflow if findings are detected | No | `false` | -| `upload-artifact` | Upload SARIF scan report as a job artifact | No | `true` | -| `manage-issue` | Create/update/close tracked GitHub issue | No | `false` | - -#### Secrets - -| Name | Description | Required | -| :------ | :----------------------------------------------------------- | :------- | -| `token` | GitHub token with pull-requests and issues write permissions | No | - -#### Usage - -```yaml -# .github/workflows/security.yml -name: Security Audit - -on: - push: - branches: [main] - paths-ignore: - - "**.md" - pull_request: - branches: [main] - paths-ignore: - - "**.md" - schedule: - - cron: "0 4 * * 4" # weekly Thursday at 04:00 UTC - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} - -jobs: - audit: - uses: dotsem/actions/.github/workflows/trivy-audit.yml@v0.5 - permissions: - contents: read - pull-requests: write - issues: write - with: - severity: "CRITICAL,HIGH" - fail-on-findings: true - manage-issue: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} -``` - -### Flutter CI (`flutter-ci.yml`) +### Flutter CI (`ci.yml`) -Runs Flutter static analysis, unit tests, and cross-platform compilation across a specified list of targets. +Runs Flutter static analysis, unit tests, and cross-platform compilation across a specified list of targets. Builds start only after analysis and tests pass. #### Permissions Required @@ -333,13 +178,13 @@ permissions: #### Flutter Version -unless `flutter-version` is passed, the SDK version is read from `environment.flutter` in `/pubspec.yaml`. An exact version is recommended; a range (`^3.47.1`, `">=3.47.1 <4.0.0"`) installs its lower bound: +Unless `flutter-version` is passed, the SDK version is read from `environment.flutter` in `/pubspec.yaml`. An exact version is recommended; a range (`^3.47.1`, `">=3.47.1 <4.0.0"`) installs its lower bound: - ```yaml - environment: - sdk: ^3.13.1 - flutter: 3.47.1 - ``` +```yaml +environment: + sdk: ^3.13.1 + flutter: 3.47.1 +``` #### Inputs @@ -355,9 +200,9 @@ unless `flutter-version` is passed, the SDK version is read from `environment.fl | `push-build-mode` | Build mode on push/merge (defaults to `"release"`) | No | `""`** | | `concurrency-tests` | Number of concurrent test runners | No | `4` | -*_You must provide either `targets` or both `pr-targets` and `push-targets`._ +\* Provide either `targets` or both `pr-targets` and `push-targets`. -** _If none are provided. PR build-mode is `"debug"`, push build-mode is `"release"`._ +\*\* If none are provided, PR build mode is `"debug"` and push build mode is `"release"`. #### Usage @@ -379,7 +224,7 @@ concurrency: jobs: ci: - uses: dotsem/actions/.github/workflows/flutter-ci.yml@v0.5 + uses: dotactions/flutter/.github/workflows/ci.yml@v0.1 with: pr-targets: '["android-apk"]' push-targets: '["android-aab", "windows"]' @@ -392,7 +237,7 @@ jobs: # build-mode: 'release' ``` -### Flutter CD (`flutter-cd.yml`) +### Flutter CD (`cd.yml`) Builds release packages across specified platforms and uploads them directly to a GitHub release. @@ -405,20 +250,20 @@ permissions: #### Flutter Version -unless `flutter-version` is passed, the SDK version is read from `environment.flutter` in `/pubspec.yaml`. An exact version is recommended; a range (`^3.47.1`, `">=3.47.1 <4.0.0"`) installs its lower bound: +Unless `flutter-version` is passed, the SDK version is read from `environment.flutter` in `/pubspec.yaml`. An exact version is recommended; a range (`^3.47.1`, `">=3.47.1 <4.0.0"`) installs its lower bound: - ```yaml - environment: - sdk: ^3.13.1 - flutter: 3.47.1 - ``` +```yaml +environment: + sdk: ^3.13.1 + flutter: 3.47.1 +``` #### Inputs | Name | Description | Required | Default | | :------------------ | :-------------------------------------------------------------------------- | :------- | :------ | | `targets` | JSON array of targets to build (e.g. `'["linux", "windows"]'`) | Yes | - | -| `tag` | Target release tag (defaults to event tag or ref) | No | `""` | +| `tag` | Target release tag (required unless triggered by a release or tag push) | No | `""` | | `flutter-version` | Flutter SDK version (empty reads `environment.flutter` from `pubspec.yaml`) | No | `""` | | `working-directory` | Directory containing `pubspec.yaml` | No | `"."` | @@ -449,7 +294,7 @@ concurrency: jobs: cd: - uses: dotsem/actions/.github/workflows/flutter-cd.yml@v0.5 + uses: dotactions/flutter/.github/workflows/cd.yml@v0.1 permissions: contents: write with: @@ -459,17 +304,6 @@ jobs: ## Releasing (maintainers) -1. Merge to `main` with conventional-commit PR titles; these become the release notes. -2. Run the **Release** workflow (`internal-release.yml`) from `main` with a version, e.g. `0.5.0` or `0.5.0-rc.1`. -3. The workflow: - - rewrites every `dotsem/actions/...@main` self-reference to `@v` in a detached release commit - - tags it `v` and creates a GitHub release with generated notes - - moves `v0` and `v0.` to the new tag (skipped for pre-releases) - -Authenticates as a GitHub App (short-lived token, scoped to this repo), because `GITHUB_TOKEN` can't push commits that modify `.github/workflows/`. Requires: - -- repository variable `RELEASE_APP_CLIENT_ID`: the app's Client ID -- repository secret `RELEASE_APP_PRIVATE_KEY`: the app's private key -- app repository permissions **Contents: read & write** and **Workflows: read & write**, installed on this repo only +Merge to `main` with conventional-commit PR titles (they become the release notes), then run the **Release** workflow (`internal-release.yml`) from `main` with a version, e.g. `1.2.0` or `1.2.0-rc.1`. It pins self-references to the release tag, creates the GitHub release and moves the floating tags; see [dotactions/tooling](https://github.com/dotactions/tooling). -Bump the **minor** version for breaking changes and the **patch** version for everything else. +Bump the **minor** version for breaking changes and the **patch** version for everything else (`0.x`). 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/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 - ---- 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