Skip to content

Commit 93eff62

Browse files
CNB support added for Heroku FIR
1 parent 6579a75 commit 93eff62

File tree

9 files changed

+158
-21
lines changed

9 files changed

+158
-21
lines changed

.dockerignore

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Version control
2+
.git
3+
.gitignore
4+
5+
# Editor/project files
6+
.idea/
7+
.vscode/
8+
.DS_Store
9+
Thumbs.db
10+
ehthumbs.db
11+
12+
# Local/dev tools and caches
13+
.sfdx/
14+
.eslintcache
15+
.act/
16+
.cursor/
17+
18+
# Logs and debug files
19+
logs
20+
*.log
21+
22+
# Environment and secret files
23+
*.env
24+
*.dev
25+
.act
26+
27+
# Documentation and non-runtime files
28+
app.json
29+
heroku.yml
30+
project.toml
31+
inline-cnb-build.sh
32+
README.md
33+
CONTRIBUTING.md
34+
SECURITY.md
35+
LICENSE.txt
36+
CODEOWNERS
37+
CODE_OF_CONDUCT.md
38+
39+
# Build/test artifacts and images
40+
images/
41+
42+
# GitHub workflows and configs
43+
.github/

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ $RECYCLE.BIN/
3939
*.env
4040
*.dev
4141
.act
42+
.cursor/

Dockerfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ ARG DEBIAN_FRONTEND=noninteractive
1010
# https://devcenter.heroku.com/articles/build-docker-images-heroku-yml#set-build-time-environment-variables
1111
ARG RUNNER_VERSION=latest
1212

13+
# this ARG can be overridden changing the heroku.yml, it must be 'x64' (or 'arm64' when supported along with Dockerfile)
14+
# https://devcenter.heroku.com/articles/build-docker-images-heroku-yml#set-build-time-environment-variables
15+
ARG RUNNER_ARCH=x64
16+
1317
USER root
1418

1519
# Switch to bash shell.
@@ -66,7 +70,7 @@ WORKDIR ${ACTIONS_DIR}
6670

6771
# Download the latest GitHub Actions runner package.
6872
COPY install-actions-runner.sh /tmp/install-actions-runner.sh
69-
RUN sh /tmp/install-actions-runner.sh "$RUNNER_VERSION" && rm -f /tmp/install-actions-runner.sh
73+
RUN sh /tmp/install-actions-runner.sh "$RUNNER_VERSION" "$RUNNER_ARCH" && rm -f /tmp/install-actions-runner.sh
7074

7175
# ------------------------------------------------------------------------------
7276
# Copy files and set permissions

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
## Heroku-hosted runner for GitHub Actions
22

3-
This project defines a `Dockerfile` to run a custom Heroku-hosted runner for Github Actions (see also [self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners)).
3+
This project defines a `Dockerfile`/`CNB` to build and run a custom Heroku-hosted runner for Github Actions (see also [self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners)).
44

