dev, staging, and prod are kept identical across the branch, the GitHub Environment, and the file under config/environments/ so there is no mapping layer to forget.
When to use this
- You are deploying Artemis City from a fork or self-hosted clone and need a promotion path between dev, staging, and prod.
- You want CI to run on every push and PR to each environment branch, with the active config validated automatically.
- You want production secrets and approvals enforced by GitHub Environments rather than living in repo-wide secrets.
ARTEMIS_ENV=dev (the default) when loading config.
Branches and approvals
Promotion flow:
dev. A push to dev triggers the Promote cascade, which runs the test gate and then fast-forwards staging and prod to the tested commit in a single run. Approval gates live on the CircleCI deploy workflows, not on promotion PRs — the cascade only moves branch pointers.
Configuration files
Each environment has a YAML file underconfig/environments/:
config/environments/dev.yamlconfig/environments/staging.yamlconfig/environments/prod.yaml
dev.yaml looks like this:
Selecting the active environment
The active environment is selected by theARTEMIS_ENV variable. Valid values are dev, staging, and prod; unset defaults to dev.
load_environment() raises ValueError if ARTEMIS_ENV is set to anything outside the valid set, and FileNotFoundError if the YAML file for the requested environment is missing — both surface early in CI.
CI/CD pipeline
CircleCI (.circleci/config.yml) is the primary CI/CD system for the repo. GitHub Actions retains a single workflow, promote.yml, which drives the promotion cascade between environment branches.
CircleCI — primary CI/CD
.circleci/config.yml runs on every branch and PR and holds the deploy jobs for each environment:
docs-mirror— fails the build ifCLAUDE.mdandAGENTS.mdhave drifted from being byte-for-byte identical.python-checks— matrix over Python 3.11, 3.12, and 3.13. Installsrequirements.txt+requirements-dev.txt, validates everyconfig/environments/*.yamlwithsrc.utils.environments.load_environment, then runspython -m pytest src/tests.typescript-api— Node 24 typecheck and test job for the Express API underapp/api/.secrets-check— secret-scanning gate.deploy— per-environment deploy job used by thedev,staging, andprodworkflows. Staging and prod are guarded by CircleCI approval steps that map to the approval counts in the table above.
dev, staging, and prod workflows are branch-filtered; a pr-checks workflow runs the same gates on pull requests without deploying.
GitHub Actions — promote.yml
promote.yml is the only workflow under .github/workflows/. It advances the environment branch pointers; it does not deploy — the branch advance is what triggers the matching CircleCI deploy workflow.
Triggers
- Push to
dev. - Manual
workflow_dispatch(promotes the currentdevtip).
resolve— pins the exactdevcommit being promoted.docs-mirrorandtest— gates. Nothing promotes unless both are green.promote-staging— fast-forwardsstagingto the tested commit.promote-prod— fast-forwardsprodto the same commit.
promote.yml are SHA-pinned, and every job runs on a hosted GitHub runner (ubuntu-22.04 for the gates and staging promotion, ubuntu-latest for the final prod fast-forward). No self-hosted runner is required to promote.
One-time setup after merge
- Default branch. Settings → Branches: rename
main→prod, setprodas the default branch, and delete the oldmainref. - GitHub Environments. Settings → Environments: create
dev,staging, andprod. Attach required reviewers, wait timers, and per-environment secrets (cloud credentials belong here, not in repo-wide secrets). - Branch protection. Require CircleCI green and the approval counts above on
dev,staging, andprodbefore merging. These rules live under Settings → Rules → Rulesets asProtect dev,Protect staging, andProtect prod. - CircleCI project. Enable the repo in CircleCI so
.circleci/config.ymlruns on every push and PR. Add per-environment deploy credentials as CircleCI contexts, and wire the placeholderdeploystep in the config to your provider (AWS, Azure, GCP, etc.).
Secrets model
Secrets are scoped per GitHub Environment (for the promotion cascade) and per CircleCI context (for deploys), not per repo, so production credentials are unreachable fromdev or staging runs. A CircleCI job that targets the dev environment can only read dev context values, even if it runs from a branch with write access to the repo. Required approvals for staging and prod are configured on the CircleCI deploy workflow, so a promotion always passes through reviewer gates regardless of who pushed to dev.