diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 0000000..df61ae9 --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,36 @@ +--- +name: Format + +on: + pull_request: + branches: [main] + paths: + - "**/*.go" + - ".github/workflows/format.yml" + +permissions: + contents: read + +jobs: + format-check: + name: Format Check + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Check formatting + run: | + if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then + echo "The following files are not properly formatted:"; + gofmt -s -l .; + echo ""; + echo "Please run 'go fmt ./...' to fix formatting issues."; + exit 1; + fi diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..2b31fc3 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,34 @@ +--- +name: Lint + +on: + pull_request: + branches: [main] + paths: + - "**/*.go" + - ".golangci.yml" + - "go.mod" + - "go.sum" + - ".github/workflows/lint.yml" + +permissions: + contents: read + +jobs: + golangci: + name: golangci-lint + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: golangci-lint + uses: golangci/golangci-lint-action@v8 + with: + version: latest diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml deleted file mode 100644 index a99f7e0..0000000 --- a/.github/workflows/pr-checks.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: PR Checks - -on: - pull_request: - branches: [ main ] - paths: - - '**/*.go' - - 'go.mod' - - 'go.sum' - - '.github/workflows/pr-checks.yml' - -jobs: - test: - name: Unit Tests - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: '1.24.0' - - - name: Cache Go modules - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Download dependencies - run: go mod download - - - name: Run tests - run: go test -v -race -coverprofile=coverage.out ./... - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - with: - file: ./coverage.out - flags: unittests - name: codecov-umbrella - fail_ci_if_error: false - - format: - name: Format Check - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: '1.24.0' - - - name: Check formatting - run: | - if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then - echo "The following files are not properly formatted:" - gofmt -s -l . - echo "" - echo "Please run 'go fmt ./...' to fix formatting issues." - exit 1 - fi - - lint: - name: Lint Check - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: '1.24.0' - - - name: Run golangci-lint - uses: golangci/golangci-lint-action@v8 - with: - version: latest - args: --timeout=5m \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..771a1bc --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,61 @@ +name: Release + +on: + push: + tags: + - 'v*.*.*' + +permissions: + contents: write + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: true + +jobs: + goreleaser: + name: Release with GoReleaser + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # GoReleaser needs full history for changelog generation + fetch-depth: 0 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Validate version match + run: | + # Extract version from git tag (strip 'v' prefix) + TAG_VERSION="${GITHUB_REF#refs/tags/v}" + echo "Tag version: v${TAG_VERSION}" + + # Extract version from cmd/root.go + CODE_VERSION=$(grep -oP 'Version = "\K[^"]+' cmd/root.go) + echo "Code version: ${CODE_VERSION}" + + # Compare versions + if [ "${TAG_VERSION}" != "${CODE_VERSION}" ]; then + echo "❌ Error: Version mismatch!" + echo " Git tag: v${TAG_VERSION}" + echo " Code: ${CODE_VERSION}" + echo "" + echo "Please update the version in cmd/root.go to match the tag." + exit 1 + fi + + echo "✅ Version match confirmed: ${CODE_VERSION}" + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..06b525c --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,34 @@ +--- +name: Tests + +on: + pull_request: + branches: [ main ] + paths: + - '**/*.go' + - 'go.mod' + - 'go.sum' + - '.github/workflows/tests.yml' + +permissions: + contents: read + +jobs: + unit-tests: + name: Unit Tests + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Download deps + run: go mod download + + - name: Run tests + run: go test -v -race diff --git a/.gitignore b/.gitignore index a22d19b..114091e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ # Binary name brokli +dist/ # Test binary, built with `go test -c` *.test @@ -31,6 +32,3 @@ go.work.sum # env file .env - -# Binary name -brokli diff --git a/.golangci.yml b/.golangci.yml index b9cb19a..dc7a5b8 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -25,13 +25,13 @@ linters: - unparam path: _test\.go paths: + - third_party + - builtin + - examples - third_party$ - builtin$ - examples$ formatters: - enable: - - gofmt - - goimports settings: gofmt: simplify: true diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..3adece7 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,40 @@ +# GoReleaser config for brokli (v2) +# See https://goreleaser.com for reference + +version: 2 + +project_name: brokli + +before: + hooks: + - go mod tidy + +builds: + - id: brokli + main: . + binary: brokli + env: + - CGO_ENABLED=0 + flags: + - -trimpath + ldflags: + - -s -w + goos: [linux, darwin, windows] + goarch: [amd64, arm64] + goarm: ["7"] + +archives: + - name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' + files: + - LICENSE* + - README* + - CHANGELOG* + +checksum: + name_template: 'SHA256SUMS_{{ .Version }}.txt' + +changelog: + use: github + +release: + prerelease: auto diff --git a/Taskfile.yml b/Taskfile.yml index 94b86f2..928b64c 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -84,6 +84,50 @@ tasks: generates: - "{{.BINARY_NAME}}" + # Release (GoReleaser) + goreleaser-install: + desc: "Install GoReleaser CLI" + cmd: go install github.com/goreleaser/goreleaser/v2@latest + + release-check: + desc: "Validate GoReleaser configuration" + cmds: + - | + if command -v goreleaser >/dev/null 2>&1; then + goreleaser check + elif [ -f ~/go/bin/goreleaser ]; then + ~/go/bin/goreleaser check + else + echo "GoReleaser not found. Install it with: task goreleaser-install"; + exit 1; + fi + + release-snapshot: + desc: "Build a local snapshot release (no publish)" + cmds: + - | + if command -v goreleaser >/dev/null 2>&1; then + goreleaser release --snapshot --clean + elif [ -f ~/go/bin/goreleaser ]; then + ~/go/bin/goreleaser release --snapshot --clean + else + echo "GoReleaser not found. Install it with: task goreleaser-install"; + exit 1; + fi + + release: + desc: "Run GoReleaser (publishes if GITHUB_TOKEN is set and tag is present)" + cmds: + - | + if command -v goreleaser >/dev/null 2>&1; then + goreleaser release --clean + elif [ -f ~/go/bin/goreleaser ]; then + ~/go/bin/goreleaser release --clean + else + echo "GoReleaser not found. Install it with: task goreleaser-install"; + exit 1; + fi + # Maintenance clean: desc: "Clean build artifacts and temporary files" @@ -91,6 +135,7 @@ tasks: - rm -f {{.BINARY_NAME}} - rm -f {{.COVERAGE_FILE}} - rm -f coverage.html + - rm -rf dist - go clean -cache - go clean -testcache diff --git a/cmd/root.go b/cmd/root.go index a065427..1cea8d8 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -7,9 +7,12 @@ import ( "github.com/spf13/cobra" ) +// Version is the version of Brokli. +var Version = "0.1.0" + var rootCmd = &cobra.Command{ Use: "brokli", - Version: "0.1.0", + Version: Version, Short: "TODO: Add a short description here", Long: `TODO: Add a longer description here`, Run: func(cmd *cobra.Command, args []string) { @@ -23,8 +26,8 @@ var versionCmd = &cobra.Command{ Short: "Print the version number of Brokli", Long: `All software has versions. This is Brokli's`, Run: func(cmd *cobra.Command, args []string) { - // Print the version number of Brokli from rootCmd - fmt.Println("Brokli v" + rootCmd.Version) + // Print the version number of Brokli + fmt.Println("Brokli v" + Version) }, } diff --git a/go.mod b/go.mod index d0dbb0e..3c3dce2 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,12 @@ module github.com/endlesstrax/brokli go 1.24.0 require ( + github.com/fatih/color v1.18.0 github.com/spf13/cobra v1.10.1 golang.org/x/net v0.44.0 ) require ( - github.com/fatih/color v1.18.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect diff --git a/pkg/fetcher/fetcher.go b/pkg/fetcher/fetcher.go index d5ec579..9f7e6c3 100644 --- a/pkg/fetcher/fetcher.go +++ b/pkg/fetcher/fetcher.go @@ -13,7 +13,9 @@ func GetHTML(url string) ([]byte, error) { if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() body, err := io.ReadAll(resp.Body) if err != nil {