Skip to content

Commit ad87598

Browse files
Copilotyurishkuro
andauthored
[jaeger] Add weekly workflow to auto-update Jaeger version from Docker Hub (#665)
* Initial plan * Add weekly workflow to update Jaeger version Co-authored-by: yurishkuro <[email protected]> * Address code review feedback: add yq installation, improve digest matching, add version validation Co-authored-by: yurishkuro <[email protected]> * Add explicit permissions block to limit GITHUB_TOKEN scope Co-authored-by: yurishkuro <[email protected]> * Refactor: extract script to .github/scripts/, use sed instead of yq, handle dry_run for cron Co-authored-by: yurishkuro <[email protected]> * rename token var Signed-off-by: Yuri Shkuro <[email protected]> --------- Signed-off-by: Yuri Shkuro <[email protected]> Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: yurishkuro <[email protected]> Co-authored-by: Yuri Shkuro <[email protected]>
1 parent 814d184 commit ad87598

File tree

2 files changed

+226
-0
lines changed

2 files changed

+226
-0
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#!/bin/bash
2+
# Script to check for new Jaeger Docker Hub releases and update Chart.yaml
3+
# Can be run standalone for testing or via the GitHub Actions workflow
4+
#
5+
# Usage: ./update-jaeger-version.sh [--dry-run]
6+
#
7+
# Environment variables:
8+
# DRY_RUN - Set to 'true' to skip making changes (same as --dry-run flag)
9+
# GITHUB_OUTPUT - If set, outputs will be written for GitHub Actions
10+
11+
set -eo pipefail
12+
13+
DOCKER_IMAGE="jaegertracing/jaeger"
14+
CHART_PATH="charts/jaeger/Chart.yaml"
15+
16+
# Parse command line arguments
17+
for arg in "$@"; do
18+
case $arg in
19+
--dry-run)
20+
DRY_RUN="true"
21+
shift
22+
;;
23+
esac
24+
done
25+
26+
# Default DRY_RUN to false if not set
27+
DRY_RUN="${DRY_RUN:-false}"
28+
29+
echo "1. Checking latest Docker tag for ${DOCKER_IMAGE}..."
30+
31+
# --- 1. Get the latest semantic version tag by checking the digest of the 'latest' tag ---
32+
echo " -> Fetching image digest for 'latest' tag..."
33+
# Get all digests from the 'latest' tag (multi-platform images have multiple digests)
34+
LATEST_RESPONSE=$(curl -sf "https://registry.hub.docker.com/v2/repositories/${DOCKER_IMAGE}/tags/latest")
35+
if [[ $? -ne 0 || -z "$LATEST_RESPONSE" ]]; then
36+
echo "Error: Failed to fetch 'latest' tag from Docker Hub. Exiting."
37+
exit 1
38+
fi
39+
40+
LATEST_DIGESTS=$(echo "$LATEST_RESPONSE" | jq -r '.images[].digest | select(. != null)')
41+
42+
if [[ -z "$LATEST_DIGESTS" ]]; then
43+
echo "Error: Could not retrieve valid digests for the 'latest' tag. Exiting."
44+
exit 1
45+
fi
46+
echo " -> Latest digests found:"
47+
echo "$LATEST_DIGESTS" | head -3
48+
49+
# Fetch a list of tags and filter to find the one that:
50+
# 1. Has any digest matching the 'latest' tag digests.
51+
# 2. Matches the semantic version pattern (e.g., 2.39.0).
52+
echo " -> Searching through tags for a semantic version matching these digests..."
53+
TAGS_JSON=$(curl -sf "https://registry.hub.docker.com/v2/repositories/${DOCKER_IMAGE}/tags?page_size=100")
54+
if [[ $? -ne 0 || -z "$TAGS_JSON" ]]; then
55+
echo "Error: Failed to fetch tags from Docker Hub. Exiting."
56+
exit 1
57+
fi
58+
59+
# Convert digests to a JSON array for jq comparison
60+
DIGESTS_ARRAY=$(echo "$LATEST_DIGESTS" | jq -R -s 'split("\n") | map(select(length > 0))')
61+
62+
LATEST_TAG=$(echo "$TAGS_JSON" | \
63+
jq -r --argjson digests "$DIGESTS_ARRAY" '
64+
.results[] |
65+
select(.images | map(.digest) | any(. as $d | $digests | index($d))) |
66+
.name' | \
67+
grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | \
68+
head -n 1)
69+
70+
if [[ -z "$LATEST_TAG" ]]; then
71+
echo "Error: Could not find a matching semantic version tag in the first 100 results. Exiting."
72+
exit 1
73+
fi
74+
75+
echo " -> Latest available version is: ${LATEST_TAG}"
76+
77+
# --- 2. Get the current appVersion and version from Chart.yaml using grep/sed ---
78+
CURRENT_APP_VERSION=$(grep '^appVersion:' "$CHART_PATH" | sed 's/appVersion: *//' | tr -d '"')
79+
CURRENT_CHART_VERSION=$(grep '^version:' "$CHART_PATH" | sed 's/version: *//' | tr -d '"')
80+
81+
echo " -> Current appVersion in ${CHART_PATH} is: ${CURRENT_APP_VERSION}"
82+
echo " -> Current chart version in ${CHART_PATH} is: ${CURRENT_CHART_VERSION}"
83+
84+
# --- 3. Compare and determine if update is needed ---
85+
if [[ "$LATEST_TAG" == "$CURRENT_APP_VERSION" ]]; then
86+
echo "Versions match. No update needed."
87+
if [[ -n "$GITHUB_OUTPUT" ]]; then
88+
echo "update_needed=false" >> "$GITHUB_OUTPUT"
89+
fi
90+
if [[ "$DRY_RUN" == "true" ]]; then
91+
echo "=============================================="
92+
echo "DRY RUN MODE - No changes needed"
93+
echo "=============================================="
94+
fi
95+
exit 0
96+
fi
97+
98+
echo "Update needed: appVersion change from ${CURRENT_APP_VERSION} to ${LATEST_TAG}."
99+
100+
# --- 4. Calculate new chart version (bump minor version) ---
101+
# Validate that the current chart version follows semantic versioning (X.Y.Z)
102+
if ! [[ "$CURRENT_CHART_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
103+
echo "Error: Current chart version '${CURRENT_CHART_VERSION}' does not match expected format X.Y.Z. Exiting."
104+
exit 1
105+
fi
106+
107+
# Parse the current chart version (e.g., 4.0.0 -> major=4, minor=0, patch=0)
108+
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_CHART_VERSION"
109+
NEW_MINOR=$((MINOR + 1))
110+
NEW_CHART_VERSION="${MAJOR}.${NEW_MINOR}.0"
111+
112+
echo " -> New chart version will be: ${NEW_CHART_VERSION}"
113+
114+
# Set outputs for GitHub Actions
115+
if [[ -n "$GITHUB_OUTPUT" ]]; then
116+
echo "update_needed=true" >> "$GITHUB_OUTPUT"
117+
echo "latest_tag=${LATEST_TAG}" >> "$GITHUB_OUTPUT"
118+
echo "current_app_version=${CURRENT_APP_VERSION}" >> "$GITHUB_OUTPUT"
119+
echo "current_chart_version=${CURRENT_CHART_VERSION}" >> "$GITHUB_OUTPUT"
120+
echo "new_chart_version=${NEW_CHART_VERSION}" >> "$GITHUB_OUTPUT"
121+
fi
122+
123+
# --- 5. Handle dry run mode ---
124+
if [[ "$DRY_RUN" == "true" ]]; then
125+
echo "=============================================="
126+
echo "DRY RUN MODE - No changes will be made"
127+
echo "=============================================="
128+
echo "Update IS needed:"
129+
echo " - appVersion: ${CURRENT_APP_VERSION} -> ${LATEST_TAG}"
130+
echo " - version: ${CURRENT_CHART_VERSION} -> ${NEW_CHART_VERSION}"
131+
echo "=============================================="
132+
exit 0
133+
fi
134+
135+
# --- 6. Update Chart.yaml using sed ---
136+
echo "Updating ${CHART_PATH}..."
137+
138+
# Update appVersion
139+
sed -i "s/^appVersion:.*/appVersion: ${LATEST_TAG}/" "$CHART_PATH"
140+
141+
# Update version
142+
sed -i "s/^version:.*/version: ${NEW_CHART_VERSION}/" "$CHART_PATH"
143+
144+
echo "Updated ${CHART_PATH}:"
145+
cat "$CHART_PATH"
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
name: Update Jaeger Version
2+
3+
on:
4+
schedule:
5+
# Runs once a week on Monday at 00:00 UTC
6+
- cron: '0 0 * * 1'
7+
workflow_dispatch:
8+
inputs:
9+
dry_run:
10+
description: 'Dry run mode - skip PR creation and only print status'
11+
required: false
12+
default: 'false'
13+
type: boolean
14+
15+
jobs:
16+
update-jaeger-version:
17+
runs-on: ubuntu-latest
18+
permissions:
19+
contents: read
20+
env:
21+
# Use the dry_run input if it exists (workflow_dispatch).
22+
# Otherwise, if the event is 'schedule' (or anything else), default to 'false'.
23+
DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run || 'false' }}
24+
steps:
25+
- name: ⬇️ Checkout repository
26+
uses: actions/checkout@v4
27+
28+
- name: 🔍 Check for new Jaeger version
29+
id: check_version
30+
run: bash ./.github/scripts/update-jaeger-version.sh
31+
32+
- name: ⚙️ Configure Git
33+
if: ${{ steps.check_version.outputs.update_needed == 'true' && env.DRY_RUN != 'true' }}
34+
run: |
35+
git config user.name "jaegertracingbot"
36+
git config user.email "[email protected]"
37+
38+
- name: 📝 Commit changes
39+
if: ${{ steps.check_version.outputs.update_needed == 'true' && env.DRY_RUN != 'true' }}
40+
id: commit
41+
run: |
42+
CHART_PATH="charts/jaeger/Chart.yaml"
43+
44+
# Check if any changes exist before committing
45+
if git diff --exit-code "$CHART_PATH"; then
46+
echo "No changes to commit. Exiting."
47+
echo "pr_needed=false" >> $GITHUB_OUTPUT
48+
else
49+
git add "$CHART_PATH"
50+
git commit -m "chore(deps): Bump Jaeger to ${{ steps.check_version.outputs.latest_tag }}"
51+
52+
# Create and switch to a new topic branch for the PR
53+
NEW_BRANCH="bot/update-jaeger-${{ steps.check_version.outputs.latest_tag }}"
54+
git checkout -b "$NEW_BRANCH"
55+
56+
echo "pr_needed=true" >> $GITHUB_OUTPUT
57+
echo "branch_name=${NEW_BRANCH}" >> $GITHUB_OUTPUT
58+
fi
59+
60+
- name: 🚀 Push new branch
61+
if: ${{ steps.commit.outputs.pr_needed == 'true' && env.DRY_RUN != 'true' }}
62+
run: |
63+
git push https://${{ secrets.JAEGERTRACINGBOT_PAT }}@github.com/${{ github.repository }} HEAD:${{ steps.commit.outputs.branch_name }}
64+
65+
- name: ✨ Create Pull Request
66+
if: ${{ steps.commit.outputs.pr_needed == 'true' && env.DRY_RUN != 'true' }}
67+
env:
68+
GH_TOKEN: ${{ secrets.JAEGERTRACINGBOT_PAT }}
69+
run: |
70+
gh pr create \
71+
--title "chore(deps): Bump Jaeger to ${{ steps.check_version.outputs.latest_tag }}" \
72+
--body "This PR was automatically generated to update the Jaeger Helm chart.
73+
74+
## Changes
75+
- **appVersion**: \`${{ steps.check_version.outputs.current_app_version }}\` → \`${{ steps.check_version.outputs.latest_tag }}\`
76+
- **version**: \`${{ steps.check_version.outputs.current_chart_version }}\` → \`${{ steps.check_version.outputs.new_chart_version }}\`
77+
78+
## Source
79+
Docker Hub image: [jaegertracing/jaeger](https://hub.docker.com/r/jaegertracing/jaeger)" \
80+
--base main \
81+
--head ${{ steps.commit.outputs.branch_name }}

0 commit comments

Comments
 (0)