Skip to content

Build Prod Release (Build + Manifest) #93

Build Prod Release (Build + Manifest)

Build Prod Release (Build + Manifest) #93

Workflow file for this run

name: Build Prod Release (Build + Manifest)
# Combined workflow: builds images, updates versions, stitches manifests, creates ONE PR
# Replaces the two-step flow of prod-build-images.yml → (merge PR) → prod-build-manifest.yml
#
# Beta mode: builds images tagged {version}-beta.N, creates a GitHub pre-release
# with manifests attached. N auto-increments from existing {version}-beta.*
# pre-releases, so repeated beta runs coexist (2.0.8-beta.1, .2, .3 ...) instead
# of overwriting. Does NOT bump SPLUNK-VERSION or create a PR.
# Use beta to test a release candidate before committing to the version.
"on":
workflow_dispatch:
inputs:
version_bump:
description: 'Version bump type'
required: true
type: choice
options:
- patch
- minor
- major
- none (keep current)
- custom
default: 'patch'
custom_version:
description: 'Custom version (only when version_bump=custom, requires single service)'
required: false
type: string
default: ''
services:
description: 'Services to build (comma-separated, or "all")'
required: false
type: string
default: 'all'
beta:
description: 'Beta build — tags images with -beta.N suffix (N auto-increments), creates pre-release, does NOT bump SPLUNK-VERSION'
required: false
type: boolean
default: false
no_cache:
description: 'Disable build cache'
required: false
type: boolean
default: false
jobs:
# ─────────────────────────────────────────────
# Step 1: Determine the version
# ─────────────────────────────────────────────
determine-version:
if: github.repository == 'splunk/opentelemetry-demo'
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
base_version: ${{ steps.version.outputs.base_version }}
is_beta: ${{ steps.version.outputs.is_beta }}
is_hotfix: ${{ steps.version.outputs.is_hotfix }}
is_full_release: ${{ steps.version.outputs.is_full_release }}
is_custom: ${{ steps.version.outputs.is_custom }}
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: '3.x'
- run: pip install pyyaml
- name: Make scripts executable
run: |
chmod +x .github/scripts/bump-version.py
chmod +x .github/scripts/manage-hotfix.py
- name: Determine version
id: version
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
CURRENT_VERSION=$(cat SPLUNK-VERSION)
BUMP_TYPE="${{ inputs.version_bump }}"
SERVICES="${{ inputs.services }}"
CUSTOM_VERSION="${{ inputs.custom_version }}"
echo "Current version: $CURRENT_VERSION"
echo "Services to build: $SERVICES"
# Validate custom version usage
if [[ "$BUMP_TYPE" == "custom" ]]; then
if [[ "$SERVICES" == "all" ]]; then
echo "❌ ERROR: Custom version requires a single service, not 'all'"
exit 1
fi
if [[ -z "$CUSTOM_VERSION" ]]; then
echo "❌ ERROR: Custom version selected but no version provided"
exit 1
fi
SERVICE_COUNT=$(echo "$SERVICES" | tr ',' '\n' | wc -l)
if [[ "$SERVICE_COUNT" -gt 1 ]]; then
echo "❌ ERROR: Custom version requires exactly one service, got: $SERVICES"
exit 1
fi
fi
# Determine if this is a hotfix build
IS_HOTFIX="false"
if [[ "$SERVICES" != "all" ]] && [[ "$BUMP_TYPE" == "none (keep current)" ]]; then
IS_HOTFIX="true"
fi
# Calculate version
if [[ "$BUMP_TYPE" == "custom" ]]; then
VERSION="$CUSTOM_VERSION"
VERSION_STATUS="custom"
elif [[ "$IS_HOTFIX" == "true" ]]; then
SERVICE_NAME=$(echo "$SERVICES" | tr ',' '\n' | head -1 | xargs)
VERSION=$(python3 .github/scripts/manage-hotfix.py add "$SERVICE_NAME")
VERSION_STATUS="hotfix"
elif [[ "$BUMP_TYPE" == "none (keep current)" ]]; then
VERSION="$CURRENT_VERSION"
VERSION_STATUS="unchanged"
else
VERSION=$(python3 .github/scripts/bump-version.py "$CURRENT_VERSION" "$BUMP_TYPE")
VERSION_STATUS="bumped"
fi
IS_FULL_RELEASE="false"
if [[ "$SERVICES" == "all" && "$VERSION_STATUS" == "bumped" ]]; then
IS_FULL_RELEASE="true"
fi
IS_CUSTOM="false"
if [[ "$VERSION_STATUS" == "custom" ]]; then
IS_CUSTOM="true"
fi
# Beta: append -beta.N suffix, don't actually bump anything.
# Iteration N auto-increments from existing v{base}-beta.* pre-releases
# so repeated beta runs coexist (2.0.8-beta.1, .2, .3 ...) rather than
# overwriting. Stateless — derived from GitHub releases, nothing committed.
IS_BETA="${{ inputs.beta }}"
BASE_VERSION="$VERSION"
if [[ "$IS_BETA" == "true" ]]; then
EXISTING=$(gh release list --repo "$GITHUB_REPOSITORY" --limit 200 \
--json tagName --jq '.[].tagName' 2>/dev/null \
| grep -E "^v${BASE_VERSION}-beta(\.[0-9]+)?$" || true)
MAXN=0
while IFS= read -r tag; do
[[ -z "$tag" ]] && continue
# legacy bare "-beta" (no number) counts as iteration 0
n=$(echo "$tag" | sed -E "s/^v${BASE_VERSION}-beta\.?([0-9]*)$/\1/")
[[ "$n" =~ ^[0-9]+$ ]] || n=0
(( n > MAXN )) && MAXN=$n
done <<< "$EXISTING"
NEXT=$(( MAXN + 1 ))
VERSION="${BASE_VERSION}-beta.${NEXT}"
IS_FULL_RELEASE="false" # Beta never triggers full release behavior
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "base_version=$BASE_VERSION" >> $GITHUB_OUTPUT
echo "is_beta=$IS_BETA" >> $GITHUB_OUTPUT
echo "is_hotfix=$IS_HOTFIX" >> $GITHUB_OUTPUT
echo "is_full_release=$IS_FULL_RELEASE" >> $GITHUB_OUTPUT
echo "is_custom=$IS_CUSTOM" >> $GITHUB_OUTPUT
echo "### Version Information" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [[ "$IS_BETA" == "true" ]]; then
echo "> 🧪 **BETA BUILD** — images tagged \`${VERSION}\`, no version commit" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
fi
echo "| Type | Version |" >> $GITHUB_STEP_SUMMARY
echo "|------|---------|" >> $GITHUB_STEP_SUMMARY
echo "| Current | \`$CURRENT_VERSION\` |" >> $GITHUB_STEP_SUMMARY
if [[ "$VERSION_STATUS" == "bumped" ]]; then
echo "| **New** | **\`$VERSION\`** |" >> $GITHUB_STEP_SUMMARY
else
echo "| **Using** | **\`$VERSION\`** ($VERSION_STATUS) |" >> $GITHUB_STEP_SUMMARY
fi
echo "| Bump | ${{ inputs.version_bump }} |" >> $GITHUB_STEP_SUMMARY
echo "| Services | ${{ inputs.services }} |" >> $GITHUB_STEP_SUMMARY
# ─────────────────────────────────────────────
# Step 2: Prepare build matrix
# ─────────────────────────────────────────────
prepare-matrix:
needs: determine-version
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
registry: ${{ steps.set-matrix.outputs.registry }}
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: '3.x'
- run: pip install pyyaml
- name: Generate build matrix
id: set-matrix
run: |
python3 << 'PYTHON_SCRIPT'
import yaml, json, os
with open('services.yaml', 'r') as f:
config = yaml.safe_load(f)
prod_registry = config.get('registry', {}).get('prod', 'ghcr.io/splunk/opentelemetry-demo')
requested = "${{ inputs.services }}".strip()
services_to_build = []
for svc in config.get('services', []):
name = svc.get('name')
if not svc.get('build', False):
continue
if requested == 'all':
include = True
else:
include = name in [s.strip() for s in requested.split(',')]
if include:
platform = svc.get('platform', 'linux/amd64,linux/arm64')
dockerfile = svc.get('dockerfile', f'src/{name}/Dockerfile')
build_target = svc.get('build_target', '')
services_to_build.append({
'name': name, 'variant': '', 'platform': platform,
'dockerfile': dockerfile, 'build_args': '',
'target': build_target
})
if not services_to_build:
matrix = {'include': [{'name': '_skip', 'variant': '', 'platform': 'linux/amd64', 'dockerfile': 'none', 'build_args': '', 'target': ''}]}
else:
matrix = {'include': services_to_build}
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
f.write(f'matrix={json.dumps(matrix)}\n')
f.write(f'registry={prod_registry}\n')
print(f"Registry: {prod_registry}")
print(f"Services to build: {len(services_to_build)}")
for svc in services_to_build:
v = f" (variant: {svc['variant']})" if svc['variant'] else ""
print(f" - {svc['name']}{v}")
PYTHON_SCRIPT
# ─────────────────────────────────────────────
# Step 3: Build and push images
# ─────────────────────────────────────────────
build-images:
needs: [determine-version, prepare-matrix]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.prepare-matrix.outputs.matrix) }}
steps:
- name: Check if skip
id: check-skip
run: |
if [[ "${{ matrix.name }}" == "_skip" ]]; then
echo "skip=true" >> $GITHUB_OUTPUT
else
echo "skip=false" >> $GITHUB_OUTPUT
fi
- uses: actions/checkout@v5
if: steps.check-skip.outputs.skip != 'true'
- uses: docker/setup-buildx-action@v4
if: steps.check-skip.outputs.skip != 'true'
- name: Log in to GHCR
if: steps.check-skip.outputs.skip != 'true'
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GHCR_TOKEN }}
- name: Extract image name
if: steps.check-skip.outputs.skip != 'true'
id: image
run: |
REGISTRY="${{ needs.prepare-matrix.outputs.registry }}"
VERSION="${{ needs.determine-version.outputs.version }}"
IMAGE_NAME="otel-${{ matrix.name }}"
if [ -n "${{ matrix.variant }}" ]; then
VERSION_TAG="${VERSION}-$(echo '${{ matrix.variant }}' | tr '[:upper:]' '[:lower:]')"
else
VERSION_TAG="${VERSION}"
fi
echo "full_image=${REGISTRY}/${IMAGE_NAME}:${VERSION_TAG}" >> $GITHUB_OUTPUT
echo "version_tag=${VERSION_TAG}" >> $GITHUB_OUTPUT
- name: Check for Dockerfile
if: steps.check-skip.outputs.skip != 'true'
id: check
run: |
if [ -f "${{ matrix.dockerfile }}" ]; then
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "exists=false" >> $GITHUB_OUTPUT
fi
- name: Build and push image
if: steps.check.outputs.exists == 'true'
uses: docker/build-push-action@v7
with:
context: .
file: ${{ matrix.dockerfile }}
target: ${{ matrix.target }}
platforms: ${{ matrix.platform }}
push: true
tags: ${{ steps.image.outputs.full_image }}
build-args: ${{ matrix.build_args }}
cache-from: type=gha,scope=${{ matrix.name }}
cache-to: type=gha,mode=max,scope=${{ matrix.name }}
no-cache: ${{ inputs.no_cache }}
- name: Image info
if: steps.check.outputs.exists == 'true'
run: |
echo "### ✅ Built: ${{ matrix.name }}${{ matrix.variant && format(' ({0})', matrix.variant) || '' }}" >> $GITHUB_STEP_SUMMARY
echo "Image: \`${{ steps.image.outputs.full_image }}\`" >> $GITHUB_STEP_SUMMARY
# ─────────────────────────────────────────────
# Step 4: Update versions + stitch manifests + PR (or pre-release for beta)
# ─────────────────────────────────────────────
update-and-release:
needs: [determine-version, prepare-matrix, build-images]
if: ${{ success() && github.repository == 'splunk/opentelemetry-demo' }}
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: '3.x'
- run: pip install pyyaml
- name: Make scripts executable
run: |
chmod +x .github/scripts/manage-hotfix.py
chmod +x .github/scripts/update-manifest-images.py
chmod +x .github/scripts/show-image-versions.py
chmod +x .github/scripts/stitch-manifests.sh
chmod +x .github/scripts/get-services.py
chmod +x .github/scripts/stage-values-with-header.sh
- name: Save current version before bump
id: old-version
run: echo "old_version=$(cat SPLUNK-VERSION)" >> $GITHUB_OUTPUT
- name: Update SPLUNK-VERSION
if: ${{ needs.determine-version.outputs.is_beta != 'true' && needs.determine-version.outputs.is_hotfix == 'false' && inputs.version_bump != 'none (keep current)' && inputs.version_bump != 'custom' }}
run: |
CURRENT=$(cat SPLUNK-VERSION)
NEW="${{ needs.determine-version.outputs.base_version }}"
echo "$NEW" > SPLUNK-VERSION
echo "✅ SPLUNK-VERSION: $CURRENT → $NEW"
- name: Clear hotfixes on full releases
if: ${{ needs.determine-version.outputs.is_full_release == 'true' }}
run: |
python3 .github/scripts/manage-hotfix.py clear
echo "✅ Hotfixes cleared"
- name: Update hotfix tracking
if: ${{ needs.determine-version.outputs.is_hotfix == 'true' }}
run: |
# determine-version ran manage-hotfix in a separate job (separate
# runner/filesystem), so .hotfix.yaml does not exist in this job's
# checkout. Regenerate it here with the SAME first-service logic so
# the Create Pull Request step can `git add .hotfix.yaml`. Both jobs
# start from the same committed baseline on main, so the increment is
# deterministic and matches the computed version.
SERVICE_NAME=$(echo "${{ inputs.services }}" | tr ',' '\n' | head -1 | xargs)
python3 .github/scripts/manage-hotfix.py add "$SERVICE_NAME"
- name: Update source manifests with new image references
run: |
REGISTRY="${{ needs.prepare-matrix.outputs.registry }}"
VERSION="${{ needs.determine-version.outputs.version }}"
SERVICES_INPUT="${{ inputs.services }}"
if [ "$SERVICES_INPUT" = "all" ]; then
SERVICES_TO_UPDATE=$(python3 <<'PYTHON_SCRIPT'
import yaml
with open('services.yaml', 'r') as f:
config = yaml.safe_load(f)
services = [svc['name'] for svc in config.get('services', []) if svc.get('build', False)]
print(' '.join(services))
PYTHON_SCRIPT
)
else
SERVICES_TO_UPDATE=$(echo "$SERVICES_INPUT" | tr ',' ' ')
fi
echo "Updating source manifests for: $SERVICES_TO_UPDATE"
for SERVICE in $SERVICES_TO_UPDATE; do
if [ "$SERVICE" = "payment" ]; then
NEW_IMAGE="${REGISTRY}/otel-payment:${VERSION}"
for MANIFEST_PATH in src/payment/payment-vA-k8s.yaml src/payment/payment-vB-k8s.yaml; do
if [ -f "$MANIFEST_PATH" ]; then
sed -i.bak -E "s|image:[ ]+[^ ]+/otel-payment:[^ ]+|image: ${NEW_IMAGE}|g" "$MANIFEST_PATH"
rm -f "${MANIFEST_PATH}.bak"
echo " ✅ $MANIFEST_PATH → $NEW_IMAGE"
fi
done
else
python3 .github/scripts/update-manifest-images.py "$SERVICE" "$REGISTRY" "otel-${SERVICE}" "$VERSION"
fi
done
- name: Generate per-service version pinning for partial builds
# For partial builds (services!=all), the stitched manifest must
# pin only the freshly-built services at the new tag and leave
# everything else at the prior stable version. Otherwise pods
# ImagePullBackOff on tags that were never pushed for un-built
# services. The stitcher already supports this via
# .service-versions.yaml; we just need to generate it.
if: ${{ inputs.services != 'all' }}
run: |
OLD_VERSION="${{ steps.old-version.outputs.old_version }}"
NEW_VERSION="${{ needs.determine-version.outputs.version }}"
BUILT_SERVICES="${{ inputs.services }}"
python3 - "$BUILT_SERVICES" "$NEW_VERSION" "$OLD_VERSION" <<'PYEOF' > .service-versions.yaml
import sys, yaml
built = {s.strip() for s in sys.argv[1].split(',') if s.strip()}
new_version, old_version = sys.argv[2], sys.argv[3]
with open('services.yaml') as f:
cfg = yaml.safe_load(f)
print('services:')
for svc in cfg.get('services', []):
if svc.get('build', False):
name = svc['name']
pin = new_version if name in built else old_version
print(f" {name}: {pin}")
PYEOF
echo "Per-service version pinning (partial build):"
cat .service-versions.yaml
- name: Stitch production manifests
id: stitch
run: |
VERSION="${{ needs.determine-version.outputs.version }}"
OUTPUT_DIR="kubernetes"
# Temporarily set SPLUNK-VERSION to the target version (stitch-manifests.sh reads it)
echo "$VERSION" > SPLUNK-VERSION
echo "Stitching regular manifest (version: $VERSION)..."
.github/scripts/stitch-manifests.sh prod "" "$OUTPUT_DIR" ""
echo "Stitching DIAB manifest..."
.github/scripts/stitch-manifests.sh prod diab "$OUTPUT_DIR" ""
MANIFEST_FILE="${OUTPUT_DIR}/splunk-astronomy-shop-${VERSION}.yaml"
MANIFEST_FILE_DIAB="${OUTPUT_DIR}/splunk-astronomy-shop-${VERSION}-diab.yaml"
MANIFEST_LAMBDA="${OUTPUT_DIR}/splunk-astronomy-shop-${VERSION}-lambda.yaml"
MANIFEST_DC_SHIM="${OUTPUT_DIR}/splunk-astronomy-shop-${VERSION}-dc-shim.yaml"
MANIFEST_SECUREAPP="${OUTPUT_DIR}/splunk-astronomy-shop-${VERSION}-secureapp.yaml"
MANIFEST_THROTTLE_DEMO="${OUTPUT_DIR}/splunk-astronomy-shop-${VERSION}-throttle-demo.yaml"
echo "manifest_file=$MANIFEST_FILE" >> $GITHUB_OUTPUT
echo "manifest_file_diab=$MANIFEST_FILE_DIAB" >> $GITHUB_OUTPUT
echo "manifest_lambda=$MANIFEST_LAMBDA" >> $GITHUB_OUTPUT
echo "manifest_dc_shim=$MANIFEST_DC_SHIM" >> $GITHUB_OUTPUT
echo "manifest_secureapp=$MANIFEST_SECUREAPP" >> $GITHUB_OUTPUT
echo "manifest_throttle_demo=$MANIFEST_THROTTLE_DEMO" >> $GITHUB_OUTPUT
echo "Generated manifests:"
for MFILE in "$MANIFEST_FILE" "$MANIFEST_FILE_DIAB" "$MANIFEST_LAMBDA" "$MANIFEST_DC_SHIM" "$MANIFEST_SECUREAPP" "$MANIFEST_THROTTLE_DEMO"; do
[ -f "$MFILE" ] && echo " [ok] $(basename $MFILE)" || echo " [--] $(basename $MFILE) (not generated)"
done
# For non-beta: restore SPLUNK-VERSION to the base version (will be committed)
# For beta: restore to original (nothing gets committed)
IS_BETA="${{ needs.determine-version.outputs.is_beta }}"
if [[ "$IS_BETA" == "true" ]]; then
echo "${{ steps.old-version.outputs.old_version }}" > SPLUNK-VERSION
else
echo "${{ needs.determine-version.outputs.base_version }}" > SPLUNK-VERSION
fi
# Remove the temporary per-service pinning file so it never gets
# committed by downstream PR/branch steps.
rm -f .service-versions.yaml
- name: Ensure values.yaml exists for this version
if: ${{ needs.determine-version.outputs.is_beta != 'true' }}
id: values
run: |
VERSION=$(cat SPLUNK-VERSION)
OLD_VERSION="${{ steps.old-version.outputs.old_version }}"
VALUES_FILE="kubernetes/splunk-astronomy-shop-${VERSION}-values.yaml"
if [ -f "$VALUES_FILE" ]; then
echo "✅ Values file already exists: $VALUES_FILE"
else
# Find the most recent values file to clone from
# Prefer the previous SPLUNK-VERSION, fall back to latest available
SOURCE=""
# Try the version before bump
if [ -n "$OLD_VERSION" ] && [ -f "kubernetes/splunk-astronomy-shop-${OLD_VERSION}-values.yaml" ]; then
SOURCE="kubernetes/splunk-astronomy-shop-${OLD_VERSION}-values.yaml"
else
# Find the latest values file by version sort
SOURCE=$(ls kubernetes/splunk-astronomy-shop-*-values.yaml 2>/dev/null | sort -V | tail -1)
fi
if [ -n "$SOURCE" ] && [ -f "$SOURCE" ]; then
cp "$SOURCE" "$VALUES_FILE"
echo "📋 Cloned values file: $SOURCE → $VALUES_FILE"
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📋 Values File" >> $GITHUB_STEP_SUMMARY
echo "No values.yaml found for \`${VERSION}\`. Cloned from \`$(basename $SOURCE)\`." >> $GITHUB_STEP_SUMMARY
echo "You can update it later via a PR if needed." >> $GITHUB_STEP_SUMMARY
else
echo "⚠️ No values file found to clone from — promote workflow will need one manually"
echo "" >> $GITHUB_STEP_SUMMARY
echo "### ⚠️ Values File Missing" >> $GITHUB_STEP_SUMMARY
echo "No values.yaml found for \`${VERSION}\` and no previous version to clone from." >> $GITHUB_STEP_SUMMARY
echo "Create one manually before running the promote workflow." >> $GITHUB_STEP_SUMMARY
fi
fi
echo "values_file=$VALUES_FILE" >> $GITHUB_OUTPUT
- name: Validate manifest YAML
run: |
for MFILE in "${{ steps.stitch.outputs.manifest_file }}" \
"${{ steps.stitch.outputs.manifest_file_diab }}" \
"${{ steps.stitch.outputs.manifest_lambda }}" \
"${{ steps.stitch.outputs.manifest_dc_shim }}" \
"${{ steps.stitch.outputs.manifest_secureapp }}" \
"${{ steps.stitch.outputs.manifest_throttle_demo }}"; do
if [ -f "$MFILE" ]; then
python3 -c "import yaml; docs=list(yaml.safe_load_all(open('$MFILE'))); print(f'$(basename $MFILE): {len(docs)} documents')"
fi
done
- name: Show image version breakdown
run: |
VERSION="${{ needs.determine-version.outputs.version }}"
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 📦 Image Versions in Manifest" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
python3 .github/scripts/show-image-versions.py \
--base-version "$VERSION" \
--format github >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Summary:**" >> $GITHUB_STEP_SUMMARY
python3 .github/scripts/show-image-versions.py \
--base-version "$VERSION" \
--summary-only >> $GITHUB_STEP_SUMMARY
- name: Create GitHub pre-release (beta only)
if: ${{ needs.determine-version.outputs.is_beta == 'true' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION="${{ needs.determine-version.outputs.version }}"
SERVICES="${{ inputs.services }}"
# Collect manifest files
ASSETS=""
for MFILE in "${{ steps.stitch.outputs.manifest_file }}" \
"${{ steps.stitch.outputs.manifest_file_diab }}" \
"${{ steps.stitch.outputs.manifest_lambda }}" \
"${{ steps.stitch.outputs.manifest_dc_shim }}" \
"${{ steps.stitch.outputs.manifest_secureapp }}" \
"${{ steps.stitch.outputs.manifest_throttle_demo }}"; do
[ -f "$MFILE" ] && ASSETS="$ASSETS $MFILE"
done
# Also attach the helm values.yaml for this version so
# `helm upgrade --install ... -f splunk-astronomy-shop-${VERSION}-values.yaml`
# can be scripted straight from the release assets. The
# earlier "Ensure values.yaml exists" step guarantees the
# source file is present (cloned from previous version if new).
# Version header is prepended to the release-asset copy (source
# in kubernetes/ stays untouched).
STAGED_VALUES=$(.github/scripts/stage-values-with-header.sh "$VERSION")
[ -n "$STAGED_VALUES" ] && [ -f "$STAGED_VALUES" ] && ASSETS="$ASSETS $STAGED_VALUES"
# Delete existing pre-release if regenerating
if gh release view "v${VERSION}" &>/dev/null; then
echo "Replacing existing pre-release v${VERSION}..."
gh release delete "v${VERSION}" --yes 2>/dev/null || true
git push origin --delete "v${VERSION}" 2>/dev/null || true
fi
# Create pre-release with manifests attached
gh release create "v${VERSION}" $ASSETS \
--title "v${VERSION} (beta)" \
--prerelease \
--notes "$(printf '%s\n' \
"## Beta Release ${VERSION}" \
"" \
"**Base version:** \`${{ needs.determine-version.outputs.base_version }}\`" \
"**Services:** ${SERVICES}" \
"**Registry:** \`${{ needs.prepare-matrix.outputs.registry }}\`" \
"" \
"### Deploy for testing" \
"\`\`\`bash" \
"# Download manifests from this release, then:" \
"kubectl apply -f splunk-astronomy-shop-${VERSION}.yaml" \
"\`\`\`" \
"" \
"> This is a **pre-release** for testing. Run the workflow again without" \
"> the beta flag to create the final release and bump SPLUNK-VERSION." \
"" \
"---" \
"Generated by Production Release workflow (beta mode)"
)"
RELEASE_URL=$(gh release view "v${VERSION}" --json url -q .url)
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 🧪 Beta Pre-Release Created" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**[v${VERSION}]($RELEASE_URL)**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Download manifests from the release to deploy and test." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "When satisfied, re-run this workflow **without** the beta flag to finalize." >> $GITHUB_STEP_SUMMARY
- name: Create Pull Request (non-beta)
if: ${{ needs.determine-version.outputs.is_beta != 'true' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
VERSION="${{ needs.determine-version.outputs.version }}"
IS_HOTFIX="${{ needs.determine-version.outputs.is_hotfix }}"
IS_FULL_RELEASE="${{ needs.determine-version.outputs.is_full_release }}"
IS_CUSTOM="${{ needs.determine-version.outputs.is_custom }}"
SERVICES="${{ inputs.services }}"
BRANCH_NAME="release/${VERSION}"
# Stage source changes only (no stitched manifests)
if [[ "$IS_HOTFIX" == "true" ]]; then
git add .hotfix.yaml
fi
if [[ "$IS_HOTFIX" == "false" && "$IS_CUSTOM" == "false" && "${{ inputs.version_bump }}" != "none (keep current)" ]]; then
git add SPLUNK-VERSION
fi
if [[ "$IS_FULL_RELEASE" == "true" ]]; then
if ! [ -f .hotfix.yaml ]; then
git rm --ignore-unmatch .hotfix.yaml 2>/dev/null || true
fi
fi
# Stage source manifests and values files only
# Stitched manifests are gitignored — they go to GitHub Release assets
git add src/*/*-k8s.yaml
git add kubernetes/splunk-astronomy-shop-*-values.yaml 2>/dev/null || true
if git diff --staged --quiet; then
echo "No changes to commit"
exit 0
fi
git checkout -b "$BRANCH_NAME"
# Commit message
if [[ "$IS_HOTFIX" == "true" ]]; then
COMMIT_TITLE="hotfix: ${VERSION} (${SERVICES})"
PR_TITLE="Hotfix: ${SERVICES} @ ${VERSION}"
elif [[ "$IS_CUSTOM" == "true" ]]; then
COMMIT_TITLE="build: ${SERVICES} @ ${VERSION} (custom)"
PR_TITLE="Custom Build: ${SERVICES} @ ${VERSION}"
elif [[ "$IS_FULL_RELEASE" == "true" ]]; then
COMMIT_TITLE="release: ${VERSION}"
PR_TITLE="Release ${VERSION}"
else
COMMIT_TITLE="build: ${VERSION} (${SERVICES})"
PR_TITLE="Production Build: ${VERSION}"
fi
git commit -m "$COMMIT_TITLE" \
-m "" \
-m "Services: ${SERVICES}" \
-m "Bump: ${{ inputs.version_bump }}" \
-m "" \
-m "Includes:" \
-m "- Container images built and pushed" \
-m "- SPLUNK-VERSION updated" \
-m "- Source k8s manifests updated"
# Delete remote branch if exists
git push origin --delete "$BRANCH_NAME" 2>/dev/null || true
git push -u origin "$BRANCH_NAME"
# Build PR body
PR_BODY=$(printf '%s\n' \
"## ${PR_TITLE}" \
"" \
"**Version:** \`${VERSION}\`" \
"**Services:** ${SERVICES}" \
"**Bump:** ${{ inputs.version_bump }}" \
"" \
"### What's included" \
"- ✅ Container images built and pushed to \`ghcr.io/splunk/opentelemetry-demo\`" \
"- ✅ SPLUNK-VERSION updated" \
"- ✅ Source k8s manifests updated with new image references" \
"" \
"### Manifests" \
"Stitched manifests will be attached to the GitHub Release when this PR is merged." \
"" \
"See the **Image Versions in Manifest** section in the workflow summary for the full breakdown." \
"" \
"---" \
"Generated by Production Release workflow"
)
gh pr create \
--title "$PR_TITLE" \
--body "$PR_BODY" \
--base main \
--head "$BRANCH_NAME"
PR_URL=$(gh pr view "$BRANCH_NAME" --json url -q .url)
echo "" >> $GITHUB_STEP_SUMMARY
echo "### ✅ Pull Request Created" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**[$PR_TITLE]($PR_URL)**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Branch: \`$BRANCH_NAME\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Merge this PR to apply all changes." >> $GITHUB_STEP_SUMMARY
echo "Stitched manifests will be attached to the release automatically." >> $GITHUB_STEP_SUMMARY
- name: Upload manifests as artifacts
uses: actions/upload-artifact@v7
with:
name: astronomy-shop-manifests-${{ needs.determine-version.outputs.version }}
path: |
${{ steps.stitch.outputs.manifest_file }}
${{ steps.stitch.outputs.manifest_file_diab }}
${{ steps.stitch.outputs.manifest_lambda }}
${{ steps.stitch.outputs.manifest_dc_shim }}
${{ steps.stitch.outputs.manifest_secureapp }}
retention-days: 90
# ─────────────────────────────────────────────
# Summary
# ─────────────────────────────────────────────
summary:
needs: [determine-version, prepare-matrix, build-images, update-and-release]
if: always()
runs-on: ubuntu-latest
steps:
- name: Build Summary
run: |
IS_BETA="${{ needs.determine-version.outputs.is_beta }}"
if [[ "$IS_BETA" == "true" ]]; then
echo "## 🧪 Beta Release Complete" >> $GITHUB_STEP_SUMMARY
else
echo "## Production Release Complete" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Item | Value |" >> $GITHUB_STEP_SUMMARY
echo "|------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| **Version** | \`${{ needs.determine-version.outputs.version }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| **Bump** | ${{ inputs.version_bump }} |" >> $GITHUB_STEP_SUMMARY
echo "| **Services** | ${{ inputs.services }} |" >> $GITHUB_STEP_SUMMARY
echo "| **Beta** | ${{ needs.determine-version.outputs.is_beta }} |" >> $GITHUB_STEP_SUMMARY
echo "| **Registry** | \`${{ needs.prepare-matrix.outputs.registry }}\` |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [[ "$IS_BETA" == "true" ]]; then
echo "✅ Images built → ✅ Manifests stitched → ✅ Pre-release created" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "> **Deploy the beta manifests to test. Re-run without beta to finalize.**" >> $GITHUB_STEP_SUMMARY
else
echo "✅ Images built → ✅ Versions updated → ✅ Manifests stitched → ✅ PR created" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "> **Merge the PR. Manifests are attached to the release after merge.**" >> $GITHUB_STEP_SUMMARY
fi