diff --git a/.github/workflows/maint-61-create-floating-v1-tag.yml b/.github/workflows/maint-61-create-floating-v1-tag.yml index 633c08c1d..48902dd30 100644 --- a/.github/workflows/maint-61-create-floating-v1-tag.yml +++ b/.github/workflows/maint-61-create-floating-v1-tag.yml @@ -1,12 +1,22 @@ name: Maint 61 Create Floating v1 Tag +# DEPRECATED: This workflow is disabled in favor of maint-73-refresh-reusable-tags.yml +# which tracks main HEAD instead of releases for immediate fix propagation. +# The v1 tag now follows a main-tracking strategy, not a release-tracking strategy. + on: workflow_dispatch: - schedule: - - cron: "30 3 * * *" - release: - types: - - published + inputs: + deprecated_notice: + description: 'DEPRECATED - Use maint-73-refresh-reusable-tags.yml instead' + required: false + type: string + # Disabled triggers - use maint-73 for tag updates + # schedule: + # - cron: "30 3 * * *" + # release: + # types: + # - published permissions: contents: write diff --git a/.github/workflows/maint-73-refresh-reusable-tags.yml b/.github/workflows/maint-73-refresh-reusable-tags.yml new file mode 100644 index 000000000..9883f056c --- /dev/null +++ b/.github/workflows/maint-73-refresh-reusable-tags.yml @@ -0,0 +1,106 @@ +name: Maint 73 Refresh Reusable Tags + +# Keep floating tags aligned with main so consumer repos always run the latest +# reusable workflows without editing every repo. +# NOTE: This replaces the release-tracking strategy from maint-61. The v1 tag +# now tracks main HEAD for immediate fix propagation, not latest v1.x release. + +on: + push: + branches: [main] + workflow_dispatch: + inputs: + tags: + description: 'Comma-separated tag names to refresh (default: v1)' + required: false + type: string + +permissions: + contents: write + +concurrency: + group: refresh-reusable-tags-${{ github.ref }} + cancel-in-progress: true + +jobs: + refresh: + name: Refresh floating tags + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Configure git + run: | + set -euo pipefail + git config user.name "stranske-automation-bot" + git config user.email "stranske-automation-bot@users.noreply.github.com" + + - name: Determine tags + id: tags + run: | + set -euo pipefail + input_tags="${{ inputs.tags }}" + if [ -z "$input_tags" ]; then + input_tags="v1" + fi + tags=$(echo "$input_tags" | tr ',' '\n' | sed 's/^ *//;s/ *$//' | grep -v '^$' | sort -u) + echo "tags<> "$GITHUB_OUTPUT" + echo "$tags" >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + + - name: Refresh tags + id: refresh + env: + TAGS: ${{ steps.tags.outputs.tags }} + TARGET_SHA: ${{ github.sha }} + run: | + set -euo pipefail + updated=0 + while read -r tag; do + if [ -z "$tag" ]; then + continue + fi + current=$(git rev-parse -q --verify "refs/tags/$tag" || echo "") + if [ "$current" = "$TARGET_SHA" ]; then + echo "Tag $tag already at $TARGET_SHA" + continue + fi + git tag -f "$tag" "$TARGET_SHA" + updated=$((updated + 1)) + done <<< "$TAGS" + + if [ "$updated" -eq 0 ]; then + echo "No tag updates needed." + exit 0 + fi + + echo "updated=$updated" >> "$GITHUB_OUTPUT" + + while read -r tag; do + if [ -n "$tag" ]; then + git push origin "refs/tags/$tag" --force + fi + done <<< "$TAGS" + + - name: Summary + if: always() + env: + TAGS: ${{ steps.tags.outputs.tags }} + UPDATED: ${{ steps.refresh.outputs.updated || '0' }} + run: | + { + echo "## Reusable Tag Refresh" + echo "" + if [ "$UPDATED" -eq 0 ]; then + echo "No updates needed - tags already at ${{ github.sha }}" + else + echo "Updated $UPDATED tag(s) to ${{ github.sha }}:" + echo "" + while IFS= read -r tag; do + [ -n "$tag" ] && echo "- $tag" + done <<< "$TAGS" + fi + } >> "$GITHUB_STEP_SUMMARY" diff --git a/docs/COMPATIBILITY.md b/docs/COMPATIBILITY.md index 0d1452c20..0b7743d2d 100644 --- a/docs/COMPATIBILITY.md +++ b/docs/COMPATIBILITY.md @@ -16,7 +16,7 @@ Breaking changes are any change that can alter a consumer’s behavior without t We support at least two major versions at any given time: -- **Current major (v1.x)** – Actively maintained with fixes and backward-compatible enhancements. Floating tag `@v1` points to the latest v1.x release. +- **Current major (v1.x)** – Actively maintained with fixes and backward-compatible enhancements. Floating tag `@v1` tracks the latest commit on `main` (updated automatically by `Maint 73 Refresh Reusable Tags` after every merge) for immediate fix propagation to consumer repos. - **Previous major** – Remains supported for a minimum of 12 months after a new major is released. Only critical fixes and security updates are provided. - **Older majors** – Enter end-of-life (EOL) after the 12-month overlap window. diff --git a/docs/INTEGRATION_GUIDE.md b/docs/INTEGRATION_GUIDE.md index fbf7ad86b..2a163edda 100644 --- a/docs/INTEGRATION_GUIDE.md +++ b/docs/INTEGRATION_GUIDE.md @@ -46,9 +46,9 @@ Choose the reference that matches your stability needs: | Reference | When to use it | Behavior | |-----------|----------------|----------| -| **Floating major tag (`@v1`)** | Default for most teams that want security/bug fixes without breaking changes. | Automatically moves forward to the latest `v1.x` release; maintained by the release workflow and the floating-tag maintenance job. | -| **Pinned release (`@v1.0.0`)** | When you need fully reproducible builds or plan to upgrade on your own schedule. | Locked to a specific release until you update the tag. | -| **Branch reference (`@main`)** | Only when testing unreleased changes. | Can include breaking changes; not guaranteed stable. | +| **Floating major tag (`@v1`)** | Default for most teams that want immediate security/bug fixes. | Automatically tracks the latest commit on `main` (not a release); maintained by `Maint 73 Refresh Reusable Tags` after every merge. Provides immediate fix propagation but may include unreleased changes. | +| **Pinned release (`@v1.0.0`)** | When you need fully reproducible builds or prefer to upgrade on your own schedule. | Locked to a specific release until you update the tag. Recommended for production stability. | +| **Branch reference (`@main`)** | Only when testing unreleased changes or developing new features. | Can include breaking changes; not guaranteed stable. | Example with both floating and pinned tags: diff --git a/docs/ci/WORKFLOWS.md b/docs/ci/WORKFLOWS.md index 5a5719391..27900e8cd 100644 --- a/docs/ci/WORKFLOWS.md +++ b/docs/ci/WORKFLOWS.md @@ -183,6 +183,7 @@ Scheduled health jobs keep the automation ecosystem aligned: * [`maint-69-sync-integration-repo.yml`](../../.github/workflows/maint-69-sync-integration-repo.yml) syncs integration-repo templates to Workflows-Integration-Tests repository (template push, manual dispatch with dry-run support). * [`maint-69-sync-labels.yml`](../../.github/workflows/maint-69-sync-labels.yml) syncs core functional labels from labels-core.yml to consumer repos (push to labels-core.yml, manual dispatch with dry-run support). * [`maint-70-fix-integration-formatting.yml`](../../.github/workflows/maint-70-fix-integration-formatting.yml) applies Black and Ruff formatting fixes to Integration-Tests repository files (manual dispatch for CI formatting failures). +* [`maint-73-refresh-reusable-tags.yml`](../../.github/workflows/maint-73-refresh-reusable-tags.yml) auto-refreshes floating tags (v1) to track main HEAD - ensures consumer repos always run latest reusable workflow versions without manual updates (push to main, manual dispatch). Replaces deprecated maint-61. * [`maint-71-auto-fix-integration.yml`](../../.github/workflows/maint-71-auto-fix-integration.yml) automatically applies formatting fixes to Integration-Tests when triggered by issue comments or workflow failures. * [`maint-71-merge-sync-prs.yml`](../../.github/workflows/maint-71-merge-sync-prs.yml) automates merging sync PRs in consumer repos - checks status, merges passing PRs, cleans up stale PRs (manual dispatch). * [`maint-72-fix-pr-body-conflicts.yml`](../../.github/workflows/maint-72-fix-pr-body-conflicts.yml) removes pr_body.md from main branch and adds to .gitignore across consumer repos - prevents merge conflicts from PR description files (manual dispatch, weekly schedule). diff --git a/docs/ci/WORKFLOW_SYSTEM.md b/docs/ci/WORKFLOW_SYSTEM.md index a0044c8c9..3a2f1f477 100644 --- a/docs/ci/WORKFLOW_SYSTEM.md +++ b/docs/ci/WORKFLOW_SYSTEM.md @@ -707,6 +707,7 @@ Keep this table handy when you are triaging automation: it confirms which workfl | **Maint 69 Sync Integration Repo** (`maint-69-sync-integration-repo.yml`, maintenance bucket) | `push` (templates), `workflow_dispatch` | Sync integration-repo templates to Workflows-Integration-Tests repository. Resolves drift detected by Health 67. Supports dry-run mode. | ⚪ Automatic/manual | [Integration sync runs](https://github.com/stranske/Workflows/actions/workflows/maint-69-sync-integration-repo.yml) | | **Maint 69 Sync Labels** (`maint-69-sync-labels.yml`, maintenance bucket) | `push` (labels-core.yml), `workflow_dispatch` | Sync core functional labels from labels-core.yml to consumer repositories. Distinguishes functional workflow labels from informational repo-specific labels. Supports dry-run mode. | ⚪ Automatic/manual | [Label sync runs](https://github.com/stranske/Workflows/actions/workflows/maint-69-sync-labels.yml) | | **Fix Integration Tests Formatting** (`maint-70-fix-integration-formatting.yml`, maintenance bucket) | `workflow_dispatch` | Manually triggered workflow to apply Black and Ruff formatting fixes to Python files in the Workflows-Integration-Tests repository when CI formatting checks fail. | ⚪ Manual only | [Formatting fix runs](https://github.com/stranske/Workflows/actions/workflows/maint-70-fix-integration-formatting.yml) | +| **Maint 73 Refresh Reusable Tags** (`maint-73-refresh-reusable-tags.yml`, maintenance bucket) | `push` (main branch), `workflow_dispatch` | Auto-refreshes floating tags (v1) to track main HEAD, ensuring consumer repos always execute latest reusable workflow versions without editing references. Replaces deprecated maint-61. | 🟢 Automated | [Tag refresh runs](https://github.com/stranske/Workflows/actions/workflows/maint-73-refresh-reusable-tags.yml) | | **Auto-Fix Integration Test Failures** (`maint-71-auto-fix-integration.yml`, maintenance bucket) | `issues` (labeled), `workflow_run` (failed) | Automatically applies Black and Ruff formatting fixes to Python files in the Workflows-Integration-Tests repository when triggered by issue labels or workflow failures. | 🟢 Automated | [Auto-fix runs](https://github.com/stranske/Workflows/actions/workflows/maint-71-auto-fix-integration.yml) | | **Merge Sync PRs** (`maint-71-merge-sync-prs.yml`, maintenance bucket) | `workflow_dispatch`, `workflow_call` | Automates merging sync PRs across consumer repos. Checks CI status, merges passing PRs, cleans up stale sync PRs. Reads consumer repo list from maint-68-sync-consumer-repos.yml. | ⚪ Manual/callable | [Merge sync runs](https://github.com/stranske/Workflows/actions/workflows/maint-71-merge-sync-prs.yml) | | **Maint 72 Fix PR Body Conflicts** (`maint-72-fix-pr-body-conflicts.yml`, maintenance bucket) | `workflow_dispatch`, `schedule` (weekly) | Removes `pr_body.md` from main and adds to `.gitignore` in consumer repos to prevent merge conflicts. | ⚪ Manual/scheduled | [PR body fix runs](https://github.com/stranske/Workflows/actions/workflows/maint-72-fix-pr-body-conflicts.yml) | diff --git a/docs/ops/CONSUMER_REPO_MAINTENANCE.md b/docs/ops/CONSUMER_REPO_MAINTENANCE.md index 6d60fa8f3..a41549a8f 100644 --- a/docs/ops/CONSUMER_REPO_MAINTENANCE.md +++ b/docs/ops/CONSUMER_REPO_MAINTENANCE.md @@ -161,6 +161,23 @@ Maint 68 is manifest-driven: Custom Gate repos are a special-case skip: their `pr-00-gate.yml` stays local. +### Reusable Workflow Versioning + +Consumer repos call reusable workflows via a floating tag (currently `v1`). +`Maint 73 Refresh Reusable Tags` automatically advances those tags to the +current `main` SHA after every merge, so consumers always execute the latest +reusable workflow logic without manual version bumps. + +**Version Strategy Change**: This differs from the older behavior documented in +`docs/INTEGRATION_GUIDE.md`, where `@v1` was documented as floating to the latest +`v1.x` release maintained by the release workflow (`maint-61`). Consumer repos now +use a **main-HEAD tracking** strategy for immediate fix propagation. For repos that +need extra stability, pin to a specific commit SHA instead of the floating `v1` tag. + +If a reusable workflow fix must ship immediately, trigger: +- `Maint 73 Refresh Reusable Tags` (updates `v1`; replaces deprecated `maint-61`) +- `Maint 68 Sync Consumer Repos` only if template files changed + ### Manual Sync Trigger ```bash diff --git a/tests/workflows/fixtures/keepalive/harness.js b/tests/workflows/fixtures/keepalive/harness.js index e8eb4819d..2804b4be1 100755 --- a/tests/workflows/fixtures/keepalive/harness.js +++ b/tests/workflows/fixtures/keepalive/harness.js @@ -353,9 +353,15 @@ async function runScenario(scenario) { }, }; + const clearTokenDefaults = + Boolean(scenario.clear_token_defaults) || + Boolean(scenario.clearTokenDefaults) || + Boolean(scenario.env?.CLEAR_TOKEN_DEFAULTS) || + Boolean(scenario.env?.clear_token_defaults); + const envOverrides = { - ACTIONS_BOT_PAT: 'dummy-token', - SERVICE_BOT_PAT: 'service-token', + ACTIONS_BOT_PAT: clearTokenDefaults ? '' : 'dummy-token', + SERVICE_BOT_PAT: clearTokenDefaults ? '' : 'service-token', GH_TOKEN: '', gh_token: '', actions_bot_pat: '', diff --git a/tests/workflows/fixtures/keepalive/missing_dispatch_token.json b/tests/workflows/fixtures/keepalive/missing_dispatch_token.json index 2988ed415..e389c137f 100644 --- a/tests/workflows/fixtures/keepalive/missing_dispatch_token.json +++ b/tests/workflows/fixtures/keepalive/missing_dispatch_token.json @@ -1,6 +1,7 @@ { "repo": {"owner": "stranske", "repo": "Workflows"}, "now": "2024-05-18T12:00:00Z", + "clear_token_defaults": true, "env": { "OPTIONS_JSON": "{}", "DRY_RUN": "false", diff --git a/tests/workflows/test_workflow_naming.py b/tests/workflows/test_workflow_naming.py index e608548ec..e04065ccf 100644 --- a/tests/workflows/test_workflow_naming.py +++ b/tests/workflows/test_workflow_naming.py @@ -226,6 +226,7 @@ def test_workflow_display_names_are_unique(): "maint-68-sync-consumer-repos.yml": "Maint 68 Sync Consumer Repos", "maint-69-sync-integration-repo.yml": "Maint 69 Sync Integration Repo", "maint-69-sync-labels.yml": "Maint 69 Sync Labels", + "maint-73-refresh-reusable-tags.yml": "Maint 73 Refresh Reusable Tags", "maint-60-release.yml": "Maint 60 Release", "maint-70-fix-integration-formatting.yml": "Fix Integration Tests Formatting", "maint-71-auto-fix-integration.yml": "Auto-Fix Integration Test Failures",