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
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
.git
.github/
specs/
Dockerfile*
.dockerignore
node_modules/
dist/
build/
Expand Down
34 changes: 33 additions & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,36 @@
<!-- SPECKIT START -->
For additional context about technologies to be used, project structure,
shell commands, and other important information, read the current plan
shell commands, and other important information, read specs/002-multiarch-image-cache/plan.md
<!-- SPECKIT END -->

<!-- tram0004 instructions start -->

# Instructions

The purpose of this project is to build a Docker container image providing the latest version of Ruby
2.6.x, including the `jemalloc` implementation of `malloc` for improved memory performance. To support
development and testing, the resulting container will be multi-architecture, supporting both
`linux/amd64` and `linux/arm64` architectures.

The project will use `dependabot` to keep the pinned versions of Ruby, OpenSSL, and jemalloc up to date.

## Docker Specification

The resulting Docker image will have a minimal runtime footprint, using a multi-stage build to limit
image contents to only the compiled Ruby binaries and necessary runtime libraries.

The `Dockerfile` will be formatted with "here-doc" `RUN` blocks for clarity and maintainability.

The image will provide the latest compatible versions of its dependencies:

* Ruby 2.6.x (currently 2.6.10)
* OpenSSL 1.1.x (currently 1.1.1w)
* jemalloc 5.3.x (currently 5.3.0)

## CICD

* The CI workflow will be implemented with GitHub Actions, using `Buildx` for multi-architecture
builds and cache management.
* Use the `pre-commit` toolchain for local development to ensure code quality and consistency.

<!-- tram0004 instructions end -->
114 changes: 88 additions & 26 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,12 @@ on:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
MULTIARCH_SCOPE: multiarch-image-cache

jobs:
build-and-push:
release:
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
platform: linux/amd64
- arch: arm64
platform: linux/arm64
permissions:
contents: read
packages: write
Expand All @@ -38,7 +32,6 @@ jobs:
uses: docker/setup-qemu-action@v3

- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
Expand All @@ -61,30 +54,99 @@ jobs:
uses: docker/build-push-action@v6
with:
context: .
platforms: ${{ matrix.platform }}
push: ${{ github.event_name != 'pull_request' }}
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
cache-from: type=gha,scope=${{ env.MULTIARCH_SCOPE }}
cache-to: type=gha,mode=max,scope=${{ env.MULTIARCH_SCOPE }}
build-args: |
RUBY_VERSION=2.6.10
OPENSSL_VERSION=1.1.1w
JEMALLOC_VERSION=5.3.0

- name: Build verification image (load)
verify-release:
if: github.event_name != 'pull_request'
needs: release
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
platform: linux/amd64
- arch: arm64
platform: linux/arm64
permissions:
contents: read
packages: read

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Resolve release image reference
run: echo "RELEASE_IMAGE=${REGISTRY}/${IMAGE_NAME}:sha-${GITHUB_SHA}" >> "$GITHUB_ENV"

- name: Pull release image
run: docker pull "$RELEASE_IMAGE"

- name: Verify Ruby and jemalloc runtime
run: |
docker buildx build \
--platform "${{ matrix.platform }}" \
--build-arg RUBY_VERSION=2.6.10 \
--build-arg OPENSSL_VERSION=1.1.1w \
--build-arg JEMALLOC_VERSION=5.3.0 \
--load \
-t "ruby2.6-jemalloc:ci-${{ matrix.arch }}" \
.
./scripts/verify-ruby-jemalloc.sh "$RELEASE_IMAGE" "${{ matrix.platform }}"

verify-pr:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
platform: linux/amd64
- arch: arm64
platform: linux/arm64
permissions:
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Build verification image (load)
uses: docker/build-push-action@v6
with:
context: .
platforms: ${{ matrix.platform }}
load: true
push: false
tags: ruby2.6-jemalloc:ci-${{ matrix.arch }}
cache-from: type=gha,scope=${{ env.MULTIARCH_SCOPE }}
cache-to: type=gha,mode=max,scope=${{ env.MULTIARCH_SCOPE }}-pr-${{ matrix.arch }}
build-args: |
RUBY_VERSION=2.6.10
OPENSSL_VERSION=1.1.1w
JEMALLOC_VERSION=5.3.0

