Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# place to write secrets, to be filled by CI pipeline or user's machine
ALCHEMY_API_KEY=
ETHERSCAN_API_KEY=
BLOCKSCOUT_API_KEY=
PRIVATE_KEY=
LIQUIDATOR_PRIVATE_KEY=
6 changes: 6 additions & 0 deletions .github/infra/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ defs, separate wallets, separate logs. They only share the image so a
single `docker push` rolls both venues forward (each can still be
pinned to a different image tag).

The non-secret half of that `env:` block is not configured in Terraform
or in GitHub Variables. `deploy-col-mar-mm.yml` reads `config/dev.env`
or `config/prd.env` — the same files the market-maker loads locally —
and passes every key it finds to the task definition, alongside the
secrets it names explicitly. Add a public setting by editing that file.

## Files

* `ecs-task.tf` — reusable Terraform module template for one MM service.
Expand Down
119 changes: 53 additions & 66 deletions .github/workflows/deploy-col-mar-mm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ name: Deploy Collateral Margin Market Maker
#
# 1. Builds one Docker image from market-maker/ and pushes to GHCR
# 2. Renders a new task-def revision for the portfolio app (MAKER_APP=portfolio)
# with image + env vars + secrets sourced from GitHub Variables and
# Secrets (no AWS Secrets Manager)
# with image + public config from config/<env>.env + GitHub Secrets
# (no AWS Secrets Manager)
# 3. Calls ecs:UpdateService to point the service at the new revision
# and to scale it to the operator-chosen desired_count
#
# Container config (contract addresses, log level, oracle URL, …) is NOT
# declared here. It comes from config/dev.env and config/prd.env, the same
# files the market-maker loads locally, and every key in the chosen file is
# passed to the task definition. To add or change one, edit that file.
#
# GitHub Variables (configure per-environment under Settings -> Environments):
# MAKER_LOG_LEVEL default "info"
# MAKER_DRY_RUN default "false"
# MAKER_CANCEL_ORDERS_ON_SHUTDOWN default "true"
# MAKER_HEALTH_PORT default "3001" (must match TF cnt_port)
# MAKER_DESIRED_COUNT default "1" (set 0 to halt without redeploy)
# PERPS_ADDRESS HashPowerPerpsDEX contract address
# FUTURES_ADDRESS Futures contract address
# ETH_PRICE_FEED_ADDRESS optional Chainlink feed
# MAKER_DESIRED_COUNT default "1" (set 0 to halt without
# redeploy) — deploy orchestration, not
# container config
#
# GitHub Secrets (configure per-environment):
# AWS_ROLE_ARN_DEV / _STG / _LMN OIDC role ARNs from the TF output github_actions_role_arn
# AWS_ROLE_ARN_DEV / _LMN OIDC role ARNs from the TF output github_actions_role_arn
# ALCHEMY_API_KEY shared Alchemy project key (composes the RPC URL)
# FUTURES_MM_PRIVATE_KEY shared portfolio signer private key (both venues)
# SLACK_WEBHOOK_URL (org or repo level) for slack-notify
Expand All @@ -35,21 +35,20 @@ on:
push:
branches:
- dev
- stg
- main
- "cicd/**"
paths:
- "market-maker/**"
- "config/*.env"
- ".github/workflows/deploy-col-mar-mm.yml"
workflow_dispatch:
inputs:
environment:
description: "Target environment (dev=DEV, stg=STG, main=LMN/PROD)"
description: "Target environment (dev=DEV, main=LMN/PROD)"
required: true
type: choice
options:
- dev
- stg
- main

permissions:
Expand Down Expand Up @@ -105,25 +104,23 @@ jobs:

# URL_HOST_PREFIX matches the per-env Route53 alias produced by the
# collateral-margin TF stack (futures_mm_endpoint in 00_outputs.tf):
# dev/stg get a subdomain, prod uses the apex.
# dev gets a subdomain, prod uses the apex.
#
# MAKER_ENV also selects both the YAML profile baked into the image
# and the config/<name>.env read at deploy time.
case "$ENV" in
dev)
SUFFIX="dev"
MAKER_ENV="dev"
URL_HOST_PREFIX="dev."
;;
stg)
SUFFIX="stg"
MAKER_ENV="stg"
URL_HOST_PREFIX="stg."
;;
main)
SUFFIX="lmn"
MAKER_ENV="prd"
URL_HOST_PREFIX=""
;;
*)
echo "::error::Unknown environment '$ENV'"
echo "::error::Unknown environment '$ENV' (expected dev or main)"
exit 1
;;
esac
Expand Down Expand Up @@ -192,12 +189,17 @@ jobs:
environment: ${{ needs.build.outputs.environment }}

steps:
# Needed for config/<env>.env, which supplies the container environment.
- name: Checkout code
uses: actions/checkout@v5
with:
fetch-depth: 1

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ needs.build.outputs.environment == 'dev' &&
secrets.AWS_ROLE_ARN_DEV || needs.build.outputs.environment == 'stg'
&& secrets.AWS_ROLE_ARN_STG || secrets.AWS_ROLE_ARN_LMN }}
secrets.AWS_ROLE_ARN_DEV || secrets.AWS_ROLE_ARN_LMN }}
aws-region: ${{ needs.build.outputs.aws_region }}
role-session-name: GitHubActions-ColMarMM-portfolio-${{ github.run_id }}

Expand Down Expand Up @@ -234,18 +236,8 @@ jobs:
REGION: ${{ needs.build.outputs.aws_region }}
IMAGE: ${{ env.GHCR_IMAGE }}:${{ needs.build.outputs.version }}

# Per-environment GitHub Variables (non-secret personality)
MAKER_LOG_LEVEL: ${{ vars.MAKER_LOG_LEVEL }}
MAKER_DRY_RUN: ${{ vars.MAKER_DRY_RUN }}
MAKER_CANCEL_ORDERS_ON_SHUTDOWN: ${{ vars.MAKER_CANCEL_ORDERS_ON_SHUTDOWN }}
MAKER_HEALTH_PORT: ${{ vars.MAKER_HEALTH_PORT }}
# Deploy orchestration (not container config)
MAKER_DESIRED_COUNT: ${{ vars.MAKER_DESIRED_COUNT }}
ETH_PRICE_FEED_ADDRESS: ${{ vars.ETH_PRICE_FEED_ADDRESS }}
HASHPRICE_ORACLE_SUBGRAPH_URL: ${{ vars.HASHPRICE_ORACLE_SUBGRAPH_URL }}

# Both venue addresses — the portfolio app quotes perps + futures.
PERPS_ADDRESS: ${{ vars.PERPS_ADDRESS }}
FUTURES_ADDRESS: ${{ vars.FUTURES_ADDRESS }}

# Per-environment GitHub Secrets (encrypted personality). Secrets
# only flow into this step's process env; they are never written to
Expand All @@ -260,6 +252,7 @@ jobs:
COMMIT_HASH: ${{ github.sha }}
run: |
set -euo pipefail
CONFIG="config/${MAKER_ENV}.env"
DESIRED_COUNT="${MAKER_DESIRED_COUNT:-1}"

echo "🚀 Deploying portfolio to ${{ needs.build.outputs.environment }}"
Expand All @@ -268,6 +261,7 @@ jobs:
echo " Task Family: ${TASK_FAMILY}"
echo " Image: ${IMAGE}"
echo " MAKER_ENV: ${MAKER_ENV}"
echo " Config: ${CONFIG}"
echo " Desired Count: ${DESIRED_COUNT}"

# Fetch current task def, strip non-registerable metadata, swap image
Expand All @@ -286,36 +280,30 @@ jobs:

# Build the full env block from scratch. Personality lives ONLY here;
# the Terraform stub deliberately ships zero env vars.
echo "🔧 Injecting environment from GitHub Variables / Secrets..."
jq -n \
--arg MAKER_APP "${MAKER_APP}" \
--arg MAKER_ENV "${MAKER_ENV}" \
--arg MAKER_HEALTH_PORT "${MAKER_HEALTH_PORT:-3001}" \
--arg MAKER_LOG_LEVEL "${MAKER_LOG_LEVEL:-info}" \
--arg MAKER_DRY_RUN "${MAKER_DRY_RUN:-false}" \
--arg MAKER_CANCEL_ORDERS_ON_SHUTDOWN "${MAKER_CANCEL_ORDERS_ON_SHUTDOWN:-true}" \
--arg COMMIT_HASH "${COMMIT_HASH}" \
--arg ETH_PRICE_FEED_ADDRESS "${ETH_PRICE_FEED_ADDRESS:-}" \
--arg ALCHEMY_API_KEY "${ALCHEMY_API_KEY}" \
--arg PRIVATE_KEY "${PRIVATE_KEY}" \
--arg PERPS_ADDRESS "${PERPS_ADDRESS}" \
--arg FUTURES_ADDRESS "${FUTURES_ADDRESS}" \
--arg HASHPRICE_ORACLE_SUBGRAPH_URL "${HASHPRICE_ORACLE_SUBGRAPH_URL}" \
'[
{name:"MAKER_APP", value:$MAKER_APP},
{name:"MAKER_ENV", value:$MAKER_ENV},
{name:"MAKER_HEALTH_PORT", value:$MAKER_HEALTH_PORT},
{name:"MAKER_LOG_LEVEL", value:$MAKER_LOG_LEVEL},
{name:"MAKER_DRY_RUN", value:$MAKER_DRY_RUN},
{name:"MAKER_CANCEL_ORDERS_ON_SHUTDOWN", value:$MAKER_CANCEL_ORDERS_ON_SHUTDOWN},
{name:"COMMIT_HASH", value:$COMMIT_HASH},
{name:"ETH_PRICE_FEED_ADDRESS", value:$ETH_PRICE_FEED_ADDRESS},
{name:"ALCHEMY_API_KEY", value:$ALCHEMY_API_KEY},
{name:"PRIVATE_KEY", value:$PRIVATE_KEY},
{name:"PERPS_ADDRESS", value:$PERPS_ADDRESS},
{name:"FUTURES_ADDRESS", value:$FUTURES_ADDRESS},
{name:"HASHPRICE_ORACLE_SUBGRAPH_URL", value:$HASHPRICE_ORACLE_SUBGRAPH_URL}
]' > env-block.json
#
# The block is every key declared in config/<env>.env plus the
# secrets and computed values from the step env above. Sourcing
# cannot clobber a secret, because no secret is named in the config
# file. jq then reads values straight out of the environment, so each
# key is written exactly once: in the config file.
echo "🔧 Injecting environment from ${CONFIG} + GitHub Secrets..."
set -a && . "$CONFIG" && set +a

KEYS=$( { sed -n 's/^[[:space:]]*\([A-Za-z_][A-Za-z0-9_]*\)=.*/\1/p' "$CONFIG"; \
printf '%s\n' MAKER_APP MAKER_ENV COMMIT_HASH ALCHEMY_API_KEY PRIVATE_KEY; } )

# Empty values are dropped so the maker applies its own defaults
# instead of parsing an empty string.
jq -n --arg keys "$KEYS" '[
$keys
| split("\n")
| unique
| .[]
| select(. != "" and ($ENV[.] // "") != "")
| {name: ., value: $ENV[.]}
]' > env-block.json

echo " Container env keys: $(jq -r '[.[].name] | join(", ")' env-block.json)"

# Replace the entire env block; also drop any stale `secrets` block
# (we no longer use Secrets Manager).
Expand Down Expand Up @@ -356,8 +344,7 @@ jobs:
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ needs.build.outputs.environment == 'dev' &&
secrets.AWS_ROLE_ARN_DEV || needs.build.outputs.environment == 'stg'
&& secrets.AWS_ROLE_ARN_STG || secrets.AWS_ROLE_ARN_LMN }}
secrets.AWS_ROLE_ARN_DEV || secrets.AWS_ROLE_ARN_LMN }}
aws-region: ${{ needs.build.outputs.aws_region }}
role-session-name: GitHubActions-ColMarMM-portfolio-Verify-${{ github.run_id }}

Expand Down
Loading
Loading