Skip to content

Commit 044824e

Browse files
authored
[#100] Added Docker image with multi-arch CI build, test, and Docker Hub publish. (#334)
1 parent c9e8b38 commit 044824e

5 files changed

Lines changed: 268 additions & 0 deletions

File tree

.dockerignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.artifacts
2+
.build
3+
.circleci
4+
.claude
5+
.git
6+
.github
7+
.idea
8+
.logs
9+
tests
10+
vendor
11+
vendor-bin
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Release Docker
2+
3+
on:
4+
push:
5+
tags:
6+
- '*'
7+
8+
permissions:
9+
contents: read
10+
11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.ref }}
13+
cancel-in-progress: false
14+
15+
jobs:
16+
push-release-to-registry:
17+
runs-on: ubuntu-latest
18+
19+
steps:
20+
- name: Checkout code
21+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
22+
with:
23+
persist-credentials: false
24+
25+
- name: Set up QEMU
26+
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4
27+
28+
- name: Set up Docker Buildx
29+
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
30+
31+
- name: Log in to Docker Hub
32+
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
33+
with:
34+
username: ${{ secrets.DOCKER_USER }}
35+
password: ${{ secrets.DOCKER_PASS }}
36+
37+
- name: Extract metadata (tags, labels) for Docker
38+
id: meta
39+
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
40+
with:
41+
images: drevops/git-artifact
42+
43+
- name: Build and push Docker image
44+
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
45+
with:
46+
context: .
47+
push: true
48+
tags: ${{ steps.meta.outputs.tags }}
49+
labels: ${{ steps.meta.outputs.labels }}
50+
platforms: linux/amd64,linux/arm64

.github/workflows/test-docker.yml

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
name: Test Docker
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
- 'feature/**'
11+
12+
permissions:
13+
contents: read
14+
15+
concurrency:
16+
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
test-docker:
21+
runs-on: ubuntu-latest
22+
23+
steps:
24+
- name: Checkout code
25+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
26+
with:
27+
persist-credentials: false
28+
29+
- name: Lint Dockerfile
30+
uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5 # v3.3.0
31+
32+
- name: Build Docker image
33+
run: docker build -t "drevops/git-artifact:test" .
34+
35+
- name: Test that the image packages and pushes an artifact
36+
run: |
37+
set -ex
38+
39+
src="${RUNNER_TEMP}/src"
40+
dst="${RUNNER_TEMP}/dst.git"
41+
42+
# Prepare a source repository with a tracked file.
43+
mkdir -p "${src}"
44+
git -C "${src}" init -b main
45+
git -C "${src}" config user.name "Test"
46+
git -C "${src}" config user.email "test@example.com"
47+
echo "artifact content" > "${src}/file.txt"
48+
git -C "${src}" add -A
49+
git -C "${src}" commit -m "Initial commit"
50+
51+
# Prepare a bare destination repository to receive the artifact.
52+
git init --bare -b main "${dst}"
53+
54+
# Package and push the artifact from inside the container. The source
55+
# is mounted at the WORKDIR and the destination is mounted as a local
56+
# remote. The commit identity is supplied via the environment (the
57+
# image trusts mounted repositories via a baked-in safe.directory).
58+
docker run --rm \
59+
-e GIT_AUTHOR_NAME="Test" \
60+
-e GIT_AUTHOR_EMAIL="test@example.com" \
61+
-e GIT_COMMITTER_NAME="Test" \
62+
-e GIT_COMMITTER_EMAIL="test@example.com" \
63+
-v "${src}":/app \
64+
-v "${dst}":/dst.git \
65+
drevops/git-artifact:test \
66+
/dst.git --branch=artifact --mode=force-push -vvv
67+
68+
# Assert the file landed on the destination branch.
69+
git -C "${dst}" show artifact:file.txt | grep -q "artifact content"
70+
71+
push-canary-to-registry:
72+
if: github.event_name == 'push'
73+
runs-on: ubuntu-latest
74+
75+
needs:
76+
- test-docker
77+
78+
steps:
79+
- name: Checkout code
80+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
81+
with:
82+
persist-credentials: false
83+
84+
- name: Set up QEMU
85+
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4
86+
87+
- name: Set up Docker Buildx
88+
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
89+
90+
- name: Log in to Docker Hub
91+
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
92+
with:
93+
username: ${{ secrets.DOCKER_USER }}
94+
password: ${{ secrets.DOCKER_PASS }}
95+
96+
- name: Extract metadata (tags, labels) for Docker
97+
id: meta
98+
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6
99+
with:
100+
images: drevops/git-artifact
101+
102+
- name: Build and push Docker image
103+
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
104+
with:
105+
context: .
106+
push: true
107+
tags: drevops/git-artifact:canary
108+
labels: ${{ steps.meta.outputs.labels }}
109+
platforms: linux/amd64,linux/arm64

