diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3266867 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,97 @@ +# Continuous Integration for the go-sdk. +# +# Runs build, vet, format check and tests, plus a cross-compile matrix across +# every OS/arch consumers ship on. The cross-compile job exists specifically to +# catch platform-portability regressions (e.g. Unix-only syscalls) at the +# source, before they reach downstream binaries. +# +# Scope matches the Makefile's PACKAGES (library packages only). The examples/ +# tree is excluded because some examples depend on generated, gitignored files +# (e.g. portable-embedded-agent/secrets_gen.go). +name: CI + +on: + push: + branches: + - main + - next + paths-ignore: + - '**.md' + - 'docs/**' + - 'LICENSE' + pull_request: + branches: + - main + - next + paths-ignore: + - '**.md' + - 'docs/**' + - 'LICENSE' + +permissions: + contents: read + +env: + PACKAGES: ./sdk/... ./agent/... ./types/... ./internal/... + +jobs: + check: + name: Build, Vet & Test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + cache: true + + - name: Vet + run: make vet + + - name: Format check + run: make fmt-check + + - name: Test + run: make test + + - name: Build + run: go build ${{ env.PACKAGES }} + + cross-compile: + name: Cross-compile (${{ matrix.goos }}/${{ matrix.goarch }}) + runs-on: ubuntu-latest + needs: check + strategy: + fail-fast: false + matrix: + include: + - goos: linux + goarch: amd64 + - goos: linux + goarch: arm64 + - goos: darwin + goarch: amd64 + - goos: darwin + goarch: arm64 + - goos: windows + goarch: amd64 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + cache: true + + - name: Cross-compile + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + CGO_ENABLED: '0' + run: go build ${{ env.PACKAGES }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..4fa8abc --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,33 @@ +# Release workflow for the go-sdk. +# +# go-sdk is a library, not a binary — for a Go module the git tag *is* the +# release (consumers fetch source via `go get`). This workflow therefore does +# not build or publish artifacts; it only creates a GitHub Release with +# auto-generated notes for the tag created by tag-release.yml. +# +# Triggered by tag-release.yml (workflow_dispatch at the new tag) or by a +# manually pushed `v*` tag. +name: Release + +on: + push: + tags: + - 'v*' + workflow_dispatch: + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true diff --git a/.github/workflows/tag-release.yml b/.github/workflows/tag-release.yml new file mode 100644 index 0000000..8a1ae03 --- /dev/null +++ b/.github/workflows/tag-release.yml @@ -0,0 +1,65 @@ +# Auto-tag when merging to main +# +# Reads the version from the VERSION file and, if the matching tag does not +# already exist, creates and pushes it. For a Go module the tag *is* the +# release, so this is what makes `go get @latest` / pinned versions resolve. +name: Tag Release + +on: + push: + branches: + - main + +jobs: + tag: + runs-on: ubuntu-latest + permissions: + contents: write + actions: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get version from VERSION file + id: version + run: | + if [ -f VERSION ]; then + VERSION=$(cat VERSION) + else + VERSION="0.0.0" + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Version: $VERSION" + + - name: Check if tag exists + id: check_tag + run: | + if git rev-parse "v${{ steps.version.outputs.version }}" >/dev/null 2>&1; then + echo "exists=true" >> $GITHUB_OUTPUT + echo "Tag v${{ steps.version.outputs.version }} already exists" + else + echo "exists=false" >> $GITHUB_OUTPUT + echo "Tag v${{ steps.version.outputs.version }} does not exist" + fi + + - name: Create and push tag + if: steps.check_tag.outputs.exists == 'false' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag -a "v${{ steps.version.outputs.version }}" -m "Release v${{ steps.version.outputs.version }}" + git push origin "v${{ steps.version.outputs.version }}" + + - name: Trigger release workflow + if: steps.check_tag.outputs.exists == 'false' + uses: actions/github-script@v7 + with: + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'release.yml', + ref: 'v${{ steps.version.outputs.version }}' + }) diff --git a/README.md b/README.md index 5daa31b..e43bb49 100644 --- a/README.md +++ b/README.md @@ -573,6 +573,25 @@ if err != nil { - Go 1.21 or later +## Releasing + +Versions are published as Git tags — for a Go module, the tag _is_ the release. +The version is driven by the [`VERSION`](VERSION) file: + +1. Bump `VERSION` (semver, no `v` prefix — e.g. `0.2.0`) in a pull request. +2. Merge to `main`. The `Tag Release` workflow reads `VERSION` and, if the + matching `vX.Y.Z` tag does not yet exist, creates and pushes it, then + triggers the `Release` workflow to publish GitHub release notes. + +Consumers then pin the new version: + +```bash +go get github.com/chatbotkit/go-sdk@v0.2.0 +``` + +While the API is still evolving the module stays on `v0.x` (minor versions may +introduce breaking changes); it will move to `v1.0.0` once the API is stable. + ## License See [LICENSE](LICENSE) for details. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.0