mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 08:36:13 +00:00
Add Docker support and CI/CD integration for SoundTouch service (#19)
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
# .dockerignore
|
||||
|
||||
# Exclude large firmware files and archives
|
||||
firmware/
|
||||
data/
|
||||
|
||||
# Exclude local build artifacts
|
||||
build/
|
||||
soundtouch-cli
|
||||
soundtouch-service
|
||||
|
||||
# Exclude Go specific files that aren't needed for build context
|
||||
# (go.mod and go.sum ARE needed, but other local stuff isn't)
|
||||
.cache/
|
||||
vendor/
|
||||
|
||||
# Exclude IDE and system files
|
||||
.idea/
|
||||
.vscode/
|
||||
.DS_Store
|
||||
|
||||
# Exclude Git history
|
||||
.git/
|
||||
.gitignore
|
||||
|
||||
# Exclude documentation and other non-essential files for the binary build
|
||||
docs/
|
||||
examples/
|
||||
scripts/
|
||||
CONTRIBUTING.md
|
||||
CODE_OF_CONDUCT.md
|
||||
LICENSE
|
||||
README.md
|
||||
@@ -77,3 +77,24 @@ updates:
|
||||
- "*scan*"
|
||||
- "securecodewarrior/*"
|
||||
- "codecov/*"
|
||||
|
||||
# Docker dependency updates
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "wednesday"
|
||||
time: "09:00"
|
||||
timezone: "UTC"
|
||||
open-pull-requests-limit: 3
|
||||
reviewers:
|
||||
- "gesellix"
|
||||
assignees:
|
||||
- "gesellix"
|
||||
commit-message:
|
||||
prefix: "docker"
|
||||
include: "scope"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- "docker"
|
||||
rebase-strategy: "auto"
|
||||
|
||||
@@ -218,10 +218,51 @@ jobs:
|
||||
go run test_import.go
|
||||
rm test_import.go
|
||||
|
||||
docker:
|
||||
name: Docker Build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
tags: |
|
||||
type=raw,value=edge,enable=${{ github.ref == 'refs/heads/main' }}
|
||||
type=ref,event=pr
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
notify:
|
||||
name: Notify Status
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test, lint, build, security, docs]
|
||||
needs: [test, lint, build, security, docs, docker]
|
||||
if: always()
|
||||
permissions:
|
||||
statuses: write
|
||||
@@ -234,7 +275,8 @@ jobs:
|
||||
"${{ needs.lint.result }}" == "success" && \
|
||||
"${{ needs.build.result }}" == "success" && \
|
||||
"${{ needs.security.result }}" == "success" && \
|
||||
"${{ needs.docs.result }}" == "success" ]]; then
|
||||
"${{ needs.docs.result }}" == "success" && \
|
||||
"${{ needs.docker.result }}" == "success" ]]; then
|
||||
echo "✅ All CI checks passed!"
|
||||
echo "status=success" >> $GITHUB_OUTPUT
|
||||
else
|
||||
@@ -244,6 +286,7 @@ jobs:
|
||||
echo "Build: ${{ needs.build.result }}"
|
||||
echo "Security: ${{ needs.security.result }}"
|
||||
echo "Docs: ${{ needs.docs.result }}"
|
||||
echo "Docker: ${{ needs.docker.result }}"
|
||||
echo "status=failure" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
id: status
|
||||
|
||||
@@ -13,6 +13,7 @@ on:
|
||||
permissions:
|
||||
contents: write
|
||||
actions: read
|
||||
packages: write
|
||||
|
||||
env:
|
||||
GO_VERSION_FILE: "go.mod"
|
||||
@@ -477,17 +478,57 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
docker:
|
||||
name: Build and Push Docker Image
|
||||
runs-on: ubuntu-latest
|
||||
needs: validate
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
tags: |
|
||||
type=semver,pattern={{version}},value=v${{ needs.validate.outputs.version }}
|
||||
type=semver,pattern={{major}}.{{minor}},value=v${{ needs.validate.outputs.version }}
|
||||
type=raw,value=latest,enable=${{ needs.validate.outputs.is_prerelease == 'false' }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
notify:
|
||||
name: Post-Release Notifications
|
||||
runs-on: ubuntu-latest
|
||||
needs: [validate, create_release, update_release]
|
||||
if: always() && (needs.create_release.result == 'success' || needs.update_release.result == 'success')
|
||||
needs: [validate, create_release, update_release, docker]
|
||||
if: always() && (needs.create_release.result == 'success' || needs.update_release.result == 'success' || needs.docker.result == 'success')
|
||||
|
||||
steps:
|
||||
- name: Notify success
|
||||
run: |
|
||||
echo "🎉 Release ${{ needs.validate.outputs.version }} completed successfully!"
|
||||
echo "📦 Binaries built for 7 platforms (CLI and Service)"
|
||||
echo "🐳 Docker image published to ghcr.io"
|
||||
echo "🔐 Checksums generated and verified"
|
||||
echo "📋 Release notes automatically generated"
|
||||
echo ""
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
# Build stage
|
||||
FROM golang:1.25.7-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy go mod and sum files
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Copy the rest of the source code
|
||||
COPY . .
|
||||
|
||||
# Build the soundtouch-service
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -o /soundtouch-service ./cmd/soundtouch-service
|
||||
|
||||
# Final stage
|
||||
FROM alpine:3.21
|
||||
|
||||
# Install necessary runtime dependencies
|
||||
RUN apk add --no-cache ca-certificates tzdata
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the binary from the builder stage
|
||||
COPY --from=builder /soundtouch-service /app/soundtouch-service
|
||||
|
||||
# Create data directory for persistence
|
||||
RUN mkdir -p /app/data
|
||||
|
||||
# Set environment variables with defaults
|
||||
ENV PORT=8000
|
||||
ENV DATA_DIR=/app/data
|
||||
ENV LOG_PROXY_BODY=false
|
||||
ENV REDACT_PROXY_LOGS=true
|
||||
|
||||
# Expose the service port
|
||||
EXPOSE 8000
|
||||
|
||||
# Run the service
|
||||
ENTRYPOINT ["/app/soundtouch-service"]
|
||||
@@ -12,6 +12,8 @@ GOFMT=gofmt
|
||||
# Build parameters
|
||||
BINARY_NAME=soundtouch-cli
|
||||
BINARY_PATH=./cmd/$(BINARY_NAME)
|
||||
SERVICE_NAME=soundtouch-service
|
||||
SERVICE_PATH=./cmd/$(SERVICE_NAME)
|
||||
EXAMPLE_MDNS_NAME=example-mdns
|
||||
EXAMPLE_MDNS_PATH=./cmd/$(EXAMPLE_MDNS_NAME)
|
||||
EXAMPLE_UPNP_NAME=example-upnp
|
||||
@@ -25,13 +27,18 @@ BUILD_DIR=./build
|
||||
|
||||
all: check build
|
||||
|
||||
build: build-cli build-examples
|
||||
build: build-cli build-service build-examples
|
||||
|
||||
build-cli:
|
||||
@echo "Building $(BINARY_NAME)..."
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
$(GOBUILD) -o $(BUILD_DIR)/$(BINARY_NAME) $(BINARY_PATH)
|
||||
|
||||
build-service:
|
||||
@echo "Building $(SERVICE_NAME)..."
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
$(GOBUILD) -o $(BUILD_DIR)/$(SERVICE_NAME) $(SERVICE_PATH)
|
||||
|
||||
build-examples:
|
||||
@echo "Building $(EXAMPLE_MDNS_NAME)..."
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
@@ -47,17 +54,21 @@ build-linux:
|
||||
@echo "Building for Linux..."
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64 $(BINARY_PATH)
|
||||
GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(BUILD_DIR)/$(SERVICE_NAME)-linux-amd64 $(SERVICE_PATH)
|
||||
|
||||
build-darwin:
|
||||
@echo "Building for macOS..."
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
GOOS=darwin GOARCH=amd64 $(GOBUILD) -o $(BUILD_DIR)/$(BINARY_NAME)-darwin-amd64 $(BINARY_PATH)
|
||||
GOOS=darwin GOARCH=arm64 $(GOBUILD) -o $(BUILD_DIR)/$(BINARY_NAME)-darwin-arm64 $(BINARY_PATH)
|
||||
GOOS=darwin GOARCH=amd64 $(GOBUILD) -o $(BUILD_DIR)/$(SERVICE_NAME)-darwin-amd64 $(SERVICE_PATH)
|
||||
GOOS=darwin GOARCH=arm64 $(GOBUILD) -o $(BUILD_DIR)/$(SERVICE_NAME)-darwin-arm64 $(SERVICE_PATH)
|
||||
|
||||
build-windows:
|
||||
@echo "Building for Windows..."
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
GOOS=windows GOARCH=amd64 $(GOBUILD) -o $(BUILD_DIR)/$(BINARY_NAME)-windows-amd64.exe $(BINARY_PATH)
|
||||
GOOS=windows GOARCH=amd64 $(GOBUILD) -o $(BUILD_DIR)/$(SERVICE_NAME)-windows-amd64.exe $(SERVICE_PATH)
|
||||
|
||||
build-examples-all:
|
||||
@echo "Building examples for all platforms..."
|
||||
@@ -108,6 +119,18 @@ dev: build-cli
|
||||
@echo "Starting development CLI..."
|
||||
$(BUILD_DIR)/$(BINARY_NAME) -help
|
||||
|
||||
dev-service: build-service
|
||||
@echo "Starting development service..."
|
||||
$(BUILD_DIR)/$(SERVICE_NAME)
|
||||
|
||||
dev-service-proxy: build-service
|
||||
@echo "Starting development service with proxy..."
|
||||
@if [ -z "$(PROXY_URL)" ]; then \
|
||||
echo "Usage: make dev-service-proxy PROXY_URL=http://localhost:8001"; \
|
||||
exit 1; \
|
||||
fi
|
||||
PYTHON_BACKEND_URL=$(PROXY_URL) $(BUILD_DIR)/$(SERVICE_NAME)
|
||||
|
||||
dev-discover: build-cli
|
||||
@echo "Running device discovery..."
|
||||
$(BUILD_DIR)/$(BINARY_NAME) -discover
|
||||
@@ -164,9 +187,10 @@ dev-scan-http: build-examples
|
||||
@echo "Scanning for HTTP mDNS services..."
|
||||
$(BUILD_DIR)/$(SCANNER_NAME) -service _http._tcp -v
|
||||
|
||||
install: build-cli
|
||||
@echo "Installing $(BINARY_NAME) to $(GOPATH)/bin..."
|
||||
install: build-cli build-service
|
||||
@echo "Installing binaries to $(GOPATH)/bin..."
|
||||
cp $(BUILD_DIR)/$(BINARY_NAME) $(GOPATH)/bin/
|
||||
cp $(BUILD_DIR)/$(SERVICE_NAME) $(GOPATH)/bin/
|
||||
|
||||
clean:
|
||||
@echo "Cleaning..."
|
||||
@@ -177,7 +201,7 @@ clean:
|
||||
release: clean check build-all
|
||||
@echo "Creating release archive..."
|
||||
@mkdir -p $(BUILD_DIR)/release
|
||||
@for binary in $(BUILD_DIR)/$(BINARY_NAME)-*; do \
|
||||
@for binary in $(BUILD_DIR)/$(BINARY_NAME)-* $(BUILD_DIR)/$(SERVICE_NAME)-*; do \
|
||||
if [ -f "$$binary" ]; then \
|
||||
cp "$$binary" $(BUILD_DIR)/release/; \
|
||||
fi \
|
||||
@@ -186,16 +210,22 @@ release: clean check build-all
|
||||
|
||||
docker-build:
|
||||
@echo "Building Docker image..."
|
||||
docker build -t soundtouch-go:$(VERSION) .
|
||||
docker build -t soundtouch-service .
|
||||
|
||||
docker-dev: docker-build
|
||||
@echo "Running development container..."
|
||||
docker run --rm -it --network host soundtouch-go:$(VERSION)
|
||||
docker-run-host:
|
||||
@echo "Running Docker container..."
|
||||
@echo "Note: --network host is used for discovery (Linux only). For macOS/Windows use port mapping."
|
||||
docker run --rm -it --network host -v $$(pwd)/data:/app/data soundtouch-service
|
||||
|
||||
docker-run-ports:
|
||||
@echo "Running Docker container with port mapping (discovery will be manual)..."
|
||||
docker run --rm -it -p 8000:8000 -v $$(pwd)/data:/app/data soundtouch-service
|
||||
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@echo " build - Build the CLI tool and examples"
|
||||
@echo " build - Build the CLI tool, service, and examples"
|
||||
@echo " build-cli - Build only the CLI tool"
|
||||
@echo " build-service - Build only the service"
|
||||
@echo " build-examples - Build only the example programs"
|
||||
@echo " build-all - Build for all platforms"
|
||||
@echo " test - Run tests"
|
||||
@@ -206,6 +236,8 @@ help:
|
||||
@echo " lint - Run golangci-lint"
|
||||
@echo " tidy - Tidy dependencies"
|
||||
@echo " dev - Build and show CLI help"
|
||||
@echo " dev-service - Build and run service locally"
|
||||
@echo " dev-service-proxy - Build and run service with proxy (PROXY_URL=url required)"
|
||||
@echo " dev-discover - Build and run device discovery"
|
||||
@echo " dev-info - Build and get device info (HOST=ip required)"
|
||||
@echo " dev-mdns - Build and run mDNS discovery example"
|
||||
@@ -217,14 +249,17 @@ help:
|
||||
@echo " dev-scan-all - Scan all mDNS services on network"
|
||||
@echo " dev-scan-soundtouch - Scan specifically for SoundTouch mDNS services"
|
||||
@echo " dev-scan-http - Scan for HTTP mDNS services"
|
||||
@echo " install - Install binary to GOPATH/bin"
|
||||
@echo " install - Install binaries to GOPATH/bin"
|
||||
@echo " clean - Clean build artifacts"
|
||||
@echo " release - Create release binaries"
|
||||
@echo " docker-build - Build Docker image"
|
||||
@echo " docker-dev - Run development container"
|
||||
@echo " docker-run-host - Run container with host networking (Linux discovery)"
|
||||
@echo " docker-run-ports - Run container with port mapping (macOS/Windows/No discovery)"
|
||||
@echo " help - Show this help message"
|
||||
@echo ""
|
||||
@echo "Examples:"
|
||||
@echo " make dev-service"
|
||||
@echo " make dev-service-proxy PROXY_URL=http://192.168.1.50:8001"
|
||||
@echo " make dev-discover"
|
||||
@echo " make dev-info HOST=192.168.1.10"
|
||||
@echo " make dev-mdns"
|
||||
|
||||
@@ -97,13 +97,69 @@ The `soundtouch-service` is a local server that emulates Bose's cloud services,
|
||||
# Install the service
|
||||
go install github.com/gesellix/bose-soundtouch/cmd/soundtouch-service@latest
|
||||
|
||||
# Start with default settings (http://localhost:8000)
|
||||
# Start with default settings (http://localhost:8000, proxying to http://localhost:8001)
|
||||
soundtouch-service
|
||||
|
||||
# Or configure with environment variables
|
||||
PORT=9000 DATA_DIR=/my/data soundtouch-service
|
||||
PORT=9000 PYTHON_BACKEND_URL=http://your-python-backend:8001 DATA_DIR=/my/data soundtouch-service
|
||||
```
|
||||
|
||||
#### Running with Docker
|
||||
|
||||
You can also run the SoundTouch service using Docker or Docker Compose.
|
||||
|
||||
> **Note for macOS and Windows users**: The `--net host` option is only supported on Linux. On macOS and Windows, service discovery (mDNS, UPnP) will not work automatically within the container. You will need to manually enter your device's IP address in the management UI, and the service will communicate with it directly.
|
||||
|
||||
##### Using Docker
|
||||
|
||||
**Linux (with host networking for discovery):**
|
||||
```bash
|
||||
docker run -d \
|
||||
--name soundtouch-service \
|
||||
--network host \
|
||||
-v $(pwd)/data:/app/data \
|
||||
ghcr.io/gesellix/bose-soundtouch:latest
|
||||
```
|
||||
|
||||
**macOS / Windows (with port mapping):**
|
||||
```bash
|
||||
docker run -d \
|
||||
--name soundtouch-service \
|
||||
-p 8000:8000 \
|
||||
-v $(pwd)/data:/app/data \
|
||||
ghcr.io/gesellix/bose-soundtouch:latest
|
||||
```
|
||||
|
||||
##### Using Docker Compose
|
||||
|
||||
Create a `docker-compose.yml` file:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
soundtouch-service:
|
||||
image: ghcr.io/gesellix/bose-soundtouch:latest
|
||||
container_name: soundtouch-service
|
||||
# Linux users: use host networking for device discovery
|
||||
# network_mode: host
|
||||
# macOS/Windows users: use port mapping (discovery will be manual)
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
- PORT=8000
|
||||
- DATA_DIR=/app/data
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
And run:
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
> **Note**: `--network host` is required for device discovery via UPnP and mDNS to work correctly within the container.
|
||||
|
||||
#### Device Migration Example
|
||||
|
||||
```bash
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
services:
|
||||
soundtouch-service:
|
||||
build: .
|
||||
container_name: soundtouch-service
|
||||
# network_mode: host # Linux only, required for discovery
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
- PORT=8000
|
||||
- DATA_DIR=/app/data
|
||||
- LOG_PROXY_BODY=false
|
||||
- REDACT_PROXY_LOGS=true
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
restart: unless-stopped
|
||||
Reference in New Issue
Block a user