5-
The runner is hosted on [Heroku as a docker image](https://devcenter.heroku.com/articles/build-docker-images-heroku-yml) via `heroku.yml`.
5+
The runner is hosted on Heroku as a [docker image](https://devcenter.heroku.com/articles/build-docker-images-heroku-yml) via `heroku.yml` or [OCI image](https://devcenter.heroku.com/articles/buildpacks#cloud-native-buildpacks) via `project.toml`.
66

77
## How it works
88

@@ -102,7 +102,7 @@ This project includes a workflow that can be run manually or automatically sched
102102

103103
To take advantage of the above automation you need to [fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) or [mirror](https://docs.github.com/en/repositories/creating-and-managing-repositories/duplicating-a-repository#mirroring-a-repository-in-another-location) this repository to your private organisation's repository and enable workflows run.
104104
105-
If you need to implement version pinning to avoid potential supply chain vulnerabilities, it's possible to configure a specific runner version (see `RUNNER_VERSION` in heroku.yml). In this case you'll have to modify manually the version and run the above mentioned workflow manually and the scheduled workflow execution can be disabled.
105+
If you need to implement version pinning to avoid potential supply chain vulnerabilities, it's possible to configure a specific runner version (see `RUNNER_VERSION` in heroku.yml/project.toml). In this case you'll have to modify manually the version and run the above mentioned workflow manually and the scheduled workflow execution can be disabled.
106106
107107
Whenever the runner package is downloaded (either the latest or a specific version) the SHA256 checksum is verified, if the computed checksum does not match with the expected one the build fails.
108108
@@ -112,10 +112,11 @@ This new release:
112112
- Uses ephemeral compute to allow [autoscaling](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/autoscaling-with-self-hosted-runners#using-ephemeral-runners-for-autoscaling) and [hardening](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#hardening-for-self-hosted-runners) of self-hosted runners. Ephemeral runners are short-lived containers that are executed only once for a single job, providing isolated environments to reduce the risk of data leakage
113113
- Uses a [base image](https://devcenter.heroku.com/articles/stack) that is curated and maintained by Heroku
114114
- Logs the self-runner name to manage it from the GitHub dashboard
115-
- Reduces the Docker image footprint and it's possible to run it as one-off dyn
115+
- Reduces the Docker/OCI image footprint and it's possible to run it as one-off dyno (Cedar only)
116116
- Includes all the recent GitHub self-hosted runners features (e.g. labels, groups, ...) and streamlines the configuration and setup
117-
- Integrates the [Heroku Button](https://www.heroku.com/elements/buttons) to install the runner in one-click
117+
- Integrates the [Heroku Button](https://www.heroku.com/elements/buttons) to install the runner in one-click (Cedar only)
118118
- Supports fine-grained GitHub tokens for granular permission control
119+
- Can be executed on both Heroku Cedar and Heroku Next Generation Platform Fir
119120

120121
## Security Notes and Recommendations
121122
Below are summarised some general recommendations to improve security for using GitHub Actions and self-hosted runners, for a complete guide and further details please refer to [Security hardening for GitHub Actions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions):
@@ -155,7 +156,6 @@ Below are summarised some general recommendations to improve security for using
155156
## Limits and Considerations
156157
- As the runner image is not based on the [standard GitHub dockerfile](https://github.com/actions/runner/blob/main/images/Dockerfile) then some Actions might not work as expected
157158
- Currently, it's not possible to run GitHub Actions requiring docker/rootless-docker as they need higher privileges that are not allowed on Heroku dynos for security reasons
158-
- The runner cannot be executed on [Fir](https://devcenter.heroku.com/changelog-items/3071) yet, requiring an ARM based image
159159

160160

161161
## Credits

heroku.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
build:
44
config:
55
RUNNER_VERSION: latest
6+
RUNNER_ARCH: x64
67
docker:
78
runner: Dockerfile
89

inline-cnb-build.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
echo "---> Heroku-hosted runner for GitHub Actions buildpack (CNB style)"
5+
6+
# Set APP_DIR to current working directory (where application source is located during build)
7+
APP_DIR="$(pwd)"
8+
9+
# remove all the files in the buildpack working directory that are not required for the build
10+
# Only keep start.sh and install-actions-runner.sh files
11+
find . -mindepth 1 ! -name "start.sh" ! -name "install-actions-runner.sh" -delete
12+
13+
chmod +x "${APP_DIR}/start.sh"
14+
15+
mkdir -p "${APP_DIR}/actions-runner"
16+
17+
# Use the version from env or default to 'latest'
18+
RUNNER_VERSION="${RUNNER_VERSION:-latest}"
19+
20+
# Use the architecture from CNB_TARGET_ARCH that can be amd64=>x64 or arm64
21+
if [[ "${CNB_TARGET_ARCH}" == "amd64" ]]; then
22+
RUNNER_ARCH="x64"
23+
else
24+
RUNNER_ARCH="${CNB_TARGET_ARCH:-arm64}"
25+
fi
26+
27+
# Run the install script
28+
chmod +x "${APP_DIR}/install-actions-runner.sh"
29+
cd "${APP_DIR}/actions-runner" && "${APP_DIR}/install-actions-runner.sh" "$RUNNER_VERSION" "$RUNNER_ARCH"
30+
rm "${APP_DIR}/install-actions-runner.sh"
31+
32+
# Write launch.toml to define the default process
33+
# The launch.toml must be created in CNB_LAYERS_DIR even when running from workspace
34+
cat > "${CNB_LAYERS_DIR}/launch.toml" <<EOL
35+
[[processes]]
36+
type = "runner"
37+
command = ["${APP_DIR}/start.sh"]
38+
default = true
39+
EOL

install-actions-runner.sh

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,34 @@ validate_version() {
1212
fi
1313
}
1414

15+
# Function to validate architecture input
16+
validate_arch() {
17+
local arch="$1"
18+
if [[ "$arch" == "x64" || "$arch" == "arm64" ]]; then
19+
echo "$arch"
20+
else
21+
echo "Error: Architecture must be 'x64' or 'arm64'"
22+
exit 1
23+
fi
24+
}
25+
1526
# Check if version parameter is provided
1627
if [ -z "$1" ]; then
1728
echo "Error: Please provide a version (e.g. 'latest' or '2.320.1')."
18-
echo "Usage: $0 <version>"
29+
echo "Usage: $0 <version> <arch>"
30+
exit 1
31+
fi
32+
33+
# Check if architecture parameter is provided
34+
if [ -z "$2" ]; then
35+
echo "Error: Please provide the runner architecture ('x64' or 'arm64')."
36+
echo "Usage: $0 <version> <arch>"
1937
exit 1
2038
fi
2139

22-
# Validate the version input
40+
# Validate the version and architecture input
2341
VERSION_INPUT=$(validate_version "$1")
42+
ARCH_INPUT=$(validate_arch "$2")
2443

2544
# Define the GitHub repository and API endpoint
2645
if [ "$VERSION_INPUT" == "latest" ]; then
@@ -42,9 +61,9 @@ if [ -z "$RUNNER_VERSION" ]; then
4261
fi
4362
echo "Latest version: ${RUNNER_VERSION}"
4463

45-
# Define the asset name (e.g., for Linux x64; adjust as needed)
64+
# Define the asset name (e.g., for Linux x64 / arm64)
4665
RUNNER_OS="linux"
47-
RUNNER_ARCH="x64"
66+
RUNNER_ARCH="$ARCH_INPUT"
4867

4968
ASSET_NAME="actions-runner-${RUNNER_OS}-${RUNNER_ARCH}-${RUNNER_VERSION}.tar.gz"
5069
# printf is used to parse RELEASE_JSON as it might contain unescaped control characters (like newlines, tabs, ...)
@@ -88,10 +107,10 @@ else
88107
fi
89108
fi
90109

91-
# Extract the runner and install some additional dependencies
110+
# Extract the runner. The additional dependencies usually required are already included using Heroku stack.
92111
# https://github.com/actions/runner/blob/main/docs/start/envlinux.md#install-net-core-3x-linux-dependencies
93-
tar xvfz "${ASSET_NAME}" && ./bin/installdependencies.sh
112+
tar xvfz "${ASSET_NAME}"
94113

95114
# Clean up
96115
rm -f "${ASSET_NAME}"
97-
echo "Cleaned up downloaded file."
116+
echo "Cleaned up downloaded file."

project.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[_]
2+
schema-version = "0.2"
3+
4+
[[io.buildpacks.group]]
5+
id = "heroku/github-actions-runner"
6+
version = "1.0.0"
7+
8+
[io.buildpacks.group.script]
9+
api = "0.11"
10+
inline = "./inline-cnb-build.sh"
11+
12+
[build]
13+
builder = "heroku/builder:24"
14+
15+
[[io.buildpacks.build.env]]
16+
name='RUNNER_VERSION'
17+
value='latest'
18+
19+
# to build it locally with pack for arm64/amd64:
20+
# pack build my-runner-app --builder heroku/builder:24 --platform linux/arm64
21+
# pack build my-runner-app --builder heroku/builder:24 --platform linux/amd64

start.sh

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
# This file inspired by tutorial at
44
# https://testdriven.io/blog/github-actions-docker/
5+
# It selects the runner directory based on the packaging method (Dockerfile or CNB buildpack)
56

67
# Requests a temporary token to register a GitHub runner.
78
# https://docs.github.com/en/rest/reference/actions#create-a-registration-token-for-an-organization
@@ -15,7 +16,7 @@
1516
# GitHub access tokens can contain alphanumeric characters (a-z, A-Z, 0-9), underscores (_)
1617
[[ "${GITHUB_ACCESS_TOKEN}" ]] || { echo "GITHUB_ACCESS_TOKEN is required"; exit 1; }
1718
[[ "${GITHUB_ACCESS_TOKEN}" =~ ^[a-zA-Z0-9_]+$ ]] || { echo "GITHUB_ACCESS_TOKEN contains invalid characters"; exit 1; }
18-
# saving it localli before removing the env var (see unset_vars)
19+
# saving it locally before removing the env var (see unset_vars)
1920
LOCAL_GITHUB_ACCESS_TOKEN="${GITHUB_ACCESS_TOKEN}"
2021

2122
# HIDDEN_ENV_VARS must only contain a list of space separated Heroku env vars. Those can contain uppercase letters (A-Z), numbers (0-9), underscores (_)
@@ -80,11 +81,19 @@ detachRunner() {
8081
--token "${GITHUB_REG_TOKEN}"
8182
}
8283

83-
# Recall that this script is running in our dockerized image on Heroku.
84-
# Our Dockerfile created a user named 'docker' and the following directory is
85-
# where it installed the GitHub Actions self-hosting runner package.
86-
# We now navigate to that directory to start the runner.
87-
cd "${HOME}"/actions-runner || { echo "error while changing directory to ${HOME}/actions-runner"; exit 1; }
84+
# Directory selection logic
85+
# If the installation was done in the Dockerfile, use the Dockerfile path
86+
# Otherwise, default to CNB path that is in the current working directory
87+
if [[ -d "${HOME}/actions-runner" ]]; then
88+
RUNNER_DIR="${HOME}/actions-runner"
89+
elif [[ -d "${PWD}/actions-runner" ]]; then
90+
RUNNER_DIR="${PWD}/actions-runner"
91+
else
92+
echo "Could not determine runner directory. Neither CNB nor Dockerfile path exists." >&2
93+
exit 1
94+
fi
95+
96+
cd "$RUNNER_DIR" || { echo "error while changing directory to $RUNNER_DIR"; exit 1; }
8897

8998
attachRunner
9099

@@ -132,4 +141,4 @@ unset_vars
132141
PID=$!
133142
wait $PID
134143

135-
echo "[self-hosted runner] Exiting ..."
144+
echo "[self-hosted runner] Exiting ..."

0 commit comments

Comments
 (0)