From f5fe64509bb54420e17070bf4c7871e1839f6c4d Mon Sep 17 00:00:00 2001 From: Markus Waldheim Date: Sun, 24 May 2026 14:46:18 +0200 Subject: [PATCH 1/4] feat(sdk): bootstrap plugin SDK core and repo hygiene Signed-off-by: Markus Waldheim --- .github/renovate.json | 38 ++++++++++++++++++++ .github/workflows/ci.yaml | 51 +++++++++++++++++++++++++++ .github/workflows/dco.yaml | 41 ++++++++++++++++++++++ .github/workflows/renovate.yaml | 31 +++++++++++++++++ .github/workflows/reuse.yaml | 24 +++++++++++++ .github/workflows/scorecard.yaml | 43 +++++++++++++++++++++++ .gitignore | 37 ++++++++++++++++++++ README.md | 41 +++++++++++++++++++--- go.mod | 20 ++++++++++- go.sum | 60 ++++++++++++++++++++++++++++++++ sdk/discovery.go | 18 ++++++++++ sdk/discovery_test.go | 21 +++++++++++ sdk/doc.go | 9 +++++ sdk/handshake.go | 23 ++++++++++++ sdk/handshake_test.go | 15 ++++++++ sdk/logger.go | 36 +++++++++++++++++++ sdk/types.go | 16 +++++++++ sdk/types_test.go | 24 +++++++++++++ 18 files changed, 542 insertions(+), 6 deletions(-) create mode 100644 .github/renovate.json create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/dco.yaml create mode 100644 .github/workflows/renovate.yaml create mode 100644 .github/workflows/reuse.yaml create mode 100644 .github/workflows/scorecard.yaml create mode 100644 .gitignore create mode 100644 go.sum create mode 100644 sdk/discovery.go create mode 100644 sdk/discovery_test.go create mode 100644 sdk/doc.go create mode 100644 sdk/handshake.go create mode 100644 sdk/handshake_test.go create mode 100644 sdk/logger.go create mode 100644 sdk/types.go create mode 100644 sdk/types_test.go diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..b46cf64 --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended", + ":dependencyDashboard", + ":semanticCommits", + "helpers:pinGitHubActionDigests" + ], + "labels": ["type: chore"], + "reviewers": ["mwaldheim", "tboerger"], + "schedule": ["before 6am on monday"], + "golang": { + "postUpdateOptions": ["gomodTidy"] + }, + "packageRules": [ + { + "description": "Group all GitHub Actions minor/patch updates", + "matchManagers": ["github-actions"], + "matchUpdateTypes": ["minor", "patch", "digest"], + "groupName": "GitHub Actions", + "automerge": false + }, + { + "description": "Group Go module minor/patch updates", + "matchManagers": ["gomod"], + "matchUpdateTypes": ["minor", "patch"], + "groupName": "Go dependencies", + "automerge": false + }, + { + "description": "Automerge Go toolchain patch updates", + "matchManagers": ["gomod"], + "matchDepTypes": ["golang"], + "matchUpdateTypes": ["patch"], + "automerge": true + } + ] +} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..6a10006 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,51 @@ +# CI — build, test and lint on every push and PR +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + test: + name: Test (Go ${{ matrix.go }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + go: ["1.24", "1.25", "1.26"] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version: ${{ matrix.go }} + cache: true + + - name: Download modules + run: go mod download + + - name: Build + run: go build ./... + + - name: Test + run: go test ./... + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version: stable + cache: true + + - uses: golangci/golangci-lint-action@v4 + with: + version: latest diff --git a/.github/workflows/dco.yaml b/.github/workflows/dco.yaml new file mode 100644 index 0000000..b7c57e1 --- /dev/null +++ b/.github/workflows/dco.yaml @@ -0,0 +1,41 @@ +# DCO — require Signed-off-by on all commits +name: DCO + +on: + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + dco: + name: DCO Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + fetch-depth: 0 + + - name: Check Signed-off-by on all commits + run: | + BASE="${{ github.event.pull_request.base.sha }}" + HEAD="${{ github.event.pull_request.head.sha }}" + MISSING=0 + + while IFS= read -r sha; do + body=$(git log -1 --format="%B" "$sha") + if ! echo "$body" | grep -qE "^Signed-off-by: .+ <.+>$"; then + echo "::error::Commit $sha is missing a valid Signed-off-by trailer." + echo " Message: $(git log -1 --format='%s' $sha)" + MISSING=$((MISSING + 1)) + fi + done < <(git log --format="%H" "$BASE..$HEAD") + + if [ "$MISSING" -gt 0 ]; then + echo "" + echo "::error::$MISSING commit(s) are missing 'Signed-off-by: Name '." + echo "Fix with: git rebase --signoff HEAD~$MISSING && git push --force-with-lease" + exit 1 + fi + echo "All commits have a valid Signed-off-by trailer ✓" diff --git a/.github/workflows/renovate.yaml b/.github/workflows/renovate.yaml new file mode 100644 index 0000000..8454c26 --- /dev/null +++ b/.github/workflows/renovate.yaml @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: 2026 The semrel Authors + +name: Renovate + +on: + schedule: + - cron: "0 5 * * 1" + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + issues: write + +concurrency: + group: renovate + cancel-in-progress: false + +jobs: + renovate: + name: Renovate + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run Renovate + uses: renovatebot/github-action@v46.1.14 + with: + token: ${{ secrets.RENOVATE_TOKEN != '' && secrets.RENOVATE_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/reuse.yaml b/.github/workflows/reuse.yaml new file mode 100644 index 0000000..7c99914 --- /dev/null +++ b/.github/workflows/reuse.yaml @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: 2026 The semrel Authors + +name: REUSE + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +permissions: + contents: read + +jobs: + reuse: + name: REUSE Compliance + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: REUSE lint + uses: fsfe/reuse-action@bb774aa972c2a89ff34781233d275075cbddf542 # v5.0.0 diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml new file mode 100644 index 0000000..88a1ac7 --- /dev/null +++ b/.github/workflows/scorecard.yaml @@ -0,0 +1,43 @@ +# OpenSSF Scorecard +name: Scorecard + +on: + push: + branches: [main] + schedule: + - cron: "30 2 * * 1" # Weekly on Monday + +permissions: read-all + +jobs: + scorecard: + name: Scorecard Analysis + runs-on: ubuntu-latest + permissions: + security-events: write + id-token: write + contents: read + actions: read + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: true + + - uses: ossf/scorecard-action@v2.4.3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SCORECARD_TOKEN: ${{ secrets.SCORECARD_TOKEN }} + with: + results_file: scorecard-results.sarif + results_format: sarif + publish_results: true + + - uses: actions/upload-artifact@v4 + with: + name: scorecard-results + path: scorecard-results.sarif + retention-days: 5 + + - uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: scorecard-results.sarif diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b98156c --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# Binaries +/bin/ +/dist/ +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Go build cache +/vendor/ + +# Test output +*.test +*.out +coverage.txt +coverage.html + +# Editor artifacts +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# OS artifacts +.DS_Store +Thumbs.db + +# Release artifacts +/release/ +checksums.txt +*.sig +*.intoto.jsonl + +# Local config overrides +.semrel.local.yaml diff --git a/README.md b/README.md index 36d9fca..fbf6b1c 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,42 @@ # go-semrel-plugins -**Status:** Work in Progress (migrating from go-semrel monorepo) +Shared SDK helpers for semrel out-of-process plugins. -## Setup +This repository contains the Go-side bootstrap utilities used by plugin binaries +defined in ADR-001 (go-plugin + gRPC transport). -\\\ash +## Install + +```bash go get github.com/GoSemantics/go-semrel-plugins -\\\ +``` + +## Available SDK helpers + +- `sdk.HandshakeConfig`: shared go-plugin handshake config (protocol + cookie) +- `sdk.NewLogger`: logger configured to write to stderr (stdout is handshake-only) +- `sdk.LocalPluginPath`: local discovery path builder +- `sdk.PluginType`: constants for supported plugin categories + +## Example + +```go +package main + +import ( + goplugin "github.com/hashicorp/go-plugin" + + "github.com/GoSemantics/go-semrel-plugins/sdk" +) + +func main() { + logger := sdk.NewLogger("my-plugin") -See [go-semrel documentation](https://github.com/GoSemantics/go-semrel) for context. + goplugin.Serve(&goplugin.ServeConfig{ + HandshakeConfig: sdk.HandshakeConfig, + Plugins: map[string]goplugin.Plugin{}, + GRPCServer: goplugin.DefaultGRPCServer, + Logger: logger, + }) +} +``` diff --git a/go.mod b/go.mod index aff5c6f..b16c79e 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,22 @@ module github.com/GoSemantics/go-semrel-plugins go 1.24 -require github.com/GoSemantics/go-semrel-api v0.1.0 +require ( + github.com/hashicorp/go-hclog v1.6.3 + github.com/hashicorp/go-plugin v1.6.3 +) + +require ( + github.com/fatih/color v1.13.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/oklog/run v1.0.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect + google.golang.org/grpc v1.58.3 // indirect + google.golang.org/protobuf v1.36.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d79e73a --- /dev/null +++ b/go.sum @@ -0,0 +1,60 @@ +github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= +github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= +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/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= +github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= +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.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +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.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +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.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/sdk/discovery.go b/sdk/discovery.go new file mode 100644 index 0000000..b09fe48 --- /dev/null +++ b/sdk/discovery.go @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2026 The semrel Authors + +package sdk + +import ( + "path/filepath" + "runtime" +) + +const LocalPluginRoot = ".semrel" + +// LocalPluginPath returns the ADR-001 compliant local plugin path: +// .semrel/_/// +func LocalPluginPath(pluginName, version, binaryName string) string { + platform := runtime.GOOS + "_" + runtime.GOARCH + return filepath.Join(LocalPluginRoot, platform, pluginName, version, binaryName) +} diff --git a/sdk/discovery_test.go b/sdk/discovery_test.go new file mode 100644 index 0000000..8fa5386 --- /dev/null +++ b/sdk/discovery_test.go @@ -0,0 +1,21 @@ +package sdk + +import ( + "path/filepath" + "runtime" + "testing" +) + +func TestLocalPluginPath(t *testing.T) { + got := LocalPluginPath("commit-analyzer-default", "v0.1.0", "plugin") + want := filepath.Join( + LocalPluginRoot, + runtime.GOOS+"_"+runtime.GOARCH, + "commit-analyzer-default", + "v0.1.0", + "plugin", + ) + if got != want { + t.Fatalf("unexpected local plugin path: got %q want %q", got, want) + } +} diff --git a/sdk/doc.go b/sdk/doc.go new file mode 100644 index 0000000..86f4807 --- /dev/null +++ b/sdk/doc.go @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2026 The semrel Authors + +// Package sdk contains shared helpers for semrel out-of-process plugins. +// +// It intentionally keeps a small surface so plugin binaries can bootstrap +// quickly while the generated gRPC bindings evolve in lockstep with the +// semrel protocol. +package sdk diff --git a/sdk/handshake.go b/sdk/handshake.go new file mode 100644 index 0000000..cb92066 --- /dev/null +++ b/sdk/handshake.go @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2026 The semrel Authors + +package sdk + +import goplugin "github.com/hashicorp/go-plugin" + +const ( + // ProtocolVersion identifies the go-plugin protocol for semantic_release.v1. + ProtocolVersion = 1 + + // MagicCookieKey and MagicCookieValue protect against accidental execution + // of non-semrel binaries by requiring a matching handshake cookie. + MagicCookieKey = "SEMREL_PLUGIN" + MagicCookieValue = "v1" +) + +// HandshakeConfig is the shared go-plugin handshake used by host and plugins. +var HandshakeConfig = goplugin.HandshakeConfig{ + ProtocolVersion: ProtocolVersion, + MagicCookieKey: MagicCookieKey, + MagicCookieValue: MagicCookieValue, +} diff --git a/sdk/handshake_test.go b/sdk/handshake_test.go new file mode 100644 index 0000000..b36ac2d --- /dev/null +++ b/sdk/handshake_test.go @@ -0,0 +1,15 @@ +package sdk + +import "testing" + +func TestHandshakeConfig(t *testing.T) { + if HandshakeConfig.ProtocolVersion != ProtocolVersion { + t.Fatalf("unexpected protocol version: %d", HandshakeConfig.ProtocolVersion) + } + if HandshakeConfig.MagicCookieKey != MagicCookieKey { + t.Fatalf("unexpected cookie key: %s", HandshakeConfig.MagicCookieKey) + } + if HandshakeConfig.MagicCookieValue != MagicCookieValue { + t.Fatalf("unexpected cookie value: %s", HandshakeConfig.MagicCookieValue) + } +} diff --git a/sdk/logger.go b/sdk/logger.go new file mode 100644 index 0000000..5e03b59 --- /dev/null +++ b/sdk/logger.go @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2026 The semrel Authors + +package sdk + +import ( + "io" + "os" + + "github.com/hashicorp/go-hclog" +) + +// NewLogger returns a logger configured for plugin binaries. +// +// ADR-001 requires stdout to stay reserved for the go-plugin handshake, +// therefore logs are always written to stderr. +func NewLogger(name string) hclog.Logger { + return hclog.New(&hclog.LoggerOptions{ + Name: name, + Level: hclog.Info, + Output: os.Stderr, + }) +} + +// NewLoggerWithOutput allows tests/custom setups while preserving sane defaults. +func NewLoggerWithOutput(name string, output io.Writer) hclog.Logger { + if output == nil { + output = os.Stderr + } + + return hclog.New(&hclog.LoggerOptions{ + Name: name, + Level: hclog.Info, + Output: output, + }) +} diff --git a/sdk/types.go b/sdk/types.go new file mode 100644 index 0000000..6852771 --- /dev/null +++ b/sdk/types.go @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2026 The semrel Authors + +package sdk + +// PluginType represents one of the supported plugin categories. +type PluginType string + +const ( + PluginTypeProvider PluginType = "provider" + PluginTypeCICondition PluginType = "ci-condition" + PluginTypeCommitAnalyzer PluginType = "commit-analyzer" + PluginTypeChangelogGenerator PluginType = "changelog-generator" + PluginTypeFilesUpdater PluginType = "files-updater" + PluginTypeHooks PluginType = "hooks" +) diff --git a/sdk/types_test.go b/sdk/types_test.go new file mode 100644 index 0000000..5e8490e --- /dev/null +++ b/sdk/types_test.go @@ -0,0 +1,24 @@ +package sdk + +import "testing" + +func TestPluginTypesAreStable(t *testing.T) { + if PluginTypeProvider != "provider" { + t.Fatalf("unexpected provider type: %q", PluginTypeProvider) + } + if PluginTypeCICondition != "ci-condition" { + t.Fatalf("unexpected ci type: %q", PluginTypeCICondition) + } + if PluginTypeCommitAnalyzer != "commit-analyzer" { + t.Fatalf("unexpected commit analyzer type: %q", PluginTypeCommitAnalyzer) + } + if PluginTypeChangelogGenerator != "changelog-generator" { + t.Fatalf("unexpected changelog type: %q", PluginTypeChangelogGenerator) + } + if PluginTypeFilesUpdater != "files-updater" { + t.Fatalf("unexpected files updater type: %q", PluginTypeFilesUpdater) + } + if PluginTypeHooks != "hooks" { + t.Fatalf("unexpected hooks type: %q", PluginTypeHooks) + } +} From 74931650b807b3518176189a81c531f264a6fffc Mon Sep 17 00:00:00 2001 From: Markus Waldheim Date: Sun, 24 May 2026 14:48:49 +0200 Subject: [PATCH 2/4] ci: fix REUSE compliance and lint toolchain mismatch Signed-off-by: Markus Waldheim --- .github/workflows/ci.yaml | 6 +- .reuse/dep5 | 7 ++ LICENSES/Apache-2.0.txt | 198 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+), 3 deletions(-) create mode 100644 .reuse/dep5 create mode 100644 LICENSES/Apache-2.0.txt diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6a10006..290f064 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -43,9 +43,9 @@ jobs: - uses: actions/setup-go@v5 with: - go-version: stable + go-version: "1.26" cache: true - - uses: golangci/golangci-lint-action@v4 + - uses: golangci/golangci-lint-action@v8 with: - version: latest + version: v2.1.6 diff --git a/.reuse/dep5 b/.reuse/dep5 new file mode 100644 index 0000000..aea15fa --- /dev/null +++ b/.reuse/dep5 @@ -0,0 +1,7 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: semrel-plugins +Source: https://github.com/SemRels/semrel-plugins + +Files: * +Copyright: 2026 The semrel Authors +License: Apache-2.0 diff --git a/LICENSES/Apache-2.0.txt b/LICENSES/Apache-2.0.txt new file mode 100644 index 0000000..ec462e0 --- /dev/null +++ b/LICENSES/Apache-2.0.txt @@ -0,0 +1,198 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship made available under + the License, as indicated by a copyright notice that is included in + or attached to the work (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean, as submitted to the Licensor for inclusion + in the Work by the copyright owner or by an individual or Legal Entity + authorized to submit on behalf of the copyright owner. For the purposes + of this definition, "submitted" means any form of electronic, verbal, + or written communication sent to the Licensor or its representatives, + including but not limited to communication on electronic mailing lists, + source code control systems, and issue tracking systems that are managed + by, or on behalf of, the Licensor for the purpose of recording and + discussing improvements to the Work, but excluding communication that is + conspicuously marked or designated in writing by the copyright owner as + "Not a Contribution." + + "Contributor" shall mean Licensor and any Legal Entity on behalf of + whom a Contribution has been received by the Licensor and included + within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by the combined contribution of their + Contribution(s) with the Work to which such Contribution(s) was + submitted. If You institute patent litigation against any entity + (including a cross-claim or counterclaim in a lawsuit) alleging that + the Work or any form of Contribution embodied within the Work + constitutes direct or contributory patent infringement, then any + patent licenses granted to You under this License for that Work shall + terminate as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 47fa59de4703d4a67053a651e5e4a1a0381dbd4e Mon Sep 17 00:00:00 2001 From: Markus Waldheim Date: Sun, 24 May 2026 14:56:02 +0200 Subject: [PATCH 3/4] chore: update CI Go version to 1.24 and add CONTRIBUTING guide Signed-off-by: Markus Waldheim --- .github/workflows/ci.yaml | 2 +- CONTRIBUTING.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 CONTRIBUTING.md diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 290f064..c620035 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -43,7 +43,7 @@ jobs: - uses: actions/setup-go@v5 with: - go-version: "1.26" + go-version: "1.24" cache: true - uses: golangci/golangci-lint-action@v8 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..42c9bb8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,5 @@ +# Contributing to go-semrel-plugins + +For plugin release publishing conventions, binary naming, checksums, and registry visibility, follow the canonical guide in [`go-semrel-registry`](https://github.com/SemRels/go-semrel-registry/blob/main/docs/release-guide.md). + +Use this repository for SDK work and for publishing plugin releases that should be aggregated into the registry. From aaec8be68796718ff5274b6a6b5547f19c536069 Mon Sep 17 00:00:00 2001 From: Markus Waldheim Date: Sun, 24 May 2026 15:12:52 +0200 Subject: [PATCH 4/4] chore: rename registry repo to semrel-registry Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 42c9bb8..33ff24f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,5 @@ # Contributing to go-semrel-plugins -For plugin release publishing conventions, binary naming, checksums, and registry visibility, follow the canonical guide in [`go-semrel-registry`](https://github.com/SemRels/go-semrel-registry/blob/main/docs/release-guide.md). +For plugin release publishing conventions, binary naming, checksums, and registry visibility, follow the canonical guide in [`semrel-registry`](https://github.com/SemRels/semrel-registry/blob/main/docs/release-guide.md). Use this repository for SDK work and for publishing plugin releases that should be aggregated into the registry.