Skip to content

Commit

Permalink
Include client payload data
Browse files Browse the repository at this point in the history
  • Loading branch information
mthalman committed Jun 28, 2024
1 parent 549e3ef commit 59cd32e
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 16 deletions.
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,47 @@ The algorithm for deriving the image names is described below:
1. Starting from the last stage, walk the stage hierarchy until the root stage is found.
1. The image name of the root stage is considered the base image name.

## Dispatch Payload

When the repository dispatch occurs, a payload is sent along with it. This payload includes metadata describing the state of the image that resulted in an update being needed. This payload can be retrieved in the workflow that responds to the dispatch via the `dispatch-payload` output of the action.

```yaml
name: Build Image
on:
repository_dispatch:
types: [base-image-update]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Show Rebuild Info
if: ${{ github.event.client_payload }}
run: |
target="${{ github.event.client_payload.updates[0].targetImageName }}"
base="${{ github.event.client_payload.updates[0].baseImageName }}"
echo "Rebuilding $target to be in sync with $base"
```

### Payload Schema

Each object in the `updates` array represents an image that the action has determined requires an update.
Currently the action only supports targeting single images and so this array will always have a single element.
See #3 for support for multiple images.

```json
{
"updates": [
{
"targetImageName": "Name of the target image provided as input to the action",
"targetImageDigest": "Current digest of the target image",
"dockerfile": "Relative path of the Dockerfile",
"baseImageName": "Name of the base image that was either provided as input to the action or derived via other state",
"baseImageDigest": "Current digest of the base image"
}
]
}
```

## Examples

### Explicitly set base stage name
Expand Down
48 changes: 38 additions & 10 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,16 @@ inputs:
outputs:
dispatch-sent:
description: "A value indicating whether a repository dispatch was sent"
value: ${{ steps.report-status.outputs.dispatch-sent }}
value: ${{ steps.check-image.outputs.SEND_DISPATCH }}
dispatch-payload:
description: "The payload data that was sent with the dispatch"
value: ${{ steps.check-image.outputs.OUTPUT_DISPATCH_PAYLOAD }}

