Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/bootstrap-fog-version-check-stub-sweep.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Sweep for branches missing the FOG version check stub

# Runs daily. Lists fogproject branches, filters to the ones
# check-fog-version.yml watches (working-1.6/dev-branch/rc-*/feature-*,
# `stable` excluded), and hands off any that are missing the stub to
# bootstrap-fog-version-check-stub.yml.
#
# This exists instead of a create-triggered stub in fogproject because
# GitHub only resolves a `create`-event workflow from the repo's default
# branch, which for fogproject is `stable` - and this project's automation
# is kept off of `stable` entirely. A daily delay before a brand-new branch
# starts being watched is an acceptable trade for that: it only delays when
# watching starts, it doesn't cause a missed version fix on a branch
# already being watched.

on:
schedule:
- cron: "30 4 * * *"
workflow_dispatch: {}

jobs:
discover:
runs-on: ubuntu-24.04
outputs:
branches: ${{ steps.discover.outputs.branches }}
steps:
- uses: actions/create-github-app-token@v3
id: app-token
with:
client-id: ${{ vars.FOG_WORKFLOWS_APPID }}
private-key: ${{ secrets.FOG_WORKFLOWS_PRIVATE_KEY }}
owner: FOGProject
repositories: "fogproject"

- name: Find watched branches missing the stub
id: discover
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
set -e
all_branches=$(gh api repos/FOGProject/fogproject/branches --paginate --jq '.[].name')

missing=()
for b in $all_branches; do
case "$b" in
working-1.6|dev-branch|rc-*|feature-*) ;;
*) continue ;;
esac
if ! gh api "repos/FOGProject/fogproject/contents/.github/workflows/check-fog-version.yml?ref=$b" >/dev/null 2>&1; then
missing+=("$b")
fi
done

json=$(printf '%s\n' "${missing[@]:-}" | jq -R . | jq -sc 'map(select(length > 0))')
echo "branches=$json" >> "$GITHUB_OUTPUT"
echo "Missing the stub on: $json"

bootstrap:
needs: discover
if: needs.discover.outputs.branches != '[]'
strategy:
matrix:
branch: ${{ fromJson(needs.discover.outputs.branches) }}
uses: ./.github/workflows/bootstrap-fog-version-check-stub.yml
with:
branch: ${{ matrix.branch }}
89 changes: 89 additions & 0 deletions .github/workflows/bootstrap-fog-version-check-stub.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: Bootstrap FOG version check stub

# Single-branch worker, called by bootstrap-fog-version-check-stub-sweep.yml
# for each fogproject branch missing the stub (also runnable by hand via
# workflow_dispatch against one branch).
#
# check-fog-version.yml's push trigger only fires on a branch that already
# has a copy of that stub file committed to it. New feature-*/rc-* branches
# cut from dev-branch inherit it for free once dev-branch has it, but a
# branch created from anywhere else (or before dev-branch had it) would
# silently never be watched. This copies the canonical stub - whatever is
# currently committed on dev-branch - onto the given branch directly, if it
# matches the watched patterns and doesn't already have it.
#
# Never touches `stable` (excluded from the watched patterns, same as
# check-fog-version.yml itself) or any branch outside those patterns.
# Deliberately not triggered by anything in fogproject itself (unlike
# check-fog-version.yml's push stub) - GitHub only resolves a `create`-event
# workflow from fogproject's default branch, which is `stable`, and this
# repo's automation is kept off of `stable` entirely. The sweep workflow
# polls instead.

on:
workflow_call:
inputs:
branch:
required: true
type: string
workflow_dispatch:
inputs:
branch:
description: "fogproject branch to bootstrap the stub onto"
required: true
type: string

jobs:
bootstrap-stub:
runs-on: ubuntu-24.04
steps:
- name: Check branch matches watched patterns
id: match
env:
BRANCH: ${{ inputs.branch }}
run: |
matches=false
case "$BRANCH" in
working-1.6|dev-branch|rc-*|feature-*) matches=true ;;
esac
echo "matches=$matches" >> "$GITHUB_OUTPUT"

