Skip to content

Commit 51c68d2

Browse files
webmaxruCopilot
andcommitted
Add content versioning and GitHub Releases for the living book
Version the book CONTENT independently of the site generator and tooling, and surface the version in both the online edition and the generated PDF. - content/VERSION (source of truth) + content/CHANGELOG.md (release notes) - scripts/content_version.py: shared stdlib-only parser + CLI (version/tag/notes) used by the generator, PDF builder, and release workflow - site/generate.py: surface v<version> in header pill, cover, breadcrumbs, footers, book cover/colophon; new versions.html history page; sitemap + llms - scripts/build_pdf.py: stamp the version into the PDF running footer - site/assets/style.css: scoped version pill + release-history styles - .github/workflows/release-content.yml: cut content-vX.Y on VERSION change, build the versioned PDF, and attach it; idempotent + self-healing, and manual dispatch always derives the version from the checked-out ref so releases can never be mislabeled - README: document the versioning + release process Releases published: content-v1.0 (pre-APM) and content-v1.1 (Latest, with PDF). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent d7103aa commit 51c68d2

28 files changed

Lines changed: 784 additions & 42 deletions
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
name: Release book content
2+
3+
# Publishes a GitHub Release for the book's CONTENT edition whenever content/VERSION changes.
4+
#
5+
# The book is a living document: its prose (content/) is versioned independently of the site
6+
# generator and tooling. content/VERSION is the source of truth, content/CHANGELOG.md is the
7+
# history, and each version is shipped here as a Release tagged `content-vX.Y` with the matching
8+
# single-file PDF attached — so every published state stays reproducible and downloadable.
9+
#
10+
# This job is CONTENT-only: it is not triggered by site/tooling changes, and it is idempotent —
11+
# if a release for the current version already exists, it does nothing.
12+
13+
on:
14+
push:
15+
branches: [main]
16+
paths:
17+
- "content/VERSION"
18+
# CHANGELOG is included so a notes-only fix can retrigger a release that
19+
# failed on a previous push (the idempotency guard prevents duplicates).
20+
- "content/CHANGELOG.md"
21+
# Manual runs (re)cut the release for whatever ref they are dispatched against:
22+
# the version, PDF, and notes always come from that ref's content/VERSION and
23+
# content/CHANGELOG.md, so a dispatched release can never be mislabeled.
24+
workflow_dispatch:
25+
26+
# Creating a release and pushing the tag needs write access to repository contents.
27+
permissions:
28+
contents: write
29+
30+
concurrency:
31+
group: release-content
32+
cancel-in-progress: false
33+
34+
jobs:
35+
release:
36+
runs-on: ubuntu-latest
37+
steps:
38+
- name: Checkout
39+
uses: actions/checkout@v4
40+
with:
41+
fetch-depth: 0
42+
43+
- name: Set up Python
44+
uses: actions/setup-python@v5
45+
with:
46+
python-version: "3.12"
47+
48+
- name: Resolve content version, tag, and asset name
49+
id: meta
50+
run: |
51+
# Always derive from the checked-out content/VERSION so the tag, PDF,
52+
# and notes describe exactly this ref — no override can desync them.
53+
VERSION="$(python scripts/content_version.py version)"
54+
TAG="$(python scripts/content_version.py tag "$VERSION")"
55+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
56+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
57+
echo "asset=gh-aw-book-v$VERSION.pdf" >> "$GITHUB_OUTPUT"
58+
echo "Resolved content version $VERSION -> tag $TAG"
59+
60+
- name: Decide whether to create, repair, or skip this release
61+
id: guard
62+
env:
63+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
64+
run: |
65+
TAG="${{ steps.meta.outputs.tag }}"
66+
ASSET="${{ steps.meta.outputs.asset }}"
67+
if gh release view "$TAG" >/dev/null 2>&1; then
68+
if gh release view "$TAG" --json assets --jq '.assets[].name' | grep -Fxq "$ASSET"; then
69+
echo "Release $TAG already exists with asset $ASSET — nothing to do."
70+
echo "action=skip" >> "$GITHUB_OUTPUT"
71+
else
72+
echo "Release $TAG exists but PDF asset $ASSET is missing — will (re)upload it."
73+
echo "action=upload" >> "$GITHUB_OUTPUT"
74+
fi
75+
else
76+
echo "No release for $TAG yet — will create it."
77+
echo "action=create" >> "$GITHUB_OUTPUT"
78+
fi
79+
80+
- name: Write release notes from the changelog
81+
if: steps.guard.outputs.action == 'create'
82+
run: |
83+
{
84+
echo "## GitHub Agentic Workflows — content ${{ steps.meta.outputs.tag }}"
85+
echo
86+
python scripts/content_version.py notes "${{ steps.meta.outputs.version }}"
87+
echo
88+
echo "---"
89+
echo
90+
echo "📖 Read online: https://aw.isainative.dev/ · [Version history](https://aw.isainative.dev/versions.html)"
91+
echo
92+
echo "📄 The single-file PDF for this version is attached below."
93+
} > release-notes.md
94+
cat release-notes.md
95+
96+
- name: Build the versioned PDF edition
97+
if: steps.guard.outputs.action != 'skip'
98+
run: |
99+
python -m pip install --upgrade pip
100+
python -m pip install pyyaml
101+
python site/generate.py
102+
python -m pip install -r scripts/requirements-pdf.txt
103+
python -m playwright install --with-deps chromium
104+
python scripts/build_pdf.py
105+
cp site/gh-aw-book.pdf "${{ steps.meta.outputs.asset }}"
106+
107+
- name: Create the GitHub Release
108+
if: steps.guard.outputs.action == 'create'
109+
env:
110+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
111+
run: |
112+
gh release create "${{ steps.meta.outputs.tag }}" \
113+
"${{ steps.meta.outputs.asset }}#GitHub Agentic Workflows — v${{ steps.meta.outputs.version }} (PDF)" \
114+
--title "Content v${{ steps.meta.outputs.version }}" \
115+
--notes-file release-notes.md \
116+
--target "${GITHUB_SHA}"
117+
118+
- name: Attach the missing PDF to the existing release
119+
if: steps.guard.outputs.action == 'upload'
120+
env:
121+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
122+
run: |
123+
gh release upload "${{ steps.meta.outputs.tag }}" \
124+
"${{ steps.meta.outputs.asset }}#GitHub Agentic Workflows — v${{ steps.meta.outputs.version }} (PDF)" \
125+
--clobber

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,16 @@ content/
158158
playbook-brief.md # scope / intent
159159
toc.yml # chapter spec (source of truth)
160160
outline.md # architect's working outline
161+
VERSION # content edition version (source of truth), e.g. 1.1
162+
CHANGELOG.md # per-version content history (release-notes source)
161163
research/ # per-chapter theory + capability notes (generated by the fleet)
162164
examples/ # compile-verified .md workflow examples (generated by the fleet)
163165
site/
164166
generate.py # scaffolds the site from content/toc.yml
165167
index.html # generated
166168
chapters/*.html # generated: one page per chapter in toc.yml
167169
book.html # generated: single-page edition (source for the PDF)
170+
versions.html # generated: content version history, linked to GitHub Releases
168171
gh-aw-book.pdf # build artifact (gitignored): the downloadable PDF
169172
assets/ # style.css + app.js + analytics.js (built beacon)
170173
analytics/
@@ -174,6 +177,7 @@ azure/
174177
package.json # analytics build tooling (esbuild bundle + report command)
175178
scripts/
176179
build_pdf.py # renders site/book.html → site/gh-aw-book.pdf (Playwright)
180+
content_version.py # content version + changelog parser (site, PDF, release workflow)
177181
requirements-pdf.txt # Python deps for the PDF build
178182
run-fleet.ps1 # convenience launcher
179183
report.ps1 # engagement report (PowerShell)
@@ -241,6 +245,52 @@ current book.
241245

242246
---
243247

248+
## Content versioning & releases
249+
250+
This is a **living book**, so its **content** carries its own version — independent of the site
251+
generator, PDF renderer, analytics beacon, and other tooling in this repo. Only the prose under
252+
[`content/`](content/) is versioned; changing the build scripts or styling does **not** bump the
253+
book's version.
254+
255+
- **Source of truth:** [`content/VERSION`](content/VERSION) — a single line, e.g. `1.1`.
256+
- **History:** [`content/CHANGELOG.md`](content/CHANGELOG.md) — human-readable notes per version,
257+
following [Semantic Versioning](https://semver.org/) applied to prose (MAJOR = structural rewrite
258+
or reordering; MINOR = new chapters/sections/material; PATCH = corrections and clarifications).
259+
- **Helper:** [`scripts/content_version.py`](scripts/content_version.py) is the shared parser used by
260+
the site generator, the PDF builder, and the release workflow:
261+
```powershell
262+
python scripts/content_version.py version # -> 1.1
263+
python scripts/content_version.py tag # -> content-v1.1
264+
python scripts/content_version.py notes # release notes for the current version (from the changelog)
265+
```
266+
267+
**Where the version shows up.** `site/generate.py` surfaces `v<version>` across the online edition
268+
(a pill in the header, the cover "Edition" line, the chapter breadcrumbs and footers, and the
269+
single-page/PDF cover), and generates a dedicated **[Version history](https://aw.isainative.dev/versions.html)**
270+
page (`site/versions.html`) rendered from the changelog and linked to each GitHub Release. The PDF's
271+
running footer is stamped with the same version.
272+
273+
**GitHub Releases.** Each content version is published as a GitHub Release tagged
274+
`content-v<version>` with the matching single-file PDF attached, so every past state of the book
275+
stays reproducible and downloadable. This is automated by
276+
[`.github/workflows/release-content.yml`](.github/workflows/release-content.yml): when a push to
277+
`main` changes `content/VERSION`, it builds the versioned PDF, pulls the release notes from the
278+
changelog, and cuts the release. The job is **idempotent** — if a release for the current version
279+
already exists it does nothing, and if that release is somehow missing its PDF it re-attaches it. It
280+
can also be run on demand via **workflow_dispatch**, which (re)cuts the release for whatever ref it
281+
is dispatched against — the version, PDF, and notes always come from that ref's `content/VERSION`,
282+
so a release can never be mislabeled.
283+
284+
**To publish a new version:**
285+
286+
1. Edit the chapters under `content/chapters/`.
287+
2. Add a `## [x.y] - YYYY-MM-DD` entry to [`content/CHANGELOG.md`](content/CHANGELOG.md).
288+
3. Bump [`content/VERSION`](content/VERSION) to `x.y`.
289+
4. Merge to `main` — the deploy workflow republishes the site (now showing the new version) and the
290+
release workflow cuts `content-vx.y` with the PDF attached.
291+
292+
---
293+
244294
## Privacy-friendly analytics (cookieless RUM)
245295

246296
The published site is instrumented with **cookieless Real User Monitoring** via Azure Application

content/CHANGELOG.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Changelog — Book Content
2+
3+
All notable changes to the **book content** — the chapters and their source under
4+
[`content/`](./) — are recorded here. This log tracks the *content edition* only. The site
5+
generator, PDF renderer, analytics beacon, and other tooling evolve with the repository's commit
6+
history and are deliberately **not** part of this version line.
7+
8+
The current content version lives in [`content/VERSION`](./VERSION). Each version is published as a
9+
GitHub Release tagged `content-vMAJOR.MINOR` with the matching single-file PDF edition attached, so
10+
every published state of this living book stays reproducible and downloadable.
11+
12+
Versioning follows [Semantic Versioning](https://semver.org/) applied to prose:
13+
14+
- **MAJOR** — a structural rewrite, or reordering of parts/chapters.
15+
- **MINOR** — new chapters, sections, or substantive new material.
16+
- **PATCH** — corrections, clarifications, and small edits that add no new material.
17+
18+
## [1.1] - 2026-07-08
19+
20+
Added Agent Package Manager (APM) coverage so the fleet chapters explain how shared agentic
21+
components are distributed and governed as supply-chain dependencies.
22+
23+
### Added
24+
25+
- **Chapter 11 — Reuse & Memory:** APM dependencies via `shared/apm.md`, showing how imported shared
26+
components are declared and resolved like packaged dependencies.
27+
- **Chapter 13 — Governance & FinOps:** APM supply-chain governance with `apm-policy.yml`, covering
28+
provenance and policy for third-party agentic components.
29+
30+
## [1.0] - 2026-07-03
31+
32+
Initial release of the complete book: fourteen chapters across three parts, each anchored to a
33+
concept and carrying a verified, compilable example workflow, following the Repo Assistant from a
34+
single triage workflow to a governed multi-repo fleet.
35+
36+
### Added
37+
38+
- **Part I — The Individual:** Chapters 1–5 — what agentic workflows are, your first workflow,
39+
anatomy & the compile model, triggers, and engines.
40+
- **Part II — The Team:** Chapters 6–10 — safe outputs, defense in depth, tools & MCP, continuous
41+
triage & docs, and continuous review & testing.
42+
- **Part III — The Organization:** Chapters 11–14 — reuse & memory, observability & debugging,
43+
governance & FinOps, and fleets & adoption.

content/VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.1

scripts/build_pdf.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,20 @@
3232
BOOK_PAGE = "book.html"
3333
PDF_PATH = SITE / "gh-aw-book.pdf"
3434

35-
# Running footer: book title on the left, "Page N / M" on the right. The
35+
# Content version stamped into the PDF footer (source of truth: content/VERSION).
36+
sys.path.insert(0, str(ROOT / "scripts"))
37+
import content_version # noqa: E402 (path set up above)
38+
39+
CONTENT_VERSION = content_version.read_version()
40+
41+
# Running footer: book title + content version on the left, "Page N / M" on the right. The
3642
# pageNumber / totalPages spans are filled in by Chromium.
3743
FOOTER_TEMPLATE = (
3844
'<div style="width:100%;font-family:\'Hanken Grotesk\',Arial,sans-serif;'
3945
'font-size:8px;color:#8a93a6;padding:0 14mm;display:flex;'
4046
'justify-content:space-between;align-items:center;">'
41-
'<span>GitHub Agentic Workflows \u2014 An Interactive Book</span>'
47+
'<span>GitHub Agentic Workflows \u2014 An Interactive Book '
48+
f'\u00b7 v{CONTENT_VERSION}</span>'
4249
'<span>Page <span class="pageNumber"></span> / <span class="totalPages"></span></span>'
4350
"</div>"
4451
)

0 commit comments

Comments
 (0)