Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/wordpress-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ on:

jobs:
announce:
name: Announce the plugin release on Discord
name: Record the plugin release on GitHub and announce it on Discord
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Checkout code
Expand All @@ -22,3 +24,4 @@ jobs:
run: node scripts/announce-wordpress-release.mjs
env:
DISCORD_RELEASES_WEBHOOK_URL: ${{ secrets.DISCORD_RELEASES_WEBHOOK_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
73 changes: 68 additions & 5 deletions scripts/announce-wordpress-release.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Posts a WordPress plugin release to the Discord releases channel, from the changelog in README.txt.
// The plugin ships through WordPress.org SVN, which GitHub never sees: the "WordPress release" workflow is run by
// hand after the SVN commit, and this script refuses to post until WordPress.org serves the Stable tag.
// Usage: DISCORD_RELEASES_WEBHOOK_URL=… node scripts/announce-wordpress-release.mjs (DRY_RUN=1 prints the message)
// Records a WordPress plugin release on GitHub and posts it to the Discord releases channel, from the changelog in
// README.txt. The plugin ships through WordPress.org SVN, which GitHub never sees: the "WordPress release" workflow is
// run by hand after the SVN commit. It refuses to run until WordPress.org serves the Stable tag, and the GitHub release
// (`wordpress@<version>`) is the record that the version was announced, so a second run posts nothing.
// Usage: DISCORD_RELEASES_WEBHOOK_URL=… GITHUB_TOKEN=… GITHUB_REPOSITORY=… GITHUB_SHA=… node scripts/announce-wordpress-release.mjs
// Without DISCORD_RELEASES_WEBHOOK_URL, or with DRY_RUN=1, it prints the Discord message and the GitHub release instead.
import { readFile } from 'node:fs/promises'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
Expand All @@ -14,11 +16,14 @@ export const EXIT_CODES = {
discord_rejected_message: 4,
wordpress_org_unreachable: 5,
not_live_on_wordpress_org: 6,
github_unreachable: 7,
github_rejected_release: 8,
}

const PLUGIN_URL = 'https://wordpress.org/plugins/simplepdf-embed/'
const PLUGIN_INFO_URL = 'https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&slug=simplepdf-embed'
const SOURCE_URL = 'https://github.com/SimplePDF/simplepdf-embed/tree/main/wordpress'
const GITHUB_API_URL = 'https://api.github.com'

// The Stable tag and its `= <version> =` changelog bullets, or null without a Stable tag.
export const parseReadme = (readme) => {
Expand Down Expand Up @@ -66,6 +71,45 @@ export const buildWordPressReleaseMessage = ({ version, entries }) => ({
],
})

// make_latest is off: the "Latest" badge stays on the SDK packages' releases.
export const buildGitHubRelease = ({ version, entries, commitSha }) => ({
tag_name: `wordpress@${version}`,
target_commitish: commitSha,
name: `SimplePDF Embed for WordPress ${version}`,
body: ["## What's new", '', ...entries.map((entry) => `- ${entry}`), '', `[WordPress.org plugin page](${PLUGIN_URL})`].join('\n'),
make_latest: 'false',
})

const githubRequest = ({ token, repository, method, pathname, body }) =>
fetch(`${GITHUB_API_URL}/repos/${repository}${pathname}`, {
method,
headers: {
accept: 'application/vnd.github+json',
authorization: `Bearer ${token}`,
'x-github-api-version': '2022-11-28',
...(body === undefined ? {} : { 'content-type': 'application/json' }),
},
...(body === undefined ? {} : { body: JSON.stringify(body) }),
})

// 'exists' | 'missing', or null when GitHub cannot say.
const findGitHubRelease = async ({ token, repository, tagName }) => {
try {
const response = await githubRequest({ token, repository, method: 'GET', pathname: `/releases/tags/${encodeURIComponent(tagName)}` })
if (response.status === 404) {
return 'missing'
}
return response.ok ? 'exists' : null
} catch {
return null
}
}

const createGitHubRelease = async ({ token, repository, release }) => {
const response = await githubRequest({ token, repository, method: 'POST', pathname: '/releases', body: release })
return response.ok ? null : `${response.status} ${await response.text()}`
}

const fetchLiveVersion = async () => {
try {
const response = await fetch(PLUGIN_INFO_URL)
Expand All @@ -90,9 +134,10 @@ const main = async () => {
}

const message = buildWordPressReleaseMessage(release)
const githubRelease = buildGitHubRelease({ ...release, commitSha: process.env.GITHUB_SHA ?? 'main' })
const webhookUrl = process.env.DISCORD_RELEASES_WEBHOOK_URL
if (webhookUrl === undefined || webhookUrl === '' || process.env.DRY_RUN === '1') {
console.log(JSON.stringify(message, null, 2))
console.log(JSON.stringify({ discord: message, github: githubRelease }, null, 2))
return EXIT_CODES.success
}

Expand All @@ -106,12 +151,30 @@ const main = async () => {
return EXIT_CODES.not_live_on_wordpress_org
}

const github = { token: process.env.GITHUB_TOKEN ?? '', repository: process.env.GITHUB_REPOSITORY ?? 'SimplePDF/simplepdf-embed' }
const existingRelease = await findGitHubRelease({ ...github, tagName: githubRelease.tag_name })
if (existingRelease === null) {
console.error(`Could not check GitHub for the ${githubRelease.tag_name} release`)
return EXIT_CODES.github_unreachable
}
if (existingRelease === 'exists') {
console.log(`${githubRelease.tag_name} is already released and announced: nothing to do`)
return EXIT_CODES.success
}

const failure = await postToDiscord({ webhookUrl, message })
if (failure !== null) {
console.error(`Discord rejected the announcement for ${release.version}: ${failure}`)
return EXIT_CODES.discord_rejected_message
}
console.log(`Announced SimplePDF Embed for WordPress ${release.version}`)

const releaseFailure = await createGitHubRelease({ ...github, release: githubRelease })
if (releaseFailure !== null) {
console.error(`Announced on Discord, but GitHub rejected the ${githubRelease.tag_name} release (create it by hand so a rerun does not post again): ${releaseFailure}`)
return EXIT_CODES.github_rejected_release
}
console.log(`Released ${githubRelease.tag_name} on GitHub`)
return EXIT_CODES.success
}

Expand Down
12 changes: 11 additions & 1 deletion scripts/announce-wordpress-release.test.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import { test } from 'node:test'
import { buildWordPressReleaseMessage, parseReadme } from './announce-wordpress-release.mjs'
import { buildGitHubRelease, buildWordPressReleaseMessage, parseReadme } from './announce-wordpress-release.mjs'

const README = `=== SimplePDF Embed ===
Stable tag: 1.2.0
Expand Down Expand Up @@ -40,3 +40,13 @@ test('the message links the plugin page, never pings, and lists every entry', ()
assert.equal(embed.description, "**What's new**\n- New settings page\n- Update web-embed-pdf to 1.9.0")
assert.match(embed.fields[1].value, /\[Plugin page\]\(https:\/\/wordpress\.org\/plugins\/simplepdf-embed\/\)/)
})

test('the GitHub release is tagged wordpress@<version> on the workflow commit and never takes the Latest badge', () => {
const githubRelease = buildGitHubRelease({ ...parseReadme(README), commitSha: 'abc1234' })

assert.equal(githubRelease.tag_name, 'wordpress@1.2.0')
assert.equal(githubRelease.target_commitish, 'abc1234')
assert.equal(githubRelease.name, 'SimplePDF Embed for WordPress 1.2.0')
assert.equal(githubRelease.make_latest, 'false')
assert.match(githubRelease.body, /^## What's new\n\n- New settings page\n- Update web-embed-pdf to 1\.9\.0\n/)
})
2 changes: 1 addition & 1 deletion wordpress/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,5 @@ svn commit -m 'Release <TAG>' --username bendersej # the SVN password from Wor
```
Leave the older `tags/*` folders git tracks out of `svn add`: they already exist on WordPress.org.

7. Once https://wordpress.org/plugins/simplepdf-embed/ shows the new version, announce it on Discord: run the **WordPress release** workflow (`gh workflow run wordpress-release.yaml --repo SimplePDF/simplepdf-embed`). It posts the `README.txt` changelog for the Stable tag, and refuses to post until WordPress.org serves that version.
7. Once https://wordpress.org/plugins/simplepdf-embed/ shows the new version, run the **WordPress release** workflow (`gh workflow run wordpress-release.yaml --repo SimplePDF/simplepdf-embed`). It creates the `wordpress@<TAG>` GitHub release and posts the `README.txt` changelog for the Stable tag to Discord. It refuses to run until WordPress.org serves that version, and does nothing when the GitHub release already exists.
8. Commit the new `svn/tags/<TAG>/` to git.
Loading