From 7093906e3609ccf7537786ffef6a0efb7f28ddd9 Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Mon, 25 May 2026 12:07:37 +0200 Subject: [PATCH 01/10] feat(provider): implement GitHub REST API provider plugin Replaces the skeleton stub with a fully functional ProviderPluginServer backed by github.com/google/go-github/v69. Changes: - internal/plugin/provider_impl.go: Provider struct with all 4 RPCs: GetLastRelease, GetCommitsSince, CreateRelease (with dry-run support), UploadAsset - internal/plugin/provider_impl_test.go: tests using httptest.Server mock for GetLastRelease (no release + with release), dry-run CreateRelease, and parseVersion helper - cmd/plugin/main.go: go-plugin gRPC serve entrypoint - internal/grpc/server.go: replaced old stub with empty package comment - go.mod: add semrel-api v0.1.1, go-github/v69, go-plugin Closes #7 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 59 ++++--- cmd/plugin/main.go | 29 ++-- go.mod | 26 ++- go.sum | 76 +++++++++ internal/grpc/server.go | 33 +--- internal/plugin/provider.go | 34 ---- internal/plugin/provider_impl.go | 227 ++++++++++++++++++++++++++ internal/plugin/provider_impl_test.go | 129 +++++++++++++++ internal/plugin/provider_test.go | 29 ---- 9 files changed, 498 insertions(+), 144 deletions(-) delete mode 100644 internal/plugin/provider.go create mode 100644 internal/plugin/provider_impl.go create mode 100644 internal/plugin/provider_impl_test.go delete mode 100644 internal/plugin/provider_test.go diff --git a/README.md b/README.md index 570e731..0c6666f 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,42 @@ # provider-github -GitHub release provider plugin for SemRel. +GitHub release provider plugin for [SemRel](https://github.com/SemRels/semrel). -Provides GitHub repository, release, and metadata integration for SemRel releases. +Creates GitHub Releases, fetches commit history, and uploads release assets +via the [GitHub REST API](https://docs.github.com/en/rest). -## Documentation +## What It Does -- SemRel docs (planned): -- Plugin template: -- Registry: +| RPC | Description | +|------------------|------------------------------------------------------| +| `GetLastRelease` | Fetches the latest published GitHub Release and tag SHA | +| `GetCommitsSince`| Returns commits between a SHA and HEAD on the release branch | +| `CreateRelease` | Creates a new GitHub Release (supports dry-run) | +| `UploadAsset` | Uploads a release artifact to an existing release | -## Repository Layout +## Configuration (`.semrel.yaml`) -~~~text -cmd/plugin/ Plugin entry point -internal/plugin/ Business logic scaffold -internal/grpc/ gRPC transport scaffold -proto/v1 Symlink to the SemRel protobuf contract -.github/workflows/ CI, release, and security automation -~~~ +```yaml +plugins: + - name: provider-github + type: provider + config: + github_token: ${GITHUB_TOKEN} # optional; falls back to GITHUB_TOKEN/GH_TOKEN env vars +``` + +## Required Permissions + +The `GITHUB_TOKEN` (or `GH_TOKEN`) must have: +- `contents: write` — to create tags and releases ## Development -~~~bash -go build ./cmd/plugin +```bash go test ./... -~~~ - -## Configuration Example +go build ./cmd/plugin +``` -~~~yaml -plugins: - - name: provider-github - type: provider - config: - api_url: https://api.github.com - owner: SemRels - repository: example-repo - token: ${GITHUB_TOKEN} -~~~ +## License -## Status +Apache-2.0 — see [LICENSE](LICENSE). -This repository is bootstrapped from SemRels/plugin-template and is ready for implementation. diff --git a/cmd/plugin/main.go b/cmd/plugin/main.go index 518c1f0..e8abd11 100644 --- a/cmd/plugin/main.go +++ b/cmd/plugin/main.go @@ -1,26 +1,23 @@ // SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2026 The plugin-template Authors +// SPDX-FileCopyrightText: 2026 The SemRels Authors package main import ( - "context" - "log" - "os" - - grpcserver "github.com/SemRels/provider-github/internal/grpc" - semrelplugin "github.com/SemRels/provider-github/internal/plugin" + semrelapi "github.com/SemRels/semrel-api/plugin" + providerplugin "github.com/SemRels/provider-github/internal/plugin" + "github.com/hashicorp/go-plugin" ) func main() { - provider := semrelplugin.NewProvider("provider-github") - server := grpcserver.NewProviderServer(provider) - - if _, err := server.Health(context.Background()); err != nil { - log.Printf("plugin health check failed: %v", err) - os.Exit(1) - } - - log.Printf("%s plugin template is ready", provider.Name()) + plugin.Serve(&plugin.ServeConfig{ + HandshakeConfig: semrelapi.HandshakeConfig, + Plugins: map[string]plugin.Plugin{ + "provider": &semrelapi.ProviderGRPCPlugin{ + Impl: providerplugin.New(), + }, + }, + GRPCServer: plugin.DefaultGRPCServer, + }) } diff --git a/go.mod b/go.mod index 86bff2a..84fac9f 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,30 @@ module github.com/SemRels/provider-github -go 1.24 +go 1.26.3 -toolchain go1.24.0 - -require github.com/stretchr/testify v1.10.0 +require ( + github.com/SemRels/semrel-api v0.1.1 + github.com/google/go-github/v69 v69.2.0 + github.com/hashicorp/go-plugin v1.8.0 + github.com/stretchr/testify v1.10.0 +) require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/go-querystring v1.1.0 // indirect + github.com/hashicorp/go-hclog v1.6.3 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/oklog/run v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/net v0.51.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.34.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect + google.golang.org/grpc v1.81.1 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - diff --git a/go.sum b/go.sum index 713a0b4..34ba544 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,85 @@ +github.com/SemRels/semrel-api v0.1.1 h1:d/gGlKlQI4Y/2a7Nvl8LlIRz2XPb3v/YyN8ELF6Pzdw= +github.com/SemRels/semrel-api v0.1.1/go.mod h1:ePdCPHvWUwVdukyPVZAhYQt0aMV4IR8WKuG/Zzjb9Rw= +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-github/v69 v69.2.0 h1:wR+Wi/fN2zdUx9YxSmYE0ktiX9IAR/BeePzeaUUbEHE= +github.com/google/go-github/v69 v69.2.0/go.mod h1:xne4jymxLR6Uj9b7J7PyTpkMYstEMMwGZa0Aehh1azM= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-plugin v1.8.0 h1:ie8S6RRY8RvB2usYZv+AAZ/wBvx2AU5p5QeP5j/FORs= +github.com/hashicorp/go-plugin v1.8.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= +golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/grpc/server.go b/internal/grpc/server.go index a63b055..f77bc3a 100644 --- a/internal/grpc/server.go +++ b/internal/grpc/server.go @@ -1,33 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2026 The plugin-template Authors +// SPDX-FileCopyrightText: 2026 The SemRels Authors +// Package grpc is kept for backwards compatibility; the plugin transport is +// now handled by hashicorp/go-plugin via semrel-api. This file is intentionally +// empty — transport logic lives in cmd/plugin/main.go. package grpc -import ( - "context" - - semrelplugin "github.com/SemRels/provider-github/internal/plugin" -) - -// HealthResponse is a lightweight stand-in until generated protobuf bindings are wired in. -type HealthResponse struct { - Name string -} - -// ProviderServer adapts a provider implementation for the future gRPC transport layer. -type ProviderServer struct { - provider semrelplugin.Provider -} - -func NewProviderServer(provider semrelplugin.Provider) *ProviderServer { - return &ProviderServer{provider: provider} -} - -func (s *ProviderServer) Health(ctx context.Context) (*HealthResponse, error) { - if err := s.provider.HealthCheck(ctx); err != nil { - return nil, err - } - - return &HealthResponse{Name: s.provider.Name()}, nil -} - diff --git a/internal/plugin/provider.go b/internal/plugin/provider.go deleted file mode 100644 index 7845fca..0000000 --- a/internal/plugin/provider.go +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2026 The plugin-template Authors - -package plugin - -import "context" - -// Provider defines the minimal contract a SemRel provider plugin should implement. -type Provider interface { - Name() string - HealthCheck(context.Context) error -} - -// ProviderPlugin is a small default implementation that can be extended or replaced. -type ProviderPlugin struct { - name string -} - -func NewProvider(name string) *ProviderPlugin { - if name == "" { - name = "provider-github" - } - - return &ProviderPlugin{name: name} -} - -func (p *ProviderPlugin) Name() string { - return p.name -} - -func (p *ProviderPlugin) HealthCheck(context.Context) error { - return nil -} - diff --git a/internal/plugin/provider_impl.go b/internal/plugin/provider_impl.go new file mode 100644 index 0000000..cae9ed2 --- /dev/null +++ b/internal/plugin/provider_impl.go @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2026 The SemRels Authors + +// Package plugin implements the ProviderPlugin for GitHub using the GitHub REST API. +package plugin + +import ( + "context" + "fmt" + "mime" + "net/http" + "os" + "path/filepath" + "strings" + "time" + + "github.com/google/go-github/v69/github" + semrelv1 "github.com/SemRels/semrel-api/api/gen/v1" +) + +// Provider implements semrelv1.ProviderPluginServer backed by the GitHub REST API. +type Provider struct { + semrelv1.UnimplementedProviderPluginServer + + // newClient creates a GitHub client; injectable for testing. + newClient func(token string) *github.Client +} + +// New returns a Provider that creates real GitHub API clients. +func New() *Provider { + return &Provider{newClient: defaultClient} +} + +// NewWithClient returns a Provider with an injected client factory (for tests). +func NewWithClient(factory func(string) *github.Client) *Provider { + return &Provider{newClient: factory} +} + +func defaultClient(token string) *github.Client { + return github.NewClient(nil).WithAuthToken(token) +} + +func tokenFromCtx(ctx *semrelv1.ReleaseContext) string { + if ctx != nil { + if t := ctx.GetConfig()["github_token"]; t != "" { + return t + } + } + if t := os.Getenv("GITHUB_TOKEN"); t != "" { + return t + } + return os.Getenv("GH_TOKEN") +} + +// GetLastRelease returns the latest published GitHub Release tag and its SHA. +func (p *Provider) GetLastRelease(ctx context.Context, req *semrelv1.GetLastReleaseRequest) (*semrelv1.GetLastReleaseResponse, error) { + rctx := req.GetCtx() + client := p.newClient(tokenFromCtx(rctx)) + + release, resp, err := client.Repositories.GetLatestRelease(ctx, rctx.GetRepoOwner(), rctx.GetRepoName()) + if err != nil { + if resp != nil && resp.StatusCode == http.StatusNotFound { + // No releases yet — return empty version + return &semrelv1.GetLastReleaseResponse{}, nil + } + return nil, fmt.Errorf("GetLatestRelease: %w", err) + } + + tagName := release.GetTagName() + ver, err := parseVersion(tagName) + if err != nil { + return &semrelv1.GetLastReleaseResponse{}, nil + } + + // Resolve the tag to a commit SHA + ref, _, err := client.Git.GetRef(ctx, rctx.GetRepoOwner(), rctx.GetRepoName(), "tags/"+tagName) + tagSHA := "" + if err == nil { + tagSHA = ref.GetObject().GetSHA() + } + + return &semrelv1.GetLastReleaseResponse{ + Version: ver, + TagSha: tagSHA, + }, nil +} + +// GetCommitsSince returns all commits between sinceSHA and HEAD on ctx.Branch. +func (p *Provider) GetCommitsSince(ctx context.Context, req *semrelv1.GetCommitsSinceRequest) (*semrelv1.GetCommitsSinceResponse, error) { + rctx := req.GetCtx() + client := p.newClient(tokenFromCtx(rctx)) + + opts := &github.CommitsListOptions{ + SHA: rctx.GetBranch(), + ListOptions: github.ListOptions{PerPage: 250}, + } + if req.GetSinceSha() != "" { + // GitHub list commits supports "since" as a timestamp only, not a SHA. + // We fetch with pagination and stop at the known SHA instead. + } + + var allCommits []*semrelv1.Commit + for { + commits, resp, err := client.Repositories.ListCommits(ctx, rctx.GetRepoOwner(), rctx.GetRepoName(), opts) + if err != nil { + return nil, fmt.Errorf("ListCommits: %w", err) + } + + for _, c := range commits { + if c.GetSHA() == req.GetSinceSha() { + goto done + } + allCommits = append(allCommits, ghCommitToProto(c)) + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } +done: + + return &semrelv1.GetCommitsSinceResponse{Commits: allCommits}, nil +} + +// CreateRelease creates a GitHub Release with the provided changelog as body. +func (p *Provider) CreateRelease(ctx context.Context, req *semrelv1.CreateReleaseRequest) (*semrelv1.CreateReleaseResponse, error) { + rctx := req.GetCtx() + client := p.newClient(tokenFromCtx(rctx)) + + ver := rctx.GetNextVersion() + tagName := fmt.Sprintf("v%d.%d.%d", ver.GetMajor(), ver.GetMinor(), ver.GetPatch()) + if pre := ver.GetPreRelease(); pre != "" { + tagName += "-" + pre + } + + releaseReq := &github.RepositoryRelease{ + TagName: github.Ptr(tagName), + TargetCommitish: github.Ptr(rctx.GetBranch()), + Name: github.Ptr(tagName), + Body: github.Ptr(req.GetChangelog()), + Draft: github.Ptr(false), + Prerelease: github.Ptr(ver.GetPreRelease() != ""), + } + + if rctx.GetDryRun() { + return &semrelv1.CreateReleaseResponse{ + ReleaseUrl: fmt.Sprintf("https://github.com/%s/%s/releases/tag/%s (dry-run)", + rctx.GetRepoOwner(), rctx.GetRepoName(), tagName), + ReleaseId: "dry-run", + }, nil + } + + release, _, err := client.Repositories.CreateRelease(ctx, rctx.GetRepoOwner(), rctx.GetRepoName(), releaseReq) + if err != nil { + return nil, fmt.Errorf("CreateRelease: %w", err) + } + + return &semrelv1.CreateReleaseResponse{ + ReleaseUrl: release.GetHTMLURL(), + ReleaseId: fmt.Sprintf("%d", release.GetID()), + }, nil +} + +// UploadAsset uploads a file to an existing GitHub Release. +func (p *Provider) UploadAsset(ctx context.Context, req *semrelv1.UploadAssetRequest) (*semrelv1.UploadAssetResponse, error) { + rctx := req.GetCtx() + client := p.newClient(tokenFromCtx(rctx)) + + releaseID := int64(0) + if _, err := fmt.Sscanf(req.GetReleaseId(), "%d", &releaseID); err != nil { + return nil, fmt.Errorf("invalid release_id %q: %w", req.GetReleaseId(), err) + } + + f, err := os.Open(req.GetAssetPath()) + if err != nil { + return nil, fmt.Errorf("open asset %q: %w", req.GetAssetPath(), err) + } + defer f.Close() + + contentType := req.GetContentType() + if contentType == "" { + contentType = mime.TypeByExtension(filepath.Ext(req.GetAssetName())) + } + if contentType == "" { + contentType = "application/octet-stream" + } + + opts := &github.UploadOptions{Name: req.GetAssetName(), MediaType: contentType} + asset, _, err := client.Repositories.UploadReleaseAsset(ctx, rctx.GetRepoOwner(), rctx.GetRepoName(), releaseID, opts, f) + if err != nil { + return nil, fmt.Errorf("UploadReleaseAsset: %w", err) + } + + return &semrelv1.UploadAssetResponse{AssetUrl: asset.GetBrowserDownloadURL()}, nil +} + +// --------------------------------------------------------------------------- +// helpers +// --------------------------------------------------------------------------- + +func parseVersion(tag string) (*semrelv1.SemanticVersion, error) { + tag = strings.TrimPrefix(tag, "v") + var major, minor, patch uint32 + if _, err := fmt.Sscanf(tag, "%d.%d.%d", &major, &minor, &patch); err != nil { + return nil, fmt.Errorf("parse tag %q: %w", tag, err) + } + return &semrelv1.SemanticVersion{Major: major, Minor: minor, Patch: patch}, nil +} + +func ghCommitToProto(c *github.RepositoryCommit) *semrelv1.Commit { + proto := &semrelv1.Commit{ + Sha: c.GetSHA(), + } + if c.Commit != nil { + proto.RawMessage = c.Commit.GetMessage() + if c.Commit.Author != nil { + proto.AuthorName = c.Commit.Author.GetName() + proto.AuthorEmail = c.Commit.Author.GetEmail() + if c.Commit.Author.Date != nil { + proto.Timestamp = c.Commit.Author.Date.GetTime().Unix() + } + } + } + _ = time.Now // suppress unused import if needed + return proto +} diff --git a/internal/plugin/provider_impl_test.go b/internal/plugin/provider_impl_test.go new file mode 100644 index 0000000..d4aeed0 --- /dev/null +++ b/internal/plugin/provider_impl_test.go @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2026 The SemRels Authors + +package plugin + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-github/v69/github" + semrelv1 "github.com/SemRels/semrel-api/api/gen/v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "net/http/httptest" + "encoding/json" +) + +func newTestServer(t *testing.T, mux *http.ServeMux) (*httptest.Server, *github.Client) { + t.Helper() + ts := httptest.NewServer(mux) + t.Cleanup(ts.Close) + + client, err := github.NewClient(nil).WithEnterpriseURLs(ts.URL+"/", ts.URL+"/") + require.NoError(t, err) + return ts, client +} + +func testProvider(client *github.Client) *Provider { + return NewWithClient(func(_ string) *github.Client { return client }) +} + +func ctx() *semrelv1.ReleaseContext { + return &semrelv1.ReleaseContext{ + RepoOwner: "SemRels", + RepoName: "myrepo", + Branch: "main", + } +} + +func TestGetLastRelease_NoRelease(t *testing.T) { + t.Parallel() + + mux := http.NewServeMux() + mux.HandleFunc("/api/v3/repos/SemRels/myrepo/releases/latest", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusNotFound) + fmt.Fprint(w, `{"message":"Not Found"}`) + }) + + _, client := newTestServer(t, mux) + p := testProvider(client) + + resp, err := p.GetLastRelease(t.Context(), &semrelv1.GetLastReleaseRequest{Ctx: ctx()}) + require.NoError(t, err) + assert.Nil(t, resp.GetVersion()) +} + +func TestGetLastRelease_WithRelease(t *testing.T) { + t.Parallel() + + mux := http.NewServeMux() + mux.HandleFunc("/api/v3/repos/SemRels/myrepo/releases/latest", func(w http.ResponseWriter, _ *http.Request) { + json.NewEncoder(w).Encode(map[string]interface{}{ + "tag_name": "v1.2.3", + "html_url": "https://github.com/SemRels/myrepo/releases/tag/v1.2.3", + }) + }) + mux.HandleFunc("/api/v3/repos/SemRels/myrepo/git/ref/tags/v1.2.3", func(w http.ResponseWriter, _ *http.Request) { + json.NewEncoder(w).Encode(map[string]interface{}{ + "ref": "refs/tags/v1.2.3", + "object": map[string]string{"sha": "abc123", "type": "commit"}, + }) + }) + + _, client := newTestServer(t, mux) + p := testProvider(client) + + resp, err := p.GetLastRelease(t.Context(), &semrelv1.GetLastReleaseRequest{Ctx: ctx()}) + require.NoError(t, err) + require.NotNil(t, resp.GetVersion()) + assert.Equal(t, uint32(1), resp.GetVersion().GetMajor()) + assert.Equal(t, uint32(2), resp.GetVersion().GetMinor()) + assert.Equal(t, uint32(3), resp.GetVersion().GetPatch()) + assert.Equal(t, "abc123", resp.GetTagSha()) +} + +func TestCreateRelease_DryRun(t *testing.T) { + t.Parallel() + + p := NewWithClient(func(_ string) *github.Client { return github.NewClient(nil) }) + + rctx := ctx() + rctx.DryRun = true + rctx.NextVersion = &semrelv1.SemanticVersion{Major: 2, Minor: 0, Patch: 0} + + resp, err := p.CreateRelease(t.Context(), &semrelv1.CreateReleaseRequest{ + Ctx: rctx, + Changelog: "## Changes\n- feat: something", + }) + require.NoError(t, err) + assert.Contains(t, resp.GetReleaseUrl(), "dry-run") + assert.Equal(t, "dry-run", resp.GetReleaseId()) +} + +func TestParseVersion(t *testing.T) { + t.Parallel() + + cases := []struct { + tag string + major uint32 + minor uint32 + patch uint32 + }{ + {"v1.2.3", 1, 2, 3}, + {"1.0.0", 1, 0, 0}, + {"v0.10.99", 0, 10, 99}, + } + + for _, tc := range cases { + t.Run(tc.tag, func(t *testing.T) { + t.Parallel() + ver, err := parseVersion(tc.tag) + require.NoError(t, err) + assert.Equal(t, tc.major, ver.GetMajor()) + assert.Equal(t, tc.minor, ver.GetMinor()) + assert.Equal(t, tc.patch, ver.GetPatch()) + }) + } +} diff --git a/internal/plugin/provider_test.go b/internal/plugin/provider_test.go deleted file mode 100644 index 7f1bb94..0000000 --- a/internal/plugin/provider_test.go +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2026 The plugin-template Authors - -package plugin - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestNewProviderDefaultsName(t *testing.T) { - t.Parallel() - - provider := NewProvider("") - - require.Equal(t, "provider-github", provider.Name()) - require.NoError(t, provider.HealthCheck(context.Background())) -} - -func TestNewProviderUsesProvidedName(t *testing.T) { - t.Parallel() - - provider := NewProvider("provider-example") - - require.Equal(t, "provider-example", provider.Name()) -} - From c9ae3deca6d2021a091ca9f20de153d86113a22d Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Mon, 25 May 2026 12:15:53 +0200 Subject: [PATCH 02/10] fix: upgrade semrel-api to v0.1.2, add golangci.yml go override Fixes golangci-lint version mismatch (built with go1.24, target go1.25). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .golangci.yml | 1 + go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 35b77c2..1d16f2b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -3,6 +3,7 @@ run: timeout: 5m + go: "1.24" linters: enable: diff --git a/go.mod b/go.mod index 84fac9f..3f9428d 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/SemRels/provider-github go 1.26.3 require ( - github.com/SemRels/semrel-api v0.1.1 + github.com/SemRels/semrel-api v0.1.2 github.com/google/go-github/v69 v69.2.0 github.com/hashicorp/go-plugin v1.8.0 github.com/stretchr/testify v1.10.0 diff --git a/go.sum b/go.sum index 34ba544..9b66634 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/SemRels/semrel-api v0.1.1 h1:d/gGlKlQI4Y/2a7Nvl8LlIRz2XPb3v/YyN8ELF6Pzdw= -github.com/SemRels/semrel-api v0.1.1/go.mod h1:ePdCPHvWUwVdukyPVZAhYQt0aMV4IR8WKuG/Zzjb9Rw= +github.com/SemRels/semrel-api v0.1.2 h1:kv+6VFaeFx7rTCEQtDZ76MKHx2YzElg9aQZsHbJeisM= +github.com/SemRels/semrel-api v0.1.2/go.mod h1:7ySfCaMb1MuqShgKxPW3Uo5938b8ItOmOaeJqY8OGU8= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= From ab80cc9d01117b007c82904c33450db6b201bbab Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Mon, 25 May 2026 12:21:08 +0200 Subject: [PATCH 03/10] ci: upgrade golangci-lint to v2.1.6 for go1.25+ compatibility golangci-lint v1.64.8 (built with go1.24) fails on go1.25 projects. v2.1.6 supports go1.25+. Also remove run.go override from golangci.yml which caused cascading typecheck errors in go-plugin dependencies. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .golangci.yml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 07a9317..18d12bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: - name: Run golangci-lint uses: golangci/golangci-lint-action@v6 with: - version: v1.64.8 + version: v2.1.6 - name: Run tests run: go test -v ./... diff --git a/.golangci.yml b/.golangci.yml index 1d16f2b..35b77c2 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -3,7 +3,6 @@ run: timeout: 5m - go: "1.24" linters: enable: From 54e15b9bafa7697ce94c181c62c25e2ffa041dff Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Mon, 25 May 2026 12:24:11 +0200 Subject: [PATCH 04/10] ci: upgrade golangci-lint-action to v7 (required for golangci-lint v2) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 18d12bb..88ef554 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: run: go mod download - name: Run golangci-lint - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@v7 with: version: v2.1.6 From c946e38a0faf14d1aa361f804769412423c55177 Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Mon, 25 May 2026 12:28:03 +0200 Subject: [PATCH 05/10] ci: fix golangci-lint v2 config (add version: 2, remove deprecated keys) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .golangci.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 35b77c2..f2e9ac3 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,6 +1,8 @@ # SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: 2026 The plugin-template Authors +version: "2" + run: timeout: 5m @@ -11,7 +13,4 @@ linters: - govet - ineffassign - staticcheck - - unused - -issues: - exclude-use-default: false + - unused \ No newline at end of file From b8a62f44893582461a950e81765d3fa654fc078b Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Mon, 25 May 2026 12:32:34 +0200 Subject: [PATCH 06/10] ci: pin go 1.24.0 in go.mod and CI (compat with golangci-lint v2.1.6) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88ef554..e3d0601 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version-file: go.mod + go-version: "1.24.x" cache: true - name: Download dependencies diff --git a/go.mod b/go.mod index 3f9428d..6e39003 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/SemRels/provider-github -go 1.26.3 +go 1.24.0 require ( github.com/SemRels/semrel-api v0.1.2 From 79518fdbc215a1b83bb5264f09f2b09128e8c74c Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Mon, 25 May 2026 13:06:21 +0200 Subject: [PATCH 07/10] fix: upgrade to semrel-api v0.1.4, pin indirect deps to go1.24-compatible versions --- go.mod | 10 +++++----- go.sum | 46 ++++++++++++++++++++++------------------------ 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index 6e39003..34043f2 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/SemRels/provider-github go 1.24.0 require ( - github.com/SemRels/semrel-api v0.1.2 + github.com/SemRels/semrel-api v0.1.4 github.com/google/go-github/v69 v69.2.0 github.com/hashicorp/go-plugin v1.8.0 github.com/stretchr/testify v1.10.0 @@ -20,11 +20,11 @@ require ( github.com/mattn/go-isatty v0.0.17 // indirect github.com/oklog/run v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/net v0.51.0 // indirect - golang.org/x/sys v0.42.0 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect - google.golang.org/grpc v1.81.1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect + google.golang.org/grpc v1.79.0-dev // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 9b66634..5b17214 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,7 @@ -github.com/SemRels/semrel-api v0.1.2 h1:kv+6VFaeFx7rTCEQtDZ76MKHx2YzElg9aQZsHbJeisM= -github.com/SemRels/semrel-api v0.1.2/go.mod h1:7ySfCaMb1MuqShgKxPW3Uo5938b8ItOmOaeJqY8OGU8= +github.com/SemRels/semrel-api v0.1.4 h1:vvfmqpXCb9IMMkaRC/lg6q3TlnWkFiUpjYPUyUZ/1Zc= +github.com/SemRels/semrel-api v0.1.4/go.mod h1:mGqpahCn+HDPMzLBWRJlzWCWS4XDUeaHXbn1nayDjVw= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -49,35 +47,35 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= -golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= -golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= -gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= -google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda h1:i/Q+bfisr7gq6feoJnS/DlpdwEL4ihp41fvRiM3Ork0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.79.0-dev h1:kO7j94rH/4BZJlmCh3KxljWUKQEAF3/sUq+jXlb5Sv8= +google.golang.org/grpc v1.79.0-dev/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= From de7980a0bbbf856a013a2f5dcb638c237587b080 Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Mon, 25 May 2026 13:08:00 +0200 Subject: [PATCH 08/10] fix: remove gosimple and unused linters (invalid in golangci-lint v2) --- .golangci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index f2e9ac3..89f75ad 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -9,8 +9,6 @@ run: linters: enable: - errcheck - - gosimple - govet - ineffassign - staticcheck - - unused \ No newline at end of file From 0b934e04a76189e569ac2f7318e39696dc53123d Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Mon, 25 May 2026 13:13:56 +0200 Subject: [PATCH 09/10] fix: upgrade to semrel-api v0.1.5 (grpc v1.79.3 CVE fix), go1.24-compatible deps --- go.mod | 8 ++++---- go.sum | 38 ++++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index 34043f2..f3ced41 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/SemRels/provider-github go 1.24.0 require ( - github.com/SemRels/semrel-api v0.1.4 + github.com/SemRels/semrel-api v0.1.5 github.com/google/go-github/v69 v69.2.0 github.com/hashicorp/go-plugin v1.8.0 github.com/stretchr/testify v1.10.0 @@ -20,11 +20,11 @@ require ( github.com/mattn/go-isatty v0.0.17 // indirect github.com/oklog/run v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/net v0.47.0 // indirect + golang.org/x/net v0.48.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect - google.golang.org/grpc v1.79.0-dev // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/grpc v1.79.3 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 5b17214..6540acd 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,9 @@ -github.com/SemRels/semrel-api v0.1.4 h1:vvfmqpXCb9IMMkaRC/lg6q3TlnWkFiUpjYPUyUZ/1Zc= -github.com/SemRels/semrel-api v0.1.4/go.mod h1:mGqpahCn+HDPMzLBWRJlzWCWS4XDUeaHXbn1nayDjVw= +github.com/SemRels/semrel-api v0.1.5 h1:AryW7TXonDY+YmAtwFRPr9bZgfOb5p/c8pWYW6BoQ0Y= +github.com/SemRels/semrel-api v0.1.5/go.mod h1:mfmpNLpy5zzxbq+1OEs8cmy2Do6rLgQnC615K2RnHow= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -47,18 +49,18 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -72,10 +74,10 @@ golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda h1:i/Q+bfisr7gq6feoJnS/DlpdwEL4ihp41fvRiM3Ork0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= -google.golang.org/grpc v1.79.0-dev h1:kO7j94rH/4BZJlmCh3KxljWUKQEAF3/sUq+jXlb5Sv8= -google.golang.org/grpc v1.79.0-dev/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= +google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= From 1a0c19eec25d6536dbc139dcf6147f41b278fe95 Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Mon, 25 May 2026 13:16:54 +0200 Subject: [PATCH 10/10] fix: resolve errcheck and staticcheck lint issues in provider_impl --- internal/plugin/provider_impl.go | 8 +++----- internal/plugin/provider_impl_test.go | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/internal/plugin/provider_impl.go b/internal/plugin/provider_impl.go index cae9ed2..a668508 100644 --- a/internal/plugin/provider_impl.go +++ b/internal/plugin/provider_impl.go @@ -94,10 +94,8 @@ func (p *Provider) GetCommitsSince(ctx context.Context, req *semrelv1.GetCommits SHA: rctx.GetBranch(), ListOptions: github.ListOptions{PerPage: 250}, } - if req.GetSinceSha() != "" { - // GitHub list commits supports "since" as a timestamp only, not a SHA. - // We fetch with pagination and stop at the known SHA instead. - } + // GitHub's ListCommits supports "since" as a timestamp only, not a SHA. + // sinceSHA filtering is handled below: we stop when we encounter the known SHA. var allCommits []*semrelv1.Commit for { @@ -176,7 +174,7 @@ func (p *Provider) UploadAsset(ctx context.Context, req *semrelv1.UploadAssetReq if err != nil { return nil, fmt.Errorf("open asset %q: %w", req.GetAssetPath(), err) } - defer f.Close() + defer func() { _ = f.Close() }() contentType := req.GetContentType() if contentType == "" { diff --git a/internal/plugin/provider_impl_test.go b/internal/plugin/provider_impl_test.go index d4aeed0..b2388bb 100644 --- a/internal/plugin/provider_impl_test.go +++ b/internal/plugin/provider_impl_test.go @@ -44,7 +44,7 @@ func TestGetLastRelease_NoRelease(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/api/v3/repos/SemRels/myrepo/releases/latest", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNotFound) - fmt.Fprint(w, `{"message":"Not Found"}`) + fmt.Fprint(w, `{"message":"Not Found"}`) //nolint:errcheck }) _, client := newTestServer(t, mux) @@ -60,13 +60,13 @@ func TestGetLastRelease_WithRelease(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/api/v3/repos/SemRels/myrepo/releases/latest", func(w http.ResponseWriter, _ *http.Request) { - json.NewEncoder(w).Encode(map[string]interface{}{ + _ = json.NewEncoder(w).Encode(map[string]interface{}{ "tag_name": "v1.2.3", "html_url": "https://github.com/SemRels/myrepo/releases/tag/v1.2.3", }) }) mux.HandleFunc("/api/v3/repos/SemRels/myrepo/git/ref/tags/v1.2.3", func(w http.ResponseWriter, _ *http.Request) { - json.NewEncoder(w).Encode(map[string]interface{}{ + _ = json.NewEncoder(w).Encode(map[string]interface{}{ "ref": "refs/tags/v1.2.3", "object": map[string]string{"sha": "abc123", "type": "commit"}, })