GitHub Actions Workflow Architecture
Overview
The Smart Smoker v2 project uses a clean, reusable workflow architecture that eliminates redundancy and provides clear separation of concerns. Each workflow has a single responsibility and can be composed together as needed.
Current Workflow Architecture
Core Reusable Workflows
1. install.yml - Dependency Management
- Purpose: Sets up Node.js environment and installs all dependencies
- Features:
- Workspace artifact upload for reuse across jobs
- Dependency caching for faster builds
- Single source of truth for environment setup
- Used by: Called internally by
build.yml
2. build.yml - Application Builder
- Purpose: Builds applications and optionally creates Docker images
- Modes:
test: Run Jest tests onlybuild: Build applications without Docker exportbuild-and-export: Build applications and export Docker images as artifacts- Features:
- Calls
install.ymlinternally for dependencies - Matrix strategy for parallel builds
- Configurable app selection via JSON array
- Conditional Docker image export
3. publish.yml - Docker Hub Publisher
- Purpose: Publishes Docker images to Docker Hub
- Features:
- Downloads image artifacts from build jobs
- Pushes to Docker Hub with version tags
- Automatic
latesttagging for release versions - Matrix strategy for parallel publishing
Orchestrator Workflows
4. ci-tests.yml - Pull Request Validation
- Purpose: Validates code changes on pull requests
- Process:
- Run tests for all applications (calls
build.ymlwith mode="test") - Build validation (calls
build.ymlwith mode="build") - Benefits: Fast feedback, parallel execution, no redundant installs
5. release-please.yml - Release Cutter
- Purpose: Keeps the release PR (version bump + CHANGELOG) up to date on
every push to
master, and on merge tagsvX.Y.Z+ publishes the GitHub Release - Builds nothing itself — it only produces the
release: publishedevent that the two pipelines below listen for - Auth:
RUNNER_PAT, notGITHUB_TOKEN(see Release Process)
6. release.yml - Smoker Release Pipeline
- Purpose: Builds and publishes the device-side images for a release
- Process:
- Build smoker apps — smoker, device-service, electron-shell — from the tag
(calls
build.ymlwith mode="build", i.e. a compile-only validation; it exports no Docker image artifacts) - Publish those Docker images with
:latest+:vX.Y.Z(callspublish.ymlwith mode="release" andprebuild: true— publish checks out fresh source and rebuildsdist/itself, because those Dockerfiles COPY prebuilt output)
Smoker devices are not deployed to here: publishing :latest is their
deployment, applied by Watchtower on the device.
7. prod-deploy.yml - Production Pipeline
- Purpose: Turns a published Release into a running production cloud
- Process:
- Resolve + validate the version from the release tag (or dispatch input)
- Probe Docker Hub; skip the build when
:vX.Y.Zalready exists - Build backend + frontend from the release tag (calls
publish.ymlwith mode="release",prebuild: true) — no:nightlypromotion - Deploy over SSH from the proxmox runner via
scripts/deploy-cloud.sh, with health check, rollback and Discord notification — the job targets theproductionenvironment, which still gates it on a required-reviewer approval + 5-minute wait timer (removal pending) - Blocking post-deploy smoke gate on a GitHub-hosted runner
Deployment Workflows
8. device-deploy.yml - Device Deployment
- Purpose: Deploys a compose file to a smoker device (virtual or the physical Pi) over SSH, with backup, health check and automatic rollback
- Note: Only needed when the compose file changes. Image updates reach devices via Watchtower — see Physical Smoker Device
9. dev-deploy.yml / nightly.yml - Development Cloud
- Purpose: Build and deploy
:nightlyto dev-cloud on master merges - Note:
:nightlynever reaches production or the physical device
10. docs.yml - Documentation
- Purpose: Builds and deploys documentation
- Unchanged: Existing MkDocs deployment
Benefits of Current Architecture
1. Resource Efficiency
- Single
npm run bootstrapper workflow execution (no redundant installs) - Parallel builds with shared dependencies
- Efficient artifact-based image sharing
2. Maintainability
- Single source of truth for setup logic (
install.yml) - Reusable components with clear responsibilities
- Clean separation of concerns (install → build → publish → deploy)
3. Flexibility
- Easy to add new applications to build matrix
- Conditional publishing and deployment
- Composable workflows for different scenarios
4. Developer Experience
- Fast CI feedback through parallelization
- Clear workflow visualization in GitHub Actions
- Easy to debug specific stages independently
Workflow Composition Examples
Pull Request Testing
# ci-tests.yml calls:
build.yml (mode: "test") → Tests all apps
build.yml (mode: "build") → Validates builds
Production Release
# merge of the release PR → release-please.yml tags vX.Y.Z + publishes a Release
#
# release.yml (release: published) calls:
build.yml (smoker apps, mode: "build-and-export") → Creates artifacts
publish.yml → Pushes device images (`:latest` + `:vX.Y.Z`)
# devices: Watchtower picks up `:latest` on its next poll
#
# prod-deploy.yml (release: published) calls:
publish.yml (backend + frontend, mode: "release", ref: vX.Y.Z) → builds from the tag
scripts/deploy-cloud.sh over SSH → deploys prod pinned to vX.Y.Z
scripts/smoke → blocking post-deploy gate
Usage Examples
Running Tests Only
uses: ./.github/workflows/build.yml
with:
apps: '["backend", "frontend"]'
mode: "test"
ref: ${{ github.ref }}
Building and Exporting Docker Images
uses: ./.github/workflows/build.yml
with:
apps: '["smoker", "device-service"]'
mode: "build-and-export"
version: "1.0.0"
ref: "v1.0.0"
Publishing Docker Images
uses: ./.github/workflows/publish.yml
with:
images: '["smoker_image", "backend_image"]'
version: "1.0.0"
secrets: inherit
Current File Structure
.github/workflows/
├── # Core Reusable Workflows
├── install.yml # Dependency setup & workspace artifacts
├── build.yml # Application building & Docker image creation
├── publish.yml # Docker Hub publishing
├──
├── # Orchestrator Workflows
├── ci-tests.yml # PR validation & testing
├── pr-title-lint.yml # Conventional PR title (release-please input)
├── release-please.yml # Release PR → tag + GitHub Release
├── release.yml # Smoker image release pipeline
├──
├── # Deployment Workflows
├── prod-deploy.yml # Production cloud: build from tag, deploy, smoke
├── dev-deploy.yml # Dev cloud deployment (`:nightly`)
├── device-deploy.yml # Smoker device deployment (compose changes only)
└── docs.yml # Documentation deployment
Architecture Principles
- Single Responsibility: Each workflow does one thing well
- Composable: Workflows can be combined for different scenarios
- Reusable: No duplicate logic across workflows
- Testable: Each component can be tested independently
- Maintainable: Clear ownership and minimal interdependencies
Adding New Applications
To add a new application to the build pipeline:
- Add to build matrix: Include app name in the
appsJSON array - Update build.yml: Add build commands for the new app if needed
- Update Dockerfiles: Ensure proper Dockerfile exists
- Test locally: Run the workflow with the new app included
No changes needed to core workflow logic - the architecture is designed to scale.