Skip to main content

Native Pull Requests for Vendored Component Updates

· 4 min read
Erik Osterman
Founder @ Cloud Posse

Keeping vendored components current across dozens of repositories doesn't scale as a manual habit. Someone has to notice a new upstream release, edit the right version: field without breaking a comment or a template, then commit, push, and open a pull request. That has to happen for every component, on some kind of schedule, forever. Most teams either let it slip until something forces an update, or bolt on a third-party GitHub Action just to automate the commit-and-PR part.

The Problem

The atmos vendor update command already finds and writes newer versions locally. Turning that into a reviewable pull request meant scripting Git branch/commit/push logic and a GitHub API client yourself, or reaching for an external action with its own permissions model, its own config format, and its own release cadence to track.

The Fix

The --pull-request flag does the whole cycle natively: discover available updates, write them with the same format-preserving editor vendor update already uses, create or reuse a branch, commit, push, and open or update a pull request — through a provider-neutral Git registry (GitHub today; GitLab and Bitbucket can register without any command changes).

atmos vendor update --pull-request

Nothing happens unless there's actually an update: no updates means no branch, no commit, no push, no PR. Atmos fetches the base branch but never writes to it. It reuses an existing feature branch for the same scope and pushes it fast-forward only — repeated runs update the same PR instead of piling up duplicates.

How to Use It

Scope updates to a named group instead of updating everything at once:

vendor:
update:
groups:
platform:
include: ["terraform/vpc", "terraform/eks/*"]
exclude: ["terraform/eks/legacy"]
ci:
pull_request:
branch_prefix: atmos/component-updater
title: "chore(components): update {{ .scope.name }}"
labels: [component-update]
atmos vendor update --group platform --pull-request

For a scheduled run, the official container image is all a workflow needs — no third-party action performs the update, commit, push, or PR creation:

on:
schedule: [{ cron: "17 3 * * 1" }]
permissions:
contents: write
pull-requests: write
jobs:
update:
runs-on: ubuntu-latest
container:
image: ghcr.io/cloudposse/atmos:${{ vars.ATMOS_VERSION }}
steps:
- uses: actions/checkout@v6
- run: atmos vendor update --pull-request
env: { GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} }

Set execution.mode: worktree when you'd rather the whole discover-branch-commit-push cycle ran in an isolated checkout instead of the workflow's own working tree — useful if other steps in the same job depend on an unmodified checkout while the update runs. In GitHub Actions, every run also appends a summary showing scope, counts, and the resulting PR link, independent of whether an update was found.

Triggering Downstream Workflows

The example above uses the default GITHUB_TOKEN. This token is fine for opening the PR, but GitHub deliberately excludes its own default Actions token from re-triggering on: pull_request/ on: push workflows. As a result, a plan/validate workflow that's supposed to run against the new PR won't fire.

Pair the Component Updater with the github/sts auth integration to get a token that does trigger downstream Actions — a just-in-time GitHub App installation token, minted through Atmos Pro STS and exported as ATMOS_PRO_GITHUB_TOKEN. The --pull-request flag already prefers this token over GITHUB_TOKEN:

atmos auth exec --identity github-sts -- atmos vendor update --pull-request

No other flags or config changes are required. atmos auth exec mints and exports the token for the wrapped command's environment. The Component Updater's GitHub client picks it up automatically.

Get Involved

Questions about scopes, groups, or CI publishing are welcome in the Atmos GitHub repository and the community Slack.