Skip to main content
Artemis City ships with three long-lived environment branches that map 1:1 to GitHub Environments and deploy targets. Use them to separate integration work from pre-production rehearsals and production traffic, and to keep production credentials unreachable from lower environments. The names 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.
If you only run Artemis City locally, you can ignore the branch and workflow setup and just set ARTEMIS_ENV=dev (the default) when loading config.

Branches and approvals

Promotion flow:
Feature branches always target 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 under config/environments/:
  • config/environments/dev.yaml
  • config/environments/staging.yaml
  • config/environments/prod.yaml
A dev.yaml looks like this:
Keep the same top-level keys across all three files so the loader can return a consistent shape. Secrets do not belong in these YAML files — they live in per-environment GitHub Environment secrets.

Selecting the active environment

The active environment is selected by the ARTEMIS_ENV variable. Valid values are dev, staging, and prod; unset defaults to dev.
Load the matching config in code:
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 if CLAUDE.md and AGENTS.md have drifted from being byte-for-byte identical.
  • python-checks — matrix over Python 3.11, 3.12, and 3.13. Installs requirements.txt + requirements-dev.txt, validates every config/environments/*.yaml with src.utils.environments.load_environment, then runs python -m pytest src/tests.
  • typescript-api — Node 24 typecheck and test job for the Express API under app/api/.
  • secrets-check — secret-scanning gate.
  • deploy — per-environment deploy job used by the dev, staging, and prod workflows. Staging and prod are guarded by CircleCI approval steps that map to the approval counts in the table above.
The 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 current dev tip).
Jobs (sequential)
  1. resolve — pins the exact dev commit being promoted.
  2. docs-mirror and test — gates. Nothing promotes unless both are green.
  3. promote-staging — fast-forwards staging to the tested commit.
  4. promote-prod — fast-forwards prod to the same commit.
All actions in 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

  1. Default branch. Settings → Branches: rename mainprod, set prod as the default branch, and delete the old main ref.
  2. GitHub Environments. Settings → Environments: create dev, staging, and prod. Attach required reviewers, wait timers, and per-environment secrets (cloud credentials belong here, not in repo-wide secrets).
  3. Branch protection. Require CircleCI green and the approval counts above on dev, staging, and prod before merging. These rules live under Settings → Rules → Rulesets as Protect dev, Protect staging, and Protect prod.
  4. CircleCI project. Enable the repo in CircleCI so .circleci/config.yml runs on every push and PR. Add per-environment deploy credentials as CircleCI contexts, and wire the placeholder deploy step 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 from dev 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.
Last modified on July 15, 2026