Create GitHub Discussions #16
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 query = `query($owner: String!, $repo: String!) { | |
repository(owner: $owner, name: $repo) { | |
discussionCategories(first: 10) { | |
nodes { | |
id | |
name | |
} | |
} | |
} | |
}`; | |
const { repository } = await github.graphql(query, { | |
owner: context.repo.owner, | |
repo: context.repo.repo | |
}); | |
let category = repository.discussionCategories.nodes.find(c => c.name === "app-votes"); | |
if (!category) { | |
throw new Error("The 'app-votes' category does not exist. Please create it manually in the repository."); | |
} | |
const { data: files } = await github.rest.pulls.listFiles({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
pull_number: context.issue.number | |
}); | |
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(); | |
const frontmatterMatch = content.match(/---\n([\s\S]*?)\n---/); | |
if (!frontmatterMatch) continue; | |
const frontmatter = frontmatterMatch[1]; | |
const appData = {}; | |
frontmatter.split('\n').forEach(line => { | |
const match = line.match(/^(\w+):\s*(?:"([^"]*)"|'([^']*)'|([^"'\s].*))\s*$/); | |
if (match) { | |
appData[match[1]] = match[2] || match[3] || match[4]; | |
} | |
}); | |
try { | |
if (!appData.name || !appData.author) { | |
console.log(`Skipping file ${file.filename || file.path}: Missing required fields in frontmatter`); | |
continue; | |
} | |
const query = `query($searchQuery: String!) { | |
search(query: $searchQuery, type: DISCUSSION, first: 10) { | |
nodes { | |
... on Discussion { | |
title | |
} | |
} | |
} | |
}`; | |
const searchResult = await github.graphql(query, { | |
searchQuery: `repo:${context.repo.owner}/${context.repo.repo} "${appData.name} by ${appData.author}" in:title` | |
}); | |
const existingDiscussion = searchResult.search.nodes.find(node => | |
node.title === `Vote: ${appData.name} by ${appData.author}` | |
); | |
if (!existingDiscussion) { | |
await github.rest.discussions.create({ | |
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: category.id | |
}); | |
console.log(`Created discussion for ${appData.name}`); | |
} else { | |
console.log(`Discussion already exists for ${appData.name}`); | |
} | |
} catch (error) { | |
console.error(`Error processing ${file.filename || file.path}:`, error); | |
continue; | |
} | |
} | |
- name: Create Discussions for Existing Apps | |
if: github.event_name == 'workflow_dispatch' && github.event.inputs.createForExisting == 'true' | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const query = `query($owner: String!, $repo: String!) { | |
repository(owner: $owner, name: $repo) { | |
discussionCategories(first: 10) { | |
nodes { | |
id | |
name | |
} | |
} | |
} | |
}`; | |
const { repository } = await github.graphql(query, { | |
owner: context.repo.owner, | |
repo: context.repo.repo | |
}); | |
let category = repository.discussionCategories.nodes.find(c => c.name === "app-votes"); | |
if (!category) { | |
throw new Error("The 'app-votes' category does not exist. Please create it manually in the repository."); | |
} | |
const { data: contents } = await github.rest.repos.getContent({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
path: 'src/content/apps' | |
}); | |
for (const file of contents) { | |
if (file.type !== 'file' || file.name.endsWith('_template.md')) continue; | |
const content = Buffer.from((await github.rest.repos.getContent({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
path: file.path | |
})).data.content, 'base64').toString(); | |
const frontmatterMatch = content.match(/---\n([\s\S]*?)\n---/); | |
if (!frontmatterMatch) continue; | |
const frontmatter = frontmatterMatch[1]; | |
const appData = {}; | |
frontmatter.split('\n').forEach(line => { | |
const match = line.match(/^(\w+):\s*(?:"([^"]*)"|'([^']*)'|([^"'\s].*))\s*$/); | |
if (match) { | |
appData[match[1]] = match[2] || match[3] || match[4]; | |
} | |
}); | |
try { | |
if (!appData.name || !appData.author) { | |
console.log(`Skipping file ${file.filename || file.path}: Missing required fields in frontmatter`); | |
continue; | |
} | |
const query = `query($searchQuery: String!) { | |
search(query: $searchQuery, type: DISCUSSION, first: 10) { | |
nodes { | |
... on Discussion { | |
title | |
} | |
} | |
} | |
}`; | |
const searchResult = await github.graphql(query, { | |
searchQuery: `repo:${context.repo.owner}/${context.repo.repo} "${appData.name} by ${appData.author}" in:title` | |
}); | |
const existingDiscussion = searchResult.search.nodes.find(node => | |
node.title === `Vote: ${appData.name} by ${appData.author}` | |
); | |
if (!existingDiscussion) { | |
await github.rest.discussions.create({ | |
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: category.id | |
}); | |
console.log(`Created discussion for ${appData.name}`); | |
} else { | |
console.log(`Discussion already exists for ${appData.name}`); | |
} | |
} catch (error) { | |
console.error(`Error processing ${file.filename || file.path}:`, error); | |
continue; | |
} | |
} |