diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 76a5b36ff..9e6d1abd8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -475,6 +475,126 @@ jobs: path: dist/ retention-days: 3 + release_ui_gtk3: + # Legacy GTK3/WebKit2GTK 4.1 UI build for distros without WebKitGTK 6.0 + # (Ubuntu 22.04, Debian 12, RHEL 9, Fedora <=39). Runs on ubuntu-22.04 so + # the binary links against the oldest supported glibc. + runs-on: ubuntu-22.04 + outputs: + release_ui_gtk3_artifact_url: ${{ steps.upload_release_ui_gtk3.outputs.artifact-url }} + steps: + - name: Checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + fetch-depth: 0 # It is required for GoReleaser to work properly + persist-credentials: false + + - name: Parse semver string + id: semver_parser + uses: netbirdio/shared-actions/actions/parse-semver@be5df6047383da2236e02243cceb857d8567c27e # v0.0.2 + + - name: Set snapshot flag + if: ${{ !startsWith(github.ref, 'refs/tags/v') }} + run: | + echo "flags=--snapshot" >> $GITHUB_ENV + + - name: Set build vars + if: ${{ startsWith(github.ref, 'refs/tags/v') }} + run: | + if [[ "x-${{ steps.semver_parser.outputs.prerelease }}" == "x-" && "x-${{ github.repository }}" == "x-netbirdio/netbird" ]]; then + echo "x-${{ github.repository }}" + echo "x-${{ steps.semver_parser.outputs.prerelease }}" + echo "SKIP_PUBLISH=false" >> $GITHUB_ENV + else + echo "x-${{ github.repository }}" + echo "x-${{ steps.semver_parser.outputs.prerelease }}" + fi + + - name: Set up Go + uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + with: + go-version-file: "go.mod" + cache: false + - name: Cache Go modules + uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-ui-gtk3-go-releaser-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-ui-gtk3-go-releaser- + + - name: Install modules + run: go mod tidy + + - name: check git status + run: git --no-pager diff --exit-code + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Set up pnpm + uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0 + with: + version: 11 + + - name: Install dependencies + run: sudo apt update && sudo apt install -y -q libgtk-3-dev libwebkit2gtk-4.1-dev + + - name: Decode GPG signing key + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + env: + GPG_RPM_PRIVATE_KEY: ${{ secrets.GPG_RPM_PRIVATE_KEY }} + run: | + echo "$GPG_RPM_PRIVATE_KEY" | base64 -d > /tmp/gpg-rpm-signing-key.asc + echo "GPG_RPM_KEY_FILE=/tmp/gpg-rpm-signing-key.asc" >> $GITHUB_ENV + + - name: Install wails3 CLI + # Version derived from go.mod so the binding generator always matches + # the wails runtime the binary links against. + run: | + WAILS_VERSION=$(go list -m -f '{{.Version}}' github.com/wailsapp/wails/v3) + go install github.com/wailsapp/wails/v3/cmd/wails3@$WAILS_VERSION + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@5daf1e915a5f0af01ddbcd89a43b8061ff4f1a89 # v7.2.2 + with: + version: ${{ env.GORELEASER_VER }} + args: release --config .goreleaser_ui_gtk3.yaml --clean ${{ env.flags }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + UPLOAD_DEBIAN_SECRET: ${{ secrets.PKG_UPLOAD_SECRET }} + UPLOAD_YUM_SECRET: ${{ secrets.PKG_UPLOAD_SECRET }} + GPG_RPM_KEY_FILE: ${{ env.GPG_RPM_KEY_FILE }} + NFPM_NETBIRD_UI_RPM_GTK3_PASSPHRASE: ${{ secrets.GPG_RPM_PASSPHRASE }} + SKIP_PUBLISH: ${{ env.SKIP_PUBLISH }} + - name: Verify RPM signatures + run: | + docker run --rm -v $(pwd)/dist:/dist fedora:41 bash -c ' + dnf install -y -q rpm-sign curl >/dev/null 2>&1 + curl -sSL https://pkgs.netbird.io/yum/repodata/repomd.xml.key -o /tmp/rpm-pub.key + rpm --import /tmp/rpm-pub.key + echo "=== Verifying RPM signatures ===" + for rpm_file in /dist/*.rpm; do + [ -f "$rpm_file" ] || continue + echo "--- $(basename $rpm_file) ---" + rpm -K "$rpm_file" + done + ' + - name: Clean up GPG key + if: always() + run: rm -f /tmp/gpg-rpm-signing-key.asc + - name: upload non tags for debug purposes + id: upload_release_ui_gtk3 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1 + with: + name: release-ui-gtk3 + path: dist/ + retention-days: 3 + release_ui_darwin: runs-on: macos-latest outputs: @@ -688,7 +808,7 @@ jobs: comment_release_artifacts: name: Comment release artifacts runs-on: ubuntu-latest - needs: [release, release_ui, release_ui_darwin] + needs: [release, release_ui, release_ui_gtk3, release_ui_darwin] if: ${{ always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository }} permissions: contents: read @@ -700,12 +820,14 @@ jobs: env: RELEASE_RESULT: ${{ needs.release.result }} RELEASE_UI_RESULT: ${{ needs.release_ui.result }} + RELEASE_UI_GTK3_RESULT: ${{ needs.release_ui_gtk3.result }} RELEASE_UI_DARWIN_RESULT: ${{ needs.release_ui_darwin.result }} RELEASE_ARTIFACT_URL: ${{ needs.release.outputs.release_artifact_url }} LINUX_PACKAGES_ARTIFACT_URL: ${{ needs.release.outputs.linux_packages_artifact_url }} WINDOWS_PACKAGES_ARTIFACT_URL: ${{ needs.release.outputs.windows_packages_artifact_url }} MACOS_PACKAGES_ARTIFACT_URL: ${{ needs.release.outputs.macos_packages_artifact_url }} RELEASE_UI_ARTIFACT_URL: ${{ needs.release_ui.outputs.release_ui_artifact_url }} + RELEASE_UI_GTK3_ARTIFACT_URL: ${{ needs.release_ui_gtk3.outputs.release_ui_gtk3_artifact_url }} RELEASE_UI_DARWIN_ARTIFACT_URL: ${{ needs.release_ui_darwin.outputs.release_ui_darwin_artifact_url }} GHCR_IMAGES_MARKDOWN: ${{ needs.release.outputs.ghcr_images }} with: @@ -728,6 +850,7 @@ jobs: ['Windows packages', process.env.WINDOWS_PACKAGES_ARTIFACT_URL, process.env.RELEASE_RESULT], ['macOS packages', process.env.MACOS_PACKAGES_ARTIFACT_URL, process.env.RELEASE_RESULT], ['UI artifacts', process.env.RELEASE_UI_ARTIFACT_URL, process.env.RELEASE_UI_RESULT], + ['UI GTK3 artifacts', process.env.RELEASE_UI_GTK3_ARTIFACT_URL, process.env.RELEASE_UI_GTK3_RESULT], ['UI macOS artifacts', process.env.RELEASE_UI_DARWIN_ARTIFACT_URL, process.env.RELEASE_UI_DARWIN_RESULT], ]; @@ -784,7 +907,7 @@ jobs: trigger_signer: runs-on: ubuntu-latest - needs: [release, release_ui, release_ui_darwin, test_windows_installer] + needs: [release, release_ui, release_ui_gtk3, release_ui_darwin, test_windows_installer] if: startsWith(github.ref, 'refs/tags/') steps: - name: Trigger binaries sign pipelines diff --git a/.goreleaser_ui_gtk3.yaml b/.goreleaser_ui_gtk3.yaml new file mode 100644 index 000000000..516d6f800 --- /dev/null +++ b/.goreleaser_ui_gtk3.yaml @@ -0,0 +1,116 @@ +version: 2 +env: + - SKIP_PUBLISH={{ if index .Env "SKIP_PUBLISH" }}{{ .Env.SKIP_PUBLISH }}{{ else }}true{{ end }} +project_name: netbird-ui + +before: + hooks: + # Bindings are gitignored; regenerate before the frontend build so + # the @wailsio/runtime Vite plugin can resolve them (vite refuses to + # build without them). + - sh -c 'cd client/ui && wails3 generate bindings -clean=true -ts' + - sh -c 'cd client/ui/frontend && pnpm install --frozen-lockfile && pnpm build' + +builds: + # Legacy GTK3 / WebKit2GTK 4.1 build for distros without WebKitGTK 6.0 + # (Ubuntu 22.04, Debian 12, RHEL 9, Fedora <=39). The gtk3 tag flips the + # Wails Linux backend to the GTK3 stack and swaps our GTK4-only XEmbed + # tray host for the pure-Go stub (client/ui/xembed_host_gtk3_linux.go). + # Must be built on the oldest supported glibc (ubuntu-22.04 runner). + - id: netbird-ui-gtk3 + dir: client/ui + binary: netbird-ui + env: + - CGO_ENABLED=1 + goos: + - linux + goarch: + - amd64 + ldflags: + - -s -w -X github.com/netbirdio/netbird/version.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser + mod_timestamp: "{{ .CommitTimestamp }}" + tags: + - production + - gtk3 + +archives: + - id: linux-gtk3-arch + name_template: "{{ .ProjectName }}-linux-gtk3_{{ .Version }}_{{ .Os }}_{{ .Arch }}" + builds: + - netbird-ui-gtk3 + +nfpms: + # Same package_name as the GTK4 packages -- the two are mutually-exclusive + # alternatives served from separate repo paths (see uploads below); a given + # distro points at exactly one of them. + - maintainer: Netbird + description: Netbird client UI. + homepage: https://netbird.io/ + license: BSD-3-Clause + vendor: NetBird + id: netbird_ui_deb_gtk3 + package_name: netbird-ui + builds: + - netbird-ui-gtk3 + formats: + - deb + scripts: + postinstall: "release_files/ui-post-install.sh" + contents: + - src: client/ui/build/linux/netbird.desktop + dst: /usr/share/applications/org.wails.netbird.desktop + - src: client/ui/build/appicon.png + dst: /usr/share/pixmaps/netbird.png + dependencies: + - netbird (>= 0.75.0) + - libgtk-3-0 + - libwebkit2gtk-4.1-0 + + - maintainer: Netbird + description: Netbird client UI. + homepage: https://netbird.io/ + license: BSD-3-Clause + vendor: NetBird + id: netbird_ui_rpm_gtk3 + package_name: netbird-ui + builds: + - netbird-ui-gtk3 + formats: + - rpm + scripts: + postinstall: "release_files/ui-post-install.sh" + contents: + - src: client/ui/build/linux/netbird.desktop + dst: /usr/share/applications/org.wails.netbird.desktop + - src: client/ui/build/appicon.png + dst: /usr/share/pixmaps/netbird.png + dependencies: + - netbird >= 0.75.0 + - (gtk3 or libgtk-3-0) + - (webkit2gtk4.1 or libwebkit2gtk-4_1-0) + + rpm: + signature: + key_file: '{{ if index .Env "GPG_RPM_KEY_FILE" }}{{ .Env.GPG_RPM_KEY_FILE }}{{ end }}' + +uploads: + # The gtk3 packages reuse the netbird-ui package name, so they live in + # dedicated repo paths (deb distribution `gtk3`, yum path `yum-gtk3`) that + # legacy distros point their repo config at. + - name: debian-gtk3 + skip: "{{ .Env.SKIP_PUBLISH }}" + ids: + - netbird_ui_deb_gtk3 + mode: archive + target: https://pkgs.wiretrustee.com/debian/pool/{{ .ArtifactName }};deb.distribution=gtk3;deb.component=main;deb.architecture={{ if .Arm }}armhf{{ else }}{{ .Arch }}{{ end }};deb.package= + username: dev@wiretrustee.com + method: PUT + + - name: yum-gtk3 + skip: "{{ .Env.SKIP_PUBLISH }}" + ids: + - netbird_ui_rpm_gtk3 + mode: archive + target: https://pkgs.wiretrustee.com/yum-gtk3/{{ .Arch }}{{ if .Arm }}{{ .Arm }}{{ end }} + username: dev@wiretrustee.com + method: PUT diff --git a/client/ui/xembed_host_gtk3_linux.go b/client/ui/xembed_host_gtk3_linux.go new file mode 100644 index 000000000..b1f18cc90 --- /dev/null +++ b/client/ui/xembed_host_gtk3_linux.go @@ -0,0 +1,40 @@ +//go:build linux && gtk3 && !(linux && 386) + +package main + +import ( + "errors" + + "github.com/godbus/dbus/v5" +) + +// The legacy GTK3 / WebKit2GTK 4.1 build (-tags gtk3) drops the in-process +// XEmbed StatusNotifierWatcher entirely. The real implementation +// (xembed_host_linux.go + xembed_tray_linux.c) links GTK4 and uses GTK4-only +// popup-menu APIs that have no drop-in GTK3 equivalent, so rather than port the +// C layer we stub the host out on gtk3 builds. The tray still works on every +// desktop that ships its own StatusNotifierWatcher (KDE, GNOME+AppIndicator, +// Cinnamon/xapp, XFCE, …); only the minimal-WM fallback (Fluxbox/OpenBox/i3/ +// dwm/vanilla GNOME) is unavailable on gtk3 packages. See LINUX-TRAY.md. + +// xembedHost is a placeholder so the package compiles on gtk3 builds; the real +// type (with X11/GTK4 state) lives in xembed_host_linux.go. It is never +// instantiated here because xembedTrayAvailable always reports false. +type xembedHost struct{} + +// run satisfies the call in tray_watcher_linux.go; unreachable on gtk3 because +// newXembedHost never returns a non-nil host. +func (*xembedHost) run() {} + +// xembedTrayAvailable always reports false on gtk3 builds, so the watcher probe +// loop in startStatusNotifierWatcher exits immediately and newXembedHost is +// never reached. recenter_linux.go's predicate becomes a harmless no-op too. +func xembedTrayAvailable() bool { + return false +} + +// newXembedHost exists only to satisfy the reference in tray_watcher_linux.go; +// it is unreachable because xembedTrayAvailable returns false on gtk3. +func newXembedHost(conn *dbus.Conn, busName string, objPath dbus.ObjectPath) (*xembedHost, error) { + return nil, errors.New("xembed host unsupported on gtk3 build") +} diff --git a/client/ui/xembed_host_linux.go b/client/ui/xembed_host_linux.go index ba7a5d07c..5551cd02a 100644 --- a/client/ui/xembed_host_linux.go +++ b/client/ui/xembed_host_linux.go @@ -1,4 +1,4 @@ -//go:build linux && !(linux && 386) +//go:build linux && !gtk3 && !(linux && 386) package main diff --git a/client/ui/xembed_tray_linux.c b/client/ui/xembed_tray_linux.c index 07ec74bb2..86da8bf70 100644 --- a/client/ui/xembed_tray_linux.c +++ b/client/ui/xembed_tray_linux.c @@ -1,3 +1,5 @@ +//go:build linux && !gtk3 && !(linux && 386) + #include "xembed_tray_linux.h" #include