name: Release on: push: tags: - "v*" branches: - main pull_request: env: SIGN_PIPE_VER: "v0.1.1" GORELEASER_VER: "v2.14.3" PRODUCT_NAME: "NetBird" COPYRIGHT: "NetBird GmbH" concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || github.actor_id }} cancel-in-progress: true jobs: release_freebsd_port: name: "FreeBSD Port / Build & Test" runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v4 - name: Generate FreeBSD port diff run: bash release_files/freebsd-port-diff.sh - name: Generate FreeBSD port issue body run: bash release_files/freebsd-port-issue-body.sh - name: Check if diff was generated id: check_diff run: | if ls netbird-*.diff 1> /dev/null 2>&1; then echo "diff_exists=true" >> $GITHUB_OUTPUT else echo "diff_exists=false" >> $GITHUB_OUTPUT echo "No diff file generated (port may already be up to date)" fi - name: Extract version if: steps.check_diff.outputs.diff_exists == 'true' id: version run: | VERSION=$(ls netbird-*.diff | sed 's/netbird-\(.*\)\.diff/\1/') echo "version=$VERSION" >> $GITHUB_OUTPUT echo "Generated files for version: $VERSION" cat netbird-*.diff - name: Test FreeBSD port if: steps.check_diff.outputs.diff_exists == 'true' uses: vmactions/freebsd-vm@v1 with: usesh: true copyback: false release: "15.0" prepare: | # Install required packages pkg install -y git curl portlint go # Install Go for building GO_TARBALL="go1.25.5.freebsd-amd64.tar.gz" GO_URL="https://go.dev/dl/$GO_TARBALL" curl -LO "$GO_URL" tar -C /usr/local -xzf "$GO_TARBALL" # Clone ports tree (shallow, only what we need) git clone --depth 1 --filter=blob:none https://git.FreeBSD.org/ports.git /usr/ports cd /usr/ports run: | set -e -x export PATH=$PATH:/usr/local/go/bin # Find the diff file echo "Finding diff file..." DIFF_FILE=$(find $PWD -name "netbird-*.diff" -type f 2>/dev/null | head -1) echo "Found: $DIFF_FILE" if [[ -z "$DIFF_FILE" ]]; then echo "ERROR: Could not find diff file" find ~ -name "*.diff" -type f 2>/dev/null || true exit 1 fi # Apply the generated diff from /usr/ports (diff has a/security/netbird/... paths) cd /usr/ports patch -p1 -V none < "$DIFF_FILE" # Show patched Makefile version=$(cat security/netbird/Makefile | grep -E '^DISTVERSION=' | awk '{print $NF}') cd /usr/ports/security/netbird export BATCH=yes make package pkg add ./work/pkg/netbird-*.pkg netbird version | grep "$version" echo "FreeBSD port test completed successfully!" - name: Upload FreeBSD port files if: steps.check_diff.outputs.diff_exists == 'true' uses: actions/upload-artifact@v4 with: name: freebsd-port-files path: | ./netbird-*-issue.txt ./netbird-*.diff retention-days: 30 release: runs-on: ubuntu-latest-m env: flags: "" steps: - name: Parse semver string id: semver_parser uses: booxmedialtd/ws-action-parse-semver@v1 with: input_string: ${{ (startsWith(github.ref, 'refs/tags/v') && github.ref) || 'refs/tags/v0.0.0' }} version_extractor_regex: '\/v(.*)$' - if: ${{ !startsWith(github.ref, 'refs/tags/v') }} run: echo "flags=--snapshot" >> $GITHUB_ENV - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 # It is required for GoReleaser to work properly - name: Set up Go uses: actions/setup-go@v5 with: go-version-file: "go.mod" cache: false - name: Cache Go modules uses: actions/cache@v4 with: path: | ~/go/pkg/mod ~/.cache/go-build key: ${{ runner.os }}-go-releaser-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go-releaser- - name: Install modules run: go mod tidy - name: check git status run: git --no-pager diff --exit-code - name: Set up QEMU uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to Docker hub if: github.event_name != 'pull_request' uses: docker/login-action@v1 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_TOKEN }} - name: Log in to the GitHub container registry if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.CI_DOCKER_PUSH_GITHUB_TOKEN }} - name: Install OS build dependencies run: sudo apt update && sudo apt install -y -q gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu - 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 goversioninfo run: go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@233067e - name: Generate windows syso amd64 run: goversioninfo -icon client/ui/assets/netbird.ico -manifest client/manifest.xml -product-name ${{ env.PRODUCT_NAME }} -copyright "${{ env.COPYRIGHT }}" -ver-major ${{ steps.semver_parser.outputs.major }} -ver-minor ${{ steps.semver_parser.outputs.minor }} -ver-patch ${{ steps.semver_parser.outputs.patch }} -ver-build 0 -file-version ${{ steps.semver_parser.outputs.fullversion }}.0 -product-version ${{ steps.semver_parser.outputs.fullversion }}.0 -o client/resources_windows_amd64.syso - name: Generate windows syso arm64 run: goversioninfo -arm -64 -icon client/ui/assets/netbird.ico -manifest client/manifest.xml -product-name ${{ env.PRODUCT_NAME }} -copyright "${{ env.COPYRIGHT }}" -ver-major ${{ steps.semver_parser.outputs.major }} -ver-minor ${{ steps.semver_parser.outputs.minor }} -ver-patch ${{ steps.semver_parser.outputs.patch }} -ver-build 0 -file-version ${{ steps.semver_parser.outputs.fullversion }}.0 -product-version ${{ steps.semver_parser.outputs.fullversion }}.0 -o client/resources_windows_arm64.syso - name: Run GoReleaser id: goreleaser uses: goreleaser/goreleaser-action@v4 with: version: ${{ env.GORELEASER_VER }} args: release --clean ${{ env.flags }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_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_RPM_PASSPHRASE: ${{ secrets.GPG_RPM_PASSPHRASE }} - 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/*amd64*.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: Tag and push images (amd64 only) if: | (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || (github.event_name == 'push' && github.ref == 'refs/heads/main') run: | resolve_tags() { if [[ "${{ github.event_name }}" == "pull_request" ]]; then echo "pr-${{ github.event.pull_request.number }}" else echo "main sha-$(git rev-parse --short HEAD)" fi } tag_and_push() { local src="$1" img_name tag dst img_name="${src%%:*}" for tag in $(resolve_tags); do dst="${img_name}:${tag}" echo "Tagging ${src} -> ${dst}" docker tag "$src" "$dst" docker push "$dst" done } export -f tag_and_push resolve_tags echo '${{ steps.goreleaser.outputs.artifacts }}' | \ jq -r '.[] | select(.type == "Docker Image") | select(.goarch == "amd64") | .name' | \ grep '^ghcr.io/' | while read -r SRC; do tag_and_push "$SRC" done - name: upload non tags for debug purposes uses: actions/upload-artifact@v4 with: name: release path: dist/ retention-days: 7 - name: upload linux packages uses: actions/upload-artifact@v4 with: name: linux-packages path: dist/netbird_linux** retention-days: 7 - name: upload windows packages uses: actions/upload-artifact@v4 with: name: windows-packages path: dist/netbird_windows** retention-days: 7 - name: upload macos packages uses: actions/upload-artifact@v4 with: name: macos-packages path: dist/netbird_darwin** retention-days: 7 release_ui: runs-on: ubuntu-latest steps: - name: Parse semver string id: semver_parser uses: booxmedialtd/ws-action-parse-semver@v1 with: input_string: ${{ (startsWith(github.ref, 'refs/tags/v') && github.ref) || 'refs/tags/v0.0.0' }} version_extractor_regex: '\/v(.*)$' - if: ${{ !startsWith(github.ref, 'refs/tags/v') }} run: echo "flags=--snapshot" >> $GITHUB_ENV - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 # It is required for GoReleaser to work properly - name: Set up Go uses: actions/setup-go@v5 with: go-version-file: "go.mod" cache: false - name: Cache Go modules uses: actions/cache@v4 with: path: | ~/go/pkg/mod ~/.cache/go-build key: ${{ runner.os }}-ui-go-releaser-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-ui-go-releaser- - name: Install modules run: go mod tidy - name: check git status run: git --no-pager diff --exit-code - name: Install dependencies run: sudo apt update && sudo apt install -y -q libappindicator3-dev gir1.2-appindicator3-0.1 libxxf86vm-dev gcc-mingw-w64-x86-64 - 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 LLVM-MinGW for ARM64 cross-compilation run: | cd /tmp wget -q https://github.com/mstorsjo/llvm-mingw/releases/download/20250709/llvm-mingw-20250709-ucrt-ubuntu-22.04-x86_64.tar.xz echo "60cafae6474c7411174cff1d4ba21a8e46cadbaeb05a1bace306add301628337 llvm-mingw-20250709-ucrt-ubuntu-22.04-x86_64.tar.xz" | sha256sum -c tar -xf llvm-mingw-20250709-ucrt-ubuntu-22.04-x86_64.tar.xz echo "/tmp/llvm-mingw-20250709-ucrt-ubuntu-22.04-x86_64/bin" >> $GITHUB_PATH - name: Install goversioninfo run: go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@233067e - name: Generate windows syso amd64 run: goversioninfo -64 -icon client/ui/assets/netbird.ico -manifest client/ui/manifest.xml -product-name ${{ env.PRODUCT_NAME }}-"UI" -copyright "${{ env.COPYRIGHT }}" -ver-major ${{ steps.semver_parser.outputs.major }} -ver-minor ${{ steps.semver_parser.outputs.minor }} -ver-patch ${{ steps.semver_parser.outputs.patch }} -ver-build 0 -file-version ${{ steps.semver_parser.outputs.fullversion }}.0 -product-version ${{ steps.semver_parser.outputs.fullversion }}.0 -o client/ui/resources_windows_amd64.syso - name: Generate windows syso arm64 run: goversioninfo -arm -64 -icon client/ui/assets/netbird.ico -manifest client/ui/manifest.xml -product-name ${{ env.PRODUCT_NAME }}-"UI" -copyright "${{ env.COPYRIGHT }}" -ver-major ${{ steps.semver_parser.outputs.major }} -ver-minor ${{ steps.semver_parser.outputs.minor }} -ver-patch ${{ steps.semver_parser.outputs.patch }} -ver-build 0 -file-version ${{ steps.semver_parser.outputs.fullversion }}.0 -product-version ${{ steps.semver_parser.outputs.fullversion }}.0 -o client/ui/resources_windows_arm64.syso - name: Run GoReleaser uses: goreleaser/goreleaser-action@v4 with: version: ${{ env.GORELEASER_VER }} args: release --config .goreleaser_ui.yaml --clean ${{ env.flags }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_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_PASSPHRASE: ${{ secrets.GPG_RPM_PASSPHRASE }} - 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 uses: actions/upload-artifact@v4 with: name: release-ui path: dist/ retention-days: 3 release_ui_darwin: runs-on: macos-latest steps: - if: ${{ !startsWith(github.ref, 'refs/tags/v') }} run: echo "flags=--snapshot" >> $GITHUB_ENV - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 # It is required for GoReleaser to work properly - name: Set up Go uses: actions/setup-go@v5 with: go-version-file: "go.mod" cache: false - name: Cache Go modules uses: actions/cache@v4 with: path: | ~/go/pkg/mod ~/.cache/go-build key: ${{ runner.os }}-ui-go-releaser-darwin-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-ui-go-releaser-darwin- - name: Install modules run: go mod tidy - name: check git status run: git --no-pager diff --exit-code - name: Run GoReleaser id: goreleaser uses: goreleaser/goreleaser-action@v4 with: version: ${{ env.GORELEASER_VER }} args: release --config .goreleaser_ui_darwin.yaml --clean ${{ env.flags }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: upload non tags for debug purposes uses: actions/upload-artifact@v4 with: name: release-ui-darwin path: dist/ retention-days: 3 trigger_signer: runs-on: ubuntu-latest needs: [release, release_ui, release_ui_darwin] if: startsWith(github.ref, 'refs/tags/') steps: - name: Trigger binaries sign pipelines uses: benc-uk/workflow-dispatch@v1 with: workflow: Sign bin and installer repo: netbirdio/sign-pipelines ref: ${{ env.SIGN_PIPE_VER }} token: ${{ secrets.SIGN_GITHUB_TOKEN }} inputs: '{ "tag": "${{ github.ref }}", "skipRelease": false }'