runs:
using: "composite"
steps:
- name: Check Image
id: check-image
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
Expand All @@ -46,16 +51,20 @@ runs:
throw "'dockerfile' input not provided. This is required when 'base-image-name' is not provided."
}
$dockerBumpCheckerVersion = "0.2.0"
$dockerBumpCheckerVersion = "dev"
$containerName = "docker-bump-checker"
$containerSrcPath = "/src"
if ($dockerfile.StartsWith(${env:GITHUB_WORKSPACE})) {
$dockerfile = $dockerfile.Substring(${env:GITHUB_WORKSPACE}.Length).TrimStart('/')
}
# The repo directory will be volume-mounted into the container so the Dockerfile path needs to be modified accordingly
$dockerfile = "$containerSrcPath/$dockerfile"
$result = docker run `
--name $containerName `
-v ${env:GITHUB_WORKSPACE}:$containerSrcPath `
-w $containerSrcPath `
ghcr.io/mthalman/docker-bump-checker:$dockerBumpCheckerVersion `
-BaseImage `"$baseImage`" `
-TargetImage `"$targetImage`" `
Expand All @@ -64,7 +73,7 @@ runs:
-Architecture `"$arch`"
$dockerRunExitCode = $LASTEXITCODE
docker cp ${containerName}:/home/app/log.txt log.txt
docker cp ${containerName}:/home/app/log.txt log.txt > $null 2>&1
if ($LASTEXITCODE -ne 0) {
throw "Unable to retrieve log"
}
Expand All @@ -79,23 +88,42 @@ runs:
if ($LASTEXITCODE -ne 0) {
throw "command failed"
}
$result = $result | ConvertFrom-Json
# Need to track two different payloads. This is only to account for the scenario where no dispatch is sent.
# In that scenario, we don't send the dispatch but the repository-dispatch action will still validate its
# client-payload input which needs to be valid JSON. So that is defaulted here. The other payload is the
# output of this composite action. That needs to be empty when no dispatch is sent. In the scenario where
# a dispatch is sent, both these payload variables get set to the same value.
$actionPayload = "{}"
$outputPayload = ""
if ($result.sendDispatch -eq "true") {
$payloadObj = @{
updates = $result.updates
}
$actionPayload = ,$payloadObj | ConvertTo-Json -Compress
$outputPayload = $actionPayload
}
echo "SEND_DISPATCH=$result" >> "$env:GITHUB_ENV"
echo "SEND_DISPATCH=$($result.sendDispatch)" >> $env:GITHUB_OUTPUT
echo "ACTION_DISPATCH_PAYLOAD=$actionPayload" >> $env:GITHUB_OUTPUT
echo "OUTPUT_DISPATCH_PAYLOAD=$outputPayload" >> $env:GITHUB_OUTPUT
- name: Repository Dispatch
if: env.SEND_DISPATCH == 'true'
if: ${{ steps.check-image.outputs.SEND_DISPATCH }} == 'true'
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ inputs.token }}
repository: ${{ inputs.repository }}
event-type: ${{ inputs.event-type }}
client-payload: ${{ steps.check-image.outputs.ACTION_DISPATCH_PAYLOAD }}
- name: Report Status
id: report-status
shell: pwsh
run: |
if ($env:SEND_DISPATCH -eq "true") {
if ("${{ steps.check-image.outputs.SEND_DISPATCH }}" -eq "true") {
echo "A repository dispatch was sent to '${{ inputs.repository }}' with event type '${{ inputs.event-type }}'."
} else {
echo "No repository dispatch was sent."
}
echo "dispatch-sent=$env:TRIGGER_WORKFLOW" >> "$env:GITHUB_OUTPUT"
1 change: 1 addition & 0 deletions container/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ COPY --from=installer /usr/share/powershell /usr/share/powershell
COPY --from=installer /root/.dotnet/tools /home/app/.dotnet/tools
COPY --from=installer ["/symlinks", "/usr/bin"]
COPY *.ps1 /scripts/
COPY *.psm1 /scripts/

# Returns 'true' in the output if the image is out-of-date in relation to its base image; otherwise, 'false'.
ENTRYPOINT ["pwsh", "-c", "/scripts/entrypoint.ps1"]
36 changes: 31 additions & 5 deletions container/check-image.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,49 @@ param(
[string]$BaseImage,

[Parameter(Mandatory = $True)]
[string]$Architecture
[string]$Architecture,

[string]$DockerfilePath
)

$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
Set-StrictMode -Version 2.0

Import-Module $PSScriptRoot/common.psm1
function GetDigest($imageName) {
$digestCmd = "dredge manifest resolve $imageName --os linux --arch $Architecture"
$digest = $(InvokeTool $digestCmd "dredge manifest resolve failed")
return $digest
}

$cmd = "dredge image compare layers --output json $BaseImage $TargetImage --os linux --arch $Architecture"
$layerComparisonStr = $(InvokeTool $cmd "dredge image compare failed")
Import-Module $PSScriptRoot/common.psm1

$compareCmd = "dredge image compare layers --output json $BaseImage $TargetImage --os linux --arch $Architecture"
$layerComparisonStr = $(InvokeTool $compareCmd "dredge image compare failed")
$layerComparison = $layerComparisonStr | ConvertFrom-Json

$imageUpToDate = [bool]$($layerComparison.summary.targetIncludesAllBaseLayers)
$sendDispatch = ([string](-not $imageUpToDate)).ToLower()

$targetDigest = $(GetDigest $TargetImage)
$baseDigest = $(GetDigest $BaseImage)

LogMessage "Send dispatch: $sendDispatch"

return $sendDispatch
$updates = @()
if (-not $imageUpToDate) {
$updates += @{
targetImageName = $TargetImage
targetImageDigest = $targetDigest
dockerfile = $DockerfilePath
baseImageName = $BaseImage
baseImageDigest = $baseDigest
}
}

$result = @{
sendDispatch = $sendDispatch
updates = $updates
} | ConvertTo-Json

return $result
2 changes: 1 addition & 1 deletion container/entrypoint.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ if (-not $BaseImage) {
$BaseImage = $(& $PSScriptRoot/get-base-image.ps1 -DockerfilePath $DockerfilePath -BaseStageName $BaseStageName)
}

$result = $(& $PSScriptRoot/check-image.ps1 -TargetImage $targetImage -BaseImage $BaseImage -Architecture $Architecture)
$result = $(& $PSScriptRoot/check-image.ps1 -TargetImage $targetImage -BaseImage $BaseImage -Architecture $Architecture -DockerfilePath $DockerfilePath)

return $result

0 comments on commit 59cd32e

Please sign in to comment.