diff --git a/.forgejo/workflows/android-emulator-tests.yml b/.forgejo/workflows/android-emulator-tests.yml index 29a356e..6804514 100644 --- a/.forgejo/workflows/android-emulator-tests.yml +++ b/.forgejo/workflows/android-emulator-tests.yml @@ -17,15 +17,6 @@ jobs: with: fetch-depth: 50 - - uses: cachix/install-nix-action@v27 - with: - nix_path: nixpkgs=channel:nixos-25.11 - - - name: Enable Nix flakes - run: | - mkdir -p ~/.config/nix - echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf - - name: Install Android SDK run: | SDK="${ANDROID_HOME:-$HOME/Android/Sdk}" @@ -40,4 +31,4 @@ jobs: fi - name: Run Android Integration Tests - run: nix develop --no-warn-dirty --command task integration-android + run: task integration-android diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 92bd77b..80d2dc6 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -16,14 +16,11 @@ jobs: with: fetch-depth: 50 - - uses: cachix/install-nix-action@v27 - with: - nix_path: nixpkgs=channel:nixos-25.11 - - - name: Enable Nix flakes + - name: Install Dagger & Task run: | - mkdir -p ~/.config/nix - echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf + curl -L https://dl.dagger.io/dagger/install.sh | BIN_DIR=/usr/local/bin sh + curl -sL https://taskfile.dev/install.sh | sh -s -- -b /usr/local/bin + sudo apt-get update && sudo apt-get install -y stunnel4 netcat-openbsd - name: Setup Dagger Remote Engine (via stunnel) env: @@ -32,19 +29,10 @@ jobs: DAGGER_CA_CERT: ${{ secrets.DAGGER_CA_CERT }} DAGGER_CLIENT_CERT: ${{ secrets.DAGGER_CLIENT_CERT }} DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} - run: nix develop --no-warn-dirty --command scripts/setup_dagger_remote.sh - - - name: Setup Dagger Remote Engine (via stunnel) - env: - DAGGER_STUNNEL_URL1: ${{ secrets.DAGGER_STUNNEL_URL1 }} - DAGGER_STUNNEL_URL2: ${{ secrets.DAGGER_STUNNEL_URL2 }} - DAGGER_CA_CERT: ${{ secrets.DAGGER_CA_CERT }} - DAGGER_CLIENT_CERT: ${{ secrets.DAGGER_CLIENT_CERT }} - DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} - run: nix develop --no-warn-dirty --command scripts/setup_dagger_remote.sh + run: scripts/setup_dagger_remote.sh - name: Run Full Check Suite - run: nix develop --no-warn-dirty --command dagger call --progress=plain -m ci check --source . + run: dagger call --progress=plain -m ci check --source . build-linux: name: Build Linux Release @@ -57,14 +45,11 @@ jobs: with: fetch-depth: 50 - - uses: cachix/install-nix-action@v27 - with: - nix_path: nixpkgs=channel:nixos-25.11 - - - name: Enable Nix flakes + - name: Install Dagger & Task run: | - mkdir -p ~/.config/nix - echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf + curl -L https://dl.dagger.io/dagger/install.sh | BIN_DIR=/usr/local/bin sh + curl -sL https://taskfile.dev/install.sh | sh -s -- -b /usr/local/bin + sudo apt-get update && sudo apt-get install -y stunnel4 netcat-openbsd - name: Setup Dagger Remote Engine (via stunnel) env: @@ -73,7 +58,7 @@ jobs: DAGGER_CA_CERT: ${{ secrets.DAGGER_CA_CERT }} DAGGER_CLIENT_CERT: ${{ secrets.DAGGER_CLIENT_CERT }} DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} - run: nix develop --no-warn-dirty --command scripts/setup_dagger_remote.sh + run: scripts/setup_dagger_remote.sh - name: Build & Deploy Linux to server continue-on-error: true @@ -83,7 +68,7 @@ jobs: SSH_HOST: ${{ secrets.SSH_HOST }} run: | HASH=$(git rev-parse --short HEAD) - nix develop --no-warn-dirty --command dagger call --progress=plain -m ci deploy-linux --source . --ssh-key env:SSH_PRIVATE_KEY --ssh-user "$SSH_USER" --ssh-host "$SSH_HOST" --commit-hash "$HASH" + dagger call --progress=plain -m ci deploy-linux --source . --ssh-key env:SSH_PRIVATE_KEY --ssh-user "$SSH_USER" --ssh-host "$SSH_HOST" --commit-hash "$HASH" deploy-playstore: name: Build & Deploy to Play Store @@ -96,14 +81,11 @@ jobs: with: fetch-depth: 50 - - uses: cachix/install-nix-action@v27 - with: - nix_path: nixpkgs=channel:nixos-25.11 - - - name: Enable Nix flakes + - name: Install Dagger & Task run: | - mkdir -p ~/.config/nix - echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf + curl -L https://dl.dagger.io/dagger/install.sh | BIN_DIR=/usr/local/bin sh + curl -sL https://taskfile.dev/install.sh | sh -s -- -b /usr/local/bin + sudo apt-get update && sudo apt-get install -y stunnel4 netcat-openbsd - name: Setup Dagger Remote Engine (via stunnel) env: @@ -112,40 +94,14 @@ jobs: DAGGER_CA_CERT: ${{ secrets.DAGGER_CA_CERT }} DAGGER_CLIENT_CERT: ${{ secrets.DAGGER_CLIENT_CERT }} DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} - run: nix develop --no-warn-dirty --command scripts/setup_dagger_remote.sh - - - name: Install Android SDK (cached on runner between runs) - run: | - SDK="${ANDROID_HOME:-$HOME/Android/Sdk}" - if [ ! -d "$SDK/platforms/android-34" ]; then - echo "Android SDK not found, installing..." - wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O /tmp/cmdtools.zip - mkdir -p "$SDK/cmdline-tools" - unzip -q /tmp/cmdtools.zip -d "$SDK/cmdline-tools" - [ -d "$SDK/cmdline-tools/cmdline-tools" ] && mv "$SDK/cmdline-tools/cmdline-tools" "$SDK/cmdline-tools/latest" - yes | "$SDK/cmdline-tools/latest/bin/sdkmanager" --licenses >/dev/null 2>&1 || true - "$SDK/cmdline-tools/latest/bin/sdkmanager" "platform-tools" "build-tools;34.0.0" "platforms;android-34" - else - echo "Android SDK cached, skipping install." - fi - - - name: Prepare Keystore - env: - ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} - run: | - if [ -n "$ANDROID_KEYSTORE_BASE64" ]; then - echo "$ANDROID_KEYSTORE_BASE64" | base64 -d > android/app/upload-keystore.jks - else - echo "Error: ANDROID_KEYSTORE_BASE64 secret is not set." - exit 1 - fi + run: scripts/setup_dagger_remote.sh - name: Build & Deploy to Play Store env: ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} PLAY_STORE_CONFIG_JSON: ${{ secrets.PLAY_STORE_CONFIG_JSON }} run: | - nix develop --no-warn-dirty --command dagger call --progress=plain -m ci publish-android --source . --play-store-config env:PLAY_STORE_CONFIG_JSON + dagger call --progress=plain -m ci publish-android --source . --play-store-config env:PLAY_STORE_CONFIG_JSON - name: Build & Deploy APK to server continue-on-error: true @@ -156,7 +112,7 @@ jobs: ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} run: | HASH=$(git rev-parse --short HEAD) - nix develop --no-warn-dirty --command dagger call --progress=plain -m ci deploy-apk --source . --ssh-key env:SSH_PRIVATE_KEY --ssh-user "$SSH_USER" --ssh-host "$SSH_HOST" --commit-hash "$HASH" + dagger call --progress=plain -m ci deploy-apk --source . --ssh-key env:SSH_PRIVATE_KEY --ssh-user "$SSH_USER" --ssh-host "$SSH_HOST" --commit-hash "$HASH" publish-website: name: Publish Website Build History @@ -172,14 +128,11 @@ jobs: with: fetch-depth: 1 - - uses: cachix/install-nix-action@v27 - with: - nix_path: nixpkgs=channel:nixos-25.11 - - - name: Enable Nix flakes + - name: Install Dagger & Task run: | - mkdir -p ~/.config/nix - echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf + curl -L https://dl.dagger.io/dagger/install.sh | BIN_DIR=/usr/local/bin sh + curl -sL https://taskfile.dev/install.sh | sh -s -- -b /usr/local/bin + sudo apt-get update && sudo apt-get install -y stunnel4 netcat-openbsd - name: Setup Dagger Remote Engine (via stunnel) env: @@ -188,7 +141,7 @@ jobs: DAGGER_CA_CERT: ${{ secrets.DAGGER_CA_CERT }} DAGGER_CLIENT_CERT: ${{ secrets.DAGGER_CLIENT_CERT }} DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} - run: nix develop --no-warn-dirty --command scripts/setup_dagger_remote.sh + run: scripts/setup_dagger_remote.sh - name: Generate build history and deploy website continue-on-error: true @@ -197,4 +150,4 @@ jobs: SSH_USER: ${{ secrets.SSH_USER }} SSH_HOST: ${{ secrets.SSH_HOST }} run: | - nix develop --no-warn-dirty --command dagger call --progress=plain -m ci publish-website --source . --ssh-key env:SSH_PRIVATE_KEY --ssh-user "$SSH_USER" --ssh-host "$SSH_HOST" + dagger call --progress=plain -m ci publish-website --source . --ssh-key env:SSH_PRIVATE_KEY --ssh-user "$SSH_USER" --ssh-host "$SSH_HOST" diff --git a/.forgejo/workflows/website.yml b/.forgejo/workflows/website.yml index 09d6e6a..8c8150d 100644 --- a/.forgejo/workflows/website.yml +++ b/.forgejo/workflows/website.yml @@ -19,14 +19,11 @@ jobs: with: submodules: recursive - - uses: cachix/install-nix-action@v27 - with: - nix_path: nixpkgs=channel:nixos-25.11 - - - name: Enable Nix flakes + - name: Install Dagger & Task run: | - mkdir -p ~/.config/nix - echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf + curl -L https://dl.dagger.io/dagger/install.sh | BIN_DIR=/usr/local/bin sh + curl -sL https://taskfile.dev/install.sh | sh -s -- -b /usr/local/bin + sudo apt-get update && sudo apt-get install -y stunnel4 netcat-openbsd - name: Setup Dagger Remote Engine (via stunnel) env: @@ -35,7 +32,7 @@ jobs: DAGGER_CA_CERT: ${{ secrets.DAGGER_CA_CERT }} DAGGER_CLIENT_CERT: ${{ secrets.DAGGER_CLIENT_CERT }} DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} - run: nix develop --no-warn-dirty --command scripts/setup_dagger_remote.sh + run: scripts/setup_dagger_remote.sh - name: Build & Deploy Website env: @@ -43,9 +40,9 @@ jobs: SSH_USER: ${{ secrets.WEBSITE_SSH_USER }} SSH_HOST: ${{ secrets.WEBSITE_SSH_HOST }} run: | - nix develop --no-warn-dirty --command dagger call --progress=plain -m ci publish-website --source . --ssh-key env:SSH_PRIVATE_KEY --ssh-user "$SSH_USER" --ssh-host "$SSH_HOST" + dagger call --progress=plain -m ci publish-website --source . --ssh-key env:SSH_PRIVATE_KEY --ssh-user "$SSH_USER" --ssh-host "$SSH_HOST" - name: Verify Website env: SSH_HOST: ${{ secrets.WEBSITE_SSH_HOST }} - run: nix develop --no-warn-dirty --command scripts/website-verify.sh + run: scripts/website-verify.sh diff --git a/ci/main.go b/ci/main.go index 53b6eec..a44be14 100644 --- a/ci/main.go +++ b/ci/main.go @@ -36,10 +36,6 @@ func (m *Ci) Base(source *dagger.Directory) *dagger.Container { "clang", "cmake", "ninja-build", "pkg-config", "libgtk-3-dev", "liblzma-dev", "libsecret-1-dev", "libgcrypt20-dev", "libjsoncpp-dev", "sqlite3", "curl", "python3", "iproute2"}). - WithExec([]string{"curl", "-sL", "https://github.com/stalwartlabs/mail-server/releases/download/v0.14.1/stalwart-x86_64-unknown-linux-gnu.tar.gz", "-o", "/tmp/stalwart.tar.gz"}). - WithExec([]string{"tar", "-xzf", "/tmp/stalwart.tar.gz", "-C", "/usr/local/bin", "stalwart"}). - WithExec([]string{"chmod", "+x", "/usr/local/bin/stalwart"}). - WithExec([]string{"rm", "/tmp/stalwart.tar.gz"}). WithMountedCache("/root/.pub-cache", dag.CacheVolume("flutter-pub-cache")). WithMountedCache("/root/.gradle", dag.CacheVolume("gradle-cache")). WithEnvVariable("PUB_CACHE", "/root/.pub-cache"). @@ -66,6 +62,23 @@ func (m *Ci) Deployer(sshKey *dagger.Secret) *dagger.Container { WithEnvVariable("RSYNC_RSH", "ssh -o StrictHostKeyChecking=no -i /root/.ssh/id_ed25519") } +// Latest Stalwart Mail Server as a Dagger Service +func (m *Ci) Stalwart(source *dagger.Directory) *dagger.Service { + config := source.Directory("stalwart-dev").File("config.toml") + + return dag.Container(). + From("stalwartlabs/stalwart:latest"). + WithFile("/etc/stalwart/config.toml", config). + // Create data dir in /tmp where permissions are usually more relaxed. + // Note: The Stalwart image might run as a non-root user. + WithExec([]string{"/bin/sh", "-c", "mkdir -p /tmp/stalwart && chmod 777 /tmp/stalwart"}). + WithExposedPort(8080). // JMAP + WithExposedPort(1430). // IMAP + WithExposedPort(1025). // SMTP + WithExposedPort(4190). // ManageSieve + AsService() +} + // Setup environment: pub get and build_runner func (m *Ci) Setup(source *dagger.Directory) *dagger.Container { return m.Base(source). @@ -145,8 +158,25 @@ func (m *Ci) Check(ctx context.Context, source *dagger.Directory) (string, error return coverage, err } - // Run backend tests (requires Stalwart) - testBackend, err := setup.WithExec([]string{"stalwart-dev/test.sh"}).Stdout(ctx) + // Run backend tests (requires Stalwart Service) + stalwart := m.Stalwart(source) + testBackend, err := setup. + WithServiceBinding("stalwart", stalwart). + WithEnvVariable("STALWART_IMAP_HOST", "stalwart"). + WithEnvVariable("STALWART_SMTP_HOST", "stalwart"). + WithEnvVariable("STALWART_URL", "http://stalwart:8080"). + WithEnvVariable("STALWART_IMAP_PORT", "1430"). + WithEnvVariable("STALWART_SMTP_PORT", "1025"). + WithEnvVariable("STALWART_SIEVE_PORT", "4190"). + WithEnvVariable("STALWART_USER_B", "alice@example.com"). + WithEnvVariable("STALWART_PASS_B", "secret"). + // USER_C/PASS_C needed for multi-account tests + WithEnvVariable("STALWART_USER_C", "bob@example.com"). + WithEnvVariable("STALWART_PASS_C", "secret"). + // We can't use stalwart-dev/test.sh directly because it tries to START stalwart. + // We just run the tests against the bound service. + WithExec([]string{"flutter", "test", "test/backend"}). + Stdout(ctx) if err != nil { return testBackend, err } @@ -225,8 +255,8 @@ func (m *Ci) PublishWebsite( // Build and return the Linux bundle func (m *Ci) BuildLinux(source *dagger.Directory) *dagger.Directory { return m.Setup(source). - WithExec([]string{"flutter", "build", "linux", "--debug"}). - Directory("build/linux/x64/debug/bundle") + WithExec([]string{"flutter", "build", "linux", "--release"}). + Directory("build/linux/x64/release/bundle") } // Build and return the Linux bundle (release) diff --git a/stalwart-dev/config.toml b/stalwart-dev/config.toml index c11a30b..82af49e 100644 --- a/stalwart-dev/config.toml +++ b/stalwart-dev/config.toml @@ -1,10 +1,5 @@ # Minimal Stalwart Mail configuration for local development and integration tests. # -# Do not start directly — use stalwart-dev/start, which substitutes $STALWART_PORT -# and writes a per-clone config into /tmp/stalwart-dev-PORT/ before starting. -# -# Check: curl http://localhost:$STALWART_PORT/.well-known/jmap -# # HTTP only — localhost testing, no TLS. # Two test accounts (alice, bob) for multi-account sync tests. @@ -13,27 +8,27 @@ hostname = "localhost" [[server.listener]] id = "jmap" -bind = ["127.0.0.1:8080"] +bind = ["0.0.0.0:8080"] protocol = "http" [[server.listener]] id = "imap" -bind = ["127.0.0.1:1430"] +bind = ["0.0.0.0:1430"] protocol = "imap" [[server.listener]] id = "smtp" -bind = ["127.0.0.1:1025"] +bind = ["0.0.0.0:1025"] protocol = "smtp" [[server.listener]] id = "managesieve" -bind = ["127.0.0.1:4190"] +bind = ["0.0.0.0:4190"] protocol = "managesieve" [store."db"] type = "sqlite" -path = "/tmp/stalwart-dev/data.sqlite" +path = "/tmp/stalwart/data.sqlite" [storage] data = "db"