From afa9529afd30f86e343c47b2e1ddd239382b65b6 Mon Sep 17 00:00:00 2001 From: Philip Peterson <1326208+philip-peterson@users.noreply.github.com> Date: Tue, 25 Aug 2026 14:15:35 -0800 Subject: [PATCH] Add Dagger module, thin CI workflows, and Gitea Actions --- .dagger/main.go | 79 ++++++++++++++++ .gitea/workflows/ci.yml | 24 +++++ .gitea/workflows/desktop.yml | 174 +++++++++++++++++++++++++++++++++++ .github/workflows/ci.yml | 24 +++++ .gitignore | 2 + dagger.json | 8 ++ 6 files changed, 311 insertions(+) create mode 100644 .dagger/main.go create mode 100644 .gitea/workflows/ci.yml create mode 100644 .gitea/workflows/desktop.yml create mode 100644 .github/workflows/ci.yml create mode 100644 dagger.json diff --git a/.dagger/main.go b/.dagger/main.go new file mode 100644 index 00000000..0096d76e --- /dev/null +++ b/.dagger/main.go @@ -0,0 +1,79 @@ +package main + +import ( + "context" + + "dagger/quintodrome/internal/dagger" +) + +type Quintodrome struct{} + +// Ci runs the full Linux CI: Go build + test and JS build + test. +func (m *Quintodrome) Ci(ctx context.Context, + // +defaultPath="." + src *dagger.Directory, +) error { + if err := m.BuildGo(ctx, src); err != nil { + return err + } + if err := m.TestGo(ctx, src); err != nil { + return err + } + if err := m.BuildJS(ctx, src); err != nil { + return err + } + if err := m.TestJS(ctx, src); err != nil { + return err + } + return nil +} + +// BuildGo compiles the Navidrome server and all packages. +func (m *Quintodrome) BuildGo(ctx context.Context, src *dagger.Directory) error { + _, err := goContainer(). + WithDirectory("/repo", src). + WithWorkdir("/repo"). + WithExec([]string{"go", "build", "-tags", "netgo,sqlite_fts5", "./..."}). + Sync(ctx) + return err +} + +// TestGo runs the Go test suite. +func (m *Quintodrome) TestGo(ctx context.Context, src *dagger.Directory) error { + _, err := goContainer(). + WithDirectory("/repo", src). + WithWorkdir("/repo"). + WithExec([]string{"go", "test", "-tags", "netgo,sqlite_fts5", "./..."}). + Sync(ctx) + return err +} + +// BuildJS builds the Navidrome web UI. +func (m *Quintodrome) BuildJS(ctx context.Context, src *dagger.Directory) error { + _, err := jsContainer(). + WithDirectory("/repo", src). + WithWorkdir("/repo/ui"). + WithExec([]string{"npm", "ci", "--ignore-scripts"}). + WithExec([]string{"npm", "run", "build"}). + Sync(ctx) + return err +} + +// TestJS runs the JS test suite. +func (m *Quintodrome) TestJS(ctx context.Context, src *dagger.Directory) error { + _, err := jsContainer(). + WithDirectory("/repo", src). + WithWorkdir("/repo/ui"). + WithExec([]string{"npm", "ci", "--ignore-scripts"}). + WithExec([]string{"npm", "test"}). + Sync(ctx) + return err +} + +func goContainer() *dagger.Container { + return dag.Container().From("golang:1.26") +} + +func jsContainer() *dagger.Container { + return dag.Container().From("node:24") +} diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 00000000..181d1e53 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,24 @@ +name: CI + +on: + push: + branches: [master] + tags: ["v*"] + pull_request: + branches: [master] + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + ci: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Install Dagger CLI + run: curl -fsSL https://dl.dagger.io/dagger/install.sh | sh + + - name: Run CI (Dagger) + run: dagger call ci diff --git a/.gitea/workflows/desktop.yml b/.gitea/workflows/desktop.yml new file mode 100644 index 00000000..db6c30a1 --- /dev/null +++ b/.gitea/workflows/desktop.yml @@ -0,0 +1,174 @@ +name: Desktop Release + +# Builds the Quintodrome desktop app (Tauri) for macOS (.app/.dmg) and +# Windows (.exe/.msi), bundling the Navidrome server as a sidecar, and +# attaches the artifacts to the Gitea release on `v*` tags. +# +# Note: artifacts are unsigned (no Apple/Windows code-signing certs), so users +# will hit Gatekeeper/SmartScreen prompts. + +on: + push: + tags: ["v*"] + workflow_dispatch: + +concurrency: + group: desktop-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-desktop: + name: Build desktop (${{ matrix.target }}) + strategy: + fail-fast: false + matrix: + include: + - os: macos-latest + target: aarch64-apple-darwin + - os: macos-13 # Intel runner + target: x86_64-apple-darwin + - os: windows-latest + target: x86_64-pc-windows-msvc + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v6 + + - name: Setup Node + uses: actions/setup-node@v6 + with: + node-version: 24 + cache: npm + cache-dependency-path: ui/package-lock.json + + - name: Setup Go + uses: actions/setup-go@v6 + with: + go-version-file: go.mod + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache Rust build + uses: Swatinem/rust-cache@v2 + with: + workspaces: desktop/src-tauri + + # Navidrome's sqlite_fts5 tag needs CGO, so on Windows we need a gcc. + - name: Setup Windows CGO (mingw) + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + install: mingw-w64-x86_64-gcc + update: false + + - name: Add mingw to PATH + if: runner.os == 'Windows' + shell: bash + run: echo "C:/msys64/mingw64/bin" >> $GITHUB_PATH + + - name: Build Navidrome web UI + shell: bash + run: | + cd ui + # --ignore-scripts skips the workbox postinstall (bin/update-workbox.sh), + # a bash script that fails under cmd.exe on Windows. It's only needed + # for the PWA service worker, which the desktop app doesn't use. + npm ci --ignore-scripts + npm run build + + - name: Build Navidrome server binary + shell: bash + run: | + GIT_SHA=$(git rev-parse --short HEAD) + if [[ "$GITHUB_REF" == refs/tags/* ]]; then + GIT_TAG="${GITHUB_REF_NAME#v}" + else + GIT_TAG="dev" + fi + + if [[ "$RUNNER_OS" == "Windows" ]]; then + export CGO_ENABLED=1 + OUTPUT="navidrome.exe" + else + OUTPUT="navidrome" + fi + + go build \ + -ldflags="-X github.com/navidrome/navidrome/consts.gitSha=$GIT_SHA -X github.com/navidrome/navidrome/consts.gitTag=$GIT_TAG" \ + -tags=netgo,sqlite_fts5 \ + -o "$OUTPUT" + + # Stage the sidecar under the target-triple name tauri expects. + mkdir -p desktop/src-tauri/binaries + if [[ "$RUNNER_OS" == "Windows" ]]; then + cp navidrome.exe "desktop/src-tauri/binaries/navidrome-${{ matrix.target }}.exe" + else + cp navidrome "desktop/src-tauri/binaries/navidrome-${{ matrix.target }}" + chmod +x "desktop/src-tauri/binaries/navidrome-${{ matrix.target }}" + fi + + - name: Build Tauri app + shell: bash + run: | + cd desktop + npx --yes @tauri-apps/cli@2 build + + - name: Collect artifacts (macOS) + if: runner.os == 'macOS' + shell: bash + run: | + mkdir -p dist + BUNDLE=desktop/src-tauri/target/release/bundle + ditto -c -k --keepParent "$BUNDLE/macos/Quintodrome.app" "dist/Quintodrome-${{ matrix.target }}.app.zip" + cp "$BUNDLE"/dmg/*.dmg dist/ 2>/dev/null || true + ls -la dist + + - name: Collect artifacts (Windows) + if: runner.os == 'Windows' + shell: bash + run: | + mkdir -p dist + BUNDLE=desktop/src-tauri/target/release/bundle + cp "$BUNDLE"/nsis/*.exe dist/ 2>/dev/null || true + cp "$BUNDLE"/msi/*.msi dist/ 2>/dev/null || true + ls -la dist + + - name: Upload artifacts + uses: actions/upload-artifact@v7 + with: + name: desktop-${{ matrix.target }} + path: dist/* + retention-days: 7 + + release: + name: Publish Gitea release + needs: build-desktop + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@v8 + with: + path: dist + pattern: desktop-* + merge-multiple: true + + - run: ls -R dist + + - name: Create release and upload assets + env: + TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + TAG="${GITHUB_REF_NAME#v}" + RELEASE_ID=$(curl -sS -X POST "$GITHUB_SERVER_URL/api/v1/repos/$GITHUB_REPOSITORY/releases" \ + -H "Authorization: token $TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"tag_name\":\"$TAG\",\"name\":\"$TAG\",\"draft\":false}" \ + | jq -r '.id') + for f in dist/*; do + curl -sS -X POST "$GITHUB_SERVER_URL/api/v1/repos/$GITHUB_REPOSITORY/releases/$RELEASE_ID/assets?name=$(basename "$f")" \ + -H "Authorization: token $TOKEN" \ + -H "Content-Type: application/octet-stream" \ + --data-binary @"$f" + done diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..181d1e53 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,24 @@ +name: CI + +on: + push: + branches: [master] + tags: ["v*"] + pull_request: + branches: [master] + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + ci: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Install Dagger CLI + run: curl -fsSL https://dl.dagger.io/dagger/install.sh | sh + + - name: Run CI (Dagger) + run: dagger call ci diff --git a/.gitignore b/.gitignore index fc8eaac6..faa0df96 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,5 @@ openspec/ .agents go.work* .worktrees/ +.dagger/internal/ +.dagger/querybuilder/ diff --git a/dagger.json b/dagger.json new file mode 100644 index 00000000..88f9e6b6 --- /dev/null +++ b/dagger.json @@ -0,0 +1,8 @@ +{ + "name": "quintodrome", + "engineVersion": "v0.20.6", + "sdk": { + "source": "go" + }, + "source": ".dagger" +}