Skip to content

Running deploy in CI

cordless deploy was built for interactive use first, but nothing about it actually depends on a terminal. There’s no config to unlock, no separate “CI mode”, just three things lining up: AWS credentials, your Discord credentials, and no confirmation prompts blocking the run.

deploy itself never prompts for anything, there’s no input() call anywhere in it, so it was already safe to run non-interactively. The only commands that ask anything are cordless init (one required question about endpoint, skip it with --endpoint function_url or --endpoint api_gateway) and cordless destroy (pass --yes to skip its confirmation).

cordless doesn’t implement its own credential handling: deploy/destroy construct a plain boto3.Session(), so they pick up whatever boto3’s standard credential chain finds: environment variables, a shared credentials file, an IAM role attached to the runner, or short-lived credentials from OIDC federation. In GitHub Actions, that means aws-actions/configure-aws-credentials already works with zero cordless-specific setup: it assumes a role via OIDC and exports AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY/AWS_SESSION_TOKEN as environment variables, which cordless deploy then just inherits:

permissions:
id-token: write # required for OIDC
contents: read
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/cordless-deploy
aws-region: us-east-1
- run: pip install cordless
- run: cordless deploy --register

No long-lived access keys stored as repository secrets, and no cordless flag needed to opt into it. See the IAM permissions section for exactly what to scope that role down to.

DISCORD_PUBLIC_KEY and DISCORD_BOT_TOKEN fall back to the process environment when they aren’t set some other way, so exporting them as CI secrets is enough, no .env file needed (and you shouldn’t commit one, that would mean checking in secrets):

- run: cordless deploy --register
env:
DISCORD_PUBLIC_KEY: ${{ secrets.DISCORD_PUBLIC_KEY }}
DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }}

This fallback is lowest priority: a .env file, cordless.toml’s [deploy.env], or an explicit --env KEY=VALUE flag all still win if present, same as they do locally. It only fills in what nothing else already set, so it’s safe to leave real environment variables exported in a shell without them silently overriding a project’s own .env during local development.

Anything else your bot needs baked into its Lambda environment (a STRIPE_KEY, a bucket name, whatever) isn’t covered by this fallback, those go through [deploy.env] in cordless.toml or repeated --env KEY=VALUE flags, same as they do outside CI.

name: Deploy bot
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/cordless-deploy
aws-region: us-east-1
- run: pip install cordless
- run: cordless deploy --register
env:
DISCORD_PUBLIC_KEY: ${{ secrets.DISCORD_PUBLIC_KEY }}
DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }}

deploy is safe to run on every push: creating/updating an existing function is idempotent, and a run that fails partway through leaves already-created resources in place rather than a half-torn-down state. Rerunning it picks up wherever it stopped instead of erroring on things that already exist.