diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e49caa8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,120 @@ +name: CI + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + go: + name: Go checks + runs-on: macos-latest + steps: + - name: Check out source + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Check formatting + shell: bash + run: | + go fmt ./... + if ! git diff --quiet -- '*.go'; then + echo 'Go formatting changed tracked files:' + git diff -- '*.go' + exit 1 + fi + + - name: Vet + run: go vet ./... + + - name: Test + run: go test ./... + + - name: Test with race detector + run: go test -race ./... + + - name: Build CLI + run: go build -trimpath ./cmd/things-cli + + - name: Build release architectures + shell: bash + run: | + mkdir -p "$RUNNER_TEMP/things-cli-build" + GOOS=darwin GOARCH=amd64 go build -trimpath \ + -o "$RUNNER_TEMP/things-cli-build/things-cli-darwin-amd64" \ + ./cmd/things-cli + GOOS=darwin GOARCH=arm64 go build -trimpath \ + -o "$RUNNER_TEMP/things-cli-build/things-cli-darwin-arm64" \ + ./cmd/things-cli + + release-config: + name: GoReleaser validation + runs-on: ubuntu-latest + steps: + - name: Check out source + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Install Syft for SBOM generation + uses: anchore/sbom-action/download-syft@v0.24.0 + + - name: Check GoReleaser configuration + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: v2.17.1 + args: check + + - name: Validate snapshot release + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: v2.17.1 + args: release --snapshot --clean + + docs: + name: Documentation build + runs-on: ubuntu-latest + steps: + - name: Check out source + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: docs/package-lock.json + + - name: Install documentation dependencies + run: npm --prefix docs ci + + - name: Build documentation + run: npm --prefix docs run build + + - name: Upload documentation artifact + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v4 + with: + name: things-cli-docs-${{ github.run_id }} + path: docs/dist + if-no-files-found: error + retention-days: 3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..995ad05 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,81 @@ +name: Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: read + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false + +jobs: + release: + name: Publish release + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + attestations: write + steps: + - name: Check out source and tag history + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Validate release tag + shell: bash + run: | + tag_commit="$(git rev-list -n 1 "$GITHUB_REF")" + if [[ -z "$tag_commit" || "$tag_commit" != "$GITHUB_SHA" ]]; then + echo "::error::${GITHUB_REF} does not resolve to the workflow commit ($GITHUB_SHA)." + exit 1 + fi + + git fetch origin main --no-tags + if ! git merge-base --is-ancestor "$tag_commit" origin/main; then + echo "::error::Release tags must point at a commit reachable from main." + exit 1 + fi + + - name: Vet + run: go vet ./... + + - name: Test + run: go test ./... + + - name: Verify Homebrew token + env: + HAS_TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN != '' }} + run: | + if [[ "$HAS_TAP_GITHUB_TOKEN" != "true" ]]; then + echo '::error::TAP_GITHUB_TOKEN is required to publish the Homebrew cask.' + exit 1 + fi + + - name: Install Syft for SBOM generation + uses: anchore/sbom-action/download-syft@v0.24.0 + + - name: Release with GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: v2.17.1 + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }} + + - name: Attest release artifacts + uses: actions/attest-build-provenance@v2 + with: + subject-path: 'dist/*' diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 8c17e89..cc3adab 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -29,6 +29,9 @@ archives: checksum: name_template: "checksums.txt" +sboms: + - artifacts: archive + changelog: use: github sort: asc @@ -38,7 +41,7 @@ changelog: - "^test:" - "^chore:" -brews: +homebrew_casks: - name: things-cli repository: owner: thaodangspace @@ -47,8 +50,6 @@ brews: homepage: "https://github.com/thaodangspace/things-cli" description: "Things 3 CLI for agents and humans (JSON output by default)" license: "MIT" - directory: Formula - install: | - bin.install "things-cli" - test: | - system "#{bin}/things-cli", "--version" + directory: Casks + binaries: + - things-cli diff --git a/Makefile b/Makefile index 42dbe7e..c646c4d 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ vet: ## Run go vet tidy: ## Tidy Go modules go mod tidy -check: fmt tidy vet test ## Format, tidy, vet, and test +check: fmt tidy vet test build ## Format, tidy, vet, test, and build docs-install: ## Install documentation site dependencies npm --prefix docs ci