Skip to content

Commit

Permalink
Github changelog
Browse files Browse the repository at this point in the history
  • Loading branch information
shvgn authored and konstantin-axenov committed Nov 15, 2021
1 parent 1ad707e commit f6c1b37
Show file tree
Hide file tree
Showing 7 changed files with 378 additions and 0 deletions.
63 changes: 63 additions & 0 deletions .github/actions/milestone-changelog/action.yml
@@ -0,0 +1,63 @@
name: Milestone Changelog
description: Re-generates changelog for given milestone
inputs:
milestone:
description: Milestone object containing number and title
required: true
token:
description: Github token
required: true
runs:
using: "composite"
steps:
- name: Parse input
id: args
shell: bash
run: |
echo "::set-output name=milestone_title::${{ fromJSON(inputs.milestone).title }}"
echo "::set-output name=milestone_number::${{ fromJSON(inputs.milestone).number }}"
# The gh utility has a shortcut to filter merged PRs by milestone, while it would require
# multiple calls in JS rest client
- name: Find Merged Pull Requsts
id: merged_milestone
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.token }}
run: |
prs="$(gh pr list \
--repo '${{ github.repository }}' \
--search 'milestone:${{ steps.args.outputs.milestone_title }}' \
--state merged \
--json number,url,title,body,state,milestone)"
echo "::set-output name=prs::${prs}"
- name: Collect Changelog
id: changelog
uses: deckhouse/changelog-action@main
with:
token: ${{ inputs.token }}
pull_requests: ${{ steps.merged_milestone.outputs.prs }}

- name: Write Changelog File
id: file
shell: bash
run: |
mkdir -p ./CHANGELOG
filename='./CHANGELOG/CHANGELOG-${{ steps.args.outputs.milestone_title }}.yml'
cat > "$filename" <<EOBODYINACTION
${{ steps.changelog.outputs.yaml }}
EOBODYINACTION
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3.10.1
with:
commit-message: Re-generate changelog
base: main
branch: changelog/${{ steps.args.outputs.milestone_title }}
milestone: ${{ steps.args.outputs.milestone_number }}
title: Changelog ${{ steps.args.outputs.milestone_title }}
body: ${{ steps.changelog.outputs.markdown }}
labels: changelog, auto
token: ${{ inputs.token }}
delete-branch: true
72 changes: 72 additions & 0 deletions .github/pull_request_template.md
@@ -0,0 +1,72 @@
## Description
<!---
Describe your changes in detail.
Please let users know if your feature influences critical cluster components
(restarts of ingress-controllers, control-plane, Prometheus, etc).
-->

## Why we need it and what problem does it solve?
<!---
This is the most important paragraph.
You have to describe the main goal of your feature.
If it fixes an issue, place a link to the issue here.
If it fixes an obvious bug, please tell users about the impact and effect of the problem.
-->

## Changelog entries
<!---
Describe the changes so they will be included in a release changelog.
Find examples and documentation below.
-->

```changes
module: <kebab-case>
type: fix | feature
description: <what effectively changes>
note: <what to expect>
```

<!---
ABOUT CHANGES BLOCK
"Changes" block contains a list of YAML documents. It describes a changelog
entry that is collected to a release changelog.
Fields:
module
Required. Affected module in kebab case, e.g. "node-manager".
type
Required. The change type: only "fix" and "feature" supported.
description
Optional. The changelog entry. Omit to use pull request title.
note
Optional. Any notable detail, e.g. expected restarts, downtime, config changes, migrations, etc.
Since the syntax is YAML, `note` may contain multi-line text.
There can be multiple docs in single `changes` block, and multiple `changes`
blocks in the PR body.
Example:
```changes
module: node-manager
type: fix
description: "Nodes with outdated manifests are no longer provisioned on *InstanceClass update."
note: |
Expect nodes of "Cloud" type to restart.
Node checksum calculation is fixes as well as a race condition during
the machines (MCM) rendering which caused outdated nodes to spawn.
---
module: cloud-provider-aws
type: feature
description: "Node restarts can be avoided by pinning a checksum to a node group in config values."
note: Recommended to use as a last resort.
```
-->
39 changes: 39 additions & 0 deletions .github/scripts/changelog-command-validate.js
@@ -0,0 +1,39 @@
//@ts-check

// Gets issue in context, ensures it is PR with milestone, and returns the milestone
module.exports = async ({ github, core, context }) => {
const { status, data: issue } = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});

if (status != 200) {
core.warning(`GET issue error: status ${status}`);
return;
}

const whatsWrong = validate(issue);
if (whatsWrong) {
core.warning(whatsWrong);
return;
}

return issue.milestone && issue.milestone.status == "open";
};

