Skip to content

JudyZhuHz triggered release comment #13

JudyZhuHz triggered release comment

JudyZhuHz triggered release comment #13

name: PR Release Comment
run-name: ${{ github.actor }} triggered release comment
on:
workflow_dispatch:
inputs:
test_version:
description: 'Test version number (e.g., 3.4.0)'
required: true
test_pr_number:
description: 'Test PR number (e.g., 2) - required for manual testing'
required: true
workflow_run:
workflows: ["Deploy CD"]
types:
- completed
jobs:
comment-on-prs:
name: Comment on Released PRs
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
steps:
- name: Checkout Repository
uses: actions/checkout@v3
with:
fetch-depth: 0
ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }}
- name: Get Version and Tag Info
id: tag-info
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ github.event.inputs.test_version }}"
DEPLOY_SHA="${{ github.sha }}"
PR_NUMBER="${{ github.event.inputs.test_pr_number }}"
else
DEPLOY_SHA="${{ github.event.workflow_run.head_sha }}"
PR_NUMBER=""
git fetch --tags
NEAREST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
VERSION=""
if [ -n "$NEAREST_TAG" ]; then
TAG_SHA=$(git rev-list -n 1 "$NEAREST_TAG" 2>/dev/null || echo "")
if [ "$TAG_SHA" = "$DEPLOY_SHA" ]; then
VERSION="$NEAREST_TAG"
fi
fi
fi
STABLE_VERSION=""
TAG_FOR_STABLE="${VERSION:-$NEAREST_TAG}"
if [ -n "$TAG_FOR_STABLE" ]; then
STABLE_VERSION=$(echo "${TAG_FOR_STABLE#v}" | sed 's/-next\..*//' | sed 's/-[a-z]*\..*//')
fi
CHANGELOG_FILE=""
if [ -n "$STABLE_VERSION" ]; then
FILE_NAME="v${STABLE_VERSION//./_}"
CHANGELOG_FILE="/tmp/changelog_${FILE_NAME}.json"
git fetch origin documentation 2>/dev/null
if ! git show "origin/documentation:docs/changelog/logs/${FILE_NAME}.json" > "$CHANGELOG_FILE" 2>/dev/null; then
CHANGELOG_FILE=""
fi
fi
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "stable_version=${STABLE_VERSION}" >> $GITHUB_OUTPUT
echo "deploy_sha=${DEPLOY_SHA}" >> $GITHUB_OUTPUT
echo "pr_number=${PR_NUMBER}" >> $GITHUB_OUTPUT
echo "changelog_file=${CHANGELOG_FILE}" >> $GITHUB_OUTPUT
- name: Get Packages and Versions from Changelog
id: get-prs
uses: actions/github-script@v7
with:
script: |
const deploySha = '${{ steps.tag-info.outputs.deploy_sha }}';
const stableVersion = '${{ steps.tag-info.outputs.stable_version }}';
const manualPrNumber = '${{ steps.tag-info.outputs.pr_number }}';
const owner = 'webex';
const repo = 'webex-js-sdk';
const emptyResult = (prNum, fetchSuccess) => {
core.setOutput('pr_numbers', JSON.stringify(prNum ? [prNum] : []));
core.setOutput('package_versions', JSON.stringify({}));
core.setOutput('primary_package', '');
core.setOutput('fetch_success', String(fetchSuccess));
};
// --- Step 1: Discover PR number ---
let prNumber = manualPrNumber;
if (!prNumber && deploySha) {
try {
const prs = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner, repo, commit_sha: deploySha
});
const mergedPR = prs.data.find(pr => pr.merged_at);
if (mergedPR) {
prNumber = String(mergedPR.number);
}
} catch (error) {
console.log(`Failed to discover PR from commit: ${error.message}`);
}
}
if (!prNumber) {
emptyResult(null, true);
return;
}
console.log(`PR #${prNumber}, deploy SHA: ${deploySha}`);
// --- Step 2: Read changelog from local file (fetched via git in shell step) ---
const changelogFile = '${{ steps.tag-info.outputs.changelog_file }}';
let changelog = {};
let fetchSuccess = false;
if (!changelogFile) {
emptyResult(prNumber, false);
return;
}
try {
const fs = require('fs');
changelog = JSON.parse(fs.readFileSync(changelogFile, 'utf8'));
fetchSuccess = true;
} catch (error) {
console.log(`Failed to read changelog: ${error.message}`);
emptyResult(prNumber, false);
return;
}
// --- Step 3: Match deploy SHA against changelog entries ---
const packageVersions = {};
for (const [pkgName, versions] of Object.entries(changelog)) {
for (const [ver, data] of Object.entries(versions)) {
const commits = data.commits || {};
if (deploySha in commits) {
packageVersions[pkgName] = ver;
for (const [alongPkg, alongVer] of Object.entries(data.alongWith || {})) {
if (!packageVersions[alongPkg]) {
packageVersions[alongPkg] = alongVer;
}
}
break;
}
}
}
const directlyChanged = Object.keys(packageVersions).filter(p => p !== 'webex' && p !== 'webex-node');
const primaryPackage = directlyChanged[0] || Object.keys(packageVersions)[0] || '';
core.setOutput('pr_numbers', JSON.stringify([prNumber]));
core.setOutput('package_versions', JSON.stringify(packageVersions));
core.setOutput('primary_package', primaryPackage);
core.setOutput('fetch_success', 'true');
- name: Post Release Comment on PR
uses: actions/github-script@v7
with:
script: |
const version = '${{ steps.tag-info.outputs.version }}';
const stableVersion = '${{ steps.tag-info.outputs.stable_version }}';
const prNumbersRaw = '${{ steps.get-prs.outputs.pr_numbers }}';
const packageVersionsRaw = '${{ steps.get-prs.outputs.package_versions }}';
const primaryPackage = '${{ steps.get-prs.outputs.primary_package }}';
const fetchSuccess = '${{ steps.get-prs.outputs.fetch_success }}' === 'true';
const prNumbers = prNumbersRaw ? JSON.parse(prNumbersRaw) : [];
const packageVersions = packageVersionsRaw ? JSON.parse(packageVersionsRaw) : {};
const owner = 'webex';
const repo = 'webex-js-sdk';
const prNumber = prNumbers[0];
if (!prNumber) return;
const repoUrl = `https://github.com/${owner}/${repo}`;
const packageEntries = Object.entries(packageVersions);
const hasPackages = packageEntries.length > 0;
let commentBody;
if (hasPackages) {
const changelogUrl = new URL('https://web-sdk.webex.com/changelog/');
if (stableVersion) {
changelogUrl.searchParams.set('stable_version', stableVersion);
}
const changelogPackage = packageVersions['webex'] ? 'webex' : primaryPackage;
changelogUrl.searchParams.set('package', changelogPackage);
changelogUrl.searchParams.set('version', packageVersions[changelogPackage]);
const releaseLine = version
? `| **Released in:** [\`${version}\`](${repoUrl}/releases/tag/${version}) |`
: '';
const rows = packageEntries
.sort(([a], [b]) => {
if (a === 'webex') return -1;
if (b === 'webex') return 1;
if (a === primaryPackage) return -1;
if (b === primaryPackage) return 1;
return a.localeCompare(b);
})
.map(([pkg, ver]) => `| \`${pkg}\` | \`${ver}\` |`)
.join('\n');
const packagesTable = [
'',
'| Packages Updated | Version |',
'|---------|---------|',
rows,
''
].join('\n');
commentBody = [
'| :tada: Your changes are now available! |',
'|---|',
releaseLine,
`| :book: **[View full changelog →](${changelogUrl})** |`,
packagesTable,
'Thank you for your contribution!',
'',
`_:robot: This is an automated message. For questions, please refer to the [documentation](${repoUrl}#readme)._`
].filter(Boolean).join('\n');
} else if (fetchSuccess) {
commentBody = [
'| :white_check_mark: Your changes have been merged! |',
'|---|',
'| No packages affected. |',
'',
'Thank you for your contribution!',
'',
`_:robot: This is an automated message. For questions, please refer to the [documentation](${repoUrl}#readme)._`
].join('\n');
} else {
commentBody = [
'| :white_check_mark: Your changes have been merged! |',
'|---|',
'',
'Thank you for your contribution!',
'',
`_:robot: This is an automated message. For questions, please refer to the [documentation](${repoUrl}#readme)._`
].join('\n');
}
try {
let pr;
try {
pr = await github.rest.pulls.get({
owner, repo,
pull_number: parseInt(prNumber)
});
} catch (error) {
return;
}
if (!pr.data.merged_at) return;
const existingComments = await github.rest.issues.listComments({
owner, repo,
issue_number: parseInt(prNumber),
per_page: 100
});
const detailedComment = existingComments.data.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('Your changes are now available')
);
const mergedComment = existingComments.data.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('Your changes have been merged')
);
if (detailedComment) return;
if (!hasPackages && mergedComment) return;
if (mergedComment && hasPackages) {
await github.rest.issues.updateComment({
owner, repo,
comment_id: mergedComment.id,
body: commentBody
});
} else {
await github.rest.issues.createComment({
owner, repo,
issue_number: parseInt(prNumber),
body: commentBody
});
}
} catch (error) {
core.warning(`Failed to comment on PR #${prNumber}: ${error.message}`);
}