mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-03 11:28:38 -04:00
The frontend uses pnpm 11 (packageManager field, v11 lockfile, and the allowBuilds key in pnpm-workspace.yaml is a pnpm 10+ feature), but the release_ui job's pnpm/action-setup was pinned to v9. v9 rejects the workspace file with 'packages field missing or empty' before the frontend build hook can run.
747 lines
30 KiB
YAML
747 lines
30 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- "v*"
|
|
branches:
|
|
- main
|
|
pull_request:
|
|
|
|
env:
|
|
SIGN_PIPE_VER: "v0.1.4"
|
|
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-24.04-8-core
|
|
outputs:
|
|
release_artifact_url: ${{ steps.upload_release.outputs.artifact-url }}
|
|
linux_packages_artifact_url: ${{ steps.upload_linux_packages.outputs.artifact-url }}
|
|
windows_packages_artifact_url: ${{ steps.upload_windows_packages.outputs.artifact-url }}
|
|
macos_packages_artifact_url: ${{ steps.upload_macos_packages.outputs.artifact-url }}
|
|
ghcr_images: ${{ steps.tag_and_push_images.outputs.images_markdown }}
|
|
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/build/windows/icon.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/build/windows/icon.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)
|
|
id: tag_and_push_images
|
|
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: |
|
|
set -euo pipefail
|
|
|
|
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
|
|
}
|
|
|
|
ghcr_package_url() {
|
|
local image="$1" package encoded_package
|
|
package="${image#ghcr.io/}"
|
|
package="${package#*/}"
|
|
package="${package%%:*}"
|
|
encoded_package="${package//\//%2F}"
|
|
echo "https://github.com/orgs/netbirdio/packages/container/package/${encoded_package}"
|
|
}
|
|
|
|
image_refs=()
|
|
|
|
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"
|
|
image_refs+=("$dst")
|
|
done
|
|
}
|
|
|
|
cat > /tmp/goreleaser-artifacts.json <<'JSON'
|
|
${{ steps.goreleaser.outputs.artifacts }}
|
|
JSON
|
|
|
|
mapfile -t src_images < <(
|
|
jq -r '.[] | select(.type == "Docker Image") | select(.goarch == "amd64") | .name | select(startswith("ghcr.io/"))' /tmp/goreleaser-artifacts.json
|
|
)
|
|
|
|
for src in "${src_images[@]}"; do
|
|
tag_and_push "$src"
|
|
done
|
|
|
|
{
|
|
echo "images_markdown<<EOF"
|
|
if [[ ${#image_refs[@]} -eq 0 ]]; then
|
|
echo "_No GHCR images were pushed._"
|
|
else
|
|
printf '%s\n' "${image_refs[@]}" | sort -u | while read -r image; do
|
|
printf -- '- [`%s`](%s)\n' "$image" "$(ghcr_package_url "$image")"
|
|
done
|
|
fi
|
|
echo "EOF"
|
|
} >> "$GITHUB_OUTPUT"
|
|
- name: upload non tags for debug purposes
|
|
id: upload_release
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: release
|
|
path: dist/
|
|
retention-days: 7
|
|
- name: upload linux packages
|
|
id: upload_linux_packages
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: linux-packages
|
|
path: dist/netbird_linux**
|
|
retention-days: 7
|
|
- name: upload windows packages
|
|
id: upload_windows_packages
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: windows-packages
|
|
path: dist/netbird_windows**
|
|
retention-days: 7
|
|
- name: upload macos packages
|
|
id: 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
|
|
outputs:
|
|
release_ui_artifact_url: ${{ steps.upload_release_ui.outputs.artifact-url }}
|
|
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: Set up Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '20'
|
|
|
|
- name: Set up pnpm
|
|
uses: pnpm/action-setup@v3
|
|
with:
|
|
version: 11
|
|
|
|
- name: Install dependencies
|
|
run: sudo apt update && sudo apt install -y -q libgtk-4-dev libwebkitgtk-6.0-dev libsoup-3.0-dev libayatana-appindicator3-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: 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: Generate windows syso amd64
|
|
run: goversioninfo -64 -icon client/ui/build/windows/icon.ico -manifest client/ui/build/windows/wails.exe.manifest -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/build/windows/icon.ico -manifest client/ui/build/windows/wails.exe.manifest -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
|
|
id: upload_release_ui
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: release-ui
|
|
path: dist/
|
|
retention-days: 3
|
|
|
|
release_ui_darwin:
|
|
runs-on: macos-latest
|
|
outputs:
|
|
release_ui_darwin_artifact_url: ${{ steps.upload_release_ui_darwin.outputs.artifact-url }}
|
|
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: Set up Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '20'
|
|
- name: Set up pnpm
|
|
uses: pnpm/action-setup@v3
|
|
with:
|
|
version: 11
|
|
- 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
|
|
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
|
|
id: upload_release_ui_darwin
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: release-ui-darwin
|
|
path: dist/
|
|
retention-days: 3
|
|
|
|
test_windows_installer:
|
|
name: "Windows Installer / Build Test"
|
|
runs-on: windows-2022
|
|
needs: [release, release_ui]
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: amd64
|
|
wintun_arch: amd64
|
|
- arch: arm64
|
|
wintun_arch: arm64
|
|
defaults:
|
|
run:
|
|
shell: powershell
|
|
env:
|
|
PackageWorkdir: netbird_windows_${{ matrix.arch }}
|
|
downloadPath: '${{ github.workspace }}\temp'
|
|
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(.*)$'
|
|
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Add 7-Zip to PATH
|
|
run: echo "C:\Program Files\7-Zip" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
|
|
|
- name: Download release artifacts
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: release
|
|
path: release
|
|
|
|
- name: Download UI release artifacts
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: release-ui
|
|
path: release-ui
|
|
|
|
- name: Stage binaries into dist
|
|
run: |
|
|
$workdir = "dist\${{ env.PackageWorkdir }}"
|
|
New-Item -ItemType Directory -Force -Path $workdir | Out-Null
|
|
$client = Get-ChildItem -Recurse -Path release -Filter "netbird_*_windows_${{ matrix.arch }}.tar.gz" | Select-Object -First 1
|
|
$ui = Get-ChildItem -Recurse -Path release-ui -Filter "netbird-ui-windows_*_windows_${{ matrix.arch }}.tar.gz" | Select-Object -First 1
|
|
if (-not $client) { Write-Host "::error::client tarball not found for ${{ matrix.arch }}"; exit 1 }
|
|
if (-not $ui) { Write-Host "::error::ui tarball not found for ${{ matrix.arch }}"; exit 1 }
|
|
Write-Host "Client: $($client.FullName)"
|
|
Write-Host "UI: $($ui.FullName)"
|
|
tar -zvxf $client.FullName -C $workdir
|
|
tar -zvxf $ui.FullName -C $workdir
|
|
Get-ChildItem $workdir
|
|
|
|
- name: Download wintun
|
|
uses: carlosperate/download-file-action@v2
|
|
id: download-wintun
|
|
with:
|
|
file-url: https://pkgs.netbird.io/wintun/wintun-0.14.1.zip
|
|
file-name: wintun.zip
|
|
location: ${{ env.downloadPath }}
|
|
sha256: '07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51'
|
|
|
|
- name: Decompress wintun files
|
|
run: tar -zvxf "${{ steps.download-wintun.outputs.file-path }}" -C ${{ env.downloadPath }}
|
|
|
|
- name: Move wintun.dll into dist
|
|
run: mv ${{ env.downloadPath }}\wintun\bin\${{ matrix.wintun_arch }}\wintun.dll ${{ github.workspace }}\dist\${{ env.PackageWorkdir }}\
|
|
|
|
- name: Download EnVar plugin for NSIS
|
|
uses: carlosperate/download-file-action@v2
|
|
with:
|
|
file-url: https://nsis.sourceforge.io/mediawiki/images/7/7f/EnVar_plugin.zip
|
|
file-name: envar_plugin.zip
|
|
location: ${{ github.workspace }}
|
|
|
|
- name: Extract EnVar plugin
|
|
run: 7z x -o"${{ github.workspace }}/NSIS_Plugins" "${{ github.workspace }}/envar_plugin.zip"
|
|
|
|
- name: Download ShellExecAsUser plugin for NSIS (amd64 only)
|
|
uses: carlosperate/download-file-action@v2
|
|
if: matrix.arch == 'amd64'
|
|
with:
|
|
file-url: https://nsis.sourceforge.io/mediawiki/images/6/68/ShellExecAsUser_amd64-Unicode.7z
|
|
file-name: ShellExecAsUser_amd64-Unicode.7z
|
|
location: ${{ github.workspace }}
|
|
|
|
- name: Extract ShellExecAsUser plugin (amd64 only)
|
|
if: matrix.arch == 'amd64'
|
|
run: 7z x -o"${{ github.workspace }}/NSIS_Plugins" "${{ github.workspace }}/ShellExecAsUser_amd64-Unicode.7z"
|
|
|
|
- name: Set up Go for wails3 CLI
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version-file: "go.mod"
|
|
cache: false
|
|
|
|
- name: Install wails3 CLI
|
|
# Version derived from go.mod so the bootstrapper payload always
|
|
# matches the wails runtime the binary links against.
|
|
shell: bash
|
|
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: Stage WebView2 bootstrapper for installers
|
|
# Both client/installer.nsis and client/netbird.wxs reference
|
|
# client/MicrosoftEdgeWebview2Setup.exe. wails3 writes it there.
|
|
# The signing pipeline (netbirdio/sign-pipelines) does the same
|
|
# step for release builds; this mirrors it for PR sanity testing.
|
|
shell: bash
|
|
run: wails3 generate webview2bootstrapper -dir client
|
|
|
|
- name: Build NSIS installer
|
|
uses: joncloud/makensis-action@v3.3
|
|
with:
|
|
additional-plugin-paths: ${{ github.workspace }}/NSIS_Plugins/Plugins
|
|
script-file: client/installer.nsis
|
|
arguments: "/V4 /DARCH=${{ matrix.arch }}"
|
|
env:
|
|
APPVER: ${{ steps.semver_parser.outputs.major }}.${{ steps.semver_parser.outputs.minor }}.${{ steps.semver_parser.outputs.patch }}.${{ github.run_id }}
|
|
|
|
- name: Rename NSIS installer
|
|
run: mv netbird-installer.exe netbird_installer_test_windows_${{ matrix.arch }}.exe
|
|
|
|
- name: Install WiX
|
|
run: |
|
|
dotnet tool install --global wix --version 6.0.2
|
|
wix extension add WixToolset.Util.wixext/6.0.2
|
|
|
|
- name: Build MSI installer
|
|
env:
|
|
NETBIRD_VERSION: "${{ steps.semver_parser.outputs.fullversion }}"
|
|
run: wix build -arch ${{ matrix.arch == 'amd64' && 'x64' || 'arm64' }} -ext WixToolset.Util.wixext -o netbird_installer_test_windows_${{ matrix.arch }}.msi .\client\netbird.wxs -d ProcessorArchitecture=${{ matrix.arch == 'amd64' && 'x64' || 'arm64' }} -d ArchSuffix=${{ matrix.arch }}
|
|
|
|
- name: Upload installer artifacts
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: windows-installer-test-${{ matrix.arch }}
|
|
path: |
|
|
netbird_installer_test_windows_${{ matrix.arch }}.exe
|
|
netbird_installer_test_windows_${{ matrix.arch }}.msi
|
|
retention-days: 3
|
|
|
|
comment_release_artifacts:
|
|
name: Comment release artifacts
|
|
runs-on: ubuntu-latest
|
|
needs: [release, release_ui, release_ui_darwin]
|
|
if: ${{ always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository }}
|
|
permissions:
|
|
contents: read
|
|
issues: write
|
|
pull-requests: write
|
|
steps:
|
|
- name: Create or update PR comment
|
|
uses: actions/github-script@v7
|
|
env:
|
|
RELEASE_RESULT: ${{ needs.release.result }}
|
|
RELEASE_UI_RESULT: ${{ needs.release_ui.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_DARWIN_ARTIFACT_URL: ${{ needs.release_ui_darwin.outputs.release_ui_darwin_artifact_url }}
|
|
GHCR_IMAGES_MARKDOWN: ${{ needs.release.outputs.ghcr_images }}
|
|
with:
|
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
script: |
|
|
const marker = '<!-- netbird-release-artifacts -->';
|
|
const { owner, repo } = context.repo;
|
|
const issue_number = context.payload.pull_request.number;
|
|
const runUrl = `${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}`;
|
|
const shortSha = context.payload.pull_request.head.sha.slice(0, 7);
|
|
|
|
const artifactCell = (url, result) => {
|
|
if (url) return `[Download](${url})`;
|
|
return result && result !== 'success' ? `_Not available (${result})_` : '_Not available_';
|
|
};
|
|
|
|
const artifacts = [
|
|
['All release artifacts', process.env.RELEASE_ARTIFACT_URL, process.env.RELEASE_RESULT],
|
|
['Linux packages', process.env.LINUX_PACKAGES_ARTIFACT_URL, process.env.RELEASE_RESULT],
|
|
['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 macOS artifacts', process.env.RELEASE_UI_DARWIN_ARTIFACT_URL, process.env.RELEASE_UI_DARWIN_RESULT],
|
|
];
|
|
|
|
const artifactRows = artifacts
|
|
.map(([name, url, result]) => `| ${name} | ${artifactCell(url, result)} |`)
|
|
.join('\n');
|
|
|
|
const ghcrImages = (process.env.GHCR_IMAGES_MARKDOWN || '').trim() || '_No GHCR images were pushed._';
|
|
|
|
const body = [
|
|
marker,
|
|
'## Release artifacts',
|
|
'',
|
|
`Built for PR head \`${shortSha}\` in [workflow run #${process.env.GITHUB_RUN_NUMBER}](${runUrl}).`,
|
|
'',
|
|
'| Artifact | Link |',
|
|
'| --- | --- |',
|
|
artifactRows,
|
|
'',
|
|
'### GHCR images (amd64)',
|
|
ghcrImages,
|
|
'',
|
|
'_This comment is updated by the Release workflow. Artifact links expire according to the workflow retention policy._',
|
|
].join('\n');
|
|
|
|
const comments = await github.paginate(github.rest.issues.listComments, {
|
|
owner,
|
|
repo,
|
|
issue_number,
|
|
per_page: 100,
|
|
});
|
|
|
|
const previous = comments.find(comment =>
|
|
comment.user?.type === 'Bot' && comment.body?.includes(marker)
|
|
);
|
|
|
|
if (previous) {
|
|
await github.rest.issues.updateComment({
|
|
owner,
|
|
repo,
|
|
comment_id: previous.id,
|
|
body,
|
|
});
|
|
core.info(`Updated release artifacts comment ${previous.id}`);
|
|
} else {
|
|
const { data } = await github.rest.issues.createComment({
|
|
owner,
|
|
repo,
|
|
issue_number,
|
|
body,
|
|
});
|
|
core.info(`Created release artifacts comment ${data.id}`);
|
|
}
|
|
|
|
trigger_signer:
|
|
runs-on: ubuntu-latest
|
|
needs: [release, release_ui, release_ui_darwin, test_windows_installer]
|
|
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 }'
|