Label issues in release #4
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: Label issues in release | |
on: | |
workflow_dispatch: | |
inputs: | |
tag: | |
description: 'Release tag' | |
required: true | |
type: string | |
label_name: | |
description: 'Name of the label' | |
required: true | |
type: string | |
label_description: | |
description: 'Description of the label' | |
default: '' | |
required: false | |
type: string | |
label_color: | |
description: 'A color of the added label' | |
default: 'FFFFFF' | |
required: false | |
type: string | |
workflow_call: | |
inputs: | |
tag: | |
description: 'Release tag' | |
required: true | |
type: string | |
label_name: | |
description: 'Name of the label' | |
required: true | |
type: string | |
label_description: | |
description: 'Description of the label' | |
default: '' | |
required: false | |
type: string | |
label_color: | |
description: 'A color of the added label' | |
default: 'FFFFFF' | |
required: false | |
type: string | |
outputs: | |
issues: | |
description: "JSON encoded list of issues linked to commits in the release" | |
value: ${{ jobs.run.outputs.issues }} | |
concurrency: | |
group: ${{ github.workflow }}-${{ github.ref }} | |
cancel-in-progress: true | |
jobs: | |
run: | |
name: Run | |
runs-on: ubuntu-latest | |
outputs: | |
issues: ${{ steps.linked_issues.outputs.result }} | |
steps: | |
- name: Getting tags of the two latestest releases | |
id: tags | |
uses: actions/github-script@v6 | |
env: | |
TAG: ${{ inputs.tag }} | |
with: | |
script: | | |
const { repository: { releases: { nodes: releases } } } = await github.graphql(` | |
query ($owner: String!, $repo: String!) { | |
repository(owner: $owner, name: $repo) { | |
releases(first: 10, orderBy: { field: CREATED_AT, direction: DESC }) { | |
nodes { | |
name | |
tagName | |
tagCommit { | |
oid | |
} | |
isPrerelease | |
isDraft | |
publishedAt | |
} | |
} | |
} | |
} | |
`, | |
{ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
} | |
); | |
if (releases[0].tagName !== process.env.TAG) { | |
core.setFailed(`Release with tag ${ process.env.TAG } is not latest one.`); | |
return; | |
} | |
const latestTag = process.env.TAG; | |
const [ { tagName: previousTag } ] = releases | |
.slice(1) | |
.filter(({ isDraft }) => !isDraft); | |
core.info('Found following tags:'); | |
core.info(` latest: ${ latestTag }`); | |
core.info(` second latest: ${ previousTag }`); | |
core.setOutput('latest', latestTag); | |
core.setOutput('previous', previousTag); | |
- name: Looking for commits between two releases | |
id: commits | |
uses: actions/github-script@v6 | |
env: | |
PREVIOUS_TAG: ${{ steps.tags.outputs.previous }} | |
LATEST_TAG: ${{ steps.tags.outputs.latest }} | |
with: | |
script: | | |
const { data: { commits: commitsInRelease } } = await github.request('GET /repos/{owner}/{repo}/compare/{basehead}', { | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
basehead: `${ process.env.PREVIOUS_TAG }...${ process.env.LATEST_TAG }`, | |
}); | |
if (commitsInRelease.length === 0) { | |
core.notice(`No commits found between ${ process.env.PREVIOUS_TAG } and ${ process.env.LATEST_TAG }`); | |
return []; | |
} | |
const commits = commitsInRelease.map(({ sha }) => sha); | |
core.startGroup(`Found ${ commits.length } commits`); | |
commits.forEach((sha) => { | |
core.info(sha); | |
}) | |
core.endGroup(); | |
return commits; | |
- name: Looking for issues linked to commits | |
id: linked_issues | |
uses: actions/github-script@v6 | |
env: | |
COMMITS: ${{ steps.commits.outputs.result }} | |
with: | |
script: | | |
const commits = JSON.parse(process.env.COMMITS); | |
if (commits.length === 0) { | |
return []; | |
} | |
const map = {}; | |
core.startGroup(`Looking for linked issues`); | |
for (const sha of commits) { | |
const result = await getLinkedIssuesForCommitPR(sha); | |
result.forEach((issue) => { | |
map[issue] = issue; | |
}); | |
} | |
core.endGroup(); | |
const issues = Object.values(map); | |
if (issues.length > 0) { | |
core.startGroup(`Found ${ issues.length } unique issues`); | |
issues.sort().forEach((issue) => { | |
core.info(`#${ issue } - https://github.com/${ context.repo.owner }/${ context.repo.repo }/issues/${ issue }`); | |
}) | |
core.endGroup(); | |
} else { | |
core.notice('No linked issues found.'); | |
} | |
return issues; | |
async function getLinkedIssuesForCommitPR(sha) { | |
core.info(`Fetching issues for commit: ${ sha }`); | |
const response = await github.graphql(` | |
query ($owner: String!, $repo: String!, $sha: GitObjectID!) { | |
repository(owner: $owner, name: $repo) { | |
object(oid: $sha) { | |
... on Commit { | |
associatedPullRequests(first: 10) { | |
nodes { | |
number | |
title | |
state | |
merged | |
closingIssuesReferences(first: 10) { | |
nodes { | |
number | |
title | |
closed | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
`, { | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
sha, | |
}); | |
if (!response) { | |
core.info('Nothing has found.'); | |
return []; | |
} | |
const { repository: { object: { associatedPullRequests } } } = response; | |
const issues = associatedPullRequests | |
.nodes | |
.map(({ closingIssuesReferences: { nodes: issues } }) => issues.map(({ number }) => number)) | |
.flat(); | |
core.info(`Found following issues: ${ issues.join(', ') || '-' }\n`); | |
return issues; | |
} | |
- name: Creating label | |
id: label_creating | |
uses: actions/github-script@v6 | |
env: | |
LABEL_NAME: ${{ inputs.label_name }} | |
LABEL_COLOR: ${{ inputs.label_color }} | |
LABEL_DESCRIPTION: ${{ inputs.label_description }} | |
with: | |
script: | | |
try { | |
const result = await github.request('POST /repos/{owner}/{repo}/labels', { | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
name: process.env.LABEL_NAME, | |
color: process.env.LABEL_COLOR, | |
description: process.env.LABEL_DESCRIPTION, | |
}); | |
core.info('Label was created.'); | |
} catch (error) { | |
if (error.status === 422) { | |
core.info('Label already exist.'); | |
} else { | |
core.setFailed(error.message); | |
} | |
} | |
- name: Adding label to issues | |
id: labeling_issues | |
uses: actions/github-script@v6 | |
env: | |
LABEL_NAME: ${{ inputs.label_name }} | |
ISSUES: ${{ steps.linked_issues.outputs.result }} | |
with: | |
script: | | |
const issues = JSON.parse(process.env.ISSUES); | |
if (issues.length === 0) { | |
core.notice('No issues has found. Nothing to label.'); | |
return; | |
} | |
for (const issue of issues) { | |
core.info(`Adding label to the issue #${ issue }...`); | |
await addLabelToIssue(issue, process.env.LABEL_NAME); | |
core.info('Done.\n'); | |
} | |
async function addLabelToIssue(issue, label) { | |
return await github.request('POST /repos/{owner}/{repo}/issues/{issue_number}/labels', { | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: issue, | |
labels: [ label ], | |
}); | |
} |