Skip to content

Commit c327be2

Browse files
authored
feat: add ARM64 support for multi-arch Docker builds (#1)
1 parent 5193c58 commit c327be2

File tree

5 files changed

+234
-93
lines changed

5 files changed

+234
-93
lines changed

.dockerignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.git
2+
.github
3+
.dockerignore
4+
.gitignore
5+
.env*
6+
*.md
7+
LICENSE
8+
docker-compose*.yml
9+
versions.json

.github/workflows/publish.yml

Lines changed: 94 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,34 @@ on:
77

88
env:
99
REGISTRY: ghcr.io
10+
IMAGE_NAME: ${{ github.repository_owner }}/${{ github.event.repository.name }}
1011

1112
jobs:
12-
build-and-push:
13+
load-versions:
1314
runs-on: ubuntu-latest
15+
outputs:
16+
build-matrix: ${{ steps.set-matrix.outputs.build_matrix }}
17+
merge-matrix: ${{ steps.set-matrix.outputs.merge_matrix }}
18+
steps:
19+
- uses: actions/checkout@v6
20+
- id: set-matrix
21+
run: |
22+
RUNNERS='[{"runner":"ubuntu-latest","platform":"linux/amd64"},{"runner":"ubuntu-24.04-arm","platform":"linux/arm64"}]'
23+
BUILD_MATRIX=$(jq -c --argjson runners "$RUNNERS" '{include: [.[] | . as $v | $runners[] | . + $v]}' versions.json)
24+
echo "build_matrix=$BUILD_MATRIX" >> "$GITHUB_OUTPUT"
25+
echo "merge_matrix=$(jq -c '{include: .}' versions.json)" >> "$GITHUB_OUTPUT"
26+
27+
build:
28+
needs: load-versions
29+
runs-on: ${{ matrix.runner }}
30+
timeout-minutes: 30
1431
permissions:
1532
contents: read
1633
packages: write
1734

1835
strategy:
19-
matrix:
20-
include:
21-
- pg_version: "17.9"
22-
postgis_version: "3.6.2"
23-
pgvector_version: "0.8.2"
24-
latest: true
25-
- pg_version: "16.13"
26-
postgis_version: "3.6.2"
27-
pgvector_version: "0.8.2"
28-
latest: false
29-
- pg_version: "15.17"
30-
postgis_version: "3.6.2"
31-
pgvector_version: "0.8.2"
32-
latest: false
33-
- pg_version: "14.22"
34-
postgis_version: "3.6.2"
35-
pgvector_version: "0.8.2"
36-
latest: false
36+
fail-fast: false
37+
matrix: ${{ fromJSON(needs.load-versions.outputs.build-matrix) }}
3738

3839
steps:
3940
- name: Checkout repository
@@ -53,22 +54,86 @@ jobs:
5354
id: meta
5455
uses: docker/metadata-action@v6
5556
with:
56-
images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ github.event.repository.name }}
57-
tags: |
58-
type=raw,value=postgres-${{ matrix.pg_version }}-postgis-${{ matrix.postgis_version }}-pgvector-${{ matrix.pgvector_version }}
59-
type=raw,value=latest,enable=${{ matrix.latest }}
60-
type=ref,event=tag
57+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
6158

