This commit is contained in:
+22
-1
@@ -8,7 +8,28 @@ jobs:
|
|||||||
runs-on: docker-host
|
runs-on: docker-host
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.2.2
|
- 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
|
- name: Push app image tagged with the commit hash
|
||||||
env:
|
env:
|
||||||
IMAGE: gitea.larvit.se/${{ github.repository }}:${{ github.sha }}
|
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 |
|
| 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 |
|
| `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) |
|
| `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 |
|
| `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
|
**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.
|
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
|
**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
|
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
|
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)
|
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
|
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`)
|
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
|
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
|
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