Skip to content

Push PR Image

Push PR Image #8

Workflow file for this run

name: Push PR Image
# Runs after "Build PR" completes. Because workflow_run executes in the context
# of the base repository, it has the secrets/permissions needed to push to GHCR
# even when the PR comes from a fork. Downloads the image artifacts built by the
# PR run, pushes per-arch images, and stitches them into a multi-arch manifest
# tagged `pr-<number>` so people can install/test a PR with a single pull.
on:
workflow_run:
workflows: ["Build PR"]
types: [completed]
permissions:
packages: write
pull-requests: write
jobs:
push:
if: |
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'pull_request'
runs-on: ubuntu-24.04
strategy:
matrix:
arch: [amd64, arm64]
steps:
- name: Prepare
env:
REPOSITORY: '${{ github.repository }}'
run: echo "REPOSITORY_LC=${REPOSITORY,,}" >> ${GITHUB_ENV}
- name: Get PR number
id: pr
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_INFO=$(gh api "repos/${{ github.repository }}/pulls?state=all" \
--jq ".[] | select(.head.sha == \"${{ github.event.workflow_run.head_sha }}\") | {number: .number, state: .state}" \
| head -1)
PR_NUMBER=$(echo "$PR_INFO" | jq -r '.number // empty')
PR_STATE=$(echo "$PR_INFO" | jq -r '.state // empty')
if [[ ! "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
echo "::error::Could not resolve PR number for SHA ${{ github.event.workflow_run.head_sha }} (got: '${PR_NUMBER}')" >&2
exit 1
fi
if [[ "$PR_STATE" != "open" ]]; then
echo "::notice::PR #${PR_NUMBER} is already ${PR_STATE}, skipping image push."
echo "skip=true" >> $GITHUB_OUTPUT
fi
echo "number=${PR_NUMBER}" >> $GITHUB_OUTPUT
- name: Download image tar
if: steps.pr.outputs.skip != 'true'
uses: actions/download-artifact@v4
with:
name: image-${{ matrix.arch }}
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Login to GHCR
if: steps.pr.outputs.skip != 'true'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Load and push
if: steps.pr.outputs.skip != 'true'
run: |
docker load < image.tar
TAG="ghcr.io/${{ env.REPOSITORY_LC }}:pr-${{ steps.pr.outputs.number }}-${{ matrix.arch }}"
docker tag "openmower-app-pr:latest" "$TAG"
docker push "$TAG"
merge:
needs: push
runs-on: ubuntu-24.04
steps:
- name: Prepare
env:
REPOSITORY: '${{ github.repository }}'
run: echo "REPOSITORY_LC=${REPOSITORY,,}" >> ${GITHUB_ENV}
- name: Get PR number
id: pr
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_INFO=$(gh api "repos/${{ github.repository }}/pulls?state=all" \
--jq ".[] | select(.head.sha == \"${{ github.event.workflow_run.head_sha }}\") | {number: .number, state: .state}" \
| head -1)
PR_NUMBER=$(echo "$PR_INFO" | jq -r '.number // empty')
PR_STATE=$(echo "$PR_INFO" | jq -r '.state // empty')
if [[ ! "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
echo "::error::Could not resolve PR number for SHA ${{ github.event.workflow_run.head_sha }} (got: '${PR_NUMBER}')" >&2
exit 1
fi
if [[ "$PR_STATE" != "open" ]]; then
echo "::notice::PR #${PR_NUMBER} is already ${PR_STATE}, skipping manifest creation."
echo "skip=true" >> $GITHUB_OUTPUT
fi
echo "number=${PR_NUMBER}" >> $GITHUB_OUTPUT
- name: Set up Docker Buildx
if: steps.pr.outputs.skip != 'true'
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
if: steps.pr.outputs.skip != 'true'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create manifest
if: steps.pr.outputs.skip != 'true'
run: |
REPO="ghcr.io/${{ env.REPOSITORY_LC }}"
PR="${{ steps.pr.outputs.number }}"
docker buildx imagetools create \
-t "${REPO}:pr-${PR}" \
"${REPO}:pr-${PR}-amd64" \
"${REPO}:pr-${PR}-arm64"
comment:
needs: merge
runs-on: ubuntu-24.04
steps:
- name: Prepare
env:
REPOSITORY: '${{ github.repository }}'
run: echo "REPOSITORY_LC=${REPOSITORY,,}" >> ${GITHUB_ENV}
- name: Get PR number
id: pr
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_INFO=$(gh api "repos/${{ github.repository }}/pulls?state=all" \
--jq ".[] | select(.head.sha == \"${{ github.event.workflow_run.head_sha }}\") | {number: .number, state: .state}" \
| head -1)
PR_NUMBER=$(echo "$PR_INFO" | jq -r '.number // empty')
PR_STATE=$(echo "$PR_INFO" | jq -r '.state // empty')
if [[ ! "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
echo "::error::Could not resolve PR number for SHA ${{ github.event.workflow_run.head_sha }} (got: '${PR_NUMBER}')" >&2
exit 1
fi
if [[ "$PR_STATE" != "open" ]]; then
echo "::notice::PR #${PR_NUMBER} is already ${PR_STATE}, skipping comment."
echo "skip=true" >> $GITHUB_OUTPUT
fi
echo "number=${PR_NUMBER}" >> $GITHUB_OUTPUT
- name: Post / update install instructions
if: steps.pr.outputs.skip != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR: ${{ steps.pr.outputs.number }}
IMAGE: ghcr.io/${{ env.REPOSITORY_LC }}:pr-${{ steps.pr.outputs.number }}
run: |
MARKER="<!-- pr-test-image -->"
BODY=$(cat <<EOF
${MARKER}
## 🧪 Test this PR
A multi-arch image (amd64 + arm64) for this PR has been pushed to:
\`\`\`
${IMAGE}
\`\`\`
### OpenMowerOS v2
1. Go to \`http://<mower-ip>:5001/compose/openmower\` in your browser.
2. Switch to edit mode.
3. In the \`compose.yaml\` editor, add the following above the \`# Dockge-specific extras shown in the UI\` line (mind the 2-space indentation, like the other containers):
\`\`\`yaml
app:
image: ${IMAGE}
container_name: app
ports:
- 3000:3000
restart: unless-stopped
\`\`\`
4. Add the URL under \`x-dockge\`:
\`\`\`yaml
x-dockge:
urls:
- http://\${HOSTNAME}:8080
- http://\${HOSTNAME}:3000
\`\`\`
5. Click \`Deploy\`, then open \`http://<mower-ip>:3000\`.
### Plain Docker
\`\`\`bash
docker run --rm -p 3000:3000 ${IMAGE}
\`\`\`
> This comment updates automatically on every new push to the PR.
EOF
)
# Sticky comment: update existing one (by marker) instead of spamming.
COMMENT_ID=$(gh api "repos/${{ github.repository }}/issues/${PR}/comments" \
--jq ".[] | select(.body | startswith(\"${MARKER}\")) | .id" | head -1)
if [[ -n "$COMMENT_ID" ]]; then
gh api --method PATCH "repos/${{ github.repository }}/issues/comments/${COMMENT_ID}" \
-f body="$BODY" >/dev/null
echo "Updated comment ${COMMENT_ID}"
else
gh api --method POST "repos/${{ github.repository }}/issues/${PR}/comments" \
-f body="$BODY" >/dev/null
echo "Created new comment"
fi