Files
Meshtastic-Android/.github/workflows/promote.yml

283 lines
9.6 KiB
YAML

name: Promote Release
on:
workflow_call:
inputs:
base_version:
description: 'The base version for the release (e.g., 2.3.0)'
required: true
type: string
tag_name:
description: 'The tag that triggered the release'
required: true
type: string
release_name:
description: 'The desired name for the GitHub release'
required: true
type: string
final_tag:
description: 'The final tag for the release'
required: true
type: string
commit_sha:
description: 'The commit SHA to tag'
required: false
type: string
channel:
description: 'The channel to promote to'
required: true
type: string
from_channel:
description: 'The channel to promote from'
required: true
type: string
secrets:
GSERVICES:
required: true
KEYSTORE:
required: true
KEYSTORE_FILENAME:
required: true
KEYSTORE_PROPERTIES:
required: true
DATADOG_APPLICATION_ID:
required: true
DATADOG_CLIENT_TOKEN:
required: true
GOOGLE_MAPS_API_KEY:
required: true
GOOGLE_PLAY_JSON_KEY:
required: true
GRADLE_ENCRYPTION_KEY:
required: true
DISCORD_WEBHOOK_ANDROID:
required: false
concurrency:
group: ${{ github.workflow }}-${{ inputs.tag_name }}
cancel-in-progress: true
permissions:
contents: write
pull-requests: write
statuses: write
id-token: write
attestations: write
jobs:
prepare-build-info:
runs-on: ubuntu-24.04-arm
outputs:
APP_VERSION_NAME: ${{ steps.prep_version.outputs.APP_VERSION_NAME }}
APP_VERSION_CODE: ${{ steps.calculate_version_code.outputs.versionCode }}
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: ${{ inputs.commit_sha || inputs.tag_name }}
fetch-depth: 0
submodules: 'recursive'
- name: Prep APP_VERSION_NAME
id: prep_version
env:
INPUT_TAG_NAME: ${{ inputs.tag_name }}
run: |
VERSION_NAME=$(echo $INPUT_TAG_NAME | sed 's/-.*//' | sed 's/v//')
echo "APP_VERSION_NAME=$VERSION_NAME" >> $GITHUB_OUTPUT
echo "Parsed Version: $VERSION_NAME"
- name: Extract VERSION_CODE_OFFSET from config.properties
id: get_version_code_offset
run: |
OFFSET=$(grep '^VERSION_CODE_OFFSET=' config.properties | cut -d'=' -f2)
echo "VERSION_CODE_OFFSET=$OFFSET" >> $GITHUB_OUTPUT
- name: Calculate Version Code from Git Commit Count
id: calculate_version_code
run: |
COMMIT_COUNT=$(git rev-list --count HEAD)
OFFSET=${{ steps.get_version_code_offset.outputs.VERSION_CODE_OFFSET }}
VERSION_CODE=$((COMMIT_COUNT + OFFSET))
echo "versionCode=$VERSION_CODE" >> $GITHUB_OUTPUT
shell: bash
promote-release:
runs-on: ubuntu-24.04-arm
needs: [ prepare-build-info ]
steps:
- name: Promote to next channel
uses: kevin-david/promote-play-release@v1.2.0
with:
service-account-json-raw: ${{ secrets.GOOGLE_PLAY_JSON_KEY }}
package-name: 'com.geeksville.mesh'
from-track: ${{ inputs.from_channel == 'closed' && 'NewAlpha' || (inputs.from_channel == 'open' && 'beta' || 'internal') }}
to-track: ${{ inputs.channel == 'closed' && 'NewAlpha' || (inputs.channel == 'open' && 'beta' || 'production') }}
user-fraction: ${{ (inputs.channel == 'production' && '0.1') || (inputs.channel == 'open' && '0.5') || '1.0' }}
update-github-release:
runs-on: ubuntu-24.04-arm
needs: [ prepare-build-info, promote-release ]
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: ${{ inputs.commit_sha || inputs.tag_name }}
fetch-depth: 0
submodules: 'recursive'
- name: Push Git Tag on Success
if: ${{ inputs.commit_sha != '' }}
run: |
git tag ${{ inputs.final_tag }} ${{ inputs.commit_sha }}
git push origin ${{ inputs.final_tag }}
- name: Update GitHub Release with gh CLI
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release edit ${{ inputs.tag_name }} \
--tag ${{ inputs.final_tag }} \
--title "${{ inputs.release_name }} (${{ needs.prepare-build-info.outputs.APP_VERSION_CODE }})" \
--draft=false \
--prerelease=${{ inputs.channel != 'production' }}
- name: Stamp CHANGELOG.md for release
if: ${{ inputs.channel == 'production' }}
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ inputs.final_tag }}
VERSION: ${{ inputs.base_version }}
REPO: ${{ github.repository }}
run: |
DATE=$(date -u +%Y-%m-%d)
# Checkout CHANGELOG.md from main (we're on a tag checkout)
git fetch origin main
git checkout -b "changelog/v${VERSION}" origin/main
# Find the previous production tag for the full-range notes
PREV_PROD=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | grep -v "^v${VERSION}$" | head -1)
# Generate a single flat changelog for the entire release using GitHub API
FLAT_NOTES=""
if [ -n "$PREV_PROD" ]; then
FLAT_NOTES=$(gh api "repos/${REPO}/releases/generate-notes" \
-f tag_name="$TAG" \
-f target_commitish="$(git rev-parse "$TAG")" \
-f previous_tag_name="$PREV_PROD" \
--jq '.body' 2>/dev/null || true)
# Strip boilerplate
FLAT_NOTES=$(echo "$FLAT_NOTES" \
| sed '/^<!-- .* -->/d' \
| sed '/^## What'\''s Changed$/d' \
| sed '/^\*\*Full Changelog\*\*/d' \
| sed '/./,$!d' \
| sed -e :a -e '/^\n*$/{$d;N;ba}')
fi
# Build the versioned section and reset unreleased
python3 -c "
import sys
version, date, notes = sys.argv[1], sys.argv[2], sys.argv[3]
with open('CHANGELOG.md', 'r') as f:
content = f.read()
us = '<!-- UNRELEASED_START -->'
ue = '<!-- UNRELEASED_END -->'
rs = '<!-- RELEASED_START -->'
start = content.find(us)
end = content.find(ue)
rstart = content.find(rs)
if start == -1 or end == -1 or rstart == -1:
print('Markers not found in CHANGELOG.md')
sys.exit(1)
versioned = f'## [{version}] - {date}\n\n{notes}'
fresh = '## [Unreleased]\n\n*No changes yet.*'
new_content = (
content[:start + len(us)] + '\n' + fresh + '\n'
+ content[end:rstart + len(rs)] + '\n\n' + versioned + '\n'
+ content[rstart + len(rs):]
)
with open('CHANGELOG.md', 'w') as f:
f.write(new_content)
" "$VERSION" "$DATE" "$FLAT_NOTES"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add CHANGELOG.md
git diff --cached --quiet || {
BRANCH="automation/changelog-v${VERSION}"
git checkout -B "$BRANCH"
git commit -m "docs: release CHANGELOG.md for v${VERSION}"
git push origin "$BRANCH" --force
gh pr create \
--title "docs: release CHANGELOG.md for v${VERSION}" \
--body "Automated changelog stamp for production release v${VERSION}." \
--head "$BRANCH" \
--base main \
--label "automation" \
--label "skip-changelog"
# Post required commit status so the PR isn't blocked
COMMIT_SHA=$(git rev-parse HEAD)
gh api "repos/${{ github.repository }}/statuses/${COMMIT_SHA}" \
-f state="success" \
-f context="Check Workflow Status" \
-f description="Skipped — changelog-only PR"
}
- name: Notify Discord
if: ${{ inputs.channel != 'internal' }}
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK_ANDROID }}
VERSION: ${{ inputs.final_tag }}
CHANNEL: ${{ inputs.channel }}
run: |
if [[ -z "$DISCORD_WEBHOOK" ]]; then
echo "No Discord webhook provided. Skipping notification."
exit 0
fi
# Determine Track Name for Display
if [ "$CHANNEL" == "closed" ]; then TRACK="Alpha (Closed)"; fi
if [ "$CHANNEL" == "open" ]; then TRACK="Beta (Open)"; fi
if [ "$CHANNEL" == "production" ]; then TRACK="Production"; fi
# Construct JSON Payload
PAYLOAD=$(cat <<EOF
{
"content": null,
"embeds": [
{
"title": "🚀 New Android Release: $VERSION",
"description": "A new build has been promoted to the **$TRACK** track.",
"color": 5763719,
"fields": [
{
"name": "Track",
"value": "$TRACK",
"inline": true
},
{
"name": "Version",
"value": "$VERSION",
"inline": true
}
],
"url": "https://github.com/meshtastic/Meshtastic-Android/releases/tag/$VERSION"
}
]
}
EOF
)
curl -H "Content-Type: application/json" -d "$PAYLOAD" "$DISCORD_WEBHOOK"