- name: Verify Ruby and jemalloc runtime
run: |
./scripts/verify-ruby-jemalloc.sh \
"ruby2.6-jemalloc:ci-${{ matrix.arch }}" \
"${{ matrix.platform }}"
./scripts/verify-ruby-jemalloc.sh "ruby2.6-jemalloc:ci-${{ matrix.arch }}" "${{ matrix.platform }}"
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ build/
# Editor / OS
.DS_Store
Thumbs.db
*.tmp
*.swp
.vscode/
.idea/

2 changes: 1 addition & 1 deletion .specify/feature.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"feature_directory": "specs/001-use-heredocs"
"feature_directory": "specs/002-multiarch-image-cache"
}
22 changes: 21 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
usage:
@echo "usage: make [usage|lint|build|build-amd64|build-arm64|verify|verify-amd64|verify-arm64]"
@echo "usage: make [usage|lint|build|build-amd64|build-arm64|build-release|verify|verify-amd64|verify-arm64|verify-release]"

REGISTRY ?= ghcr.io/umnlibraries/ruby2.6-jemalloc-docker
MULTIARCH_TAG ?= sha-$(shell git rev-parse --short HEAD 2>/dev/null || echo local)
MULTIARCH_PLATFORMS ?= linux/amd64,linux/arm64
MULTIARCH_CACHE_DIR ?= .buildx-cache

lint:
pre-commit run --all-file
Expand All @@ -13,6 +18,17 @@ build-amd64:
build-arm64:
docker buildx build --platform linux/arm64 --load -t ruby2.6-jemalloc:arm64 .

build-release:
docker buildx build \
--platform $(MULTIARCH_PLATFORMS) \
--cache-from type=local,src=$(MULTIARCH_CACHE_DIR) \
--cache-to type=local,dest=$(MULTIARCH_CACHE_DIR)-new,mode=max \
--push \
-t $(REGISTRY):$(MULTIARCH_TAG) \
.
@rm -rf $(MULTIARCH_CACHE_DIR)
@mv $(MULTIARCH_CACHE_DIR)-new $(MULTIARCH_CACHE_DIR)

verify: build
./scripts/verify-ruby-jemalloc.sh ruby2.6-jemalloc:local

Expand All @@ -21,3 +37,7 @@ verify-amd64: build-amd64

verify-arm64: build-arm64
./scripts/verify-ruby-jemalloc.sh ruby2.6-jemalloc:arm64 linux/arm64

verify-release: build-release
./scripts/verify-ruby-jemalloc.sh $(REGISTRY):$(MULTIARCH_TAG) linux/amd64
./scripts/verify-ruby-jemalloc.sh $(REGISTRY):$(MULTIARCH_TAG) linux/arm64
32 changes: 29 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,22 @@ Long Docker build sequences are expressed with shell here-doc `RUN` blocks so ea
is easier to review and maintain. This keeps ordering explicit while avoiding fragile line
continuation chains.

## Multi-Platform Release and Caching

The release workflow publishes one image tag that resolves to both `linux/amd64` and
`linux/arm64` variants. GitHub Actions uses Buildx cache import/export so unchanged work can be
reused across repeated builds.

Local contributors can mirror the release workflow with:

```sh
make build-release
make verify-release
```

`make build-release` publishes the multi-platform image tag and preserves cache in `.buildx-cache`
between runs. `make verify-release` pulls the published tag and verifies both architectures.

## Usage

### Pull from GitHub Container Registry
Expand Down Expand Up @@ -63,6 +79,13 @@ make verify-amd64
make verify-arm64
```

For the multi-platform release flow, use:

```sh
make build-release
make verify-release
```

The verification script checks:

- Ruby reports `2.6.x`
Expand All @@ -71,7 +94,10 @@ The verification script checks:

## CI/CD

GitHub Actions builds the image on every push to `main` and publishes it to the [GitHub Container Registry](https://ghcr.io/umnlibraries/ruby2.6-jemalloc-docker). Pull requests trigger a build-only run (no push) to validate the `Dockerfile`.
GitHub Actions publishes one multi-platform image tag on pushes to `main` and manual release
runs. Pull requests run per-platform verification builds with cache reuse enabled so changes are
validated without publishing.

CI runs a build matrix for `linux/amd64` and `linux/arm64` and executes runtime verification for
each platform.
The release job publishes a single manifest-backed tag for `linux/amd64` and `linux/arm64`, while
the verification job checks the published tag on `main` and uses cached per-platform build/load
runs on pull requests.
Loading
Loading