name: 'Gordon Deploy' description: 'Build and push container images to a Gordon registry on tag push' author: 'Gordon Contributors' branding: icon: 'upload-cloud' color: 'blue' inputs: registry: description: 'Gordon registry URL (e.g., registry.mydomain.com)' required: true username: description: 'Registry username (token subject for token auth)' required: true password: description: 'Registry password or token' required: true image: description: 'Image name (defaults to repository name)' required: false default: '' tag: description: 'Override image tag (defaults to git tag or short SHA)' required: false default: '' dockerfile: description: 'Path to Dockerfile (defaults to ./Dockerfile)' required: false default: './Dockerfile' context: description: 'Build context path (defaults to .)' required: false default: '.' platforms: description: 'Target platforms (e.g., linux/amd64,linux/arm64)' required: false default: '' push-latest: description: 'Also push with :latest tag' required: false default: 'true' cache-from: description: 'Cache source (e.g., type=gha)' required: false default: 'type=gha' cache-to: description: 'Cache destination (e.g., type=gha,mode=max)' required: false default: 'type=gha,mode=max' outputs: image: description: 'Full image reference that was pushed' value: ${{ steps.meta.outputs.image }} tag: description: 'Tag that was pushed' value: ${{ steps.meta.outputs.tag }} digest: description: 'Image digest' value: ${{ steps.push.outputs.digest }} runs: using: 'composite' steps: - name: Validate inputs shell: bash env: INPUT_DOCKERFILE: ${{ inputs.dockerfile }} run: | if [[ ! -f "${INPUT_DOCKERFILE}" ]]; then echo "::error::Dockerfile not found at ${INPUT_DOCKERFILE}" exit 1 fi - name: Extract metadata id: meta shell: bash env: INPUT_IMAGE: ${{ inputs.image }} INPUT_TAG: ${{ inputs.tag }} INPUT_REGISTRY: ${{ inputs.registry }} run: | reject_line_breaks() { [[ "$2" != *$'\n'* && "$2" != *$'\r'* ]] || { echo "::error::$1 must not contain line breaks" exit 1 } } reject_line_breaks "registry" "$INPUT_REGISTRY" reject_line_breaks "image" "$INPUT_IMAGE" reject_line_breaks "tag" "$INPUT_TAG" IMAGE_NAME="${INPUT_IMAGE:-${GITHUB_REPOSITORY#*/}}" if [[ -n "${INPUT_TAG}" ]]; then TAG="${INPUT_TAG}" elif [[ "$GITHUB_REF" == refs/tags/* ]]; then TAG="${GITHUB_REF#refs/tags/}" else TAG="${GITHUB_SHA:0:7}" fi [[ "$INPUT_REGISTRY" =~ ^[a-zA-Z0-9.-]+(:[0-9]{1,5})?$ ]] || { echo "::error::registry must be a hostname with an optional port" exit 1 } if [[ "$INPUT_REGISTRY" =~ :([0-9]+)$ ]]; then PORT="${BASH_REMATCH[1]}" [[ "$PORT" != 0* && $((10#$PORT)) -ge 1 && $((10#$PORT)) -le 65535 ]] || { echo "::error::registry port must be between 1 and 65535 without leading zeros" exit 1 } fi [[ "$IMAGE_NAME" =~ ^[a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)*$ ]] || { echo "::error::image contains invalid Docker repository components" exit 1 } [[ "$TAG" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$ ]] || { echo "::error::tag is not a valid Docker tag" exit 1 } FULL_IMAGE="${INPUT_REGISTRY}/${IMAGE_NAME}:${TAG}" printf 'image=%s\nimage_name=%s\ntag=%s\nregistry=%s\n' \ "${FULL_IMAGE}" "${IMAGE_NAME}" "${TAG}" "${INPUT_REGISTRY}" >> "${GITHUB_OUTPUT}" echo "Building: ${FULL_IMAGE}" - name: Set up QEMU uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - name: Log in to Gordon registry uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 with: registry: ${{ inputs.registry }} username: ${{ inputs.username }} password: ${{ inputs.password }} - name: Build tags id: tags shell: bash env: META_IMAGE: ${{ steps.meta.outputs.image }} META_IMAGE_NAME: ${{ steps.meta.outputs.image_name }} INPUT_REGISTRY: ${{ inputs.registry }} INPUT_PUSH_LATEST: ${{ inputs.push-latest }} run: | TAGS="${META_IMAGE}" if [[ "${INPUT_PUSH_LATEST}" == "true" ]]; then TAGS="${TAGS}"$'\n'"${INPUT_REGISTRY}/${META_IMAGE_NAME}:latest" fi DELIMITER="gordon_$(openssl rand -hex 16)" while grep -Fqx "$DELIMITER" <<< "$TAGS"; do DELIMITER="gordon_$(openssl rand -hex 16)" done printf 'tags<<%s\n%s\n%s\n' "$DELIMITER" "$TAGS" "$DELIMITER" >> "$GITHUB_OUTPUT" - name: Build and push id: push uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 env: DOCKER_BUILD_SUMMARY: false with: context: ${{ inputs.context }} file: ${{ inputs.dockerfile }} push: true tags: ${{ steps.tags.outputs.tags }} platforms: ${{ inputs.platforms }} cache-from: ${{ inputs.cache-from }} cache-to: ${{ inputs.cache-to }} provenance: false - name: Summary shell: bash env: META_IMAGE: ${{ steps.meta.outputs.image }} META_TAG: ${{ steps.meta.outputs.tag }} PUSH_DIGEST: ${{ steps.push.outputs.digest }} run: | echo "## Gordon Deploy Summary" >> "$GITHUB_STEP_SUMMARY" echo "" >> $GITHUB_STEP_SUMMARY echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY printf '| Image | `%s` |\n' "$META_IMAGE" >> "$GITHUB_STEP_SUMMARY" printf '| Tag | `%s` |\n' "$META_TAG" >> "$GITHUB_STEP_SUMMARY" printf '| Digest | `%s` |\n' "$PUSH_DIGEST" >> "$GITHUB_STEP_SUMMARY" echo "" >> $GITHUB_STEP_SUMMARY echo "Container deployed to Gordon registry and will be automatically deployed." >> $GITHUB_STEP_SUMMARY