mirror of
https://github.com/community-scripts/ProxmoxVED.git
synced 2026-02-24 21:47:26 +00:00
Workflow
This commit is contained in:
263
.github/workflows/changelog-pr.yaml
vendored
Normal file
263
.github/workflows/changelog-pr.yaml
vendored
Normal file
@@ -0,0 +1,263 @@
|
||||
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: context.repo.repo,
|
||||
base: "main",
|
||||
state: "closed",
|
||||
sort: "updated",
|
||||
direction: "desc",
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
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())
|
||||
)
|
||||
).forEach(pr => {
|
||||
|
||||
const prLabels = pr.labels.map(label => label.name.toLowerCase());
|
||||
if (pr.user.login === "push-app-to-main") {
|
||||
const scriptName = pr.title;
|
||||
if (scriptName) {
|
||||
try {
|
||||
const { data: relatedIssues } = await github.rest.issues.listForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: "ProxmoxVED",
|
||||
state: "all",
|
||||
label: "Started Migration To ProxmoxVE",
|
||||
per_page: 5
|
||||
});
|
||||
const matchingIssue = relatedIssues.find(issue =>
|
||||
issue.title.toLowerCase().includes(scriptName.toLowerCase())
|
||||
);
|
||||
if (matchingIssue) {
|
||||
const issueAuthor = matchingIssue.user.login;
|
||||
const issueAuthorUrl = `https://github.com/${issueAuthor}`;
|
||||
const prNote = `- ${pr.title} [@${issueAuthor}](${issueAuthorUrl}) ([#${pr.number}](${pr.html_url}))`;
|
||||
} else {
|
||||
const prNote = `- ${pr.title} ([#${pr.number}](${pr.html_url}))`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error fetching related issues: ${error}`);
|
||||
const prNote = `- ${pr.title} ([#${pr.number}](${pr.html_url}))`;
|
||||
}
|
||||
} else {
|
||||
const prNote = `- ${pr.title} ([#${pr.number}](${pr.html_url}))`;
|
||||
|
||||
}else{
|
||||
const prNote = `- ${pr.title} [@${pr.user.login}](https://github.com/${pr.user.login}) ([#${pr.number}](${pr.html_url}))`;
|
||||
}
|
||||
const updateScriptsCategory = categorizedPRs.find(category =>
|
||||
category.labels.some(label => prLabels.includes(label))
|
||||
);
|
||||
|
||||
if (updateScriptsCategory) {
|
||||
|
||||
const subCategory = updateScriptsCategory.subCategories.find(sub =>
|
||||
sub.labels.some(label => prLabels.includes(label))
|
||||
);
|
||||
|
||||
if (subCategory) {
|
||||
subCategory.notes.push(prNote);
|
||||
} else {
|
||||
updateScriptsCategory.notes.push(prNote);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(JSON.stringify(categorizedPRs, null, 2));
|
||||
|
||||
return categorizedPRs;
|
||||
}
|
||||
main().catch(error => {
|
||||
console.error("Error in script:", error);
|
||||
});
|
||||
|
||||
- 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
|
||||
Reference in New Issue
Block a user