action.yml RawHistoryFolder
1name: 'Gordon Deploy'
2description: 'Build and push container images to a Gordon registry on tag push'
3author: 'Gordon Contributors'
4
5branding:
6 icon: 'upload-cloud'
7 color: 'blue'
8
9inputs:
10 registry:
11 description: 'Gordon registry URL (e.g., registry.mydomain.com)'
12 required: true
13 username:
14 description: 'Registry username (token subject for token auth)'
15 required: true
16 password:
17 description: 'Registry password or token'
18 required: true
19 image:
20 description: 'Image name (defaults to repository name)'
21 required: false
22 default: ''
23 tag:
24 description: 'Override image tag (defaults to git tag or short SHA)'
25 required: false
26 default: ''
27 dockerfile:
28 description: 'Path to Dockerfile (defaults to ./Dockerfile)'
29 required: false
30 default: './Dockerfile'
31 context:
32 description: 'Build context path (defaults to .)'
33 required: false
34 default: '.'
35 platforms:
36 description: 'Target platforms (e.g., linux/amd64,linux/arm64)'
37 required: false
38 default: ''
39 push-latest:
40 description: 'Also push with :latest tag'
41 required: false
42 default: 'true'
43 cache-from:
44 description: 'Cache source (e.g., type=gha)'
45 required: false
46 default: 'type=gha'
47 cache-to:
48 description: 'Cache destination (e.g., type=gha,mode=max)'
49 required: false
50 default: 'type=gha,mode=max'
51
52outputs:
53 image:
54 description: 'Full image reference that was pushed'
55 value: ${{ steps.meta.outputs.image }}
56 tag:
57 description: 'Tag that was pushed'
58 value: ${{ steps.meta.outputs.tag }}
59 digest:
60 description: 'Image digest'
61 value: ${{ steps.push.outputs.digest }}
62
63runs:
64 using: 'composite'
65 steps:
66 - name: Validate inputs
67 shell: bash
68 env:
69 INPUT_DOCKERFILE: ${{ inputs.dockerfile }}
70 run: |
71 if [[ ! -f "${INPUT_DOCKERFILE}" ]]; then
72 echo "::error::Dockerfile not found at ${INPUT_DOCKERFILE}"
73 exit 1
74 fi
75
76 - name: Extract metadata
77 id: meta
78 shell: bash
79 env:
80 INPUT_IMAGE: ${{ inputs.image }}
81 INPUT_TAG: ${{ inputs.tag }}
82 INPUT_REGISTRY: ${{ inputs.registry }}
83 run: |
84 reject_line_breaks() {
85 [[ "$2" != *$'\n'* && "$2" != *$'\r'* ]] || {
86 echo "::error::$1 must not contain line breaks"
87 exit 1
88 }
89 }
90 reject_line_breaks "registry" "$INPUT_REGISTRY"
91 reject_line_breaks "image" "$INPUT_IMAGE"
92 reject_line_breaks "tag" "$INPUT_TAG"
93
94 IMAGE_NAME="${INPUT_IMAGE:-${GITHUB_REPOSITORY#*/}}"
95 if [[ -n "${INPUT_TAG}" ]]; then
96 TAG="${INPUT_TAG}"
97 elif [[ "$GITHUB_REF" == refs/tags/* ]]; then
98 TAG="${GITHUB_REF#refs/tags/}"
99 else
100 TAG="${GITHUB_SHA:0:7}"
101 fi
102
103 [[ "$INPUT_REGISTRY" =~ ^[a-zA-Z0-9.-]+(:[0-9]{1,5})?$ ]] || {
104 echo "::error::registry must be a hostname with an optional port"
105 exit 1
106 }
107 if [[ "$INPUT_REGISTRY" =~ :([0-9]+)$ ]]; then
108 PORT="${BASH_REMATCH[1]}"
109 [[ "$PORT" != 0* && $((10#$PORT)) -ge 1 && $((10#$PORT)) -le 65535 ]] || {
110 echo "::error::registry port must be between 1 and 65535 without leading zeros"
111 exit 1
112 }
113 fi
114 [[ "$IMAGE_NAME" =~ ^[a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)*$ ]] || {
115 echo "::error::image contains invalid Docker repository components"
116 exit 1
117 }
118 [[ "$TAG" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$ ]] || {
119 echo "::error::tag is not a valid Docker tag"
120 exit 1
121 }
122
123 FULL_IMAGE="${INPUT_REGISTRY}/${IMAGE_NAME}:${TAG}"
124
125 printf 'image=%s\nimage_name=%s\ntag=%s\nregistry=%s\n' \
126 "${FULL_IMAGE}" "${IMAGE_NAME}" "${TAG}" "${INPUT_REGISTRY}" >> "${GITHUB_OUTPUT}"
127
128 echo "Building: ${FULL_IMAGE}"
129
130 - name: Set up QEMU
131 uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
132
133 - name: Set up Docker Buildx
134 uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
135
136 - name: Log in to Gordon registry
137 uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
138 with:
139 registry: ${{ inputs.registry }}
140 username: ${{ inputs.username }}
141 password: ${{ inputs.password }}
142
143 - name: Build tags
144 id: tags
145 shell: bash
146 env:
147 META_IMAGE: ${{ steps.meta.outputs.image }}
148 META_IMAGE_NAME: ${{ steps.meta.outputs.image_name }}
149 INPUT_REGISTRY: ${{ inputs.registry }}
150 INPUT_PUSH_LATEST: ${{ inputs.push-latest }}
151 run: |
152 TAGS="${META_IMAGE}"
153 if [[ "${INPUT_PUSH_LATEST}" == "true" ]]; then
154 TAGS="${TAGS}"$'\n'"${INPUT_REGISTRY}/${META_IMAGE_NAME}:latest"
155 fi
156 DELIMITER="gordon_$(openssl rand -hex 16)"
157 while grep -Fqx "$DELIMITER" <<< "$TAGS"; do
158 DELIMITER="gordon_$(openssl rand -hex 16)"
159 done
160 printf 'tags<<%s\n%s\n%s\n' "$DELIMITER" "$TAGS" "$DELIMITER" >> "$GITHUB_OUTPUT"
161
162 - name: Build and push
163 id: push
164 uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
165 env:
166 DOCKER_BUILD_SUMMARY: false
167 with:
168 context: ${{ inputs.context }}
169 file: ${{ inputs.dockerfile }}
170 push: true
171 tags: ${{ steps.tags.outputs.tags }}
172 platforms: ${{ inputs.platforms }}
173 cache-from: ${{ inputs.cache-from }}
174 cache-to: ${{ inputs.cache-to }}
175 provenance: false
176
177 - name: Summary
178 shell: bash
179 env:
180 META_IMAGE: ${{ steps.meta.outputs.image }}
181 META_TAG: ${{ steps.meta.outputs.tag }}
182 PUSH_DIGEST: ${{ steps.push.outputs.digest }}
183 run: |
184 echo "## Gordon Deploy Summary" >> "$GITHUB_STEP_SUMMARY"
185 echo "" >> $GITHUB_STEP_SUMMARY
186 echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
187 echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
188 printf '| Image | `%s` |\n' "$META_IMAGE" >> "$GITHUB_STEP_SUMMARY"
189 printf '| Tag | `%s` |\n' "$META_TAG" >> "$GITHUB_STEP_SUMMARY"
190 printf '| Digest | `%s` |\n' "$PUSH_DIGEST" >> "$GITHUB_STEP_SUMMARY"
191 echo "" >> $GITHUB_STEP_SUMMARY
192 echo "Container deployed to Gordon registry and will be automatically deployed." >> $GITHUB_STEP_SUMMARY