Update versions.json (#4222) #2501
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: Create Changelog Pull Request | |
on: | |
push: | |
branches: ["main"] | |
workflow_dispatch: | |
jobs: | |
update-changelog-pull-request: | |
runs-on: runner-cluster-htl-set | |
env: | |
CONFIG_PATH: .github/changelog-pr-config.json | |
BRANCH_NAME: github-action-update-changelog | |
AUTOMATED_PR_LABEL: "automated pr" | |
permissions: | |
contents: write | |
pull-requests: write | |
steps: | |
- name: Generate a token | |
id: generate-token | |
uses: actions/create-github-app-token@v1 | |
with: | |
app-id: ${{ vars.APP_ID }} | |
private-key: ${{ secrets.APP_PRIVATE_KEY }} | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
- name: Get latest dates in changelog | |
run: | | |
DATES=$(grep -E '^## [0-9]{4}-[0-9]{2}-[0-9]{2}' CHANGELOG.md | head -n 2 | awk '{print $2}') | |
LATEST_DATE=$(echo "$DATES" | sed -n '1p') | |
SECOND_LATEST_DATE=$(echo "$DATES" | sed -n '2p') | |
TODAY=$(date -u +%Y-%m-%d) | |
echo "TODAY=$TODAY" >> $GITHUB_ENV | |
if [[ "$LATEST_DATE" == "$TODAY" ]]; then | |
echo "LATEST_DATE=$SECOND_LATEST_DATE" >> $GITHUB_ENV | |
else | |
echo "LATEST_DATE=$LATEST_DATE" >> $GITHUB_ENV | |
fi | |
- name: Get categorized pull requests | |
id: get-categorized-prs | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
async function main() { | |
const fs = require('fs').promises; | |
const path = require('path'); | |
const configPath = path.resolve(process.env.CONFIG_PATH); | |
const fileContent = await fs.readFile(configPath, 'utf-8'); | |
const changelogConfig = JSON.parse(fileContent); | |
const categorizedPRs = changelogConfig.map(obj => ({ | |
...obj, | |
notes: [], | |
subCategories: obj.subCategories ?? ( | |
obj.labels.includes("update script") ? [ | |
{ title: "π Bug Fixes", labels: ["bugfix"], notes: [] }, | |
{ title: "β¨ New Features", labels: ["feature"], notes: [] }, | |
{ title: "π₯ Breaking Changes", labels: ["breaking change"], notes: [] }, | |
{ title: "π§ Refactor", labels: ["refactor"], notes: [] }, | |
] : | |
obj.labels.includes("maintenance") ? [ | |
{ title: "π Bug Fixes", labels: ["bugfix"], notes: [] }, | |
{ title: "β¨ New Features", labels: ["feature"], notes: [] }, | |
{ title: "π₯ Breaking Changes", labels: ["breaking change"], notes: [] }, | |
{ title: "π‘ API", labels: ["api"], notes: [] }, | |
{ title: "Github", labels: ["github"], notes: [] }, | |
{ title: "π Documentation", labels: ["documentation"], notes: [] }, | |
{ title: "π§ Refactor", labels: ["refactor"], notes: [] } | |
] : | |
obj.labels.includes("website") ? [ | |
{ title: "π Bug Fixes", labels: ["bugfix"], notes: [] }, | |
{ title: "β¨ New Features", labels: ["feature"], notes: [] }, | |
{ title: "π₯ Breaking Changes", labels: ["breaking change"], notes: [] }, | |
{ title: "Script Information", labels: ["json"], notes: [] } | |
] : [] | |
) | |
})); | |
const latestDateInChangelog = new Date(process.env.LATEST_DATE); | |
latestDateInChangelog.setUTCHours(23, 59, 59, 999); | |
const { data: pulls } = await github.rest.pulls.list({ | |
owner: context.repo.owner, | |
repo: "ProxmoxVE", | |
base: "main", | |
state: "closed", | |
sort: "updated", | |
direction: "desc", | |
per_page: 100, | |
}); | |
const filteredPRs = pulls.filter(pr => | |
pr.merged_at && | |
new Date(pr.merged_at) > latestDateInChangelog && | |
!pr.labels.some(label => | |
["invalid", "wontdo", process.env.AUTOMATED_PR_LABEL].includes(label.name.toLowerCase()) | |
) | |
); | |
for (const pr of filteredPRs) { | |
const prLabels = pr.labels.map(label => label.name.toLowerCase()); | |
if (pr.user.login.includes("push-app-to-main[bot]")) { | |
const scriptName = pr.title; | |
try { | |
const { data: relatedIssues } = await github.rest.issues.listForRepo({ | |
owner: context.repo.owner, | |
repo: "ProxmoxVED", | |
state: "all", | |
labels: ["Started Migration To ProxmoxVE"] | |
}); | |
const matchingIssue = relatedIssues.find(issue => | |
issue.title.toLowerCase().includes(scriptName.toLowerCase()) | |
); | |
if (matchingIssue) { | |
const issueAuthor = matchingIssue.user.login; | |
const issueAuthorUrl = `https://github.com/${issueAuthor}`; | |
prNote = `- ${pr.title} [@${issueAuthor}](${issueAuthorUrl}) ([#${pr.number}](${pr.html_url}))`; | |
} | |
else { | |
prNote = `- ${pr.title} ([#${pr.number}](${pr.html_url}))`; | |
} | |
} catch (error) { | |
console.error(`Error fetching related issues: ${error}`); | |
prNote = `- ${pr.title} ([#${pr.number}](${pr.html_url}))`; | |
} | |
}else{ | |
prNote = `- ${pr.title} [@${pr.user.login}](https://github.com/${pr.user.login}) ([#${pr.number}](${pr.html_url}))`; | |
} | |
if (prLabels.includes("new script")) { | |
const newScriptCategory = categorizedPRs.find(category => | |
category.title === "New Scripts" || category.labels.includes("new script")); | |
if (newScriptCategory) { | |
newScriptCategory.notes.push(prNote); | |
} | |
} else { | |
let categorized = false; | |
const priorityCategories = categorizedPRs.slice(); | |
for (const category of priorityCategories) { | |
if (categorized) break; | |
if (category.labels.some(label => prLabels.includes(label))) { | |
if (category.subCategories && category.subCategories.length > 0) { | |
const subCategory = category.subCategories.find(sub => | |
sub.labels.some(label => prLabels.includes(label)) | |
); | |
if (subCategory) { | |
subCategory.notes.push(prNote); | |
} else { | |
category.notes.push(prNote); | |
} | |
} else { | |
category.notes.push(prNote); | |
} | |
categorized = true; | |
} | |
} | |
} | |
} | |
return categorizedPRs; | |
} | |
return await main(); | |
- name: Update CHANGELOG.md | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const fs = require('fs').promises; | |
const path = require('path'); | |
const today = process.env.TODAY; | |
const latestDateInChangelog = process.env.LATEST_DATE; | |
const changelogPath = path.resolve('CHANGELOG.md'); | |
const categorizedPRs = ${{ steps.get-categorized-prs.outputs.result }}; | |
console.log(JSON.stringify(categorizedPRs, null, 2)); | |
let newReleaseNotes = `## ${today}\n\n`; | |
for (const { title, notes, subCategories } of categorizedPRs) { | |
const hasSubcategories = subCategories && subCategories.length > 0; | |
const hasMainNotes = notes.length > 0; | |
const hasSubNotes = hasSubcategories && subCategories.some(sub => sub.notes && sub.notes.length > 0); | |
if (hasMainNotes || hasSubNotes) { | |
newReleaseNotes += `### ${title}\n\n`; | |
} | |
if (hasMainNotes) { | |
newReleaseNotes += ` ${notes.join("\n")}\n\n`; | |
} | |
if (hasSubcategories) { | |
for (const { title: subTitle, notes: subNotes } of subCategories) { | |
if (subNotes && subNotes.length > 0) { | |
newReleaseNotes += ` - #### ${subTitle}\n\n`; | |
newReleaseNotes += ` ${subNotes.join("\n ")}\n\n`; | |
} | |
} | |
} | |
} | |
const changelogContent = await fs.readFile(changelogPath, 'utf-8'); | |
const changelogIncludesTodaysReleaseNotes = changelogContent.includes(`\n## ${today}`); | |
const regex = changelogIncludesTodaysReleaseNotes | |
? new RegExp(`## ${today}.*(?=## ${latestDateInChangelog})`, "gs") | |
: new RegExp(`(?=## ${latestDateInChangelog})`, "gs"); | |
const newChangelogContent = changelogContent.replace(regex, newReleaseNotes); | |
await fs.writeFile(changelogPath, newChangelogContent); | |
- name: Check for changes | |
id: verify-diff | |
run: | | |
git diff --quiet . || echo "changed=true" >> $GITHUB_ENV | |
- name: Commit and push changes | |
if: env.changed == 'true' | |
run: | | |
git config --global user.name "github-actions[bot]" | |
git config --global user.email "github-actions[bot]@users.noreply.github.com" | |
git add CHANGELOG.md | |
git commit -m "Update CHANGELOG.md" | |
git checkout -b $BRANCH_NAME || git checkout $BRANCH_NAME | |
git push origin $BRANCH_NAME --force | |
- name: Create pull request if not exists | |
if: env.changed == 'true' | |
env: | |
GH_TOKEN: ${{ steps.generate-token.outputs.token }} | |
run: | | |
PR_EXISTS=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') | |
if [ -z "$PR_EXISTS" ]; then | |
gh pr create --title "[Github Action] Update CHANGELOG.md" \ | |
--body "This PR is auto-generated by a Github Action to update the CHANGELOG.md file." \ | |
--head $BRANCH_NAME \ | |
--base main \ | |
--label "$AUTOMATED_PR_LABEL" | |
fi | |
- name: Approve pull request | |
if: env.changed == 'true' | |
env: | |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
run: | | |
PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') | |
if [ -n "$PR_NUMBER" ]; then | |
gh pr review $PR_NUMBER --approve | |
fi | |
- name: Re-approve pull request after update | |
if: env.changed == 'true' | |
env: | |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
run: | | |
PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') | |
if [ -n "$PR_NUMBER" ]; then | |
gh pr review $PR_NUMBER --approve | |
fi |