mirror of https://github.com/theNewDynamic/gohugo-theme-ananke.git

Patrick Kollitsch
29.37.2024 819f6730370988d3e0524a4f000550d153b2f534
build(feat): add discord notification for new releases

Signed-off-by: Patrick Kollitsch <patrick@davids-neighbour.com>
1 files added
3 files modified
170 ■■■■■ changed files
.github/scripts/posterboy.mjs 162 ●●●●● patch | view | raw | blame | history
.gitignore 2 ●●●●● patch | view | raw | blame | history
package-lock.json 3 ●●●● patch | view | raw | blame | history
package.json 3 ●●●● patch | view | raw | blame | history
.github/scripts/posterboy.mjs
New file
@@ -0,0 +1,162 @@
import { readFile, writeFile, access, mkdir } from 'node:fs/promises';
import path from 'node:path';
import fs from 'node:fs';
import dotenv from 'dotenv';
import { homedir } from 'node:os';
const userHomeDir = homedir();
// Load .env files
const GLOBAL_ENV_PATH = path.join(userHomeDir, '.env');
const LOCAL_ENV_PATH = path.resolve('.env');
/**
 * Load environment variables from a file if it exists.
 * @param {string} filePath
 * @returns {object} Parsed environment variables
 */
function loadEnvFile(filePath) {
  if (fs.existsSync(filePath)) {
    return dotenv.parse(fs.readFileSync(filePath));
  }
  return {};
}
// Merge global and local .env configurations
const globalEnv = loadEnvFile(GLOBAL_ENV_PATH);
const localEnv = loadEnvFile(LOCAL_ENV_PATH);
process.env = { ...globalEnv, ...process.env, ...localEnv };
// Configurable values
const DISCORD_WEBHOOK = process.env.DISCORD_WEBHOOK || '';
const GITHUB_DEV_TOKEN = process.env.GITHUB_DEV_TOKEN || '';
const GITHUB_REPO = process.env.GITHUB_REPO || 'theNewDynamic/gohugo-theme-ananke';
const DEFAULT_MESSAGE_TEMPLATE = 'New release: {{tag_name}} - {{html_url}}';
const MESSAGE_TEMPLATE = process.env.MESSAGE_TEMPLATE || DEFAULT_MESSAGE_TEMPLATE;
const CACHE_DIR = './cache';
const CACHE_FILE = 'github-releases.json';
const CACHE_FILE_PATH = path.join(CACHE_DIR, CACHE_FILE);
/**
 * Ensures the cache directory exists, creating it if necessary.
 * @returns {Promise<void>}
 */
async function ensureCacheDirectory() {
  try {
    await access(CACHE_DIR);
  } catch {
    try {
      await mkdir(CACHE_DIR, { recursive: true });
    } catch (err) {
      console.error(`Failed to create cache directory: ${err.message}`);
      process.exit(1);
    }
  }
}
/**
 * Reads the cache file or returns an empty array if not found.
 * @returns {Promise<string[]>}
 */
async function readCache() {
  try {
    const data = await readFile(CACHE_FILE_PATH, 'utf8');
    return JSON.parse(data) || [];
  } catch {
    return [];
  }
}
/**
 * Writes data to the cache file.
 * @param {string[]} data
 */
async function writeCache(data) {
  await writeFile(CACHE_FILE_PATH, JSON.stringify(data, null, 2));
}
/**
 * Fetches the latest release from the GitHub REST API.
 * @returns {Promise<{ tag_name: string, html_url: string } | null>}
 */
async function fetchLatestRelease() {
  try {
    const response = await fetch('https://api.github.com/repos/' + GITHUB_REPO + '/releases', {
      headers: {
        Authorization: `token ${GITHUB_DEV_TOKEN}`,
      },
    });
    if (!response.ok) {
      throw new Error(`GitHub API request failed: ${response.statusText}`);
    }
    const releases = await response.json();
    if (!Array.isArray(releases) || releases.length === 0) {
      console.log('No releases found.');
      return null;
    }
    return releases[0];
  } catch (err) {
    console.error('Failed to fetch releases:', err.message);
    return null;
  }
}
/**
 * Posts a message to Discord using a webhook.
 * @param {string} message
 */
async function postToDiscord(message) {
  try {
    const response = await fetch(DISCORD_WEBHOOK, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ content: message }),
    });
    if (!response.ok) {
      throw new Error(`Failed to post to Discord: ${response.statusText}`);
    }
    console.log('Posted to Discord successfully.');
  } catch (err) {
    console.error('Failed to post to Discord:', err.message);
  }
}
/**
 * Formats the release message using the template.
 * @param {Record<string, string>} releaseData
 * @returns {string}
 */
function formatMessage(releaseData) {
  return MESSAGE_TEMPLATE.replace(/{{\s*(\w+)\s*}}/g, (_, key) => releaseData[key] || '');
}
/**
 * Main function to fetch the latest GitHub release and post it to Discord.
 */
async function main() {
  try {
    await ensureCacheDirectory();
    const cachedIds = await readCache();
    const latestRelease = await fetchLatestRelease();
    if (latestRelease && !cachedIds.includes(latestRelease.tag_name)) {
      const message = formatMessage(latestRelease);
      await postToDiscord(message);
      cachedIds.push(latestRelease.tag_name);
      await writeCache(cachedIds);
    } else {
      console.log('No new releases to post.');
    }
  } catch (err) {
    console.error('Error:', err.message);
  }
}
main();
.gitignore
@@ -1,6 +1,7 @@
# OS
.DS_Store
Thumbs.db
.env
# IDEs
.buildpath
@@ -26,6 +27,7 @@
/src/client.config.json
/styleguide/
/docs/
cache
/junit.xml
partials/structure/stylesheet.html
package-lock.json
@@ -17,7 +17,8 @@
      "devDependencies": {
        "@davidsneighbour/markdownlint-config": "^2024.4.8",
        "@davidsneighbour/release-config": "2024.4.8",
        "@davidsneighbour/tools": "2024.4.8"
        "@davidsneighbour/tools": "2024.4.8",
        "dotenv": "^16.4.5"
      }
    },
    "node_modules/@azu/format-text": {
package.json
@@ -40,7 +40,8 @@
  "devDependencies": {
    "@davidsneighbour/markdownlint-config": "^2024.4.8",
    "@davidsneighbour/release-config": "2024.4.8",
    "@davidsneighbour/tools": "2024.4.8"
    "@davidsneighbour/tools": "2024.4.8",
    "dotenv": "^16.4.5"
  },
  "scripts": {
    "deploy": "cd exampleSite; hugo;",