Nightly Debug Build #110
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Nightly Debug Build | |
| on: | |
| schedule: | |
| - cron: "0 2 * * *" # every day at 02:00 UTC | |
| workflow_dispatch: | |
| inputs: | |
| force: | |
| description: 'Force a build even when change detection finds nothing' | |
| type: boolean | |
| default: false | |
| permissions: | |
| contents: write | |
| actions: read # `gh run list` reads this workflow's own run history (see the gate) | |
| # Per-ref: a newer run on the same branch supersedes the older one, but nightly | |
| # runs on different branches don't fight over one global slot (a static group | |
| # cancelled whichever started second — see two manual dispatches racing). | |
| concurrency: | |
| group: nightly-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # ── Cheap gate: decide whether a nightly is warranted at all, so the two heavy | |
| # build jobs below can fan out in parallel instead of one waiting on the other. ── | |
| check: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| skip: ${{ steps.check.outputs.skip }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check whether a nightly build is warranted | |
| id: check | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| # Manual dispatch with force=true always builds, bypassing the gate below | |
| # (`inputs.force` is empty on the schedule, so this only fires on a | |
| # deliberate workflow_dispatch). | |
| if [ "${{ inputs.force }}" = "true" ]; then | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| # Paths whose changes actually affect a shipped artifact — the Android | |
| # module or the desktop installers (docs/ci/chore commits must not trigger | |
| # a nightly). Kept in sync with release.yml's change detection. | |
| paths=( | |
| 'module/*.sh' | |
| 'module/bin/kasumi-proxyctl' | |
| 'module/bin/utils.sh' | |
| 'module/webroot/cgi-bin/' | |
| 'frontend/src/' | |
| 'crates/' | |
| 'src-tauri/' | |
| 'scripts/fetch-binaries.sh' | |
| 'scripts/binaries.json' | |
| 'scripts/binary-versions.sh' | |
| 'scripts/package-release.sh' | |
| ) | |
| # New code since the *previous nightly run*, not a rigid 24h window. The | |
| # daily cron anchors the diff to the last time the nightly looked, so a | |
| # commit 25h old still counts (the old window silently dropped it) and an | |
| # unchanged tree never rebuilds. The previous run's head SHA is read from | |
| # this workflow's own run history (built or skipped — a run always records | |
| # the HEAD it saw, so the baseline never drifts). No prior run to compare | |
| # against (first run under this logic, or history pruned) ⇒ build. | |
| prev=$(gh run list --workflow nightly.yml -L 30 \ | |
| --json databaseId,headSha,createdAt \ | |
| --jq "map(select(.databaseId != ${GITHUB_RUN_ID})) | sort_by(.createdAt) | last | .headSha // empty") | |
| if [ -z "$prev" ] || ! git cat-file -e "$prev^{commit}" 2>/dev/null; then | |
| since_nightly=1 | |
| else | |
| since_nightly=$(git log --oneline "$prev"..HEAD -- "${paths[@]}" 2>/dev/null | wc -l) | |
| fi | |
| # Code commits since the last release tag. Without this a build still | |
| # fires right after a release: the release-bump commit touches an artifact | |
| # path (binary-versions.sh), so it counts as new since the last nightly, | |
| # yet the release already shipped everything — last_tag..HEAD has nothing | |
| # new to build. | |
| last_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "") | |
| if [ -z "$last_tag" ]; then | |
| since_release=$since_nightly | |
| else | |
| since_release=$(git log "$last_tag"..HEAD --oneline -- "${paths[@]}" 2>/dev/null | wc -l) | |
| fi | |
| if [ "$since_nightly" -gt 0 ] && [ "$since_release" -gt 0 ]; then | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| # ── Android module debug zip. Mirrors release.yml's `module`/`desktop` split; | |
| # runs in parallel with `desktop` once `check` clears them both. ── | |
| module: | |
| needs: check | |
| if: needs.check.outputs.skip != 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install Nix | |
| uses: DeterminateSystems/nix-installer-action@da36cb69b1c3247ad7a1f931ebfd954a1105ef14 # v14 | |
| with: | |
| extra-conf: | | |
| accept-flake-config = true | |
| - name: Cachix | |
| uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 | |
| with: | |
| name: kasumi-proxy | |
| authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} | |
| - name: Install, lint & test | |
| run: | | |
| nix develop .#android --command bash -euo pipefail -c ' | |
| bun install --frozen-lockfile | |
| bunx biome check | |
| bun run frontend/scripts/check-i18n.ts | |
| ( cd frontend && bunx vitest run ) | |
| ' | |
| - name: Build debug zip | |
| run: nix develop .#android --command bash -euo pipefail -c 'scripts/package-release.sh' | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: kasumi-proxy-debug-${{ github.sha }} | |
| path: build/*.zip | |
| retention-days: 7 | |
| # ── Desktop installers as nightly artifacts (unsigned), same fetch+bundle path | |
| # as the release workflow but uploaded to the run instead of a GitHub release. ── | |
| desktop: | |
| needs: check | |
| if: needs.check.outputs.skip != 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| - os: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| with: | |
| toolchain: stable | |
| targets: ${{ matrix.target }} | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 | |
| with: | |
| bun-version: latest | |
| - name: Install Tauri CLI | |
| uses: taiki-e/install-action@15449e3094499af05d8d964a1c884208e4b8b595 # v2 | |
| with: | |
| tool: tauri-cli | |
| - name: Linux desktop deps | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libwebkit2gtk-4.1-dev libgtk-3-dev librsvg2-dev \ | |
| libayatana-appindicator3-dev patchelf | |
| - name: Build the UI | |
| run: | | |
| bun install --frozen-lockfile | |
| ( cd frontend && bun run build ) | |
| - name: Fetch desktop binaries | |
| shell: bash | |
| run: scripts/fetch-binaries.sh desktop ${{ matrix.target }} | |
| - name: Build desktop bundles | |
| shell: bash | |
| env: | |
| # linuxdeploy/appimagetool mount themselves with FUSE, which the runner | |
| # doesn't provide — extract-and-run instead, or AppImage bundling aborts | |
| # the whole job ("failed to run linuxdeploy") after the .deb is built. | |
| APPIMAGE_EXTRACT_AND_RUN: 1 | |
| run: | | |
| # Build + stage the privilege helper as a sidecar (see release.yml). | |
| t="${{ matrix.target }}" | |
| ext=""; case "$t" in *windows*) ext=".exe";; esac | |
| cargo build --release --target "$t" --bin kasumi-helper | |
| cp "target/$t/release/kasumi-helper$ext" "src-tauri/binaries/kasumi-helper-$t$ext" | |
| ver=$(scripts/app-version.sh) # product version from module.prop | |
| # Nightly artifacts are unsigned: override the global | |
| # `bundle.createUpdaterArtifacts` so `tauri build` doesn't demand the | |
| # minisign key (only release.yml signs). The updater never points at | |
| # nightly, so no .sig is needed here. | |
| cargo tauri build --target "$t" \ | |
| --config src-tauri/tauri.bundle.conf.json \ | |
| --config "{\"version\":\"$ver\",\"bundle\":{\"createUpdaterArtifacts\":false}}" | |
| - name: Collect artifacts | |
| shell: bash | |
| run: | | |
| dir="target/${{ matrix.target }}/release/bundle" | |
| mkdir -p dist | |
| find "$dir" -type f \ | |
| \( -name '*.deb' -o -name '*.rpm' -o -name '*.AppImage' -o -name '*-setup.exe' -o -name '*.msi' \) \ | |
| -exec cp {} dist/ \; | |
| ls -la dist | |
| - name: Assemble Windows portable zip | |
| if: runner.os == 'Windows' | |
| shell: bash | |
| run: | | |
| # Portable build: app exe + cores (suffix stripped) + wintun.dll + | |
| # libcronet.dll + a `portable.dat` marker that pins all state next to the | |
| # exe at runtime. | |
| t="${{ matrix.target }}" | |
| out="dist/portable/Kasumi-Proxy" | |
| mkdir -p "$out" | |
| cp "target/$t/release/kasumi-desktop.exe" "$out/" | |
| cp "target/$t/release/kasumi-helper.exe" "$out/" | |
| for c in xray sing-box tun2socks hev-socks5-tunnel; do | |
| cp "src-tauri/binaries/$c-$t.exe" "$out/$c.exe" | |
| done | |
| cp src-tauri/binaries/wintun.dll "$out/" | |
| # libcronet.dll must sit next to sing-box for its naive outbound to load. | |
| cp src-tauri/binaries/libcronet.dll "$out/" | |
| # msys-2.0.dll must sit next to hev-socks5-tunnel (msys2 build) to load. | |
| cp src-tauri/binaries/msys-2.0.dll "$out/" | |
| : > "$out/portable.dat" | |
| name=$(scripts/artifact-name.sh windows-portable) | |
| ( cd dist/portable && 7z a -tzip "../$name" Kasumi-Proxy >/dev/null ) | |
| rm -rf dist/portable | |
| ls -la dist | |
| - name: Assemble Linux portable zip | |
| if: runner.os == 'Linux' | |
| shell: bash | |
| run: | | |
| # Portable build: app binary + cores (suffix stripped) + a `portable.dat` | |
| # marker that pins all state next to the binary at runtime (USB-stick run). | |
| t="${{ matrix.target }}" | |
| out="dist/portable/Kasumi-Proxy" | |
| mkdir -p "$out" | |
| cp "target/$t/release/kasumi-desktop" "$out/" | |
| cp "target/$t/release/kasumi-helper" "$out/" | |
| for c in xray sing-box tun2socks hev-socks5-tunnel; do | |
| cp "src-tauri/binaries/$c-$t" "$out/$c" | |
| done | |
| # libcronet.so must sit next to sing-box for its naive outbound to load. | |
| cp src-tauri/binaries/libcronet.so "$out/" | |
| chmod +x "$out"/* | |
| : > "$out/portable.dat" | |
| name=$(scripts/artifact-name.sh linux-portable) | |
| ( cd dist/portable && zip -qr "../$name" Kasumi-Proxy ) | |
| rm -rf dist/portable | |
| ls -la dist | |
| - name: Upload desktop artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: kasumi-desktop-${{ matrix.target }}-${{ github.sha }} | |
| path: dist/* | |
| retention-days: 7 |