- uses: actions/create-github-app-token@v3
if: steps.match.outputs.matches == 'true'
id: app-token
with:
client-id: ${{ vars.FOG_WORKFLOWS_APPID }}
private-key: ${{ secrets.FOG_WORKFLOWS_PRIVATE_KEY }}
owner: FOGProject
repositories: "fogproject"

- uses: actions/checkout@v7
if: steps.match.outputs.matches == 'true'
with:
token: ${{ steps.app-token.outputs.token }}
repository: FOGProject/fogproject
ref: ${{ inputs.branch }}

- name: Copy the stub from dev-branch if this branch doesn't have it yet
if: steps.match.outputs.matches == 'true'
env:
BRANCH: ${{ inputs.branch }}
BOT_SLUG: ${{ steps.app-token.outputs.app-slug }}
run: |
set -e
stub_path=".github/workflows/check-fog-version.yml"

if [ -f "$stub_path" ]; then
echo "Stub already present on $BRANCH - nothing to do."
exit 0
fi

git fetch --depth 1 origin dev-branch
mkdir -p "$(dirname "$stub_path")"
git show origin/dev-branch:"$stub_path" > "$stub_path"

git config user.name "${BOT_SLUG}[bot]"
git config user.email "${BOT_SLUG}[bot]@users.noreply.github.com"
git add "$stub_path"
git commit -m "ci: bootstrap check-fog-version stub on ${BRANCH}"
git push origin "HEAD:${BRANCH}"
61 changes: 61 additions & 0 deletions .github/workflows/check-fog-version-sweep.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Sweep FOG version check across all watched branches

# Failsafe catch-all, running daily in addition to the push-triggered stub in
# fogproject. The push trigger should catch essentially everything, but this
# covers anything it misses - a missed webhook delivery, an Actions outage
# during the original push, a branch whose stub hadn't been bootstrapped yet
# at push time, etc.
#
# Lists fogproject branches, filters to the ones check-fog-version.yml
# watches (working-1.6/dev-branch/rc-*/feature-*, `stable` excluded - owned
# by stable-releases.yml's release flow), and runs the real check-and-fix
# workflow against every one of them, whether or not the push stub is even
# present there yet.

on:
schedule:
- cron: "45 4 * * *"
workflow_dispatch: {}

jobs:
discover:
runs-on: ubuntu-24.04
outputs:
branches: ${{ steps.discover.outputs.branches }}
steps:
- uses: actions/create-github-app-token@v3
id: app-token
with:
client-id: ${{ vars.FOG_WORKFLOWS_APPID }}
private-key: ${{ secrets.FOG_WORKFLOWS_PRIVATE_KEY }}
owner: FOGProject
repositories: "fogproject"

- name: List watched branches
id: discover
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
set -e
all_branches=$(gh api repos/FOGProject/fogproject/branches --paginate --jq '.[].name')

watched=()
for b in $all_branches; do
case "$b" in
working-1.6|dev-branch|rc-*|feature-*) watched+=("$b") ;;
esac
done

json=$(printf '%s\n' "${watched[@]:-}" | jq -R . | jq -sc 'map(select(length > 0))')
echo "branches=$json" >> "$GITHUB_OUTPUT"
echo "Watched branches: $json"

check-version:
needs: discover
if: needs.discover.outputs.branches != '[]'
strategy:
matrix:
branch: ${{ fromJson(needs.discover.outputs.branches) }}
uses: ./.github/workflows/check-fog-version.yml
with:
branch: ${{ matrix.branch }}
132 changes: 132 additions & 0 deletions .github/workflows/check-fog-version.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
name: Check FOG version

# Reusable workflow backing:
# - the push-triggered stub in FOGProject/fogproject's
# .github/workflows/check-fog-version.yml (instant, per-branch), and
# - check-fog-version-sweep.yml in this repo (daily, all watched branches -
# a failsafe catch-all for anything the push trigger misses, e.g. a
# missed webhook delivery or an Actions outage during the original push).
# Also directly workflow_dispatch-able against a single branch by hand.
# See fog-docs' Development section for the full picture of why this is
# split across two repos.
#
# Recomputes FOG_VERSION/FOG_CHANNEL using the same formula as fogproject's
# .githooks/pre-commit. That hook is client-side and never runs on a PR merged
# via GitHub's web UI, so this independently recomputes what the version
# should be and, if it disagrees with what's actually committed, pushes the
# fix directly to the branch that drifted - no fix branch, no PR, since this
# is a mechanical correction of the same kind the hook already makes locally.
#
# Never invoked for `stable` - that branch's version is owned entirely by
# stable-releases.yml's release flow.

