├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── SECURITY.md ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ ├── dependency-review.yml │ ├── lint.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .markdownlint.json ├── .prettierignore ├── .prettierrc.json ├── .yamllint ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── __tests__ ├── main.test.js └── testdata │ ├── dl.json │ └── go.mod ├── action.yml ├── babel.config.cjs ├── dist └── index.js ├── docs ├── action-matrix-summary.png ├── action-run.png └── job-summary.png ├── eslint.config.mjs ├── package-lock.json ├── package.json └── src ├── go-versions.js └── main.js /.gitattributes: -------------------------------------------------------------------------------- 1 | dist/** -diff linguist-generated=true -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @arnested 2 | -------------------------------------------------------------------------------- /.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: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 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 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | blank_issues_enabled: true 3 | contact_links: 4 | - name: Ask a question or get support 5 | url: https://github.com/arnested/go-version-action/discussions 6 | about: Please use the discussions forum 7 | - name: Do you need inspiration for your GitHub Action workflows? 8 | url: https://github.com/search?q=%22arnested%2Fgo-version-action%22+path%3A.github%2Fworkflows%2F&type=code 9 | about: Search all of GitHub for uses of the action in repositories 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always 11 | frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | All versions published [on the marketplace](https://github.com/marketplace/actions/go-version-action) are supported. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | Security issues can be reported to [Arne Jørgensen](https://github.com/arnested), 10 | either by [mail](mailto:arne@arnested.dk), [Signal](https://signal.me/#p/+4521650113), or any other channel you prefer and 11 | trust (see my [Keybase profile](https://keybase.io/arnested)). 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | - package-ecosystem: npm 5 | directory: / 6 | schedule: 7 | interval: monthly 8 | timezone: Europe/Copenhagen 9 | - package-ecosystem: github-actions 10 | directory: / 11 | schedule: 12 | interval: monthly 13 | timezone: Europe/Copenhagen 14 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'CodeQL' 3 | 4 | on: 5 | workflow_run: 6 | workflows: ['Build and test'] 7 | branches: [main] 8 | types: 9 | - completed 10 | pull_request: 11 | branches: [main] 12 | schedule: 13 | - cron: '20 18 * * 5' 14 | 15 | jobs: 16 | analyze: 17 | name: Analyze 18 | runs-on: ubuntu-latest 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | language: 24 | - 'javascript' 25 | 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@v4 29 | 30 | # Initializes the CodeQL tools for scanning. 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@v3 33 | with: 34 | languages: ${{ matrix.language }} 35 | 36 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 37 | # If this step fails, then you should remove it and run the build manually (see below) 38 | - name: Autobuild 39 | uses: github/codeql-action/autobuild@v3 40 | 41 | - run: | 42 | npm install 43 | 44 | - name: Perform CodeQL Analysis 45 | uses: github/codeql-action/analyze@v3 46 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Dependency Review Action 3 | # 4 | # This Action will scan dependency manifest files that change as part of a Pull Reqest, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. 5 | # 6 | # Source repository: https://github.com/actions/dependency-review-action 7 | # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement 8 | name: 'Dependency Review' 9 | on: [pull_request] 10 | 11 | permissions: 12 | contents: read 13 | 14 | jobs: 15 | dependency-review: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: 'Checkout Repository' 19 | uses: actions/checkout@v4 20 | - name: 'Dependency Review' 21 | uses: actions/dependency-review-action@v4 22 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Lint 3 | on: pull_request 4 | 5 | jobs: 6 | yamllint: 7 | name: yaml 8 | runs-on: ubuntu-22.04 9 | steps: 10 | - uses: actions/checkout@v4 11 | - name: Run yamllint 12 | uses: frenck/action-yamllint@v1.5.0 13 | with: 14 | strict: true 15 | 16 | markdownlint: 17 | name: markdown 18 | runs-on: ubuntu-22.04 19 | steps: 20 | - uses: actions/checkout@v4 21 | - name: Run markdownlint 22 | uses: DavidAnson/markdownlint-cli2-action@v20 23 | 24 | actionlint: 25 | name: action 26 | runs-on: ubuntu-22.04 27 | steps: 28 | - uses: actions/checkout@v4 29 | - name: Run actionlint 30 | uses: reviewdog/action-actionlint@v1 31 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release 3 | on: 4 | workflow_run: 5 | workflows: ['Build and test'] 6 | branches: [main] 7 | types: 8 | - completed 9 | 10 | jobs: 11 | release: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | - name: Rebuild dist 18 | id: dist 19 | run: | 20 | npm install 21 | npm run package 22 | git config user.name github-actions 23 | git config user.email github-actions@github.com 24 | git add --force dist/index.js 25 | (git commit -m 'Publish dist' && (echo 'changed=true' >> "$GITHUB_OUTPUT")) || (echo 'changed=false' >> "$GITHUB_OUTPUT") 26 | - name: Push rebuild dist 27 | if: ${{ steps.dist.outputs.changed == 'true' }} 28 | run: | 29 | git push origin main 30 | - name: Bump version and push tag 31 | uses: anothrNick/github-tag-action@1.73.0 32 | if: ${{ steps.dist.outputs.changed == 'true' }} 33 | id: version 34 | env: 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | WITH_V: true 37 | DEFAULT_BUMP: patch 38 | - name: Major version tag 39 | id: major-version 40 | if: ${{ (steps.dist.outputs.changed == 'true') && (steps.version.outputs.new_tag != '') }} 41 | uses: actions/github-script@v7 42 | with: 43 | script: | 44 | core.setOutput('tag', '${{ steps.version.outputs.new_tag }}'.split('.')[0]) 45 | - name: Tag ${{ steps.major-version.outputs.tag }} 46 | if: ${{ (steps.dist.outputs.changed == 'true') && (steps.version.outputs.new_tag != '') }} 47 | run: | 48 | git tag --force ${{ steps.major-version.outputs.tag }} 49 | git push origin ${{ steps.major-version.outputs.tag }} --force 50 | - name: Get current date 51 | if: ${{ (steps.dist.outputs.changed == 'true') && (steps.version.outputs.new_tag != '') }} 52 | id: date 53 | run: echo "date=$(date --iso-8601)" >> "$GITHUB_OUTPUT" 54 | - name: Build full ChangeLog 55 | if: ${{ (steps.dist.outputs.changed == 'true') && (steps.version.outputs.new_tag != '') }} 56 | run: npx conventional-changelog-cli --release-count=0 --preset=eslint --outfile="${{ runner.temp }}/FullChangeLog.md" 57 | - name: Create/update major version release 58 | if: ${{ (steps.dist.outputs.changed == 'true') && (steps.version.outputs.new_tag != '') }} 59 | uses: ncipollo/release-action@v1 60 | with: 61 | token: ${{ secrets.GITHUB_TOKEN }} 62 | allowUpdates: true 63 | tag: ${{ steps.major-version.outputs.tag }} 64 | name: ${{ steps.version.outputs.new_tag }} (${{ steps.date.outputs.date }}) 65 | bodyFile: ${{ runner.temp }}/FullChangeLog.md 66 | - name: Build ChangeLog 67 | if: ${{ (steps.dist.outputs.changed == 'true') && (steps.version.outputs.new_tag != '') }} 68 | run: npx conventional-changelog-cli --release-count=2 --outfile="${{ runner.temp }}/ChangeLog.md" 69 | - name: Create release 70 | if: ${{ (steps.dist.outputs.changed == 'true') && (steps.version.outputs.new_tag != '') }} 71 | uses: ncipollo/release-action@v1 72 | with: 73 | token: ${{ secrets.GITHUB_TOKEN }} 74 | tag: ${{ steps.version.outputs.new_tag }} 75 | name: Release ${{ steps.version.outputs.new_tag }} 76 | bodyFile: ${{ runner.temp }}/ChangeLog.md 77 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Build and test 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | - name: Build ChangeLog 17 | run: npx conventional-changelog-cli 18 | - run: | 19 | npm install 20 | - run: | 21 | npm run all 22 | test: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v4 26 | - run: | 27 | npm install 28 | npm run package 29 | - uses: ./ 30 | id: go-version-1 31 | with: 32 | working-directory: ./__tests__/testdata 33 | - run: | 34 | test '${{ steps.go-version-1.outputs.module }}' == 'example.com/go/testmodule' 35 | - run: | 36 | test '${{ steps.go-version-1.outputs.go-mod-version }}' == '1.16' 37 | - run: | 38 | test '${{ steps.go-version-1.outputs.minimal }}' == '1.16' 39 | - run: | 40 | test '${{ steps.go-version-1.outputs.latest }}' == '1.24' 41 | - run: | 42 | test '${{ steps.go-version-1.outputs.matrix }}' == '["1.16","1.17","1.18","1.19","1.20","1.21","1.22","1.23","1.24"]' 43 | - uses: ./ 44 | id: go-version-2 45 | with: 46 | working-directory: ./__tests__/testdata 47 | unsupported: false 48 | - run: | 49 | test '${{ steps.go-version-2.outputs.module }}' == 'example.com/go/testmodule' 50 | - run: | 51 | test '${{ steps.go-version-2.outputs.go-mod-version }}' == '1.16' 52 | - run: | 53 | test '${{ steps.go-version-2.outputs.minimal }}' == '1.23' 54 | - run: | 55 | test '${{ steps.go-version-2.outputs.latest }}' == '1.24' 56 | - run: | 57 | test '${{ steps.go-version-2.outputs.matrix }}' == '["1.23","1.24"]' 58 | - uses: ./ 59 | id: go-version-3 60 | with: 61 | working-directory: ./__tests__/testdata 62 | unsupported: false 63 | patch-level: true 64 | - run: | 65 | test '${{ steps.go-version-3.outputs.module }}' == 'example.com/go/testmodule' 66 | - run: | 67 | test '${{ steps.go-version-3.outputs.go-mod-version }}' == '1.16' 68 | - run: | 69 | test '${{ steps.go-version-3.outputs.minimal }}' == '1.23.10' 70 | - run: | 71 | test '${{ steps.go-version-3.outputs.latest }}' == '1.24.4' 72 | - run: | 73 | test '${{ steps.go-version-3.outputs.matrix }}' == '["1.23.10","1.24.4"]' 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directory 2 | node_modules 3 | 4 | # Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # Diagnostic reports (https://nodejs.org/api/report.html) 14 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | lib-cov 24 | 25 | # Coverage directory used by tools like istanbul 26 | coverage 27 | *.lcov 28 | 29 | # nyc test coverage 30 | .nyc_output 31 | 32 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 33 | .grunt 34 | 35 | # Bower dependency directory (https://bower.io/) 36 | bower_components 37 | 38 | # node-waf configuration 39 | .lock-wscript 40 | 41 | # Compiled binary addons (https://nodejs.org/api/addons.html) 42 | build/Release 43 | 44 | # Dependency directories 45 | jspm_packages/ 46 | 47 | # TypeScript v1 declaration files 48 | typings/ 49 | 50 | # TypeScript cache 51 | *.tsbuildinfo 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Optional REPL history 60 | .node_repl_history 61 | 62 | # Output of 'npm pack' 63 | *.tgz 64 | 65 | # Yarn Integrity file 66 | .yarn-integrity 67 | 68 | # dotenv environment variables file 69 | .env 70 | .env.test 71 | 72 | # parcel-bundler cache (https://parceljs.org/) 73 | .cache 74 | 75 | # next.js build output 76 | .next 77 | 78 | # nuxt.js build output 79 | .nuxt 80 | 81 | # vuepress build output 82 | .vuepress/dist 83 | 84 | # Serverless directories 85 | .serverless/ 86 | 87 | # FuseBox cache 88 | .fusebox/ 89 | 90 | # DynamoDB Local files 91 | .dynamodb/ 92 | 93 | # OS metadata 94 | .DS_Store 95 | Thumbs.db 96 | 97 | # Ignore built ts files 98 | __tests__/runner/* 99 | 100 | dist/ 101 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD013": { "code_blocks": false } 3 | } 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | lib/ 3 | node_modules/ -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": false, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": false, 9 | "arrowParens": "avoid" 10 | } 11 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | ignore-from-file: 5 | - .gitignore 6 | 7 | rules: 8 | indentation: 9 | spaces: 2 10 | line-length: disable 11 | truthy: 12 | check-keys: false 13 | braces: 14 | min-spaces-inside: 1 15 | max-spaces-inside: 1 16 | min-spaces-inside-empty: 0 17 | max-spaces-inside-empty: 0 18 | -------------------------------------------------------------------------------- /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 make 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 within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be 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 . 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], 71 | version 1.4, available at 72 | 73 | 74 | [homepage]: https://www.contributor-covenant.org 75 | 76 | For answers to common questions about this code of conduct, see 77 | 78 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Developer Certificate of Origin 2 | 3 | ```text 4 | Version 1.1 5 | 6 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 7 | 1 Letterman Drive 8 | Suite D4700 9 | San Francisco, CA, 94129 10 | 11 | Everyone is permitted to copy and distribute verbatim copies of this 12 | license document, but changing it is not allowed. 13 | 14 | 15 | Developer's Certificate of Origin 1.1 16 | 17 | By making a contribution to this project, I certify that: 18 | 19 | (a) The contribution was created in whole or in part by me and I 20 | have the right to submit it under the open source license 21 | indicated in the file; or 22 | 23 | (b) The contribution is based upon previous work that, to the best 24 | of my knowledge, is covered under an appropriate open source 25 | license and I have the right under that license to submit that 26 | work with modifications, whether created in whole or in part 27 | by me, under the same open source license (unless I am 28 | permitted to submit under a different license), as indicated 29 | in the file; or 30 | 31 | (c) The contribution was provided directly to me by some other 32 | person who certified (a), (b) or (c) and I have not modified 33 | it. 34 | 35 | (d) I understand and agree that this project and the contribution 36 | are public and that a record of the contribution (including all 37 | personal information I submit with it, including my sign-off) is 38 | maintained indefinitely and may be redistributed consistent with 39 |   this project or the open source license(s) involved. 40 | ``` 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2021, 2022, 2023 Arne Jørgensen 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go version action 2 | 3 | A GitHub action for using the latest released Go version and your 4 | projects minimal support Go version (from go.mod), and a build matrix 5 | of them, and all versions in between. 6 | 7 | ## Motive 8 | 9 | Being consistent is hard. 10 | 11 | I used to hard-code the Go versions my projects needed for test and 12 | builds in my GitHub Actions workflow files. 13 | 14 | Of course, the result was that I used different versions in the 15 | `go.mod` file and my workflow files. 16 | 17 | Whenever a new version of Go was released, I forgot to add the new 18 | version to my build matrix and my projects weren't tested on the new 19 | release(s). 20 | 21 | So, I build this action. 22 | 23 | The action reads the minimal supported Go version from your `go.mod` 24 | file and exposes it as a variable to your workflow. 25 | 26 | It also pulls the list of release tags from 27 | [https://go.dev/dl/](https://go.dev/dl/) and exposes the latest 28 | released Go version as a variable as well. 29 | 30 | From the list of released Go versions and the minimal version, your 31 | module supports we also build a "matrix" variable to be used as a 32 | build matrix. 33 | 34 | While we are at it, we also extract the module path from the `go.mod` 35 | file, even though it hasn't really anything to do with versions ;) 36 | 37 | ## Inputs 38 | 39 | If your `go mod` file is located in a non-standard location, you can 40 | specify the working directory where it is located: 41 | 42 | ```yaml 43 | working-directory: 44 | description: Working directory where your go.mod file is located 45 | required: false 46 | default: . 47 | unstable: 48 | description: Include unstable versions of Go (beta, release candidates) 49 | required: false 50 | default: 'false' 51 | unsupported: 52 | description: Include unsupported versions of Go 53 | required: false 54 | default: 'true' 55 | patch-level: 56 | description: Include the patch levels on the versions (default is major.minor) 57 | required: false 58 | default: 'false' 59 | ``` 60 | 61 | ## Outputs 62 | 63 | ```yaml 64 | go-mod-version: 65 | description: The Go version specified by go.mod 66 | latest: 67 | description: The latest Go version 68 | minimal: 69 | description: The minimal Go version 70 | matrix: 71 | description: A (stringified) array of Go versions from the minimal supported version to the latest released version 72 | module: 73 | description: The Go module path (as specified by go.mod) 74 | ``` 75 | 76 | ## Examples 77 | 78 | Let's say your `go.mod` specifies Go 1.13 as the minimal supported 79 | version and you want your workflow to set up Go version 1.13 using the 80 | [actions/setup-go](https://github.com/actions/setup-go) action: 81 | 82 | ```yaml 83 | name: My Go workflow 84 | on: pull_request 85 | 86 | jobs: 87 | my-go-workflow: 88 | runs-on: ubuntu-latest 89 | steps: 90 | - uses: actions/checkout@v3 91 | - uses: arnested/go-version-action@v1 92 | id: go-version 93 | - name: Install Go ${{ steps.go-version.outputs.minimal }} 94 | uses: actions/setup-go@v3 95 | with: 96 | go-version: ${{ steps.go-version.outputs.minimal }} 97 | check-latest: true 98 | ``` 99 | 100 | ![Log of running action](docs/action-run.png) 101 | 102 | If you want do a matrix test of all Go versions from your minimally 103 | supported version up to the latest released version we need to do a 104 | bit more. 105 | 106 | We have to run the version lookup as a separate job and let the test 107 | job depend on it: 108 | 109 | ```yaml 110 | on: push 111 | name: Test 112 | 113 | jobs: 114 | go-versions: 115 | name: Lookup Go versions 116 | runs-on: ubuntu-latest 117 | outputs: 118 | matrix: ${{ steps.versions.outputs.matrix }} 119 | steps: 120 | - uses: actions/checkout@v3 121 | - uses: arnested/go-version-action@v1 122 | id: versions 123 | test: 124 | name: Test 125 | runs-on: ubuntu-latest 126 | needs: go-versions 127 | strategy: 128 | matrix: 129 | version: ${{ fromJSON(needs.go-versions.outputs.matrix) }} 130 | steps: 131 | - uses: actions/checkout@v3 132 | - name: Install Go 133 | uses: actions/setup-go@v3 134 | with: 135 | go-version: ${{ matrix.version }} 136 | check-latest: true 137 | - name: Go test 138 | run: go test -v -race -cover -covermode=atomic -coverprofile=coverage.txt ./... 139 | ``` 140 | 141 | ![The workflow summary](docs/action-matrix-summary.png) 142 | 143 | ## Summary 144 | 145 | The action writes a [GitHub Actions Job 146 | Summary](https://github.blog/2022-05-09-supercharging-github-actions-with-job-summaries/) 147 | with values it identified. 148 | 149 | This example is from running with: 150 | 151 | ```yaml 152 | unsupported: false 153 | patch-level: true 154 | ``` 155 | 156 | ![Job summary](docs/job-summary.png) 157 | -------------------------------------------------------------------------------- /__tests__/main.test.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import { 3 | getGoModVersion, 4 | gomod, 5 | latest, 6 | matrix, 7 | minimal, 8 | modulename 9 | } from '../src/go-versions.js' 10 | 11 | test('test module name', () => { 12 | const content = gomod('__tests__/testdata/go.mod') 13 | expect(modulename(content)).toEqual('example.com/go/testmodule') 14 | }) 15 | 16 | test('test go mod version', () => { 17 | const content = gomod('__tests__/testdata/go.mod') 18 | expect(getGoModVersion(content)).toEqual('1.16') 19 | }) 20 | 21 | test('test minimal version', () => { 22 | expect(minimal(['1.16', '1.17', '1.18'])).toEqual('1.16') 23 | }) 24 | 25 | test('test latest version', () => { 26 | expect(latest(['1.16', '1.17', '1.18'])).toEqual('1.18') 27 | }) 28 | 29 | test('test version matrix', () => { 30 | const t = JSON.parse(fs.readFileSync('__tests__/testdata/dl.json', 'utf8')) 31 | const m = matrix('1.16', false, false, t) 32 | expect(m).toEqual(['1.16', '1.17', '1.18']) 33 | }) 34 | 35 | test('test unstable version matrix', () => { 36 | const t = JSON.parse(fs.readFileSync('__tests__/testdata/dl.json', 'utf8')) 37 | const m = matrix('1.16', true, false, t) 38 | expect(m).toEqual([ 39 | '1.16beta1', 40 | '1.16rc1', 41 | '1.16', 42 | '1.17beta1', 43 | '1.17rc1', 44 | '1.17rc2', 45 | '1.17', 46 | '1.18beta1', 47 | '1.18beta2', 48 | '1.18rc1', 49 | '1.18' 50 | ]) 51 | }) 52 | 53 | test('test patch level version matrix', () => { 54 | const t = JSON.parse(fs.readFileSync('__tests__/testdata/dl.json', 'utf8')) 55 | const m = matrix('1.16', false, true, t) 56 | expect(m).toEqual([ 57 | '1.16', 58 | '1.16.1', 59 | '1.16.2', 60 | '1.16.3', 61 | '1.16.4', 62 | '1.16.5', 63 | '1.16.6', 64 | '1.16.7', 65 | '1.16.8', 66 | '1.16.9', 67 | '1.16.10', 68 | '1.16.11', 69 | '1.16.12', 70 | '1.16.13', 71 | '1.16.14', 72 | '1.16.15', 73 | '1.17', 74 | '1.17.1', 75 | '1.17.2', 76 | '1.17.3', 77 | '1.17.4', 78 | '1.17.5', 79 | '1.17.6', 80 | '1.17.7', 81 | '1.17.8', 82 | '1.18' 83 | ]) 84 | }) 85 | 86 | test('test patch level, unstable version matrix', () => { 87 | const t = JSON.parse(fs.readFileSync('__tests__/testdata/dl.json', 'utf8')) 88 | const m = matrix('1.16', true, true, t) 89 | expect(m).toEqual([ 90 | '1.16beta1', 91 | '1.16rc1', 92 | '1.16', 93 | '1.16.1', 94 | '1.16.2', 95 | '1.16.3', 96 | '1.16.4', 97 | '1.16.5', 98 | '1.16.6', 99 | '1.16.7', 100 | '1.16.8', 101 | '1.16.9', 102 | '1.16.10', 103 | '1.16.11', 104 | '1.16.12', 105 | '1.16.13', 106 | '1.16.14', 107 | '1.16.15', 108 | '1.17beta1', 109 | '1.17rc1', 110 | '1.17rc2', 111 | '1.17', 112 | '1.17.1', 113 | '1.17.2', 114 | '1.17.3', 115 | '1.17.4', 116 | '1.17.5', 117 | '1.17.6', 118 | '1.17.7', 119 | '1.17.8', 120 | '1.18beta1', 121 | '1.18beta2', 122 | '1.18rc1', 123 | '1.18' 124 | ]) 125 | }) 126 | -------------------------------------------------------------------------------- /__tests__/testdata/go.mod: -------------------------------------------------------------------------------- 1 | module example.com/go/testmodule 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/jimmyfrasche/autoreadme v0.0.0-20180503232641-58e67811d607 7 | golang.org/x/tools v0.0.0-20191026034945-b2104f82a97d 8 | ) 9 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Go version action 3 | author: Arne Jørgensen 4 | description: A GitHub Action providing the latest Go version, your project's minimum Go version, and a build matrix to your workflow 5 | branding: 6 | icon: box 7 | color: blue 8 | inputs: 9 | working-directory: 10 | description: Working directory where your go.mod file is located 11 | required: false 12 | default: . 13 | unstable: 14 | description: Include unstable versions of Go (beta, release candidates) 15 | required: false 16 | default: 'false' 17 | unsupported: 18 | description: Include unsupported versions of Go 19 | required: false 20 | default: 'true' 21 | patch-level: 22 | description: Include the patch levels on the versions (default is major.minor) 23 | required: false 24 | default: 'false' 25 | outputs: 26 | go-mod-version: 27 | description: The Go version specified by go.mod 28 | latest: 29 | description: The latest Go version 30 | minimal: 31 | description: The minimal Go version 32 | matrix: 33 | description: A (stringified) array of Go versions from the minimal supported version to the latest released version 34 | module: 35 | description: The Go module path (as specified by go.mod) 36 | runs: 37 | using: node20 38 | main: dist/index.js 39 | -------------------------------------------------------------------------------- /babel.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 'current' 8 | } 9 | } 10 | ] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /docs/action-matrix-summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnested/go-version-action/b32660f6c48d7829b7505f9cd560b6f0d05e0725/docs/action-matrix-summary.png -------------------------------------------------------------------------------- /docs/action-run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnested/go-version-action/b32660f6c48d7829b7505f9cd560b6f0d05e0725/docs/action-run.png -------------------------------------------------------------------------------- /docs/job-summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arnested/go-version-action/b32660f6c48d7829b7505f9cd560b6f0d05e0725/docs/job-summary.png -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import path from "node:path"; 3 | import { fileURLToPath } from "node:url"; 4 | import js from "@eslint/js"; 5 | import { FlatCompat } from "@eslint/eslintrc"; 6 | 7 | const __filename = fileURLToPath(import.meta.url); 8 | const __dirname = path.dirname(__filename); 9 | const compat = new FlatCompat({ 10 | baseDirectory: __dirname, 11 | recommendedConfig: js.configs.recommended, 12 | allConfig: js.configs.all 13 | }); 14 | 15 | export default [{ 16 | ignores: ["**/dist/", "**/node_modules/"], 17 | }, ...compat.extends("eslint:recommended"), { 18 | languageOptions: { 19 | globals: { 20 | ...globals.node, 21 | ...globals.jest, 22 | }, 23 | 24 | ecmaVersion: 2020, 25 | sourceType: "module", 26 | }, 27 | }]; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "go-version-action", 3 | "private": true, 4 | "description": "Go Version action", 5 | "main": "src/main.js", 6 | "scripts": { 7 | "format": "prettier --write **/*.js", 8 | "format-check": "prettier --check **/*.js", 9 | "lint": "eslint .", 10 | "lint-fix": "eslint --fix .", 11 | "test": "jest", 12 | "package": "ncc build --minify", 13 | "all": "npm run format && npm run lint && npm run package && npm test" 14 | }, 15 | "type": "module", 16 | "keywords": [ 17 | "actions", 18 | "go" 19 | ], 20 | "author": "Arne Jørgensen", 21 | "license": "MIT", 22 | "dependencies": { 23 | "@actions/core": "^1.11.1", 24 | "node-fetch": "~2.7.0", 25 | "semver": "^7.7.2" 26 | }, 27 | "devDependencies": { 28 | "@babel/preset-env": "^7.27.2", 29 | "@vercel/ncc": "^0.38.3", 30 | "babel-jest": "^29.7.0", 31 | "eslint": "^9.28.0", 32 | "eslint-plugin-github": "^6.0.0", 33 | "eslint-plugin-jest": "^28.12.0", 34 | "jest": "^29.7.0", 35 | "js-yaml": "^4.1.0", 36 | "prettier": "3.5.3" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/go-versions.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import fetch from 'node-fetch' 3 | import semverCoerce from 'semver/functions/coerce.js' 4 | import semverGte from 'semver/functions/gte.js' 5 | import semverLte from 'semver/functions/lte.js' 6 | 7 | const gomod = path => { 8 | return fs.readFileSync(path, 'utf8') 9 | } 10 | 11 | const getGoModVersion = content => { 12 | const r = /\ngo ([^\n]*)\n/s 13 | const matches = r.exec(content) 14 | if (matches === null) { 15 | throw new Error('No go version in go.mod') 16 | } 17 | return matches[1] 18 | } 19 | 20 | const modulename = content => { 21 | const r = /module ([^\n]*)\n/s 22 | const matches = r.exec(content) 23 | if (matches === null) { 24 | throw new Error('No module path in go.mod') 25 | } 26 | return matches[1] 27 | } 28 | 29 | const getVersions = async withUnsupported => { 30 | let url = 'https://go.dev/dl/?mode=json' 31 | if (withUnsupported) { 32 | url += '&include=all' 33 | } 34 | const response = await fetch(url) 35 | if (!response.ok) { 36 | throw new Error( 37 | `Could not fetch Go versions from https://go.dev/dl/: ${response.status}` 38 | ) 39 | } 40 | const result = await response.json() 41 | return result 42 | } 43 | 44 | const matrix = (min, withUnstable, withPatchLevel, tags) => { 45 | const minClean = semverCoerce(min) 46 | if (minClean === null) { 47 | throw new Error(`Minimal version isn't quite right: ${min}`) 48 | } 49 | if (!withUnstable) { 50 | tags = tags.filter(tag => tag.stable === true) 51 | } 52 | const r = /^go(.*)$/ 53 | let versions = tags.map(tag => { 54 | const matches = r.exec(tag.version) 55 | return matches ? matches[1] : tag.version 56 | }) 57 | versions = versions.filter(v => { 58 | const v2 = semverCoerce(v) 59 | return v2 !== null && semverGte(v2, minClean) 60 | }) 61 | if (!withPatchLevel) { 62 | versions = versions.map(version => { 63 | const parts = version.split('.') 64 | return `${parts[0]}.${parts[1]}` 65 | }) 66 | } 67 | versions = [...new Set(versions)] 68 | return versions.reverse() 69 | } 70 | 71 | const latest = versions => { 72 | return versions.reduce((acc, val) => { 73 | const a = semverCoerce(acc) 74 | const v = semverCoerce(val) 75 | if (v !== null && a !== null && semverGte(v, a)) { 76 | return val 77 | } 78 | return acc 79 | }) 80 | } 81 | 82 | const minimal = versions => { 83 | return versions.reduce((acc, val) => { 84 | const a = semverCoerce(acc) 85 | const v = semverCoerce(val) 86 | if (v !== null && a !== null && semverLte(v, a)) { 87 | return val 88 | } 89 | return acc 90 | }) 91 | } 92 | 93 | export { 94 | gomod, 95 | latest, 96 | matrix, 97 | minimal, 98 | modulename, 99 | getGoModVersion, 100 | getVersions 101 | } 102 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core' 2 | import { 3 | getVersions, 4 | gomod, 5 | latest, 6 | matrix, 7 | minimal, 8 | modulename, 9 | getGoModVersion 10 | } from './go-versions.js' 11 | 12 | async function run() { 13 | try { 14 | if (process.env.GITHUB_TOKEN !== undefined) { 15 | core.warning( 16 | 'arnested/go-version-action no longer needs a GITHUB_TOKEN. You should remove it.' 17 | ) 18 | } 19 | 20 | const workingDirectory = core.getInput('working-directory') 21 | const withUnsupported = core.getBooleanInput('unsupported') 22 | const withUnstable = core.getBooleanInput('unstable') 23 | const withPatchLevel = core.getBooleanInput('patch-level') 24 | const content = gomod(`${workingDirectory}/go.mod`) 25 | const name = modulename(content) 26 | const goModVersion = getGoModVersion(content) 27 | const versions = await getVersions(withUnsupported) 28 | const mat = matrix(goModVersion, withUnstable, withPatchLevel, versions) 29 | const lat = latest(mat) 30 | const min = minimal(mat) 31 | 32 | core.setOutput('module', name) 33 | core.setOutput('go-mod-version', goModVersion) 34 | core.setOutput('minimal', min) 35 | core.setOutput('matrix', mat) 36 | core.setOutput('latest', lat) 37 | core.info(`go module path: ${name}`) 38 | core.info(`go mod version: ${goModVersion}`) 39 | core.info(`minimal go version: ${min}`) 40 | core.info(`latest go version: ${lat} - from https://go.dev/dl/`) 41 | core.info(`go version matrix: ${mat} - from https://go.dev/dl/`) 42 | 43 | const htmlMat = mat 44 | .map(v => `Go ${v}`) 45 | .join('
') 46 | 47 | await core.summary 48 | .addTable([ 49 | [ 50 | {data: 'Output', header: true}, 51 | {data: 'Value', header: true} 52 | ], 53 | ['module', `${name}`], 54 | [ 55 | 'go.mod version', 56 | `Go ${goModVersion}` 57 | ], 58 | ['minimal', `Go ${min}`], 59 | ['latest', `Go ${lat}`], 60 | ['matrix', `${htmlMat}`] 61 | ]) 62 | .write() 63 | } catch (error) { 64 | core.setFailed(error.message) 65 | } 66 | } 67 | 68 | run() 69 | --------------------------------------------------------------------------------