Skip to content

Nightly Bump

Nightly Bump #166

Workflow file for this run

name: Nightly Bump
on:
schedule:
- cron: '0 2 * * *' # 2 AM UTC daily
workflow_dispatch: # allow manual trigger
concurrency:
group: nightly-bump
cancel-in-progress: true
permissions:
contents: write
pull-requests: write
issues: write
# ─────────────────────────────────────────────────────────────────────────────
# Nightly: bump every submodule to its latest upstream default branch and push
# the result to the throwaway `nightly/bump-candidate` branch. The reusable
# `tests-and-doctests` pipeline then runs the nix tests + doc-tests against that
# candidate state. ONLY when every test passes is the candidate promoted to
# `nightly/bump` and its PR opened/refreshed — so `nightly/bump` and its PR
# always point at a bumped state that is known-green.
#
# On failure: the candidate branch is left for inspection, `nightly/bump` and its
# PR are untouched, and the logos-core team is pinged via the failure tracking
# issue (notify-team) from the test pipeline.
#
# The exact same pipeline can be run manually against any branch — see
# .github/workflows/tests-and-doctests.yml (Actions tab → "Tests and Doc-tests"
# → Run workflow on your branch). That's the way to bump some modules in a
# branch and test the whole workspace state of that branch.
# ─────────────────────────────────────────────────────────────────────────────
jobs:
bump:
name: Bump submodules and stage candidate
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: write
outputs:
changed: ${{ steps.check.outputs.changed }}
date: ${{ steps.meta.outputs.date }}
repos: ${{ steps.check.outputs.repos }}
sha: ${{ steps.stage.outputs.sha }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Initialize submodules
run: |
git config --global url."https://github.com/".insteadOf "git@github.com:"
git submodule update --init --depth 1 --jobs 4
- name: Bump submodules to latest
run: |
# Set origin/HEAD for each submodule so --remote resolves the correct
# default branch (master or main) rather than always assuming master.
git submodule foreach --quiet '
git remote set-head origin --auto 2>/dev/null || true
'
git submodule update --remote --depth 1 --jobs 4
- name: Compute date
id: meta
run: echo "date=$(date -u +%Y-%m-%d)" >> "$GITHUB_OUTPUT"
- name: Check for changes
id: check
run: |
if git diff --quiet; then
echo "changed=false" >> "$GITHUB_OUTPUT"
echo "No submodule updates found." >> "$GITHUB_STEP_SUMMARY"
else
echo "changed=true" >> "$GITHUB_OUTPUT"
changed=""
while IFS= read -r line; do
[[ -z "$line" ]] && continue
repo=$(echo "$line" | sed 's|^repos/||')
changed="$changed $repo"
done < <(git diff --name-only -- repos/)
changed=$(echo "$changed" | xargs)
echo "repos=$changed" >> "$GITHUB_OUTPUT"
echo "## Updated repos" >> "$GITHUB_STEP_SUMMARY"
for r in $changed; do echo "- \`$r\`" >> "$GITHUB_STEP_SUMMARY"; done
fi
- name: Install Nix
if: steps.check.outputs.changed == 'true'
# Nix 2.35.1. Must stay >= 2.26: below that, `--override-input` DISCARDS the
# overridden input's own lock and resolves its children from the parent's stale
# lock, so a doc-test that overrides an input can silently build months-old deps
# and still report green. v27 shipped 2.22 and did exactly that.
uses: cachix/install-nix-action@v31.11.0
with:
extra_nix_config: |
experimental-features = nix-command flakes
- name: Setup Cachix
if: steps.check.outputs.changed == 'true'
uses: cachix/cachix-action@v15
with:
name: logos-co
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- name: Add workspace tools to PATH
if: steps.check.outputs.changed == 'true'
run: echo "${{ github.workspace }}/scripts" >> "$GITHUB_PATH"
- name: Sync dep-graph
if: steps.check.outputs.changed == 'true'
run: ws sync-graph --quiet
- name: Update flake.lock
if: steps.check.outputs.changed == 'true'
run: |
updated_inputs=""
for repo in ${{ steps.check.outputs.repos }}; do
if grep -qE "^[[:space:]]*${repo}([[:space:]]*=|\.)" flake.nix; then
echo "Updating flake input: $repo"
nix flake lock --update-input "$repo"
updated_inputs="$updated_inputs $repo"
else
echo "Skipping $repo (not a top-level flake input)"
fi
done
if [[ -n "$updated_inputs" ]]; then
echo "## Updated flake.lock inputs" >> "$GITHUB_STEP_SUMMARY"
for r in $updated_inputs; do echo "- \`$r\`" >> "$GITHUB_STEP_SUMMARY"; done
else
echo "No matching flake inputs to update." >> "$GITHUB_STEP_SUMMARY"
fi
# Push the bumped workspace to the throwaway `nightly/bump-candidate` branch
# so the reusable test pipeline can check it out. Nothing consumes this
# branch except that pipeline; `nightly/bump` and its PR are only advanced
# to this state after every test passes (see the promote job).
- name: Stage candidate branch
id: stage
if: steps.check.outputs.changed == 'true'
run: |
branch="nightly/bump-candidate"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git checkout -b "$branch"
git add repos/ nix/dep-graph.nix flake.nix flake.lock
git commit -m "$(printf '%s\n\n%s\n' \
"chore: nightly submodule bump ${{ steps.meta.outputs.date }}" \
"Updated: ${{ steps.check.outputs.repos }}")"
git push --force -u origin "$branch"
echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
# Run the nix tests + doc-tests against the staged candidate state, publish the
# reports under nightly/<date>/, and notify the logos-core team on any failure.
# No PR comment here: the PR is created/refreshed only after this passes, in the
# promote job below.
tests-and-doctests:
needs: bump
if: ${{ needs.bump.outputs.changed == 'true' }}
uses: ./.github/workflows/tests-and-doctests.yml
with:
ref: nightly/bump-candidate
report-base: nightly/${{ needs.bump.outputs.date }}
notify-team: true
secrets: inherit
# Promote the tested-green candidate to `nightly/bump` and open/refresh its PR.
# Gated on the WHOLE test pipeline succeeding (result == 'success'), so
# `nightly/bump` and its PR never advance to a state that failed tests. If any
# test fails this job is skipped and `nightly/bump`/its PR stay as they were.
promote:
name: Promote candidate to nightly/bump
needs:
- bump
- tests-and-doctests
if: ${{ needs.bump.outputs.changed == 'true' && needs.tests-and-doctests.result == 'success' }}
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Promote candidate and open/refresh PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DATE: ${{ needs.bump.outputs.date }}
REPOS: ${{ needs.bump.outputs.repos }}
SHA: ${{ needs.bump.outputs.sha }}
run: |
branch="nightly/bump"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Fast-forward `nightly/bump` to the exact commit that passed tests.
git fetch origin nightly/bump-candidate
git push --force origin "$SHA:refs/heads/$branch"
owner="${GITHUB_REPOSITORY%%/*}"
repo="${GITHUB_REPOSITORY#*/}"
report="https://${owner}.github.io/${repo}/nightly/${DATE}/"
title="chore: nightly submodule bump ${DATE}"
body="$(printf '%s\n' \
'## Nightly Submodule Bump' \
'' \
'Bumped all submodules to their latest upstream default branch.' \
'' \
'### Updated repos' \
"$(for r in ${REPOS}; do echo "- \`$r\`"; done)" \
'' \
'### Checks' \
'✅ All nix tests + doc-tests passed against this state in the linked' \
'**Tests and Doc-tests** pipeline before this branch/PR was updated.' \
'' \
"Full doc-test report index: ${report}" \
'' \
'Auto-generated by the nightly bump workflow.')"
existing=$(gh pr list --head "$branch" --state open --json number --jq '.[0].number' 2>/dev/null || true)
if [[ -n "$existing" ]]; then
gh pr edit "$existing" --title "$title" --body "$body"
echo "Updated PR #$existing"
else
gh pr create --head "$branch" --title "$title" --body "$body"
echo "Created PR for $branch"
fi