Skip to content

Podman PR Push

Podman PR Push #8119

name: Podman PR Push
on:
workflow_run:
workflows:
- 'PR Build Image (Hermetic)'
types:
- completed
jobs:
podman-push:
name: Push Podman Image to Registry
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_run' }}
permissions:
contents: read
issues: write
pull-requests: write
steps:
- name: Get PR number from workflow run
id: get-pr-info
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_TARGET_REPO: ${{ github.repository }}
# If the PR is from a fork, prefix it with `<owner-login>:`, otherwise only the PR branch name is relevant:
PR_BRANCH: |-
${{
(github.event.workflow_run.head_repository.owner.login != github.event.workflow_run.repository.owner.login)
&& format('{0}:{1}', github.event.workflow_run.head_repository.owner.login, github.event.workflow_run.head_branch)
|| github.event.workflow_run.head_branch
}}
FULL_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
SHORT_SHA=$(echo "${{ env.FULL_SHA }}" | cut -c1-8)
# Need to use gh cli instead of `events.workflow_run.pull_requests` because the latter doesn't work for PRs from forks
# Refer to https://github.com/orgs/community/discussions/25220
PR_NUMBER=$(gh pr view --repo "${PR_TARGET_REPO}" "${PR_BRANCH}" --json number --jq '.number')
if [ -n "$PR_NUMBER" ]; then
echo "Found PR number: $PR_NUMBER"
echo "PR branch: $PR_BRANCH"
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
echo "short-sha=$SHORT_SHA" >> $GITHUB_OUTPUT
else
echo "::error::Failed to determine PR number for branch: $PR_BRANCH"
echo "## Failed to determine PR number" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Could not find a PR for branch: \`$PR_BRANCH\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "No PR comment could be posted for this workflow run." >> $GITHUB_STEP_SUMMARY
exit 1
fi
- name: Comment build failure
if: ${{ github.event.workflow_run.conclusion != 'success' }}
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
PR_NUMBER: ${{ steps.get-pr-info.outputs.pr_number }}
BUILD_WORKFLOW_URL: ${{ github.event.workflow_run.html_url }}
BUILD_CONCLUSION: ${{ github.event.workflow_run.conclusion }}
with:
script: |
const prNumber = process.env.PR_NUMBER;
const buildUrl = process.env.BUILD_WORKFLOW_URL;
const conclusion = process.env.BUILD_CONCLUSION;
const body = `The container image [build workflow](${buildUrl}) finished with status: \`${conclusion}\`.\n\n`
github.rest.issues.createComment({
issue_number: parseInt(prNumber),
owner: context.repo.owner,
repo: context.repo.repo,
body: body
})
- name: Determine artifact name
if: ${{ github.event.workflow_run.conclusion == 'success' }}
id: get-artifact-name
env:
PR_NUMBER: ${{ steps.get-pr-info.outputs.pr_number }}
WORKFLOW_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
# For workflow_run, extract from the event context
SHORT_SHA=$(echo "${{ env.WORKFLOW_HEAD_SHA }}" | cut -c1-8)
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
ARTIFACT_NAME="podman-image-${{ env.PR_NUMBER }}-${SHORT_SHA}"
SKIP_ARTIFACT_NAME="pr-${{ env.PR_NUMBER }}-${SHORT_SHA}-isSkipped"
echo "Using artifact name: $ARTIFACT_NAME"
echo "Using skip artifact name: $SKIP_ARTIFACT_NAME"
echo "artifact_name=$ARTIFACT_NAME" >> $GITHUB_OUTPUT
echo "skip_artifact_name=$SKIP_ARTIFACT_NAME" >> $GITHUB_OUTPUT
- name: Download Skip Status Artifact
if: ${{ github.event.workflow_run.conclusion == 'success' }}
id: download-skip-status
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
env:
SKIP_ARTIFACT_NAME: ${{ steps.get-artifact-name.outputs.skip_artifact_name }}
with:
name: ${{ env.SKIP_ARTIFACT_NAME }}
path: ./rhdh-skip-artifacts
run-id: ${{ github.event.workflow_run.id || github.run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
- name: Check Skip Status
if: ${{ github.event.workflow_run.conclusion == 'success' }}
id: check-skip
run: |
if [ -f "./rhdh-skip-artifacts/isSkipped.txt" ]; then
IS_SKIPPED_RAW=$(cat ./rhdh-skip-artifacts/isSkipped.txt)
# Sanitize: Allow only 'true' or 'false', default to false if unknown to skip artifact download in case of injection
if [[ "$IS_SKIPPED_RAW" == "false" ]]; then
IS_SKIPPED="false"
else
IS_SKIPPED="true"
fi
echo "Found skip status: $IS_SKIPPED_RAW"
echo "Sanitized skip status: $IS_SKIPPED"
echo "is_skipped=$IS_SKIPPED" >> $GITHUB_OUTPUT
else
echo "Skip status artifact not found, skipping push"
echo "is_skipped=true" >> $GITHUB_OUTPUT
fi
- name: Download Image Artifacts
if: ${{ github.event.workflow_run.conclusion == 'success' && steps.check-skip.outputs.is_skipped != 'true' }}
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
env:
ARTIFACT_NAME: ${{ steps.get-artifact-name.outputs.artifact_name }}
with:
name: ${{ env.ARTIFACT_NAME }}
path: ./rhdh-podman-artifacts
run-id: ${{ github.event.workflow_run.id || github.run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Load and prepare image
if: ${{ github.event.workflow_run.conclusion == 'success' && steps.check-skip.outputs.is_skipped != 'true' }}
id: prepare
run: |
# Check if artifacts exist
if [ ! -f "./rhdh-podman-artifacts/image.tar" ]; then
echo "Error: image.tar not found in artifacts"
echo "This may make sense if the build was skipped"
exit 1
fi
# Load the image from tar file (contains all tags)
podman load -i ./rhdh-podman-artifacts/image.tar
# Read metadata
TAGS_LIST=$(cat ./rhdh-podman-artifacts/tags.txt)
echo "Loaded images:"
podman images
echo "Full tags from metadata:"
printf '%s\n' "$TAGS_LIST"
# SECURITY: Use a random delimiter to prevent output injection from artifact poisoning
DELIMITER=$(openssl rand -hex 16)
echo "tags<<$DELIMITER" >> $GITHUB_OUTPUT
printf '%s\n' "$TAGS_LIST" >> $GITHUB_OUTPUT
echo "$DELIMITER" >> $GITHUB_OUTPUT
- name: Push Images
if: ${{ github.event.workflow_run.conclusion == 'success' && steps.check-skip.outputs.is_skipped != 'true' }}
uses: redhat-actions/push-to-registry@5ed88d269cf581ea9ef6dd6806d01562096bee9c # v2.8
env:
PUSHED_TAGS: ${{ steps.prepare.outputs.tags }}
with:
tags: ${{ env.PUSHED_TAGS }}
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_TOKEN }}
- name: Comment skip status
if: ${{ github.event.workflow_run.conclusion == 'success' && steps.check-skip.outputs.is_skipped == 'true' }}
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
env:
PR_NUMBER: ${{ steps.get-pr-info.outputs.pr_number }}
BUILD_WORKFLOW_URL: ${{ github.event.workflow_run.html_url }}
PUBLISH_WORKFLOW_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
with:
script: |
const prNumber = process.env.PR_NUMBER;
const buildUrl = process.env.BUILD_WORKFLOW_URL;
const publishUrl = process.env.PUBLISH_WORKFLOW_URL;
const body = `The container image [build](${buildUrl}) and [publish](${publishUrl}) workflows were skipped (either due to [skip-build] tag or no relevant changes with existing image).\n`;
github.rest.issues.createComment({
issue_number: parseInt(prNumber),
owner: context.repo.owner,
repo: context.repo.repo,
body: body
})
- name: Comment the image pull link
if: ${{ github.event.workflow_run.conclusion == 'success' && steps.check-skip.outputs.is_skipped != 'true' }}
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
PUSHED_TAGS: ${{ steps.prepare.outputs.tags }}
PR_NUMBER: ${{ steps.get-pr-info.outputs.pr_number }}
BUILD_WORKFLOW_URL: ${{ github.event.workflow_run.html_url }}
PUBLISH_WORKFLOW_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
with:
script: |
const prNumber = process.env.PR_NUMBER;
const pushedTags = process.env.PUSHED_TAGS;
const buildUrl = process.env.BUILD_WORKFLOW_URL;
const publishUrl = process.env.PUBLISH_WORKFLOW_URL;
// This shouldn't happen since Push Images would've failed, but just in case a mistake is made in the workflow logic or logic changes
const noTagsError = (reason) => {
const body = `The image was [built](${buildUrl}) and [published](${publishUrl}) but no tags were found: ${reason}.\n`;
github.rest.issues.createComment({
issue_number: parseInt(prNumber),
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});
core.setFailed(reason);
};
if (!pushedTags) {
noTagsError('No pushed tags output found');
return;
}
const tags = pushedTags.trim().split('\n').filter(tag => tag.trim());
if (tags.length === 0) {
noTagsError('Pushed tags output was empty');
return;
}
console.log(`Found ${tags.length} tags:`, tags);
const tagLinks = tags.map(fullTag => {
return `* [\`${fullTag}\`](https://${fullTag})`;
}).join('\n');
const body = `Image was [built](${buildUrl}) and [published](${publishUrl}) successfully. It is available at:\n\n${tagLinks}\n`;
console.log(`Creating comment for PR ${prNumber} with body:\n ${body}`);
github.rest.issues.createComment({
issue_number: parseInt(prNumber),
owner: context.repo.owner,
repo: context.repo.repo,
body: body
})
- name: Comment publish failure
if: ${{ failure() && github.event.workflow_run.conclusion == 'success' }}
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
PR_NUMBER: ${{ steps.get-pr-info.outputs.pr_number }}
BUILD_WORKFLOW_URL: ${{ github.event.workflow_run.html_url }}
PUBLISH_WORKFLOW_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
with:
script: |
const prNumber = process.env.PR_NUMBER;
const buildUrl = process.env.BUILD_WORKFLOW_URL;
const publishUrl = process.env.PUBLISH_WORKFLOW_URL;
const body = `The container image was [built](${buildUrl}) successfully but failed to [publish](${publishUrl}) to the registry.\n`;
github.rest.issues.createComment({
issue_number: parseInt(prNumber),
owner: context.repo.owner,
repo: context.repo.repo,
body: body
})