This repository provides a template to rapidly deploy a modern web application stack to OpenShift using GitHub Actions, incorporating best practices for CI/CD, security, and observability. By hitting the ground running we can save weeks‑to‑months of development time, receive regular updates, and built‑in Maintenance Mode Automation, which allows an app to go on autopilot for security and dependency updates. This limits developmer involvement to critical issues only, drastically reducing labor costs and freeing up resources for higher priority work.
Includes:
- Pull Request-based pipeline
- Sandboxed development environments
- Gated/controlled production deployments (optional)
- Container publishing (ghcr.io) and importing (OpenShift)
- Security, vulnerability, infrastructure, and container scan tools
- Out-of-the-box alignment with OWASP ASVS Level 1 & 2 controls (see SECURITY.md)
- Automatic dependency patching via bcgov/renovate-config
- Maintenance Mode Automation (hands‑off updates, low‑dev mode) via the same Renovate config
- Enforced code reviews and workflow jobs (pass|fail)
- OpenShift Templates
- Prometheus Metrics export from Backend/Frontend
- Resource Tuning with Horizontal Pod Autoscaler
- Affinity and anti-affinity for Scheduling on different worker nodes
- Rolling updates with zero downtime in PROD
- Database Migrations with Flyway
- Pod disruption budgets for high availability
- Self-healing through probes/checks (startup, readiness, liveness)
- Sample application stack:
- Database: Postgres, Flyway
- Frontend: TypeScript, Caddy Server with Coraza WAF
- Backend: TypeScript, Nest.js
Setup takes an hour or less. Scripts in scripts/ do the GitHub and OpenShift parts. Each one previews its changes by default, makes them only with --apply, and is safe to re-run: it changes only what differs and leaves everything else alone. Running any of them without --apply verifies the setup: it ends with All set: nothing to change., or exits non-zero with the number of things to change. It confirms secrets exist but can't verify their values. Run them yourself, with your own logins.
-
Accounts (by hand): GitHub accounts for the team. For BC Government, also IDIR accounts and membership in the bcgov GitHub organization (instructions).
-
OpenShift namespaces (by hand): request a project set (DEV, TEST and PROD) from Platform Services at the registry. You get a licence plate, e.g.
abc123, and namespacesabc123-dev,abc123-testandabc123-prod. -
Create the repository (by hand): use this repository as a template, with
bcgov/quickstart-openshiftselected under Repository template. -
Log in (by hand):
gh auth login, then in the OpenShift console select your name (top right) >Copy login commandand paste it into a shell (oc login --token=... --server=...). You need admin rights on the repo and edit rights in all three namespaces. Then run steps 5 to 7 with one command, which previews by default and exits non-zero if anything needs changing; add--applyto make the changes:scripts/setup.sh --plate abc123 --reviewers alice,bcgov/my-team # preview, then add --apply -
OpenShift deploy account: creates the
github-actionsservice account,editrole binding and token Secret in each namespace.scripts/setup-openshift.sh --plate abc123 # preview, then add --apply -
GitHub environments, secrets and variables: creates the
testandprodenvironments (PROD with required reviewers if you name them; without--reviewersit reports the current ones), and sets theOC_NAMESPACEvariable andOC_TOKENandDB_PASSWORDsecrets for pull requests (repository level), TEST and PROD, plus theOC_SERVERvariable. Without--plateit reads the plate from existingOC_NAMESPACEvariables. It won't generate aDB_PASSWORDfor a namespace that already has a database, since a new password would lock the app out. Secrets that are already set are never overwritten. Secret values are piped fromocoropensslstraight intogh secret set; they're never printed or written to disk, and previews show only the secret's name.scripts/setup-environments.sh --plate abc123 --reviewers alice,bcgov/my-team # preview, then add --apply -
GitHub repository settings: the
mainruleset, pull request settings and private vulnerability reporting (see Repository Configuration).scripts/setup-github.sh # preview, then add --apply -
First pull request: open one to run the pipeline, then make the packages public (see Packages).
-
Optional (by hand): SonarCloud, Renovate, Sysdig, a vanity URL with TLS, PROD deployment rules and team members, all described below.
Workflows read these values. Repository-level values are used by pull request deploys; test and prod environment values override them for those deploys. Steps 5 and 6 set the required ones.
| Environment | Name | Description |
|---|---|---|
| none | vars.OC_SERVER |
OpenShift API server (repository-level) |
| none | vars.OC_NAMESPACE |
PR namespace, <plate>-dev |
| none | secrets.OC_TOKEN |
PR service token |
| none | secrets.DB_PASSWORD |
PR database password |
| test | vars.OC_NAMESPACE |
TEST namespace, <plate>-test |
| test | secrets.OC_TOKEN |
TEST service token |
| test | secrets.DB_PASSWORD |
TEST database password |
| prod | vars.OC_NAMESPACE |
PROD namespace, <plate>-prod |
| prod | secrets.OC_TOKEN |
PROD service token |
| prod | secrets.DB_PASSWORD |
PROD database password |
| prod (opt) | vars.ROUTE_HOST |
Vanity URL hostname (repository or environment) |
| prod (opt) | secrets.TLS_CERTIFICATE |
Leaf certificate PEM for vanity URL |
| prod (opt) | secrets.TLS_PRIVATE_KEY |
Private key PEM for vanity URL |
| prod (opt) | secrets.TLS_CA_CERTIFICATE |
Issuing CA PEM for vanity URL |
| none (opt) | secrets.SONAR_TOKEN_BACKEND, secrets.SONAR_TOKEN_FRONTEND |
See SonarCloud |
| none (opt) | secrets.SYSDIG_API_TOKEN |
PROD alert sync |
DB_PASSWORD is generated separately for each environment. Don't change it after the first deploy: PostgreSQL keeps its first password.
Copied the template before OC_NAMESPACE became a variable? Move it: gh variable set OC_NAMESPACE --body <plate>-dev, and the same with --env test / --env prod (or run scripts/setup-environments.sh --apply). The old secret is unused and can be deleted.
Set these by hand with gh secret set / gh variable set when you need them.
SYSDIG_API_TOKEN
Sysdig API token used to sync the PROD email-alert set on every merge. Sourced from the in-cluster Secret materialized by your SysdigTeam CR (BC Gov platform-services provisions this in your *-tools namespace). Optional — if unset, the monitor-prod step no-ops with a warning, so the deploy still succeeds.
- Reference:
${{ secrets.SYSDIG_API_TOKEN }} - Alert templates live in
monitoring/alerts/. Add or remove files to customize the alert set per app — seebcgov/action-sysdig-monitorfor the template schema and placeholder vocabulary.
TLS_CERTIFICATE, TLS_PRIVATE_KEY, TLS_CA_CERTIFICATE
PEM-formatted certificates and private key for custom vanity URL TLS on the PROD frontend OpenShift Route. Optional — required only when vars.ROUTE_HOST is configured.
- References:
${{ secrets.TLS_CERTIFICATE }},${{ secrets.TLS_PRIVATE_KEY }},${{ secrets.TLS_CA_CERTIFICATE }} - Validated and applied on-demand via the standalone
.github/workflows/route-tls.ymlworkflow usingbcgov/actions-openshift/route-tls, which checks key matching, CA chain, expiration, and archives previous certificates to an OpenShift secret. - Store these as
prodenvironment secrets (not repository secrets), and configure theprodenvironment's deployment branches and tags to restrict access (e.g. tomainor release tags); environment scoping alone does not prevent a pull-request job from requesting these secrets. - Do not pass PEMs or private keys as
workflow_dispatchinputs—dispatch inputs are recorded in plaintext in workflow run logs. Rotation is handled by updating theprodsecrets and triggering theRoute TLSworkflow withdry_run=trueto validate, thendry_run=falseto apply.
When receiving a certificate package from Entrust, map the files to GitHub secrets as follows:
| Secret | File in package | Notes |
|---|---|---|
TLS_CERTIFICATE |
<host>.pem |
Leaf certificate only (CN/SAN matches hostname). |
TLS_PRIVATE_KEY |
<host>.key |
Unencrypted private key (BEGIN PRIVATE KEY). Never commit this. |
TLS_CA_CERTIFICATE |
Entrust OV TLS Issuing RSA CA 2.pem |
Issuing intermediate CA only (one PEM). |
Leave out root certificates (e.g. Sectigo Public Server Authentication Root R46.pem or USERTrust RSA Certification Authority.pem) because clients already have them in their trust stores. Omit <host>.csr (the signing request is finished).
Upload these secrets to the prod environment via GitHub CLI:
CERT_HOST='myapp.gov.bc.ca' # Replace with the certificate filename prefix
gh secret set TLS_CERTIFICATE --env prod < "${CERT_HOST}.pem"
gh secret set TLS_PRIVATE_KEY --env prod < "${CERT_HOST}.key"
gh secret set TLS_CA_CERTIFICATE --env prod < 'Entrust OV TLS Issuing RSA CA 2.pem'ROUTE_HOST
Vanity URL hostname for the application (e.g., myapp.gov.bc.ca — hostname only, no scheme). Optional — can be set as a repository variable or environment variable on prod. When set, the standalone Route TLS workflow (.github/workflows/route-tls.yml) configures an OpenShift Route with custom TLS for the frontend service.
- Reference:
${{ vars.ROUTE_HOST }}
By default, an environment allows deployments from any branch or tag ("No restriction"). To ensure pull requests and untrusted branches cannot access production secrets without adding human approval gates, configure deployment restrictions:
- Select
Settings > Environments > prod - Under Deployment branches and tags, change the dropdown to Selected branches and tags
- Click Add deployment branch or tag rule:
- To gate by branch: select Branch and enter
main(or your protected release branch). - To gate by release tag: select Tag and enter a pattern like
v*.*.*orv*so only release tags can trigger deployment.
- To gate by branch: select Branch and enter
Two workflows can deploy PROD, and both are active by default:
merge.ymldeploys every merge to TEST, runs the tests, then deploys PROD by PR number.release.ymldeploys a published GitHub release to PROD. It finds the PR that the release's commit came from and deploys that PR's images, the same onesmerge.ymldeployed. Nothing is rebuilt.
To make PROD release-only, comment out the deploy-prod, monitor-prod and promote jobs in merge.yml. Merges then go to TEST and stop, and PROD changes only when you publish a release.
Notes:
- Publish releases from a commit on
mainthat was merged from a PR. Commits pushed straight tomainhave no PR images to deploy. - If the
prodenvironment limits deployment branches and tags, add a tag rule for your release tags (e.g.v*), or releases can't deploy. - Both workflows share a concurrency group, so they never deploy PROD at the same time.
Dependabot and Mend Renovate can both provide dependency updates using pull requests. Dependabot is simpler to configure, while Renovate is much more configurable and lighter on resources.
A configuration file (renovate.json) is included with this template that sources configuration from our renovate repository.
Tip
Once Renovate is set up, you can enable Maintenance Mode Automation by following the checklist in the Maintenance Mode Automation section of this README. This will let Renovate auto‑merge safe updates after all CI checks pass.
BC Government employees can request having the Renovate and Renovate Approve GitHub Apps installed on their repository through the CITZ Service Desk. Most others can add their repositories directly from the Renovate and Renovate Approve GitHub Apps.
Dependabot is no longer recommended as an alternative to Renovate for generating security, vulnerability and dependency pull requests. It can still be used to generate warnings under the GitHub Security tab, which is only viewable by repository administrators.
SonarCloud performs static code analysis, test coverage tracking, and quality gate checks via the bcgov/action-test-and-analyse action in .github/workflows/analysis.yml. SonarCloud integration is optional—the test suites and CI pipeline run successfully without it.
BC Government employees can request SonarCloud projects via devops-requests (select Request for import GitHub repository on SonarCloud) or directly through the CITZ Service Desk.
Before submitting the request, ensure the designated project administrator has logged in to the SonarCloud console under bcgov-sonarcloud using their GitHub account.
Fill out the intake form using the following selections:
| Field | Selection | Notes |
|---|---|---|
| GitHub username | <admin-username> |
Must be the user who logged into SonarCloud above; will be assigned project administrator. |
| GitHub Repo URL | https://github.com/bcgov/<repo> |
Full repository URL. |
| New Code Definition | Previous version | Quality gates evaluate changes relative to previous releases rather than a rolling day count. |
| Setup as a monorepo | Yes | Quickstart repositories contain both backend and frontend. |
| Additional Comments | backend, frontend |
Explicitly list component names so separate SonarCloud sub-projects are created. |
Once the platform team provisions the projects, two separate SonarCloud projects are created (one for backend and one for frontend):
- Generate analysis tokens in SonarCloud for both projects and save them as repository secrets:
SONAR_TOKEN_BACKENDandSONAR_TOKEN_FRONTEND. Both tokens are required; this repository does not use a single sharedSONAR_TOKEN. - Update
-Dsonar.projectKeyin.github/workflows/analysis.ymlfor both jobs to match your repository (e.g.<repo>_backendand<repo>_frontendinstead ofquickstart-openshift_*).
This repository uses Knip for dependency scanning to identify unused dependencies and exports. Knip runs automatically as part of the Analysis workflow via the bcgov/action-test-and-analyse action.
Note: As a template repository, Knip runs in warning mode (non-blocking) to allow teams to customize dependencies without build failures. Teams can optionally change dep_scan: warning to dep_scan: error in their forks to enforce dependency scanning as a blocking check.
When Knip identifies unused dependencies, you have two options:
- Remove the dependency - If it's truly unused, remove it from
package.json - Report as false positive - If the dependency is used but not detected by static analysis
Do not create team-specific knip.config.ts files. All Knip configuration is managed centrally in the upstream action repository.
If you encounter a false positive (a dependency that is used but flagged as unused), report it upstream:
-
Verify it's a false positive by checking:
- Is it exported from a utility file (e.g.,
test-utils.tsx)? - Is it used via dynamic imports?
- Is it required by build tools or other dependencies?
- Is it used in configuration files that Knip doesn't analyze?
- Is it exported from a utility file (e.g.,
-
Open a PR to the upstream repository:
- Repository:
bcgov/action-test-and-analyse - File:
.knip.json - Add the dependency to the
ignoreDependenciesarray
- Repository:
-
Include justification in your PR:
- Why it's a false positive
- How the dependency is used
- Example: "Exported from test-utils files for use in tests but may not be directly imported yet. Common pattern in testing utilities."
If @testing-library/user-event is flagged but exported from test-utils.tsx:
Upstream PR to bcgov/action-test-and-analyse/.knip.json:
{
"ignoreDependencies": [
"swagger-ui-express",
"rimraf",
"@types/node",
"@types/react",
"@types/react-dom",
"@testing-library/user-event"
],
"ignoreBinaries": [
"rimraf"
]
}PR Description:
Add
@testing-library/user-eventto ignoreDependenciesThis dependency is exported from test-utils files for use in tests but may not be directly imported yet. This is a common pattern in testing utilities where dependencies are re-exported for convenience.
- Exported APIs: Dependencies exported from utility files (like
test-utils.tsx) that are intended for use but may not be directly imported yet - Indirect usage: Dependencies used by build tools, scripts, or other dependencies that static analysis can't detect
- Dynamic imports: Dependencies loaded via dynamic imports or string-based requires
- Configuration files: Dependencies used in config files that aren't detected by static analysis
- Type-only imports: TypeScript type-only imports that are stripped at runtime
scripts/setup-github.sh (checklist step 7) sets:
-
Ruleset
mainon the default branch: blocks deletion and force pushes; requires a pull request with 1 approval and resolved conversations, merged by squash only; requires CodeQL code scanning results with no alerts at high or higher; and requires these status checks, which must be up to date withmain:PR Results(PR workflow)Analysis Results(Analysis workflow)Validate Results(PR Validate workflow)
Each is the summary job of a workflow that runs on every pull request. The jobs they summarize (builds, deploys, tests, scans) are left out because they're skipped when their paths don't change, which would block merges. Repository admins may bypass the ruleset only by merging a pull request. Trivy results are still reported to the Security tab, but Trivy is deliberately not a merge gate because it fails often. Other rules already in the ruleset (e.g. Copilot code review) are kept.
-
Pull requests: squash merging only, defaulting to the pull request title; suggest updating branches; allow auto-merge; delete head branches after merge.
-
Private vulnerability reporting, which SECURITY.md points reporters to.
Packages are available from your repository (link on right). All should have visibility set to public for the workflows to run successfully.
E.g. https://github.com/bcgov/quickstart-openshift/packages
Don't forget to add your team members!
- Select Settings (gear, top right) *> Collaborators and teams (under
Access) - Click
Add peopleorAdd teams - Use the search box to find people or teams
- Choose a role (read, triage, write, maintain, admin)
- Click Add
This repository is architected and hardened out-of-the-box to align with Levels 1 and 2 of the OWASP Application Security Verification Standard (ASVS) v4.0.3. A detailed security mapping matrix is documented in SECURITY.md, detailing our implementation of:
- Active WAF Defense: Inline Coraza WAF running inside the Caddy reverse proxy.
- Tiered Isolation: NetworkPolicies enforcing network boundaries between the frontend, backend, and database tiers.
- Platform/Container Hardening: Read-only root filesystems, non-root execution, privilege escalation blocks, default seccomp profiles, and drop capabilities.
- Build-Time & Dynamic Testing: Automated static analysis (Trivy, CodeQL), dependency auditing (Renovate, Knip), and weekly dynamic vulnerability scans (OWASP ZAP).
Out of the box, the deployment templates in this repository enforce strict container-level security contexts:
- Read-Only Root Filesystems: Containers cannot write to the root filesystem at runtime (
readOnlyRootFilesystem: true). - Non-Root Execution: Containers are blocked from executing as the root user (
runAsNonRoot: true). - Privilege Escalation Blocked: Containers cannot gain more privileges than their parent process (
allowPrivilegeEscalation: false).
If your application requires writing files at runtime (e.g., temporary caches, uploaded attachments, config logs):
- Do not disable the security context.
- Mount a memory-backed (
tmpfs) volume on the writable path usingemptyDirinopenshift.deploy.yml:volumeMounts: - mountPath: /tmp name: tmp volumes: - name: tmp emptyDir: medium: Memory sizeLimit: 256Mi
- Always specify a
sizeLimit. Running memory-backed volumes without a size limit allows a runaway container to exhaust the host node's RAM, triggering an out-of-memory (OOM) storm that will terminate other pods.
These workflows and actions enforce a pull request based flow.
flowchart TD
A1(PR_Env_1) -->|tests| B
A2(PR_Env_2) -->|tests| B
A3(PR_Env_3) -->|tests| B
Ad@{ shape: text, label: "..." }
An(PR Env n) -->|tests| B
B(TEST_Env) -->|tests| C(PROD_Env)
%% Define styles with good contrast for light/dark modes
%% PR Environments (using distinct, reasonably bright colors)
style A1 fill:#ffeadb,stroke:#ff8c42,stroke-width:2px,color:#5c3d1e %% Light Orange/Orange
style A2 fill:#dbeaff,stroke:#4285f4,stroke-width:2px,color:#1a3f7a %% Light Blue/Blue
style A3 fill:#dfffea,stroke:#34a853,stroke-width:2px,color:#154b24 %% Light Green/Green
style An fill:#fce8ff,stroke:#a142f4,stroke-width:2px,color:#4d1e7a %% Light Purple/Purple
%% TEST Environment
style B fill:#e6f4ea,stroke:#34a853,stroke-width:3px,color:#154b24 %% Lighter Green/Green
%% PROD Environment
style C fill:#fff4d8,stroke:#fbbc05,stroke-width:3px,color:#7a5f01 %% Light Gold/Gold
%% Link style
linkStyle default stroke:#757575,stroke-width:1px
Here's a more detailed view showing a single pull request.
flowchart TD
A(Developer)
B(Pull Request)
Ba(Build Images,<br/>Deploy Images,<br/>E2E Tests)
Bb(Unit Tests,<br/>Security Analysis,<br/>Vulnerability Analysis)
Bc(Validate PR Title,<br/>Provide User Feedback)
Bd(Code Review)
C{Verify Results}
D(Merge)
E(Deploy Images to TEST)
F{E2E Tests,<br/>Load Tests,<br/>Analysis}
G(Deploy Images to PROD)
H(Tag Images as PROD)
A --> B
B --> Ba --> C
B --> Bb --> C
B --> Bc --> C
B --> Bd --> C
C -- fail --> A
C -- pass --> D --> E --> F
F -- fail --> A
F -- pass --> G --> H
%% Define styles with good contrast for light/dark modes
%% Developer & PR Actions (Blue)
style A fill:#dbeaff,stroke:#4285f4,stroke-width:2px,color:#1a3f7a
style B fill:#dbeaff,stroke:#4285f4,stroke-width:2px,color:#1a3f7a
%% PR Checks & Validation (Light Green)
style Ba fill:#e6f4ea,stroke:#34a853,stroke-width:2px,color:#154b24
style Bb fill:#e6f4ea,stroke:#34a853,stroke-width:2px,color:#154b24
style Bc fill:#e6f4ea,stroke:#34a853,stroke-width:2px,color:#154b24
%% Code Review (Light Gold - requires attention)
style Bd fill:#fff4d8,stroke:#fbbc05,stroke-width:2px,color:#7a5f01
%% Decision Points (Purple)
style C fill:#fce8ff,stroke:#a142f4,stroke-width:2px,color:#4d1e7a
style F fill:#fce8ff,stroke:#a142f4,stroke-width:2px,color:#4d1e7a
%% Merge & TEST Deployment (Green)
style D fill:#dfffea,stroke:#34a853,stroke-width:2px,color:#154b24
style E fill:#e6f4ea,stroke:#34a853,stroke-width:3px,color:#154b24
%% PROD Deployment & Tagging (Gold)
style G fill:#fff4d8,stroke:#fbbc05,stroke-width:3px,color:#7a5f01
style H fill:#fff4d8,stroke:#fbbc05,stroke-width:3px,color:#7a5f01
%% Link style
linkStyle default stroke:#757575,stroke-width:1px
Runs on pull request submission.
- Provides safe, sandboxed deployment environments
- Build action pushes to GitHub Container Registry (ghcr.io)
- Build triggers select new builds vs reusing builds
- Deploy only when changes are made
- Deployment includes curl checks and optional penetration tests
- Run tests (e2e, load, integration) when changes are made
- Posts sticky comment with deployment links when deployments occur
- Other checks and updates as required
Runs on pull request submission.
- Enforces conventional commits in PR title
Runs on pull request submission or merge to the default branch.
- Unit tests (should include coverage)
- CodeQL/GitHub security reporting (now handled as GitHub default!)
- Trivy password, vulnerability and security scanning
Runs on pull request close or merge.
- Cleans up OpenShift objects/artifacts
- Merge retags successful build images as
latest
Runs on merge to main branch.
- Code scanning and reporting to GitHub Security overview
- Zero-downtime* TEST deployment
- Penetration tests on TEST deployment (optional)
- Zero-downtime* PROD deployment
- Labels successful deployment images as PROD
- Sysdig email alerts synced to PROD (no-op if
SYSDIG_API_TOKENis unset)
* excludes database changes
Runs on scheduled job (cronjob) or workflow dispatch.
- PR environment purge
- Generate SchemaSpy documentation
- Tests (e2e, load, integration) on TEST deployment
This repository supports operating downstream projects in a low-maintenance, sustainment-only mode. When configured, dependency updates can be completely automated with zero manual intervention required for minor and patch updates, and optional hands-off major version bumps.
- Automated Dependency Updates: The repository includes a
renovate.jsonfile configured to extendbcgov/renovate-config. This allows Mend Renovate to scan for updates, group related package updates together, and prepare automated Pull Requests. - Branch Protection & Gated Merging: GitHub branch protection rules on
mainensure that no PR is merged without verifying that the application remains functional. - CI/CD Validation: The
PRworkflow builds, deploys, and runs tests against sandboxed preview environments;PR Validateenforces PR title/description conventions. - (Optional) Automerging: If Renovate automerge is enabled and all required checks pass, Renovate can merge updates back to the
mainbranch, triggering an automated rolling deployment.
Before transitioning an application utilizing this template to full maintenance mode, downstream teams should verify and meet the following criteria:
- Robust Automated Test Coverage: A high test coverage threshold (e.g., 70% or higher for both statements and branches) should be enforced. Unit, integration, and end-to-end tests must be capable of catching regressions automatically.
- PR Preview Environments: Ensure sandboxed preview environments deploy reliably to OpenShift for all pull requests.
- Automated Smoke Tests / Probes: The application must implement runtime health checks that verify connectivity to database instances and external downstream services (e.g., S3, Keycloak, or mail services).
- Automerge Policies: Verify that the Mend Renovate GitHub app is integrated and allowed to manage automerges for passing builds.
To safely automate dependency updates, runtime health checks are mandatory to catch runtime connectivity failures before an update is merged.
This template provides out-of-the-box support for self-healing and verification through container probes:
- NestJS Backend: Implements an
/api/healthendpoint using NestJS Terminus (currently checks DB connectivity via Prisma). - Frontend (Caddy/Vite): OpenShift readiness/liveness probes are configured to check
/to confirm the web server is responsive and serving assets.
Tip: For a fully mature maintenance mode setup, customize the backend health checks to query external APIs and database migrations. If a dependency goes down, the health check should return 503 Service Unavailable, blocking the automated merge.
- Renovate creates PRs for dependency updates (npm packages, GitHub Actions, Docker base images).
- All status checks pass successfully:
- Linting and unit tests complete.
- Preview environment successfully deploys to OpenShift.
- Automated smoke tests verify the preview environment is fully functional.
- No merge conflicts are detected.
- A dependency upgrade causes compile, build, lint, or test failures.
- A smoke test or deep health check fails post-deployment.
- A security vulnerability scanner (e.g., Trivy or CodeQL) flags a new vulnerability.






