CI: mirror main + tags to GitHub after every merge to main #3

Merged
lilleman merged 1 commits from github-mirror into main 2026-07-03 15:51:07 +02:00
3 changed files with 34 additions and 2 deletions
Showing only changes of commit 6e60df7008 - Show all commits
+17
View File
@@ -0,0 +1,17 @@
name: Mirror
on:
push:
branches: [main]
workflow_dispatch:
jobs:
github-mirror:
runs-on: docker-host
steps:
- uses: actions/checkout@v4.2.2
with:
fetch-depth: 0
- run: |
git push --force \
"https://x-access-token:${{ secrets.MIRROR_GITHUB_TOKEN }}@github.com/larvit/plainpages.git" \
refs/remotes/origin/main:refs/heads/main 'refs/tags/*:refs/tags/*'
+16 -1
View File
@@ -8,6 +8,10 @@ folders** — the admin UI for a webshop, a public service portal, a school sche
water-treatment dashboard — without rebuilding auth, the menu, and the design system every water-treatment dashboard — without rebuilding auth, the menu, and the design system every
time. time.
> **True home: <https://gitea.larvit.se/larvit/plainpages>** — development, issues, and PRs
> live there. [github.com/larvit/plainpages](https://github.com/larvit/plainpages) is a
> read-only mirror, force-synced on every merge to `main`.
## Quick start ## Quick start
> **Requirements:** **Docker** and **Docker Compose** — and nothing else. > **Requirements:** **Docker** and **Docker Compose** — and nothing else.
@@ -1170,6 +1174,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`) | | `ci.yml` | push, any branch except `main` | the full gate (`bash ci.sh`) |
| `mirror.yml` | push to `main`, or manual | force-push `main` + tags to the [GitHub mirror](https://github.com/larvit/plainpages) |
`main` is not re-tested on push — its commits are meant to arrive already green from a `main` is not re-tested on push — its commits are meant to arrive already green from a
gated branch, so the status check to gate a merge on is `CI / full-gate (push)`. gated branch, so the status check to gate a merge on is `CI / full-gate (push)`.
@@ -1180,6 +1185,15 @@ 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.
**GitHub mirror** — [github.com/larvit/plainpages](https://github.com/larvit/plainpages) is a
read-only mirror; after every merge, `mirror.yml` force-pushes `main` and all tags there,
overwriting any drift (refs deleted on Gitea are not pruned). One-time setup: a dedicated
GitHub machine account with write access to the GitHub repo (whose `main` must not block
force-pushes), and a fine-grained PAT scoped to that repo (Contents: read & write), stored
as the Gitea Actions secret `MIRROR_GITHUB_TOKEN` (repo Settings → Actions → Secrets; Gitea
rejects secret names starting with `GITHUB_`/`GITEA_`). Trigger the workflow manually for
the first sync — until the secret exists, the mirror job fails loud on each merge.
**One-time server setup** — register an **One-time server setup** — register an
[act_runner](https://docs.gitea.com/usage/actions/act-runner) in host mode with the label [act_runner](https://docs.gitea.com/usage/actions/act-runner) in host mode with the label
`docker-host` (config: `labels: ["docker-host:host"]`) on a machine with Docker Engine + `docker-host` (config: `labels: ["docker-host:host"]`) on a machine with Docker Engine +
@@ -1402,7 +1416,8 @@ 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; see CI/CD .gitea/workflows/ Gitea Actions: ci.yml — the full gate (ci.sh) on every branch push except main;
mirror.yml — force-sync main + tags to the GitHub mirror; see CI/CD
``` ```
## Extending the core ## Extending the core
+1 -1
View File
@@ -5,7 +5,7 @@
- [x] Make the internal admin pages for users groups etc into a plugin instead in the examples folder and remove them from the internal source. Add a part in the quick start about copying this plugin into the plugins folder to enable GUI user- and group admining. - [x] Make the internal admin pages for users groups etc into a plugin instead in the examples folder and remove them from the internal source. Add a part in the quick start about copying this plugin into the plugins folder to enable GUI user- and group admining.
- [x] CI/CD - Test on push to any branch except main. (`.gitea/workflows/ci.yml` runs `bash ci.sh`; the one-time act_runner setup it needs is documented in README → CI/CD.) - [x] CI/CD - Test on push to any branch except main. (`.gitea/workflows/ci.yml` runs `bash ci.sh`; the one-time act_runner setup it needs is documented in README → CI/CD.)
- [x] CI/CD - Require PR to main and don't allow merge if tests does not pass. Only allow linear history and history that leaves the last commit hash on main the exact same as on the branch we just merged in. (Gitea branch protection on main + fast-forward-only merge style, set via API; documented in README → CI/CD.) - [x] CI/CD - Require PR to main and don't allow merge if tests does not pass. Only allow linear history and history that leaves the last commit hash on main the exact same as on the branch we just merged in. (Gitea branch protection on main + fast-forward-only merge style, set via API; documented in README → CI/CD.)
- [ ] CI/CD - Sync up to github after every successful merge to main, URL: git@github.com:larvit/plainpages.git - also note the true home top of the README. Force push to github, it should only ever be a mirror of the gitea.larvit.se repository. - [x] CI/CD - Sync up to github after every successful merge to main, URL: git@github.com:larvit/plainpages.git - also note the true home top of the README. Force push to github, it should only ever be a mirror of the gitea.larvit.se repository. (`.gitea/workflows/mirror.yml` force-pushes main + tags over HTTPS with a dedicated account's PAT in the `MIRROR_GITHUB_TOKEN` secret; setup documented in README → CI/CD.)
- [ ] CI/CD - Build docker images as part of the requirements to be able to merge to main. Push them with the git commit hash as docker tag. Push to container registry at Gitea. - [ ] CI/CD - Build docker images as part of the requirements to be able to merge to main. Push them with the git commit hash as docker tag. Push to container registry at Gitea.
- [ ] CI/CD - Re-tag docker images from git hash to semver when a semver git tag is pushed. - [ ] CI/CD - Re-tag docker images from git hash to semver when a semver git tag is pushed.
- [ ] CI/CD - Require human to make docker hub account - sync docker images to docker hub after each deploy build. - [ ] CI/CD - Require human to make docker hub account - sync docker images to docker hub after each deploy build.