Skip to content

push-new-version

push-new-version #35

name: Push_new_version
# this workflow is triggered by another repository
# WARN : need write main protection rule
# repository settings requirement : Allow GitHub Actions to create and approve pull requests
permissions:
contents: write
pull-requests: write
## Actions > Secrets
# BYPASS_USER_PAT - fine-grained PAT with content+pullrequest write from user that can review
on:
repository_dispatch:
types: [push-new-version]
jobs:
handle-dispatch:
runs-on: ubuntu-latest
strategy:
matrix:
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
node-version: [ 20.x ]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
# inputs github.event.client_payload : version,label,label_fr,description,description_fr,note,download
- name: Read input variables from client_payload as env variables
run: |
# Function to write a variable to $GITHUB_ENV handling newlines and quotes
write_env_var() {
local var_name="$1"
local payload_value="$2"
# Handle newlines and quotes to avoid issues in the environment file
echo -n "${var_name}=" >> $GITHUB_ENV
echo -e "${payload_value}" | sed ':a;N;$!ba;s/\n/\\n/g; s/'\''/’/g' >> $GITHUB_ENV
}
# Extract fields from payload and process for multiline handling
write_env_var "VERSION" "${{ github.event.client_payload.version }}"
write_env_var "LABEL" "${{ github.event.client_payload.label }}"
write_env_var "LABEL_FR" "${{ github.event.client_payload.label_fr }}"
write_env_var "DESCRIPTION" "${{ github.event.client_payload.description }}"
write_env_var "DESCRIPTION_FR" "${{ github.event.client_payload.description_fr }}"
write_env_var "NOTE" "${{ github.event.client_payload.note }}"
write_env_var "DOWNLOAD" "${{ github.event.client_payload.download }}"
# craft a new branch name
echo "BRANCH_NAME=update-version-${{ github.sha }}" >> $GITHUB_ENV
- name: Run push_new_version script
run: |
node scripts/push_new_version.mjs \
--commitAndPush "false" \
--version "$VERSION" \
--label "$LABEL" \
--label_fr "$LABEL_FR" \
--description "$DESCRIPTION" \
--description_fr "$DESCRIPTION_FR" \
--note "$NOTE" \
--download "$DOWNLOAD"
- name: Create a pull request
id: cpr
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.BYPASS_USER_PAT }}
# base: main
branch: ${{ env.BRANCH_NAME}}
commit-message: Add version ${{ env.VERSION }}
title: Automated version update to ${{ env.VERSION }}
body: This PR updates the version file automatically. Generated from `push_new_version.yml`.
author: ChickArmy[bot] <github-actions[bot][email protected]>
- name: Create Pull request outputs
if: ${{ steps.cpr.outputs.pull-request-number }}
run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
# WARN this step expects at least 1 check in success to pass ! (0 check results in timeout error)
- name: Wait for PR related Checks to Pass
if: ${{ steps.cpr.outputs.pull-request-number }}
uses: actions/github-script@v7
with:
github-token: ${{ secrets.BYPASS_USER_PAT }}
script: |
const prNumber = ${{ steps.cpr.outputs.pull-request-number }};
let allChecksCompleted = false;
const checkInterval = 30 * 1000; // 30 seconds
const maxAttempts = 20; // Total wait time = maxAttempts * checkInterval
let attempt = 0;
let checks = [];
while (attempt < maxAttempts) {
console.log(`Checking status of CI/CD checks (Attempt ${attempt + 1}/${maxAttempts})...`);
try {
// Get PR details to retrieve PR SHA
const { data: { head: { sha } } } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
});
console.log(`ℹ️ PR #${prNumber} ref is ${sha}`);
// Get PR checks
const { data } = await github.rest.checks.listForRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: sha,
});
checks = data.check_runs;
if (checks.length < 1) {
console.log('⌛ Await until CI/CD at least one check_run appears.');
} else {
console.log("checks :\n" + JSON.stringify(checks, null, 2));
allChecksCompleted = checks.every(check => check.status === 'completed');
if (allChecksCompleted) {
console.log(`ℹ️ ${checks.length} CI/CD checks have completed.`);
break;
} else {
console.log(`⌛ Not all ${checks.length} checks are complete yet. Waiting...`);
}
}
} catch (error) {
if (error.status === 404) {
console.log('⌛ Pull request not yet available, waiting...');
} else {
throw error; // Rethrow if it's a different error
}
}
await new Promise(resolve => setTimeout(resolve, checkInterval));
attempt++;
}
if (!allChecksCompleted) {
throw new Error(`🔴 Some of ${checks.length} CI/CD checks did not complete within the expected timeframe.`);
}
// Check if all completed checks passed
const failedChecks = [];
allChecksPassed = checks.every(check => {
const passed = check.conclusion === 'success';
if (!passed) {
// Gather details for failed checks
failedChecks.push({
name: check.name,
conclusion: check.conclusion,
completed_at: check.completed_at,
html_url: check.html_url
});
}
return passed;
});
if (!allChecksPassed) {
console.log('Some checks failed:');
failedChecks.forEach(check => {
console.log(`- Name: ${check.name}`);
console.log(` Conclusion: ${check.conclusion}`);
console.log(` Completed at: ${check.completed_at}`);
console.log(` Details: ${check.html_url}`);
});
throw new Error('🔴 Some CI/CD checks did not pass successfully.');
} else {
console.log(`✅ ${checks.length} CI/CD checks in SUCCESS 🥇`);
}
- name: Merge Pull Request using authorized user to bypass rule
if: ${{ steps.cpr.outputs.pull-request-number }}
uses: actions/github-script@v7
with:
github-token: ${{ secrets.BYPASS_USER_PAT }}
script: |
const pull_number = ${{ steps.cpr.outputs.pull-request-number }};
console.log(`Merging pull request: #${pull_number}`);
await github.rest.pulls.merge({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number,
merge_method: 'rebase' // or 'squash' or 'merge'
});
console.log(`✅ Pull request #${pull_number} merged successfully. 🥳`);