name: Daily .NET Version Check
on:
schedule:
- cron: "0 8 * * *"
workflow_dispatch:
env:
DOCKER_IMAGE: docker.io/svrnty/dotnet
RELEASES_INDEX_URL: https://dotnetcli.azureedge.net/dotnet/release-metadata/releases-index.json
jobs:
check-versions:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Log in to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_SVRNTY_USERNAME }}
password: ${{ secrets.DOCKERHUB_SVRNTY_ACCESS_TOKEN }}
- name: Check for new .NET versions
id: check
run: |
set -euo pipefail
# Fetch release metadata
RELEASES=$(curl -fsSL "$RELEASES_INDEX_URL")
# Filter active/go-live versions (excludes EOL automatically)
SUPPORTED=$(echo "$RELEASES" | jq -c '[.["releases-index"][] | select(.["support-phase"] == "active" or .["support-phase"] == "go-live")]')
# Find highest LTS and STS majors
LTS_MAJOR=$(echo "$SUPPORTED" | jq -r '[.[] | select(.["release-type"] == "lts")] | sort_by(.["channel-version"] | split(".") | map(tonumber)) | last | .["channel-version"] | split(".")[0]')
STS_MAJOR=$(echo "$SUPPORTED" | jq -r '[.[] | select(.["release-type"] == "sts")] | sort_by(.["channel-version"] | split(".") | map(tonumber)) | last | .["channel-version"] | split(".")[0] // ""')
# Export for later steps
echo "supported=$SUPPORTED" >> $GITHUB_OUTPUT
echo "lts_major=$LTS_MAJOR" >> $GITHUB_OUTPUT
echo "sts_major=$STS_MAJOR" >> $GITHUB_OUTPUT
# Check if any new images need building
NEEDS_BUILD=false
while read -r CHANNEL RUNTIME SDK TYPE; do
MAJOR=$(echo "$CHANNEL" | cut -d. -f1)
echo "Checking .NET $MAJOR: runtime=$RUNTIME sdk=$SDK type=$TYPE"
if ! docker manifest inspect "$DOCKER_IMAGE:runtime-$RUNTIME" > /dev/null 2>&1; then
echo " New version detected: runtime-$RUNTIME"
NEEDS_BUILD=true
break
else
echo " Image runtime-$RUNTIME already exists, skipping"
fi
done < <(echo "$SUPPORTED" | jq -r '.[] | "\(.["channel-version"]) \(.["latest-runtime"]) \(.["latest-sdk"]) \(.["release-type"])"')
echo "needs_build=$NEEDS_BUILD" >> $GITHUB_OUTPUT
- name: Update README version tables
id: readme
run: |
set -euo pipefail
SUPPORTED='${{ steps.check.outputs.supported }}'
LTS_MAJOR="${{ steps.check.outputs.lts_major }}"
STS_MAJOR="${{ steps.check.outputs.sts_major }}"
ARCH_BADGES='
'
# Table headers
README_HEADER='| Version |
|
|
| Arch | EOL |'
README_SEP='|---------|---------|-------------------|-----|------|-----|'
DOCKERHUB_HEADER='| Version | `runtime` | `runtime-invariant` | `sdk` | Arch | EOL |'
DOCKERHUB_SEP='|---------|-----------|---------------------|-------|------|-----|'
echo "$README_HEADER" > /tmp/readme_rows.txt
echo "$README_SEP" >> /tmp/readme_rows.txt
echo "$DOCKERHUB_HEADER" > /tmp/dockerhub_rows.txt
echo "$DOCKERHUB_SEP" >> /tmp/dockerhub_rows.txt
> /tmp/badge_versions.txt
# Build version rows from API data (sorted by major descending)
echo "$SUPPORTED" | jq -r 'sort_by(.["channel-version"] | split(".") | map(tonumber)) | reverse | .[] | "\(.["channel-version"]) \(.["latest-runtime"]) \(.["latest-sdk"]) \(.["release-type"]) \(.["eol-date"] // "TBD")"' | \
while read -r CHANNEL RUNTIME SDK TYPE EOL; do
MAJOR=$(echo "$CHANNEL" | cut -d. -f1)
# Version label
if [ "$TYPE" = "lts" ]; then
LABEL="**.NET ${MAJOR}** (LTS)"
elif [ "$TYPE" = "sts" ]; then
LABEL="**.NET ${MAJOR}** (STS)"
else
LABEL="**.NET ${MAJOR}**"
fi
# Floating tags only for highest LTS/STS
if [ "$MAJOR" = "$LTS_MAJOR" ]; then
RT_TAGS="\`runtime-${MAJOR}\` \`runtime-lts\`"
RI_TAGS="\`runtime-invariant-${MAJOR}\` \`runtime-invariant-lts\`"
SDK_TAGS="\`sdk-${MAJOR}\` \`sdk-lts\`"
elif [ "$MAJOR" = "$STS_MAJOR" ]; then
RT_TAGS="\`runtime-${MAJOR}\` \`runtime-sts\`"
RI_TAGS="\`runtime-invariant-${MAJOR}\` \`runtime-invariant-sts\`"
SDK_TAGS="\`sdk-${MAJOR}\` \`sdk-sts\`"
else
RT_TAGS="\`runtime-${MAJOR}\`"
RI_TAGS="\`runtime-invariant-${MAJOR}\`"
SDK_TAGS="\`sdk-${MAJOR}\`"
fi
echo "| ${LABEL} | ${RT_TAGS} | ${RI_TAGS} | ${SDK_TAGS} | ${ARCH_BADGES} | ${EOL} |" >> /tmp/readme_rows.txt
echo "| ${LABEL} | ${RT_TAGS} | ${RI_TAGS} | ${SDK_TAGS} | amd64 arm64 | ${EOL} |" >> /tmp/dockerhub_rows.txt
echo -n "${MAJOR} " >> /tmp/badge_versions.txt
done
README_ROWS=$(cat /tmp/readme_rows.txt)
DOCKERHUB_ROWS=$(cat /tmp/dockerhub_rows.txt)
BADGE_VERSIONS=$(cat /tmp/badge_versions.txt | xargs | sed 's/ /%20|%20/g')
# Update README.md — version table
awk '
// { print; found=1; next }
// { found=0 }
found { next }
{ print }
' README.md > /tmp/readme_new.md
# Insert new rows after BEGIN marker
awk -v rows="$README_ROWS" '
// { print; print rows; next }
{ print }
' /tmp/readme_new.md > README.md
# Update README.md — .NET badge
sed -i "s|.*|
|" README.md
# Update DOCKERHUB.md — version table
awk '
// { print; found=1; next }
// { found=0 }
found { next }
{ print }
' DOCKERHUB.md > /tmp/dockerhub_new.md
awk -v rows="$DOCKERHUB_ROWS" '
// { print; print rows; next }
{ print }
' /tmp/dockerhub_new.md > DOCKERHUB.md
# Check if anything changed
if git diff --quiet README.md DOCKERHUB.md; then
echo "readme_changed=false" >> $GITHUB_OUTPUT
echo "READMEs are up to date"
else
echo "readme_changed=true" >> $GITHUB_OUTPUT
echo "READMEs need updating"
git diff README.md DOCKERHUB.md
fi
- name: Commit README updates
if: steps.readme.outputs.readme_changed == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add README.md DOCKERHUB.md
git commit -m "Update supported .NET versions in README"
git push
- name: Trigger build workflow
if: steps.check.outputs.needs_build == 'true'
uses: actions/github-script@v7
with:
script: |
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'build.yaml',
ref: context.ref
})