├── .github
├── CODEOWNERS
├── dependabot.yml
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── bug_report.md
│ └── feature_request.md
├── SECURITY.md
└── workflows
│ ├── github-actions.yml
│ ├── yaml.yml
│ ├── markdown.yml
│ ├── lint.yml
│ ├── dependency-review.yml
│ ├── test.yml
│ └── release.yml
├── .prettierignore
├── .gitattributes
├── .markdownlint.json
├── docs
├── action-run.png
├── job-summary.png
└── action-matrix-summary.png
├── babel.config.cjs
├── __tests__
├── testdata
│ └── go.mod
└── main.test.js
├── .prettierrc.json
├── .yamllint
├── eslint.config.mjs
├── package.json
├── LICENSE
├── action.yml
├── CONTRIBUTING.md
├── .gitignore
├── src
├── main.js
└── go-versions.js
├── CODE_OF_CONDUCT.md
└── README.md
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @arnested
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist/
2 | lib/
3 | node_modules/
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | dist/** -diff linguist-generated=true
--------------------------------------------------------------------------------
/.markdownlint.json:
--------------------------------------------------------------------------------
1 | {
2 | "MD013": { "code_blocks": false }
3 | }
4 |
--------------------------------------------------------------------------------
/docs/action-run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arnested/go-version-action/HEAD/docs/action-run.png
--------------------------------------------------------------------------------
/docs/job-summary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arnested/go-version-action/HEAD/docs/job-summary.png
--------------------------------------------------------------------------------
/docs/action-matrix-summary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arnested/go-version-action/HEAD/docs/action-matrix-summary.png
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/__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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.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/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/workflows/github-actions.yml:
--------------------------------------------------------------------------------
1 | ---
2 | on: pull_request
3 | name: GitHub Actions
4 |
5 | # Detect if this action is already running, and cancel it.
6 | # This most likely happened because a second push has been made to a branch.
7 | concurrency:
8 | group: ${{ github.repository_id }}-${{ github.workflow }}-${{ github.ref }}
9 | cancel-in-progress: true
10 |
11 | permissions:
12 | contents: read
13 | pull-requests: write
14 |
15 | jobs:
16 | actionlint:
17 | name: Actionlint
18 | runs-on: ubuntu-24.04
19 | steps:
20 | - uses: actions/checkout@v6
21 | - uses: reviewdog/action-actionlint@v1
22 |
--------------------------------------------------------------------------------
/.github/workflows/yaml.yml:
--------------------------------------------------------------------------------
1 | ---
2 | on: pull_request
3 | name: YAML
4 | permissions:
5 | contents: read
6 |
7 | # Detect if this action is already running, and cancel it.
8 | # This most likely happened because a second push has been made to a branch.
9 | concurrency:
10 | group: ${{ github.repository_id }}-${{ github.workflow }}-${{ github.ref }}
11 | cancel-in-progress: true
12 |
13 | jobs:
14 | yaml-cs-fixer:
15 | name: Yamllint
16 | runs-on: ubuntu-24.04
17 | steps:
18 | - uses: actions/checkout@v6
19 | - name: Run Yamllint
20 | uses: frenck/action-yamllint@v1.5.0
21 | with:
22 | strict: true
23 |
--------------------------------------------------------------------------------
/.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/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/workflows/markdown.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Markdown
3 | on: pull_request
4 |
5 | # Detect if this action is already running, and cancel it.
6 | # This most likely happened because a second push has been made to a branch.
7 | concurrency:
8 | group: ${{ github.repository_id }}-${{ github.workflow }}-${{ github.ref }}
9 | cancel-in-progress: true
10 |
11 | permissions:
12 | contents: read
13 | pull-requests: write
14 |
15 | jobs:
16 | markdown:
17 | name: Markdownlint
18 | runs-on: ubuntu-24.04
19 | steps:
20 | - uses: actions/checkout@v6
21 | - name: Run markdownlint
22 | uses: DavidAnson/markdownlint-cli2-action@v21
23 | with:
24 | globs: |
25 | *.md
26 | docs/*.md
27 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Lint
3 | on: pull_request
4 | permissions:
5 | contents: read
6 |
7 | jobs:
8 | yamllint:
9 | name: yaml
10 | runs-on: ubuntu-24.04
11 | steps:
12 | - uses: actions/checkout@v6
13 | - name: Run yamllint
14 | uses: frenck/action-yamllint@v1.5.0
15 | with:
16 | strict: true
17 |
18 | markdownlint:
19 | name: markdown
20 | runs-on: ubuntu-24.04
21 | steps:
22 | - uses: actions/checkout@v6
23 | - name: Run markdownlint
24 | uses: DavidAnson/markdownlint-cli2-action@v21
25 |
26 | actionlint:
27 | name: action
28 | runs-on: ubuntu-24.04
29 | steps:
30 | - uses: actions/checkout@v6
31 | - name: Run actionlint
32 | uses: reviewdog/action-actionlint@v1
33 |
--------------------------------------------------------------------------------
/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 | }];
--------------------------------------------------------------------------------
/.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-24.04
17 | steps:
18 | - name: 'Checkout Repository'
19 | uses: actions/checkout@v6
20 | - name: 'Dependency Review'
21 | uses: actions/dependency-review-action@v4
22 |
--------------------------------------------------------------------------------
/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.3"
26 | },
27 | "devDependencies": {
28 | "@babel/preset-env": "^7.28.5",
29 | "@vercel/ncc": "^0.38.4",
30 | "babel-jest": "^30.2.0",
31 | "eslint": "^9.39.1",
32 | "eslint-plugin-github": "^6.0.0",
33 | "eslint-plugin-jest": "^29.2.1",
34 | "globals": "^16.5.0",
35 | "jest": "^30.2.0",
36 | "js-yaml": "^4.1.1",
37 | "prettier": "3.7.3"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/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.
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Build and test
3 | permissions:
4 | contents: read
5 | on:
6 | pull_request:
7 | push:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-24.04
14 | steps:
15 | - uses: actions/checkout@v6
16 | with:
17 | fetch-depth: 0
18 | - name: Build ChangeLog
19 | run: npx conventional-changelog-cli
20 | - run: |
21 | npm install
22 | - run: |
23 | npm run all
24 | test:
25 | runs-on: ubuntu-24.04
26 | steps:
27 | - uses: actions/checkout@v6
28 | - run: |
29 | npm install
30 | npm run package
31 | - uses: ./
32 | id: go-version-1
33 | with:
34 | working-directory: ./__tests__/testdata
35 | - run: |
36 | test '${{ steps.go-version-1.outputs.module }}' == 'example.com/go/testmodule'
37 | - run: |
38 | test '${{ steps.go-version-1.outputs.go-mod-version }}' == '1.16'
39 | - run: |
40 | test '${{ steps.go-version-1.outputs.minimal }}' == '1.16'
41 | - run: |
42 | test '${{ steps.go-version-1.outputs.latest }}' == '1.25'
43 | - run: |
44 | 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","1.25"]'
45 | - uses: ./
46 | id: go-version-2
47 | with:
48 | working-directory: ./__tests__/testdata
49 | unsupported: false
50 | - run: |
51 | test '${{ steps.go-version-2.outputs.module }}' == 'example.com/go/testmodule'
52 | - run: |
53 | test '${{ steps.go-version-2.outputs.go-mod-version }}' == '1.16'
54 | - run: |
55 | test '${{ steps.go-version-2.outputs.minimal }}' == '1.24'
56 | - run: |
57 | test '${{ steps.go-version-2.outputs.latest }}' == '1.25'
58 | - run: |
59 | test '${{ steps.go-version-2.outputs.matrix }}' == '["1.24","1.25"]'
60 | - uses: ./
61 | id: go-version-3
62 | with:
63 | working-directory: ./__tests__/testdata
64 | unsupported: false
65 | patch-level: true
66 | - run: |
67 | test '${{ steps.go-version-3.outputs.module }}' == 'example.com/go/testmodule'
68 | - run: |
69 | test '${{ steps.go-version-3.outputs.go-mod-version }}' == '1.16'
70 | - run: |
71 | test '${{ steps.go-version-3.outputs.minimal }}' == '1.24.11'
72 | - run: |
73 | test '${{ steps.go-version-3.outputs.latest }}' == '1.25.5'
74 | - run: |
75 | test '${{ steps.go-version-3.outputs.matrix }}' == '["1.24.11","1.25.5"]'
76 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/__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 |
--------------------------------------------------------------------------------
/.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-24.04
13 | permissions:
14 | contents: write
15 | steps:
16 | - uses: actions/checkout@v6
17 | with:
18 | fetch-depth: 0
19 | - name: Rebuild dist
20 | id: dist
21 | run: |
22 | npm install
23 | npm run package
24 | git config user.name github-actions
25 | git config user.email github-actions@github.com
26 | git add --force dist/index.js
27 | (git commit -m 'Publish dist' && (echo 'changed=true' >> "$GITHUB_OUTPUT")) || (echo 'changed=false' >> "$GITHUB_OUTPUT")
28 | - name: Push rebuild dist
29 | if: ${{ steps.dist.outputs.changed == 'true' }}
30 | run: |
31 | git push origin main
32 | - name: Bump version and push tag
33 | uses: anothrNick/github-tag-action@1.75.0
34 | if: ${{ steps.dist.outputs.changed == 'true' }}
35 | id: version
36 | env:
37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38 | WITH_V: true
39 | DEFAULT_BUMP: patch
40 | - name: Major version tag
41 | id: major-version
42 | if: ${{ (steps.dist.outputs.changed == 'true') && (steps.version.outputs.new_tag != '') }}
43 | uses: actions/github-script@v8
44 | with:
45 | script: |
46 | core.setOutput('tag', '${{ steps.version.outputs.new_tag }}'.split('.')[0])
47 | - name: Tag ${{ steps.major-version.outputs.tag }}
48 | if: ${{ (steps.dist.outputs.changed == 'true') && (steps.version.outputs.new_tag != '') }}
49 | run: |
50 | git tag --force ${{ steps.major-version.outputs.tag }}
51 | git push origin ${{ steps.major-version.outputs.tag }} --force
52 | - name: Get current date
53 | if: ${{ (steps.dist.outputs.changed == 'true') && (steps.version.outputs.new_tag != '') }}
54 | id: date
55 | run: echo "date=$(date --iso-8601)" >> "$GITHUB_OUTPUT"
56 | - name: Build full ChangeLog
57 | if: ${{ (steps.dist.outputs.changed == 'true') && (steps.version.outputs.new_tag != '') }}
58 | run: npx conventional-changelog-cli --release-count=0 --preset=eslint --outfile="${{ runner.temp }}/FullChangeLog.md"
59 | - name: Create/update major version release
60 | if: ${{ (steps.dist.outputs.changed == 'true') && (steps.version.outputs.new_tag != '') }}
61 | uses: ncipollo/release-action@v1
62 | with:
63 | token: ${{ secrets.GITHUB_TOKEN }}
64 | allowUpdates: true
65 | tag: ${{ steps.major-version.outputs.tag }}
66 | name: ${{ steps.version.outputs.new_tag }} (${{ steps.date.outputs.date }})
67 | bodyFile: ${{ runner.temp }}/FullChangeLog.md
68 | - name: Build ChangeLog
69 | if: ${{ (steps.dist.outputs.changed == 'true') && (steps.version.outputs.new_tag != '') }}
70 | run: npx conventional-changelog-cli --release-count=2 --outfile="${{ runner.temp }}/ChangeLog.md"
71 | - name: Create release
72 | if: ${{ (steps.dist.outputs.changed == 'true') && (steps.version.outputs.new_tag != '') }}
73 | uses: ncipollo/release-action@v1
74 | with:
75 | token: ${{ secrets.GITHUB_TOKEN }}
76 | tag: ${{ steps.version.outputs.new_tag }}
77 | name: Release ${{ steps.version.outputs.new_tag }}
78 | bodyFile: ${{ runner.temp }}/ChangeLog.md
79 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | 
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 | 
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 | 
157 |
--------------------------------------------------------------------------------