Composite Actions
Making up the reusable workflows (notify,
bump-images, and
report-merged), odp-releaser ships four
composite GitHub Actions for deploy repos that need more control than
bump-images.yml offers — most commonly to run extra steps after the bump
(e.g. syncing the freshly published image to another registry that isn't natively reported) before
anything is committed, report deployments and pull request comments back
to source repos from a custom workflow, or triggering additional deployment steps.
The actions live in this repo and are referenced with the standard
owner/repo/path@ref syntax:
uses: gulfofmaine/odp-releaser/.github/actions/install@<sha-or-tag>
uses: gulfofmaine/odp-releaser/.github/actions/bump_images@<sha-or-tag>
uses: gulfofmaine/odp-releaser/.github/actions/report_deployment@<sha-or-tag>
uses: gulfofmaine/odp-releaser/.github/actions/comment_on_pr@<sha-or-tag>
bump_images, report_deployment and comment_on_pr each install the
odp-releaser CLI, via the sibling install action at their own ref (GitHub's
self-repository syntax,
uses: $/.github/actions/install).
install
Installs the odp-releaser CLI with uv. The
CLI is installed from the action's own repository files, so the CLI version
always matches the action ref — pinning the uses: reference is enough to
pin the CLI too.
Optional — the other three call it themselves. Reach for it when a job runs the
CLI in its own run: steps, or to control install_uv or the cache key. It is
a no-op when the CLI is already on the PATH, so first install wins: pin it and
its siblings to the same ref.
- name: Install ODP Releaser
uses: gulfofmaine/odp-releaser/.github/actions/install@<sha-or-tag>
# with:
# install_uv: "false" # if the job already provides uv on the PATH
Install ODP Releaser
- uses: gulfofmaine/odp-releaser/.github/actions/install@<sha-or-tag>
Install uv (optional) and the odp-releaser CLI from this action's repository.
Inputs: ¤
| Name | Description | Default |
|---|---|---|
install_uv
¤
|
Whether to install uv with astral-sh/setup-uv. Set to "false" when the job already provides uv on the PATH. |
true
|
cache_suffix
¤
|
Suffix for setup-uv's cache key, used to keep the uv cache keyed to the odp-releaser version being installed. Defaults to the ref this action was referenced at. Only used when install_uv is "true". |
odp-releaser-${{ github.action_ref }}
|
Source of gulfofmaine/odp-releaser/.github/actions/install@<sha-or-tag>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | |
bump_images
Runs odp-releaser bump-images against the repository_dispatch
client_payload and the checked-out deploy repo's image manifest config,
then — depending on the image's update_mode — commits the change directly
or opens a pull request, exactly like bump-images.yml.
Prerequisites:
- The deploy repo is checked out, with credentials that can push (unless
stage_onlyis"true"). - Only when the image manifest asks for a sync (
deployed_aswithsync: true):skopeoon the PATH (preinstalled on GitHub-hosted ubuntu runners), and the destination registry already logged in to by an earlier step (docker/login-action, orconfigure-aws-credentials+amazon-ecr-login). skopeo reads those credentials from$HOME/.docker/config.jsonvia the containers credential search order, so no separateskopeo loginstep is needed. A caller that configures a Docker credential helper instead is the exception, the credentials are then not in that file, and skopeo won't resolve them. See Syncing images for what the sync does and when to ask for one.
stage_only: bump without committing
Set stage_only: "true" to write the manifest changes and git add them
without making a commit or opening a pull request (the image's
update_mode is ignored). Your workflow then owns the follow-up: add
whatever steps you need — the action's outputs carry the image name and
digest — and commit the staged changes yourself.
on:
repository_dispatch:
types: [image-published]
jobs:
bump:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@<sha> # v7
- name: Bump images
id: bump
uses: gulfofmaine/odp-releaser/.github/actions/bump_images@<sha-or-tag>
with:
stage_only: "true"
- name: Sync image to the deploy registry
if: steps.bump.outputs.changed == 'true'
env:
IMAGE_NAME: ${{ steps.bump.outputs.image_name }}
DIGEST: ${{ steps.bump.outputs.digest }}
NEW_TAG: ${{ steps.bump.outputs.new_tag }}
run: |
# The destination needs the tag the bump just wrote. Without it the
# copy lands as `:latest` while the manifest points at `newTag:
# <tag>`, so the deploy reads a tag nothing ever pushed.
crane copy "$IMAGE_NAME@$DIGEST" \
"registry.example.com/${IMAGE_NAME#*/}:$NEW_TAG"
- name: Commit bump
if: steps.bump.outputs.changed == 'true'
env:
COMMIT_MESSAGE: ${{ steps.bump.outputs.commit_message }}
run: |
git config user.name "odp-releaser[bot]"
git config user.email "odp-releaser[bot]@users.noreply.github.com"
git commit -m "$COMMIT_MESSAGE"
git push
bump_images.yml reference
Bump images
- uses: gulfofmaine/odp-releaser/.github/actions/bump_images@<sha-or-tag>
Bump image references in deployment manifests, then commit, open a PR, or just stage the changes.
Inputs: ¤
| Name | Description | Default |
|---|---|---|
client_payload
¤
|
repository_dispatch client_payload JSON produced by |
${{ toJSON(github.event.client_payload) }}
|
config_path
¤
|
Path to the image manifest config file. |
.github/image_manifest.yaml
|
verbosity
¤
|
CLI verbosity: 0=warning, 1=info (default), 2 or more=debug. Maps to the CLI's -v/-vv/-vvv flags (capped at 3). |
1
|
git_user_name
¤
|
Git author/committer name for direct commits. |
odp-releaser[bot]
|
git_user_email
¤
|
Git author/committer email for direct commits. |
odp-releaser[bot]@users.noreply.github.com
|
stage_only
¤
|
When "true", write the manifest changes and |
false
|
sync
¤
|
When "false", skip the image sync even for configs whose |
true
|
token
¤
|
Token used to push the bump commit or open the pull request. Pass an app-minted token if the resulting commit/PR should trigger CI. When the image manifest configures |
${{ github.token }}
|
| Testing aids | ||
dry_run
¤
|
When "true", run the CLI with --dry-run (no manifest files written) and skip the stage, commit, and pull-request steps. Outputs are still produced. Testing aid; used by this repo's own e2e CI. |
false
|
| Reporter app credentials | ||
reporter_apps
¤
|
Optional JSON object mapping source owner -> {app_id, private_key} reporter app credentials. Used (with organization "Members: read" granted to the app) to check |
|
reporter_app_id
¤
|
Optional App ID of the source org's reporter GitHub App, used to check |
|
reporter_app_private_key
¤
|
Optional private key matching reporter_app_id. |
|
Outputs: ¤
| Name | Description |
|---|---|
image_name
¤
|
Image name the bump ran for (no tag or digest). |
digest
¤
|
Digest (sha256:...) of the image the bump ran for. |
new_tag
¤
|
Tag the manifests were bumped to. For a release event this is the release ref, not the payload's |
changed
¤
|
Whether any manifests changed ("true"/"false"). |
update_mode
¤
|
Update mode resolved from the image manifest config ("commit"/"pull_request"). |
environment
¤
|
GitHub environment name resolved from the image manifest config for deployment reporting; empty when unconfigured. |
environment_url
¤
|
Deployment "View deployment" URL resolved (and templated) from the image manifest config; empty when unconfigured. |
pull_request_url
¤
|
URL of the bump pull request; empty unless a pull_request-mode bump opened or updated one. |
branch_name
¤
|
Branch name used for pull_request mode. |
commit_message
¤
|
Generated commit message for the bump. |
pr_title
¤
|
Generated pull request title for the bump. |
pr_body
¤
|
Generated pull request body for the bump (includes the embedded report metadata). |
reviewers
¤
|
Comma-separated GitHub usernames requested as reviewers on the bump pull request; empty when none are configured. |
team_reviewers
¤
|
Comma-separated GitHub team slugs requested as reviewers on the bump pull request; empty when none are configured. |
comment_enabled
¤
|
Whether commenting back on the source pull request is enabled for this image ("true"/"false"). |
comment_pr_number
¤
|
Source pull request the comment lands on; empty for events that carry no pull request (release, workflow_dispatch). |
comment_staged_template
¤
|
Resolved comment template for a bump pull request awaiting review, unrendered; pass to comment_on_pr. |
comment_deployed_template
¤
|
Resolved comment template for a landed bump, unrendered. |
sync_source_ref
¤
|
Image reference the sync copies from, pinned by digest; empty when no config asked for a sync. |
sync_destinations
¤
|
Newline-separated destination references the image was (or would be) copied to; empty when no config asked for a sync. |
synced
¤
|
Whether the sync step actually ran to completion ("true"/"false"). False when nothing was configured, the sync was skipped (sync: "false"), or this was a dry run. |
Source of gulfofmaine/odp-releaser/.github/actions/bump_images@<sha-or-tag>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 | |
report_deployment
Runs odp-releaser report-deployment, which creates (or finds) a
GitHub deployment
on the source repository at the commit that built the image and sets its
status — success for a bump committed directly, queued for a bump pull
request that still needs review. bump-images.yml runs this action after a
successful bump, and report-merged.yml runs it when a bump PR merges; use
it directly when composing your own workflow from the bump_images action.
Provide exactly one of:
client_payload— right after a bump, the same payload the bump ran with;pr_body— after a bump pull request closed, the body of that PR. The payload, environment, and environment URL thatbump_imagesembedded in the body at bump time are read back out, and the queued deployment from the bump is found (same commit + environment) and updated instead of a duplicate being created. A body without embedded metadata is a friendly no-op, so running on any closed PR is safe.
Prerequisites:
- Reporter app credentials for the source org — see
GitHub Apps. The minted token is scoped to
the single source repository with
deployments: writeonly.
A failed report exits non-zero and fails the step; wrap the action in
continue-on-error: true (as bump-images.yml does) when reporting should
be best-effort rather than a hard failure.
on:
pull_request:
types: [closed]
jobs:
report:
if: >-
github.event.pull_request.merged == true &&
startsWith(github.event.pull_request.head.ref, 'odp-releaser/')
runs-on: ubuntu-latest
steps:
- name: Report merged deployment
uses: gulfofmaine/odp-releaser/.github/actions/report_deployment@<sha-or-tag>
with:
pr_body: ${{ github.event.pull_request.body }}
environment_url: >-
${{ github.server_url }}/${{ github.repository }}/commit/${{
github.event.pull_request.merge_commit_sha }}
reporter_app_id: ${{ secrets.REPORTER_APP_ID }}
reporter_app_private_key: ${{ secrets.REPORTER_APP_PRIVATE_KEY }}
(That example is what
report-merged.yml
packages up — prefer the reusable workflow unless you need to customize it.)
report_deployment.yml Reference
Report deployment
- uses: gulfofmaine/odp-releaser/.github/actions/report_deployment@<sha-or-tag>
Report a completed image bump back to the source repo as a GitHub deployment and status.
Inputs: ¤
| Name | Description | Default |
|---|---|---|
client_payload
¤
|
repository_dispatch client_payload JSON produced by |
|
pr_body
¤
|
Body of a merged bump pull request; the payload, environment, and environment URL embedded at bump time are read from it. Provide either this or client_payload. A body without embedded metadata is a no-op. |
|
update_mode
¤
|
How the bump landed ("commit" reports a success deployment, "pull_request" reports a queued one). |
commit
|
environment
¤
|
GitHub environment name for the deployment. An environment embedded in pr_body wins; empty falls back to the deploy repo's owner/name slug. |
|
environment_url
¤
|
"View deployment" link for the deployment status — typically the bump commit or pull request URL. A URL embedded in pr_body wins. |
|
verbosity
¤
|
CLI verbosity: 0=warning, 1=info (default), 2 or more=debug. Maps to the CLI's -v/-vv/-vvv flags (capped at 3). |
1
|
| Reporter app credentials | ||
reporter_apps
¤
|
JSON object mapping source owner -> {app_id, private_key} reporter app credentials, for deploy repos that report to multiple source orgs. |
|
reporter_app_id
¤
|
App ID of the reporter GitHub App installed on the source repos. |
|
reporter_app_private_key
¤
|
Private key matching reporter_app_id. |
|
Source of gulfofmaine/odp-releaser/.github/actions/report_deployment@<sha-or-tag>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | |
comment_on_pr
Runs odp-releaser comment, which posts (or updates) a markdown comment on
the source repository's pull request saying which image was bumped and
where — the readable counterpart to the deployment record.
bump-images.yml runs this action after the deployment report, and
report-merged.yml runs it when a bump PR merges; use it directly when
composing your own workflow from the bump_images action.
Which template is used follows update_mode: pull_request posts the staged
comment (the bump is waiting on review, nothing is live), commit posts the
deployed one. See
Pull request comments for the
templates and their placeholders.
Provide exactly one of:
client_payload— right after a bump, the same payload the bump ran with, with the templates passed in from thebump_imagesoutputs;pr_body— after a bump pull request closed, the body of that PR. The payload, environment, comment templates and source pull request number thatbump_imagesembedded at bump time are read back out, so no image manifest (and no deploy-repo checkout) is needed. A body without embedded metadata, or one from before comment support, is a friendly no-op.
Reruns update the same comment rather than adding another: it is found by an invisible marker keyed on the deploy repo, the image, and the environment, so another deploy repo's or another image's comment on the same pull request is never touched.
Prerequisites:
- Reporter app credentials for the source org, whose app has been granted
Pull requests: Read and writeand whose installations have accepted that permission — see Pull request comments. The minted token is scoped to the single source repository withpull_requests: writeonly.
Nothing is posted, and the step still succeeds, when comment_enabled is
"false", when the chosen template is empty, or when there is no source pull
request to comment on (only push payloads carry one). Other failures exit
non-zero; wrap the action in continue-on-error: true (as bump-images.yml
does) when commenting should be best-effort.
- name: Bump images
id: bump
uses: gulfofmaine/odp-releaser/.github/actions/bump_images@<sha-or-tag>
- name: Comment on the source pull request
if: steps.bump.outputs.comment_pr_number != ''
continue-on-error: true
uses: gulfofmaine/odp-releaser/.github/actions/comment_on_pr@<sha-or-tag>
with:
update_mode: ${{ steps.bump.outputs.update_mode }}
environment: ${{ steps.bump.outputs.environment }}
bump_url: ${{ steps.bump.outputs.pull_request_url }}
pr_number: ${{ steps.bump.outputs.comment_pr_number }}
comment_enabled: ${{ steps.bump.outputs.comment_enabled }}
staged_template: ${{ steps.bump.outputs.comment_staged_template }}
deployed_template: ${{ steps.bump.outputs.comment_deployed_template }}
reporter_app_id: ${{ secrets.REPORTER_APP_ID }}
reporter_app_private_key: ${{ secrets.REPORTER_APP_PRIVATE_KEY }}
comment_on_pr.yml reference
Comment on source pull request
- uses: gulfofmaine/odp-releaser/.github/actions/comment_on_pr@<sha-or-tag>
Comment on the source repository's pull request saying where an image was deployed.
Inputs: ¤
| Name | Description | Default |
|---|---|---|
client_payload
¤
|
repository_dispatch client_payload JSON produced by |
|
pr_body
¤
|
Body of a merged bump pull request; the payload, environment, comment templates and source pull request number embedded at bump time are read from it. Provide either this or client_payload. A body without embedded metadata is a no-op. |
|
update_mode
¤
|
How the bump landed ("commit" posts the deployed comment, "pull_request" posts the staged one). |
commit
|
environment
¤
|
GitHub environment name named in the comment, and part of the comment's identity. An environment embedded in pr_body wins; empty falls back to the deploy repo's owner/name slug. |
|
environment_url
¤
|
Available to templates as |
|
bump_url
¤
|
Where the bump itself lives — the bump commit or pull request URL — available to templates as |
|
run_url
¤
|
Available to templates as |
|
pr_number
¤
|
Source pull request to comment on (the |
|
comment_enabled
¤
|
Whether to comment at all (the |
true
|
staged_template
¤
|
Comment body used for a |
|
deployed_template
¤
|
Comment body used once the bump has landed (the |
|
verbosity
¤
|
CLI verbosity: 0=warning, 1=info (default), 2 or more=debug. Maps to the CLI's -v/-vv/-vvv flags (capped at 3). |
1
|
| Reporter app credentials | ||
reporter_apps
¤
|
JSON object mapping source owner -> {app_id, private_key} reporter app credentials, for deploy repos that report to multiple source orgs. |
|
reporter_app_id
¤
|
App ID of the reporter GitHub App installed on the source repos. It must be granted |
|
reporter_app_private_key
¤
|
Private key matching reporter_app_id. |
|
Source of gulfofmaine/odp-releaser/.github/actions/comment_on_pr@<sha-or-tag>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | |