diff --git a/.github/commitlint.config.mjs b/.github/commitlint.config.mjs new file mode 100644 index 0000000..51b1c33 --- /dev/null +++ b/.github/commitlint.config.mjs @@ -0,0 +1,29 @@ +/* Taken from: https://github.com/wagoid/commitlint-github-action/blob/7f0a61df502599e1f1f50880aaa7ec1e2c0592f2/commitlint.config.mjs */ +/* eslint-disable import/no-extraneous-dependencies */ +import { maxLineLength } from '@commitlint/ensure' + +const bodyMaxLineLength = 100 + +const validateBodyMaxLengthIgnoringDeps = (parsedCommit) => { + const { type, scope, body } = parsedCommit + const isDepsCommit = + type === 'chore' && (scope === 'deps' || scope === 'deps-dev') + + return [ + isDepsCommit || !body || maxLineLength(body, bodyMaxLineLength), + `body's lines must not be longer than ${bodyMaxLineLength}`, + ] +} + +export default { + extends: ['@commitlint/config-conventional'], + plugins: ['commitlint-plugin-function-rules'], + rules: { + 'body-max-line-length': [0], + 'function-rules/body-max-line-length': [ + 2, + 'always', + validateBodyMaxLengthIgnoringDeps, + ], + }, +} diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000..c344a50 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,44 @@ +version: 2 +updates: +- package-ecosystem: github-actions + commit-message: + prefix: chore + include: scope + directory: / + schedule: + interval: monthly + groups: + github-actions: + patterns: + - "*" + update-types: + - "minor" + - "patch" +- package-ecosystem: docker + commit-message: + prefix: chore + include: scope + directory: / + schedule: + interval: monthly + groups: + docker: + patterns: + - "*" + update-types: + - "minor" + - "patch" +- package-ecosystem: gomod + commit-message: + prefix: chore + include: scope + directory: / + schedule: + interval: monthly + groups: + gomod: + patterns: + - "*" + update-types: + - "minor" + - "patch" diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 5d219e8..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,10 +0,0 @@ -version: 2 -updates: -- package-ecosystem: github-actions - directory: / - schedule: - interval: daily -- package-ecosystem: gomod - directory: / - schedule: - interval: daily diff --git a/.github/dependency-review-config.yaml b/.github/dependency-review-config.yaml new file mode 100644 index 0000000..08389a1 --- /dev/null +++ b/.github/dependency-review-config.yaml @@ -0,0 +1,20 @@ +# https://github.com/cncf/foundation/blob/main/allowed-third-party-license-policy.md +allow-licenses: +- 'Apache-2.0' +- 'BSD-2-Clause' +- 'BSD-2-Clause-FreeBSD' +- 'BSD-3-Clause' +- 'ISC' +- 'MIT' +- 'PostgreSQL' +- 'Python-2.0' +- 'X11' +- 'Zlib' + +allow-dependencies-licenses: +# this action is GPL-3 but it is only used in CI +# https://github.com/actions/dependency-review-action/issues/530#issuecomment-1638291806 +- pkg:githubactions/vladopajic/go-test-coverage@bcd064e5ceef1ccec5441519eb054263b6a44787 +# this package is MPL-2.0 and has a CNCF exception +# https://github.com/cncf/foundation/blob/9b8c9173c2101c1b4aedad3caf2c0128715133f6/license-exceptions/cncf-exceptions-2022-04-12.json#L43C17-L43C47 +- pkg:golang/github.com/go-sql-driver/mysql diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..cf66760 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,46 @@ +name: build +on: + pull_request: + branches: + - main +permissions: {} +jobs: + build-snapshot: + permissions: + contents: read + runs-on: ubuntu-latest + strategy: + matrix: + binary: + - jiratime + steps: + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + with: + ref: ${{ github.event.pull_request.head.sha }} + - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + with: + go-version: stable + - run: echo "GOVERSION=$(go version)" >> "$GITHUB_ENV" + - uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200 # v6.0.0 + id: goreleaser + with: + version: latest + args: build --clean --verbose --single-target --snapshot + check-tag: + permissions: + contents: read + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + with: + fetch-depth: 0 + - id: ccv + uses: smlx/ccv@d3de774e9b607b079940a7a86952f44643743336 # v0.9.0 + with: + write-tag: false + - run: | + echo "new-tag=$NEW_TAG" + echo "new-tag-version=$NEW_TAG_VERSION" + env: + NEW_TAG: ${{steps.ccv.outputs.new-tag}} + NEW_TAG_VERSION: ${{steps.ccv.outputs.new-tag-version}} diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml deleted file mode 100644 index a70b77b..0000000 --- a/.github/workflows/codeql-analysis.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: "CodeQL" -on: - push: - branches: - - main - pull_request: - # The branches below must be a subset of the branches above - branches: - - main - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - language: - - go - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/commitlint.yaml b/.github/workflows/commitlint.yaml deleted file mode 100644 index 99fc87c..0000000 --- a/.github/workflows/commitlint.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: Lint Commit Messages -on: pull_request - -jobs: - commitlint: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Lint Commits - uses: wagoid/commitlint-github-action@v5 diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 05f02d4..1ce55c8 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -1,28 +1,29 @@ -name: Coverage +name: coverage on: push: branches: - main - +permissions: {} jobs: coverage: + permissions: + contents: write runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Configure git - run: | - git config --global user.name "$GITHUB_ACTOR" - git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com" - - name: Set up go - uses: actions/setup-go@v5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: - go-version: "^1.19" + go-version: stable - name: Calculate coverage - run: go test -v -covermode=count -coverprofile=coverage.out ./... - - name: Convert coverage to lcov - uses: jandelgado/gcov2lcov-action@v1.0.9 - - name: Coveralls - uses: coverallsapp/github-action@v2.3.0 + run: | + go test -v -covermode=atomic -coverprofile=cover.out.raw -coverpkg=./... ./... + # remove generated code from coverage calculation + grep -Ev 'internal/mock|_enumer.go' cover.out.raw > cover.out + - name: Generage coverage badge + uses: vladopajic/go-test-coverage@1079cd4e58dda229c04ffdb6324fc3756b8542ff # v2.10.1 with: - github-token: ${{ secrets.github_token }} + profile: cover.out + local-prefix: github.com/${{ github.repository }} + git-token: ${{ secrets.GITHUB_TOKEN }} + # orphan branch for storing badges + git-branch: badges diff --git a/.github/workflows/dependabot-automerge.yaml b/.github/workflows/dependabot-automerge.yaml index 8f3942a..df319e8 100644 --- a/.github/workflows/dependabot-automerge.yaml +++ b/.github/workflows/dependabot-automerge.yaml @@ -1,17 +1,24 @@ # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions#enable-auto-merge-on-a-pull-request -name: Dependabot auto-merge -on: pull_request - -permissions: - contents: write - pull-requests: write - +name: dependabot auto-merge +on: + pull_request: + branches: + - main +permissions: {} jobs: - dependabot: + dependabot-automerge: + permissions: + contents: write + pull-requests: write runs-on: ubuntu-latest - if: ${{ github.actor == 'dependabot[bot]' }} + if: github.actor == 'dependabot[bot]' steps: - - name: Enable auto-merge for Dependabot PRs + - name: Fetch dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@5e5f99653a5b510e8555840e80cbf1514ad4af38 # v2.1.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - name: Enable auto-merge for Dependabot PRs # these still need approval before merge run: gh pr merge --auto --merge "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} diff --git a/.github/workflows/dependency-review.yaml b/.github/workflows/dependency-review.yaml new file mode 100644 index 0000000..2c706e1 --- /dev/null +++ b/.github/workflows/dependency-review.yaml @@ -0,0 +1,16 @@ +name: dependency review +on: + pull_request: + branches: + - main +permissions: {} +jobs: + dependency-review: + permissions: + contents: read + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3 + with: + config-file: .github/dependency-review-config.yaml diff --git a/.github/workflows/golangci-lint.yaml b/.github/workflows/golangci-lint.yaml deleted file mode 100644 index 44c600f..0000000 --- a/.github/workflows/golangci-lint.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: golangci-lint -on: pull_request - -jobs: - golangci: - name: lint - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Install Go - uses: actions/setup-go@v5 - with: - go-version: "^1.19" - - name: golangci-lint - uses: golangci/golangci-lint-action@v6 - with: - version: latest diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..578f92f --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,40 @@ +name: lint +on: + pull_request: + branches: + - main +permissions: {} +jobs: + lint-go: + permissions: + contents: read + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + with: + go-version: stable + - uses: golangci/golangci-lint-action@a4f60bb28d35aeee14e6880718e0c85ff1882e64 # v6.0.1 + with: + args: --timeout=180s --enable gocritic + lint-commits: + permissions: + contents: read + pull-requests: read + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + with: + fetch-depth: 0 + - uses: wagoid/commitlint-github-action@7f0a61df502599e1f1f50880aaa7ec1e2c0592f2 # v6.0.1 + with: + configFile: .github/commitlint.config.mjs + lint-actions: + permissions: + contents: read + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + - uses: docker://rhysd/actionlint:1.7.0@sha256:601d6faeefa07683a4a79f756f430a1850b34d575d734b1d1324692202bf312e # v1.7.0 + with: + args: -color diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..2186005 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,61 @@ +name: release +on: + push: + branches: + - main +permissions: {} +jobs: + release-tag: + permissions: + # create tag + contents: write + runs-on: ubuntu-latest + outputs: + new-tag: ${{ steps.ccv.outputs.new-tag }} + steps: + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + with: + fetch-depth: 0 + - name: Bump tag if necessary + id: ccv + uses: smlx/ccv@d3de774e9b607b079940a7a86952f44643743336 # v0.9.0 + release-build: + permissions: + # create release + contents: write + # use OIDC token for signing + id-token: write + # required by attest-build-provenance + attestations: write + needs: release-tag + if: needs.release-tag.outputs.new-tag == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 + with: + fetch-depth: 0 + - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + with: + go-version: stable + - name: Set up environment + run: echo "GOVERSION=$(go version)" >> "$GITHUB_ENV" + - uses: advanced-security/sbom-generator-action@375dee8e6144d9fd0ec1f5667b4f6fb4faacefed # v0.0.1 + id: sbom + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Move sbom to avoid dirty git + run: mv "$GITHUB_SBOM_PATH" ./sbom.spdx.json + env: + GITHUB_SBOM_PATH: ${{ steps.sbom.outputs.fileName }} + - uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200 # v6.0.0 + id: goreleaser + with: + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_SBOM_PATH: ./sbom.spdx.json + # attest archives + - uses: actions/attest-build-provenance@49df96e17e918a15956db358890b08e61c704919 # v1.2.0 + with: + subject-path: "dist/*.tar.gz" diff --git a/.github/workflows/tag-release.yaml b/.github/workflows/tag-release.yaml deleted file mode 100644 index 3219c49..0000000 --- a/.github/workflows/tag-release.yaml +++ /dev/null @@ -1,45 +0,0 @@ -name: Tag and Release - -on: - push: - branches: - - main - -jobs: - tag: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Configure Git - run: | - git config --global user.name "$GITHUB_ACTOR" - git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com" - - name: Setup go - uses: actions/setup-go@v5 - with: - go-version: "^1.19" - - name: Install ccv - run: > - curl -sSL https://github.com/smlx/ccv/releases/download/v0.3.2/ccv_0.3.2_linux_amd64.tar.gz - | sudo tar -xz -C /usr/local/bin ccv - - name: Bump tag if necessary - id: tag - run: | - if [ -z $(git tag -l $(ccv)) ]; then - git tag $(ccv) - git push --tags - echo "new=true" >> $GITHUB_OUTPUT - fi - - name: Set up environment - run: echo "GOVERSION=$(go version)" >> $GITHUB_ENV - - name: Run GoReleaser - if: steps.tag.outputs.new == 'true' - uses: goreleaser/goreleaser-action@v6 - with: - version: latest - args: release --rm-dist - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e4f52fa..bcf70ee 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,19 +1,19 @@ -name: Test Suite -on: pull_request - +name: test +on: + pull_request: + branches: + - main +permissions: {} jobs: - go-test: + test-go: + permissions: + contents: read runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Configure git - run: | - git config --global user.name "$GITHUB_ACTOR" - git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com" - - name: Set up go - uses: actions/setup-go@v5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: - go-version: "^1.19" - - name: Run tests - run: go test -v ./... + ref: ${{ github.event.pull_request.head.sha }} + - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 + with: + go-version: stable + - run: go test -v ./... diff --git a/.goreleaser.yml b/.goreleaser.yml index c21ffb2..acaa593 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,5 +1,9 @@ +version: 2 builds: -- main: ./cmd/jiratime +- &buildDefinition + id: jiratime + binary: jiratime + main: ./cmd/jiratime ldflags: - > -s -w @@ -7,6 +11,15 @@ builds: -X "main.date={{.Date}}" -X "main.goVersion={{.Env.GOVERSION}}" -X "main.projectName={{.ProjectName}}" - -X "main.version={{.Version}}" + -X "main.version=v{{.Version}}" env: - CGO_ENABLED=0 + goos: + - linux + - darwin + goarch: + - amd64 + - arm64 + +changelog: + use: github-native diff --git a/cmd/jiratime/authorize.go b/cmd/jiratime/authorize.go index 9da6cb8..a2746f5 100644 --- a/cmd/jiratime/authorize.go +++ b/cmd/jiratime/authorize.go @@ -23,11 +23,19 @@ func startRedirectServer(ctx context.Context, state string, c chan<- string) { mux := http.NewServeMux() // Create a new redirect route mux.HandleFunc("/oauth/redirect", func(w http.ResponseWriter, r *http.Request) { + // first, check the state matches + if state != r.URL.Query().Get("state") { + fmt.Fprintf(os.Stderr, `invalid state: expected "%s", got "%s"`, + state, r.URL.Query().Get("state")) + w.WriteHeader(http.StatusBadRequest) + return + } // First, we need to get the value of the `code` query param err := r.ParseForm() if err != nil { - fmt.Fprintf(os.Stdout, "could not parse query: %v", err) + fmt.Fprintf(os.Stderr, "could not parse query: %v", err) w.WriteHeader(http.StatusBadRequest) + return } c <- r.FormValue("code") _, _ = w.Write([]byte("Authorization successful. You may now close this page.")) @@ -94,7 +102,7 @@ func (cmd *AuthorizeCmd) Run() error { context.WithValue(ctx, oauth2.HTTPClient, &http.Client{Timeout: 4 * time.Second}), code) if err != nil { - log.Fatal(err) + return fmt.Errorf("couldn't exchange token: %v", err) } auth.Token = tok if err = config.WriteAuth(auth); err != nil { diff --git a/cmd/jiratime/version.go b/cmd/jiratime/version.go index 6eee539..f80aa88 100644 --- a/cmd/jiratime/version.go +++ b/cmd/jiratime/version.go @@ -3,13 +3,13 @@ package main import ( "encoding/json" "fmt" + "runtime" ) // These variables are set by GoReleaser during the build. var ( commit string date string - goVersion string projectName string version string ) @@ -31,7 +31,7 @@ func (*VersionCmd) Run() error { version, commit, date, - goVersion, + runtime.Version(), }) if err != nil { return err