From 63ef4faacfa787a85971c9aec09c40d2248e8847 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 29 Oct 2023 13:11:16 -0400 Subject: [PATCH] feat: generate changelog file --- .github/workflows/ci.yml | 8 +++ README.md | 30 +++++---- action.yml | 130 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc64833..6d10f79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,16 +36,19 @@ jobs: id: vars run: | if [ "${{ github.event_name }}" == "pull_request" ]; then + changelog_branch=pr-changelogs discussion_category=tests publish_pre_release=true release_tag=pr-${{ github.event.pull_request.number }}-${{ github.run_id }} else + changelog_branch=changelog discussion_category=announcements publish_pre_release=${{ steps.setup-release.outputs.publish_pre_release }} release_tag=${{ steps.setup-release.outputs.release_tag }} fi # set outputs + echo "changelog_branch=$changelog_branch" >> $GITHUB_OUTPUT echo "discussion_category=$discussion_category" >> $GITHUB_OUTPUT echo "publish_stable_release=$publish_stable_release" >> $GITHUB_OUTPUT echo "release_tag=$release_tag" >> $GITHUB_OUTPUT @@ -57,6 +60,7 @@ jobs: allowUpdates: false artifacts: '' body: '' + changelogBranch: ${{ steps.vars.outputs.changelog_branch }} discussionCategory: ${{ steps.vars.outputs.discussion_category }} generateReleaseNotes: true name: ${{ steps.vars.outputs.release_tag }} @@ -64,6 +68,10 @@ jobs: tag: ${{ steps.vars.outputs.release_tag }} token: ${{ secrets.GH_BOT_TOKEN }} + - name: Print changelog + run: | + echo "${{ steps.action.outputs.changelog }}" + - name: Sleep if: ${{ always() && github.event_name == 'pull_request' }} run: sleep 60 diff --git a/README.md b/README.md index 02a3c4c..e43bec6 100644 --- a/README.md +++ b/README.md @@ -23,17 +23,25 @@ steps: ## Inputs -| Name | Description | Default | Required | -|----------------------|--------------------------------------------------------------------------------------|-----------------|----------| -| allowUpdates | An optional flag which indicates if we should update a release if it already exists. | `true` | `false` | -| artifacts | The artifacts to upload. | `*artifacts/*` | `false` | -| body | The body of the release. | | `false` | -| discussionCategory | The category for the discussion. | `announcements` | `false` | -| generateReleaseNotes | Indicates if release notes should be automatically generated. | `false` | `false` | -| name | The version to create. | | `true` | -| prerelease | Whether the release is a prerelease. | `false` | `false` | -| tag | The tag to create. | | `true` | -| token | GitHub Token. | | `true` | +| Name | Description | Default | Required | +|----------------------|---------------------------------------------------------------------------------------------------|-----------------|----------| +| allowUpdates | An optional flag which indicates if we should update a release if it already exists. | `true` | `false` | +| artifacts | The artifacts to upload. | `*artifacts/*` | `false` | +| body | The body of the release. | | `false` | +| changelog | Whether to create a changelog file. The changelog will be generated from the GitHub releases API. | `true` | `false` | +| changelogBranch | The branch to store the changelog in. | `changelog` | `false` | +| discussionCategory | The category for the discussion. | `announcements` | `false` | +| generateReleaseNotes | Indicates if release notes should be automatically generated. | `false` | `false` | +| name | The version to create. | | `true` | +| prerelease | Whether the release is a prerelease. | `false` | `false` | +| tag | The tag to create. | | `true` | +| token | GitHub Token. | | `true` | + +## Outputs + +| Name | Description | +|-----------|------------------------------------------| +| changelog | The contents of the generated changelog. | ## See Also diff --git a/action.yml b/action.yml index 71d43b3..3dc0b58 100644 --- a/action.yml +++ b/action.yml @@ -15,6 +15,14 @@ inputs: description: 'The body of the release.' required: false default: '' + changelog: + description: 'Whether to create a changelog file.' + required: false + default: 'true' + changelogBranch: + description: 'The branch to store the changelog in.' + required: false + default: 'changelog' discussionCategory: description: 'The category of the discussion.' required: false @@ -57,3 +65,125 @@ runs: prerelease: ${{ inputs.prerelease }} tag: ${{ inputs.tag }} token: ${{ inputs.token }} + + - name: Emtpy sha + id: empty_sha + if: >- + inputs.changelog == 'true' && + (github.repository == 'LizardByte/create-release-action' || + (github.event_name == 'push' && github.ref == 'refs/heads/master')) + run: | + # get empty sha from git + # https://stackoverflow.com/a/9766506 + # empty sha is derived from `git hash-object -t tree /dev/null` + echo "emtpy_sha=$(git hash-object -t tree /dev/null)" >> $GITHUB_ENV + shell: bash + + - name: Create Changelog + if: >- + inputs.changelog == 'true' && + (github.repository == 'LizardByte/create-release-action' || + (github.event_name == 'push' && github.ref == 'refs/heads/master')) + uses: actions/github-script@v6 + with: + github-token: ${{ inputs.token }} + script: | + // get inputs + const changelogBranch = core.getInput('changelogBranch') + + // -- general logic of this script -- + // get changelog content from github releases api, use body of each release + // store in changelog branch + // if changelog branch doesn't exist, create it with no parents, history, etc. + // only the changelog should be in this branch, none of the code on the current branch + + // check if changelog branch exists + const changelogBranchExists = await github.repos.getBranch({ + owner: context.repo.owner, + repo: context.repo.repo, + branch: changelogBranch + }).then(() => true).catch(() => false) + + // create empty/orphaned changelog branch if it doesn't exist + if (!changelogBranchExists) { + emtpy_sha = process.env.emtpy_sha + + await github.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `refs/heads/${changelogBranch}`, + sha: emtpy_sha + }) + } + + // only checkout changelog branch + await github.git.checkout({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: changelogBranch + }) + + // get all releases and sort by date created, page if required + let releases = [] + let page = 1 + let per_page = 100 + let total = 0 + do { + const response = await github.repos.listReleases({ + owner: context.repo.owner, + repo: context.repo.repo, + per_page: per_page, + page: page + }) + releases = releases.concat(response.data) + total = response.data.length + page++ + } while (total == per_page) + // sort releases by date created + releases.sort((a, b) => { + return new Date(a.created_at) - new Date(b.created_at) + }) + + // create a CHANGELOG.md and initialize it + changelog = '# Changelog\n\n` + changelog += 'All notable changes to this project will be documented in this file.\n\n' + changelog += 'The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\n' + changelog += 'and this project adheres to [Calendar Versioning](https://calver.org/).\n\n' + + // loop through each release + for (const release of releases) { + // get release body + const body = release.body + // get release tag + const tag = release.tag_name + // get release date + const date = release.created_at + // get release name + const name = release.name + + // add release to changelog + changelog += `## [${tag}] - ${date}\n\n` + changelog += `${body}\n\n` + } + + // add urls to end of changelog + for (const release of releases.reverse()) { + // get release url + const url = release.html_url + + // add release url to changelog + changelog += `[${url}]: ${url}\n` + } + + // write changelog to CHANGELOG.md + await github.repos.createOrUpdateFileContents({ + owner: context.repo.owner, + repo: context.repo.repo, + path: 'CHANGELOG.md', + message: 'chore: update CHANGELOG.md', + content: Buffer.from(changelog).toString('base64'), + branch: changelogBranch + }) + + // set github output + core.setOutput('changelog', changelog)