10 KiB
S6 — Manual versioned production deployment through Gitea
Status: REFINED
Agreed after the grill-me interview; shared understanding confirmed.
User story
As a developer, I want to manually release a verified main-branch build from Gitea to the Synology, choosing a semantic version increment, so that I control when the launching partner receives a new production version and can recover the previous working version when necessary.
This replaces the original automatic-deployment story. Build, test and image publication remain automatic; production deployment requires a deliberate action.
Existing foundation and scope
- Extend the implemented S4 pipeline, which verifies builds and publishes matching frontend/backend images with immutable full commit SHA tags for successful main builds. Deploy those existing images without rebuilding them.
- Target one production Docker Compose stack on the Synology (
linux/amd64). The existing Gitea runner triggers deployment over SSH. - Keep one active backend. A brief restart interruption is acceptable, including interruption of a scheduling calculation; the user may restart the calculation.
- Production is private to the LAN or existing VPN. A configurable Synology hostname/IP and port provide access without an application login.
- Publish only the frontend port. Keep the backend reachable within the Compose network; route browser API requests through the frontend proxy.
- Actual server addresses, ports, paths and account configuration must be established and documented during implementation, without committing secrets.
Manual release interface
Provide a separate Deploy production workflow using Gitea's native manual
workflow trigger (workflow_dispatch). Its Run workflow popup offers:
- New release or Retry release mode.
- For a new release: a release-type choice of patch, minor or major.
- For a retry: the existing release version tag.
Provide a manual rollback action for the previous successful production release. The native Gitea button need not be renamed or embedded into an existing build run. Gitea 1.25.4 supports the required modal and choice inputs.
New release eligibility and ordering
- Run the release workflow from main. Branch builds cannot deploy production.
- Pin the current main SHA at workflow start. Require successful build checks and image publication for that exact SHA; missing or failed verification leaves production unchanged. Both images must be retrievable before replacement.
- Recheck main immediately before changing production. If it has advanced, abort without changing production. Once replacement starts, finish deployment or recovery using the pinned SHA; later commits require another manual release.
- Serialize deployment, recovery, rollback, version allocation and tag creation. Prevent stale runs from replacing newer successful production releases.
- Reject a new release for an already release-tagged commit and direct the operator to retry or rollback instead. Repeated clicks must not create extra versions or deploy out of order.
Semantic versioning and Git tags
- Use one shared application version for frontend and backend, represented by
an immutable annotated Git tag
vMAJOR.MINOR.PATCHon the verified SHA. - The first release is
v1.0.0. Inspect remote tags before initialization and fail visibly on conflicting existing history instead of overwriting it. - Subsequent releases increment the highest existing release version tag, including tags belonging to failed deployments. A rollback does not lower the version baseline. Patch increments PATCH; minor increments MINOR and resets PATCH; major increments MAJOR and resets MINOR and PATCH.
- For example, from
v1.2.3, the choices producev1.2.4,v1.3.0orv2.0.0. - After checking eligibility and image availability, create the tag before changing production. This establishes tag-writing access before replacement. Never move a release tag or reuse its version for different code.
- Retain a tag if deployment subsequently fails or aborts. Record the outcome so a tag is not mistaken for proof of successful production deployment.
- A retry reuses the existing tag and exact SHA without allocating a version. Require successful build verification and matching available images. Retrying an older release must not supersede a newer successful release. Retrying an existing release is an explicit exception to the current-main requirement.
- Rollback restores an existing release and creates no new version. It is an explicit exception to the prohibition on replacing newer code with older code.
- Continue using SHA-tagged container images; the Git release tag and deployment record map the semantic version to that SHA and image pair.
- Maven/npm version fields and automated changelog generation are outside S6. Do not introduce a version-bump commit and rebuild into the release path.
Deployment checks and recovery
- Retrieve the candidate images and validate prerequisites before replacing the running release. Frontend and backend must use the same selected commit SHA.
- Within five minutes of starting the replacement, require all of:
- Backend readiness succeeds.
- The frontend page is served.
- A read-only API request succeeds through the frontend proxy.
- Both running containers match the selected SHA/image pair.
- Checks must not create or modify user data. Log the selected SHA/version and final outcome.
- A failed candidate automatically restores the previous successful image pair and checks its health. The workflow remains failed even if recovery succeeds.
- Retain references to the current and previous successful releases and ensure their images remain available. A manual rollback targets that previous release; fail before modifying production if its required images cannot be retrieved.
- If the first deployment fails with no previous release, or recovery itself fails, stop with a clear failure report and manual recovery instructions. Preserve diagnostics; do not enter an endless deployment/recovery loop.
- Record successful production release state separately from allocated Git tags, so failed attempts do not become the rollback target.
Data policy and S16/S17 boundaries
S16 is not a prerequisite for S6. Until S16 is implemented, application state is held in memory and may be lost during deployment, rollback or recovery. The launching-partner phase explicitly accepts this limitation. Restoring old images does not restore lost data.
- State the temporary data-loss limitation in the workflow description and deployment runbook. The operator is responsible for informing the launching partner; do not add a further approval step to each deployment.
- After S16 is implemented, deployments and rollbacks must preserve saved data. Keep persistent storage outside replaceable containers and do not delete it during deployment or recovery.
- After S16, ordinary release and rollback paths require database compatibility with the previous application version. Incompatible migrations require a separately planned maintenance procedure; image rollback cannot undo them.
- Database backup and restore remain separate S17 work. S6 does not promise recovery of data lost before persistence or through storage loss.
Credentials and operational documentation
- Use a dedicated Synology deployment account with SSH key authentication and a pinned server host key. Document the narrowly required deployment permissions.
- Use a separate registry credential with pull access for production and a credential with the required repository permission for release-tag creation.
- Store deployment secrets in Gitea Actions secrets, provision server-side credentials securely where needed, and keep secret values out of Git and logs.
- Document initial provisioning, configuration, release/retry/rollback operation, credential setup, health checks, release records and manual recovery.
- Retain deployment diagnostics for fourteen days, including failed deployment and recovery outcomes, without exposing credentials.
Acceptance criteria
- Successful main builds still automatically verify and publish matching SHA images; publishing does not automatically change production.
- Gitea's manual workflow form provides new/retry mode and the appropriate release-type or existing-tag inputs; invalid combinations fail clearly.
- The first release and subsequent patch/minor/major releases allocate the agreed versions and immutable annotated remote Git tags on verified SHAs.
- Duplicate new releases, tag conflicts, concurrent attempts, stale main snapshots and older pending deployments cannot overwrite release identity or replace a newer successful release.
- An unverified release or missing image is rejected before production changes; branch builds cannot deploy production.
- A real Synology deployment serves the frontend and proxied API over the private endpoint, with both containers running the selected image pair.
- Readiness, frontend, proxied read-only API and image-identity checks establish success within five minutes or trigger recovery.
- A deliberately failed release restores the previous successful version, while the workflow reports failure. Failed first deployment and failed recovery produce actionable diagnostics and manual recovery instructions.
- A failed release can be retried with its existing tag/SHA without allocating another version or superseding a newer successful release.
- Manual rollback demonstrably restores the previous successful release with no new version, and required retained images are available.
- The temporary pre-S16 data-loss policy is visible in workflow documentation and the runbook. Once S16 applies, demonstrate saved data surviving both deployment and rollback, including the migration compatibility requirement.
- Server setup and operational recovery documentation are usable; diagnostics and release outcomes are retained for fourteen days without secrets.
Implementation handoff
This specification records the confirmed design. Implementing workflows, release automation, Compose configuration, Synology provisioning and collecting actual deployment/recovery evidence are subsequent work. Configuration alone does not complete S6.