docs: detail Phase 1 Forgejo CI/CD implementation #2

Closed
dernbu wants to merge 3 commits from feat/forgejo-cicd-foundation into dev
Owner

Summary

  • turn Phase 1 of PLAN.md into an implementation-ready CI/CD execution plan
  • define eight ordered work packages for workspace setup, health scaffolds, Compose, validation, the guarded host wrapper, deploy workflows, Caddy, and acceptance rehearsals
  • establish non-negotiable security invariants, three acceptance gates, an evidence matrix, commit strategy, risks, and required operator inputs
  • link the detailed document from the master Phase 1 section

Key contracts

  • pull-request validation remains unprivileged and has no Docker socket, application secrets, or deployment sudo
  • protected dev maps only to staging; protected main maps only to production and the shared edge
  • deployments require validation-complete for the exact SHA and go through a root-owned, operator-installed wrapper
  • staging and production retain separate projects, networks, storage, credentials, and release state
  • failed health checks re-run a recorded prior application release; database restoration is never automatic
  • CloudFront DNS and the existing public blog remain untouched during Phase 1

Validation

  • git diff --check
  • verified both relative documentation links resolve locally
  • reviewed the plan against PLAN.md sections 15-18, 22-24, and ADR 0009
  • confirmed the branch is one commit ahead of current origin/dev

Documentation only: this merge request does not add workflows, application scaffolds, containers, host configuration, or deployment privileges.

## Summary - turn Phase 1 of `PLAN.md` into an implementation-ready CI/CD execution plan - define eight ordered work packages for workspace setup, health scaffolds, Compose, validation, the guarded host wrapper, deploy workflows, Caddy, and acceptance rehearsals - establish non-negotiable security invariants, three acceptance gates, an evidence matrix, commit strategy, risks, and required operator inputs - link the detailed document from the master Phase 1 section ## Key contracts - pull-request validation remains unprivileged and has no Docker socket, application secrets, or deployment `sudo` - protected `dev` maps only to staging; protected `main` maps only to production and the shared edge - deployments require `validation-complete` for the exact SHA and go through a root-owned, operator-installed wrapper - staging and production retain separate projects, networks, storage, credentials, and release state - failed health checks re-run a recorded prior application release; database restoration is never automatic - CloudFront DNS and the existing public blog remain untouched during Phase 1 ## Validation - `git diff --check` - verified both relative documentation links resolve locally - reviewed the plan against `PLAN.md` sections 15-18, 22-24, and ADR 0009 - confirmed the branch is one commit ahead of current `origin/dev` Documentation only: this merge request does not add workflows, application scaffolds, containers, host configuration, or deployment privileges.
@ -0,0 +126,4 @@
| Project | Services | Networks | Persistent state | Updated by |
|---|---|---|---|---|
| `personal-blog-edge` | Caddy | external staging and production edge networks | Caddy data/config and bounded access logs | production workflow only; manual bootstrap before first deployment |
Author
Owner

Hmm so I think instead the Caddy should be inside the staging / prod docker compose projects. I will have additional infrastructure do the TLS termination and dns registration, but the Caddy for each project acts as a reverse proxy in front of all the services.

Hmm so I think instead the Caddy should be inside the staging / prod docker compose projects. I will have additional infrastructure do the TLS termination and dns registration, but the Caddy for each project acts as a reverse proxy in front of all the services.
@ -0,0 +175,4 @@
Do not copy GitHub-specific event, token, or expression assumptions without a
small compatibility workflow proving them on this Forgejo instance.
### 5.3 DNS and pre-cutover TLS
Author
Owner

No special cms.* domain, can we do this? I'd prefer the strapi to be under blog.yongbeom.com/admin. Help me also check strapi supports this feature that it can occupy a domain subpath instead of a separate domain. Also let's make the sub domain _blog instead, and later on as the last step we can do a cutover by modifying the dns record.

No special cms.* domain, can we do this? I'd prefer the strapi to be under blog.yongbeom.com/admin. Help me also check strapi supports this feature that it can occupy a domain subpath instead of a separate domain. Also let's make the sub domain _blog instead, and later on as the last step we can do a cutover by modifying the dns record.
@ -0,0 +201,4 @@
Host files:
```text
/etc/personal-blog/staging.env
Author
Owner

.env wont be on the host. All stored as runner secrets that we echo to dotenv on the runner at the start of the Workflow and then clean up afterwards. Volumes in ~/.volumes/blog/{staging,production}, backed up into backblaze, cloudflare buckets with restic. We can add backup as the last step. Only setup needed on the host should be the infra and the runner, no other global files.

.env wont be on the host. All stored as runner secrets that we echo to dotenv on the runner at the start of the Workflow and then clean up afterwards. Volumes in ~/.volumes/blog/{staging,production}, backed up into backblaze, cloudflare buckets with restic. We can add backup as the last step. Only setup needed on the host should be the infra and the runner, no other global files.
@ -0,0 +425,4 @@
2. validate the argument count, literal environment, and lowercase full SHA;
3. map the environment to a hard-coded protected branch, project, env file,
release/state/upload roots, networks, aliases, and smoke target;
4. acquire `/var/lock/personal-blog-docker.lock` with a bounded wait;
Author
Owner

No need lock, forgejo takes care of it

No need lock, forgejo takes care of it
@ -0,0 +435,4 @@
8. verify path ownership/modes plus disk and memory headroom;
9. render Compose, run topology assertions, and build environment+SHA-tagged
images before replacing a running service;
10. run the exact application command:
Author
Owner

Let's have a just file in the base directory for all the commands, and the runner runs just xyz

Let's have a just file in the base directory for all the commands, and the runner runs just xyz
@ -0,0 +440,4 @@
/etc/personal-blog/<environment>.env -f infra/compose.yaml up -d --build
--remove-orphans`;
11. for production only, validate and reconcile the edge project from the same
release after the application is ready enough to accept edge traffic;
Author
Owner

Make validation as simple as possible

Make validation as simple as possible
@ -0,0 +490,4 @@
- waits a bounded time for `validation-complete` success on that exact SHA and
fails closed on missing, pending, cancelled, failed, ambiguous, or stale
status;
- invokes only `sudo ... staging "$DEPLOY_SHA"`.
Author
Owner

Why need sudo? Just docker compose up/down

Why need sudo? Just docker compose up/down
dernbu closed this pull request 2026-08-10 13:10:10 +02:00

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
dernbu/personal-blog!2
No description provided.