Fix release proposal workflow and add post-release branch sync #1
Workflow file for this run
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 Release Tags | |
| on: | |
| pull_request: | |
| types: [closed] | |
| branches: | |
| - main | |
| - develop | |
| permissions: | |
| contents: write | |
| jobs: | |
| create-tags: | |
| # Only run if PR was merged and has release labels | |
| if: | | |
| github.event.pull_request.merged == true && | |
| (contains(github.event.pull_request.labels.*.name, 'release:setuptools-scm') || | |
| contains(github.event.pull_request.labels.*.name, 'release:vcs-versioning')) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Create tags and releases | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| const pr = context.payload.pull_request; | |
| const prTitle = pr.title; | |
| const mergeCommitSha = pr.merge_commit_sha; | |
| const labels = pr.labels.map(l => l.name); | |
| console.log(`Processing PR #${pr.number}: ${prTitle}`); | |
| console.log(`Merge commit: ${mergeCommitSha}`); | |
| console.log(`Labels: ${labels.join(', ')}`); | |
| const tagsCreated = []; | |
| // Helper to extract version from PR title | |
| function extractVersion(title, packageName) { | |
| const regex = new RegExp(`${packageName} v(\\d+\\.\\d+\\.\\d+)`); | |
| const match = title.match(regex); | |
| return match ? match[1] : null; | |
| } | |
| // Helper to extract changelog section for a version | |
| async function extractChangelog(packageDir, version) { | |
| try { | |
| const { data: file } = await github.rest.repos.getContent({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| path: `${packageDir}/CHANGELOG.md`, | |
| ref: mergeCommitSha | |
| }); | |
| const content = Buffer.from(file.content, 'base64').toString('utf-8'); | |
| const lines = content.split('\n'); | |
| let inSection = false; | |
| let changelog = []; | |
| for (const line of lines) { | |
| if (line.startsWith(`## ${version}`)) { | |
| inSection = true; | |
| continue; // Skip the header line | |
| } | |
| if (inSection && line.match(/^## \d/)) { | |
| break; // Next version section | |
| } | |
| if (inSection) { | |
| changelog.push(line); | |
| } | |
| } | |
| // Trim leading/trailing empty lines | |
| while (changelog.length > 0 && changelog[0].trim() === '') { | |
| changelog.shift(); | |
| } | |
| while (changelog.length > 0 && changelog[changelog.length - 1].trim() === '') { | |
| changelog.pop(); | |
| } | |
| return changelog.join('\n'); | |
| } catch (error) { | |
| console.log(`Could not extract changelog: ${error.message}`); | |
| return `Release ${version}`; | |
| } | |
| } | |
| // Helper to create tag and release | |
| async function createTagAndRelease(packageName, packageDir, tagPrefix) { | |
| const version = extractVersion(prTitle, packageName); | |
| if (!version) { | |
| throw new Error(`Failed to extract ${packageName} version from PR title: ${prTitle}`); | |
| } | |
| const tagName = `${tagPrefix}-v${version}`; | |
| console.log(`Creating tag: ${tagName} at ${mergeCommitSha}`); | |
| // Create annotated tag via API | |
| const { data: tagObject } = await github.rest.git.createTag({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| tag: tagName, | |
| message: `Release ${packageName} v${version}`, | |
| object: mergeCommitSha, | |
| type: 'commit', | |
| tagger: { | |
| name: 'github-actions[bot]', | |
| email: 'github-actions[bot]@users.noreply.github.com', | |
| date: new Date().toISOString() | |
| } | |
| }); | |
| // Create ref for the tag | |
| await github.rest.git.createRef({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: `refs/tags/${tagName}`, | |
| sha: tagObject.sha | |
| }); | |
| console.log(`Tag ${tagName} created`); | |
| tagsCreated.push(tagName); | |
| // Extract changelog | |
| const changelog = await extractChangelog(packageDir, version); | |
| // Create GitHub release | |
| const { data: release } = await github.rest.repos.createRelease({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| tag_name: tagName, | |
| name: `${packageName} v${version}`, | |
| body: changelog, | |
| draft: false, | |
| prerelease: false | |
| }); | |
| console.log(`Release created: ${release.html_url}`); | |
| return { tagName, version }; | |
| } | |
| // Process setuptools-scm | |
| if (labels.includes('release:setuptools-scm')) { | |
| console.log('\n--- Processing setuptools-scm ---'); | |
| await createTagAndRelease('setuptools-scm', 'setuptools-scm', 'setuptools-scm'); | |
| } | |
| // Process vcs-versioning | |
| if (labels.includes('release:vcs-versioning')) { | |
| console.log('\n--- Processing vcs-versioning ---'); | |
| await createTagAndRelease('vcs-versioning', 'vcs-versioning', 'vcs-versioning'); | |
| } | |
| // Write summary | |
| const summary = `## Tags Created\n\n${tagsCreated.map(t => `- \`${t}\``).join('\n')}\n\nPyPI upload will be triggered automatically by tag push.`; | |
| await core.summary.addRaw(summary).write(); | |
| console.log(`\nDone! Created tags: ${tagsCreated.join(', ')}`); |