function validate(issue) {
if (!issue.pull_request) {
return "Not pull request, skip.";
}

if (!issue.milestone) {
return "No milestone, skip.";
}

if (issue.milestone.state != "open") {
return `Milestone ${issue.milestone.title} is not open.`;
}

return "";
}
34 changes: 34 additions & 0 deletions .github/workflows/changelog-command-dispatch.yml
@@ -0,0 +1,34 @@
name: Changelog Command Dispatch
on:
issue_comment:
types: [created]
jobs:
dispatch:
name: Dispatch Changelog Event
runs-on: ubuntu-latest
if: |
github.event.issue.pull_request &&
github.event.issue.milestone.state == 'open' &&
contains(github.event.issue.labels.*.name, 'changelog') &&
contains(github.event.issue.labels.*.name, 'auto')
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Find milestone
id: milestone
uses: actions/github-script@v5
with:
result-encoding: json
script: |
const validate = require('./.github/scripts/changelog-command-validate.js')
return await validate({ github, core, context })
- name: Slash Command Dispatch
if: steps.milestone.outputs.result
uses: peter-evans/slash-command-dispatch@v2
with:
token: ${{ secrets.CHANGELOG_ACCESS_TOKEN }}
commands: changelog
dispatch-type: repository
issue-type: pull-request
25 changes: 25 additions & 0 deletions .github/workflows/changelog-command.yml
@@ -0,0 +1,25 @@
name: Changelog Command
on:
repository_dispatch:
types: [changelog-command]
jobs:
changelog:
name: Milestone Changelog
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Upgrade changelog
uses: ./.github/actions/milestone-changelog
with:
milestone: ${{ toJSON( github.event.client_payload.pull_request.milestone ) }}
token: ${{ secrets.CHANGELOG_ACCESS_TOKEN }}

- name: Add reaction for success
uses: peter-evans/create-or-update-comment@v1
with:
token: ${{ secrets.CHANGELOG_ACCESS_TOKEN }}
repository: ${{ github.event.client_payload.github.payload.repository.full_name }}
comment-id: ${{ github.event.client_payload.github.payload.comment.id }}
reaction-type: hooray
41 changes: 41 additions & 0 deletions .github/workflows/changelog.md
@@ -0,0 +1,41 @@
# Changelog Workflows

## What they do

Release changelog is generated on milestone assignment or PR merging or by calling command
`/changelog` in 'changelog PR' comments.

### Requirements

Changelog generation relies on repo secret `CHANGELOG_ACCESS_TOKEN` which must
have `workflow` permission.

### Code

The changelog toolchain consists of these files

```
.github/
actions/
milestone-changelog/
action.yml
scripts/
changelog-command-validate.js
workflows/
changelog-command-dispatch.yml
changelog-command.yml
changelog.yml
```

**Milestone changelog action** creates or updates a changelog PR for a given
*open* milesone. It is used by two workflows.

**Changelog workflow** re-generates all changelog PRs on push to the main
branch.

**Changelog command dispatch** handles `/changelog` command and dispathes the
`changelog-command` repository event.

**Changelog command** handles the `changelog-command` in the contet of a
changelog pull request, and calls the action to update the PR in-place with
fresh changelog.
104 changes: 104 additions & 0 deletions .github/workflows/changelog.yml
@@ -0,0 +1,104 @@
name: Changelog
on:
pull_request:
types:
- "closed"
- "edited"
branches:
- "main"
issues:
types:
- "milestoned"
# We don't track "demilestoned" event type. If we did, changing a milestone would always
# trigger duplicating workflows one of which would fail due to concurrent updates of the same
# changelog branch. We hope, that milestones change to other milestones, and are not removed
# at all. To update changelog, one should call `/changelog` command in a changelog PR.
# - "demilestoned"
jobs:
filter:
name: Filter Issues
runs-on: ubuntu-latest
# In all cases:
# skipping the changelog PR itself, provided it is detected by label
#
# Conditions
# 1. pull_request is edited or merged
# OR
# 2. milestone is set/removed (via issue api), we check the related PR in step
if: |
(
github.event.pull_request &&
github.event.pull_request.state == 'closed' &&
github.event.pull_request.merged &&
github.event.pull_request.milestone.state == 'open' &&
!contains(github.event.issue.labels.*.name, 'changelog')
) || (
(github.event.action == 'milestoned' || github.event.action == 'demilestoned') &&
github.event.issue.pull_request &&
!contains(github.event.issue.labels.*.name, 'changelog')
)
steps:
- name: Check PR
id: pr
uses: actions/github-script@v5
with:
script: |
// 'demilestoned' should also be checked if used as trigger
if (context.eventName === 'milestoned') {
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
if (pr.state !== 'closed' || !pr.merged) {
// Skip this PR, the change is not applied yet
return;
}
}
core.setOutput("ok", "ok")
outputs:
ok: steps.pr.outputs.ok

milestones:
if: needs.filter.outputs.ok == "ok"
name: Open Milestones
runs-on: ubuntu-latest
needs: filter
steps:
- name: Find Open Milestones
id: milestones
env:
GITHUB_TOKEN: ${{ secrets.CHANGELOG_ACCESS_TOKEN }}
run: |
# We expect the number of simultaneously open milestones will not exceed 10.
# https://docs.github.com/en/rest/reference/issues#milestones
milestones="$(gh api 'repos/${{ github.repository }}/milestones?state=open&per_page=100'
count="$(echo $milestones | jq '. | length')"
echo "::set-output name=list::${milestones}"
echo "::set-output name=count::${count}"
outputs:
list: ${{ steps.milestones.outputs.list }}
count: ${{ steps.milestones.outputs.count }}

chanegelogs:
if: needs.milestones.outputs.count > 0
name: Changelog ${{ matrix.milestone.title }}
runs-on: ubuntu-latest
needs: milestones
strategy:
matrix:
milestone: ${{ fromJSON( needs.milestones.outputs.list ) }}
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Create changelog
uses: ./.github/actions/milestone-changelog
with:
milestone: ${{ toJSON( matrix.milestone ) }}
token: ${{ secrets.CHANGELOG_ACCESS_TOKEN }}

0 comments on commit f6c1b37

Please sign in to comment.