Journal
Sections
Reading settings
Natural voice recorded for this course, the same in every browser.
No voice for this language in this browser.
More voices
Code and English terms
Off: the page’s voice reads everything, with no pause between two voices.
No voice for this language in this browser.
More voices
More voices: Microsoft Edge has natural online voices, and Windows (Settings › Time & language › Speech), macOS and Android install others.
Progress
Section titled “Progress”- Sample code: the same slug function in .NET (xUnit v3) and Java (JUnit 6)
- Lesson 1: first workflow, reading runs with
gh - Lesson 2: matrix build on three OSes, setup-dotnet, setup-java, Maven cache
- Lesson 3: triggers, filters, inputs, concurrency
- Lesson 4: expressions, contexts, outputs, conditions
- Lesson 5: caches and artifacts (NuGet cache with
packages.lock.json,actions/cache, artifacts between jobs) - Lesson 6: reusable workflows and composite actions
- Lesson 7: security — permissions, secrets, pinning, OIDC
- Lesson 8: deploying to GitHub Pages
- Lesson 9: debugging runs
- Lesson 10: writing your own action
2026-09-14 — Local first: two xUnit v3 traps
Section titled “2026-09-14 — Local first: two xUnit v3 traps”Before writing any workflow, dotnet test on the sample project (SDK 10.0.112 locally, xunit.v3 4.0.1) failed twice:
Testing with VSTest target is no longer supported by Microsoft.Testing.Platform on .NET 10 SDK and later.Fix:"test": { "runner": "Microsoft.Testing.Platform" }inglobal.json.CS0246: The type or namespace name 'Theory' could not be found. WithImplicitUsingsenabled, this package version didn’t addusing Xunit. Fix: theusingin the test file.
The Java side (mvn -B verify, JUnit 6.1.3, Maven 3.9.16) passed first time.
2026-09-14 — First push: four workflows, one invalid
Section titled “2026-09-14 — First push: four workflows, one invalid”Pushing the four lesson workflows (commit 61e7e42) started them all. GHA 01, GHA 02 (6 jobs) and GHA 04 passed. GHA 03 appeared under its file name, failed in 0 s, with no job: You have an error in your yaml syntax on line 41, a run: echo "cron: …" line (an unquoted value containing : ). The next push, unrelated to that file, failed it again, despite its paths filter.
A second commit the same afternoon repeated the mistake in gha-04-exercises.yml (run: echo "next: $BUILD_LABEL"); this time python -c "import yaml; yaml.safe_load(...)" caught it before the push. Lesson: validate YAML locally, every time.
2026-09-14 — Two deployments of this site collided
Section titled “2026-09-14 — Two deployments of this site collided”Pushes 61e7e42 and cb69dca, 14 s apart, each started Deploy to GitHub Pages. The second deploy job failed:
##[error]HttpError: Deployment request failed for cb69dca950b667b876d8d32cfe778a24b6320c59 due to in progress deployment. Please cancel 61e7e4235b7bc138f2f35cd7f61195bdaed4b019 first or wait for it to complete.The site stayed on the previous version until the next push. Fix in deploy.yml (commit 5c9896f): concurrency: { group: pages, cancel-in-progress: false }. Several sessions push to this repository, so collisions were bound to happen again.
2026-09-14 — Measurements
Section titled “2026-09-14 — Measurements”- Maven cache,
gha-02first run → second run: Ubuntu 16 s → 10 s, Windows 43 s → 28 s, macOS 20 s → 9 s. .NET jobs unchanged (21/60/20 s → 22/57/22 s): no NuGet cache yet. global.json10.0.100+latestFeature→setup-dotnetinstalled SDK 10.0.401.- Temurin 25.0.4 was preinstalled on
ubuntu-latest(Resolved Java 25.0.4+1 from tool-cache). macos-latestis arm64 (setup-java-macOS-arm64-maven-…cache key).- Default
run:shell:/usr/bin/bash -e {0}(Ubuntu),/bin/bash -e {0}(macOS),pwsh -command ". '{0}'"(Windows). Explicitshell: bashon Windows:bash.EXE --noprofile --norc -e -o pipefail {0}, bash 5.3.15. GITHUB_TOKENdefault permissions on this repository:Contents: read,Metadata: read,Packages: read. The token was 377 characters long.- Concurrency with
cancel-in-progress: true: push run cancelled by a manual run 41 s later, itself cancelled 10 s later by another one before its job started.
2026-09-14 — Lesson 5: a cache that saves nothing, an artifact conflict that doesn’t happen
Section titled “2026-09-14 — Lesson 5: a cache that saves nothing, an artifact conflict that doesn’t happen”setup-dotnetcache: truewithout anypackages.lock.json:Dependencies lock file is not found in /home/runner/work/learn/learn. AddedRestorePackagesWithLockFileinDirectory.Build.props, committed the two lock files,dotnet restore --locked-modein CI.- NuGet cache, miss → hit: restore step 3 s → 1 s (Ubuntu, macOS), 5 s → 4 s (Windows), for a 54 MB cache per OS. The download of the cache costs about what it saves on a project with three test packages. Kept for the lesson and for the lock files.
actions/cacheoutputcache-hit: empty on a miss,falseon arestore-keysmatch,trueon an exact hit.- Two matrix jobs uploading an artifact named
os: the README announces conflict errors; three runs out of three, both uploads succeeded and the run had two artifacts namedos.download-artifactandgh run download --name osboth returned the Ubuntu one, without warning. - Artifact expiry: 7 days with
retention-days: 7, otherwise 90 days (gh api repos/spareilleux/learn/actions/permissions/artifact-and-log-retention→{"days":90,"maximum_allowed_days":90}).
2026-09-14 — Lesson 6: reuse, and a branch for an invalid workflow
Section titled “2026-09-14 — Lesson 6: reuse, and a branch for an invalid workflow”- The composite action
dotnet-restorerestored the NuGet cache saved bygha-05: same key and path, another workflow. Caches belong to the repository and branch. - In the called workflow:
github.workflowis the caller’s name,job.workflow_refpoints togha-06-dotnet-test.yml, the caller’s workflow-levelenvis empty. - Matrix of two reusable workflow calls: the output
tested-onwasWindows, the job that finished last. - Composite
run:withoutshell::Required property is missing: shell, only when a job runs the action. - Undeclared input:
startup_failure, no job, andgh run viewdoesn’t show the reason (the run page does). Tested from a temporary branchgha-06-invalidso thatmainnever carried an invalid workflow. Deleting the remote branch was blocked by a local safety hook: it’s still there, to delete by hand.
2026-09-14 — Lesson 7: permissions that don’t restrict, and an injection that runs
Section titled “2026-09-14 — Lesson 7: permissions that don’t restrict, and an injection that runs”gh run listfrom a job withcontents: readonly (noactions: read) worked: public data of a public repository. Replaced by a label creation with an empty name: 403Resource not accessible by integrationwithoutissues: write, 422Validation Failedwith it — nothing created either way.- Job-level
permissions: actions: read→ the token lostContents, andactions/checkoutstill worked on this public repository. workflow_dispatchinput$(whoami)pasted with${{ }}intorun:printedrunner; throughenv:it printed$(whoami).::add-mask::hides the value only from the lines printed after it.- OIDC token for audience
learn-course: lifetime 300 s,sub=repo:spareilleux@6644695/learn@1368550922:ref:refs/heads/main, the immutable format of repositories created after 2026-07-15.
2026-09-14 — Lesson 8: this site’s deployment, taken apart
Section titled “2026-09-14 — Lesson 8: this site’s deployment, taken apart”- A deploy run:
build30 s (310 pages built in 7.47 s,github-pagesartifact of 9,108,634 bytes kept 1 day),deploy8 s. Deployment statuses:waiting→queued→in_progress→successin 13 s. - The concurrency queue worked: a push 41 s after another one waited 14 s, and its
buildjob was created at 13:32:18, the second the previousdeployjob finished. gh workflow run deploy.yml --ref gha-06-invalid: the build ran, the deploy job was rejected (Branch "gha-06-invalid" is not allowed to deploy to github-pages due to environment protection rules), the live site didn’t change. Checked beforehand that no deployment ofmainwas pending, since the branch run shares thepagesconcurrency group.https://spareilleux.github.io/method/→ 404: a root-absolute link leaves the project site.
2026-09-14 — Lesson 9: timeouts that take longer, and a failure that counts as success
Section titled “2026-09-14 — Lesson 9: timeouts that take longer, and a failure that counts as success”gh run view --log-failed: 15 lines out of 169; the job cancelled by its timeout isn’t included.gh run rerun --debug:##[debug]condition evaluations,RUNNER_DEBUG=1, thenoisyjob from 70 to 190 lines, and arunner-diagnostic-logsfolder (Runner and Worker logs) in the archive.gh run rerun --failedre-ran the failed and the cancelled jobs, and kept the successful one from attempt 1.timeout-minutes: 1on a job: cancelled after 87 s, three times, withsleep 90orsleep 300. On a step: failed after 72 s,The action 'Run date -u +%T' has timed out after 1 minutes.- A run whose jobs are successful, failed with
continue-on-error, or cancelled by a timeout endscancelled. With only thecontinue-on-errorfailure, it endssuccess, andneeds.<job>.resultissuccess.
2026-09-14 — Lesson 10: three failures before a working action
Section titled “2026-09-14 — Lesson 10: three failures before a working action”require()in the JavaScript action:require is not defined in ES module scope, locally, because the repository’spackage.json(the Astro site’s) says"type": "module". Switched toimport; a CommonJS copy failed the same way on the runner.- Container action, first push, committed without the executable bit on purpose:
exec: "/entrypoint.sh": permission denied. Files created on Windows are committed as100644(core.filemodeisfalse); fixed withgit update-index --chmod=+x. - Container action on
windows-latest:Container action is only supported on Linux. required: trueisn’t enforced by the runner; an unknown input is only a warning (Unexpected input(s) 'txt', valid inputs are ['text', 'max-length']).- Step durations: JavaScript action under 1 s on the three OSes, container action 5 s (image build included).
Open questions
Section titled “Open questions”- Does the
scheduletrigger ofgha-03(Mondays 06:17 UTC) run on time? To verify on 2026-09-21. - Which artifact does
download-artifactpick when two share a name, and is it stable? To verify. - Does
actions/checkoutfail on a private repository when the job token has nocontentspermission? To verify. - Why is a 1-minute job timeout enforced after 87 s? To verify with longer limits.
- What exactly does a pull request page show for a required check skipped by
paths? To verify.