Skip to content

upstream-check

upstream-check #1

name: upstream-check
# Watches every upstream for new stable releases, so a security fix does not
# sit unnoticed. Opens an issue rather than auto-merging, because a new tag can
# move the lines a patch depends on.
on:
schedule:
- cron: '17 6 * * *'
workflow_dispatch:
permissions:
contents: read
issues: write
jobs:
check:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- key: SERVER_TAG
repo: mattermost/mattermost
patches: patches/server
- key: CALLS_TAG
repo: mattermost/mattermost-plugin-calls
patches: patches/calls
steps:
- uses: actions/checkout@v4
- id: check
run: |
set -euo pipefail
source upstream.env
current="${!MATRIX_KEY}"
if ! refs="$(git ls-remote --tags --refs "https://github.com/${{ matrix.repo }}.git" 2>/dev/null)"; then
echo "::error::Could not reach ${{ matrix.repo }}"; exit 1
fi
latest="$(printf '%s\n' "$refs" | awk -F/ '{print $NF}' \
| grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1)"
if [ -z "$latest" ]; then echo "::error::No stable tags found"; exit 1; fi
if [ "$latest" != "$current" ]; then echo "new_tag=$latest" >> "$GITHUB_OUTPUT"; fi
echo "current=$current" >> "$GITHUB_OUTPUT"
env:
MATRIX_KEY: ${{ matrix.key }}
- id: verify
if: steps.check.outputs.new_tag
continue-on-error: true
run: |
git clone --quiet --depth 1 --branch "${{ steps.check.outputs.new_tag }}" \
"https://github.com/${{ matrix.repo }}.git" /tmp/upstream
for patch in ${{ matrix.patches }}/*.patch; do
[ -e "$patch" ] || continue
echo "checking $(basename "$patch")"
git -C /tmp/upstream apply --check "$patch"
done
- if: steps.check.outputs.new_tag
uses: actions/github-script@v7
env:
NEW_TAG: ${{ steps.check.outputs.new_tag }}
CURRENT: ${{ steps.check.outputs.current }}
REPO: ${{ matrix.repo }}
KEY: ${{ matrix.key }}
PATCHES_OK: ${{ steps.verify.outcome }}
with:
script: |
const { NEW_TAG, CURRENT, REPO, KEY, PATCHES_OK } = process.env;
const title = `${REPO} ${NEW_TAG} is available`;
const open = await github.rest.issues.listForRepo({
owner: context.repo.owner, repo: context.repo.repo,
state: 'open', labels: 'upstream',
});
if (open.data.some((i) => i.title === title)) return;
const clean = PATCHES_OK === 'success';
const body = clean
? `\`${REPO}\` released \`${NEW_TAG}\`, we are on \`${CURRENT}\`. Every patch still applies cleanly.\n\n`
+ `To ship it: set \`${KEY}=${NEW_TAG}\` in \`upstream.env\`, bump \`CALLS_VERSION\` if this is the calls plugin, and push a tag.`
: `\`${REPO}\` released \`${NEW_TAG}\`, we are on \`${CURRENT}\`. **At least one patch no longer applies** and needs rebasing.\n\n`
+ `The workflow log says which hunk failed. Remember that a patch which cannot be rebased in time can be dropped for one release rather than holding everything up.`;
await github.rest.issues.create({
owner: context.repo.owner, repo: context.repo.repo,
title, body, labels: ['upstream'],
});