This commit is contained in:
+22
-1
@@ -8,7 +8,28 @@ jobs:
|
||||
runs-on: docker-host
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.2
|
||||
- run: bash ci.sh
|
||||
with:
|
||||
fetch-depth: 0 # the merge-base below needs history; checkout defaults to depth 1
|
||||
- name: Detect a docs-only branch
|
||||
id: scope
|
||||
run: |
|
||||
git fetch --no-tags --quiet origin +refs/heads/main:refs/remotes/origin/main
|
||||
base=$(git merge-base refs/remotes/origin/main HEAD) \
|
||||
|| { echo 'No merge-base with main - running the gate'; echo 'docs_only=false' >> "$GITHUB_OUTPUT"; exit 0; }
|
||||
changed=$(git diff --name-only "$base" HEAD)
|
||||
printf '%s\n' "$changed"
|
||||
# Skip only when there IS a diff and every path is *.md. An empty diff, an unreadable
|
||||
# range, anything else - fall through to the gate.
|
||||
if [ -n "$changed" ] && ! printf '%s\n' "$changed" | grep -qvE '\.md$'; then
|
||||
echo 'docs_only=true' >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo 'docs_only=false' >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- name: Full gate
|
||||
if: steps.scope.outputs.docs_only != 'true'
|
||||
run: bash ci.sh
|
||||
# Never gated on docs_only: release.yml re-tags this exact image, and ff-only merges make
|
||||
# every branch head a main commit - a main commit without an image is an unreleasable one.
|
||||
- name: Push app image tagged with the commit hash
|
||||
env:
|
||||
IMAGE: gitea.larvit.se/${{ github.repository }}:${{ github.sha }}
|
||||
|
||||
@@ -1173,7 +1173,7 @@ Gitea Actions (`.gitea/workflows/`) runs the pipeline; the test job runs
|
||||
|
||||
| Workflow | Trigger | Does |
|
||||
| --- | --- | --- |
|
||||
| `ci.yml` | push, any branch except `main` | the full gate (`bash ci.sh`), then build + push the app image |
|
||||
| `ci.yml` | push, any branch except `main` | the full gate (`bash ci.sh`) — skipped on a docs-only branch — then build + push the app image |
|
||||
| `release.yml` | push of a `vX.Y.Z` tag | re-tag that commit's image as `X.Y.Z`, `X.Y`, `X`, `latest`; sync those tags to Docker Hub |
|
||||
| `mirror.yml` | push to `main` or any tag, or manual | force-push `main` + tags to the [GitHub mirror](https://github.com/larvit/plainpages) |
|
||||
| `registry-cleanup.yml` | nightly cron, or manual | delete registry images that are neither release-tagged nor a branch head |
|
||||
@@ -1188,6 +1188,12 @@ no repo files involved): direct pushes are blocked, changes land via PR only, th
|
||||
**fast-forward-only** — history stays linear and `main`'s head is the exact commit hash of
|
||||
the merged branch, which is why the branch's push-triggered status carries over.
|
||||
|
||||
**Docs-only branches skip the gate** — when every path a branch changes against its merge-base
|
||||
with `main` ends in `.md`, `ci.yml` skips `bash ci.sh`. The job itself still runs and still
|
||||
builds + pushes the commit-hash image, so `CI / full-gate (push)` reports green and neither the
|
||||
merge gate nor `release.yml` notices the difference. Everything else in a diff — code, compose,
|
||||
Ory config, the workflows themselves — runs the full gate, as does an empty or unreadable diff.
|
||||
|
||||
**Container images** — after a green gate, `ci.yml` builds the app image and pushes it to the
|
||||
Gitea container registry as `gitea.larvit.se/larvit/plainpages:<full commit hash>`. Because
|
||||
merges are fast-forward-only, the image for any `main` commit already exists — it was built
|
||||
@@ -1494,7 +1500,7 @@ plugins/ Drop-in plugin folders (scanned at /app/plugins; bind-mount
|
||||
examples/ Copy-in reference material, mirroring the mount dirs: plugins/scheduling/ (the reference plugin — list/form over an upstream + permission-gated nav), plugins/admin/ (the system-admin plugin — Users/Groups/Roles/OAuth2-clients over Ory via ctx.system), both copied into plugins/; and config/menu.ts (the menu/branding template copied into config/); shifts-upstream/ is the dev mock backend the scheduling plugin reads/writes (stand-in for your real service)
|
||||
e2e-tests/ Playwright E2E: visual.spec (design system, Ory-free) + auth-refresh.spec (token timeout/re-mint) + oauth-login.spec (OAuth2 login + consent) + full-flow.spec (browser UI: password/SSO login, menu-by-role, admin CRUD, plugin page, logout) + devstack-login.spec (regression: login works from the banner's localhost URL and 127.0.0.1 is canonicalised, on the plain `docker compose up` topology); proxy.ts (same-origin gateway) + mock-oidc.ts (mock SSO provider) back full-flow. e2e-tests/Dockerfile + e2e-tests/compose.{visual,auth,oauth,full,devstack}.yml run them
|
||||
ci.sh The full CI gate: typecheck → unit tests → every E2E suite, each on a fresh, always-torn-down stack (`bash ci.sh`)
|
||||
.gitea/workflows/ Gitea Actions: ci.yml — the full gate (ci.sh) on every branch push except main;
|
||||
.gitea/workflows/ Gitea Actions: ci.yml — the full gate (ci.sh) on every branch push except main, skipped for docs-only branches;
|
||||
mirror.yml — force-sync main + tags to the GitHub mirror; see CI/CD
|
||||
README-dockerhub.md The Docker Hub repository description (docker.io/larvit/plainpages) — pasted into the Docker Hub overview by hand when it changes; see CI/CD
|
||||
```
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// Guards the docs-only fast path in .gitea/workflows/ci.yml: a branch that only touches *.md
|
||||
// skips `ci.sh`, but must still publish its commit-hash image — release.yml re-tags that exact
|
||||
// image, and fast-forward-only merges make every branch head a main commit.
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
const ci = readFileSync(new URL("../.gitea/workflows/ci.yml", import.meta.url), "utf8");
|
||||
const steps = ci.split("\n - ").slice(1);
|
||||
const step = (needle: string) => {
|
||||
const found = steps.filter((s) => s.includes(needle));
|
||||
assert.equal(found.length, 1, `exactly one step contains ${needle}`);
|
||||
return found[0]!;
|
||||
};
|
||||
|
||||
test("checkout is unshallow — the merge-base with main needs the branch's history", () => {
|
||||
assert.match(step("actions/checkout"), /fetch-depth: 0/);
|
||||
});
|
||||
|
||||
test("a docs-only branch skips the test gate", () => {
|
||||
assert.match(step("bash ci.sh"), /if: steps\.scope\.outputs\.docs_only != 'true'/);
|
||||
});
|
||||
|
||||
test("a docs-only branch still pushes its commit-hash image", () => {
|
||||
assert.doesNotMatch(step("docker push"), /^\s*if:/m);
|
||||
});
|
||||
|
||||
test("only *.md counts as docs, and anything unexpected runs the gate", () => {
|
||||
const detect = step("docs_only=");
|
||||
assert.ok(detect.includes("\\.md$"), "the non-docs match is a *.md suffix test");
|
||||
assert.ok(detect.includes("docs_only=false"), "the fallback is the full gate, never a skip");
|
||||
});
|
||||
Reference in New Issue
Block a user