diff --git a/.github/workflows/release_please.yml b/.github/workflows/release_please.yml index a6dd0c1e..f6587e77 100644 --- a/.github/workflows/release_please.yml +++ b/.github/workflows/release_please.yml @@ -14,8 +14,51 @@ jobs: runs-on: ubuntu-latest steps: - uses: google-github-actions/release-please-action@v3 + id: release with: release-type: node package-name: release-please-action - default-branch: master - token: ${{secrets.CR_PAT}} \ No newline at end of file + default-branch: dev + token: ${{secrets.CR_PAT}} + + # Checkout repos + - uses: actions/checkout@v3 + with: + repository: koenkk/zigbee2mqtt + path: ./z2m + - uses: actions/checkout@v3 + with: + repository: koenkk/zigbee2mqtt + path: ./z2m-master + ref: master + - uses: actions/checkout@v3 + with: + repository: koenkk/zigbee2mqtt + path: ./z2m-changelog + ref: release-please--branches--dev--components--release-please-action + + - name: Update latest-dev tag + run: | + cd z2m + git push --delete origin latest-dev + git tag latest-dev + git push origin latest-dev + - name: Update latest-dev release changelog + run: | + PR=$(echo '${{ steps.release.outputs.pr }}' | jq -r '.number') + MASTER_Z2M_VERSION=$(cat z2m-master/package.json | jq -r '.version') + MASTER_ZHC_VERSION=$(cat z2m-master/package.json | jq -r '.dependencies."zigbee-herdsman-converters"') + MASTER_ZH_VERSION=$(cat z2m-master/package.json | jq -r '.dependencies."zigbee-herdsman"') + cp z2m-changelog/CHANGELOG.md z2m + cd z2m + npm ci + node scripts/generateChangelog.js $MASTER_Z2M_VERSION $MASTER_ZHC_VERSION $MASTER_ZH_VERSION >> ../changelog + env: + GH_TOKEN: ${{secrets.CR_PAT}} + - uses: ncipollo/release-action@v1 + with: + bodyFile: changelog + prerelease: true + name: latest-dev + allowUpdates: true + tag: latest-dev diff --git a/scripts/generateChangelog.js b/scripts/generateChangelog.js new file mode 100644 index 00000000..7586eedb --- /dev/null +++ b/scripts/generateChangelog.js @@ -0,0 +1,87 @@ +/* eslint max-len: 0 */ +const path = require('path'); +const fs = require('fs'); +const process = require('process'); +const {execSync} = require('child_process'); +const zhc = require('zigbee-herdsman-converters'); + +const z2mTillVersion = process.argv[2]; +const zhcTillVersion = process.argv[3]; +const zhTillVersion = process.argv[4]; + +const changelogs = [ + {tillVersion: z2mTillVersion, project: 'koenkk/zigbee2mqtt', + contents: fs.readFileSync(path.join(__dirname, '..', 'CHANGELOG.md'), 'utf-8').split('\n')}, + {tillVersion: zhcTillVersion, project: 'koenkk/zigbee-herdsman-converters', + contents: fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'zigbee-herdsman-converters', 'CHANGELOG.md'), 'utf-8').split('\n')}, + {tillVersion: zhTillVersion, project: 'koenkk/zigbee-herdsman', + contents: fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'zigbee-herdsman', 'CHANGELOG.md'), 'utf-8').split('\n')}, +]; + +const releaseRe = /## \[(.+)\]/; +const changes = {features: [], fixes: [], detect: [], add: []}; +let context = null; +const changeRe = [ + /^\* (\*\*(.+):\*\*)?(.+)\((\[#\d+\]\(.+\))\) \(\[(.+)\]\(https:.+\)$/, + /^\* (\*\*(.+):\*\*)?(.+)(https:\/\/github\.com.+) \(\[(.+)\]\(https:.+\)$/, + /^\* (\*\*(.+):\*\*)?(.+)() \(\[(.+)\]\(https:.+\)$/, +]; + +for (const changelog of changelogs) { + for (const line of changelog.contents) { + const releaseMatch = line.match(releaseRe); + const changeMatch = changeRe.map((re) => line.match(re)).find((e) => e); + if (releaseMatch) { + if (releaseMatch[1] === changelog.tillVersion) { + break; + } + } else if (line === '### Features') { + context = 'features'; + } else if (line === '### Bug Fixes') { + context = 'fixes'; + } else if (line.startsWith('* **ignore:**')) { + continue; + } else if (changeMatch) { + const localContext = changeMatch[2] ? changeMatch[2] : context; + if (!changes[localContext]) throw new Error(`Unknown context: ${localContext}`); + + let message = changeMatch[3].trim(); + if (message.endsWith('.')) message = message.substring(0, message.length - 1); + if (localContext === 'add') { + const model = zhc.definitions.find((d) => d.model === message); + if (!model) throw new Error(`${message} does not exist`); + message = `\`${model.model}\` ${model.vendor} ${model.description}`; + } + + let issue = changeMatch[4].trim(); + if (issue && !issue.startsWith('[#')) issue = `[#${issue.split('/').pop()}](${issue})`; + if (!issue) issue = '_NO_ISSUE_'; + + const user = execSync(`curl -s https://api.github.com/repos/${changelog.project}/commits/${changeMatch[5]} | jq -r '.author.login'`).toString().trim(); + + changes[localContext].push(`- ${issue} ${message} (@${user})`); + } else if (line === '# Changelog' || !line) { + continue; + } else { + throw new Error(`Unmatched line: ${line}`); + } + } +} + +let result = ''; +const names = [ + ['features', 'Improvements'], + ['fixes', 'Fixes'], + ['add', 'New supported devices'], + ['detect', 'Fixed device detections'], +]; +for (const name of names) { + result += `# ${name[1]}\n`; + if (name[0] === 'add') { + result += `This release adds support for ${changes['add'].length} devices: \n`; + } + changes[name[0]].forEach((e) => result += `${e}\n`); + result += '\n'; +} + +console.log(result.trim());