Skip to content

Commit b63bfa9

Browse files
committed
Re-implement ci-cd.yml using custom local actions
1 parent 4de7ebf commit b63bfa9

File tree

7 files changed

+427
-102
lines changed

7 files changed

+427
-102
lines changed
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
name: "Build & Upload .tar Image"
2+
description: "Builds image, then uploads it as a workflow artefact to be shared across other jobs"
3+
inputs:
4+
build-add-hosts:
5+
description: "List of a customs host-to-IP mapping (e.g., docker:10.180.0.1)"
6+
required: false
7+
build-allow:
8+
description: "List of extra privileged entitlement (e.g., network.host,security.insecure)"
9+
required: false
10+
build-attests:
11+
description: "List of attestation parameters (e.g., type=sbom,generator=image)"
12+
required: false
13+
build-build-args:
14+
description: "List of build-time variables"
15+
required: false
16+
build-build-contexts:
17+
description: "List of additional build contexts (e.g., name=path)"
18+
required: false
19+
build-builder:
20+
description: "Builder instance"
21+
required: false
22+
build-cache-from:
23+
description: "List of external cache sources for buildx (e.g., user/app:cache, type=local,src=path/to/dir)"
24+
required: false
25+
build-cache-to:
26+
description: "List of cache export destinations for buildx (e.g., user/app:cache, type=local,dest=path/to/dir)"
27+
required: false
28+
build-cgroup-parent:
29+
description: "Optional parent cgroup for the container used in the build"
30+
required: false
31+
build-context:
32+
description: "Build's context is the set of files located in the specified PATH or URL"
33+
required: false
34+
build-file:
35+
description: "Path to the Dockerfile"
36+
required: false
37+
#build-labels:
38+
# description: "List of metadata for an image"
39+
# required: false
40+
build-load:
41+
description: "Load is a shorthand for --output=type=docker"
42+
required: false
43+
default: 'false'
44+
build-network:
45+
description: "Set the networking mode for the RUN instructions during build"
46+
required: false
47+
build-no-cache:
48+
description: "Do not use cache when building the image"
49+
required: false
50+
default: 'false'
51+
build-no-cache-filters:
52+
description: "Do not cache specified stages"
53+
required: false
54+
#build-outputs:
55+
# description: "List of output destinations (format: type=local,dest=path)"
56+
# required: false
57+
build-platforms:
58+
description: "List of target platforms for build"
59+
required: false
60+
build-provenance:
61+
description: "Generate provenance attestation for the build (shorthand for --attest=type=provenance)"
62+
required: false
63+
build-pull:
64+
description: "Always attempt to pull all referenced images"
65+
required: false
66+
default: 'false'
67+
#build-push:
68+
# description: "Push is a shorthand for --output=type=registry"
69+
# required: false
70+
# default: 'false'
71+
build-sbom:
72+
description: "Generate SBOM attestation for the build (shorthand for --attest=type=sbom)"
73+
required: false
74+
build-secrets:
75+
description: "List of secrets to expose to the build (e.g., key=string, GIT_AUTH_TOKEN=mytoken)"
76+
required: false
77+
build-secret-files:
78+
description: "List of secret files to expose to the build (e.g., key=filename, MY_SECRET=./secret.txt)"
79+
required: false
80+
build-shm-size:
81+
description: "Size of /dev/shm (e.g., 2g)"
82+
required: false
83+
build-ssh:
84+
description: "List of SSH agent socket or keys to expose to the build"
85+
required: false
86+
#build-tags:
87+
# description: "List of tags"
88+
# required: false
89+
build-target:
90+
description: "Sets the target stage to build"
91+
required: false
92+
build-ulimit:
93+
description: "Ulimit options (e.g., nofile=1024:1024)"
94+
required: false
95+
build-github-token:
96+
description: "GitHub Token used to authenticate against a repository for Git context"
97+
default: ${{ github.token }}
98+
required: false
99+
upload-name:
100+
default: ${{ github.event.repository.name }}
101+
upload-tag:
102+
default: ${{ github.run_id }}
103+
outputs:
104+
image-name:
105+
description: "The container image that got downloaded and made available. Effectively <download-name>:<download-tag>. Also available via `env.IMAGE_NAME`"
106+
value: ""
107+
runs:
108+
using: "composite"
109+
steps:
110+
- uses: docker/setup-buildx-action@v3
111+
- uses: docker/metadata-action@v5
112+
id: meta
113+
with:
114+
images: ${{ inputs.upload-name }}:${{ inputs.upload-tag }}
115+
- uses: actions/cache@v3
116+
with:
117+
path: /tmp/.buildx-cache
118+
key: ${{ runner.os }}-buildx
119+
restore-keys: |
120+
${{ runner.os }}-buildx-
121+
- name: Build image
122+
uses: docker/build-push-action@v5
123+
with:
124+
add-hosts: ${{ inputs.build-add-hosts }}
125+
allow: ${{ inputs.build-allow }}
126+
attests: ${{ inputs.build-attests }}
127+
build-args: ${{ inputs.build-build-args }}
128+
build-contexts: ${{ inputs.build-build-contexts }}
129+
builder: ${{ inputs.build-builder }}
130+
cache-from: ${{ inputs.build-cache-from }}
131+
cache-to: ${{ inputs.build-cache-to }}
132+
cgroup-parent: ${{ inputs.build-cgroup-parent }}
133+
context: ${{ inputs.build-context }}
134+
file: ${{ inputs.build-file }}
135+
labels: ${{ steps.meta.outputs.labels }}
136+
load: ${{ inputs.build-load }}
137+
network: ${{ inputs.build-network }}
138+
no-cache: ${{ inputs.build-no-cache }}
139+
no-cache-filters: ${{ inputs.build-no-cache-filters }}
140+
outputs: type=docker,dest=/tmp/${{ inputs.upload-name }}.tar
141+
platforms: ${{ inputs.build-platforms }}
142+
provenance: ${{ inputs.build-provenance }}
143+
pull: ${{ inputs.build-pull }}
144+
push: false
145+
sbom: ${{ inputs.build-sbom }}
146+
secrets: ${{ inputs.build-secrets }}
147+
#secret-envs: ${{ inputs.build-secret-envs }}
148+
secret-files: ${{ inputs.build-secret-files }}
149+
shm-size: ${{ inputs.build-shm-size }}
150+
ssh: ${{ inputs.build-ssh }}
151+
tags: ${{ inputs.upload-name }}:${{ inputs.upload-tag }}
152+
target: ${{ inputs.build-target }}
153+
ulimit: ${{ inputs.build-ulimit }}
154+
github-token: ${{ inputs.build-github-token }}
155+
- uses: actions/upload-artifact@v3
156+
with:
157+
name: ${{ inputs.upload-name }}
158+
path: /tmp/${{ inputs.upload-name }}.tar
159+
- name: Set outputs
160+
run: |
161+
set -euo pipefail
162+
163+
debug_log() {
164+
if [ "${RUNNER_DEBUG:-0}" = "1" ]; then
165+
echo "##[debug]$1"
166+
fi
167+
}
168+
169+
set_output() {
170+
local value="$1"
171+
local output_key="$2"
172+
local env_key="${3:-}"
173+
174+
if [ -z "$value" ] || [ -z "$output_key" ]; then
175+
echo "Missing essential arguments to set_output_and_env"
176+
return 1
177+
fi
178+
179+
# If env_key is empty, convert output_key to upper snake case
180+
if [ -z "$env_key" ]; then
181+
env_key=$(echo "$output_key" | awk '{print toupper($0)}' | sed 's/-/_/g')
182+
fi
183+
184+
echo "$output_key=$value" >> "$GITHUB_OUTPUT"
185+
echo "$env_key=$value" >> "$GITHUB_ENV"
186+
}
187+
188+
image_value="${{ inputs.upload-name }}:${{ inputs.upload-tag }}"
189+
set_output "$image_value" "image-name"
190+
debug_log "GitHub Output: $(cat "$GITHUB_OUTPUT")\nGitHub Env: $(cat "$GITHUB_ENV")"
191+
shell: bash
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: "Download & Load .tar Image"
2+
description: "Download image from build-tar-image action and make it available for testing, pushing, etc."
3+
inputs:
4+
download-name:
5+
default: ${{ github.event.repository.name }}
6+
download-tag:
7+
default: ${{ github.run_id }}
8+
outputs:
9+
image-name:
10+
description: "The container image that got downloaded and made available. Effectively <download-name>:<download-tag>. Also available via `env.IMAGE_NAME`"
11+
value: ""
12+
runs:
13+
using: "composite"
14+
steps:
15+
- name: Download artifact
16+
uses: actions/download-artifact@v3
17+
with:
18+
name: ${{ inputs.download-name }}
19+
path: /tmp
20+
- name: Load image
21+
run: docker load --input /tmp/${{ inputs.download-name }}.tar
22+
shell: bash
23+
- name: Set outputs
24+
run: |
25+
set -euo pipefail
26+
27+
debug_log() {
28+
if [ "${RUNNER_DEBUG:-0}" = "1" ]; then
29+
echo "##[debug]$1"
30+
fi
31+
}
32+
33+
set_output() {
34+
local value="$1"
35+
local output_key="$2"
36+
local env_key="${3:-}"
37+
38+
if [ -z "$value" ] || [ -z "$output_key" ]; then
39+
echo "Missing essential arguments to set_output_and_env"
40+
return 1
41+
fi
42+
43+
# If env_key is empty, convert output_key to upper snake case
44+
if [ -z "$env_key" ]; then
45+
env_key=$(echo "$output_key" | awk '{print toupper($0)}' | sed 's/-/_/g')
46+
fi
47+
48+
echo "$output_key=$value" >> "$GITHUB_OUTPUT"
49+
echo "$env_key=$value" >> "$GITHUB_ENV"
50+
}
51+
52+
image_value="${{ inputs.download-name }}:${{ inputs.download-tag }}"
53+
set_output "$image_value" "image-name"
54+
debug_log "GitHub Output: $(cat "$GITHUB_OUTPUT")\nGitHub Env: $(cat "$GITHUB_ENV")"
55+
shell: bash
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: "Tag and Push Image"
2+
description: "Log in to Docker registry, generate metadata, re-tag the source image, and push it"
3+
inputs:
4+
source-image:
5+
description: "Name of the image to re-tag and push"
6+
required: true
7+
semver:
8+
description: "Semantic version"
9+
required: true
10+
exists:
11+
description: "Does the version tag exist?"
12+
required: true
13+
push:
14+
description: "If false runs a dry-run that doesn't actually push"
15+
default: "true"
16+
runs:
17+
using: "composite"
18+
steps:
19+
- name: Generate Docker Metadata
20+
uses: docker/metadata-action@v5
21+
id: meta
22+
with:
23+
images: ${{ github.repository }}
24+
tags: |
25+
type=ref,event=branch,prefix=branch-,enable=${{ github.ref != format('refs/heads/{0}', 'main') }}
26+
type=semver,pattern=v{{version}},value=${{ inputs.semver }},enable=${{ github.ref == format('refs/heads/{0}', 'main') && fromJSON(inputs.exists) == false }}
27+
type=semver,pattern=v{{major}}.{{minor}},value=${{ inputs.semver }},enable=${{ github.ref == format('refs/heads/{0}', 'main') && fromJSON(inputs.exists) == false }}
28+
type=semver,pattern=v{{major}},value=${{ inputs.semver }},enable=${{ github.ref == format('refs/heads/{0}', 'main') && fromJSON(inputs.exists) == false && !(startsWith(inputs.semver, '0')) }}
29+
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') && fromJSON(inputs.exists) == false }}
30+
type=sha,format=long
31+
- name: Tag image
32+
run: |
33+
echo "${{ steps.meta.outputs.tags }}" | while read -r tag; do
34+
echo "Processing tag: $tag"
35+
docker tag ${{ inputs.source-image }} "$tag"
36+
done
37+
shell: bash
38+
- name: Push image
39+
if: inputs.push == 'true'
40+
run: docker push --all-tags ghcr.io/${{ inputs.repository }}
41+
shell: bash

