Organization-level GitHub configuration for Amera, including PR templates, reusable workflows, and Dependabot automation.
Automated vulnerability lifecycle management across all org repos, combining an AWS Lambda webhook handler for real-time event handling with GitHub Actions workflows for infrastructure maintenance.
Overview
graph TD
subgraph webhook ["Org Webhook → AWS Lambda"]
GH["GitHub Events"] --> Lambda["dependabot-webhook\n(AWS Lambda)"]
Lambda -->|"dependabot_alert.created"| AlertHandler[Alert Handler]
Lambda -->|"pull_request.opened"| PRHandler[PR Handler]
Lambda -->|"pull_request.closed+merged"| MergeHandler[Merge Handler]
end
subgraph actions ["Slack + Linear + GitHub API"]
AlertHandler -->|"post"| Slack["Slack\n(GHSA-keyed thread)"]
AlertHandler -->|"create ticket"| Linear["Linear\n(GHSA in title)"]
PRHandler -->|"reply in thread"| Slack
PRHandler -->|"comment on ticket"| Linear
PRHandler -->|"auto-merge / label"| GitHubAPI[GitHub API]
MergeHandler -->|"reply: resolved"| Slack
end
subgraph infra [Infrastructure Workflows]
Refresh["refresh_codeartifact_token\n(every 10h)"] -->|"rotates"| CASecret["Org Dependabot secret:\nCA_TOKEN"]
Sync["sync_dependabot_python\n(daily)"] -->|"opens PRs"| DYml["dependabot.yml\n(per repo)"]
end
Vulnerability lifecycle (detailed)
graph TD
subgraph alert_phase [1 - Alert Created]
A1["dependabot_alert webhook"] --> A2["Post Slack message\n(includes GHSA ID)"]
A1 --> A3["Create Linear ticket\n(GHSA ID in title)"]
end
subgraph pr_opened [2 - PR Opened]
B1["pull_request webhook\n(sender: dependabot)"] --> B2["Look up GHSA ID\n(via Dependabot alerts API)"]
B2 --> B3["Find Slack thread by GHSA ID"]
B3 --> B4[Reply in thread]
B2 --> B5[Find Linear ticket by GHSA ID]
B5 --> B6[Comment on ticket]
B5 --> B7["Inject 'Fixes AMR-123'\ninto PR body"]
B1 --> B8{"Update type?"}
B8 -->|"patch/minor"| B9[Enable auto-merge]
B8 -->|"major"| B10["Add 'major-update' label"]
end
subgraph pr_merged [3 - PR Merged]
C1["pull_request webhook\n(closed+merged)"] --> C2[Reply in Slack thread: resolved]
C1 --> C3["Linear auto-closes ticket\n(via PR body keyword)"]
end
alert_phase --> pr_opened
pr_opened --> pr_merged
GitHub App (AMERABOT) — used by the Lambda for GitHub API calls (auto-merge, labels, alert lookup, PR edits) and by workflows for elevated permissions.
- Create a GitHub App in the
amera-appsorg with these permissions:- Dependabot alerts: Read-only
- Organization Dependabot secrets: Read and write (for
refresh_codeartifact_token) - Contents: Read and write (for
sync_dependabot_python) - Pull requests: Read and write (for
sync_dependabot_pythonand the Lambda)
- Install it on all repos
- Note the installation ID from
https://github.com/organizations/amera-apps/settings/installations
Org webhook — delivers dependabot_alert and pull_request events to the Lambda.
- Go to org Settings → Webhooks → Add webhook
- Payload URL: the Lambda's function URL or API Gateway endpoint
- Content type:
application/json - Secret: a strong random string (same value stored as
GITHUB_WEBHOOK_SECRETin the Lambda) - Events: select Dependabot alerts and Pull requests
Slack bot scopes — chat:write plus channels:history (public) or groups:history (private) for thread lookup.
Org secrets (for GitHub Actions workflows only):
| Secret | Description |
|---|---|
AMERABOT_APP_ID |
GitHub App ID |
AMERABOT_APP_PRIVATE_KEY |
GitHub App private key |
AWS_ACCESS_KEY_ID |
IAM user for CodeArtifact token generation |
AWS_SECRET_ACCESS_KEY |
IAM user for CodeArtifact token generation |
The AWS IAM user should have minimal permissions: codeartifact:GetAuthorizationToken and sts:GetServiceLinkedRoleDeletionStatus.
Org variables (for GitHub Actions workflows only):
| Variable | Description |
|---|---|
SLACK_DEPENDABOT_ALERTS_CHANNEL_ID |
Slack channel (used by sync_dependabot_python) |
LINEAR_AMERA_TEAM_ID |
Linear team (used by sync_dependabot_python) |
LINEAR_DEPENDABOT_ALERTS_PROJECT_ID |
Linear project (used by sync_dependabot_python) |
LINEAR_TRIAGE_STATE_ID |
Linear "Triage" workflow state — tickets land here for immediate visibility |
AWS_REGION |
AWS region for CodeArtifact (us-east-1) |
AWS_OWNER_ID |
AWS account ID / domain owner (371568547021) |
The webhook handler is deployed as an AWS Lambda. Source, configuration, and deployment instructions live in infra/aws/lambda/dependabot/.
.github/workflows/refresh_codeartifact_token.yml
Dependabot needs access to the private CodeArtifact registry to resolve packages like amera-core and amera-workflow. CodeArtifact tokens expire after 12 hours, so this workflow rotates the token every 10 hours and stores it as an org-level Dependabot secret (CA_TOKEN).
graph LR
Cron["Schedule\n(every 10h)"] --> WF[refresh_codeartifact_token]
WF -->|"AWS creds"| CA[CodeArtifact]
CA -->|"12h token"| WF
WF -->|"gh secret set"| Secret["Org Dependabot secret:\nCA_TOKEN"]
Secret -->|"read by"| DB["Dependabot\n(all repos)"]
Runs on the aws self-hosted runner group (AWS CLI is pre-installed). Uses gh secret set --org --app dependabot to update the secret without manual encryption.
The workflow also supports workflow_dispatch for manual runs if a token needs immediate rotation.
.github/workflows/sync_dependabot_python.yml
Dependabot requires a .github/dependabot.yml in each repo — there's no way to inherit it at the org level. This workflow maintains a single template (.github/dependabot-python-template.yml) and syncs it to all Python repos (any repo with a pyproject.toml in the root).
graph TD
Cron["Schedule\n(daily 11:00 UTC)"] --> Sync[sync_dependabot_python]
Sync -->|"reads"| Template["dependabot-python-template.yml\n(this repo)"]
Sync -->|"for each repo"| Check{"Has pyproject.toml?"}
Check -->|"yes + out of date"| PR["Open PR:\nchore/sync-dependabot-config"]
Check -->|"no or up-to-date"| Skip[Skip]
PR --> Slack["Slack summary"]
PR --> Linear["Linear ticket per repo\n(Triage)"]
How it works:
- Lists all repos in the org
- For each non-archived repo, checks if
pyproject.tomlexists - Compares the repo's
.github/dependabot.ymlto the template — skips if already matching - Skips if an open sync PR already exists from a previous run
- Creates a branch, commits the template, and opens a PR
- After processing all repos, posts a Slack summary and creates one Linear ticket per repo (in Triage) for each PR opened
PRs are opened (not direct pushes) to comply with branch protection rules requiring at least one approving review.
Some repos may need a custom dependabot.yml or should be excluded entirely. Add them to the skipRepos array at the top of the actions/github-script block in sync_dependabot_python.yml:
const skipRepos = ['some-special-repo', 'another-exception']Skipped repos appear in the workflow run log for auditability.
To change the Dependabot config across all repos:
- Edit
.github/dependabot-python-template.ymlin this repo - Merge to
main - Wait for the next scheduled sync or trigger manually via
workflow_dispatch - Review and merge the PRs opened in each repo