Skip to content

Commit 9689390

Browse files
Add publish-image workflow
1 parent b9bfcd0 commit 9689390

File tree

4 files changed

+203
-6
lines changed

4 files changed

+203
-6
lines changed
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
name: Build and publish container image to GHCR
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
tags:
8+
- 'v*'
9+
pull_request:
10+
workflow_dispatch:
11+
12+
concurrency:
13+
group: publish-${{ github.workflow }}-${{ github.ref }}
14+
cancel-in-progress: true
15+
16+
permissions:
17+
contents: read
18+
packages: write
19+
id-token: write
20+
attestations: write
21+
22+
env:
23+
IMAGE_DESCRIPTION: Instantly publish your own books on the web for free, no publisher required.
24+
SOURCE_URL: https://github.com/${{ github.repository }}
25+
26+
jobs:
27+
build:
28+
name: Build and push image (${{ matrix.arch }})
29+
runs-on: ${{ matrix.runner }}
30+
timeout-minutes: 45
31+
strategy:
32+
fail-fast: false
33+
matrix:
34+
include:
35+
- runner: ubuntu-latest
36+
platform: linux/amd64
37+
arch: amd64
38+
- runner: ubuntu-24.04-arm
39+
platform: linux/arm64
40+
arch: arm64
41+
env:
42+
REGISTRY: ghcr.io
43+
IMAGE_NAME: ${{ github.repository }}
44+
steps:
45+
- name: Checkout
46+
uses: actions/checkout@v5.0.0
47+
48+
- name: Set up Docker Buildx
49+
uses: docker/setup-buildx-action@v3.11.1
50+
51+
- name: Log in to GHCR
52+
uses: docker/login-action@v3.5.0
53+
with:
54+
registry: ${{ env.REGISTRY }}
55+
username: ${{ github.actor }}
56+
password: ${{ secrets.GITHUB_TOKEN }}
57+
58+
- name: Compute canonical image name (lowercase)
59+
id: vars
60+
shell: bash
61+
run: |
62+
set -eu
63+
IMAGE_REF="${IMAGE_NAME:-$GITHUB_REPOSITORY}"
64+
CANONICAL_IMAGE="${REGISTRY}/${IMAGE_REF,,}"
65+
echo "canonical=${CANONICAL_IMAGE}" >> "$GITHUB_OUTPUT"
66+
67+
- name: Extract Docker metadata (tags, labels) with arch suffix
68+
id: meta
69+
uses: docker/metadata-action@v5.8.0
70+
with:
71+
images: ${{ steps.vars.outputs.canonical }}
72+
tags: |
73+
type=ref,event=branch
74+
type=ref,event=tag
75+
type=sha,format=short,prefix=sha-
76+
type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
77+
type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
78+
type=semver,pattern={{major}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
79+
flavor: |
80+
latest=false
81+
suffix=-${{ matrix.arch }}
82+
labels: |
83+
org.opencontainers.image.source=${{ env.SOURCE_URL }}
84+
85+
- name: Build and push (${{ matrix.platform }})
86+
id: build
87+
uses: docker/build-push-action@v6.18.0
88+
with:
89+
context: .
90+
file: Dockerfile
91+
build-args: |
92+
OCI_SOURCE=${{ env.SOURCE_URL }}
93+
OCI_DESCRIPTION=${{ env.IMAGE_DESCRIPTION }}
94+
platforms: ${{ matrix.platform }}
95+
push: ${{ github.event_name != 'pull_request' }}
96+
tags: ${{ steps.meta.outputs.tags }}
97+
labels: ${{ steps.meta.outputs.labels }}
98+
cache-from: type=gha,scope=${{ matrix.platform }}
99+
cache-to: type=gha,scope=${{ matrix.platform }},mode=max
100+
sbom: false
101+
provenance: false
102+
103+
# - name: Attest image provenance (per-arch)
104+
# if: github.event_name != 'pull_request'
105+
# uses: actions/attest-build-provenance@v3.0.0
106+
# with:
107+
# subject-name: ${{ steps.vars.outputs.canonical }}
108+
# subject-digest: ${{ steps.build.outputs.digest }}
109+
# push-to-registry: false
110+
111+
manifest:
112+
name: Create multi-arch manifest and sign
113+
needs: build
114+
if: github.event_name != 'pull_request'
115+
runs-on: ubuntu-latest
116+
timeout-minutes: 20
117+
env:
118+
REGISTRY: ghcr.io
119+
IMAGE_NAME: ${{ github.repository }}
120+
steps:
121+
- name: Set up Docker Buildx (for imagetools)
122+
uses: docker/setup-buildx-action@v3.11.1
123+
124+
- name: Log in to GHCR
125+
uses: docker/login-action@v3.5.0
126+
with:
127+
registry: ${{ env.REGISTRY }}
128+
username: ${{ github.actor }}
129+
password: ${{ secrets.GITHUB_TOKEN }}
130+
131+
- name: Compute canonical image name (lowercase)
132+
id: vars
133+
shell: bash
134+
run: |
135+
set -eu
136+
IMAGE_REF="${IMAGE_NAME:-$GITHUB_REPOSITORY}"
137+
CANONICAL_IMAGE="${REGISTRY}/${IMAGE_REF,,}"
138+
echo "canonical=${CANONICAL_IMAGE}" >> "$GITHUB_OUTPUT"
139+
140+
- name: Compute base tags (no suffix)
141+
id: meta
142+
uses: docker/metadata-action@v5.8.0
143+
with:
144+
images: ${{ steps.vars.outputs.canonical }}
145+
tags: |
146+
type=ref,event=branch
147+
type=ref,event=tag
148+
type=sha,format=short,prefix=sha-
149+
type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
150+
type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
151+
type=semver,pattern={{major}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
152+
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
153+
flavor: |
154+
latest=false
155+
labels: |
156+
org.opencontainers.image.source=${{ env.SOURCE_URL }}
157+
158+
- name: Create multi-arch manifests
159+
shell: bash
160+
run: |
161+
set -eu
162+
tags="${{ steps.meta.outputs.tags }}"
163+
echo "Creating manifests for tags:"
164+
printf '%s\n' "$tags"
165+
while IFS= read -r tag; do
166+
[ -z "$tag" ] && continue
167+
echo "Creating manifest for $tag"
168+
src_tag="$tag"
169+
if [[ "$tag" == *:latest && "${GITHUB_REF}" == refs/tags/* ]]; then
170+
ref="${GITHUB_REF#refs/tags/}"
171+
src_tag="${tag%:latest}:$ref"
172+
fi
173+
if [ -n "${IMAGE_DESCRIPTION:-}" ]; then
174+
docker buildx imagetools create \
175+
--tag "$tag" \
176+
--annotation "index:org.opencontainers.image.description=${IMAGE_DESCRIPTION}" \
177+
"${src_tag}-amd64" \
178+
"${src_tag}-arm64"
179+
else
180+
docker buildx imagetools create \
181+
--tag "$tag" \
182+
"${src_tag}-amd64" \
183+
"${src_tag}-arm64"
184+
fi
185+
done <<< "$tags"
186+
187+
- name: Install Cosign
188+
uses: sigstore/cosign-installer@v3.9.2
189+
190+
- name: Cosign sign all tags (keyless OIDC)
191+
shell: bash
192+
run: |
193+
set -eu
194+
tags="${{ steps.meta.outputs.tags }}"
195+
printf '%s\n' "$tags"
196+
while IFS= read -r tag; do
197+
[ -z "$tag" ] && continue
198+
echo "Signing $tag"
199+
cosign sign --yes "$tag"
200+
done <<< "$tags"

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# syntax = docker/dockerfile:1
22

33
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
4-
ARG RUBY_VERSION=3.3.5
4+
ARG RUBY_VERSION=3.4.7
55
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base
66

77
# Rails app lives here
@@ -19,7 +19,7 @@ FROM --platform=$TARGETPLATFORM base as build
1919

2020
# Install packages needed to build gems
2121
RUN apt-get update -qq && \
22-
apt-get install --no-install-recommends -y build-essential git libvips pkg-config
22+
apt-get install --no-install-recommends -y build-essential git libyaml-dev libvips pkg-config
2323

2424
# Install application gems
2525
COPY Gemfile Gemfile.lock .ruby-version ./

Dockerfile-export

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# syntax = docker/dockerfile:1
22

33
# Make sure it matches the Ruby version in .ruby-version and Gemfile
4-
ARG RUBY_VERSION=3.3.5
4+
ARG RUBY_VERSION=3.4.7
55
FROM ruby:$RUBY_VERSION-slim as base
66

77
# Install the tools we need

config/environments/production.rb

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,5 @@
6262
# Do not dump schema after migrations.
6363
config.active_record.dump_schema_after_migration = false
6464

65-
# SQLite is good, actually
66-
config.active_record.sqlite3_production_warning = false
67-
6865
config.active_job.queue_adapter = :resque
6966
end

0 commit comments

Comments
 (0)