From 3faafb363521c46d9f02deabb2cc619056a7d183 Mon Sep 17 00:00:00 2001 From: Tobias Gesellchen Date: Fri, 9 Jan 2026 12:56:12 +0100 Subject: [PATCH] refactor: Use Go version from go.mod in workflows - Remove Go version matrix from CI workflow - Use go-version-file option to read from go.mod - Update cache keys to use go.mod hash - Ensure single source of truth for Go version - Simplify maintenance by centralizing version management --- .github/workflows/ci.yml | 338 +++++++++++----------- .github/workflows/release.yml | 514 +++++++++++++++++----------------- 2 files changed, 424 insertions(+), 428 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc9b7e6..9122736 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,74 +2,70 @@ name: CI on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] workflow_dispatch: jobs: test: name: Test runs-on: ubuntu-latest - strategy: - matrix: - go-version: ['1.21', '1.22', '1.23'] steps: - - name: Checkout code - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: ${{ matrix.go-version }} + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: "go.mod" - - name: Cache Go modules - uses: actions/cache@v4 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go-${{ matrix.go-version }}- + - name: Cache Go modules + uses: actions/cache@v4 + 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: Download dependencies + run: go mod download - - name: Verify dependencies - run: go mod verify + - name: Verify dependencies + run: go mod verify - - name: Run tests - run: go test -v -race -coverprofile=coverage.out ./... + - name: Run tests + run: go test -v -race -coverprofile=coverage.out ./... - - name: Upload coverage to Codecov - if: matrix.go-version == '1.23' - uses: codecov/codecov-action@v4 - with: - file: ./coverage.out - flags: unittests - name: codecov-umbrella - fail_ci_if_error: false + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + 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@v4 + - name: Checkout code + uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: '1.23' + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: "go.mod" - - name: Run golangci-lint - uses: golangci/golangci-lint-action@v4 - with: - version: latest - args: --timeout=5m + - name: Run golangci-lint + uses: golangci/golangci-lint-action@v4 + with: + version: latest + args: --timeout=5m build: name: Build @@ -84,89 +80,89 @@ jobs: goarch: arm64 steps: - - name: Checkout code - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: '1.23' + - name: Set up Go + uses: actions/setup-go@v5 + 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: 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@v4 - with: - name: soundtouch-cli-${{ matrix.goos }}-${{ matrix.goarch }} - path: soundtouch-cli-* + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: soundtouch-cli-${{ matrix.goos }}-${{ matrix.goarch }} + path: soundtouch-cli-* security: name: Security Scan runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: '1.23' + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: "go.mod" - - name: Run Gosec Security Scanner - uses: securecodewarrior/github-action-gosec@master - with: - args: ./... + - name: Run Gosec Security Scanner + uses: securecodewarrior/github-action-gosec@master + with: + args: ./... - - name: Run Nancy vulnerability scanner - run: | - go install github.com/sonatypecommunity/nancy@latest - go list -json -deps ./... | nancy sleuth + - name: Run Nancy vulnerability scanner + run: | + go install github.com/sonatypecommunity/nancy@latest + go list -json -deps ./... | nancy sleuth docs: name: Documentation Check runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@v4 - - 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: 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..." + - 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 + # 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 + # 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 @@ -174,50 +170,50 @@ jobs: if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - - name: Checkout code - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: '1.23' + - name: Set up Go + uses: actions/setup-go@v5 + 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 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 + - 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" - ) + 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" + ) - func main() { - // Test basic client creation - c := client.New("192.168.1.100", 8090) - fmt.Printf("Client created for %s\n", c.BaseURL()) + func main() { + // Test basic client creation + c := client.New("192.168.1.100", 8090) + fmt.Printf("Client created for %s\n", c.BaseURL()) - // Test models can be imported - var info models.Info - fmt.Printf("Info model available: %T\n", info) + // Test models can be imported + var info models.Info + fmt.Printf("Info model available: %T\n", info) - // Test discovery can be imported - var scanner discovery.Scanner - fmt.Printf("Scanner available: %T\n", scanner) + // Test discovery can be imported + var scanner discovery.Scanner + fmt.Printf("Scanner available: %T\n", scanner) - fmt.Println("All imports successful!") - } - EOF + fmt.Println("All imports successful!") + } + EOF - go run test_import.go - rm test_import.go + go run test_import.go + rm test_import.go notify: name: Notify Status @@ -226,35 +222,35 @@ jobs: if: always() 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 "status=failure" >> $GITHUB_OUTPUT - exit 1 - fi - id: status + - 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 "status=failure" >> $GITHUB_OUTPUT + exit 1 + fi + id: status - - name: Update commit status - if: always() - uses: actions/github-script@v7 - with: - script: | - const state = '${{ steps.status.outputs.status }}' === 'success' ? 'success' : 'failure'; - const description = state === 'success' ? 'All checks passed' : 'Some checks failed'; + - name: Update commit status + if: always() + uses: actions/github-script@v7 + with: + script: | + const state = '${{ steps.status.outputs.status }}' === 'success' ? 'success' : 'failure'; + const description = state === 'success' ? 'All checks passed' : 'Some checks failed'; - github.rest.repos.createCommitStatus({ - owner: context.repo.owner, - repo: context.repo.repo, - sha: context.sha, - state: state, - description: description, - context: 'CI Pipeline' - }); + github.rest.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: context.sha, + state: state, + description: description, + context: 'CI Pipeline' + }); diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e9a872a..cc539ac 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,18 +3,18 @@ name: Release on: push: tags: - - 'v*' + - "v*" release: types: [published] workflow_dispatch: inputs: tag: - description: 'Tag to release (e.g., v1.0.0)' + description: "Tag to release (e.g., v1.0.0)" required: true - default: 'v1.0.0' + default: "v1.0.0" env: - GO_VERSION: '1.23' + GO_VERSION_FILE: "go.mod" jobs: validate: @@ -25,52 +25,52 @@ jobs: is_prerelease: ${{ steps.version.outputs.is_prerelease }} steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 - - name: Validate tag format - id: version - run: | - if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then - TAG_NAME="${{ github.event.inputs.tag }}" - else - TAG_NAME="${GITHUB_REF#refs/tags/}" - fi + - name: Validate tag format + id: version + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + TAG_NAME="${{ github.event.inputs.tag }}" + else + TAG_NAME="${GITHUB_REF#refs/tags/}" + fi - echo "Tag name: $TAG_NAME" + echo "Tag name: $TAG_NAME" - # Validate semantic versioning format - if [[ ! "$TAG_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then - echo "❌ Invalid tag format: $TAG_NAME" - echo "Expected format: v1.2.3 or v1.2.3-beta.1" - exit 1 - fi + # Validate semantic versioning format + if [[ ! "$TAG_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then + echo "❌ Invalid tag format: $TAG_NAME" + echo "Expected format: v1.2.3 or v1.2.3-beta.1" + exit 1 + fi - # Extract version without 'v' prefix - VERSION=${TAG_NAME#v} - echo "version=$VERSION" >> $GITHUB_OUTPUT + # Extract version without 'v' prefix + VERSION=${TAG_NAME#v} + echo "version=$VERSION" >> $GITHUB_OUTPUT - # Check if prerelease (contains hyphen) - if [[ "$TAG_NAME" =~ - ]]; then - echo "is_prerelease=true" >> $GITHUB_OUTPUT - echo "📦 Prerelease detected: $TAG_NAME" - else - echo "is_prerelease=false" >> $GITHUB_OUTPUT - echo "🚀 Stable release detected: $TAG_NAME" - fi + # Check if prerelease (contains hyphen) + if [[ "$TAG_NAME" =~ - ]]; then + echo "is_prerelease=true" >> $GITHUB_OUTPUT + echo "📦 Prerelease detected: $TAG_NAME" + else + echo "is_prerelease=false" >> $GITHUB_OUTPUT + echo "🚀 Stable release detected: $TAG_NAME" + fi - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: ${{ env.GO_VERSION }} + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: ${{ env.GO_VERSION_FILE }} - - name: Run tests before release - run: | - echo "Running final tests before release..." - go test -v ./... - echo "✅ All tests passed" + - name: Run tests before release + run: | + echo "Running final tests before release..." + go test -v ./... + echo "✅ All tests passed" build: name: Build Release Binaries @@ -96,64 +96,64 @@ jobs: goarch: amd64 steps: - - name: Checkout code - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: ${{ env.GO_VERSION }} + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: ${{ env.GO_VERSION_FILE }} - - name: Cache Go modules - uses: actions/cache@v4 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }} + - name: Cache Go modules + uses: actions/cache@v4 + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.mod') }}-${{ hashFiles('**/go.sum') }} - - name: Build binary - env: - GOOS: ${{ matrix.goos }} - GOARCH: ${{ matrix.goarch }} - GOARM: ${{ matrix.goarm }} - CGO_ENABLED: 0 - run: | - # Determine output filename - BINARY_NAME="soundtouch-cli" - ARCH_SUFFIX="${{ matrix.goos }}-${{ matrix.goarch }}" + - name: Build binary + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + GOARM: ${{ matrix.goarm }} + CGO_ENABLED: 0 + run: | + # Determine output filename + BINARY_NAME="soundtouch-cli" + ARCH_SUFFIX="${{ matrix.goos }}-${{ matrix.goarch }}" - if [[ "${{ matrix.goarm }}" != "" ]]; then - ARCH_SUFFIX="${ARCH_SUFFIX}v${{ matrix.goarm }}" - fi + if [[ "${{ matrix.goarm }}" != "" ]]; then + ARCH_SUFFIX="${ARCH_SUFFIX}v${{ matrix.goarm }}" + fi - if [[ "${{ matrix.goos }}" == "windows" ]]; then - OUTPUT_NAME="${BINARY_NAME}-v${{ needs.validate.outputs.version }}-${ARCH_SUFFIX}.exe" - else - OUTPUT_NAME="${BINARY_NAME}-v${{ needs.validate.outputs.version }}-${ARCH_SUFFIX}" - fi + if [[ "${{ matrix.goos }}" == "windows" ]]; then + OUTPUT_NAME="${BINARY_NAME}-v${{ needs.validate.outputs.version }}-${ARCH_SUFFIX}.exe" + else + OUTPUT_NAME="${BINARY_NAME}-v${{ needs.validate.outputs.version }}-${ARCH_SUFFIX}" + fi - echo "Building: $OUTPUT_NAME" + echo "Building: $OUTPUT_NAME" - # Build with optimizations and version info - go build \ - -ldflags="-s -w -X main.version=v${{ needs.validate.outputs.version }} -X main.commit=${{ github.sha }} -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ - -o "$OUTPUT_NAME" \ - ./cmd/soundtouch-cli + # Build with optimizations and version info + go build \ + -ldflags="-s -w -X main.version=v${{ needs.validate.outputs.version }} -X main.commit=${{ github.sha }} -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ + -o "$OUTPUT_NAME" \ + ./cmd/soundtouch-cli - # Verify binary was created and is executable - ls -la "$OUTPUT_NAME" - file "$OUTPUT_NAME" + # Verify binary was created and is executable + ls -la "$OUTPUT_NAME" + file "$OUTPUT_NAME" - echo "binary_name=$OUTPUT_NAME" >> $GITHUB_OUTPUT - id: build + echo "binary_name=$OUTPUT_NAME" >> $GITHUB_OUTPUT + id: build - - name: Upload build artifact - uses: actions/upload-artifact@v4 - with: - name: ${{ steps.build.outputs.binary_name }} - path: ${{ steps.build.outputs.binary_name }} - retention-days: 1 + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.build.outputs.binary_name }} + path: ${{ steps.build.outputs.binary_name }} + retention-days: 1 checksums: name: Generate Checksums @@ -161,57 +161,57 @@ jobs: needs: [validate, build] steps: - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - path: ./binaries + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: ./binaries - - name: Generate checksums - run: | - cd binaries + - name: Generate checksums + run: | + cd binaries - # Flatten directory structure (artifacts are in subdirs) - find . -name "soundtouch-cli-*" -exec mv {} . \; + # Flatten directory structure (artifacts are in subdirs) + find . -name "soundtouch-cli-*" -exec mv {} . \; - # Remove empty directories - find . -type d -empty -delete + # Remove empty directories + find . -type d -empty -delete - # Generate SHA256 checksums - sha256sum soundtouch-cli-* > checksums.sha256 + # Generate SHA256 checksums + sha256sum soundtouch-cli-* > checksums.sha256 - # Generate SHA512 checksums - sha512sum soundtouch-cli-* > checksums.sha512 + # Generate SHA512 checksums + sha512sum soundtouch-cli-* > checksums.sha512 - echo "📋 Generated checksums:" - cat checksums.sha256 + echo "📋 Generated checksums:" + cat checksums.sha256 - # Verify all expected files are present - EXPECTED_COUNT=7 # Based on build matrix - ACTUAL_COUNT=$(ls soundtouch-cli-* | wc -l) + # Verify all expected files are present + EXPECTED_COUNT=7 # Based on build matrix + ACTUAL_COUNT=$(ls soundtouch-cli-* | wc -l) - if [[ $ACTUAL_COUNT -ne $EXPECTED_COUNT ]]; then - echo "❌ Expected $EXPECTED_COUNT binaries, found $ACTUAL_COUNT" - ls -la - exit 1 - fi + if [[ $ACTUAL_COUNT -ne $EXPECTED_COUNT ]]; then + echo "❌ Expected $EXPECTED_COUNT binaries, found $ACTUAL_COUNT" + ls -la + exit 1 + fi - echo "✅ All $ACTUAL_COUNT binaries present" + echo "✅ All $ACTUAL_COUNT binaries present" - - name: Upload checksums - uses: actions/upload-artifact@v4 - with: - name: checksums - path: | - binaries/checksums.sha256 - binaries/checksums.sha512 - retention-days: 1 + - name: Upload checksums + uses: actions/upload-artifact@v4 + with: + name: checksums + path: | + binaries/checksums.sha256 + binaries/checksums.sha512 + retention-days: 1 - - name: Upload all release assets - uses: actions/upload-artifact@v4 - with: - name: release-assets - path: binaries/ - retention-days: 1 + - name: Upload all release assets + uses: actions/upload-artifact@v4 + with: + name: release-assets + path: binaries/ + retention-days: 1 create_release: name: Create GitHub Release @@ -220,138 +220,138 @@ jobs: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 - - name: Download release assets - uses: actions/download-artifact@v4 - with: - name: release-assets - path: ./release-assets + - name: Download release assets + uses: actions/download-artifact@v4 + with: + name: release-assets + path: ./release-assets - - name: Generate release notes - id: release_notes - run: | - TAG_NAME="${GITHUB_REF#refs/tags/}" - VERSION="${TAG_NAME#v}" + - name: Generate release notes + id: release_notes + run: | + TAG_NAME="${GITHUB_REF#refs/tags/}" + VERSION="${TAG_NAME#v}" - # Generate comprehensive release notes - cat > release_notes.md << EOF - # Bose SoundTouch Go Library $TAG_NAME + # Generate comprehensive release notes + cat > release_notes.md << EOF + # Bose SoundTouch Go Library $TAG_NAME - A comprehensive Go library for controlling Bose SoundTouch speakers with 100% API coverage, real-time WebSocket events, and production-ready features. + A comprehensive Go library for controlling Bose SoundTouch speakers with 100% API coverage, real-time WebSocket events, and production-ready features. - ## 🎯 Key Features + ## 🎯 Key Features - - **100% API Coverage**: All 19 official endpoints + 6 useful extensions (25 total) - - **Real-time Events**: WebSocket support with auto-reconnect and comprehensive event handling - - **Multiroom Control**: Complete zone management and coordination - - **Production Ready**: Connection pooling, error handling, circuit breakers, monitoring - - **Excellent Documentation**: 4000+ lines including Getting Started, Cookbook, Troubleshooting, and Deployment guides - - **CLI Tool**: Full-featured command-line interface with all endpoints + - **100% API Coverage**: All 19 official endpoints + 6 useful extensions (25 total) + - **Real-time Events**: WebSocket support with auto-reconnect and comprehensive event handling + - **Multiroom Control**: Complete zone management and coordination + - **Production Ready**: Connection pooling, error handling, circuit breakers, monitoring + - **Excellent Documentation**: 4000+ lines including Getting Started, Cookbook, Troubleshooting, and Deployment guides + - **CLI Tool**: Full-featured command-line interface with all endpoints - ## 🚀 Quick Start + ## 🚀 Quick Start - \`\`\`bash - go get github.com/user_account/bose-soundtouch@$TAG_NAME - \`\`\` + \`\`\`bash + go get github.com/user_account/bose-soundtouch@$TAG_NAME + \`\`\` - \`\`\`go - package main + \`\`\`go + package main - import ( - "fmt" - "log" + import ( + "fmt" + "log" - "github.com/user_account/bose-soundtouch/pkg/client" - ) + "github.com/user_account/bose-soundtouch/pkg/client" + ) - func main() { - // Create client - c := client.New("192.168.1.100", 8090) + func main() { + // Create client + c := client.New("192.168.1.100", 8090) - // Get device info - info, err := c.GetInfo() - if err != nil { - log.Fatal(err) - } + // Get device info + info, err := c.GetInfo() + if err != nil { + log.Fatal(err) + } - fmt.Printf("Device: %s\\n", info.Name) - } - \`\`\` + fmt.Printf("Device: %s\\n", info.Name) + } + \`\`\` - ## 📚 Documentation + ## 📚 Documentation - - [Getting Started Guide](docs/GETTING-STARTED.md) - 10-minute tutorial from discovery to WebSocket monitoring - - [API Cookbook](docs/API-COOKBOOK.md) - 1000+ lines of real-world patterns and examples - - [Troubleshooting Guide](docs/TROUBLESHOOTING.md) - Systematic issue resolution - - [Deployment Guide](docs/DEPLOYMENT.md) - Production deployment examples (Docker, K8s, systemd) + - [Getting Started Guide](docs/GETTING-STARTED.md) - 10-minute tutorial from discovery to WebSocket monitoring + - [API Cookbook](docs/API-COOKBOOK.md) - 1000+ lines of real-world patterns and examples + - [Troubleshooting Guide](docs/TROUBLESHOOTING.md) - Systematic issue resolution + - [Deployment Guide](docs/DEPLOYMENT.md) - Production deployment examples (Docker, K8s, systemd) - ## 🔧 CLI Tool + ## 🔧 CLI Tool - Download the CLI tool for your platform from the assets below: + Download the CLI tool for your platform from the assets below: - \`\`\`bash - # Quick device discovery - ./soundtouch-cli -discover + \`\`\`bash + # Quick device discovery + ./soundtouch-cli -discover - # Get device information - ./soundtouch-cli -host 192.168.1.100 -info + # Get device information + ./soundtouch-cli -host 192.168.1.100 -info - # Monitor real-time events - ./soundtouch-cli -host 192.168.1.100 -nowplaying - \`\`\` + # Monitor real-time events + ./soundtouch-cli -host 192.168.1.100 -nowplaying + \`\`\` - ## 🧪 Tested Hardware + ## 🧪 Tested Hardware - - Bose SoundTouch 10 - - Bose SoundTouch 20 - - All core functionality validated on real devices + - Bose SoundTouch 10 + - Bose SoundTouch 20 + - All core functionality validated on real devices - ## 📈 What's New in $TAG_NAME + ## 📈 What's New in $TAG_NAME - $(git log --pretty=format:"- %s" $(git describe --tags --abbrev=0 HEAD^)..HEAD 2>/dev/null || echo "- Initial release with complete feature set") + $(git log --pretty=format:"- %s" $(git describe --tags --abbrev=0 HEAD^)..HEAD 2>/dev/null || echo "- Initial release with complete feature set") - ## 🏗️ Supported Platforms + ## 🏗️ Supported Platforms - This release includes pre-built binaries for: - - Linux (amd64, arm64, armv7) - - macOS (Intel & Apple Silicon) - - Windows (amd64) - - FreeBSD (amd64) + This release includes pre-built binaries for: + - Linux (amd64, arm64, armv7) + - macOS (Intel & Apple Silicon) + - Windows (amd64) + - FreeBSD (amd64) - ## 🔐 Checksums + ## 🔐 Checksums - SHA256 checksums are provided in \`checksums.sha256\` to verify download integrity. + SHA256 checksums are provided in \`checksums.sha256\` to verify download integrity. - ## 🤝 Contributing + ## 🤝 Contributing - Contributions welcome! See our documentation for examples and patterns. + Contributions welcome! See our documentation for examples and patterns. - ## 📄 License + ## 📄 License - MIT License - see [LICENSE](LICENSE) file. - EOF + MIT License - see [LICENSE](LICENSE) file. + EOF - echo "release_notes_file=release_notes.md" >> $GITHUB_OUTPUT + echo "release_notes_file=release_notes.md" >> $GITHUB_OUTPUT - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - tag_name: ${{ github.ref_name }} - name: "Bose SoundTouch Go Library ${{ github.ref_name }}" - body_path: ${{ steps.release_notes.outputs.release_notes_file }} - draft: false - prerelease: ${{ needs.validate.outputs.is_prerelease == 'true' }} - files: | - release-assets/soundtouch-cli-* - release-assets/checksums.sha256 - release-assets/checksums.sha512 - fail_on_unmatched_files: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.ref_name }} + name: "Bose SoundTouch Go Library ${{ github.ref_name }}" + body_path: ${{ steps.release_notes.outputs.release_notes_file }} + draft: false + prerelease: ${{ needs.validate.outputs.is_prerelease == 'true' }} + files: | + release-assets/soundtouch-cli-* + release-assets/checksums.sha256 + release-assets/checksums.sha512 + fail_on_unmatched_files: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} update_release: name: Update Existing Release @@ -360,23 +360,23 @@ jobs: if: github.event_name == 'release' && github.event.action == 'published' steps: - - name: Download release assets - uses: actions/download-artifact@v4 - with: - name: release-assets - path: ./release-assets + - name: Download release assets + uses: actions/download-artifact@v4 + with: + name: release-assets + path: ./release-assets - - name: Upload additional assets to existing release - uses: softprops/action-gh-release@v2 - with: - tag_name: ${{ github.event.release.tag_name }} - files: | - release-assets/soundtouch-cli-* - release-assets/checksums.sha256 - release-assets/checksums.sha512 - fail_on_unmatched_files: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload additional assets to existing release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.event.release.tag_name }} + files: | + release-assets/soundtouch-cli-* + release-assets/checksums.sha256 + release-assets/checksums.sha512 + fail_on_unmatched_files: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} notify: name: Post-Release Notifications @@ -385,16 +385,16 @@ jobs: if: always() && (needs.create_release.result == 'success' || needs.update_release.result == 'success') steps: - - name: Notify success - run: | - echo "🎉 Release ${{ needs.validate.outputs.version }} completed successfully!" - echo "📦 Binaries built for 7 platforms" - echo "🔐 Checksums generated and verified" - echo "📋 Release notes automatically generated" - echo "" - echo "🔗 Release URL: https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}" - echo "" - echo "Next steps:" - echo "- Monitor download metrics" - echo "- Update documentation if needed" - echo "- Announce to community (see scripts/post-release.md)" + - name: Notify success + run: | + echo "🎉 Release ${{ needs.validate.outputs.version }} completed successfully!" + echo "📦 Binaries built for 7 platforms" + echo "🔐 Checksums generated and verified" + echo "📋 Release notes automatically generated" + echo "" + echo "🔗 Release URL: https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}" + echo "" + echo "Next steps:" + echo "- Monitor download metrics" + echo "- Update documentation if needed" + echo "- Announce to community (see scripts/post-release.md)"