aboutsummaryrefslogtreecommitdiff
path: root/.github/scripts/announce.mjs
diff options
context:
space:
mode:
Diffstat (limited to '.github/scripts/announce.mjs')
-rwxr-xr-x.github/scripts/announce.mjs168
1 files changed, 168 insertions, 0 deletions
diff --git a/.github/scripts/announce.mjs b/.github/scripts/announce.mjs
new file mode 100755
index 000000000..9a50dbd1d
--- /dev/null
+++ b/.github/scripts/announce.mjs
@@ -0,0 +1,168 @@
+import { readFile } from 'node:fs/promises';
+import { fileURLToPath } from 'node:url';
+import { glob } from 'tinyglobby';
+import { setOutput } from './utils.mjs';
+
+const { GITHUB_REF = 'main' } = process.env;
+const baseUrl = new URL(`https://github.com/withastro/astro/blob/${GITHUB_REF}/`);
+
+const emojis = ['πŸŽ‰', 'πŸ₯³', 'πŸš€', 'πŸ§‘', '🎊', 'πŸ†', 'βœ…', '🀩', 'πŸ€–', 'πŸ™Œ'];
+const descriptors = [
+ 'new releases',
+ 'hot and fresh updates',
+ 'shiny updates',
+ 'exciting changes',
+ 'package updates',
+ 'awesome updates',
+ 'bug fixes and features',
+ 'updates',
+];
+const verbs = [
+ 'just went out!',
+ 'just launched!',
+ 'now available!',
+ 'in the wild!',
+ 'now live!',
+ 'hit the registry!',
+ 'to share!',
+ 'for you!',
+ 'for y’all! 🀠',
+ 'comin’ your way!',
+ 'comin’ atcha!',
+ 'comin’ in hot!',
+ 'freshly minted on the blockchain! (jk)',
+ '[is] out (now with 100% more reticulated splines!)',
+ '(as seen on TV!)',
+ 'just dropped!',
+ '– artisanally hand-crafted just for you.',
+ '– oh happy day!',
+ '– enjoy!',
+ 'now out. Be the first on your block to download!',
+ 'made with love πŸ’•',
+ '[is] out! Our best [version] yet!',
+ '[is] here. DOWNLOAD! DOWNLOAD! DOWNLOAD!',
+ '... HUZZAH!',
+ '[has] landed!',
+ 'landed! The internet just got a little more fun.',
+ '– from our family to yours.',
+ '– go forth and build!',
+];
+const extraVerbs = [
+ 'new',
+ 'here',
+ 'released',
+ 'freshly made',
+ 'going out',
+ 'hitting the registry',
+ 'available',
+ 'live now',
+ 'hot and fresh',
+ 'for you',
+ "comin' atcha",
+];
+
+function item(items) {
+ return items[Math.floor(Math.random() * items.length)];
+}
+
+const plurals = new Map([
+ ['is', 'are'],
+ ['has', 'have'],
+]);
+
+function pluralize(text) {
+ return text.replace(/(\[([^\]]+)\])/gm, (_, _full, match) =>
+ plurals.has(match) ? plurals.get(match) : `${match}s`,
+ );
+}
+
+function singularlize(text) {
+ return text.replace(/(\[([^\]]+)\])/gm, (_, _full, match) => `${match}`);
+}
+
+const packageMap = new Map();
+async function generatePackageMap() {
+ const packageRoot = new URL('../../packages/', import.meta.url);
+ const packages = await glob(['*/package.json', '*/*/package.json'], {
+ cwd: fileURLToPath(packageRoot),
+ expandDirectories: false,
+ ignore: ['**/node_modules/**'],
+ });
+ await Promise.all(
+ packages.map(async (pkg) => {
+ const pkgFile = fileURLToPath(new URL(pkg, packageRoot));
+ const content = await readFile(pkgFile).then((res) => JSON.parse(res.toString()));
+ packageMap.set(content.name, `./packages/${pkg.replace('/package.json', '')}`);
+ }),
+ );
+}
+
+async function generateMessage() {
+ await generatePackageMap();
+ const releases = process.argv.slice(2)[0];
+ const data = JSON.parse(releases);
+ const packages = await Promise.all(
+ data.map(({ name, version }) => {
+ const p = packageMap.get(name);
+ if (!p) {
+ throw new Error(`Unable to find entrypoint for "${name}"!`);
+ }
+ return {
+ name,
+ version,
+ url: new URL(`${p}/CHANGELOG.md#${version.replace(/\./g, '')}`, baseUrl).toString(),
+ };
+ }),
+ );
+
+ const emoji = item(emojis);
+ const descriptor = item(descriptors);
+ const verb = item(verbs);
+
+ let message = '';
+
+ if (packages.length === 1) {
+ const { name, version, url } = packages[0];
+ message += `${emoji} \`${name}@${version}\` ${singularlize(
+ verb,
+ )}\nRead the [release notes β†’](<${url}>)\n`;
+ } else {
+ message += `${emoji} Some ${descriptor} ${pluralize(verb)}\n\n`;
+ for (const { name, version, url } of packages) {
+ message += `β€’ \`${name}@${version}\` Read the [release notes β†’](<${url}>)\n`;
+ }
+ }
+
+ if (message.length < 2000) {
+ return message;
+ } else {
+ const { name, version, url } = packages.find((pkg) => pkg.name === 'astro') ?? packages[0];
+ message = `${emoji} Some ${descriptor} ${pluralize(verb)}\n\n`;
+ message += `β€’ \`${name}@${version}\` Read the [release notes β†’](<${url}>)\n`;
+
+ message += `\nAlso ${item(extraVerbs)}:`;
+
+ const remainingPackages = packages.filter((p) => p.name !== name);
+ for (const { name, version, _url } of remainingPackages) {
+ message += `\nβ€’ \`${name}@${version}\``;
+ }
+
+ if (message.length < 2000) {
+ return message;
+ } else {
+ message = `${emoji} Some ${descriptor} ${pluralize(verb)}\n\n`;
+ message += `β€’ \`${name}@${version}\` Read the [release notes β†’](<${url}>)\n`;
+
+ message += `\n\nAlso ${item(extraVerbs)}: ${remainingPackages.length} other packages!`;
+ return message;
+ }
+ }
+}
+
+async function run() {
+ const content = await generateMessage();
+ console.info(content);
+ setOutput('DISCORD_MESSAGE', content);
+}
+
+run();