Deployment
Pipeline overview
AWS CodePipeline (trigger)
└─► AWS CodeBuild (buildspec.yml)
install : Python 3.9, remove .python-version
pre_build : settings_setup.py → pip install → config_setup.py
→ openssl (prod only) → pytest → ECR login
build : docker-compose build → tag images → write deploy artifacts
post_build : docker push both images to ECR
└─► AWS CodeDeploy (ECS)
register the new task definition and roll out via appspec.yaml
Environments and accounts
Account selection happens in buildspec.yml from ENV_TYPE:
if [ "$ENV_TYPE" = "dev" ]; then
export AWS_ACCOUNT=605668041275
export ROOT_PATH=/automatic-uw-tool-web-api
else
export AWS_ACCOUNT=625663093496
export ROOT_PATH=/automatic-uw-tool-web-api
fi
export SWF_IMAGE_URI=${AWS_ACCOUNT}.dkr.ecr.us-east-1.amazonaws.com/automatic-uw-tool-swf-worker
export API_IMAGE_URI=${AWS_ACCOUNT}.dkr.ecr.us-east-1.amazonaws.com/automatic-uw-tool-web-api
export APP_NAMES=${APP_NAMES:-"automatic-uw-tool-swf-worker automatic-uw-tool-web-api"}
APP_NAMES defaults to both apps. Each token must contain swf or api (that substring is what
the build phase matches on) and doubles as the imagedefinitions artifact filename.
Environment-to-resource mapping: Configuration & secrets.
CodeBuild phases
Runtime: Python 3.9. GIT_TOKEN comes from the Secrets Manager entry
aws-codebuild-token-eva and is needed to install the private pyb-sdk.
install
Removes .python-version. uv commits a bare 3.9 pin, but pyenv (which backs CodeBuild’s
runtime-versions) needs an exact patch version and lets that file win — newer CodeBuild images
otherwise fail with pyenv: version workflows recreate the
file.3.9' is not installed. Local `uv
pre_build
-
python settings_setup.py→config/settings.json. -
pip install -r requirements.txtwithPIP_CONSTRAINTpinningsetuptools<81+wheel, sopyb-sdk’s `pkg_resourcesimport keeps working during build isolation. -
export ENV_FOR_DYNACONF=$ENV_NAME. -
python config_setup.py $CODEBUILD_INITIATOR→config/.secrets.jsonand the TR material. -
Production only — decode the base64 PFX and extract the PEMs:
PFX_PASSWORD=$(cat config/thomson_reuters/pfx_password.txt) cat config/thomson_reuters/file.pfx.base64 | base64 --decode > config/thomson_reuters/file.pfx openssl pkcs12 -in config/thomson_reuters/file.pfx -nocerts -out config/thomson_reuters/key_pass.pem -passin pass:$PFX_PASSWORD -passout pass:$PFX_PASSWORD openssl pkcs12 -in config/thomson_reuters/file.pfx -clcerts -nokeys -out config/thomson_reuters/cert.pem -passin pass:$PFX_PASSWORD openssl rsa -in config/thomson_reuters/key_pass.pem -out config/thomson_reuters/key.pem -passin pass:$PFX_PASSWORDIn dev,
config_setup.pyhas already writtencert.pemandkey.pemdirectly. -
python -m pytest— unit tests and coverage. The build fails if coverage drops below 80% (pyproject.toml). -
aws ecr get-login-password | docker loginfor the target account.
build
docker-compose -f deployment/docker-compose.yml build, then per app in APP_NAMES:
| App | Tag and artifact |
|---|---|
|
Tag |
|
Tag |
|
Tag |
The whole block is skipped when ENV_NAME is unset.
Docker images
Both images start from public.ecr.aws/docker/library/python:3.9-slim, install git (for the
git-based private dependencies), copy the repository, and pip install -r requirements.txt.
| Image | Dockerfile | Workdir | CMD |
|---|---|---|---|
Web API |
|
|
|
SWF worker |
|
|
|
Build args wired by deployment/docker-compose.yml: ENV_FOR_DYNACONF (from ENV_NAME),
AWS_ACCOUNT, GIT_TOKEN, and ROOT_PATH (Web API only).
|
The Web API image runs uvicorn with Note also that the generated |
ECS artifacts
artifacts/taskdef.json — Fargate task definition (Web API)
Family automatic-uw-tool, one container named api on port 80, 512 CPU / 2048 MB, awslogs to
/ecs/automatic-uw-tool in us-east-1. <IMAGE1_NAME> and <TASK_DEFINITION> are substituted
at deploy time.
"family": "automatic-uw-tool",
"taskRoleArn": "arn:aws:iam::625663093496:role/automatic-uw-tool-role",
"executionRoleArn": "arn:aws:iam::625663093496:role/ecsTaskExecutionRole",
"networkMode": "awsvpc",
"containerDefinitions": [
{
"name": "api",
"image": "<IMAGE1_NAME>",
artifacts/appspec.yaml — CodeDeploy ECS spec
Blue/green deployment of the ECS service, routing the ALB to container api port 80.
{
"version": 1,
"Resources": [
{
"TargetService": {
"Type": "AWS::ECS::Service",
"Properties": {
"TaskDefinition": "<TASK_DEFINITION>",
"LoadBalancerInfo": { "ContainerName": "api", "ContainerPort": 80 }
}
}
}
]
}
|
|
Building locally
export ENV_NAME=<env> AWS_ACCOUNT=<acct> GIT_TOKEN=<pat> \
ROOT_PATH=/automatic-uw-tool-web-api NAME_TAG=automatic-uw-tool
export ENV_FOR_DYNACONF=$ENV_NAME
python settings_setup.py
python config_setup.py # needs AWS credentials
docker-compose -f deployment/docker-compose.yml build
Or make docker-build — see Local development.
Runtime dependencies
requirements.txt (Python 3.9): fastapi==0.110.0, uvicorn, boto3==1.33.2,
dynaconf==3.1.8, requests==2.31.0, xmltodict, PyJWT, sentry-sdk, thefuzz (name
matching), arrow, coolname (worker thread names), werkzeug (HTTP status phrases),
hubspot-api-client, plus the private pyb-sdk and eventlogger installed from GitHub with
GIT_TOKEN.
Rolling back
Images are tagged by environment (:release, :<env>), not by commit, so "roll back" means
redeploying a previous task definition revision in ECS/CodeDeploy rather than re-pulling an
older tag. Keep that in mind before triggering a rebuild on a bad commit — the rebuild
overwrites the tag.