meshtastic-android/.github/workflows/promote-release.yml

192 wiersze
7.8 KiB
YAML

name: Promote Release
on:
workflow_dispatch:
inputs:
target_stage:
description: "Stage to promote to (auto|closed|open|production)"
required: true
default: auto
type: choice
options: [auto, closed, open, production]
base_version:
description: "Explicit base version (e.g. 2.5.0 or 2.5.0-hotfix1). If omitted, latest internal tag base is used."
required: false
allow_skip:
description: "Allow skipping intermediate stages (e.g. internal->production)"
required: false
default: "false"
dry_run:
description: "If true, only compute next tag; don't push"
required: false
default: "false"
permissions:
contents: write
jobs:
promote:
runs-on: ubuntu-latest
environment: Release
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Determine Base Version
id: base
run: |
set -euo pipefail
INPUT_BASE='${{ inputs.base_version }}'
if [ -n "$INPUT_BASE" ]; then
# Validate an internal tag exists for provided base
if ! git tag --list | grep -q "^v${INPUT_BASE}-internal\."; then
echo "No internal tag found for base version v${INPUT_BASE}." >&2
exit 1
fi
BASE_VERSION="$INPUT_BASE"
else
LATEST_INTERNAL_TAG=$(git tag --list 'v*-internal.*' --sort=-taggerdate | head -n1 || true)
if [ -z "$LATEST_INTERNAL_TAG" ]; then
echo "No internal tags found; nothing to promote." >&2
exit 1
fi
# Strip leading v and suffix -internal.N
BASE_VERSION=$(echo "$LATEST_INTERNAL_TAG" | sed -E 's/^v(.*)-internal\.[0-9]+$/\1/')
fi
echo "Base version: $BASE_VERSION"
echo "base_version=$BASE_VERSION" >> $GITHUB_OUTPUT
- name: Gather Existing Stage Tags
id: scan
run: |
set -euo pipefail
BASE='${{ steps.base.outputs.base_version }}'
INTERNAL_TAGS=$(git tag --list "v${BASE}-internal.*" | sort -V || true)
CLOSED_TAGS=$(git tag --list "v${BASE}-closed.*" | sort -V || true)
OPEN_TAGS=$(git tag --list "v${BASE}-open.*" | sort -V || true)
PROD_TAG=$(git tag --list "v${BASE}" || true)
echo "internal_tags<<EOF" >> $GITHUB_OUTPUT
echo "$INTERNAL_TAGS" >> $GITHUB_OUTPUT
echo EOF >> $GITHUB_OUTPUT
echo "closed_tags<<EOF" >> $GITHUB_OUTPUT
echo "$CLOSED_TAGS" >> $GITHUB_OUTPUT
echo EOF >> $GITHUB_OUTPUT
echo "open_tags<<EOF" >> $GITHUB_OUTPUT
echo "$OPEN_TAGS" >> $GITHUB_OUTPUT
echo EOF >> $GITHUB_OUTPUT
if [ -n "$PROD_TAG" ]; then echo "production_present=true" >> $GITHUB_OUTPUT; else echo "production_present=false" >> $GITHUB_OUTPUT; fi
if [ -z "$INTERNAL_TAGS" ]; then
echo "No internal tags found for base version $BASE." >&2
exit 1
fi
- name: Determine Current Stage
id: current
run: |
set -euo pipefail
PROD='${{ steps.scan.outputs.production_present }}'
CLOSED='${{ steps.scan.outputs.closed_tags }}'
OPEN='${{ steps.scan.outputs.open_tags }}'
if [ "$PROD" = 'true' ]; then CUR=production
elif [ -n "$OPEN" ]; then CUR=open
elif [ -n "$CLOSED" ]; then CUR=closed
else CUR=internal; fi
echo "Current highest stage: $CUR"
echo "current_stage=$CUR" >> $GITHUB_OUTPUT
- name: Decide Target Stage
id: decide
run: |
set -euo pipefail
REQ='${{ inputs.target_stage }}'
CUR='${{ steps.current.outputs.current_stage }}'
ALLOW_SKIP='${{ inputs.allow_skip }}'
order=(internal closed open production)
# helper to get index
idx() { local i=0; for s in "${order[@]}"; do [ "$s" = "$1" ] && echo $i && return; i=$((i+1)); done; echo -1; }
if [ "$REQ" = auto ]; then
CUR_IDX=$(idx "$CUR")
TARGET_IDX=$((CUR_IDX+1))
TARGET_STAGE=${order[$TARGET_IDX]:-}
if [ -z "$TARGET_STAGE" ]; then
echo "Already at production; nothing to promote." >&2
exit 1
fi
else
TARGET_STAGE=$REQ
CUR_IDX=$(idx "$CUR")
REQ_IDX=$(idx "$TARGET_STAGE")
if [ $REQ_IDX -le $CUR_IDX ]; then
echo "Requested stage $TARGET_STAGE is not ahead of current stage $CUR." >&2
exit 1
fi
if [ "$ALLOW_SKIP" != 'true' ] && [ $((CUR_IDX+1)) -ne $REQ_IDX ]; then
echo "Skipping stages not allowed (current=$CUR, requested=$TARGET_STAGE). Enable allow_skip to override." >&2
exit 1
fi
fi
echo "Target stage: $TARGET_STAGE"
echo "target_stage=$TARGET_STAGE" >> $GITHUB_OUTPUT
- name: Compute New Tag
id: tag
run: |
set -euo pipefail
BASE='${{ steps.base.outputs.base_version }}'
TARGET='${{ steps.decide.outputs.target_stage }}'
if [ "$TARGET" = production ]; then
NEW_TAG="v${BASE}"
if git tag --list | grep -q "^${NEW_TAG}$"; then
echo "Production tag ${NEW_TAG} already exists." >&2
exit 1
fi
else
EXISTING=$(git tag --list "v${BASE}-${TARGET}.*" | sed -E "s/^v.*-${TARGET}\.([0-9]+)$/\1/" | sort -n | tail -1 || true)
if [ -z "$EXISTING" ]; then NEXT=1; else NEXT=$((EXISTING+1)); fi
NEW_TAG="v${BASE}-${TARGET}.${NEXT}"
fi
echo "new_tag=$NEW_TAG" >> $GITHUB_OUTPUT
echo "Will create tag: $NEW_TAG"
- name: Resolve Commit to Tag (latest internal for base)
id: commit
run: |
set -euo pipefail
BASE='${{ steps.base.outputs.base_version }}'
LATEST_INTERNAL=$(git tag --list "v${BASE}-internal.*" --sort=-version:refname | head -n1)
if [ -z "$LATEST_INTERNAL" ]; then
echo "No internal tag found for base $BASE (unexpected)." >&2
exit 1
fi
COMMIT=$(git rev-list -n1 "$LATEST_INTERNAL")
echo "commit_sha=$COMMIT" >> $GITHUB_OUTPUT
echo "Using commit $COMMIT from $LATEST_INTERNAL"
- name: Dry Run Summary
if: ${{ inputs.dry_run == 'true' }}
run: |
echo "DRY RUN: Would tag commit ${{ steps.commit.outputs.commit_sha }} with ${{ steps.tag.outputs.new_tag }}"
echo "Current stage: ${{ steps.current.outputs.current_stage }} -> Target: ${{ steps.decide.outputs.target_stage }}"
git log -1 --oneline ${{ steps.commit.outputs.commit_sha }}
- name: Create & Push Tag
if: ${{ inputs.dry_run != 'true' }}
run: |
TAG='${{ steps.tag.outputs.new_tag }}'
COMMIT='${{ steps.commit.outputs.commit_sha }}'
MSG="Promote ${TAG} from ${{ steps.current.outputs.current_stage }} to ${{ steps.decide.outputs.target_stage }}"
git tag -a "$TAG" "$COMMIT" -m "$MSG"
git push origin "$TAG"
echo "Created and pushed $TAG"
- name: Promotion Summary
run: |
echo "### Promotion Tag Created" >> $GITHUB_STEP_SUMMARY
echo "Base Version: ${{ steps.base.outputs.base_version }}" >> $GITHUB_STEP_SUMMARY
echo "Current Stage: ${{ steps.current.outputs.current_stage }}" >> $GITHUB_STEP_SUMMARY
echo "Target Stage: ${{ steps.decide.outputs.target_stage }}" >> $GITHUB_STEP_SUMMARY
echo "New Tag: ${{ steps.tag.outputs.new_tag }}" >> $GITHUB_STEP_SUMMARY
echo "Dry Run: ${{ inputs.dry_run }}" >> $GITHUB_STEP_SUMMARY