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
107 changes: 107 additions & 0 deletions .github/workflows/semrel-release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: 2026 The plugin-template Authors
#
# semrel-release — uses semrel to version this plugin.
# Flow: push to main → semrel creates tag → release.yml builds binaries + publishes release
#
# condition-github-actions is built from source until its binary releases are stable.
name: Semrel Release

on:
push:
branches: [main]
workflow_dispatch:

concurrency:
group: semrel-release
cancel-in-progress: false

permissions:
contents: write
actions: write

jobs:
release:
name: Run semrel
runs-on: ubuntu-latest
outputs:
released: ${{ steps.semrel.outputs.released }}
tag: ${{ steps.semrel.outputs.tag }}
steps:
- name: Check out repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version: stable
cache: false

- name: Download semrel binary
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
VERSION=$(gh release view --repo SemRels/semrel --json tagName -q .tagName)
gh release download "$VERSION" --repo SemRels/semrel \
--pattern "semrel_*_linux_amd64.tar.gz" --dir /tmp
tar -xzf /tmp/semrel_*_linux_amd64.tar.gz -C /tmp
chmod +x /tmp/semrel
echo "semrel $(/tmp/semrel --version 2>/dev/null || /tmp/semrel version 2>/dev/null || echo installed)"

- name: Install plugins from source
# Build condition-github-actions from source (main branch) until binary
# releases use the subprocess interface expected by semrel's plugin runner.
# See: semrel/.semrel.yaml comment about plugin sourcing strategy.
run: |
set -euo pipefail
PLUGIN_DIR="$HOME/.semrel/plugins"
mkdir -p "$PLUGIN_DIR"
go install github.com/SemRels/condition-github-actions/cmd/plugin@main
mv "$(go env GOPATH)/bin/plugin" "$PLUGIN_DIR/semrel-plugin-condition-github-actions"
echo "Installed plugins:"
ls -la "$PLUGIN_DIR"

- name: Configure git identity
run: |
git config user.name "semrel-bot"
git config user.email "semrel-bot@users.noreply.github.com"

- name: Run semrel release
id: semrel
run: /tmp/semrel release --github-output
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Push tag to GitHub
if: steps.semrel.outputs.released == 'true'
run: |
TAG="${{ steps.semrel.outputs.tag }}"
if git ls-remote --tags origin "$TAG" | grep -q "$TAG"; then
echo "Tag $TAG already exists on remote — skipping (idempotent)."
else
git push origin "refs/tags/$TAG"
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Trigger binary build
if: steps.semrel.outputs.released == 'true'
run: |
TAG="${{ steps.semrel.outputs.tag }}"
echo "Triggering release.yml for tag $TAG"
gh workflow run release.yml --ref "$TAG" --repo "${{ github.repository }}"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Summary
run: |
if [ "${{ steps.semrel.outputs.released }}" = "true" ]; then
echo "### Released ${{ steps.semrel.outputs.tag }} 🚀" >> $GITHUB_STEP_SUMMARY
echo "**Bump:** ${{ steps.semrel.outputs.bump }}" >> $GITHUB_STEP_SUMMARY
else
echo "### No release — no releasable commits found." >> $GITHUB_STEP_SUMMARY
fi
14 changes: 7 additions & 7 deletions .reuse/dep5
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: {{PROJECT_NAME}}
Upstream-Contact: The {{PROJECT_NAME}} Authors <https://github.com/{{ORG_NAME}}/{{REPO_NAME}}>
Source: {{REPO_URL}}
Upstream-Name: plugin-template
Upstream-Contact: The plugin-template Authors <https://github.com/SemRels/plugin-template>
Source: https://github.com/SemRels/plugin-template

# ----------------------------------------------------------------------------
# Documentation and community health files
# ----------------------------------------------------------------------------
Files: *.md
.github/**/*.md
Copyright: {{COPYRIGHT_YEAR}} The {{PROJECT_NAME}} Authors
Copyright: 2026 The plugin-template Authors
License: Apache-2.0

