Skip to content

ci: publish devnotes independently of releases#536

Merged
andreatgretel merged 5 commits intomainfrom
andreatgretel/chore/publish-devnotes-workflow
Apr 13, 2026
Merged

ci: publish devnotes independently of releases#536
andreatgretel merged 5 commits intomainfrom
andreatgretel/chore/publish-devnotes-workflow

Conversation

@andreatgretel
Copy link
Copy Markdown
Contributor

@andreatgretel andreatgretel commented Apr 13, 2026

📋 Summary

Decouples devnotes (blog) publishing from package releases. Currently, new blog posts only go live when build-docs.yml runs on a release event. This adds a dedicated workflow that rebuilds the latest docs alias whenever devnotes change on main, so posts are live within minutes of merging - without affecting the rest of the published docs.

🔗 Related Issue

N/A

🔄 Changes

✨ Added

  • publish-devnotes.yml workflow triggered on pushes to main when docs/devnotes/** changes (also supports manual workflow_dispatch)

🔧 Changed

  • Nothing modified in existing workflows

🔍 Attention Areas

⚠️ Reviewers: Please pay special attention to the following:

  • publish-devnotes.yml — The workflow parses mike's gh-pages commit message (Deployed <sha> to <version> ...) to determine the source commit and version of the last deployment. It then checks out that commit, overlays only docs/devnotes/ from main, and downloads pre-built notebooks from the last successful build-docs.yml run. This ensures non-devnotes docs remain at the last intentionally deployed state.

🧪 Testing

  • make test passes — N/A, no testable logic (CI workflow only)
  • Unit tests added/updated — N/A
  • E2E tests added/updated — N/A
  • Merge a devnotes-only change to main and verify the workflow triggers
  • Verify latest docs show the new post
  • Run manually via workflow_dispatch and verify it succeeds
  • Verify existing build-docs.yml release flow still works

✅ Checklist

  • Follows commit message conventions
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable) — N/A

Description updated with AI

Adds a GitHub Actions workflow that rebuilds the `latest` docs alias
when devnotes change on main, so blog posts go live without cutting
a package release.
@andreatgretel andreatgretel marked this pull request as ready for review April 13, 2026 14:15
@andreatgretel andreatgretel requested a review from a team as a code owner April 13, 2026 14:15
@github-actions
Copy link
Copy Markdown
Contributor

Code Review: PR #536 — ci: publish devnotes independently of releases

Summary

This PR adds a new GitHub Actions workflow (.github/workflows/publish-devnotes.yml) that rebuilds the latest docs alias whenever devnotes change on main, decoupling blog post publishing from full package releases. The workflow reuses build-notebooks.yml, looks up the latest release tag, and runs mike deploy --push --update-aliases to update the docs site.

The approach is sound and the motivation is clear — blog posts shouldn't be gated on package releases. The workflow closely mirrors the existing build-docs.yml deploy job, which is good. However, there is one high-severity finding that must be fixed before merge.

Findings

1. [High] Actions must be SHA-pinned — supply-chain hardening regression

File: .github/workflows/publish-devnotes.yml, lines 24, 26, 35

The new workflow uses unpinned tag references for third-party actions:

uses: actions/checkout@v6
uses: astral-sh/setup-uv@v7
uses: actions/download-artifact@v7

All other workflows in this repo were updated in PR #517 (commit 54d51bdf, "chore: harden CI supply chain") to use SHA-pinned references with version comments:

uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7

Why it matters: Tag-based references are mutable. A compromised upstream action tag could inject malicious code into the CI pipeline. SHA pinning ensures reproducibility and integrity. Dependabot is configured to keep these SHAs up to date.

Suggested fix: Replace the three uses: lines with their SHA-pinned equivalents from build-docs.yml.

2. [Medium] Missing top-level permissions: {} declaration

File: .github/workflows/publish-devnotes.yml (top level)

The build-docs.yml workflow declares permissions: {} at the workflow level to restrict the default GITHUB_TOKEN scope, then grants contents: write only to the deploy job that needs it. The new workflow omits the top-level restriction.

While the build-notebooks reusable workflow and the deploy job both have their own permission scopes, the principle of least privilege recommends adding the top-level permissions: {} for defense-in-depth, consistent with the existing pattern:

permissions: {}

jobs:
  build-notebooks:
    ...
  deploy:
    permissions:
      contents: write
    ...

3. [Low] Minor style inconsistency with build-docs.yml

The new workflow condenses version tag lookup and extraction into a single step ("Get latest release version"), while build-docs.yml uses two separate steps ("Find the latest existing release tag" and "Extract version from release tag"). This is functionally fine and arguably cleaner. Just noting it for awareness — the condensed form is a reasonable simplification.

Verdict

Request changes — one blocking issue.

The workflow logic is correct and well-motivated. The build-notebooks reuse with use_cache: true is a good optimization. However, the unpinned action references (Finding #1) directly contradict the supply-chain hardening established in PR #517 and must be fixed before merge. Adding the top-level permissions: {} (Finding #2) is strongly recommended for consistency and security posture.

Once SHA pinning is applied and the top-level permissions restriction is added, this is good to merge.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 13, 2026

Greptile Summary

Adds a dedicated publish-devnotes.yml workflow so blog posts go live within minutes of merging to main, without requiring a full release. The implementation reads the last deployed state directly from the gh-pages commit message (extracting SOURCE_SHA and VERSION), checks out that source commit, overlays the new devnotes, downloads pre-built notebooks from the last successful build-docs.yml run, and redeploys via mike. Previous review concerns (unpinned action refs, missing actions: read permission) are fully resolved.

Confidence Score: 5/5

Safe to merge — all prior P0/P1 concerns are resolved and no new issues found.

Both previously flagged issues (unpinned action SHAs and missing actions: read permission) were fixed in follow-up commits. The workflow logic is correct: SOURCE_SHA is constrained to hex-only characters before being used in git commands, VERSION is read back from the already-deployed gh-pages state so redeployment targets the right version, and notebook artifacts are correctly downloadable from the parent build-docs.yml run (reusable-workflow artifacts are scoped to the calling workflow). No P0 or P1 issues remain.

No files require special attention.

Important Files Changed

Filename Overview
.github/workflows/publish-devnotes.yml New workflow that decouples devnotes publishing from releases; reads last deployed state from gh-pages to reconstruct the docs source, overlays new devnotes, downloads cached notebooks, and redeploys — all action refs are SHA-pinned and permissions are minimal.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A([Push to main\ndocs/devnotes/**\nor workflow_dispatch]) --> B[Checkout repo\nfetch-depth: 0]
    B --> C[git fetch gh-pages\nparse DEPLOY_MSG\nextract SOURCE_SHA + VERSION]
    C --> D{Parse OK?}
    D -- No --> E([exit 1])
    D -- Yes --> F[git checkout SOURCE_SHA\ngit checkout github.sha -- docs/devnotes/]
    F --> G[uv sync --group docs\nat SOURCE_SHA state]
    G --> H[gh run list build-docs.yml\ndownload notebooks artifact]
    H --> I{Run found?}
    I -- No --> J([exit 1])
    I -- Yes --> K[git config bot identity]
    K --> L[mike deploy --push\n--update-aliases VERSION latest]
    L --> M([gh-pages updated\nlatest alias points to new devnotes])
Loading

Reviews (4): Last reviewed commit: "Merge branch 'main' into andreatgretel/c..." | Re-trigger Greptile

nabinchha
nabinchha previously approved these changes Apr 13, 2026
Address Greptile review findings:
- Pin checkout, setup-uv, and download-artifact to commit SHAs
  matching the pattern from #517
- Add top-level permissions: {} to restrict default token scope
Instead of building the full site from main (which could include
unreleased docs), checkout the commit that latest was last built
from (tracked in gh-pages commit messages) and overlay only
docs/devnotes/ from main. Download notebooks from the last
successful build-docs run instead of rebuilding them.
andreatgretel and others added 2 commits April 13, 2026 15:20
The gh run list/download calls need actions:read on GITHUB_TOKEN,
which is denied by the top-level permissions: {} block.
@andreatgretel andreatgretel added the agent-review Trigger agentic CI review label Apr 13, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Code Review: PR #536 — ci: publish devnotes independently of releases

Summary

This PR adds a new GitHub Actions workflow (.github/workflows/publish-devnotes.yml) that rebuilds the latest docs alias whenever files under docs/devnotes/ change on main, decoupling blog-post publishing from package releases.

The approach: parse the gh-pages branch's most recent commit message (written by mike deploy) to recover the source SHA and version of the last full docs build, checkout that state, overlay fresh devnotes from the triggering commit, download pre-built notebooks from the last successful build-docs.yml run, and re-deploy with mike deploy --push --update-aliases.

Changed files: 1 added (.github/workflows/publish-devnotes.yml, +67 lines)


Findings

1. [Medium] Fragile dependency on mike's internal commit message format

File: .github/workflows/publish-devnotes.yml, lines 27-37

The workflow parses the gh-pages commit message with sed to extract SOURCE_SHA and VERSION:

SOURCE_SHA=$(echo "$DEPLOY_MSG" | sed -n 's/^Deployed \([0-9a-f]*\) to .*/\1/p')
VERSION=$(echo "$DEPLOY_MSG" | sed -n 's/^Deployed [0-9a-f]* to \([^ ]*\) .*/\1/p')