.github/actions/run-e2e/action.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: "Run Script & Assert Output"
2+
description: "Run a script via the Elixir Script Action, and verify its output matches the expected result"
3+
inputs:
4+
image-name:
5+
description: "The container image the Elixir Script Action uses to run the script"
6+
required: true
7+
script:
8+
description: "The script to run"
9+
required: true
10+
expected:
11+
description: "The expected output"
12+
required: true
13+
runs:
14+
using: "composite"
15+
steps:
16+
- name: Update action.yml to point to ${{ inputs.image-name }}
17+
run: |
18+
sed -i 's/\( image: \).*/\1${{ inputs.image-name }}/' action.yml
19+
shell: bash
20+
- name: Run gaggle/elixir_script
21+
id: run
22+
uses: ./
23+
with:
24+
script: |
25+
${{ matrix.data.script }}
26+
- name: Assert output
27+
run: |
28+
expected="${{ matrix.data.expected }}"
29+
output="${{steps.run.outputs.result}}"
30+
[[ "$output" != "$expected" ]] && echo "::error::❌ Expected '$expected', got '$output'" && exit 1
31+
echo "✅ Test passed, outputs.result: ${{toJSON(steps.run.outputs.result)}}"
32+
shell: bash

.github/actions/version/action.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: "Version Config"
2+
description: "Parse version and check if the codebase is in a pushable state"
3+
inputs:
4+
semver:
5+
description: "The version string to be parsed and checked"
6+
required: true
7+
default-branch:
8+
description: "Name of main branch"
9+
default: ${{ github.event.repository.default-branch }}
10+
debug:
11+
description: Default is to run in debug mode when the GitHub Actions step debug logging is turned on.
12+
default: ${{ runner.debug == '1' }}
13+
outputs:
14+
semver:
15+
description: "The full semantic version. Same as input.semver"
16+
value: ""
17+
major:
18+
description: "Major version, 1.2.3 -> 1"
19+
value: ""
20+
minor:
21+
description: "Minor version, 1.2.3 -> 2"
22+
value: ""
23+
patch:
24+
description: "Patch version, 1.2.3 -> 3"
25+
value: ""
26+
tag-exists:
27+
description: "Indicates whether a Git tag already exists for the version"
28+
value: ""
29+
releasable:
30+
description: "Determines if the current state of the codebase should released, determined by whether the commit is for the default branch and if the version it's associated with has already been released"
31+
value: ""
32+
runs:
33+
using: "composite"
34+
steps:
35+
- run: ${{ github.action_path }}/entrypoint.sh
36+
shell: bash
37+
env:
38+
INPUT_SEMVER: ${{ inputs.semver }}
39+
INPUT_DEFAULT_BRANCH: ${{ inputs.default-branch }}
40+
INPUT_DEBUG: ${{ inputs.debug }}

0 commit comments

Comments
 (0)