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
This commit is contained in:
Tobias Gesellchen
2026-01-09 12:58:35 +01:00
parent b6adf51767
commit 3faafb3635
2 changed files with 424 additions and 428 deletions
+167 -171
View File
@@ -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'
});
+257 -257
View File
@@ -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)"