├── .gitignore ├── .prettierignore ├── release-notes.png ├── .prettierrc.json ├── SECURITY.md ├── CONTRIBUTING.md ├── .github ├── dependabot.yml ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ ├── branchtest.yaml │ ├── stale.yml │ ├── release.yml │ ├── codeql-analysis.yml │ └── test.yml ├── eslint.config.js ├── action.yml ├── LICENSE ├── changelog.sh ├── dist └── changelog.sh ├── Makefile ├── package.json ├── index.js ├── CODE_OF_CONDUCT.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | .envrc 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | lib/ 3 | node_modules/ -------------------------------------------------------------------------------- /release-notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metcalfc/changelog-generator/HEAD/release-notes.png -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": false, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "arrowParens": "avoid" 9 | } 10 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | | Version | Supported | 6 | | ------- | ------------------ | 7 | | v4.6.2 | :white_check_mark: | 8 | | < v4.6.2 | :x: | 9 | 10 | ## Reporting a Vulnerability 11 | 12 | File an issue. 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Right now, I think the action is fairly feature complete. There are always more special cases that could be covered. This action was started as an simple changlelog. There are other more complex actions out there. 2 | 3 | I'm happy to take a look if you've got a feature you'd let to add. Please don't be offended if it doesn't make it in. Its awesome to see all the interest in the action. 4 | 5 | If you've found a bug submit an issue and PR and we will get it sorted. 6 | 7 | Thanks! 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for npm 4 | - package-ecosystem: npm 5 | directory: '/' 6 | schedule: 7 | interval: daily 8 | time: '13:00' 9 | open-pull-requests-limit: 10 10 | ignore: 11 | - dependency-name: eslint 12 | versions: 13 | - 7.20.0 14 | 15 | # Maintain dependencies for GitHub Actions 16 | - package-ecosystem: 'github-actions' 17 | directory: '/' 18 | schedule: 19 | interval: 'daily' 20 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | const js = require('@eslint/js') 2 | const globals = require('globals') 3 | 4 | module.exports = [ 5 | js.configs.recommended, 6 | { 7 | ignores: ['dist/**', 'node_modules/**'] 8 | }, 9 | { 10 | files: ['**/*.js', '**/*.mjs'], 11 | languageOptions: { 12 | ecmaVersion: 'latest', 13 | sourceType: 'module', 14 | globals: { 15 | ...globals.node 16 | } 17 | }, 18 | rules: { 19 | 'no-unused-vars': [ 20 | 'error', 21 | { argsIgnorePattern: '^_', varsIgnorePattern: '^_' } 22 | ], 23 | 'no-var': 'error', 24 | 'prefer-const': 'error', 25 | semi: ['error', 'never'], 26 | quotes: ['error', 'single', { avoidEscape: true }] 27 | } 28 | } 29 | ] 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: metcalfc 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **URL to Public action log** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/workflows/branchtest.yaml: -------------------------------------------------------------------------------- 1 | name: Test branches with slash in name 2 | on: [push] 3 | permissions: 4 | contents: read 5 | 6 | jobs: 7 | test: 8 | runs-on: ubuntu-latest 9 | steps: 10 | # To use this repository's private action, you must check out the repository 11 | - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # was: actions/checkout@v4 12 | with: 13 | fetch-depth: 0 14 | 15 | - name: Generate changelog 16 | id: changelog 17 | # refme: ignore 18 | uses: metcalfc/changelog-generator@main 19 | with: 20 | myToken: ${{ secrets.GITHUB_TOKEN }} 21 | head-ref: 'origin/test/branch' #add 'origin/` in front of your branch name 22 | base-ref: 'v1.0.0' 23 | fetch: false 24 | 25 | - name: Get the changelog 26 | run: | 27 | cat << "EOF" 28 | ${{ steps.changelog.outputs.changelog }} 29 | EOF 30 | 31 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Get a changelog between two references' 2 | author: 'Chad Metcalf' 3 | description: 'Returns the commit difference count between two git references.' 4 | inputs: 5 | mytoken: 6 | description: 'Your GITHUB_TOKEN' 7 | default: '' 8 | required: true 9 | head-ref: 10 | description: 'The name of the head reference' 11 | default: '' 12 | required: false 13 | base-ref: 14 | description: 'The name of the base reference' 15 | default: '' 16 | required: false 17 | reverse: 18 | description: 'Git log is chronological order by default. Set to true to reverse chronological order. ' 19 | default: 'false' 20 | required: false 21 | fetch: 22 | description: 'Whether to have this action fetch all other branches and tags' 23 | default: 'true' 24 | required: false 25 | outputs: 26 | changelog: 27 | description: 'Markdown formatted changelog' 28 | runs: 29 | using: 'node20' 30 | main: 'dist/index.js' 31 | branding: 32 | icon: 'list' 33 | color: 'blue' 34 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Mark stale issues and pull requests 2 | permissions: 3 | issues: write 4 | pull-requests: write 5 | 6 | on: 7 | schedule: 8 | - cron: '30 1 * * *' 9 | 10 | jobs: 11 | stale: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Close Stale Issues 16 | uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # was: actions/stale@v9 17 | with: 18 | repo-token: ${{ secrets.GITHUB_TOKEN }} 19 | stale-issue-message: 'This issue has not had any activity in 30 days. Marking it stale. It will be closed in 14 days.' 20 | stale-pr-message: 'This PR has not had any activity in 30 days. Marking it stale. It will be closed in 14 days.' 21 | close-issue-message: 'This issue has not had any activity in 44 days. Closing it.' 22 | close-pr-message: 'This PR has not had any activity in 44 days. Closing it.' 23 | days-before-stale: 30 24 | days-before-close: 14 25 | stale-issue-label: 'stale-issue' 26 | stale-pr-label: 'stale-pr' 27 | remove-stale-when-updated: true 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Chad Metcalf 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /changelog.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eou pipefail 3 | 4 | head_ref=$1 5 | base_ref=$2 6 | repo_url=$3 7 | extra_flags="" 8 | 9 | if [ "$4" == "true" ]; then 10 | extra_flags='--reverse' 11 | fi 12 | 13 | fetch=$5 14 | 15 | # By default a GitHub action checkout is shallow. Get all the tags, branches, 16 | # and history. Redirect output to standard error which we can collect in the 17 | # action. 18 | if [ "$fetch" == "true" ]; then 19 | git fetch --depth=1 origin +refs/tags/*:refs/tags/* 1>&2 20 | git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/* 1>&2 21 | git fetch --prune --unshallow 1>&2 22 | fi 23 | 24 | # if folks don't have a base ref to compare against just use the initial 25 | # commit. This will show all the changes since the beginning but I can't 26 | # think of a better default. 27 | if [ -z "$base_ref" ] 28 | then 29 | base_ref=$(git rev-list --max-parents=0 HEAD) 30 | fi 31 | 32 | # Bash quoting will get you. Do not quote the extra_flags. If its null 33 | # we want it to disappear. If you quote it, it will go to git as an "" 34 | # and thats not a valid arg. 35 | log=$(git log "${base_ref}"..."${head_ref}" \ 36 | --pretty=format:"- [%h](http://github.com/${repo_url}/commit/%H) - %s" \ 37 | ${extra_flags}) 38 | 39 | if [ -z "$log" ]; 40 | then 41 | log="No Changes." 42 | fi 43 | 44 | echo "$log" 45 | -------------------------------------------------------------------------------- /dist/changelog.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eou pipefail 3 | 4 | head_ref=$1 5 | base_ref=$2 6 | repo_url=$3 7 | extra_flags="" 8 | 9 | if [ "$4" == "true" ]; then 10 | extra_flags='--reverse' 11 | fi 12 | 13 | fetch=$5 14 | 15 | # By default a GitHub action checkout is shallow. Get all the tags, branches, 16 | # and history. Redirect output to standard error which we can collect in the 17 | # action. 18 | if [ "$fetch" == "true" ]; then 19 | git fetch --depth=1 origin +refs/tags/*:refs/tags/* 1>&2 20 | git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/* 1>&2 21 | git fetch --prune --unshallow 1>&2 22 | fi 23 | 24 | # if folks don't have a base ref to compare against just use the initial 25 | # commit. This will show all the changes since the beginning but I can't 26 | # think of a better default. 27 | if [ -z "$base_ref" ] 28 | then 29 | base_ref=$(git rev-list --max-parents=0 HEAD) 30 | fi 31 | 32 | # Bash quoting will get you. Do not quote the extra_flags. If its null 33 | # we want it to disappear. If you quote it, it will go to git as an "" 34 | # and thats not a valid arg. 35 | log=$(git log "${base_ref}"..."${head_ref}" \ 36 | --pretty=format:"- [%h](http://github.com/${repo_url}/commit/%H) - %s" \ 37 | ${extra_flags}) 38 | 39 | if [ -z "$log" ]; 40 | then 41 | log="No Changes." 42 | fi 43 | 44 | echo "$log" 45 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: create-release 2 | on: 3 | push: 4 | # Sequence of patterns matched against refs/tags 5 | tags: 6 | - 'v*' # Push events to matching v*, i.e. v1.0, v4.6.2 7 | 8 | jobs: 9 | release: 10 | permissions: 11 | contents: write 12 | runs-on: ubuntu-latest 13 | steps: 14 | # To use this repository's private action, you must check out the repository 15 | - name: Checkout 16 | uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # was: actions/checkout@v4 17 | - name: Generate changelog 18 | id: changelog 19 | uses: metcalfc/changelog-generator@3f82cef08fe5dcf57c591fe165e70e1d5032e15a # was: metcalfc/changelog-generator@v4.6.2 20 | with: 21 | myToken: ${{ secrets.GITHUB_TOKEN }} 22 | - name: Create Release 23 | id: create_release 24 | uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # was: ncipollo/release-action@v1 25 | with: 26 | tag: ${{ github.ref }} 27 | name: Release ${{ github.ref_name }} 28 | body: ${{ steps.changelog.outputs.changelog }} 29 | token: ${{ secrets.GITHUB_TOKEN }} 30 | - name: Update Major Version Tag 31 | run: | 32 | MAJOR_VERSION=$(echo "${{ github.ref_name }}" | grep -oE 'v[0-9]+') 33 | git tag -f $MAJOR_VERSION 34 | git push origin $MAJOR_VERSION --force 35 | env: 36 | GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} 37 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all help lint format check test build clean install 2 | 3 | # Default target 4 | all: lint check build 5 | 6 | # Help command 7 | help: 8 | @echo "Available commands:" 9 | @echo " make Run lint, check, and build" 10 | @echo " make help Show this help message" 11 | @echo " make lint Run ESLint" 12 | @echo " make format Format code with Prettier" 13 | @echo " make check Check formatting with Prettier" 14 | @echo " make test Run tests" 15 | @echo " make build Build the project" 16 | @echo " make clean Remove build artifacts" 17 | @echo " make install Install dependencies" 18 | 19 | # Check if Node and npm are installed 20 | check-node: 21 | @which node > /dev/null || (echo "Node.js is not installed. Please install it first." && exit 1) 22 | @which npm > /dev/null || (echo "npm is not installed. Please install it first." && exit 1) 23 | 24 | # Install dependencies 25 | install: check-node 26 | @echo "📦 Installing dependencies..." 27 | @npm install 28 | 29 | # Lint code 30 | lint: check-node 31 | @echo "🔍 Linting code..." 32 | @npm run lint 33 | 34 | # Format code 35 | format: check-node 36 | @echo "✨ Formatting code..." 37 | @npm run format 38 | 39 | # Check formatting 40 | check: check-node 41 | @echo "🔎 Checking formatting..." 42 | @npm run format-check 43 | 44 | # Run tests 45 | test: check-node 46 | @echo "🧪 Running tests..." 47 | @npm test || echo "⚠️ No tests available. Consider adding tests." 48 | 49 | # Build the project 50 | build: check-node 51 | @echo "🏗️ Building project..." 52 | @npm run build 53 | 54 | # Clean build artifacts 55 | clean: 56 | @echo "🧹 Cleaning build artifacts..." 57 | @rm -rf dist/ || true 58 | @echo "✅ Clean complete" 59 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: 'Code Scanning - Action' 2 | permissions: 3 | contents: read 4 | security-events: write 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | pull_request: 11 | schedule: 12 | # ┌───────────── minute (0 - 59) 13 | # │ ┌───────────── hour (0 - 23) 14 | # │ │ ┌───────────── day of the month (1 - 31) 15 | # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) 16 | # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) 17 | # │ │ │ │ │ 18 | # │ │ │ │ │ 19 | # │ │ │ │ │ 20 | # * * * * * 21 | - cron: '30 1 * * 0' 22 | 23 | jobs: 24 | CodeQL-Build: 25 | # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest 26 | runs-on: ubuntu-latest 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # was: actions/checkout@v4 31 | 32 | # Initializes the CodeQL tools for scanning. 33 | - name: Initialize CodeQL 34 | uses: github/codeql-action/init@v4 35 | 36 | # Override language selection by uncommenting this and choosing your languages 37 | # with: 38 | # languages: go, javascript, csharp, python, cpp, java 39 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 40 | # If this step fails, then you should remove it and run the build manually (see below). 41 | - name: Autobuild 42 | uses: github/codeql-action/autobuild@v4 43 | 44 | # ℹ️ Command-line programs to run using the OS shell. 45 | # 📚 https://git.io/JvXDl 46 | 47 | # ✏️ If the Autobuild fails above, remove it and uncomment the following 48 | # three lines and modify them (or add more) to build your code if your 49 | # project uses a compiled language 50 | 51 | #- run: | 52 | # make bootstrap 53 | # make release 54 | 55 | - name: Perform CodeQL Analysis 56 | uses: github/codeql-action/analyze@v4 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "changelog-generator", 3 | "version": "4.6.2", 4 | "description": "A GitHub Action that compares the commit differences between two branches", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "build": "ncc build ./index.js", 8 | "bump:readme": "replace 'v[1-9]+.[0-9]+.[0-9]+' \"v$npm_package_version\" ./README.md ./SECURITY.md", 9 | "bump:workflow": "replace 'v[1-9]+.[0-9]+.[0-9]+' \"v$npm_package_version\" ./.github/workflows/*yml", 10 | "format-check": "prettier --check **/*.{js,yml,json}", 11 | "format": "prettier --write **/*.{js,yml,json}", 12 | "lint": "eslint ./", 13 | "postversion": "git push && git push --tags", 14 | "precommit-msg": "echo 'Pre-commit checks...' && exit 0", 15 | "precommit": "npm run build && git add dist/", 16 | "refme": "npm exec gh-refme -- convert ./.github/workflows/*", 17 | "test": "echo \"Error: no test specified\" && exit 1", 18 | "version": "npm run bump:readme && npm run bump:workflow && git add ./dist ./README.md ./SECURITY.md ./.github/workflows/*yml" 19 | }, 20 | "pre-commit": [ 21 | "precommit-msg", 22 | "lint", 23 | "format-check", 24 | "refme", 25 | "precommit" 26 | ], 27 | "repository": { 28 | "type": "git", 29 | "url": "git+https://github.com/metcalfc/changelog-generator.git" 30 | }, 31 | "keywords": [ 32 | "releases", 33 | "commits", 34 | "commit", 35 | "difference", 36 | "branching", 37 | "branch", 38 | "difference" 39 | ], 40 | "author": "Chad Metcalf", 41 | "license": "MIT", 42 | "bugs": { 43 | "url": "https://github.com/metcalfc/changelog-generator/issues" 44 | }, 45 | "homepage": "https://github.com/metcalfc/changelog-generator#readme", 46 | "dependencies": { 47 | "@actions/core": "^2.0.1", 48 | "@actions/exec": "^1.1.1", 49 | "@actions/github": "^6.0.1" 50 | }, 51 | "devDependencies": { 52 | "@eslint/js": "^9.39.0", 53 | "@vercel/ncc": "^0.38.4", 54 | "eslint": "^9.39.1", 55 | "gh-refme": "^1.5.0", 56 | "globals": "^16.5.0", 57 | "isexe": "^3.1.1", 58 | "pre-commit": "git://github.com/metcalfc/pre-commit.git", 59 | "prettier": "^3.7.3", 60 | "replace": "^1.2.2" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test the changelog action 2 | permissions: 3 | contents: read 4 | on: [push] 5 | 6 | jobs: 7 | test: 8 | runs-on: ubuntu-latest 9 | steps: 10 | # To use this repository's private action, you must check out the repository 11 | - name: Checkout 12 | uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # was: actions/checkout@v4 13 | - name: Generate changelog 14 | id: changelog 15 | # refme: ignore 16 | uses: metcalfc/changelog-generator@main 17 | with: 18 | myToken: ${{ secrets.GITHUB_TOKEN }} 19 | head-ref: 'v0.0.2' 20 | base-ref: 'v0.0.1' 21 | - name: Reverse the generated changelog 22 | id: changelog-rev 23 | # refme: ignore 24 | uses: metcalfc/changelog-generator@main 25 | with: 26 | myToken: ${{ secrets.GITHUB_TOKEN }} 27 | head-ref: 'v0.0.2' 28 | base-ref: 'v0.0.1' 29 | reverse: 'true' 30 | - name: Explicitly do not reverse the generated changelog 31 | id: changelog-notrev 32 | # refme: ignore 33 | uses: metcalfc/changelog-generator@main 34 | with: 35 | myToken: ${{ secrets.GITHUB_TOKEN }} 36 | head-ref: 'v0.0.2' 37 | base-ref: 'v0.0.1' 38 | reverse: 'false' 39 | - name: Get the changelog 40 | run: | 41 | cat << "EOF" 42 | ${{ steps.changelog.outputs.changelog }} 43 | EOF 44 | - name: Modify the changelog 45 | id: modified 46 | run: | 47 | set -o noglob 48 | log=$(cat << "EOF" | grep -v Bumping | tac 49 | ${{ steps.changelog.outputs.changelog }} 50 | EOF 51 | ) 52 | log="${log//'%'/'%25'}" 53 | log="${log//$'\n'/'%0A'}" 54 | log="${log//$'\r'/'%0D'}" 55 | echo "modified=$log" >> $GITHUB_OUTPUT 56 | - name: Print the modified changelog 57 | run: | 58 | cat << "EOF" 59 | ${{ steps.modified.outputs.modified }} 60 | EOF 61 | - name: Generate changelog from release 62 | id: release 63 | # refme: ignore 64 | uses: metcalfc/changelog-generator@main 65 | with: 66 | myToken: ${{ secrets.GITHUB_TOKEN }} 67 | - name: Get the changelog 68 | run: | 69 | cat << "EOF" 70 | ${{ steps.release.outputs.changelog }} 71 | EOF 72 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { getInput, setFailed, setOutput } from '@actions/core' 2 | import { exec as _exec } from '@actions/exec' 3 | import { context, getOctokit } from '@actions/github' 4 | 5 | const src = __dirname 6 | 7 | async function run() { 8 | try { 9 | let headRef = getInput('head-ref') 10 | let baseRef = getInput('base-ref') 11 | const myToken = getInput('myToken') 12 | const reverse = getInput('reverse') 13 | const fetch = getInput('fetch') 14 | const octokit = new getOctokit(myToken) 15 | const { owner, repo } = context.repo 16 | const regexp = /^[.A-Za-z0-9_/\-+]*$/ 17 | 18 | if (!headRef) { 19 | headRef = context.sha 20 | } 21 | 22 | if (!baseRef) { 23 | const latestRelease = await octokit.rest.repos.getLatestRelease({ 24 | owner: owner, 25 | repo: repo 26 | }) 27 | if (latestRelease) { 28 | baseRef = latestRelease.data.tag_name 29 | } else { 30 | setFailed( 31 | `There are no releases on ${owner}/${repo}. Tags are not releases.` 32 | ) 33 | } 34 | } 35 | 36 | console.log(`head-ref: ${headRef}`) 37 | console.log(`base-ref: ${baseRef}`) 38 | 39 | if ( 40 | !!headRef && 41 | !!baseRef && 42 | regexp.test(headRef) && 43 | regexp.test(baseRef) 44 | ) { 45 | getChangelog(headRef, baseRef, owner + '/' + repo, reverse, fetch) 46 | } else { 47 | setFailed( 48 | 'Git ref names must contain only numbers, strings, underscores, periods, forward slashes, pluses, and dashes.' 49 | ) 50 | } 51 | } catch (error) { 52 | setFailed(error.message) 53 | } 54 | } 55 | 56 | async function getChangelog(headRef, baseRef, repoName, reverse, fetch) { 57 | try { 58 | let output = '' 59 | let err = '' 60 | 61 | // These are option configurations for the @actions/exec lib` 62 | const options = {} 63 | options.listeners = { 64 | stdout: data => { 65 | output += data.toString() 66 | }, 67 | stderr: data => { 68 | err += data.toString() 69 | } 70 | } 71 | options.cwd = './' 72 | 73 | await _exec( 74 | `${src}/changelog.sh`, 75 | [headRef, baseRef, repoName, reverse, fetch], 76 | options 77 | ) 78 | 79 | if (output) { 80 | console.log( 81 | '\x1b[32m%s\x1b[0m', 82 | `Changelog between ${baseRef} and ${headRef}:\n${output}` 83 | ) 84 | setOutput('changelog', output) 85 | } else { 86 | setFailed(err) 87 | process.exit(1) 88 | } 89 | } catch (err) { 90 | setFailed( 91 | `Could not generate changelog between references because: ${err.message}` 92 | ) 93 | process.exit(0) 94 | } 95 | } 96 | 97 | try { 98 | run() 99 | } catch (error) { 100 | setFailed(error.message) 101 | } 102 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at metcalfc+github@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Get a changelog between two references 2 | 3 | This Action returns a markdown formatted changelog between two git references. There are other projects that use milestones, labeled PRs, etc. Those are just too much work for simple projects. 4 | 5 | I just wanted a simple way to populate the body of a GitHub Release. 6 | 7 | Example Release Notes 8 | 9 | ## Inputs 10 | 11 | ### `mytoken` 12 | 13 | A GITHUB_TOKEN with the ability to pull from the repo in question. This is required. 14 | 15 | Why do we need `myToken`? Read more here: https://help.github.com/en/actions/automating-your-workflow-with-github-actions/authenticating-with-the-github_token#about-the-github_token-secret 16 | 17 | ### `head-ref` 18 | 19 | The name of the head reference. Default `${{github.sha}}`. 20 | 21 | ### `base-ref` 22 | 23 | The name of the second branch. Defaults to the `tag_name` of the latest GitHub release. *This must be a GitHub release. Git tags or branches will not work.* 24 | 25 | ### `reverse` 26 | 27 | Whether the order of commits should be printed in reverse. Default: 'false' 28 | 29 | ### `fetch` 30 | 31 | Whether this action should pull in all other branches and tags. Default: 'true' 32 | 33 | ## Outputs 34 | 35 | ### `changelog` 36 | 37 | Markdown formatted changelog. 38 | 39 | ## Example usage 40 | 41 | There are two blocks you will need: 42 | 43 | ### First block 44 | 45 | First you will need to generate the changelog itself. To get the changelog between the SHA of the commit that triggered the action and the tag of the latest release: 46 | 47 | ```yaml 48 | - name: Generate changelog 49 | id: changelog 50 | uses: metcalfc/changelog-generator@v4.6.2 51 | with: 52 | myToken: ${{ secrets.GITHUB_TOKEN }} 53 | ``` 54 | 55 | Or, if you have two specific references you want: 56 | 57 | ```yaml 58 | - name: Generate changelog 59 | id: changelog 60 | uses: metcalfc/changelog-generator@v4.6.2 61 | with: 62 | myToken: ${{ secrets.GITHUB_TOKEN }} 63 | head-ref: 'v0.0.2' 64 | base-ref: 'v0.0.1' 65 | ``` 66 | 67 | If you want to point to a branch containing forward slashes (https://github.com/metcalfc/changelog-generator/issues/179) do the following: 68 | 69 | ```yaml 70 | 71 | # let the checkout action do the fetching 72 | - uses: actions/checkout@v3 73 | with: 74 | fetch-depth: 0 75 | 76 | - name: Generate changelog 77 | id: changelog 78 | uses: metcalfc/changelog-generator@v4.6.2 #TODO: bump this after release 79 | with: 80 | myToken: ${{ secrets.GITHUB_TOKEN }} 81 | head-ref: 'origin/my/branch/with/slashes' #add 'origin/` in front of your branch name 82 | base-ref: 'v4.6.2' 83 | fetch: false 84 | ``` 85 | 86 | ### Second block 87 | 88 | Then you can use the resulting changelog: 89 | 90 | ```yaml 91 | - name: Get the changelog 92 | run: | 93 | cat << "EOF" 94 | ${{ steps.changelog.outputs.changelog }} 95 | EOF 96 | ``` 97 | 98 | ### Simple output modifications 99 | 100 | Some folks have asked if the action can support changing the output. For example: 101 | * Reverse order *UPDATE* as of 2021/11/22 chronological is the default and it can be reversed by setting `reverse: 'true'` in the workflow. 102 | * Ignore entries that include this string. 103 | * Etc 104 | 105 | In order to keep this action as simple as possible we aren't planning to add more flags or options. However since the output is just text you can write a command line to do anything you want. In issue #93 we had a user that wanted to list the changelog in reverse order and drop any entries with `gh-pages`. Here is how they can do that but using Bumping as the restrict word because it shows up in this projects history: 106 | 107 | ```yaml 108 | - name: Modify the changelog 109 | id: modified 110 | run: | 111 | set -o noglob 112 | log=$(cat << "EOF" | grep -v Bumping | tac 113 | ${{ steps.changelog.outputs.changelog }} 114 | EOF 115 | ) 116 | log="${log//'%'/'%25'}" 117 | log="${log//$'\n'/'%0A'}" 118 | log="${log//$'\r'/'%0D'}" 119 | echo "log=$log" >> $GITHUB_OUTPUT 120 | 121 | - name: Print the modified changelog 122 | run: | 123 | cat << "EOF" 124 | ${{ steps.modified.outputs.log }} 125 | EOF 126 | ``` 127 | 128 | You might be wondering about that set of escaping for the `log`. Thats because GitHub Actions doesn't support multiline output. Read more [here](https://github.community/t/set-output-truncates-multiline-strings/16852). 129 | 130 | ## Example use case 131 | 132 | [Generating the release notes for a GitHub Release.](.github/workflows/release.yml) 133 | 134 | ## Open Discussions for feature requests or questions 135 | 136 | Issues are for folks who are actively using the action and running into an "issue" (bug, missing doc, etc). 137 | 138 | Feature requests should be in the [discussion section.](https://github.com/metcalfc/changelog-generator/discussions). 139 | Just to set expectations the bar for a new feature getting added is going to be very high. There is a 140 | cost to adding features in the development and maintainance of the feature. So if you want to jump in and 141 | help develop and maintain lets discuss. If you want to fire off feature ideas, go for it. Just understand its 142 | very likely that without someone willing to take up the task, they won't get implemented. 143 | 144 | ## Keep up-to-date with GitHub Dependabot 145 | 146 | Since [Dependabot](https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-github-dependabot) 147 | has [native GitHub Actions support](https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#package-ecosystem), 148 | to enable it on your GitHub repo all you need to do is add the `.github/dependabot.yml` file: 149 | 150 | ```yaml 151 | version: 2 152 | updates: 153 | # Maintain dependencies for GitHub Actions 154 | - package-ecosystem: 'github-actions' 155 | directory: '/' 156 | schedule: 157 | interval: 'daily' 158 | ``` 159 | ## Troubleshooting 160 | 161 | ### Error not found 162 | 163 | ``` 164 | Error: Not Found 165 | ``` 166 | 167 | If you are seeing this error its likely that you do not yet have a GitHub release. You might have a git tag and that shows up in the release tab. The 168 | API this Action uses only works with GitHub Releases. Convert one of your tags to a release and you'll be on your way. You can check out how this 169 | repository uses this action and GitHub releases for an [example](.github/workflows/release.yml). 170 | 171 | 172 | ## Acknowledgements 173 | 174 | I took the basic framework for this action from: [jessicalostinspace/commit-difference-action](https://github.com/jessicalostinspace/commit-difference-action). Thanks @jessicalostinspace. 175 | --------------------------------------------------------------------------------