on:
workflow_call:
inputs:
branch:
required: true
type: string
workflow_dispatch:
inputs:
branch:
description: "fogproject branch to check"
required: true
type: string

jobs:
check-version:
runs-on: ubuntu-24.04
steps:
- uses: actions/create-github-app-token@v3
id: app-token
with:
client-id: ${{ vars.FOG_WORKFLOWS_APPID }}
private-key: ${{ secrets.FOG_WORKFLOWS_PRIVATE_KEY }}
owner: FOGProject
repositories: "fogproject"

- uses: actions/checkout@v7
with:
token: ${{ steps.app-token.outputs.token }}
repository: FOGProject/fogproject
ref: ${{ inputs.branch }}
fetch-depth: 0
fetch-tags: true

- name: Fetch master (needed by the version formula)
run: git fetch origin master:master

- name: Compute expected FOG_VERSION / FOG_CHANNEL
env:
GITBRANCH: ${{ inputs.branch }}
run: |
set -e
system_file="packages/web/lib/fog/system.class.php"

gitcom=$(git rev-list --tags --no-walk --max-count=1)
gitcount=$(git rev-list master..HEAD --count)

branchon=$(echo "$GITBRANCH" | awk -F'-' '{print $1}')
branchend=$(echo "$GITBRANCH" | awk -F'-' '{print $2}')

current_version=$(grep "define('FOG_VERSION'" "$system_file" | sed "s/.*FOG_VERSION', '\([^']*\)');/\1/")
current_channel=$(grep "define('FOG_CHANNEL'" "$system_file" | sed "s/.*FOG_CHANNEL', '\([^']*\)');/\1/")

verbegin=""
channel="$current_channel"
trunkversion="$current_version"

case "$branchon" in
dev)
tagversion=$(git describe --tags "$gitcom")
baseversion=${tagversion%.*}
trunkversion="${baseversion}.${gitcount}"
channel="Patches"
;;
working)
verbegin="${branchend}.0-beta"
trunkversion="${verbegin}.${gitcount}"
channel="Beta"
;;
rc)
channel="Release Candidate"
version_prefix="${branchend}.0-RC"
n=$(printf '%s\n' "$current_version" | sed -n "s/^${version_prefix}-\([0-9][0-9]*\)\$/\1/p")
if [ -n "$n" ]; then
trunkversion="${version_prefix}-$((n + 1))"
else
trunkversion="${version_prefix}-1"
fi
;;
feature)
verbegin="${branchend}.0-feature"
trunkversion="${verbegin}.${gitcount}"
channel="Feature"
;;
esac

sed -i "s/define('FOG_VERSION',.*);/define('FOG_VERSION', '$trunkversion');/g" "$system_file"
sed -i "s/define('FOG_CHANNEL',.*);/define('FOG_CHANNEL', '$channel');/g" "$system_file"

- name: Commit and push if the committed version is stale
env:
GITBRANCH: ${{ inputs.branch }}
BOT_SLUG: ${{ steps.app-token.outputs.app-slug }}
run: |
set -e
system_file="packages/web/lib/fog/system.class.php"

if git diff --quiet -- "$system_file"; then
echo "Version already correct on $GITBRANCH - nothing to do."
exit 0
fi

echo "Version drift detected on $GITBRANCH:"
git diff -- "$system_file"

git config user.name "${BOT_SLUG}[bot]"
git config user.email "${BOT_SLUG}[bot]@users.noreply.github.com"

git add "$system_file"
git commit -m "chore: fix stale FOG_VERSION/FOG_CHANNEL on ${GITBRANCH}"
git push origin "HEAD:${GITBRANCH}"