# ----------------------------------------------------------------------------
Expand All @@ -20,6 +20,7 @@ Files: Makefile
go.mod
go.sum
.golangci.yml
.semrel.yaml
*.template
.gitignore
.gitignore.template
Expand All @@ -30,8 +31,7 @@ Files: Makefile
.reuse/dep5
cmd/**/*.go
internal/**/*.go
proto/**
Copyright: {{COPYRIGHT_YEAR}} The {{PROJECT_NAME}} Authors
Copyright: 2026 The plugin-template Authors
License: Apache-2.0

# ----------------------------------------------------------------------------
Expand All @@ -40,5 +40,5 @@ License: Apache-2.0
Files: LICENSE
LICENSES/Apache-2.0.txt
NOTICE
Copyright: {{COPYRIGHT_YEAR}} The {{PROJECT_NAME}} Authors
Copyright: 2026 The plugin-template Authors
License: Apache-2.0
22 changes: 22 additions & 0 deletions .semrel.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: 2026 The plugin-template Authors
# yaml-language-server: $schema=https://semrel.io/schema/v1.json
#
# semrel uses itself to manage this plugin's releases.
# version_ceiling keeps releases in 0.x.x until deliberately promoted to 1.0.0.
# To release 1.0.0: remove version_ceiling and commit.

tagPrefix: "v"
version_ceiling: "1.0.0"
ceiling_strategy: clamp
commit_changelog: false
tag_exists_strategy: update-changelog

branches:
- name: main

plugins:
# condition-github-actions verifies we are running inside a legitimate
# GitHub Actions CI environment before any git work is done.
- uses: condition-github-actions
phase: condition
30 changes: 27 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,38 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: 2026 The plugin-template Authors

FROM golang:1.25-alpine AS build
# ── build stage ────────────────────────────────────────────────────────────────
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS build

ARG TARGETOS
ARG TARGETARCH
ARG VERSION=dev

WORKDIR /src
RUN apk add --no-cache ca-certificates git
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' -o /out/plugin ./cmd/plugin
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
go build -trimpath -ldflags="-s -w" -o /out/plugin ./cmd/plugin

# ── distroless release image ───────────────────────────────────────────────────
FROM gcr.io/distroless/static-debian12:nonroot

ARG VERSION=dev
ARG BUILD_DATE
ARG VCS_REF

LABEL org.opencontainers.image.title="semrel-plugin-plugin-template" \
org.opencontainers.image.description="semrel plugin template subprocess example" \
org.opencontainers.image.url="https://semrel.io" \
org.opencontainers.image.source="https://github.com/SemRels/plugin-template" \
org.opencontainers.image.version="${VERSION}" \
org.opencontainers.image.created="${BUILD_DATE}" \
org.opencontainers.image.revision="${VCS_REF}" \
org.opencontainers.image.licenses="Apache-2.0" \
org.opencontainers.image.vendor="SemRels"

FROM gcr.io/distroless/static-debian12
COPY --from=build /out/plugin /usr/local/bin/plugin
USER nonroot
ENTRYPOINT ["/usr/local/bin/plugin"]
79 changes: 54 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,72 @@
# {{PLUGIN_NAME}}
# plugin-template

[![Latest Release](https://img.shields.io/github/v/release/SemRels/{{PLUGIN_NAME}}?label=version&color=blue)](https://github.com/SemRels/{{PLUGIN_NAME}}/releases/latest)
[![Latest Release](https://img.shields.io/github/v/release/SemRels/plugin-template?label=version&color=blue)](https://github.com/SemRels/plugin-template/releases/latest)
[![CI](https://github.com/SemRels/plugin-template/actions/workflows/ci.yml/badge.svg)](https://github.com/SemRels/plugin-template/actions/workflows/ci.yml)
[![License](https://img.shields.io/github/license/SemRels/plugin-template)](LICENSE)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/SemRels/plugin-template/badge)](https://scorecard.dev/viewer/?uri=github.com/SemRels/plugin-template)
[![REUSE status](https://api.reuse.software/badge/github.com/SemRels/plugin-template)](https://api.reuse.software/info/github.com/SemRels/plugin-template)

> Replace this description with what your SemRel plugin does.
Template repository for SemRels plugins built as standalone Go subprocess binaries.

This repository is based on the `SemRels/plugin-template` GitHub template and provides a clean starting point for provider, analyzer, generator, updater, or hook plugins.
This template reflects the current SemRels plugin architecture: semrel executes a plain Go CLI binary as a subprocess, passes plugin configuration through `SEMREL_PLUGIN_*` variables, passes release context through `SEMREL_*` variables, reads plugin output from standard output, and treats exit code `0` as success and any non-zero exit code as failure. There is no gRPC transport, no network listener, and no protobuf plumbing to wire up.

## Repository Layout
## What the example plugin does

The scaffolded example is intentionally phase-agnostic. It reads `SEMREL_VERSION` and `SEMREL_DRY_RUN`, writes the required schema handshake to standard error, and prints a confirmation message to standard output:

```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
stderr: plugin_schema_version=1
stdout: example plugin invoked for version 1.2.3 (dry-run: false)
```

## Installation
## Plugin contract

| Item | Behavior |
| --- | --- |
| `plugin_schema_version=1` | The **first line written to stderr** must be `plugin_schema_version=1`. |
| `SEMREL_VERSION` | Required release version for this example plugin. |
| `SEMREL_DRY_RUN` | Optional boolean (`true`/`false`) indicating dry-run mode. |
| `SEMREL_PLUGIN_*` | Reserved for your phase-specific plugin configuration. |
| stdout | Write the plugin's functional output here. |
| stderr | Write diagnostics, validation errors, and logs here. |
| exit code | `0` means success; any non-zero code means failure. |

## Repository layout

Published binaries are distributed through releases and synchronized to `registry.semrel.io`.
```text
cmd/plugin/ Thin environment-variable wrapper and process entry point
internal/plugin/ Pure, testable plugin logic with no os/env dependencies
.github/workflows/ CI, release, security, template-sync, and semrel self-release automation
.semrel.yaml semrel dogfooding configuration for this repository
```

## Development
## Use this template for a real plugin

1. Create a new repository from this template or copy it into your plugin repository.
2. Rename the Go module in `go.mod` to `github.com/SemRels/<your-plugin-repo>`.
3. Rename the binary/package descriptions from `plugin-template` to your plugin name.
4. Implement your phase-specific logic in `internal/plugin/` as pure Go.
5. Keep `cmd/plugin/main.go` thin: read `SEMREL_*` / `SEMREL_PLUGIN_*` with `os.Getenv` (or an injected `getenv` in tests), translate them into a config struct, then call the internal package.
6. Extend the README tables with every `SEMREL_PLUGIN_*` and `SEMREL_*` variable your plugin consumes.
7. Update `.semrel.yaml` and replace the example phase assignment comments/configuration with the plugins and phases your real plugin release process needs.

## Local development

```bash
go build ./cmd/plugin
go build ./...
go test ./...
```

## Configuration
## Example semrel configuration

See the SemRel documentation for plugin configuration and runtime integration details:

- https://github.com/SemRels/semrel
- https://registry.semrel.io
```yaml
plugins:
- name: example-plugin
path: ~/.semrel/plugins/semrel-plugin-example
env:
SEMREL_PLUGIN_SAMPLE_OPTION: "value"
```

## Next Steps
## License

1. Replace all `{{...}}` placeholders.
2. Rename the module path in `go.mod`.
3. Implement your plugin logic in `internal/plugin/`.
4. Wire generated protobuf bindings into `internal/grpc/`.
5. Create your first tagged release with `v*.*.*`.
Apache-2.0
29 changes: 19 additions & 10 deletions cmd/plugin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,31 @@
package main

import (
"context"
"log"
"fmt"
"io"
"os"

grpcserver "github.com/SemRels/plugin-template/internal/grpc"
semrelplugin "github.com/SemRels/plugin-template/internal/plugin"
plugin "github.com/SemRels/plugin-template/internal/plugin"
)

const pluginSchemaVersion = 1

func main() {
provider := semrelplugin.NewProvider("replace-me")
server := grpcserver.NewProviderServer(provider)
os.Exit(run(os.Stdout, os.Stderr, os.Getenv))
}

func run(stdout, stderr io.Writer, getenv func(string) string) int {
_, _ = fmt.Fprintf(stderr, "plugin_schema_version=%d\n", pluginSchemaVersion)

if _, err := server.Health(context.Background()); err != nil {
log.Printf("plugin health check failed: %v", err)
os.Exit(1)
message, err := plugin.Message(plugin.Config{
Version: getenv("SEMREL_VERSION"),
DryRun: getenv("SEMREL_DRY_RUN") == "true",
})
if err != nil {
fmt.Fprintln(stderr, "plugin-template:", err)

Check failure on line 28 in cmd/plugin/main.go

View workflow job for this annotation

GitHub Actions / Build, test, lint, and scan (1.26)

Error return value of `fmt.Fprintln` is not checked (errcheck)

Check failure on line 28 in cmd/plugin/main.go

View workflow job for this annotation

GitHub Actions / Build, test, lint, and scan (1.25)

Error return value of `fmt.Fprintln` is not checked (errcheck)
return 1
}

log.Printf("%s plugin template is ready", provider.Name())
fmt.Fprintln(stdout, message)

Check failure on line 32 in cmd/plugin/main.go

View workflow job for this annotation

GitHub Actions / Build, test, lint, and scan (1.26)

Error return value of `fmt.Fprintln` is not checked (errcheck)

Check failure on line 32 in cmd/plugin/main.go

View workflow job for this annotation

GitHub Actions / Build, test, lint, and scan (1.25)

Error return value of `fmt.Fprintln` is not checked (errcheck)
return 0
}
Loading
Loading