[skip-ci] Update actions/github-script action to v8 #5
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Generate Contributor Certificate Preview | |
# This action triggers automatically when a pull request is closed, | |
# or can be run manually from the Actions tab. | |
on: | |
pull_request: | |
types: [closed] | |
branches: | |
- main | |
workflow_dispatch: | |
inputs: | |
contributor_username: | |
description: 'The GitHub username of the contributor' | |
required: true | |
pr_number: | |
description: 'The pull request number' | |
required: true | |
# Permissions needed for this workflow. | |
permissions: | |
contents: read # Write access for certificate storage | |
pull-requests: write # Write access to comment on PRs | |
actions: read # Read access for workflow actions | |
jobs: | |
screenshot_and_comment: | |
# This job runs if the PR was merged or if it's a manual trigger. | |
# The logic for first-time contributors is handled in a dedicated step below. | |
if: ${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true }} | |
runs-on: ubuntu-latest | |
steps: | |
# Step 1: Check if this is the contributor's first merged PR. | |
# This step is the source of truth and will control the execution of subsequent steps. | |
- name: Check for first merged PR | |
id: check_first_pr | |
if: ${{ github.event_name == 'pull_request' }} | |
uses: actions/github-script@v8 | |
with: | |
script: | | |
const author = context.payload.pull_request.user.login; | |
const query = `repo:${context.repo.owner}/${context.repo.repo} is:pr is:merged author:${author}`; | |
console.log(`Searching for merged PRs from @${author} with query: "${query}"`); | |
const result = await github.rest.search.issuesAndPullRequests({ q: query }); | |
const mergedPRs = result.data.total_count; | |
if (mergedPRs === 1) { | |
console.log(`SUCCESS: This is the first merged PR from @${author}. Proceeding...`); | |
core.setOutput('is_first_pr', 'true'); | |
} else { | |
console.log(`INFO: Skipping certificate generation. @${author} has ${mergedPRs} total merged PRs.`); | |
core.setOutput('is_first_pr', 'false'); | |
} | |
# Step 2: Checkout the repository containing the certificate HTML file. | |
- name: Checkout containers/automation repository | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.check_first_pr.outputs.is_first_pr == 'true' }} | |
uses: actions/checkout@v5 | |
with: | |
repository: containers/automation | |
path: automation-repo | |
# Step 3: Update the HTML file locally | |
- name: Update HTML file | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.check_first_pr.outputs.is_first_pr == 'true' }} | |
run: | | |
HTML_FILE="automation-repo/certificate-generator/certificate_generator.html" | |
CONTRIBUTOR_NAME="${{ github.event.inputs.contributor_username || github.event.pull_request.user.login }}" | |
PR_NUMBER="${{ github.event.inputs.pr_number || github.event.pull_request.number }}" | |
MERGE_DATE=$(date -u +"%B %d, %Y") | |
sed -i "/id=\"contributorName\"/s/value=\"[^\"]*\"/value=\"${CONTRIBUTOR_NAME}\"/" ${HTML_FILE} || { echo "ERROR: Failed to update contributor name."; exit 1; } | |
sed -i "/id=\"prNumber\"/s/value=\"[^\"]*\"/value=\"#${PR_NUMBER}\"/" ${HTML_FILE} || { echo "ERROR: Failed to update PR number."; exit 1; } | |
sed -i "/id=\"mergeDate\"/s/value=\"[^\"]*\"/value=\"${MERGE_DATE}\"/" ${HTML_FILE} || { echo "ERROR: Failed to update merge date."; exit 1; } | |
# Step 4: Setup Node.js environment | |
- name: Setup Node.js | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.check_first_pr.outputs.is_first_pr == 'true' }} | |
uses: actions/setup-node@v4 | |
with: | |
node-version: latest | |
# Step 5: Install Puppeteer | |
- name: Install Puppeteer | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.check_first_pr.outputs.is_first_pr == 'true' }} | |
run: | | |
npm install puppeteer || { echo "ERROR: Failed to install Puppeteer."; exit 1; } | |
# Step 6: Take a screenshot of the certificate div | |
- name: Create and run screenshot script | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.check_first_pr.outputs.is_first_pr == 'true' }} | |
run: | | |
cat <<'EOF' > screenshot.js | |
const puppeteer = require('puppeteer'); | |
const path = require('path'); | |
(async () => { | |
const browser = await puppeteer.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] }); | |
const page = await browser.newPage(); | |
const htmlPath = 'file://' + path.resolve('automation-repo/certificate-generator/certificate_generator.html'); | |
await page.goto(htmlPath, { waitUntil: 'networkidle0' }); | |
await page.setViewport({ width: 1080, height: 720 }); | |
const element = await page.$('#certificatePreview'); | |
if (!element) { | |
console.error('Could not find element #certificatePreview.'); | |
process.exit(1); | |
} | |
await element.screenshot({ path: 'certificate.png' }); | |
await browser.close(); | |
console.log('Screenshot saved as certificate.png'); | |
})().catch(err => { | |
console.error(err); | |
process.exit(1); | |
}); | |
EOF | |
node screenshot.js || { echo "ERROR: Screenshot script failed."; exit 1; } | |
# Step 7: Upload certificate image to separate repository | |
- name: Upload certificate to separate repository | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.check_first_pr.outputs.is_first_pr == 'true' }} | |
uses: actions/github-script@v8 | |
with: | |
github-token: ${{ secrets.CERTIFICATES_REPO_TOKEN }} | |
script: | | |
const fs = require('fs'); | |
try { | |
// Check if certificate.png exists | |
if (!fs.existsSync('certificate.png')) { | |
throw new Error('certificate.png not found!'); | |
} | |
// Debug: Check token and repository access | |
console.log('Testing repository access...'); | |
const certificatesOwner = process.env.CERTIFICATES_REPO_OWNER || context.repo.owner; | |
const certificatesRepo = process.env.CERTIFICATES_REPO_NAME || 'automation'; | |
// Test repository access first | |
try { | |
await github.rest.repos.get({ | |
owner: certificatesOwner, | |
repo: certificatesRepo | |
}); | |
console.log(`✅ Repository access confirmed: ${certificatesOwner}/${certificatesRepo}`); | |
} catch (accessError) { | |
console.error(`❌ Repository access failed: ${accessError.message}`); | |
throw new Error(`Cannot access repository ${certificatesOwner}/${certificatesRepo}. Check token permissions and repository existence.`); | |
} | |
// Read the certificate image | |
const imageBuffer = fs.readFileSync('certificate.png'); | |
const base64Content = imageBuffer.toString('base64'); | |
console.log(`Certificate image size: ${imageBuffer.length} bytes`); | |
// Create a unique filename with timestamp | |
const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); | |
const contributorName = context.eventName === 'workflow_dispatch' | |
? '${{ github.event.inputs.contributor_username }}' | |
: '${{ github.event.pull_request.user.login }}'; | |
const prNumber = context.eventName === 'workflow_dispatch' | |
? '${{ github.event.inputs.pr_number }}' | |
: context.issue.number; | |
const filename = `certificates/${contributorName}-${prNumber}-${timestamp}.png`; | |
// Configuration for the certificates repository | |
const certificatesBranch = process.env.CERTIFICATES_REPO_BRANCH || 'main'; | |
console.log(`Uploading to repository: ${certificatesOwner}/${certificatesRepo}`); | |
console.log(`File path: ${filename}`); | |
console.log(`Branch: ${certificatesBranch}`); | |
// Upload the file to the certificates repository | |
await github.rest.repos.createOrUpdateFileContents({ | |
owner: certificatesOwner, | |
repo: certificatesRepo, | |
path: filename, | |
message: `Add certificate for ${contributorName} from ${context.repo.owner}/${context.repo.repo} (PR #${prNumber})\n\nSigned-off-by: Podman Bot <[email protected]>`, | |
content: base64Content, | |
branch: certificatesBranch, | |
author: { | |
name: 'Podman Bot', | |
email: '[email protected]' | |
}, | |
committer: { | |
name: 'Podman Bot', | |
email: '[email protected]' | |
} | |
}); | |
// Create the image URL | |
const imageUrl = `https://github.com/${certificatesOwner}/${certificatesRepo}/raw/${certificatesBranch}/${filename}`; | |
console.log(`Certificate uploaded successfully: ${imageUrl}`); | |
// Store the image URL for the comment step | |
core.exportVariable('CERTIFICATE_IMAGE_URL', imageUrl); | |
core.exportVariable('CERTIFICATE_UPLOADED', 'true'); | |
} catch (error) { | |
console.error('Failed to upload certificate:', error); | |
console.error('Error details:', error.message); | |
// Provide helpful error message if it's likely a permissions issue | |
let errorMsg = error.message; | |
if (error.status === 404) { | |
errorMsg += ' (Repository not found - check CERTIFICATES_REPO_OWNER and CERTIFICATES_REPO_NAME environment variables, or ensure the automation repository exists and the token has access)'; | |
} else if (error.status === 403) { | |
errorMsg += ' (Permission denied - check that CERTIFICATES_REPO_TOKEN has write access to the automation repository)'; | |
} | |
core.exportVariable('CERTIFICATE_UPLOADED', 'false'); | |
core.exportVariable('UPLOAD_ERROR', errorMsg); | |
} | |
# Step 8: Comment on Pull Request with embedded image | |
- name: Comment with embedded certificate image | |
if: ${{ github.event_name == 'workflow_dispatch' || steps.check_first_pr.outputs.is_first_pr == 'true' }} | |
uses: actions/github-script@v8 | |
with: | |
script: | | |
try { | |
let body; | |
// Check if certificate was uploaded successfully | |
if (process.env.CERTIFICATE_UPLOADED === 'true') { | |
const imageUrl = process.env.CERTIFICATE_IMAGE_URL; | |
console.log(`Using uploaded certificate image: ${imageUrl}`); | |
// Create the image content with the uploaded image URL | |
const imageContent = ``; | |
body = imageContent; | |
} else { | |
console.log('Certificate upload failed, providing fallback message'); | |
const errorMsg = process.env.UPLOAD_ERROR || 'Unknown error'; | |
body = `📜 **Certificate Preview**\n\n_Certificate generation completed, but there was an issue uploading the image: ${errorMsg}_\n\nPlease check the workflow logs for more details.`; | |
} | |
if (context.eventName === 'workflow_dispatch') { | |
// Manual trigger case | |
const contributorName = '${{ github.event.inputs.contributor_username }}'; | |
const prNumber = '${{ github.event.inputs.pr_number }}'; | |
body = `📜 Certificate preview generated for @${contributorName} (PR #${prNumber}):\n\n${body}`; | |
} else { | |
// Auto trigger case for first-time contributors | |
const username = '${{ github.event.pull_request.user.login }}'; | |
body = `🎉 Congratulations on your first merged pull request, @${username}! Thank you for your contribution.\n\nHere's a preview of your certificate:\n\n${body}`; | |
} | |
const issueNumber = context.eventName === 'workflow_dispatch' ? | |
parseInt('${{ github.event.inputs.pr_number }}') : | |
context.issue.number; | |
await github.rest.issues.createComment({ | |
issue_number: issueNumber, | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
body: body, | |
}); | |
} catch (error) { | |
core.setFailed(`ERROR: Failed to comment on PR. Details: ${error.message}`); | |
} | |
# Step 9: Clean up temporary files | |
- name: Clean up temporary files | |
if: ${{ always() && (github.event_name == 'workflow_dispatch' || steps.check_first_pr.outputs.is_first_pr == 'true') }} | |
run: | | |
rm -f certificate.png |