From 2506863aacf0302079a5f744672dbbc146603517 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 20 Jul 2022 10:07:43 +1000 Subject: [PATCH] workflows: add more automation for changelogs --- .github/workflows/changelog-check.yml | 21 ++++ .github/workflows/dependabot-changelog.yml | 35 ++++++ .github/workflows/generate-changelog.yml | 28 +++++ .github/workflows/milestones.yml | 25 ++++ CHANGELOG.md | 1 + docs/changelog-process.md | 96 +++++++++++++++ go.mod | 6 +- go.sum | 7 ++ scripts/changelog.tmpl | 39 ++++++ scripts/generate-changelog.sh | 53 ++++++++ scripts/release-note.tmpl | 3 + tools.go | 22 ++++ tools/changelog-check/main.go | 134 +++++++++++++++++++++ 13 files changed, 469 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/changelog-check.yml create mode 100644 .github/workflows/dependabot-changelog.yml create mode 100644 .github/workflows/generate-changelog.yml create mode 100644 .github/workflows/milestones.yml create mode 100644 CHANGELOG.md create mode 100644 docs/changelog-process.md create mode 100644 scripts/changelog.tmpl create mode 100755 scripts/generate-changelog.sh create mode 100644 scripts/release-note.tmpl create mode 100644 tools.go create mode 100644 tools/changelog-check/main.go diff --git a/.github/workflows/changelog-check.yml b/.github/workflows/changelog-check.yml new file mode 100644 index 00000000000..a3a88376ab9 --- /dev/null +++ b/.github/workflows/changelog-check.yml @@ -0,0 +1,21 @@ +name: Changelog check +on: [pull_request_target] + +jobs: + changelog-check: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v3 + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ^1.17 + - run: make tools + - run: go run tools/cmd/changelog-check/main.go ${{ github.event.pull_request.number }} + env: + GITHUB_OWNER: cloudflare + GITHUB_REPO: cloudflare-go + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/dependabot-changelog.yml b/.github/workflows/dependabot-changelog.yml new file mode 100644 index 00000000000..30656bef4ce --- /dev/null +++ b/.github/workflows/dependabot-changelog.yml @@ -0,0 +1,35 @@ +name: Add CHANGELOG for dependabot changes +on: pull_request_target +permissions: + pull-requests: write + issues: write + repository-projects: write + contents: write +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }} + steps: + - name: Fetch dependabot metadata + id: dependabot-metadata + uses: dependabot/fetch-metadata@v1.3.3 + - uses: actions/checkout@v3 + - run: | + gh pr checkout $PR_URL + cat << EOF > .changelog/$PR_NUMBER.txt + \`\`\`release-note:dependency + provider: bumps $DEP_NAME from $DEP_PREV_VERSION to $DEP_NEXT_VERSION + \`\`\` + EOF + git config user.name github-actions[bot] + git config user.email github-actions[bot]@users.noreply.github.com + git add .changelog/$PR_NUMBER.txt + git commit -m "add CHANGELOG for #$PR_NUMBER" + git push + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_URL: ${{ github.event.pull_request.html_url }} + PR_NUMBER: ${{ github.event.pull_request.number }} + DEP_NAME: ${{ steps.dependabot-metadata.outputs.dependency-names }} + DEP_PREV_VERSION: ${{ steps.dependabot-metadata.outputs.previous-version }} + DEP_NEXT_VERSION: ${{ steps.dependabot-metadata.outputs.new-version }} diff --git a/.github/workflows/generate-changelog.yml b/.github/workflows/generate-changelog.yml new file mode 100644 index 00000000000..acd2df2b5b0 --- /dev/null +++ b/.github/workflows/generate-changelog.yml @@ -0,0 +1,28 @@ +name: Generate CHANGELOG +on: + pull_request_target: + types: [closed] + workflow_dispatch: +jobs: + GenerateChangelog: + if: github.event.pull_request.merged || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - run: cd tools && go install github.com/hashicorp/go-changelog/cmd/changelog-build + - run: ./scripts/generate-changelog.sh + - run: | + if [[ `git status --porcelain` ]]; then + if ${{github.event_name == 'workflow_dispatch'}}; then + MSG="Update CHANGELOG.md (Manual Trigger)" + else + MSG="Update CHANGELOG.md for #${{ github.event.pull_request.number }}" + fi + git config --local user.email changelogbot@cloudflare.com + git config --local user.name changelogbot + git add CHANGELOG.md + git commit -m "$MSG" + git push + fi diff --git a/.github/workflows/milestones.yml b/.github/workflows/milestones.yml new file mode 100644 index 00000000000..2c04450b99e --- /dev/null +++ b/.github/workflows/milestones.yml @@ -0,0 +1,25 @@ +on: + pull_request_target: + types: [closed] +name: Add merged PR and linked issues to current milestone of target branch +jobs: + add-merged-to-current-milestone: + if: github.event.pull_request.merged + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.base.ref }} + - id: get-current-milestone + run: | + echo ::set-output name=current_milestone::v$(head -1 CHANGELOG.md | cut -d " " -f 2) + - run: echo ${{ steps.get-current-milestone.outputs.current_milestone }} + - id: get-milestone-id + run: | + echo ::set-output name=milestone_id::$(curl -H "Authorization: Bearer ${{secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/milestones | jq 'map(select(.title == "${{ steps.get-current-milestone.outputs.current_milestone }}"))[0].number') + - run: echo ${{ steps.get-milestone-id.outputs.milestone_id }} + - uses: breathingdust/current-milestone-action@v4 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + pull_number: ${{ github.event.pull_request.number }} + milestone_number: ${{ steps.get-milestone-id.outputs.milestone_id }} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000000..b190dbe8c6f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +0.46.0 (Unreleased) diff --git a/docs/changelog-process.md b/docs/changelog-process.md new file mode 100644 index 00000000000..33bc59add7f --- /dev/null +++ b/docs/changelog-process.md @@ -0,0 +1,96 @@ +## Changelog Process + +We use the [go-changelog](https://github.com/hashicorp/go-changelog) to generate and update the changelog from files created in the `.changelog/` directory. It is important that when you raise your Pull Request, there is a changelog entry which describes the changes your contribution makes. Not all changes require an entry in the CHANGELOG, guidance follows on what changes do. + +### Changelog Format + +The changelog format requires an entry in the following format, where HEADER corresponds to the changelog category, and the entry is the changelog entry itself. The entry should be included in a file in the `.changelog` directory with the naming convention `{PR-NUMBER}.txt`. For example, to create a changelog entry for pull request 1234, there should be a file named `.changelog/1234.txt`. + +````markdown +```release-note:{HEADER} +{ENTRY} +``` +```` + +If a pull request should contain multiple changelog entries, then multiple blocks can be added to the same changelog file. For example: + +````markdown +```release-note:note +foo: The `broken` attribute has been deprecated. All configurations using `broken` should be updated to use the new `not_broken` attribute instead. +``` + +```release-note:enhancement +foo: Add `not_broken` attribute +``` +```` + +### Skipping changelog entries + +In order to skip/pass the automated checks where a CHANGELOG entry is not required, apply the `workflow/skip-changelog-entry` label. + +### Pull Request Types to CHANGELOG + +The CHANGELOG is intended to show operator-impacting changes to the codebase for a particular version. If every change or commit to the code resulted in an entry, the CHANGELOG would become less useful for operators. The lists below are general guidelines and examples for when a decision needs to be made to decide whether a change should have an entry. + +#### Changes that should have a CHANGELOG entry + +##### Resource and provider bug fixes + +A new bug entry should use the `release-note:bug` header and have a prefix indicating the file/service it corresponds to, a colon, then followed by a brief summary. + +````markdown +```release-note:bug +foo: Fix 'thing' being optional +``` +```` + +##### Resource and provider enhancements + +A new enhancement entry should use the `release-note:enhancement` header and have a prefix indicating the file/service it corresponds to, a colon, then followed by a brief summary. + +````markdown +```release-note:enhancement +foo: Add new capability +``` +```` + +##### Deprecations + +A breaking-change entry should use the `release-note:note` header and have a prefix indicating the file/service it corresponds to, a colon, then followed by a brief summary. + +````markdown +```release-note:note +foo: X attribute is being deprecated in favor of the new Y attribute +``` +```` + +##### Breaking Changes and Removals + +A breaking-change entry should use the `release-note:breaking-change` header and have a prefix indicating the file/service it corresponds to, a colon, then followed by a brief summary. + +````markdown +```release-note:breaking-change +foo: Resource no longer works for 'EXAMPLE' parameters +``` +```` + +#### Changes that may have a CHANGELOG entry + +Dependency updates: If the update contains relevant bug fixes or enhancements that affect operators, those should be called out. +Any changes which do not fit into the above categories but warrant highlighting. + +````markdown +```release-note:note +foo: Example resource now does X slightly differently +``` + +```release-note:dependency +`foo` v0.1.0 => v0.1.1 +``` +```` + +#### Changes that should _not_ have a CHANGELOG entry + +- Resource and provider documentation updates +- Testing updates +- Code refactoring (context dependant) diff --git a/go.mod b/go.mod index 8cfea2df81f..73d2b32f956 100644 --- a/go.mod +++ b/go.mod @@ -16,11 +16,13 @@ require ( github.com/urfave/cli/v2 v2.11.0 github.com/uudashr/gopkgs/v2 v2.1.2 golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 - golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 + golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 golang.org/x/tools/gopls v0.9.1 ) +require google.golang.org/appengine v1.6.7 // indirect + require ( 4d63.com/gochecknoglobals v0.1.0 // indirect github.com/Antonboom/errname v0.1.7 // indirect @@ -83,6 +85,7 @@ require ( github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect github.com/google/go-cmp v0.5.8 // indirect github.com/google/go-dap v0.6.0 // indirect + github.com/google/go-github v17.0.0+incompatible github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect github.com/gostaticanalysis/comment v1.4.2 // indirect @@ -183,6 +186,7 @@ require ( golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4 // indirect golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect + golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e // indirect golang.org/x/text v0.3.7 // indirect diff --git a/go.sum b/go.sum index 265b4721fef..c0896086593 100644 --- a/go.sum +++ b/go.sum @@ -376,6 +376,8 @@ github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-dap v0.6.0 h1:Y1RHGUtv3R8y6sXq2dtGRMYrFB2hSqyFVws7jucrzX4= github.com/google/go-dap v0.6.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ= +github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -1133,6 +1135,8 @@ golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1153,6 +1157,8 @@ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92 h1:oVlhw3Oe+1reYsE2Nqu19PDJfLzwdU3QUUrG86rLK68= +golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1464,6 +1470,7 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= diff --git a/scripts/changelog.tmpl b/scripts/changelog.tmpl new file mode 100644 index 00000000000..e5b8afa8b22 --- /dev/null +++ b/scripts/changelog.tmpl @@ -0,0 +1,39 @@ +{{- if index .NotesByType "breaking-change" }} +BREAKING CHANGES: + +{{range index .NotesByType "breaking-change" -}} +{{ template "note" .}} +{{ end -}} +{{- end -}} + +{{- if .NotesByType.note }} +NOTES: + +{{range .NotesByType.note -}} +{{ template "note" .}} +{{ end -}} +{{- end -}} + +{{- if .NotesByType.enhancement }} +ENHANCEMENTS: + +{{range .NotesByType.enhancement | sort -}} +{{ template "note" .}} +{{ end -}} +{{- end -}} + +{{- if .NotesByType.bug }} +BUG FIXES: + +{{range .NotesByType.bug | sort -}} +{{ template "note" . }} +{{ end -}} +{{- end -}} + +{{- if .NotesByType.dependency }} +DEPENDENCIES: + +{{range .NotesByType.dependency | sort -}} +{{ template "note" . }} +{{ end -}} +{{- end -}} diff --git a/scripts/generate-changelog.sh b/scripts/generate-changelog.sh new file mode 100755 index 00000000000..dbd0c3775f5 --- /dev/null +++ b/scripts/generate-changelog.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +set -o errexit +set -o nounset + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__parent="$(dirname "$__dir")" + +CHANGELOG_FILE_NAME="CHANGELOG.md" +CHANGELOG_TMP_FILE_NAME="CHANGELOG.tmp" +TARGET_SHA=$(git rev-parse HEAD) +PREVIOUS_RELEASE_SHA=$(git rev-list -n 1 $(git describe --abbrev=0 --match='v*.*.*' --tags)) + +if [ $TARGET_SHA == $PREVIOUS_RELEASE_SHA ]; then + echo "Nothing to do" + exit 0 +fi + +PREVIOUS_CHANGELOG=$(sed -n -e "/## $(git describe --abbrev=0 --match='v*.*.*' --tags | tr -d v)/,\$p" $__parent/$CHANGELOG_FILE_NAME) + +if [ -z "$PREVIOUS_CHANGELOG" ] +then + echo "Unable to locate previous changelog contents." + exit 1 +fi + +CHANGELOG=$($(go env GOPATH)/bin/changelog-build -this-release $TARGET_SHA \ + -last-release $PREVIOUS_RELEASE_SHA \ + -git-dir $__parent \ + -entries-dir .changelog \ + -changelog-template $__dir/changelog.tmpl \ + -note-template $__dir/release-note.tmpl) + +if [ -z "$CHANGELOG" ] +then + echo "No changelog generated." + exit 0 +fi + +rm -f $CHANGELOG_TMP_FILE_NAME + +sed -n -e "1{/## /p;}" $__parent/$CHANGELOG_FILE_NAME > $CHANGELOG_TMP_FILE_NAME +echo "$CHANGELOG" >> $CHANGELOG_TMP_FILE_NAME +echo >> $CHANGELOG_TMP_FILE_NAME +echo "$PREVIOUS_CHANGELOG" >> $CHANGELOG_TMP_FILE_NAME + +cp $CHANGELOG_TMP_FILE_NAME $CHANGELOG_FILE_NAME + +rm $CHANGELOG_TMP_FILE_NAME + +echo "Successfully generated changelog." + +exit 0 diff --git a/scripts/release-note.tmpl b/scripts/release-note.tmpl new file mode 100644 index 00000000000..de55697a25f --- /dev/null +++ b/scripts/release-note.tmpl @@ -0,0 +1,3 @@ +{{- define "note" -}} +* {{.Body}} ([#{{- .Issue -}}](https://github.com/cloudflare/cloudflare-go/issues/{{- .Issue -}})) +{{- end -}} diff --git a/tools.go b/tools.go new file mode 100644 index 00000000000..01d1669802a --- /dev/null +++ b/tools.go @@ -0,0 +1,22 @@ +//go:build tools +// +build tools + +package tools + +//go:generate go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs +//go:generate go install github.com/bflad/tfproviderlint/cmd/tfproviderlintx +//go:generate go install github.com/client9/misspell/cmd/misspell +//go:generate go install github.com/golangci/golangci-lint/cmd/golangci-lint +//go:generate go install github.com/hashicorp/go-changelog/cmd/changelog-build +//go:generate go install github.com/google/go-github/github +//go:generate go install golang.org/x/oauth2 + +import ( + _ "github.com/bflad/tfproviderlint/cmd/tfproviderlintx" + _ "github.com/client9/misspell/cmd/misspell" + _ "github.com/golangci/golangci-lint/cmd/golangci-lint" + _ "github.com/google/go-github/github" + _ "github.com/hashicorp/go-changelog/cmd/changelog-build" + _ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs" + _ "golang.org/x/oauth2" +) diff --git a/tools/changelog-check/main.go b/tools/changelog-check/main.go new file mode 100644 index 00000000000..d52e5a355af --- /dev/null +++ b/tools/changelog-check/main.go @@ -0,0 +1,134 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + "strconv" + "strings" + + "github.com/cloudflare/cloudflare-go" + "github.com/google/go-github/github" + "golang.org/x/oauth2" +) + +const ( + changelogEntryFileFormat = ".changelog/%d.txt" + changelogProcessDocumentation = "https://github.com/cloudflare/cloudflare-go/blob/master/docs/changelog-process.md" + changelogDetectedMessage = "changelog detected :white_check_mark:" +) + +var ( + changelogEntryPresent = false + successMessageAlreadyPresent = false +) + +func getSkipLabels() []string { + return []string{"workflow/skip-changelog-entry", "dependencies"} +} + +func main() { + ctx := context.Background() + if len(os.Args) < 2 { + log.Fatalf("Usage: changelog-check PR#\n") + } + pr := os.Args[1] + prNo, err := strconv.Atoi(pr) + if err != nil { + log.Fatalf("error parsing PR %q as a number: %s", pr, err) + } + + owner := os.Getenv("GITHUB_OWNER") + repo := os.Getenv("GITHUB_REPO") + token := os.Getenv("GITHUB_TOKEN") + + if owner == "" { + log.Fatalf("GITHUB_OWNER not set") + } + + if repo == "" { + log.Fatalf("GITHUB_REPO not set") + } + + if token == "" { + log.Fatalf("GITHUB_TOKEN not set") + } + + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: token}, + ) + tc := oauth2.NewClient(ctx, ts) + + client := github.NewClient(tc) + + pullRequest, _, err := client.PullRequests.Get(ctx, owner, repo, prNo) + if err != nil { + log.Fatalf("error retrieving pull request %s/%s#%d: %s", owner, repo, prNo, err) + } + + for _, label := range pullRequest.Labels { + for _, skipLabel := range getSkipLabels() { + if label.GetName() == skipLabel { + log.Printf("%s label found, exiting as changelog is not required\n", label.GetName()) + os.Exit(0) + } + } + } + + files, _, _ := client.PullRequests.ListFiles(ctx, owner, repo, prNo, &github.ListOptions{}) + if err != nil { + log.Fatalf("error retrieving files on pull request %s/%s#%d: %s", owner, repo, prNo, err) + } + + for _, file := range files { + if file.GetFilename() == fmt.Sprintf(changelogEntryFileFormat, prNo) { + changelogEntryPresent = true + } + } + + comments, _, _ := client.Issues.ListComments(ctx, owner, repo, prNo, &github.IssueListCommentsOptions{}) + for _, comment := range comments { + if strings.Contains(comment.GetBody(), "no changelog entry is attached to") { + if changelogEntryPresent { + client.Issues.EditComment(ctx, owner, repo, *comment.ID, &github.IssueComment{ + Body: cloudflare.StringPtr(changelogDetectedMessage), + }) + os.Exit(0) + } + log.Println("no change in status of changelog checks; exiting") + os.Exit(1) + } + + if strings.Contains(comment.GetBody(), changelogDetectedMessage) { + successMessageAlreadyPresent = true + } + } + + if changelogEntryPresent { + if !successMessageAlreadyPresent { + _, _, _ = client.Issues.CreateComment(ctx, owner, repo, prNo, &github.IssueComment{ + Body: cloudflare.StringPtr(changelogDetectedMessage), + }) + } + log.Printf("changelog found for %d, skipping remainder of checks\n", prNo) + os.Exit(0) + } + + body := "Oops! It looks like no changelog entry is attached to" + + " this PR. Please include a release note as described in " + + changelogProcessDocumentation + ".\n\nExample: " + + "\n\n~~~\n```release-note:TYPE\nRelease note" + + "\n```\n~~~\n\n" + + "If you do not require a release note to be included, please add the `workflow/skip-changelog-entry` label." + + _, _, err = client.Issues.CreateComment(ctx, owner, repo, prNo, &github.IssueComment{ + Body: &body, + }) + + if err != nil { + log.Fatalf("failed to comment on pull request %s/%s#%d: %s", owner, repo, prNo, err) + } + + os.Exit(1) +}