Skip to content

refactor(ci): extract shared build setup into a composite action#1320

Open
behdadmansouri wants to merge 1 commit into
ActivityWatch:masterfrom
behdadmansouri:refactor/ci-composite-action
Open

refactor(ci): extract shared build setup into a composite action#1320
behdadmansouri wants to merge 1 commit into
ActivityWatch:masterfrom
behdadmansouri:refactor/ci-composite-action

Conversation

@behdadmansouri

Copy link
Copy Markdown

Summary

Closes #1219

PR #1313 merged the three separate workflow files (dev-release.yml, build.yml, build-tauri.yml) into a single release.yml. This follow-up addresses the remaining duplication: the build-qt and build-tauri jobs still share ~60 identical lines of setup steps each, requiring two in-sync edits for any toolchain or cache change.

This PR introduces .github/actions/setup-aw-build/action.yml — a local composite action — and replaces the duplicated steps in both jobs with a single uses call.

What the composite action handles (genuinely shared):

  • actions/setup-python
  • actions/setup-node (controlled by skip-webui input)
  • dtolnay/rust-toolchain (controlled by skip-rust input)
  • actions/cache for node_modules (paths configurable via node-cache-paths input)
  • actions/cache for Cargo target directory (paths configurable via cargo-cache-paths input)
  • pip3 install poetry + choco install innosetup on Windows
  • scripts/package/getversion.sh version detection + $GITHUB_ENV export

What stays in each job (intentionally not shared):

  • APT system packages — Qt and Tauri require completely different system libraries (X11/Qt5 vs GTK3/WebKit2GTK); combining them would install unneeded packages on every run
  • Build, test, and package steps — command flags and retry wrappers differ

No behaviour change. CI output is identical to before; only the source of the steps moves.

Test plan

  • CI passes on a push to a feature branch (build-qt and build-tauri both run successfully)
  • The composite action's version-with-v output is visible in both build job logs
  • Cache hit rate is unchanged from before

🤖 Implemented with Claude Code — AI assistance disclosed per contribution guidelines.

The build-qt and build-tauri jobs in release.yml duplicated ~60 lines of
identical setup steps each (Python, Node, Rust, caches, poetry/choco,
version detection). A future edit to any of those steps required two
in-sync changes and was easy to miss.

This PR introduces .github/actions/setup-aw-build/action.yml — a local
composite action — and replaces the duplicated steps in both jobs with a
single `uses: ./.github/actions/setup-aw-build` call.

What the action covers (shared between both jobs):
  - actions/setup-python
  - actions/setup-node (skip-webui input)
  - dtolnay/rust-toolchain (skip-rust input)
  - actions/cache for node_modules (configurable paths via node-cache-paths)
  - actions/cache for cargo target (configurable paths via cargo-cache-paths)
  - pip3 install poetry + choco install innosetup on Windows
  - scripts/package/getversion.sh version detection

What stays in each job (intentionally not shared):
  - APT system packages — Qt and Tauri require completely different
    system libraries (X11/Qt vs GTK/WebKit)
  - Build, test, and package steps — semantics differ between the two

No behaviour change. CI output is identical; only the source of the
steps moves.

Closes ActivityWatch#1219

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR extracts ~60 lines of duplicated build-environment setup from the build-qt and build-tauri jobs in release.yml into a new local composite action at .github/actions/setup-aw-build/action.yml. The composite action accepts inputs for Python/Node/Rust versions, skip flags, and cache paths, so each job can configure it without sharing platform-specific APT or build steps.

  • New composite action (setup-aw-build): handles setup-python, setup-node, rust-toolchain, actions/cache for both node_modules and Cargo target directories, poetry + choco innosetup install, and version detection via getversion.sh.
  • release.yml replaces two identical ~30-step setup blocks with a single uses: ./.github/actions/setup-aw-build call in each job, passing job-specific cache paths as inputs; APT system packages and build/test/package steps are intentionally kept per-job.

Confidence Score: 3/5

Safe to merge functionally — builds complete correctly — but the cache key namespace changes from the original, so all warm caches will be missed on first run after landing.

The composite action substitutes runner.os (e.g. Linux) for the old matrix.os (e.g. ubuntu-24.04) in every cache key. These are different strings, so the entire node_modules and Cargo cache history is abandoned on merge. The PR's own test plan asserts 'Cache hit rate is unchanged from before', which this would fail.

.github/actions/setup-aw-build/action.yml — specifically the key: and restore-keys: fields in the cache steps at lines 69–82.

Important Files Changed

