From a25d7ea3816ea6336f00a4e67034005084525e96 Mon Sep 17 00:00:00 2001 From: Fedir RYKHTIK Date: Sat, 6 Jun 2026 10:14:11 +0200 Subject: [PATCH] feat: add automated releases (GoReleaser + GitHub Actions) and -version flag --- .github/workflows/ci.yml | 30 +++++++++++++++++++ .github/workflows/release.yml | 31 ++++++++++++++++++++ .goreleaser.yaml | 55 +++++++++++++++++++++++++++++++++++ CLAUDE.md | 14 +++++++-- Makefile | 17 +++++++++-- README.md | 35 ++++++++++++++++++++-- config.go | 7 +++++ main.go | 3 ++ 8 files changed, 185 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .goreleaser.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d1abc23 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: stable + + - name: Vet + run: make vet + + - name: Build + run: make build + + - name: Unit tests + run: make test + + - name: Functional smoke test (offline, no API quota) + run: ./yrank -top-search "fitness" -local-test -o csv | head -3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..d2a5817 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,31 @@ +name: Release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: stable + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..07c96bc --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,55 @@ +version: 2 + +before: + hooks: + - go mod tidy + +builds: + - id: yrank + main: . + binary: yrank + env: + - CGO_ENABLED=0 + ldflags: + - -s -w -X main.version={{.Version}} + goos: + - linux + - darwin + - windows + goarch: + - amd64 + - arm64 + +archives: + - id: default + name_template: >- + {{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }} + format_overrides: + - goos: windows + formats: [zip] + +checksum: + name_template: "checksums.txt" + +changelog: + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" + - "^chore:" + +# Publish a Homebrew formula to a tap repo on each tagged release. +# Requires a github.com/fedir/homebrew-tap repository and a HOMEBREW_TAP_TOKEN +# secret with write access to it. +brews: + - name: yrank + repository: + owner: fedir + name: homebrew-tap + token: "{{ .Env.HOMEBREW_TAP_TOKEN }}" + homepage: "https://github.com/fedir/yrank" + description: "Rank YouTube playlist, channel or search-result videos by engagement metrics." + license: "GPL-3.0" + test: | + assert_match "yrank", shell_output("#{bin}/yrank -version") diff --git a/CLAUDE.md b/CLAUDE.md index 1ef4004..28e0411 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,10 +15,11 @@ refactor: extract pagination logic ## Commands ```bash -make build # compile → ./yrank binary +make build # compile → ./yrank binary (injects version via -ldflags) make test # go test -race ./... make vet # go vet ./... -make clean # remove binary and coverage artifacts +make clean # remove binary, coverage artifacts and dist/ +make snapshot # local GoReleaser snapshot (no publish; needs goreleaser) # Run a single test go test -race -run TestName ./youtube/... @@ -31,8 +32,17 @@ go test -race -run TestName ./youtube/... # Run with local fixtures (no API quota consumed) ./yrank -p PLiVdPopzGBsV7TgjAw9GH43Ck9QCxrw5w -local-test ./yrank -p PLiVdPopzGBsV7TgjAw9GH43Ck9QCxrw5w -local-test -strategy all -o csv + +# Print version (overridden at build time via -ldflags -X main.version=...) +./yrank -version ``` +## CI & releases + +- `.github/workflows/ci.yml` — runs `make vet`, `make build`, `make test` and an offline `-local-test` smoke test on every push/PR to `master`. +- `.github/workflows/release.yml` — on a `v*` tag push, runs GoReleaser (`.goreleaser.yaml`): cross-compiles linux/darwin/windows × amd64/arm64, publishes a GitHub release with checksums + changelog, and updates the `fedir/homebrew-tap` formula (needs the `HOMEBREW_TAP_TOKEN` secret). +- `main.go` declares `var version = "dev"`; `-version`/`-V` print it. The Makefile and GoReleaser inject the real version through `-ldflags "-X main.version=..."`. + ## Configuration Copy `.env.example` to `.env` and set your YouTube Data API v3 key: diff --git a/Makefile b/Makefile index 7862cad..dce6dd8 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,11 @@ BINARY := yrank +VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev) +LDFLAGS := -ldflags "-s -w -X main.version=$(VERSION)" -.PHONY: build test vet clean coverage install +.PHONY: build test vet clean coverage install release snapshot build: - go build -o $(BINARY) . + go build $(LDFLAGS) -o $(BINARY) . test: go test -race ./... @@ -15,7 +17,16 @@ coverage: go test -race -coverprofile=coverage.txt ./... && go tool cover -func=coverage.txt install: - go install . + go install $(LDFLAGS) . + +# Build and publish a release for the current tag (used by CI on tag push). +release: + goreleaser release --clean + +# Build a local snapshot (no publish) to verify the release config. +snapshot: + goreleaser release --snapshot --clean clean: rm -f $(BINARY) coverage.txt + rm -rf dist diff --git a/README.md b/README.md index 13372ae..a6eecbb 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,22 @@ # yrank = Youtube Rank analyzer -[![Build Status](https://travis-ci.org/fedir/yrank.svg?branch=master)](https://travis-ci.org/fedir/yrank) +[![CI](https://github.com/fedir/yrank/actions/workflows/ci.yml/badge.svg)](https://github.com/fedir/yrank/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/fedir/yrank/branch/master/graph/badge.svg)](https://codecov.io/gh/fedir/yrank) Ranks videos in a YouTube playlist or channel by engagement metrics, so you can prioritise what to watch — especially useful for large conference playlists. ## Installation -Go 1.26+ is required. +Pre-built binaries for Linux, macOS and Windows (amd64/arm64) are attached to every +[GitHub release](https://github.com/fedir/yrank/releases). + +Homebrew: + +```bash +brew install fedir/tap/yrank +``` + +With Go 1.26+: ```bash go install github.com/fedir/yrank@latest @@ -51,6 +60,7 @@ The key is also read directly from the environment if set there. | `-m` | `0` (all) | Maximum number of results to return | | `-local-test` | `false` | Use local `testdata/` fixtures instead of live API calls (no quota consumed) | | `-d` | `false` | Debug mode — prints API URLs and IDs | +| `-version` / `-V` | — | Print version and exit | Exactly one input source — `-p`, `-c`, or `-top-search` — must be given; they are mutually exclusive. @@ -198,3 +208,24 @@ score = views / age_days * [Squeezie — Concepts originaux](sample_output/squeezie_concepts_originaux_positive_interest.md) * [Squeezie — Full channel](sample_output/squeezie_channel_positive_interest.md) * [FOSDEM 2020](sample_output/fosdem2020_positive_interest.md) + +## Releases + +Releases are fully automated with [GoReleaser](https://goreleaser.com/). Pushing a +`v*` tag triggers the [release workflow](.github/workflows/release.yml), which +cross-compiles binaries (linux/darwin/windows × amd64/arm64), publishes a GitHub +release with checksums and a changelog, and updates the Homebrew tap. + +```bash +git tag v1.2.3 +git push origin v1.2.3 +``` + +Build a local snapshot to test the release config without publishing: + +```bash +make snapshot # requires goreleaser installed locally +``` + +The `Homebrew` formula update requires a `HOMEBREW_TAP_TOKEN` repository secret with +write access to `fedir/homebrew-tap`. diff --git a/config.go b/config.go index 8970ebf..d0d8e03 100644 --- a/config.go +++ b/config.go @@ -79,9 +79,16 @@ func cliParameters() (cid, pid, topSearch, output, sorting, strategy, from, weig outFlag = flag.String("out", "", "Write output to file atomically (safer than shell redirection)") localTestFlag = flag.Bool("local-test", false, "Use local testdata/ fixtures instead of live API calls") dbg = flag.Bool("d", false, "Debug mode") + showVersion = flag.Bool("version", false, "Print version and exit") + showVersionV = flag.Bool("V", false, "Print version and exit (alias of -version)") ) flag.Parse() + if *showVersion || *showVersionV { + fmt.Printf("yrank %s\n", version) + os.Exit(0) + } + sources := 0 for _, s := range []string{*playlistID, *channelID, *searchFlag} { if s != "" { diff --git a/main.go b/main.go index e6df9bf..2317862 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,9 @@ import ( "github.com/fedir/yrank/youtube" ) +// version is overridden at build time via -ldflags "-X main.version=...". +var version = "dev" + func main() { c := configuration() cid, pid, topSearch, of, sorting, strategy, from, weightsRaw, outFile, m, d, localTest := cliParameters()