Saltar al contenido
mypipelines
Java (Spring Boot)·Consumer template·on: workflow_dispatch

Java Tag Deploy

Template: Release to PRODUCTION (manual, button-triggered) Copy to: .github/workflows/tag-deploy.yml Trigger: workflow_dispatch (Actions tab -> "Run workflow") with an explicit version. Flow: validate

templates/java-tag-deploy.yml

templates/java-tag-deploy.yml
# Template: Release to PRODUCTION (manual, button-triggered)
# Copy to: .github/workflows/tag-deploy.yml
#
# Trigger: workflow_dispatch (Actions tab -> "Run workflow") with an explicit version.
# Flow:
# validate version (vX.Y.Z, not already tagged)
# -> deploy to PROD (gated by the 'prod' GitHub Environment required reviewers)
# -> create git tag vX.Y.Z + GitHub Release (only AFTER a successful, approved deploy)
#
# Single approval gate: the reusable deploy job binds `environment: prod`, so the deploy
# pauses for manual approval. The tag/Release is created only after that succeeds — no
# orphan tags for rejected releases.
#
# NOTE: the tag is pushed by GITHUB_TOKEN. The tag-protection ruleset must list the
# GitHub Actions app in its bypass actors (see .github/ruleset/ruleset-tags.json), otherwise
# tag creation is blocked once the ruleset is enforced (active).
#
# Required secrets: Same as develop-deploy.yml
name: Release to Production
on:
workflow_dispatch:
inputs:
version:
description: 'Release version to cut and deploy (format vX.Y.Z, e.g. v1.4.0)'
required: true
type: string
# Deploys on one ref run one at a time, in order. Never cancel one mid-flight:
# a half-applied deploy is worse than a late one.
# A promotion waiting at the prod approval gate is replaced if a later
# version is dispatched behind it; re-dispatch it if it was still wanted.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: write # create tag + GitHub Release
id-token: write
jobs:
validate:
name: Validate version
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Check version format and uniqueness
env:
VERSION: ${{ inputs.version }}
REF: ${{ github.ref_name }}
run: |
if [ "$REF" != "main" ]; then
echo "::error::Release must be dispatched from 'main' (got '$REF')."; exit 1
fi
if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::version must match vX.Y.Z (got '$VERSION')"; exit 1
fi
if git rev-parse "refs/tags/${VERSION}" >/dev/null 2>&1; then
echo "::error::tag ${VERSION} already exists (releases are immutable)"; exit 1
fi
echo "Version ${VERSION} is valid and unused."
deploy:
name: Deploy to prod
needs: validate
uses: <org>/ci-templates/.github/workflows/java-main-pipeline.yml@v2
with:
run_build: true
run_test: false
run_artifact: true
image_tag: ${{ inputs.version }} # image tagged with the release version
run_deploy: true
deploy_target: 'ec2-vpn'
environment: 'prod' # <- Environment required reviewers gate the deploy
secrets: inherit
tag:
name: Tag + GitHub Release
needs: deploy
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Create tag and release
env:
VERSION: ${{ inputs.version }}
GH_TOKEN: ${{ github.token }}
run: |
if ! git rev-parse "refs/tags/${VERSION}" >/dev/null 2>&1; then
git tag "${VERSION}"
git push origin "${VERSION}"
echo "::notice::Created tag ${VERSION}."
fi
gh release view "${VERSION}" >/dev/null 2>&1 \
|| gh release create "${VERSION}" --title "${VERSION}" --generate-notes