fix(modules-12,14,15,18,19,25): editor note, refresh CI action pins, scaffold M15 merge

- M12: note the editor that `git revert -m 1 HEAD` opens (save/close, or --no-edit);
  the -m 1 / --no-ff merge teaching is unchanged.
- Refresh stale CI action pins to verified-current majors (actions/checkout @v4->@v7,
  actions/setup-python @v5->@v6; confirmed via GitHub Releases, 2026-06) across
  M14/M15/M18/M19/M25; add a Verify-before-publish item for pinned action versions.
- M15: scaffold the "slot security steps into the workflow" YAML merge (before/after
  diff, indentation caution, copy-whole-job alternative). Planted devices intact.

Closes #43
Closes #44
Closes #50

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TfzV5QvtPDz8LJS3Pu5VLT
This commit is contained in:
2026-06-22 17:30:30 -04:00
parent 315cb2c190
commit f1744f26f0
8 changed files with 74 additions and 18 deletions
@@ -296,6 +296,11 @@ do them once on purpose now.
git log --oneline -3 # you'll see a "Revert ..." commit on top git log --oneline -3 # you'll see a "Revert ..." commit on top
``` ```
> `git revert` drops you into your text editor with a pre-filled "Revert …" message — save and
> close it (in vim, type `:wq` then Enter; in nano, Ctrl-O then Ctrl-X). Or add `--no-edit` to
> keep that default message and skip the editor entirely: `git revert -m 1 HEAD --no-edit`. Either
> way you end up with the same "Revert …" commit.
7. Prove you're recovered — and notice nothing was erased: 7. Prove you're recovered — and notice nothing was erased:
```bash ```bash
+3 -3
View File
@@ -119,9 +119,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out the code - name: Check out the code
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: "3.12" python-version: "3.12"
- name: Install tools - name: Install tools
@@ -371,7 +371,7 @@ CI YAML and the actions it references drift faster than the rest of this durable
Re-check at build time: Re-check at build time:
- [ ] **Action versions.** Confirm `actions/checkout` and `actions/setup-python` major versions in - [ ] **Action versions.** Confirm `actions/checkout` and `actions/setup-python` major versions in
`ci-starter.yml` are current and not deprecated. Pinned majors (`@v4`, `@v5`) age. `ci-starter.yml` are current and not deprecated. Pinned majors (`@v7`, `@v6`) age.
- [ ] **Runner labels.** Confirm `ubuntu-latest` (and any GitLab `image:` tag) still resolves to a - [ ] **Runner labels.** Confirm `ubuntu-latest` (and any GitLab `image:` tag) still resolves to a
supported image; default runner OS versions roll forward. supported image; default runner OS versions roll forward.
- [ ] **Trigger and config syntax.** Verify the `on:` keys and overall workflow schema against the - [ ] **Trigger and config syntax.** Verify the `on:` keys and overall workflow schema against the
@@ -25,11 +25,11 @@ jobs:
steps: steps:
# Step 1: get your code onto the runner. Without this the runner is empty. # Step 1: get your code onto the runner. Without this the runner is empty.
- name: Check out the code - name: Check out the code
uses: actions/checkout@v4 uses: actions/checkout@v7
# Step 2: install the language the project needs. Pin a version so CI matches what you run. # Step 2: install the language the project needs. Pin a version so CI matches what you run.
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: "3.12" python-version: "3.12"
+55 -4
View File
@@ -337,10 +337,57 @@ runs on every push and blocks the merge.
each non-zero exit. Re-apply your Part B/C fixes (and re-stage), run it once more, and it should each non-zero exit. Re-apply your Part B/C fixes (and re-stage), run it once more, and it should
pass. pass.
2. Add a security step to your pipeline that calls it. `lab/ci-security.yml` is a provider-neutral 2. Merge the security steps into your pipeline. `lab/ci-security.yml` shows the gate as a
snippet — a job that installs the scanners and runs the script. Slot its steps into the workflow self-contained, provider-neutral job — check out, set up Python, install the scanners, run the
you built in Module 14 (the exact YAML keys follow whatever host that module used; the *shape* — script. But the `check` job you built in Module 14 *already* checks out the code and sets up
install tools, run the gate, fail on findings — is identical everywhere). Python, so you don't want a second job duplicating that work. You want its two **new** steps —
**install the scanners** and **run the gate** — added to the steps you already have. (Checkout and
Python are in the snippet only so it reads as a complete example; skip them when you merge.)
Here is exactly where they go. **Before** — the tail of your Module 14 `check` job (GitHub Actions
flavor, matching `ci-starter.yml`; on GitLab the same two steps drop into the job's `script:`):
```yaml
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Check out the code
uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install tools
run: pip install ruff
- name: Lint
run: ruff check .
- name: Test
run: python -m unittest
```
**After** — the same job with the two security steps appended; nothing else changes:
```diff
- name: Lint
run: ruff check .
- name: Test
run: python -m unittest
+ - name: Install scanners
+ run: pip install pip-audit detect-secrets
+ - name: Run the security gate
+ run: |
+ chmod +x security-scan.sh
+ ./security-scan.sh
```
> **YAML is indentation-sensitive — match the existing steps' indentation exactly.** Each new
> `- name:` lines up in the *same column* as the steps above it, and the keys under it (`run:`) sit
> one level deeper. A step pasted even one space off will silently attach to the wrong block or
> fail to parse, and the whole workflow breaks. If you'd rather keep the gate as its own job (some
> teams prefer the isolation), copy `ci-security.yml` in whole as a second job under `jobs:` in the
> same workflow file instead — that is exactly why it carries its own checkout and Python steps.
> The *shape* — install tools, run the gate, fail on findings — is identical everywhere.
3. Prove the gate has teeth: re-introduce the hardcoded key in `config.py`, commit, and push. Watch 3. Prove the gate has teeth: re-introduce the hardcoded key in `config.py`, commit, and push. Watch
the pipeline go **red** on the security step even though lint, build, and tests are still green. the pipeline go **red** on the security step even though lint, build, and tests are still green.
@@ -401,6 +448,10 @@ reproducible.
> **Expansion-zone module — these facts move fast.** Re-check at build/publish time; don't ship the > **Expansion-zone module — these facts move fast.** Re-check at build/publish time; don't ship the
> claims above from memory. > claims above from memory.
- [ ] **Pinned CI action versions.** The `ci-security.yml` snippet (and the Part D before/after diff)
pin `actions/checkout` and `actions/setup-python` to major versions (`@v7`/`@v6` at build time).
Pinned majors age — confirm they're current and not deprecated against the host's docs, the same
check the Module 14 and Module 18 CI/CD checklists carry.
- [ ] **Scanner names and install methods.** Confirm `pip-audit`, `detect-secrets`, and `bandit` are - [ ] **Scanner names and install methods.** Confirm `pip-audit`, `detect-secrets`, and `bandit` are
still maintained and still install as shown. If any has stalled, swap in a current equivalent still maintained and still install as shown. If any has stalled, swap in a current equivalent
from the *same category* and keep the prose category-first, not tool-first. from the *same category* and keep the prose category-first, not tool-first.
@@ -22,12 +22,12 @@ jobs:
runs-on: ubuntu-latest # or your self-hosted runner label (Module 19) runs-on: ubuntu-latest # or your self-hosted runner label (Module 19)
steps: steps:
- name: Check out the code - name: Check out the code
uses: actions/checkout@v4 uses: actions/checkout@v7
# Secret scanning cares about history. If your tool scans commits (not just the working # Secret scanning cares about history. If your tool scans commits (not just the working
# tree), fetch full history here — e.g. set `with: { fetch-depth: 0 }`. # tree), fetch full history here — e.g. set `with: { fetch-depth: 0 }`.
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: "3.x" python-version: "3.x"
@@ -23,8 +23,8 @@ jobs:
check: check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- uses: actions/setup-python@v5 - uses: actions/setup-python@v6
with: with:
python-version: "3.12" python-version: "3.12"
- run: pip install pytest ruff - run: pip install pytest ruff
@@ -38,7 +38,7 @@ jobs:
if: github.ref == 'refs/heads/main' if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
# Log in to your container registry (Module 16's images need a durable home, like a Git remote # Log in to your container registry (Module 16's images need a durable home, like a Git remote
# is for commits). Registry/credentials are provider-specific — supply them as secrets, # is for commits). Registry/credentials are provider-specific — supply them as secrets,
@@ -26,10 +26,10 @@ jobs:
steps: steps:
- name: Check out the code - name: Check out the code
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: "3.12" python-version: "3.12"
@@ -37,10 +37,10 @@ jobs:
steps: steps:
- name: Check out the code - name: Check out the code
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: "3.12" python-version: "3.12"