Skip to content

Commit c283f99

Browse files
authored
chore: Add deployment as container image (#7)
1 parent 875e630 commit c283f99

File tree

7 files changed

+529
-10
lines changed

7 files changed

+529
-10
lines changed

.dockerignore

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Git
2+
.git
3+
.gitignore
4+
.gitattributes
5+
6+
# Build artifacts
7+
dist/
8+
**/dist/
9+
# Allow CLI binaries for Docker builds
10+
!dist/github-actions-utils-cli-linux-*
11+
build/
12+
**/build/
13+
*.swp
14+
*.swo
15+
*~
16+
17+
# Node
18+
node_modules/
19+
**/node_modules/
20+
npm-debug.log*
21+
yarn-debug.log*
22+
yarn-error.log*
23+
.npm
24+
.pnpm-store
25+
26+
# macOS files
27+
**/.DS_Store
28+
29+
# Testing
30+
coverage/
31+
.nyc_output
32+
33+
# IDE
34+
.idea/
35+
.vscode/
36+
*.iml
37+
*.swp
38+
*.swo
39+
*~
40+
.DS_Store
41+
42+
# OS
43+
Thumbs.db
44+
45+
# Deployment
46+
deploy/
47+
.pulumi/
48+
49+
# Docs
50+
docs/
51+
*.md
52+
!README.md
53+
54+
# Examples and tests
55+
examples/
56+
*_test.go
57+
testdata/
58+
59+
# CI/CD
60+
.github/
61+
.gitlab-ci.yml
62+
.travis.yml
63+
64+
# Misc
65+
.env
66+
.env.*
67+
!.env.example
68+
tmp/
69+
temp/
70+
*.log
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
name: Build CLI Docker Images
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
tag_name:
7+
description: "Tag name for Docker images"
8+
required: true
9+
type: string
10+
is_prerelease:
11+
description: "Whether this is a prerelease"
12+
required: true
13+
type: string
14+
15+
jobs:
16+
build:
17+
name: Build CLI Docker Image (${{ matrix.platform.name }})
18+
runs-on: ubuntu-latest
19+
permissions:
20+
contents: read
21+
packages: write
22+
attestations: write
23+
id-token: write
24+
timeout-minutes: 30
25+
strategy:
26+
fail-fast: false
27+
matrix:
28+
platform:
29+
- name: linux/amd64
30+
tag: linux-amd64
31+
- name: linux/arm64
32+
tag: linux-arm64
33+
concurrency:
34+
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.platform.name }}
35+
cancel-in-progress: true
36+
37+
steps:
38+
- name: Checkout Repository
39+
uses: actions/checkout@v5
40+
41+
- name: Download CLI artifacts
42+
uses: actions/download-artifact@v6
43+
with:
44+
path: dist
45+
pattern: cli-${{ matrix.platform.tag }}
46+
merge-multiple: true
47+
48+
- name: List downloaded files
49+
run: ls -lh dist/
50+
51+
- name: Set up QEMU
52+
uses: docker/setup-qemu-action@v3
53+
54+
- name: Set up Docker Buildx
55+
uses: docker/setup-buildx-action@v3
56+
57+
- name: Docker meta
58+
id: meta
59+
uses: docker/metadata-action@v5
60+
with:
61+
images: |
62+
docker.io/${{ github.repository }}
63+
ghcr.io/${{ github.repository }}
64+
tags: |
65+
type=raw,value=${{ inputs.tag_name }}
66+
labels: |
67+
org.opencontainers.image.title=GitHub Actions Utils CLI
68+
org.opencontainers.image.description=MCP server for GitHub Actions utilities
69+
maintainer=techprimate GmbH <[email protected]>
70+
71+
- name: Login to Docker Hub
72+
uses: docker/login-action@v3
73+
with:
74+
username: ${{ vars.DOCKERHUB_USERNAME }}
75+
password: ${{ secrets.DOCKERHUB_TOKEN }}
76+
77+
- name: Login to GitHub Container Registry
78+
uses: docker/login-action@v3
79+
with:
80+
registry: ghcr.io
81+
username: ${{ github.actor }}
82+
password: ${{ secrets.GITHUB_TOKEN }}
83+
84+
- name: Build ${{ github.event_name == 'pull_request' && '(dry run)' || 'and push' }} by digest
85+
id: build
86+
uses: docker/build-push-action@v6
87+
with:
88+
context: .
89+
file: ./Dockerfile
90+
platforms: ${{ matrix.platform.name }}
91+
tags: ${{ steps.meta.outputs.tags }}
92+
labels: ${{ steps.meta.outputs.labels }}
93+
annotations: ${{ steps.meta.outputs.annotations }}
94+
cache-from: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache-${{ matrix.platform.tag }}
95+
cache-to: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache-${{ matrix.platform.tag }},mode=max
96+
outputs: type=image,push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }}
97+
env:
98+
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
99+
100+
- name: Export digest
101+
if: github.event_name != 'pull_request'
102+
run: |
103+
set -e
104+
mkdir -p /tmp/digests
105+
digest="${{ steps.build.outputs.digest }}"
106+
touch "/tmp/digests/${digest#sha256:}"
107+
ls -la /tmp/digests
108+
109+
- name: Upload digest
110+
if: github.event_name != 'pull_request'
111+
uses: actions/upload-artifact@v5
112+
with:
113+
name: cli-docker-digests-${{ matrix.platform.tag }}
114+
path: /tmp/digests/*
115+
if-no-files-found: error
116+
retention-days: 1
117+
118+
merge:
119+
name: Create Manifest List
120+
runs-on: ubuntu-latest
121+
needs: build
122+
if: github.event_name != 'pull_request'
123+
permissions:
124+
contents: read
125+
packages: write
126+
id-token: write
127+
timeout-minutes: 10
128+
concurrency:
129+
group: ${{ github.workflow }}-${{ github.ref }}-merge
130+
cancel-in-progress: true
131+
steps:
132+
- name: Download digests
133+
uses: actions/download-artifact@v6
134+
with:
135+
path: /tmp/digests
136+
pattern: cli-docker-digests-*
137+
merge-multiple: true
138+
139+
- name: List digests
140+
run: ls -la /tmp/digests
141+
142+
- name: Set up Docker Buildx
143+
uses: docker/setup-buildx-action@v3
144+
145+
- name: Create Docker Metadata
146+
id: meta
147+
uses: docker/metadata-action@v5
148+
with:
149+
images: |
150+
docker.io/${{ github.repository }}
151+
ghcr.io/${{ github.repository }}
152+
tags: |
153+
type=raw,value=${{ inputs.tag_name }}
154+
type=raw,value=latest,enable=${{ inputs.is_prerelease == 'true' }}
155+
type=semver,pattern={{version}},value=${{ inputs.tag_name }},enable=${{ inputs.is_prerelease == 'false' }}
156+
type=semver,pattern={{major}}.{{minor}},value=${{ inputs.tag_name }},enable=${{ inputs.is_prerelease == 'false' }}
157+
type=semver,pattern={{major}},value=${{ inputs.tag_name }},enable=${{ inputs.is_prerelease == 'false' }}
158+
159+
- name: Login to Docker Hub
160+
uses: docker/login-action@v3
161+
with:
162+
username: ${{ vars.DOCKERHUB_USERNAME }}
163+
password: ${{ secrets.DOCKERHUB_TOKEN }}
164+
165+
- name: Login to GitHub Container Registry
166+
uses: docker/login-action@v3
167+
with:
168+
registry: ghcr.io
169+
username: ${{ github.actor }}
170+
password: ${{ secrets.GITHUB_TOKEN }}
171+
172+
- name: Create manifest list and push
173+
working-directory: /tmp/digests
174+
run: |
175+
# Extract all tags
176+
image_tags=$(printf '%s' "$DOCKER_METADATA_OUTPUT_JSON" | jq -cr '.tags | map("-t " + .) | join(" ")')
177+
echo "Creating manifest list with tags: $image_tags"
178+
179+
# Extract unique image names (without tags) to build digest references
180+
image_names=$(printf '%s' "$DOCKER_METADATA_OUTPUT_JSON" | jq -cr '.tags | map(split(":")[0]) | unique | join(" ")')
181+
echo "Image names: $image_names"
182+
183+
# Build digest references for all images
184+
digest_refs=""
185+
for image_name in $image_names; do
186+
for digest_file in *; do
187+
digest_refs="$digest_refs ${image_name}@sha256:${digest_file}"
188+
done
189+
done
190+
echo "Digest references: $digest_refs"
191+
192+
echo "Creating manifest using buildx..."
193+
docker buildx imagetools create $image_tags $digest_refs
194+
195+
# Inspect the first tag for verification
196+
first_tag=$(printf '%s' "$DOCKER_METADATA_OUTPUT_JSON" | jq -cr '.tags[0]')
197+
docker buildx imagetools inspect "$first_tag"

.github/workflows/release.yml

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ on:
66
- "v*.*.*"
77
branches:
88
- main
9+
pull_request:
910
workflow_dispatch:
1011

1112
permissions:
@@ -28,8 +29,16 @@ jobs:
2829
id: metadata
2930
shell: bash
3031
run: |
32+
# Check if this is a pull request (dry run)
33+
if [ "${{ github.event_name }}" = "pull_request" ]; then
34+
echo "🔍 Pull Request detected - running in DRY RUN mode"
35+
COMMIT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
36+
VERSION="pr-${{ github.event.pull_request.number }}-${COMMIT_SHA}"
37+
IS_PRERELEASE="true"
38+
RELEASE_NAME="PR #${{ github.event.pull_request.number }} Test Build"
39+
TAG_NAME="pr-${{ github.event.pull_request.number }}"
3140
# Check if this is a tag push (release) or branch push (pre-release)
32-
if echo "${{ github.ref }}" | grep -q "^refs/tags/v"; then
41+
elif echo "${{ github.ref }}" | grep -q "^refs/tags/v"; then
3342
# Stable release from tag
3443
VERSION=$(echo "${{ github.ref }}" | sed 's|refs/tags/v||')
3544
IS_PRERELEASE="false"
@@ -58,22 +67,33 @@ jobs:
5867
echo "Release Name: $RELEASE_NAME"
5968
echo "Tag Name: $TAG_NAME"
6069
61-
build:
62-
name: Build Binaries
70+
build-cli-binaries:
71+
name: Build CLI Binaries
6372
needs: prepare
6473
uses: ./.github/workflows/build-binaries.yml
6574
with:
6675
version: ${{ needs.prepare.outputs.version }}
6776
commit_sha: ${{ needs.prepare.outputs.commit_sha }}
6877
build_date: ${{ needs.prepare.outputs.build_date }}
69-
secrets:
70-
DEVELOPER_ID_P12_BASE64: ${{ secrets.DEVELOPER_ID_P12_BASE64 }}
71-
DEVELOPER_ID_PASSWORD: ${{ secrets.DEVELOPER_ID_PASSWORD }}
72-
APPLE_NOTARIZATION_APPLE_ID_PASSWORD: ${{ secrets.APPLE_NOTARIZATION_APPLE_ID_PASSWORD }}
78+
secrets: inherit
79+
80+
build-cli-docker:
81+
name: Build CLI Docker Images
82+
needs: [prepare, build-cli-binaries]
83+
permissions:
84+
contents: read
85+
packages: write
86+
attestations: write
87+
id-token: write
88+
uses: ./.github/workflows/build-cli-docker.yml
89+
with:
90+
tag_name: ${{ needs.prepare.outputs.tag_name }}
91+
is_prerelease: ${{ needs.prepare.outputs.is_prerelease }}
92+
secrets: inherit
7393

7494
release:
7595
name: Create Release
76-
needs: [prepare, build]
96+
needs: [prepare, build-cli-binaries, build-cli-docker]
7797
runs-on: ubuntu-latest
7898
timeout-minutes: 10
7999
steps:
@@ -113,7 +133,7 @@ jobs:
113133
cat dist/release-notes.md
114134
115135
- name: Delete existing latest release (pre-release only)
116-
if: needs.prepare.outputs.is_prerelease == 'true'
136+
if: needs.prepare.outputs.is_prerelease == 'true' && github.event_name != 'pull_request'
117137
continue-on-error: true
118138
env:
119139
GH_TOKEN: ${{ github.token }}
@@ -122,6 +142,7 @@ jobs:
122142
gh release delete latest --yes --cleanup-tag || true
123143
124144
- name: Create Release
145+
if: github.event_name != 'pull_request'
125146
uses: softprops/action-gh-release@v2
126147
with:
127148
name: ${{ needs.prepare.outputs.release_name }}
@@ -136,3 +157,20 @@ jobs:
136157
dist/checksums.txt
137158
draft: false
138159
prerelease: ${{ needs.prepare.outputs.is_prerelease == 'true' }}
160+
161+
release-required-check:
162+
name: Release - Required Check
163+
needs:
164+
- prepare
165+
- build-cli-binaries
166+
- build-cli-docker
167+
- release
168+
if: always()
169+
runs-on: ubuntu-latest
170+
steps:
171+
# If any jobs we depend on fail, get cancelled, or time out, this job will fail.
172+
# Skipped jobs are not considered failures.
173+
- name: Check for failures
174+
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
175+
run: |
176+
echo "One of the release jobs has failed." && exit 1

0 commit comments

Comments
 (0)