62-
- name: Build and push Docker image
59+
- name: Build and push by digest
60+
id: build
6361
uses: docker/build-push-action@v7
6462
with:
6563
context: .
66-
push: true
67-
tags: ${{ steps.meta.outputs.tags }}
64+
platforms: ${{ matrix.platform }}
65+
provenance: false
66+
sbom: false
6867
labels: ${{ steps.meta.outputs.labels }}
6968
build-args: |
7069
PG_VERSION=${{ matrix.pg_version }}
7170
POSTGIS_VERSION=${{ matrix.postgis_version }}
7271
PGVECTOR_VERSION=${{ matrix.pgvector_version }}
73-
cache-from: type=gha
74-
cache-to: type=gha,mode=max
72+
cache-from: type=gha,scope=${{ matrix.pg_version }}-${{ matrix.runner }}
73+
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
74+
75+
- name: Export digest
76+
env:
77+
DIGEST: ${{ steps.build.outputs.digest }}
78+
run: |
79+
mkdir -p /tmp/digests
80+
touch "/tmp/digests/${DIGEST#sha256:}"
81+
82+
- name: Upload digest
83+
uses: actions/upload-artifact@v4
84+
with:
85+
name: digest-${{ matrix.pg_version }}-${{ matrix.runner }}
86+
path: /tmp/digests/*
87+
if-no-files-found: error
88+
retention-days: 1
89+
90+
merge:
91+
runs-on: ubuntu-latest
92+
timeout-minutes: 10
93+
needs: [load-versions, build]
94+
permissions:
95+
contents: read
96+
packages: write
97+
98+
strategy:
99+
fail-fast: false
100+
matrix: ${{ fromJSON(needs.load-versions.outputs.merge-matrix) }}
101+
102+
steps:
103+
- name: Download digests
104+
uses: actions/download-artifact@v4
105+
with:
106+
pattern: digest-${{ matrix.pg_version }}-*
107+
merge-multiple: true
108+
path: /tmp/digests
109+
110+
- name: Log in to GitHub Container Registry
111+
uses: docker/login-action@v4
112+
with:
113+
registry: ${{ env.REGISTRY }}
114+
username: ${{ github.actor }}
115+
password: ${{ secrets.GITHUB_TOKEN }}
116+
117+
- name: Extract metadata (tags, labels) for Docker
118+
id: meta
119+
uses: docker/metadata-action@v6
120+
with:
121+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
122+
tags: |
123+
type=raw,value=postgres-${{ matrix.pg_version }}-postgis-${{ matrix.postgis_version }}-pgvector-${{ matrix.pgvector_version }}
124+
type=raw,value=latest,enable=${{ matrix.latest }}
125+
type=ref,event=tag,enable=${{ matrix.latest }}
126+
127+
- name: Create multi-arch manifest and push
128+
working-directory: /tmp/digests
129+
env:
130+
REGISTRY: ${{ env.REGISTRY }}
131+
IMAGE: ${{ env.IMAGE_NAME }}
132+
run: |
133+
docker buildx imagetools create \
134+
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
135+
$(printf "${REGISTRY}/${IMAGE}@sha256:%s " *)
136+
137+
- name: Verify multi-arch manifest
138+
run: |
139+
docker buildx imagetools inspect $(jq -cr '.tags[0]' <<< "$DOCKER_METADATA_OUTPUT_JSON")

.github/workflows/test.yml

Lines changed: 72 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,28 @@ on:
77
branches: [main]
88
workflow_dispatch:
99

10+
permissions:
11+
contents: read
12+
1013
jobs:
11-
test:
14+
load-versions:
1215
runs-on: ubuntu-latest
16+
outputs:
17+
matrix: ${{ steps.set-matrix.outputs.matrix }}
18+
steps:
19+
- uses: actions/checkout@v6
20+
- id: set-matrix
21+
run: |
22+
MATRIX=$(jq -c '{include: [.[] | . as $v | {runner: "ubuntu-latest"}, {runner: "ubuntu-24.04-arm"} | . + $v]}' versions.json)
23+
echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT"
24+
25+
test:
26+
needs: load-versions
27+
runs-on: ${{ matrix.runner }}
28+
timeout-minutes: 15
1329
strategy:
14-
matrix:
15-
include:
16-
- pg_version: "17.9"
17-
postgis_version: "3.6.2"
18-
pgvector_version: "0.8.2"
19-
- pg_version: "16.13"
20-
postgis_version: "3.6.2"
21-
pgvector_version: "0.8.2"
22-
- pg_version: "15.17"
23-
postgis_version: "3.6.2"
24-
pgvector_version: "0.8.2"
25-
- pg_version: "14.22"
26-
postgis_version: "3.6.2"
27-
pgvector_version: "0.8.2"
30+
fail-fast: false
31+
matrix: ${{ fromJSON(needs.load-versions.outputs.matrix) }}
2832

2933
steps:
3034
- uses: actions/checkout@v6
@@ -42,36 +46,76 @@ jobs:
4246
PG_VERSION=${{ matrix.pg_version }}
4347
POSTGIS_VERSION=${{ matrix.postgis_version }}
4448
PGVECTOR_VERSION=${{ matrix.pgvector_version }}
49+
cache-from: type=gha,scope=${{ matrix.pg_version }}-${{ matrix.runner }}
50+
cache-to: type=gha,mode=max,scope=${{ matrix.pg_version }}-${{ matrix.runner }}
4551

46-
- name: Start PostgreSQL container and test extensions
52+
- name: Start PostgreSQL container
53+
env:
54+
PG_VERSION: ${{ matrix.pg_version }}
55+
RUNNER: ${{ matrix.runner }}
4756
run: |
48-
IMAGE_TAG="postgres-test:pg${{ matrix.pg_version }}"
49-
echo "Testing image: $IMAGE_TAG"
57+
IMAGE_TAG="postgres-test:pg${PG_VERSION}"
58+
echo "Testing image: $IMAGE_TAG (runner: $RUNNER)"
5059
5160
docker run -d --name test-db \
5261
-e POSTGRES_PASSWORD=test \
5362
-e POSTGRES_USER=test \
5463
-e POSTGRES_DB=test \
55-
$IMAGE_TAG \
64+
--health-cmd="pg_isready -U test -d test" \
65+
--health-interval=2s \
66+
--health-timeout=5s \
67+
--health-retries=30 \
68+
"$IMAGE_TAG" \
5669
postgres -c shared_preload_libraries=vector
5770
58-
echo "Waiting for PostgreSQL to start..."
59-
sleep 15
60-
61-
echo "PostgreSQL logs:"
62-
docker logs test-db
71+
echo "Waiting for PostgreSQL to become healthy..."
72+
until [ "$(docker inspect -f '{{.State.Health.Status}}' test-db)" = "healthy" ]; do
73+
if [ "$(docker inspect -f '{{.State.Status}}' test-db)" != "running" ]; then
74+
echo "Container exited unexpectedly!"
75+
docker logs test-db
76+
exit 1
77+
fi
78+
sleep 1
79+
done
80+
echo "PostgreSQL is ready."
6381
82+
- name: Test PostGIS extension
83+
run: |
6484
echo "Checking PostGIS version..."
6585
docker exec test-db psql -U test -d test -c "SELECT postgis_full_version();"
6686
87+
echo "Testing PostGIS spatial functionality..."
88+
docker exec test-db psql -U test -d test -c "SELECT ST_AsText(ST_Point(1, 2));"
89+
90+
- name: Test pgvector extension
91+
run: |
6792
echo "Checking pgvector extension version..."
6893
docker exec test-db psql -U test -d test -c "CREATE EXTENSION IF NOT EXISTS vector; SELECT extversion FROM pg_extension WHERE extname = 'vector';"
6994
70-
echo "Attempting to create a table with a vector column and insert data..."
95+
echo "Testing vector operations..."
7196
docker exec test-db psql -U test -d test -c "CREATE TABLE items (id bigserial PRIMARY KEY, embedding vector(3)); INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]'); SELECT COUNT(*) FROM items;"
7297
98+
- name: Verify installed versions match requested
99+
env:
100+
EXPECTED_PGVECTOR: ${{ matrix.pgvector_version }}
101+
EXPECTED_POSTGIS: ${{ matrix.postgis_version }}
102+
run: |
103+
ACTUAL_PGVECTOR=$(docker exec test-db psql -U test -d test -tAc \
104+
"SELECT extversion FROM pg_extension WHERE extname = 'vector';")
105+
if [ "$ACTUAL_PGVECTOR" != "$EXPECTED_PGVECTOR" ]; then
106+
echo "FAIL: expected pgvector $EXPECTED_PGVECTOR, got $ACTUAL_PGVECTOR"
107+
exit 1
108+
fi
109+
echo "pgvector version OK: $ACTUAL_PGVECTOR"
110+
111+
ACTUAL_POSTGIS=$(docker exec test-db psql -U test -d test -tAc \
112+
"SELECT extversion FROM pg_extension WHERE extname = 'postgis';")
113+
if [ "$ACTUAL_POSTGIS" != "$EXPECTED_POSTGIS" ]; then
114+
echo "FAIL: expected PostGIS $EXPECTED_POSTGIS, got $ACTUAL_POSTGIS"
115+
exit 1
116+
fi
117+
echo "PostGIS version OK: $ACTUAL_POSTGIS"
118+
73119
- name: Stop and remove container
74120
if: always()
75-
run: |
76-
docker stop test-db || true
77-
docker rm test-db || true
121+
run: docker rm -f test-db || true

Dockerfile

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,54 +13,51 @@ LABEL maintainer="TypeORM"
1313
LABEL description="PostgreSQL with PostGIS and pgvector extensions for TypeORM"
1414
LABEL org.opencontainers.image.source="https://github.com/typeorm/docker"
1515

16-
# Install base dependencies, setup PGDG repository, and install build tools
16+
# Install PostGIS, build pgvector from source, then clean up in a single layer
1717
# Note: PG_MAJOR is provided by the official postgres base image
18-
RUN apt-get update \
18+
RUN set -eux \
19+
&& apt-get update \
1920
&& apt-get install -y --no-install-recommends \
20-
lsb-release \
21-
gnupg \
22-
ca-certificates \
23-
wget \
21+
lsb-release \
22+
gnupg \
23+
ca-certificates \
24+
wget \
2425
&& wget --quiet -O /usr/share/keyrings/postgresql-archive-keyring.gpg https://www.postgresql.org/media/keys/ACCC4CF8.asc \
2526
&& sh -c 'echo "deb [signed-by=/usr/share/keyrings/postgresql-archive-keyring.gpg] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' \
2627
&& apt-get update \
2728
&& apt-get install -y --no-install-recommends \
28-
build-essential \
29-
git \
30-
make \
31-
gcc \
32-
"postgresql-server-dev-${PG_MAJOR}"
33-
34-
# Install pinned PostGIS version (apt packages use major version in name)
35-
RUN POSTGIS_MAJOR=$(echo "${POSTGIS_VERSION}" | cut -d. -f1) \
36-
&& apt-get update \
29+
build-essential \
30+
git \
31+
make \
32+
gcc \
33+
"postgresql-server-dev-${PG_MAJOR}" \
34+
&& POSTGIS_MAJOR=$(echo "${POSTGIS_VERSION}" | cut -d. -f1) \
3735
&& apt-get install -y --no-install-recommends \
38-
"postgis=${POSTGIS_VERSION}+dfsg*" \
39-
"postgresql-${PG_MAJOR}-postgis-${POSTGIS_MAJOR}=${POSTGIS_VERSION}+dfsg*" \
40-
"postgresql-${PG_MAJOR}-postgis-${POSTGIS_MAJOR}-scripts=${POSTGIS_VERSION}+dfsg*"
41-
42-
# Build and install pinned pgvector version from source
43-
RUN apt-get update \
44-
&& apt-get install -y --no-install-recommends git make gcc "postgresql-server-dev-${PG_MAJOR}" \
45-
&& mkdir -p /usr/src/pgvector \
46-
&& git clone --branch "v${PGVECTOR_VERSION}" https://github.com/pgvector/pgvector.git /usr/src/pgvector \
36+
"postgis=${POSTGIS_VERSION}+dfsg*" \
37+
"postgresql-${PG_MAJOR}-postgis-${POSTGIS_MAJOR}=${POSTGIS_VERSION}+dfsg*" \
38+
"postgresql-${PG_MAJOR}-postgis-${POSTGIS_MAJOR}-scripts=${POSTGIS_VERSION}+dfsg*" \
39+
&& git clone --branch "v${PGVECTOR_VERSION}" --depth 1 https://github.com/pgvector/pgvector.git /usr/src/pgvector \
4740
&& cd /usr/src/pgvector \
4841
&& make \
49-
&& make install
50-
51-
# Cleanup build dependencies
52-
RUN apt-get purge -y --auto-remove \
53-
build-essential \
54-
git \
55-
make \
56-
gcc \
57-
"postgresql-server-dev-${PG_MAJOR}" \
58-
wget \
42+
&& make install \
43+
&& apt-get purge -y --auto-remove \
44+
build-essential \
45+
git \
46+
make \
47+
gcc \
48+
"postgresql-server-dev-${PG_MAJOR}" \
49+
wget \
50+
lsb-release \
51+
gnupg \
5952
&& apt-get clean \
60-
&& rm -rf /var/lib/apt/lists/* \
61-
&& rm -rf /usr/src/pgvector
53+
&& rm -rf /var/lib/apt/lists/* /usr/src/pgvector \
54+
/etc/apt/sources.list.d/pgdg.list \
55+
/usr/share/keyrings/postgresql-archive-keyring.gpg
6256

6357
# Copy initialization scripts
6458
COPY docker-entrypoint-initdb.d/ /docker-entrypoint-initdb.d/
6559

60+
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
61+
CMD pg_isready -U postgres
62+
6663
EXPOSE 5432

0 commit comments

Comments
 (0)