Create GitHub Discussions #5
This file contains 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 GitHub Discussions | |
on: | |
pull_request: | |
types: [opened] | |
paths: | |
- 'src/content/apps/**' | |
workflow_dispatch: | |
inputs: | |
createForExisting: | |
description: 'Create discussions for existing apps' | |
required: true | |
default: 'true' | |
type: boolean | |
jobs: | |
create-discussion: | |
runs-on: ubuntu-latest | |
permissions: | |
discussions: write | |
pull-requests: read | |
contents: read | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Create Discussion for PR | |
if: github.event_name == 'pull_request' | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const fs = require('fs'); | |
const path = require('path'); | |
async function ensureDiscussionCategory() { | |
// Get all discussion categories | |
const { data: categories } = await github.rest.discussions.listRepoCategories({ | |
owner: context.repo.owner, | |
repo: context.repo.repo | |
}); | |
// Look for "App Voting" category | |
let category = categories.find(c => c.name === "App Voting"); | |
// Create category if it doesn't exist | |
if (!category) { | |
const { data: newCategory } = await github.rest.discussions.createRepoCategory({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
name: "App Voting", | |
description: "Vote for community apps", | |
format: "discussion" | |
}); | |
category = newCategory; | |
} | |
return category.id; | |
} | |
async function createDiscussion(content) { | |
// Parse the frontmatter manually | |
const frontmatterMatch = content.match(/---\n([\s\S]*?)\n---/); | |
if (!frontmatterMatch) return; | |
const frontmatter = frontmatterMatch[1]; | |
// Parse the simple YAML format manually | |
const appData = {}; | |
frontmatter.split('\n').forEach(line => { | |
const match = line.match(/^(\w+):\s*"?([^"]*)"?$/); | |
if (match) { | |
appData[match[1]] = match[2]; | |
} | |
}); | |
if (!appData.name || !appData.author) { | |
console.log('Missing required fields in frontmatter'); | |
return; | |
} | |
// Check if discussion already exists | |
const { data: existingDiscussions } = await github.rest.discussions.listRepositoryDiscussions({ | |
owner: context.repo.owner, | |
repo: context.repo.repo | |
}); | |
const existingDiscussion = existingDiscussions.find(d => | |
d.title === `Vote: ${appData.name} by ${appData.author}` | |
); | |
const categoryId = await ensureDiscussionCategory(); | |
if (!existingDiscussion) { | |
// Create a discussion with the retrieved category ID | |
await github.rest.discussions.createForRepo({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
title: `Vote: ${appData.name} by ${appData.author}`, | |
body: `🗳️ **Vote for this app by giving it a 👍 reaction!**\n\n${appData.description}`, | |
category_id: categoryId | |
}); | |
console.log(`Created discussion for ${appData.name}`); | |
} else { | |
console.log(`Discussion already exists for ${appData.name}`); | |
} | |
} | |
// Get the changed files from the PR | |
const { data: files } = await github.rest.pulls.listFiles({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
pull_number: context.issue.number | |
}); | |
// Find new app submissions | |
const appFiles = files.filter(file => | |
file.status === 'added' && | |
file.filename.startsWith('src/content/apps/') && | |
!file.filename.endsWith('_template.md') | |
); | |
for (const file of appFiles) { | |
const content = Buffer.from((await github.rest.repos.getContent({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
path: file.filename, | |
ref: context.payload.pull_request.head.sha | |
})).data.content, 'base64').toString(); | |
await createDiscussion(content); | |
} | |
- name: Create Discussions for Existing Apps | |
if: github.event_name == 'workflow_dispatch' && inputs.createForExisting | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const fs = require('fs'); | |
const path = require('path'); | |
async function ensureDiscussionCategory() { | |
const { data: categories } = await github.rest.discussions.listRepoCategories({ | |
owner: context.repo.owner, | |
repo: context.repo.repo | |
}); | |
let category = categories.find(c => c.name === "App Voting"); | |
if (!category) { | |
const { data: newCategory } = await github.rest.discussions.createRepoCategory({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
name: "App Voting", | |
description: "Vote for community apps", | |
format: "discussion" | |
}); | |
category = newCategory; | |
} | |
return category.id; | |
} | |
async function createDiscussion(content) { | |
// Parse the frontmatter manually | |
const frontmatterMatch = content.match(/---\n([\s\S]*?)\n---/); | |
if (!frontmatterMatch) return; | |
const frontmatter = frontmatterMatch[1]; | |
// Parse the simple YAML format manually | |
const appData = {}; | |
frontmatter.split('\n').forEach(line => { | |
const match = line.match(/^(\w+):\s*"?([^"]*)"?$/); | |
if (match) { | |
appData[match[1]] = match[2]; | |
} | |
}); | |
if (!appData.name || !appData.author) { | |
console.log('Missing required fields in frontmatter'); | |
return; | |
} | |
// Check if discussion already exists | |
const { data: existingDiscussions } = await github.rest.discussions.listRepositoryDiscussions({ | |
owner: context.repo.owner, | |
repo: context.repo.repo | |
}); | |
const existingDiscussion = existingDiscussions.find(d => | |
d.title === `Vote: ${appData.name} by ${appData.author}` | |
); | |
const categoryId = await ensureDiscussionCategory(); | |
if (!existingDiscussion) { | |
// Create a discussion with the retrieved category ID | |
await github.rest.discussions.createForRepo({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
title: `Vote: ${appData.name} by ${appData.author}`, | |
body: `🗳️ **Vote for this app by giving it a 👍 reaction!**\n\n${appData.description}`, | |
category_id: categoryId | |
}); | |
console.log(`Created discussion for ${appData.name}`); | |
} else { | |
console.log(`Discussion already exists for ${appData.name}`); | |
} | |
} | |
// Get all existing app files | |
const appsDir = 'src/content/apps'; | |
const files = fs.readdirSync(appsDir) | |
.filter(file => file.endsWith('.md') && file !== '_template.md'); | |
for (const file of files) { | |
const content = fs.readFileSync(path.join(appsDir, file), 'utf8'); | |
await createDiscussion(content); | |
} |