Create GitHub Discussions #13
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 { data: categories } = await github.rest.repos.listDiscussionCategories({ | |
owner: context.repo.owner, | |
repo: context.repo.repo | |
}); | |
let category = categories.find(c => c.name === "app-votes"); | |
if (!category) { | |
const { data: newCategory } = await github.rest.repos.createDiscussionCategory({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
name: "app-votes", | |
description: "Vote for community apps", | |
format: "discussion" | |
}); | |
category = newCategory; | |
} | |
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 result = await github.graphql(query, { | |
owner: context.repo.owner, | |
repo: context.repo.repo | |
}); | |
let category = result.repository.discussionCategories.nodes.find(c => c.name === "app-votes"); | |
if (!category) { | |
const { data: newCategory } = await github.rest.repos.createDiscussionCategory({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
name: "app-votes", | |
description: "Vote for community apps", | |
format: "discussion" | |
}); | |
category = newCategory; | |
} | |
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; | |
} | |
} |