Skip to content

upstream-watch

upstream-watch #29

name: upstream-watch
# Keeps the repo "alive": daily check for a newer upstream ABINIT release.
# Opens an issue when one appears — a human still cuts the release tag.
#
# Daily, not weekly: 10.8.3 landed on a Friday and a Monday-only schedule sat
# blind on it for three days.
#
# The job FAILS (red run) when it cannot perform the check at all — an
# unreachable or reshaped abinit.org must not look like "nothing new".
on:
schedule:
- cron: '17 6 * * *' # daily 06:17 UTC
workflow_dispatch:
permissions:
contents: read
issues: write
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check for newer ABINIT release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MAINTAINER: krabiswabbie
run: |
set -euo pipefail
# abinit.org has a history of transient TLS/DNS failures, so retry
# before concluding anything.
fetch() {
for _ in 1 2 3; do
curl -fsSL --max-time 60 "$1" && return 0
sleep 20
done
return 1
}
current="$(grep -m1 -oE 'ABINIT_VERSION=[0-9.]+' Dockerfile | cut -d= -f2)"
echo "Pinned ABINIT: $current"
# Latest stable is announced on the ABINIT homepage as "Abinit X.Y.Z".
if ! page="$(fetch https://www.abinit.org/)"; then
echo "::error::abinit.org unreachable after 3 attempts — could not check for updates."
exit 1
fi
latest="$(printf '%s' "$page" \
| grep -oiE 'Abinit [0-9]+\.[0-9]+\.[0-9]+' \
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)"
if [ -z "$latest" ]; then
echo "::error::No version string on abinit.org — the page layout probably changed; this watcher needs fixing."
exit 1
fi
echo "Latest upstream: $latest"
if [ "$current" = "$latest" ]; then
echo "Up to date."; exit 0
fi
newest="$(printf '%s\n%s\n' "$current" "$latest" | sort -V | tail -1)"
if [ "$newest" != "$latest" ]; then
echo "Pinned ($current) is newer than/equal to upstream ($latest); nothing to do."; exit 0
fi
# Confirm the forge tarball our Dockerfile downloads actually exists.
# A 404 is a real "announced but not published yet" and is worth
# staying quiet about; a dead connection is not an answer at all and
# must not be reported as one. Capturing curl's output instead of
# `|| echo 000` matters — curl already prints 000 itself on a
# connection failure, so the two concatenate into a code that matches
# nothing.
if out="$(curl -sSL --max-time 60 -o /dev/null -w '%{http_code}' \
"https://forge.abinit.org/abinit-${latest}.tar.gz" 2>/dev/null)"; then
code="$out"
else
code="000"
fi
if [ "$code" = "000" ]; then
echo "::error::forge.abinit.org unreachable — could not confirm the ${latest} tarball."
exit 1
fi
if [ "$code" != "200" ]; then
echo "Upstream announces $latest but forge tarball returns HTTP $code; not published yet, skipping."
exit 0
fi
title="Upstream ABINIT ${latest} available (pinned: ${current})"
if gh issue list --state open --search "in:title \"${title}\"" \
--json title --jq '.[].title' | grep -Fxq "$title"; then
echo "Issue already open: $title"; exit 0
fi
body="$(printf '%s\n' \
"ABINIT **${latest}** is available upstream (forge tarball verified). This repo is pinned to **${current}**." \
"" \
"To release (per README \"Bumping the ABINIT version\"):" \
"1. Edit \`ARG ABINIT_VERSION\` default in \`Dockerfile\` (both stages) to \`${latest}\`." \
"2. Update the version examples in \`README.md\`." \
"3. Commit to \`main\`." \
"4. Cut the tag: \`git tag v${latest} && git push --tags\` — GHA publishes \`ghcr.io/material-codes/abinit-base:${latest}\`." \
"" \
"Review the upstream changelog before releasing — a human should eyeball ABINIT/LibXC/toolchain changes before publishing a new base image." \
"" \
"Publishing the image is only half of it. Nothing consumes the new tag until \`material/core\` moves its pins: \`ABINIT_VERSION\` in \`.gitlab-ci.yml\` (the source of truth, it feeds the runner build-args), the \`ARG\` default in \`Dockerfile.abinitrunner\`, and the ABINIT smoke version in the \`Makefile\` — the last two govern local builds and must not drift from the first. Adopt only after a live G0W0 run: the input-variable set and the stdout parser in core were derived by iterating against one ABINIT branch, so a minor-version move can leave the runner reading output it no longer understands." \
"" \
"cc @${MAINTAINER}" \
"" \
"_Opened automatically by the upstream-watch workflow._")"
# Assign + @mention deliberately: an issue opened by github-actions[bot]
# generates NO notification unless you watch the repo, while assignment
# and mentions land in the "Participating" class, which is emailed by
# default. If the assignment ever stops working the run goes red, which
# is itself the signal.
gh issue create --title "$title" --body "$body" --assignee "$MAINTAINER"