Create GitHub Discussions #11
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.discussions.listRepoCategories({ | |
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.discussions.createRepoCategory({ | |
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*"?([^"]*)"?$/); | |
if (match) { | |
appData[match[1]] = match[2]; | |
} | |
}); | |
if (!appData.name || !appData.author) { | |
console.log('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}`); | |
} | |
} | |
- 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 { data: categories } = await github.rest.discussions.listRepoCategories({ | |
owner: context.repo.owner, | |
repo: context.repo.repo | |
}); | |
let category = categories.find(c => c.name === "app-votes"); | |
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*"?([^"]*)"?$/); | |
if (match) { | |
appData[match[1]] = match[2]; | |
} | |
}); | |
if (!appData.name || !appData.author) { | |
console.log('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}`); | |
} | |
} |