Skip to content

Instantly share code, notes, and snippets.

@darcyclarke
Last active November 29, 2023 19:57
Show Gist options
  • Save darcyclarke/4e993311cbf4b252f6d8b205d880fc34 to your computer and use it in GitHub Desktop.
Save darcyclarke/4e993311cbf4b252f6d8b205d880fc34 to your computer and use it in GitHub Desktop.
Managing Releases + Version Labels + Issues w/ Version Fields
# .github/ISSUE_TEMPLATE/bug.yml
# A basic bug template...
# NOTE: `id` must be set for the `version` & correspond to...
# whatever the value you reference later when you parse an issue
name: Bug w/ Input
description: Something bad happened
labels: [bug]
body:
- type: input
id: version
attributes:
label: What version are you using?
validations:
required: true
# .github/workflows/create-release-version-labels.yml
name: Create New Version Label & Update Issue Template
on:
release:
types: published
jobs:
label:
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
steps:
- name: Create label for new version
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Creating label for new release: ${{ github.event.release.tag_name }}"
gh label create "v${{ github.event.release.tag_name }}" --repo="${{ github.repository }}" --color="#000000" --description="Release ${{ github.event.release.tag_name }}"
# .github/workflows/label-issues.yml
name: Add Version Label to Issue
on:
issues:
types: opened
jobs:
update:
runs-on: ubuntu-latest
permissions:
issues: write
contents: read
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Parse version based on issue template
id: parse
uses: stefanbuck/github-issue-parser@v3
with:
template-path: .github/ISSUE_TEMPLATE/bug.yml
- name: Add version label to issue
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.parse.outputs.issueparser_version }}
run: |
echo "Adding version label to issue: $VERSION"
gh issue edit ${{ github.event.issue.number }} --add-label $VERSION --repo ${{ github.repository }}
# .github/workflows/passively-check-issues-version-labels.yml
name: Check Bugs for Missing Version Labels
on:
schedule:
- cron: "0 0 * * *"
jobs:
check-bug-labels:
runs-on: ubuntu-latest
permissions:
issues: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: Install dependencies
run: bun install js-yaml semver stefanbuck/github-issue-parser
- name: Find issues with missing version labels
uses: actions/github-script@v7
id: find
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs')
const template_path = '.github/ISSUE_TEMPLATE/bug-input.yml'
const template = fs.readFileSync(template_path, 'utf8')
// parser requires NODE_ENV=test to not run immediately
process.env.NODE_ENV = 'test'
// parser requires a stubbed context
const _context = {}
// parser requires a stubbed fs
const _fs = {
readFileSync: () => { return template },
writeFileSync: () => {},
}
// parser requires a stubbed actions core
const _core = {
getInput: (k) => k === 'template-path' ? template_path : '',
setOutput: (k, v) => { k.startsWith('issueparser_') ? _context[k.replace('issueparser_', '')] = v : 1 },
setFailed: (message) => { throw new Error(message) },
debug: (message) => { console.log(message) }
}
const yaml = require('js-yaml')
const semver = require('semver')
const { run: parse } = require('github-issue-parser')
const ctx = { owner: context.repo.owner, repo: context.repo.repo, }
const releases = await github.paginate(github.rest.repos.listReleases, ctx).then(releases => releases.map(release => `v${semver.coerce(release.tag_name)}`))
const labels = await github.paginate(github.rest.issues.listLabelsForRepo, ctx).then(labels => labels.map(label => label.name).filter(label => label.match(/v\d+\.\d+\.\d+/)))
const issues = await github.paginate(github.rest.issues.listForRepo, { ...ctx, ...{ labels: 'bug', state: 'open' }}).then(issues => issues)
const missing = issues.filter(issue => !issue.labels.filter(label => label.name.match(/v\d+\.\d+\.\d+/)))
const create = releases.filter(release => !labels.includes(release))
// add lables to issues missing versions
for(let i=0; i < missing.length; i++) {
const issue = missing[i]
await parse(process.env, issue.body, _fs, _core) // sets `_context.version` along with other outputs
const version = semver.coerce(_context.version)
if (version) {
const label = `v${version}`
if(!releases.includes(label) && !create.includes(label)) {
create.push(label)
}
await github.rest.issues.addLabels({ ...ctx, ...{ issue_number: issue.number, labels: [label] }})
console.log(`Added label ${label} to issue #${issue.number}`)
}
}
// create labels for missing releases
for (let j=0; create.length; j++) {
const label = create[j]
if (label) {
await github.rest.issues.createLabel({ ...ctx, ...{ name: label, description: `Release ${label}`, color: '000000' }})
console.log(`Created label ${label}`)
}
}
console.log(`Added new labels to ${missing.length} issues`)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment