Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions .github/goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
version: 2

before:
hooks:
- go mod tidy

builds:
- id: protoc-gen-connect-go-servicestruct
main: ./cmd/protoc-gen-connect-go-servicestruct
binary: protoc-gen-connect-go-servicestruct
skip: '{{ ne .Env.BUILD_COMPONENT "protoc-gen-connect-go-servicestruct" }}'
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.Commit}}
- -X main.date={{.Date}}

- id: protoc-gen-elixir-grpc
main: ./cmd/protoc-gen-elixir-grpc
binary: protoc-gen-elixir-grpc
skip: '{{ ne .Env.BUILD_COMPONENT "protoc-gen-elixir-grpc" }}'
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.Commit}}
- -X main.date={{.Date}}

archives:
- id: default
name_template: >-
{{ .Env.BUILD_COMPONENT }}_
{{- .Version }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}v{{ .Arm }}{{ end }}
files:
- LICENSE
- README.md
- cmd/*/README.md
- cmd/*/CHANGELOG.md

checksum:
name_template: "checksums.txt"

snapshot:
version_template: "{{ .Branch }}-{{ .ShortCommit }}-snapshot"

changelog:
sort: asc
use: github
filters:
exclude:
- "^docs:"
- "^test:"
- "^chore:"
- "Merge pull request"
- "Merge remote-tracking branch"
groups:
- title: Features
regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
order: 0
- title: "Bug Fixes"
regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
order: 1
- title: Others
order: 999

release:
github:
owner: TrogonStack
name: protoc-gen
draft: false
prerelease: auto
mode: append
name_template: "{{.Tag}}"
156 changes: 156 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
---
name: cd

on:
release:
types: [published]
workflow_dispatch:
inputs:
dry_run:
description: 'Run in dry-run mode (snapshot, no publish)'
required: false
default: 'true'
type: choice
options:
- 'true'
- 'false'
tag:
description: 'Tag to use for version extraction (optional, e.g., protoc-gen-elixir-grpc@v0.2.0)'
required: false
type: string

permissions:
contents: write
packages: write
id-token: write
attestations: write

jobs:
# Run tests only for manual workflow_dispatch triggers to catch issues during testing.
# For actual releases, CI has already validated the code.
# TODO: Consider removing workflow_dispatch entirely once the release process is stable.
test:
name: Run Tests
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5.0.0

- name: Set up Go
uses: actions/setup-go@v6.0.0
with:
go-version: "1.24"

- name: Run Tests
run: go test -race -v ./...

- name: Run Vet
run: go vet ./...

goreleaser:
name: Release with GoReleaser
needs: test
if: |
always() &&
(needs.test.result == 'success' || needs.test.result == 'skipped')
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5.0.0
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v6.0.0
with:
go-version: "1.24"

