Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Changelog

All notable changes to this action are documented here. This project follows
[Semantic Versioning](https://semver.org/). The moving major tag (e.g. `v4`)
always points at the latest release in that major line.

## [4.0.0] - 2026-07-02

A capability wave across cost, reliability, reach, and toil. 10 features, each
shipped as its own PR (#50–#59).

### ⚠️ Breaking changes

- **`ec2:CreateTags` is now always required.** Every launched instance (and its
volumes) is stamped with the action's signature tags — `ec2-github-runner:managed`,
`:repository`, `:label`, `:started-at` — which the cleanup reaper relies on.
Grant `ec2:CreateTags` with the condition `ec2:CreateAction = RunInstances`
(see the README permissions policy) or `RunInstances` will be denied. Previously
this permission was only needed when using `aws-resource-tags`. (#42, #45)
- **`cleanup-on-start-failure` defaults to `true`.** When a runner fails to
bootstrap or register, the action now captures the instance's console output and
**terminates** it, instead of leaving it running after a registration timeout.
Set `cleanup-on-start-failure: false` to preserve the old keep-it-running
behavior for interactive debugging. (#41)
- The root EBS volume created for `encrypt-ebs` / `volume-*` now always sets
`DeleteOnTermination: true`, so resized/encrypted volumes never leak with the
ephemeral instance. (#44)

### Added

- **Bootstrap diagnostics** (#41): per-phase `ec2-github-runner:bootstrap`
phone-home tags, fast-fail on `failed:<step>` (naming the step), console-output
capture on failure (token redacted), and `cleanup-on-start-failure`.
- **Orphan protection** (#42): `max-lifetime-minutes` TTL self-destruct and a new
`mode: cleanup` reaper (with `max-age-minutes`, `dry-run`) that terminates
leaked instances this action started in the repo. Example scheduled workflow in
`docs/cleanup-workflow.yml`.
- **Root volume configuration** (#44): `volume-size`, `volume-type`, `volume-iops`,
`volume-throughput` — composes with `encrypt-ebs`.
- **Capacity resilience** (#40): `ec2-instance-type` and `subnet-id` accept
comma-separated ordered fallback lists (subnet/AZ first, then type). New outputs
`instance-type-used`, `subnet-id-used`.
- **Spot instances** (#39): `market-type: spot` with `spot-fallback` and
`spot-max-price`; composes with capacity fallback. New output `market-type-used`.
- **Automated runner-version bumps** (#47): `scripts/bump-runner.js` (manual or
via the weekly `Bump actions/runner` workflow) updates the checksum table,
defaults, docs, and dist, then opens a no-auto-merge PR.
- **ARM64/Graviton support** (#43): `architecture: arm64`, with AMI-architecture
validation that fails fast on a mismatch.
- **Multi-runner batches** (#45): `count` (with `allow-partial`) launches N runners
behind one label for matrix builds. New output `ec2-instance-ids` (JSON array);
`stop` accepts `ec2-instance-ids`.
- **Pluggable bootstrap** (#46): `pre-runner-script` (inject steps into the built-in
bootstrap) and `user-data-template` (full override with `{{PLACEHOLDER}}`
rendering). Community-maintained Ubuntu example in `examples/user-data/`.
- **Warm pools** (#48): `reuse: stop` reuses stopped instances (stop/start) for
much faster job starts, with `reuse-pool-tag`, `reuse-max-cycles`, and reaper
draining via `reaper-stopped-max-age`. See the security note — reuse carries disk
state between jobs and is unsafe for public/untrusted-PR repos.

### IAM

- Base policy additionally needs `ec2:DescribeTags` and `ec2:GetConsoleOutput`
(diagnostics + reaper). The runner's own instance role (`iam-role-name`) may be
granted self-scoped `ec2:CreateTags` for the optional bootstrap phone-home.

### Internal

- Unit-test suite grew from 52 to 224 tests; lint, `verify-dist`, and the pinned
runner checksum verification stay green on every PR.

Earlier releases: https://github.com/namecheap/ec2-github-runner/releases
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ jobs:
aws-region: ${{ secrets.AWS_REGION }}
- name: Start EC2 runner
id: start-ec2-runner
uses: namecheap/ec2-github-runner@v3
uses: namecheap/ec2-github-runner@v4
with:
mode: start
github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
Expand Down Expand Up @@ -422,7 +422,7 @@ jobs:
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Stop EC2 runner
uses: namecheap/ec2-github-runner@v3
uses: namecheap/ec2-github-runner@v4
with:
mode: stop
github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
Expand All @@ -444,14 +444,14 @@ Set `reuse: stop` on **both** the start and stop steps:

```yml
# start
- uses: namecheap/ec2-github-runner@v3
- uses: namecheap/ec2-github-runner@v4
with:
mode: start
reuse: stop
reuse-pool-tag: ci-medium # instances are interchangeable within a pool tag
# ... other inputs ...
# stop
- uses: namecheap/ec2-github-runner@v3
- uses: namecheap/ec2-github-runner@v4
with:
mode: stop
reuse: stop
Expand Down Expand Up @@ -485,7 +485,7 @@ jobs:
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- id: start
uses: namecheap/ec2-github-runner@v3
uses: namecheap/ec2-github-runner@v4
with:
mode: start
github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
Expand Down Expand Up @@ -514,7 +514,7 @@ jobs:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- uses: namecheap/ec2-github-runner@v3
- uses: namecheap/ec2-github-runner@v4
with:
mode: stop
github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
Expand All @@ -530,7 +530,7 @@ CI runners are a textbook spot workload — short-lived, ephemeral (registered w

```yml
- name: Start EC2 runner
uses: namecheap/ec2-github-runner@v3
uses: namecheap/ec2-github-runner@v4
with:
mode: start
# ... other inputs ...
Expand All @@ -549,7 +549,7 @@ A single `subnet-id` + `ec2-instance-type` means a single point of failure: when

```yml
- name: Start EC2 runner
uses: namecheap/ec2-github-runner@v3
uses: namecheap/ec2-github-runner@v4
with:
mode: start
# ... other inputs ...
Expand All @@ -570,7 +570,7 @@ Need an extra package or a different distro? Two escape hatches mean you never h
Inject shell into the built-in (supported) bootstrap, run as root before the runner registers:

```yml
- uses: namecheap/ec2-github-runner@v3
- uses: namecheap/ec2-github-runner@v4
with:
mode: start
# ... other inputs ...
Expand All @@ -586,7 +586,7 @@ It runs under `set -euo pipefail` and a failure is tagged `failed:pre-runner-scr
Replace the bootstrap entirely with your own script (repo-relative path or inline string). The action substitutes documented placeholders and submits the result:

```yml
- uses: namecheap/ec2-github-runner@v3
- uses: namecheap/ec2-github-runner@v4
with:
mode: start
# ... other inputs ...
Expand All @@ -612,7 +612,7 @@ Graviton instances (c7g/m7g/r7g/…) deliver ~20–40% better price/performance

```yml
- name: Start EC2 runner
uses: namecheap/ec2-github-runner@v3
uses: namecheap/ec2-github-runner@v4
with:
mode: start
architecture: arm64
Expand All @@ -630,7 +630,7 @@ The runner inherits the AMI's root volume size — 8 GiB on Amazon Linux 2023. D

```yml
- name: Start EC2 runner
uses: namecheap/ec2-github-runner@v3
uses: namecheap/ec2-github-runner@v4
with:
mode: start
# ... other inputs ...
Expand Down Expand Up @@ -717,6 +717,10 @@ Please find more details about this security note on [GitHub documentation](http

> ⚠️ **`reuse: stop` (warm pools) makes this worse.** With reuse, a runner's disk carries over between jobs, so a later job can read a previous job's residue (checked-out code, caches, credentials written to disk). Only use `reuse: stop` for a **single trusted repository's** CI. Never combine it with public-repo / untrusted-PR workloads. The default `reuse: terminate` gives every job a fresh instance.

## Changelog

See [CHANGELOG.md](CHANGELOG.md) for release notes and breaking changes. Pin the moving major tag (`@v4`) for the latest release in that line, or a specific version (`@v4.0.0`) to pin exactly.

## License Summary

This code is made available under the [MIT license](LICENSE).
2 changes: 1 addition & 1 deletion docs/cleanup-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Reap leaked runners
uses: namecheap/ec2-github-runner@v3
uses: namecheap/ec2-github-runner@v4
with:
mode: cleanup
github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion examples/user-data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
## Using a template

```yml
- uses: namecheap/ec2-github-runner@v3
- uses: namecheap/ec2-github-runner@v4
with:
mode: start
github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
Expand Down