|
| 1 | +# actions-hm |
| 2 | + |
| 3 | +[](https://github.com/harmont-dev/actions-hm/actions) |
| 4 | +[](https://github.com/harmont-dev/actions-hm/releases) |
| 5 | +[](https://github.com/marketplace/actions/harmont) |
| 6 | + |
| 7 | +Run [harmont](https://harmont.dev) pipelines in GitHub Actions. One step. Automatic Docker image caching via your container registry. |
| 8 | + |
| 9 | +```yaml |
| 10 | +- uses: harmont-dev/actions-hm@v1 |
| 11 | + with: |
| 12 | + pipeline: ci |
| 13 | +``` |
| 14 | +
|
| 15 | +That's it. This installs `hm`, pulls cached Docker images from GHCR, runs your pipeline, and pushes updated images back — with automatic cleanup of stale cache entries. |
| 16 | + |
| 17 | +## Why |
| 18 | + |
| 19 | +You already define your CI with harmont. This action lets you run it on GitHub Actions without boilerplate: |
| 20 | + |
| 21 | +- **Zero config caching** — Docker images cached in GHCR with native layer deduplication |
| 22 | +- **One step** — no separate setup, login, cache-restore, cache-save dance |
| 23 | +- **Fast repeat runs** — `hm` binary cached between runs, images pulled only when changed |
| 24 | +- **Auto cleanup** — stale registry images pruned automatically (configurable retention) |
| 25 | +- **Granular control** — use sub-actions individually when you need custom steps between them |
| 26 | + |
| 27 | +## Usage |
| 28 | + |
| 29 | +### Minimal (all-in-one) |
| 30 | + |
| 31 | +```yaml |
| 32 | +name: CI |
| 33 | +
|
| 34 | +on: [push, pull_request] |
| 35 | +
|
| 36 | +permissions: |
| 37 | + contents: read |
| 38 | + packages: write |
| 39 | +
|
| 40 | +jobs: |
| 41 | + ci: |
| 42 | + runs-on: ubuntu-latest |
| 43 | + steps: |
| 44 | + - uses: actions/checkout@v4 |
| 45 | + - uses: harmont-dev/actions-hm@v1 |
| 46 | + with: |
| 47 | + pipeline: ci |
| 48 | +``` |
| 49 | + |
| 50 | +### Multiple pipelines |
| 51 | + |
| 52 | +```yaml |
| 53 | +jobs: |
| 54 | + lint: |
| 55 | + runs-on: ubuntu-latest |
| 56 | + steps: |
| 57 | + - uses: actions/checkout@v4 |
| 58 | + - uses: harmont-dev/actions-hm@v1 |
| 59 | + with: |
| 60 | + pipeline: lint |
| 61 | +
|
| 62 | + test: |
| 63 | + runs-on: ubuntu-latest |
| 64 | + steps: |
| 65 | + - uses: actions/checkout@v4 |
| 66 | + - uses: harmont-dev/actions-hm@v1 |
| 67 | + with: |
| 68 | + pipeline: test |
| 69 | + parallelism: 4 |
| 70 | +``` |
| 71 | + |
| 72 | +### Granular sub-actions |
| 73 | + |
| 74 | +For workflows that need custom steps between setup, cache, and run: |
| 75 | + |
| 76 | +```yaml |
| 77 | +jobs: |
| 78 | + ci: |
| 79 | + runs-on: ubuntu-latest |
| 80 | + permissions: |
| 81 | + contents: read |
| 82 | + packages: write |
| 83 | + steps: |
| 84 | + - uses: actions/checkout@v4 |
| 85 | +
|
| 86 | + - uses: harmont-dev/actions-hm/setup@v1 |
| 87 | +
|
| 88 | + - uses: harmont-dev/actions-hm/cache-restore@v1 |
| 89 | +
|
| 90 | + - run: | |
| 91 | + echo "Custom setup between cache restore and pipeline run" |
| 92 | + hm run ci |
| 93 | +
|
| 94 | + - uses: harmont-dev/actions-hm/cache-save@v1 |
| 95 | + if: always() |
| 96 | +``` |
| 97 | + |
| 98 | +### Pin to specific version |
| 99 | + |
| 100 | +```yaml |
| 101 | +- uses: harmont-dev/actions-hm@v1 |
| 102 | + with: |
| 103 | + version: 0.5.0 |
| 104 | +``` |
| 105 | + |
| 106 | +## Inputs |
| 107 | + |
| 108 | +| Input | Default | Description | |
| 109 | +|-------|---------|-------------| |
| 110 | +| `pipeline` | *(auto)* | Pipeline slug to run. Omit if repo has only one pipeline. | |
| 111 | +| `version` | `latest` | `hm` CLI version (`latest` or semver like `0.5.0`) | |
| 112 | +| `working-directory` | `.` | Path to repo root where `.harmont/` lives | |
| 113 | +| `parallelism` | *(cpu count)* | Max concurrent pipeline chains | |
| 114 | +| `cache` | `true` | Enable Docker image caching | |
| 115 | +| `cache-registry` | `ghcr.io` | Container registry for image caching | |
| 116 | +| `cache-registry-prefix` | *(auto)* | Registry path prefix. Default: `ghcr.io/<owner>/<repo>/harmont-cache` | |
| 117 | +| `cache-cleanup` | `true` | Delete stale images from registry after save | |
| 118 | +| `cache-cleanup-keep` | `2` | Number of old image versions to keep per step | |
| 119 | +| `extra-args` | | Additional arguments passed to `hm run` | |
| 120 | +| `token` | `github.token` | GitHub token (needs `packages:write`, `packages:delete` for cleanup) | |
| 121 | + |
| 122 | +## Outputs |
| 123 | + |
| 124 | +| Output | Description | |
| 125 | +|--------|-------------| |
| 126 | +| `hm-version` | Installed `hm` CLI version | |
| 127 | + |
| 128 | +## Sub-actions |
| 129 | + |
| 130 | +| Action | Purpose | |
| 131 | +|--------|---------| |
| 132 | +| `harmont-dev/actions-hm/setup@v1` | Install `hm` binary (cached between runs) | |
| 133 | +| `harmont-dev/actions-hm/cache-restore@v1` | Pull cached Docker images from registry | |
| 134 | +| `harmont-dev/actions-hm/cache-save@v1` | Push Docker images to registry + cleanup | |
| 135 | + |
| 136 | +## How caching works |
| 137 | + |
| 138 | +``` |
| 139 | +┌─────────────────────────────────────────────────────────────┐ |
| 140 | +│ GitHub Actions Runner │ |
| 141 | +│ │ |
| 142 | +│ 1. Pull manifest:latest from GHCR │ |
| 143 | +│ 2. Pull each step image (layer dedup = fast) │ |
| 144 | +│ 3. Re-tag as harmont-local/* so hm recognizes them │ |
| 145 | +│ 4. hm run ci (uses cached images, skips rebuilds) │ |
| 146 | +│ 5. Push changed images back to GHCR │ |
| 147 | +│ 6. Prune images older than cleanup-keep │ |
| 148 | +│ │ |
| 149 | +│ Images stored at: │ |
| 150 | +│ ghcr.io/<owner>/<repo>/harmont-cache/<step>:<hash> │ |
| 151 | +└─────────────────────────────────────────────────────────────┘ |
| 152 | +``` |
| 153 | +
|
| 154 | +**Why GHCR instead of `actions/cache`?** |
| 155 | +
|
| 156 | +- No 10 GB size limit (GHCR storage is unlimited for public repos) |
| 157 | +- Native Docker layer deduplication — shared base images stored once |
| 158 | +- Per-image granularity — only changed images push/pull |
| 159 | +- Faster for large images than tar/untar through GHA cache |
| 160 | +
|
| 161 | +## Permissions |
| 162 | +
|
| 163 | +The action needs `packages:write` on the `GITHUB_TOKEN` to push/pull cache images. For cleanup, it also needs `packages:delete`. |
| 164 | +
|
| 165 | +```yaml |
| 166 | +permissions: |
| 167 | + contents: read |
| 168 | + packages: write |
| 169 | +``` |
| 170 | + |
| 171 | +> **Note:** `packages:delete` is included in `packages:write` for tokens with full `packages` scope. If using a fine-grained PAT, ensure both are granted. |
| 172 | +
|
| 173 | +## Migrating from raw workflow steps |
| 174 | + |
| 175 | +If you currently have a manual harmont setup in your workflow: |
| 176 | + |
| 177 | +<details> |
| 178 | +<summary>Before (manual setup)</summary> |
| 179 | + |
| 180 | +```yaml |
| 181 | +steps: |
| 182 | + - uses: actions/checkout@v4 |
| 183 | + - uses: dtolnay/rust-toolchain@stable |
| 184 | + - uses: Swatinem/rust-cache@v2 |
| 185 | + - run: cargo build -p harmont-cli |
| 186 | + - uses: actions/cache/restore@v4 |
| 187 | + with: |
| 188 | + path: .harmont-cache/ |
| 189 | + key: harmont-v1-will-never-match |
| 190 | + restore-keys: harmont-v1- |
| 191 | + - run: ./target/debug/hm cache restore .harmont-cache/ |
| 192 | + - run: ./target/debug/hm run ci |
| 193 | + env: |
| 194 | + HM_NONINTERACTIVE: '1' |
| 195 | + - run: | |
| 196 | + hash=$(./target/debug/hm cache save .harmont-cache/) |
| 197 | + echo "key=harmont-v1-${hash}" >> "$GITHUB_OUTPUT" |
| 198 | + id: cache-manifest |
| 199 | + if: always() |
| 200 | + - uses: actions/cache/save@v4 |
| 201 | + if: always() |
| 202 | + with: |
| 203 | + path: .harmont-cache/ |
| 204 | + key: ${{ steps.cache-manifest.outputs.key }} |
| 205 | +``` |
| 206 | +
|
| 207 | +</details> |
| 208 | +
|
| 209 | +<details> |
| 210 | +<summary>After (this action)</summary> |
| 211 | +
|
| 212 | +```yaml |
| 213 | +steps: |
| 214 | + - uses: actions/checkout@v4 |
| 215 | + - uses: harmont-dev/actions-hm@v1 |
| 216 | + with: |
| 217 | + pipeline: ci |
| 218 | +``` |
| 219 | +
|
| 220 | +</details> |
| 221 | +
|
| 222 | +## FAQ |
| 223 | +
|
| 224 | +### Do I need Docker on the runner? |
| 225 | +
|
| 226 | +Yes. Harmont runs pipeline steps in Docker containers. Use `runs-on: ubuntu-latest` (Docker is pre-installed). |
| 227 | + |
| 228 | +### What about macOS / Windows runners? |
| 229 | + |
| 230 | +macOS runners have Docker available via colima/lima. Windows runners are not currently supported (harmont requires Linux containers). |
| 231 | + |
| 232 | +### Can I use a private registry instead of GHCR? |
| 233 | + |
| 234 | +Yes. Set `cache-registry` to your registry hostname and provide a token with push/pull access: |
| 235 | + |
| 236 | +```yaml |
| 237 | +- uses: harmont-dev/actions-hm@v1 |
| 238 | + with: |
| 239 | + pipeline: ci |
| 240 | + cache-registry: registry.example.com |
| 241 | + token: ${{ secrets.REGISTRY_TOKEN }} |
| 242 | +``` |
| 243 | + |
| 244 | +### How do I disable caching entirely? |
| 245 | + |
| 246 | +```yaml |
| 247 | +- uses: harmont-dev/actions-hm@v1 |
| 248 | + with: |
| 249 | + pipeline: ci |
| 250 | + cache: 'false' |
| 251 | +``` |
| 252 | + |
| 253 | +### How do I force a clean cache rebuild? |
| 254 | + |
| 255 | +Delete the `harmont-cache` packages from your repo's GitHub Packages, or change `cache-registry-prefix` to a new path. |
| 256 | + |
| 257 | +### The first run is slow — is that expected? |
| 258 | + |
| 259 | +Yes. The first run has no cached images, so Docker pulls base images and builds from scratch. Subsequent runs reuse cached images and are significantly faster. |
| 260 | + |
| 261 | +### What permissions does cleanup need? |
| 262 | + |
| 263 | +`packages:delete` (part of the `packages: write` scope). If your token lacks this, set `cache-cleanup: 'false'` — images accumulate but nothing breaks. |
| 264 | + |
| 265 | +## License |
| 266 | + |
| 267 | +MIT |
0 commit comments