- name: Extract Version from Tag
id: version
run: |
# Extract version from monorepo tags like protoc-gen-elixir-grpc@v0.1.0
if [ "${{ github.event_name }}" = "release" ]; then
TAG="${{ github.event.release.tag_name }}"
elif [ -n "${{ github.event.inputs.tag }}" ]; then
# Use tag from workflow_dispatch input if provided
TAG="${{ github.event.inputs.tag }}"
elif [[ "${{ github.ref }}" == refs/tags/* ]]; then
# Use the ref if it's a tag
TAG="${{ github.ref_name }}"
else
echo "ERROR: No tag provided. Please provide a tag via workflow_dispatch input."
echo "Example: gh workflow run cd.yml -f dry_run=true -f tag=protoc-gen-elixir-grpc@v0.1.0"
exit 1
fi

# Validate tag format (must contain @ separator)
if [[ ! "$TAG" =~ @ ]]; then
echo "ERROR: Tag must follow pattern 'component@version', got: ${TAG}"
echo "Examples: protoc-gen-elixir-grpc@v0.1.0, protoc-gen-connect-go-servicestruct@v0.2.0"
exit 1
fi

VERSION="${TAG##*@}"
COMPONENT="${TAG%%@*}"

# Validate version is not empty
if [ -z "$VERSION" ]; then
echo "ERROR: Failed to extract version from tag: ${TAG}"
exit 1
fi

# Validate component is known
case "$COMPONENT" in
protoc-gen-elixir-grpc|protoc-gen-connect-go-servicestruct)
echo "Valid component: ${COMPONENT}"
;;
*)
echo "ERROR: Unknown component: ${COMPONENT}"
echo "Valid components: protoc-gen-elixir-grpc, protoc-gen-connect-go-servicestruct"
exit 1
;;
esac

echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "component=${COMPONENT}" >> $GITHUB_OUTPUT
echo "Extracted component: ${COMPONENT}"
echo "Extracted version: ${VERSION}"

- name: Run GoReleaser (Snapshot)
if: github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'true'
uses: goreleaser/goreleaser-action@v6.4.0
with:
distribution: goreleaser
version: "~> v2"
args: release --snapshot --clean --skip=publish --config .github/goreleaser.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GORELEASER_CURRENT_TAG: ${{ steps.version.outputs.version }}
BUILD_COMPONENT: ${{ steps.version.outputs.component }}

- name: Run GoReleaser (Release)
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'false')
uses: goreleaser/goreleaser-action@v6.4.0
with:
distribution: goreleaser
version: "~> v2"
args: release --clean --config .github/goreleaser.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GORELEASER_CURRENT_TAG: ${{ steps.version.outputs.version }}
BUILD_COMPONENT: ${{ steps.version.outputs.component }}

- name: Attest Build Provenance
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'false')
uses: actions/attest-build-provenance@v3.0.0
with:
subject-path: "dist/**/*.tar.gz"

- name: Upload Snapshot Artifacts
if: github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'true'
uses: actions/upload-artifact@v4.6.2
with:
name: snapshot-${{ steps.version.outputs.component }}-${{ steps.version.outputs.version }}
path: dist/*
retention-days: 7
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ go.work.sum
# Generated protoc plugin binaries (root directory only)
/protoc-gen-*
/protoc-gen-*.wasm

# GoReleaser artifacts
dist/
52 changes: 19 additions & 33 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ tasks:
desc: Run fmt, vet, lint, and test
deps: [fmt, vet, lint, test]

help:
desc: Show available tasks
cmds:
- task --list

test:
desc: Run all tests
cmds:
Expand All @@ -28,16 +23,6 @@ tasks:
- echo "Running tests with coverage..."
- go test -cover ./...

test-coverage-html:
desc: Run tests and generate HTML coverage report
deps: [clean]
cmds:
- echo "Running tests and generating HTML coverage report..."
- go test -coverprofile=coverage.out -covermode=count ./...
- go tool cover -html=coverage.out -o coverage.html
- echo "Coverage report generated at coverage.html"
- echo "Open coverage.html in your browser to view the report"

fmt:
desc: Format code with gofmt
cmds:
Expand All @@ -50,17 +35,22 @@ tasks:
- echo "Running go vet..."
- go vet ./...

golangci-lint-check-installed:
desc: Check if golangci-lint is installed
internal: true
preconditions:
- sh: command -v golangci-lint
msg: |
golangci-lint not found. Install with:
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
or visit https://golangci-lint.run/welcome/install/
lint:
desc: Run golangci-lint (if available)
desc: Run golangci-lint
deps: [golangci-lint-check-installed]
cmds:
- echo "Running linter..."
- |
if command -v golangci-lint >/dev/null 2>&1; then
golangci-lint run
else
echo "golangci-lint not found, skipping lint check"
echo "Install with: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest"
fi
- golangci-lint run

build:
desc: Build the package
Expand All @@ -87,14 +77,10 @@ tasks:
- echo "Building Elixir gRPC protoc plugin binary..."
- go build -o protoc-gen-elixir-grpc ./cmd/protoc-gen-elixir-grpc

dev-test:
desc: Development workflow - fmt, vet, and generate coverage HTML
deps: [fmt, vet, test-coverage-html]
cmds:
- echo "Development test complete - check coverage.html"

ci-test:
desc: CI workflow - fmt, vet, and test with coverage
deps: [fmt, vet, test-coverage]
clean:
desc: Clean build artifacts and coverage files
cmds:
- echo "CI test complete"
- echo "Cleaning artifacts..."
- rm -rf dist/
- rm -f coverage.out coverage.html
- rm -f protoc-gen-*