Filename Overview
.github/actions/setup-aw-build/action.yml New composite action correctly centralises toolchain/cache setup, but its cache keys switched from matrix.os to runner.os, silently changing the cache namespace and causing a cold-cache run after merge.
.github/workflows/release.yml Duplicated setup blocks in build-qt and build-tauri correctly replaced with a single composite action call; APT and build/test/package steps intentionally kept per-job. No logic changes besides the cache key regression in the shared action.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    RYml[release.yml]

    subgraph qt[build-qt job]
        BQ1[checkout]
        BQ2[Set RELEASE / VERSION_TAG]
        BQ3[uses setup-aw-build]
        BQ4[Install APT deps Qt-specific]
        BQ5[Build / Test / Package]
        BQ1 --> BQ2 --> BQ3 --> BQ4 --> BQ5
    end

    subgraph tauri[build-tauri job]
        BT1[checkout]
        BT2[Set RELEASE / VERSION_TAG / TAURI_BUILD]
        BT3[uses setup-aw-build]
        BT4[Install APT deps Tauri-specific]
        BT5[Build / Test / Package]
        BT1 --> BT2 --> BT3 --> BT4 --> BT5
    end

    subgraph composite[setup-aw-build composite action]
        CA1[setup-python]
        CA2[setup-node if not skip-webui]
        CA3[rust-toolchain if not skip-rust]
        CA4[cache node_modules if not skip-webui]
        CA5[cache cargo build if not skip-rust]
        CA6[pip install poetry + choco innosetup on Windows]
        CA7[getversion.sh outputs VERSION_WITH_V]
        CA1 --> CA2 --> CA3 --> CA4 --> CA5 --> CA6 --> CA7
    end

    RYml --> qt
    RYml --> tauri
    BQ3 --> CA1
    BT3 --> CA1
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    RYml[release.yml]

    subgraph qt[build-qt job]
        BQ1[checkout]
        BQ2[Set RELEASE / VERSION_TAG]
        BQ3[uses setup-aw-build]
        BQ4[Install APT deps Qt-specific]
        BQ5[Build / Test / Package]
        BQ1 --> BQ2 --> BQ3 --> BQ4 --> BQ5
    end

    subgraph tauri[build-tauri job]
        BT1[checkout]
        BT2[Set RELEASE / VERSION_TAG / TAURI_BUILD]
        BT3[uses setup-aw-build]
        BT4[Install APT deps Tauri-specific]
        BT5[Build / Test / Package]
        BT1 --> BT2 --> BT3 --> BT4 --> BT5
    end

    subgraph composite[setup-aw-build composite action]
        CA1[setup-python]
        CA2[setup-node if not skip-webui]
        CA3[rust-toolchain if not skip-rust]
        CA4[cache node_modules if not skip-webui]
        CA5[cache cargo build if not skip-rust]
        CA6[pip install poetry + choco innosetup on Windows]
        CA7[getversion.sh outputs VERSION_WITH_V]
        CA1 --> CA2 --> CA3 --> CA4 --> CA5 --> CA6 --> CA7
    end

    RYml --> qt
    RYml --> tauri
    BQ3 --> CA1
    BT3 --> CA1
Loading

Reviews (1): Last reviewed commit: "refactor(ci): extract shared build setup..." | Re-trigger Greptile

Comment on lines +69 to +82
key: ${{ runner.os }}-node_modules-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node_modules-

- name: Cache cargo build
if: inputs.skip-rust != 'true'
uses: actions/cache@v5
env:
cache-name: cargo-build-target
with:
path: ${{ inputs.cargo-cache-paths }}
key: ${{ runner.os }}-${{ env.cache-name }}-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-${{ steps.toolchain.outputs.rustc_hash }}-

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Cache key regression: matrix.osrunner.os changes key namespace

The original steps keyed caches off ${{ matrix.os }} (e.g. ubuntu-24.04, windows-latest, macos-latest), but the composite action uses ${{ runner.os }} (e.g. Linux, Windows, macOS). These are different strings, so every existing cached entry will be missed on the first run after this PR lands. This directly contradicts the PR test plan item "Cache hit rate is unchanged from before." The fix is to thread matrix.os through as an input (e.g. runner-os) and use that in both key: and restore-keys:.

Comment on lines +57 to +62
- name: Set up Rust
if: inputs.skip-rust != 'true'
uses: dtolnay/rust-toolchain@master
id: toolchain
with:
toolchain: stable

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 dtolnay/rust-toolchain@master pins to a moving target

Using @master means the action can silently pull in breaking changes or a different toolchain resolver on every run. The original code in release.yml already used @master, so this is a pre-existing pattern, but consolidating into a shared composite action makes the blast radius larger — a single @master change now affects both build-qt and build-tauri. Consider pinning to a specific commit SHA or tag for reproducibility.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor(ci): unify build + release workflows into a single release.yml pipeline

1 participant