This relies on mike's internal commit message format: Deployed <short-sha> to <version> with MkDocs ... (from mike.commands.deploy, lines 4-6 in mike's source). This is an implementation detail, not a stable API. If mike changes the format in a future version, the workflow silently breaks at the if [ -z ... ] guard.

Suggestions (pick one):

  • Use mike's --message flag in build-docs.yml to write a controlled, parseable commit message format that this workflow can rely on.
  • Add a post-deploy step in build-docs.yml that writes structured metadata (e.g., a .deploy-info.json) to gh-pages.
  • At minimum, pin the mike version and add a comment documenting the format dependency.

2. [Medium] Expression interpolation of parsed values in shell context

File: .github/workflows/publish-devnotes.yml, lines 42, 67

run: |
  git checkout ${{ env.SOURCE_SHA }}
  git checkout ${{ github.sha }} -- docs/devnotes/
...
run: uv run mike deploy --push --update-aliases ${{ env.VERSION }} latest

${{ env.X }} is expanded by GitHub Actions' expression engine before the shell interprets the string. If VERSION contained shell metacharacters (the sed pattern [^ ]* permits ;, |, $, `, etc.), this would be a command-injection vector. The risk is low — an attacker would need push access to gh-pages — and SOURCE_SHA is constrained to [0-9a-f]*, but the fix is trivial:

# Safer: let the shell expand the quoted variable
run: git checkout "$SOURCE_SHA"
...
run: uv run mike deploy --push --update-aliases "$VERSION" latest

Note: The same pattern exists in build-docs.yml line 68 (echo ${{ env.LATEST_TAG }}), so this is a pre-existing style in the repo. Worth fixing in both places.

3. [Medium] Artifact retention window risk

File: .github/workflows/publish-devnotes.yml, lines 55-62

The workflow downloads notebooks from the last successful build-docs.yml run via gh run download. GitHub Actions artifacts expire after a configurable retention period (default 90 days). If no release (and therefore no build-docs.yml run) has occurred within that window, this step will fail. The current error message ("No successful build-docs run found") doesn't distinguish between "no run exists" and "run exists but artifacts expired."

Suggestion: Add a more descriptive error that mentions artifact retention, or consider storing built notebooks as a release asset or on gh-pages so they don't expire.

4. [Low] PR description does not match implementation

The PR body states the workflow "Reuses build-notebooks.yml with use_cache: true", but the actual implementation downloads pre-built notebooks via gh run download instead. The commit history shows this approach evolved during development. The description should be updated to reflect the final implementation.

5. [Low] Short SHA from mike may become ambiguous

Mike records git_utils.get_latest_commit('HEAD', short=True), producing a ~7-10 character abbreviated hash. This is what SOURCE_SHA will contain. Short hashes can theoretically become ambiguous in large repositories over time. Using mike's --message flag to record the full SHA (as suggested in Finding 1) would address this as well.

Positive observations

  • Actions pinned to commit SHAs — consistent with the repo's supply-chain hardening conventions.
  • Minimal permissionspermissions: {} at the workflow level, with only actions: read and contents: write scoped to the deploy job.
  • Clean overlay strategy — checking out the last deployed state and overlaying fresh devnotes avoids rebuilding everything from scratch while ensuring consistent output.
  • Good error handling::error:: annotations and exit 1 on parse failures provide clear CI feedback.
  • Tooling consistency — uv 0.9.5 and Python 3.11 match build-docs.yml.

Verdict

Approve with suggestions.

This is a well-structured workflow that solves a real operational pain point — blog posts should not be blocked on package releases. The overlay approach is clever and efficient.

The primary concern is the fragile coupling to mike's internal commit message format (Finding 1). Since this workflow runs automatically on every devnotes merge, a mike upgrade that changes the format would silently break publishing. Using mike's --message flag to write a controlled format in build-docs.yml would make this durable and also address the short-SHA concern (Finding 5).

The expression interpolation issue (Finding 2) is low-risk but trivially fixable and worth addressing for defense-in-depth. The artifact retention issue (Finding 3) is an edge case but could cause confusing failures during periods of low release frequency.

None of these issues are blocking — the workflow is correct and safe as-is for the current mike version. They are hardening improvements to make the workflow more durable over time.

@github-actions github-actions bot removed the agent-review Trigger agentic CI review label Apr 13, 2026
@andreatgretel andreatgretel merged commit aee3d3f into main Apr 13, 2026
52 checks passed
@andreatgretel andreatgretel deleted the andreatgretel/chore/publish-devnotes-workflow branch April 14, 2026 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants