main updates the production deployment. Every other branch gets an isolated deployment to test against real platform compute, and deleting a branch tears its deployment down. Anaconda uses this pattern for the flow behind its package request triage intake agent, which assesses whether an open-source package is safe to add to the main channel.
By the end of this tutorial, you will have:
- A flow that deploys to production from
mainand to an isolated branch deployment from every other branch - A GitHub Actions workflow that deploys on push and on pull request
- A GitHub Actions workflow that tears down a branch deployment when its branch is deleted
Prerequisites
- A flow you are ready to deploy to Anaconda Platform. The flow must use the
@projectdecorator. For background, see What is a project? - A GitHub repository with Actions enabled.
- A machine user that’s connected to your github repo, or admin access to create one. For details, see Programmatic access with machine users.
- The domain of your Anaconda Platform deployment and the name of the perimeter that CI deploys into.
Decide how your branches map to Workflows
How you choose to map branches to Workflows depends on your branching strategy, constraints, and goals. This topic guides you through one method, with the reasoning explained below. If your constraints and goals differ, your GitHub Actions will likely need to look different from the ones shown here.Name the project and scope the trigger
Give the flow a project name with@project. If the flow responds to platform events, arm its trigger only in production. Branch deployments share the project, so a trigger armed on every branch would make each branch consume production events.
python flow.py argo-workflows create. The @project decorator namespaces every deployment under the project name, and the flag you pass before argo-workflows create determines the deployment’s name and type:
Only the --production deployment arms @trigger, so the new-triage-ticket event has exactly one consumer.
Create a machine user for CI
CI authenticates as a machine user rather than a person. Create one scoped to the repository and to the perimeter CI deploys into.1
Create the machine user
- Select Users in the left-hand navigation, then select the Machines tab.
- Click Create New.
- Select GitHub Actions, then enter a Name and Description.
- In Attributes to validate, set Organization and Repository to the repository that runs the workflow. Leave Branch empty so jobs on every branch can authenticate.
- Click Submit.
Setting Branch to
main scopes the machine user to that branch, so jobs on other branches fail to authenticate.2
Grant privileges in the target perimeter
Open the machine user and grant it the Execute privilege in the perimeter CI deploys into. Privileges are per perimeter, so a machine user with privileges only in the
default perimeter cannot deploy into another perimeter. For details, see Roles and privileges.Deploy on every push
Create.github/workflows/deploy.yml. This workflow deploys on every push to main, on every pull request, and on demand. It deploys to production when the ref is main, and to an isolated branch deployment otherwise.
.github/workflows/deploy.yml
The
<MACHINE_USER_NAME>, <DEPLOYMENT_DOMAIN>, and <PERIMETER> values must match the values you used in the deploy workflow.- Triggers: pushes to
main, pull requests, and manual runs. Because onlybranchesis defined underpush, tag pushes do not trigger a deploy. - Concurrency:
cancel-in-progress: truekeeps one deploy per ref running, so a fast series of pushes does not race. The most recent push wins and the earlier one is canceled. - Permissions:
id-token: writelets the job request GitHub’s OIDC token, andcontents: readlets it check out the repository. - Fork guard: the
ifcondition skips pull requests from forks. A fork’s OIDC token does not carry the repository claims the machine user validates, so those jobs cannot authenticate. - Ref to target: the job resolves the branch from
GITHUB_REF_NAME, or fromGITHUB_HEAD_REFfor pull requests, and deploys with--productionformainand--branchfor everything else. - Branch normalization: GitHub branch names can contain
/and uppercase characters, which are not valid in a Metaflow branch name. The workflow lowercases the ref and replaces every character outsidea-z,0-9, and_with_, soFeature/Add-Scoringbecomesfeature_add_scoring. Use the same normalization in the teardown workflow so both operate on the same branch name. - Authentication:
service-principal-configure --github-actionsreads GitHub’s OIDC token from the job environment and exchanges it for platform credentials. No long-lived platform credentials are stored in the repository.
argo-workflows create:
--environment=fast-bakerybuilds a reproducible container from the flow’s declared dependencies.--package-suffixes=.py,.yaml,.yml,.jsonpackages non-Python files the flow reads at runtime, such as a config file.--productionor--branchselects the deployment target.
--environment, --package-suffixes, and --production or --branch are top-level Metaflow options. They must come before argo-workflows create.Tear down a branch deployment
Create.github/workflows/teardown-branch.yml. This workflow runs when a branch is deleted and removes that branch’s deployment.
.github/workflows/teardown-branch.yml
- Trigger: the
deleteevent, which fires for branch and tag deletions. Thedeleteevent does not support branch filters, so the job guards against tearing down production itself. Theifcondition runs the job only for branch deletions other thanmain, so tag deletions andmainare skipped. - Ref to branch: the
deleteevent reports the branch name ingithub.event.ref, such asfeature/add-scoring. The workflow strips arefs/heads/prefix defensively and applies the same normalization as the deploy workflow. - Safety check: the
casestatement is a second guard that refuses to run for an empty branch name,main, ormaster. - Teardown:
flowproject teardown-branch --id "<PROJECT_NAME>/<BRANCH>" --yesremoves the branch deployment. The--yesflag skips the confirmation prompt, which CI cannot answer.
--dry-run:
Verify the loop
Confirm each path of the loop works:- Push a commit to a new branch. When the deploy workflow finishes, open the Workflows view. A
package_triage.test.<BRANCH>.TriageFlowdeployment appears. - Merge the branch to
main. The deploy workflow updatespackage_triage.prod.TriageFlow. - Delete the branch. The teardown workflow runs, and the branch deployment disappears from the Workflows view.
Next steps
To build on this tutorial:- Add success and failure notifications. For details, see Setting up notifications for deployments.
- Learn how to promote assets from a feature branch before teardown. For details, see Project lifecycle.
- Adapt the authentication pattern to other CI/CD systems. For details, see CI/CD integration.