Dockerfile

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
FROM php:8.5-cli@sha256:1954ff5cd21f222c992b79d25e403b2600cec829678d5bb7076883f3a44c0d6e AS builder
2+
3+
# hadolint ignore=DL3008
4+
RUN apt-get update && \
5+
apt-get install --no-install-recommends -y libzip-dev && \
6+
apt-get clean && \
7+
rm -rf /var/lib/apt/lists/*
8+
9+
RUN docker-php-ext-install zip
10+
11+
# Install composer.
12+
# @see https://getcomposer.org/download
13+
# renovate: datasource=github-releases depName=composer/composer extractVersion=^(?<version>.*)$
14+
ENV COMPOSER_ALLOW_SUPERUSER=1
15+
# hadolint ignore=DL4006
16+
RUN version=2.8.10 && \
17+
curl -sS https://getcomposer.org/download/${version}/composer.phar.sha256sum | awk '{ print $1, "composer.phar" }' > composer.phar.sha256sum && \
18+
curl -sS -o composer.phar https://getcomposer.org/download/${version}/composer.phar && \
19+
sha256sum -c composer.phar.sha256sum && \
20+
chmod +x composer.phar && \
21+
mv composer.phar /usr/local/bin/composer && \
22+
rm composer.phar.sha256sum && \
23+
composer --version && \
24+
composer clear-cache
25+
26+
WORKDIR /app
27+
28+
COPY composer.json composer.lock /app/
29+
30+
RUN COMPOSER_MEMORY_LIMIT=-1 composer install -n --ansi --prefer-dist --optimize-autoloader
31+
32+
COPY . /app
33+
34+
RUN composer build
35+
36+
FROM php:8.5-cli@sha256:1954ff5cd21f222c992b79d25e403b2600cec829678d5bb7076883f3a44c0d6e
37+
38+
# git is required because the tool shells out to the git binary; openssh-client
39+
# enables pushing to SSH remotes such as git@github.com:org/repo.git.
40+
# hadolint ignore=DL3008
41+
RUN apt-get update && \
42+
apt-get install --no-install-recommends -y git openssh-client && \
43+
apt-get clean && \
44+
rm -rf /var/lib/apt/lists/*
45+
46+
# The container operates on repositories bind-mounted from the host at runtime -
47+
# a source and a destination whose paths are chosen by the caller and unknown at
48+
# build time - while running as root. Trust all directories so git does not
49+
# reject these host-owned mounts for dubious ownership; the container is
50+
# single-purpose and ephemeral, so the wildcard is an acceptable trust boundary.
51+
RUN git config --system --add safe.directory '*'
52+
53+
WORKDIR /app
54+
55+
COPY --from=builder /app/.build/git-artifact /usr/local/bin/git-artifact
56+
57+
RUN chmod +x /usr/local/bin/git-artifact
58+
59+
ENTRYPOINT ["/usr/local/bin/git-artifact"]

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@
1212
![GitHub release (latest by date)](https://img.shields.io/github/v/release/drevops/git-artifact)
1313
[![codecov](https://codecov.io/gh/drevops/git-artifact/branch/main/graph/badge.svg?token=QNBXCIBK5J)](https://codecov.io/gh/drevops/git-artifact)
1414
[![Total Downloads](https://poser.pugx.org/drevops/behat-screenshot/downloads)](https://packagist.org/packages/drevops/git-artifact)
15+
[![Docker Pulls](https://img.shields.io/docker/pulls/drevops/git-artifact?logo=docker)](https://hub.docker.com/r/drevops/git-artifact)
16+
![amd64](https://img.shields.io/badge/arch-linux%2Famd64-brightgreen)
17+
![arm64](https://img.shields.io/badge/arch-linux%2Farm64-brightgreen)
1518
![LICENSE](https://img.shields.io/github/license/drevops/git-artifact)
1619
![Renovate](https://img.shields.io/badge/renovate-enabled-green?logo=renovatebot)
1720

1821
[![Test PHP](https://github.com/drevops/git-artifact/actions/workflows/test-php.yml/badge.svg)](https://github.com/drevops/git-artifact/actions/workflows/test-php.yml)
22+
[![Test Docker](https://github.com/drevops/git-artifact/actions/workflows/test-docker.yml/badge.svg)](https://github.com/drevops/git-artifact/actions/workflows/test-docker.yml)
1923
[![CircleCI](https://circleci.com/gh/drevops/git-artifact.svg?style=shield)](https://circleci.com/gh/drevops/git-artifact)
2024

2125
[![Vortex Ecosystem](https://img.shields.io/badge/%F0%9F%8C%80-Vortex%20Ecosystem-2C5A68?style=for-the-badge&labelColor=65ACBC)](https://github.com/drevops/vortex)
@@ -173,6 +177,41 @@ have PHP installed on your system to run the binary.
173177

174178
Download the latest release from the [GitHub releases page](https://github.com/drevops/git-artifact/releases/latest).
175179

180+
### As a Docker container
181+
182+
The tool is also published as a multi-architecture (`linux/amd64`, `linux/arm64`) Docker image at [`drevops/git-artifact`](https://hub.docker.com/r/drevops/git-artifact), with `git` and an SSH client bundled in - no local PHP required. Mount your source repository at `/app` and pass the same arguments you would pass to the binary:
183+
184+
```shell
185+
docker run --rm \
186+
-v "${PWD}":/app \
187+
-e GIT_AUTHOR_NAME="Deployer" -e GIT_AUTHOR_EMAIL="deployer@example.com" \
188+
-e GIT_COMMITTER_NAME="Deployer" -e GIT_COMMITTER_EMAIL="deployer@example.com" \
189+
drevops/git-artifact \
190+
https://github.com/yourorg/your-repo-destination.git --branch=main
191+
```
192+
193+
The `GIT_AUTHOR_*` and `GIT_COMMITTER_*` variables set the identity for the deployment commit. To push to an SSH remote, also mount your SSH credentials read-only:
194+
195+
```shell
196+
docker run --rm \
197+
-v "${PWD}":/app \
198+
-v "${HOME}/.ssh":/root/.ssh:ro \
199+
-e GIT_AUTHOR_NAME="Deployer" -e GIT_AUTHOR_EMAIL="deployer@example.com" \
200+
-e GIT_COMMITTER_NAME="Deployer" -e GIT_COMMITTER_EMAIL="deployer@example.com" \
201+
drevops/git-artifact \
202+
git@github.com:yourorg/your-repo-destination.git --branch=main
203+
```
204+
205+
#### Image tags
206+
207+
Cross-platform (`linux/amd64`, `linux/arm64`) images are built by GitHub Actions and pushed to [Docker Hub](https://hub.docker.com/r/drevops/git-artifact):
208+
209+
- `<version>` (e.g. `1.2.3`) - published when a release tag is created on GitHub.
210+
- `latest` - published when a release tag is created on GitHub.
211+
- `canary` - published on every push to the `main` branch (latest unreleased changes).
212+
213+
Pin to a specific `<version>` tag for reproducible deployments and use `canary` only to try out unreleased changes.
214+
176215
### As a Composer dependency
177216

178217
You may also install this tool globally using Composer:

0 commit comments

Comments
 (0)