name: CI on: push: branches: [main] pull_request: branches: [main] workflow_dispatch: jobs: test: name: Test runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version-file: "go.mod" - name: Cache Go modules uses: actions/cache@v5 with: path: | ~/.cache/go-build ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.mod') }}-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - name: Download dependencies run: go mod download - name: Verify dependencies run: go mod verify - name: Run tests run: go test -v -race -coverprofile=coverage.out ./... - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 with: file: ./coverage.out flags: unittests name: codecov-umbrella fail_ci_if_error: false lint: name: Lint runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version-file: "go.mod" - name: Run golangci-lint uses: golangci/golangci-lint-action@v9 with: version: latest args: --timeout=5m build: name: Build runs-on: ubuntu-latest strategy: matrix: goos: [linux, darwin, windows] goarch: [amd64, arm64] exclude: # Windows ARM64 builds are experimental - goos: windows goarch: arm64 steps: - name: Checkout code uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version-file: "go.mod" - name: Build CLI env: GOOS: ${{ matrix.goos }} GOARCH: ${{ matrix.goarch }} run: | output_name="soundtouch-cli-${{ matrix.goos }}-${{ matrix.goarch }}" if [ "${{ matrix.goos }}" = "windows" ]; then output_name="${output_name}.exe" fi go build -o "$output_name" ./cmd/soundtouch-cli - name: Upload build artifacts uses: actions/upload-artifact@v6 with: name: soundtouch-cli-${{ matrix.goos }}-${{ matrix.goarch }} path: soundtouch-cli-* security: name: Basic Security Check runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version-file: "go.mod" - name: Run basic vulnerability check run: | go install golang.org/x/vuln/cmd/govulncheck@latest govulncheck ./... - name: Security scan reminder run: | echo "ℹ️ This is a basic security check for CI speed." echo "For comprehensive security scanning, see the Security workflow:" echo "https://github.com/${{ github.repository }}/actions/workflows/security.yml" docs: name: Documentation Check runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v6 - name: Check documentation links uses: gaurav-nelson/github-action-markdown-link-check@v1 with: use-quiet-mode: "yes" use-verbose-mode: "yes" config-file: ".github/markdown-link-check.json" - name: Validate API documentation run: | # Check that all documented endpoints exist in code echo "Validating API documentation consistency..." # Extract endpoint patterns from cookbook if [ -f "docs/API-COOKBOOK.md" ]; then echo "✓ API Cookbook exists" else echo "✗ API Cookbook missing" exit 1 fi # Check getting started guide if [ -f "docs/GETTING-STARTED.md" ]; then echo "✓ Getting Started guide exists" else echo "✗ Getting Started guide missing" exit 1 fi integration: name: Integration Test runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - name: Checkout code uses: actions/checkout@v6 - name: Set up Go uses: actions/setup-go@v6 with: go-version-file: "go.mod" - name: Test CLI build and help run: | go build -o soundtouch-cli ./cmd/soundtouch-cli ./soundtouch-cli -help - name: Test library imports run: | cat > test_import.go << 'EOF' package main import ( "fmt" "github.com/user_account/bose-soundtouch/pkg/client" "github.com/user_account/bose-soundtouch/pkg/models" "github.com/user_account/bose-soundtouch/pkg/discovery" "github.com/user_account/bose-soundtouch/pkg/config" ) func main() { // Test basic client creation c := client.NewClientFromHost("192.168.1.100") fmt.Printf("Client created for %s\n", c.BaseURL()) // Test models can be imported var info models.DeviceInfo fmt.Printf("DeviceInfo model available: %T\n", info) // Test discovery can be imported cfg := config.DefaultConfig() service := discovery.NewUnifiedDiscoveryService(cfg) fmt.Printf("Discovery service available: %T\n", service) fmt.Println("All imports successful!") } EOF go run test_import.go rm test_import.go notify: name: Notify Status runs-on: ubuntu-latest needs: [test, lint, build, security, docs] if: always() permissions: statuses: write contents: read steps: - name: Check overall status run: | if [[ "${{ needs.test.result }}" == "success" && \ "${{ needs.lint.result }}" == "success" && \ "${{ needs.build.result }}" == "success" && \ "${{ needs.security.result }}" == "success" && \ "${{ needs.docs.result }}" == "success" ]]; then echo "✅ All CI checks passed!" echo "status=success" >> $GITHUB_OUTPUT else echo "❌ Some CI checks failed" echo "Test: ${{ needs.test.result }}" echo "Lint: ${{ needs.lint.result }}" echo "Build: ${{ needs.build.result }}" echo "Security: ${{ needs.security.result }}" echo "Docs: ${{ needs.docs.result }}" echo "status=failure" >> $GITHUB_OUTPUT fi id: status - name: Update commit status if: always() uses: actions/github-script@v8 with: script: | try { const state = '${{ steps.status.outputs.status }}' === 'success' ? 'success' : 'failure'; const description = state === 'success' ? 'All checks passed' : 'Some checks failed'; await github.rest.repos.createCommitStatus({ owner: context.repo.owner, repo: context.repo.repo, sha: context.sha, state: state, description: description, context: 'CI Pipeline' }); console.log(`✅ Successfully updated commit status to: ${state}`); } catch (error) { console.log(`⚠️ Failed to update commit status: ${error.message}`); // Don't fail the workflow if status update fails }