mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-03 12:09:09 +02:00
* [misc] Move the FreeBSD port test to release 15.1 FreeBSD 15.0 reached end of life on 2026-09-30 and the ports tree marks it unsupported since freebsd/freebsd-ports@ed90b23fe9 (2026-10-01), so `make package` refuses to run on the 15.0 VM and the FreeBSD Port job fails on every PR. The pinned vmactions/freebsd-vm v1.4.8 ships a 15.1 image, so only the release needs to move. * [misc] Run the FreeBSD unit tests on release 15.1 too The job installs binary packages instead of building from the ports tree, so it kept passing on the EOL 15.0 image, but the client should be tested on the same supported release the port is built on, and the EOL image is only served from the archive mirror from now on.
1089 lines
48 KiB
YAML
1089 lines
48 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- "v*"
|
|
branches:
|
|
- main
|
|
- "release-*"
|
|
pull_request:
|
|
|
|
env:
|
|
SIGN_PIPE_VER: "v0.1.8"
|
|
GORELEASER_VER: "v2.16.0"
|
|
PRODUCT_NAME: "NetBird"
|
|
COPYRIGHT: "NetBird GmbH"
|
|
flags: ""
|
|
SKIP_PUBLISH: "true"
|
|
SKIP_DOCKER_PUSH: "false"
|
|
|
|
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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Generate FreeBSD port diff
|
|
run: bash -x release_files/freebsd-port-diff.sh
|
|
|
|
- name: Generate FreeBSD port issue body
|
|
run: bash -x 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: Read Go version from go.mod
|
|
id: goversion
|
|
run: echo "version=$(awk '/^go / {print $2}' go.mod)" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Test FreeBSD port
|
|
if: steps.check_diff.outputs.diff_exists == 'true'
|
|
env:
|
|
GO_VERSION: ${{ steps.goversion.outputs.version }}
|
|
uses: vmactions/freebsd-vm@b84ab5559b5a1bb4b8ee2737d2506a16e1737636 # v1.4.8
|
|
with:
|
|
usesh: true
|
|
copyback: false
|
|
release: "15.1"
|
|
envs: "GO_VERSION"
|
|
prepare: |
|
|
# Install required packages
|
|
pkg install -y git curl portlint
|
|
|
|
# Install Go for building
|
|
GO_TARBALL="go${GO_VERSION}.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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
|
|
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 }}
|
|
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
|
|
|
|
if [[ "x-${{ github.repository }}" != "x-netbirdio/netbird" ]]; then
|
|
echo "SKIP_DOCKER_PUSH=true" >> $GITHUB_ENV
|
|
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/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
|
|
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: run openapi generator
|
|
run: bash shared/management/http/api/generate.sh
|
|
- name: check git status
|
|
run: git --no-pager diff --exit-code
|
|
- name: Generate RPM changelog from git tags
|
|
# nfpm embeds changelog.yml into the RPM; Red Hat software certification
|
|
# requires a changelog. Generated, not committed (see .gitignore).
|
|
# chglog is a go.mod tool directive, so go.sum pins it and its deps.
|
|
run: bash release_files/rpm-changelog.sh
|
|
- name: Fill the RPM ISA provide version
|
|
# nfpm cannot emit rpmbuild's ISA provide and GoReleaser cannot template it.
|
|
run: bash release_files/rpm-provides.sh
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version: '22'
|
|
- name: Install proxy web dependencies for license collection
|
|
# proxy/collect-licenses.sh reads the UI's license terms from node_modules.
|
|
working-directory: proxy/web
|
|
run: npm ci --ignore-scripts
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 #v4.1.0
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 #v4.1.0
|
|
- name: Login to Docker hub
|
|
if: github.event_name != 'pull_request'
|
|
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
|
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@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
|
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@b66839b # v1.7.0
|
|
- 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@5daf1e915a5f0af01ddbcd89a43b8061ff4f1a89 # v7.2.2
|
|
with:
|
|
version: ${{ env.GORELEASER_VER }}
|
|
args: release --config .goreleaser.generated.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 }}
|
|
# One per nfpm id: GoReleaser looks the passphrase up as NFPM_<ID>_PASSPHRASE.
|
|
NFPM_NETBIRD_RPM_AMD64_PASSPHRASE: ${{ secrets.GPG_RPM_PASSPHRASE }}
|
|
NFPM_NETBIRD_RPM_ARM64_PASSPHRASE: ${{ secrets.GPG_RPM_PASSPHRASE }}
|
|
NFPM_NETBIRD_RPM_ARM_PASSPHRASE: ${{ secrets.GPG_RPM_PASSPHRASE }}
|
|
NFPM_NETBIRD_RPM_386_PASSPHRASE: ${{ secrets.GPG_RPM_PASSPHRASE }}
|
|
SKIP_PUBLISH: ${{ env.SKIP_PUBLISH }}
|
|
SKIP_DOCKER_PUSH: ${{ env.SKIP_DOCKER_PUSH }}
|
|
- 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' || startsWith(github.ref, 'refs/heads/release-')))
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
# $GITHUB_REF / $GITHUB_EVENT_NAME are read from the runner
|
|
# environment rather than substituted into this script with the
|
|
# workflow expression syntax: branch names may legally contain
|
|
# $(…), and interpolating github.ref would execute it.
|
|
resolve_tags() {
|
|
if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
|
|
echo "pr-${{ github.event.pull_request.number }}"
|
|
elif [[ "$GITHUB_REF" == "refs/heads/main" ]]; then
|
|
echo "main sha-$(git rev-parse --short HEAD)"
|
|
else
|
|
# Release branches get an immutable sha-* tag only — the floating
|
|
# "main" tag must never move from a release branch.
|
|
echo "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 variant=""
|
|
img_name="${src%%:*}"
|
|
# Variants share a repository with their default image, so keep
|
|
# their tag suffixes. Order matters: the first matching pattern wins.
|
|
case "$src" in
|
|
*-rootless-ubi-amd64) variant="-rootless-ubi" ;;
|
|
*-rootless-amd64) variant="-rootless" ;;
|
|
*-ubi-amd64) variant="-ubi" ;;
|
|
esac
|
|
for tag in $(resolve_tags); do
|
|
dst="${img_name}:${tag}${variant}"
|
|
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
|
|
|
|
# dockers_v2 artifacts have no top-level goarch field, so match the
|
|
# per-platform -amd64 tag suffix instead; it works for both the old
|
|
# dockers and the new dockers_v2 image naming.
|
|
mapfile -t src_images < <(
|
|
jq -r '.[] | select(.type == "Docker Image") | .name | select(startswith("ghcr.io/") and endswith("-amd64"))' /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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
|
|
with:
|
|
name: release
|
|
path: dist/
|
|
retention-days: 7
|
|
- name: upload linux packages
|
|
id: upload_linux_packages
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
|
|
with:
|
|
name: linux-packages
|
|
path: dist/netbird_linux**
|
|
retention-days: 7
|
|
- name: upload windows packages
|
|
id: upload_windows_packages
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
|
|
with:
|
|
name: windows-packages
|
|
path: dist/netbird_windows**
|
|
retention-days: 7
|
|
- name: upload macos packages
|
|
id: upload_macos_packages
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
|
|
with:
|
|
name: macos-packages
|
|
path: dist/netbird_darwin**
|
|
retention-days: 7
|
|
|
|
# Certify and publish the rootless UBI client image in the Red Hat Ecosystem
|
|
# Catalog. Stable tags only: goreleaser pushes <version>-rootless-ubi to
|
|
# ghcr.io in the release job above, and preflight submits every architecture
|
|
# of that manifest list to Pyxis. Auto-publish on the component makes the new
|
|
# version public once certification passes.
|
|
redhat_certification:
|
|
name: "Red Hat / Certify rootless UBI image"
|
|
needs: release
|
|
if: |
|
|
github.repository == 'netbirdio/netbird' &&
|
|
startsWith(github.ref, 'refs/tags/v') &&
|
|
!contains(github.ref_name, '-')
|
|
runs-on: ubuntu-24.04
|
|
permissions:
|
|
contents: read
|
|
env:
|
|
PREFLIGHT_VERSION: "1.21.0"
|
|
# sha256 of preflight-linux-amd64 from the 1.21.0 GitHub release.
|
|
# Red Hat publishes no checksum file, so the value is pinned here.
|
|
PREFLIGHT_SHA256: "5e653135503c72f8702bbe31d7643197d12937c68086879133dd6b9650a9a449"
|
|
IMAGE_REPOSITORY: "ghcr.io/netbirdio/netbird"
|
|
# Component "NetBird Client Container Image (rootless)" in Partner Connect.
|
|
# Override with the REDHAT_CERT_COMPONENT_ID repository variable if it changes.
|
|
DEFAULT_COMPONENT_ID: "6aa3ca4b4676aefdf07aaa97"
|
|
steps:
|
|
- name: Resolve image reference
|
|
id: image
|
|
env:
|
|
INPUT_VERSION: ${{ github.ref_name }}
|
|
run: |
|
|
set -euo pipefail
|
|
version="${INPUT_VERSION#v}"
|
|
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
echo "::error::Only stable x.y.z versions are certified, got '${INPUT_VERSION}'"
|
|
exit 1
|
|
fi
|
|
echo "version=${version}" >> "$GITHUB_OUTPUT"
|
|
echo "ref=${IMAGE_REPOSITORY}:${version}-rootless-ubi" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Verify the multi-arch image is on ghcr.io
|
|
env:
|
|
IMAGE_REF: ${{ steps.image.outputs.ref }}
|
|
run: |
|
|
set -euo pipefail
|
|
docker buildx imagetools inspect "$IMAGE_REF" --raw > manifest.json
|
|
for arch in amd64 arm64; do
|
|
if ! jq -e --arg a "$arch" '.manifests[] | select(.platform.architecture == $a)' manifest.json > /dev/null; then
|
|
echo "::error::${IMAGE_REF} has no ${arch} manifest"
|
|
exit 1
|
|
fi
|
|
done
|
|
echo "Manifest list for ${IMAGE_REF}:"
|
|
jq -r '.manifests[] | "\(.platform.os)/\(.platform.architecture) \(.digest)"' manifest.json
|
|
|
|
- name: Install preflight
|
|
run: |
|
|
set -euo pipefail
|
|
curl -fsSL --proto '=https' --proto-redir '=https' -o preflight \
|
|
"https://github.com/redhat-openshift-ecosystem/openshift-preflight/releases/download/${PREFLIGHT_VERSION}/preflight-linux-amd64"
|
|
echo "${PREFLIGHT_SHA256} preflight" | sha256sum -c -
|
|
chmod +x preflight
|
|
./preflight --version
|
|
|
|
- name: Run preflight checks and submit to Red Hat
|
|
env:
|
|
IMAGE_REF: ${{ steps.image.outputs.ref }}
|
|
PFLT_PYXIS_API_TOKEN: ${{ secrets.PYXIS_API_TOKEN }}
|
|
PFLT_CERTIFICATION_COMPONENT_ID: ${{ vars.REDHAT_CERT_COMPONENT_ID || env.DEFAULT_COMPONENT_ID }}
|
|
PFLT_ARTIFACTS: artifacts
|
|
PFLT_LOGFILE: artifacts/preflight.log
|
|
PFLT_LOGLEVEL: info
|
|
PFLT_JUNIT: "true"
|
|
run: |
|
|
set -euo pipefail
|
|
# No --platform: preflight walks the manifest list and submits every
|
|
# architecture in one run, grouped under one manifest-list digest.
|
|
./preflight check container "$IMAGE_REF" --submit
|
|
|
|
- name: Fail if any check did not pass
|
|
run: |
|
|
set -euo pipefail
|
|
shopt -s nullglob
|
|
results=(artifacts/results.json artifacts/*/results.json)
|
|
if [[ ${#results[@]} -eq 0 ]]; then
|
|
echo "::error::preflight produced no results.json"
|
|
exit 1
|
|
fi
|
|
status=0
|
|
for f in "${results[@]}"; do
|
|
arch="$(basename "$(dirname "$f")")"
|
|
passed="$(jq -r '.passed' "$f")"
|
|
failed="$(jq -r '[.results.failed[]?.name] | join(", ")' "$f")"
|
|
echo "${arch}: passed=${passed} ${failed:+failed checks: ${failed}}"
|
|
[[ "$passed" == "true" ]] || status=1
|
|
done
|
|
exit $status
|
|
|
|
- name: Upload preflight artifacts
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: redhat-preflight-${{ steps.image.outputs.version }}
|
|
path: artifacts/
|
|
retention-days: 30
|
|
|
|
- name: Wait for Pyxis to mark both architectures certified
|
|
env:
|
|
VERSION: ${{ steps.image.outputs.version }}
|
|
PFLT_PYXIS_API_TOKEN: ${{ secrets.PYXIS_API_TOKEN }}
|
|
COMPONENT_ID: ${{ vars.REDHAT_CERT_COMPONENT_ID || env.DEFAULT_COMPONENT_ID }}
|
|
run: |
|
|
set -euo pipefail
|
|
tag="${VERSION}-rootless-ubi"
|
|
url="https://catalog.redhat.com/api/containers/v1/projects/certification/id/${COMPONENT_ID}/images?page_size=100"
|
|
for attempt in $(seq 1 20); do
|
|
certified="$(curl -fsS --proto '=https' --proto-redir '=https' -H "X-API-KEY: ${PFLT_PYXIS_API_TOKEN}" "$url" \
|
|
| jq -r --arg t "$tag" '[.data[] | select(.repositories[]?.tags[]?.name == $t) | select(.certified == true) | .architecture] | unique | join(",")')"
|
|
echo "attempt ${attempt}: certified architectures for ${tag}: ${certified:-none}"
|
|
if [[ "$certified" == "amd64,arm64" ]]; then
|
|
echo "Both architectures certified. Auto-publish is enabled on the component, so the catalog updates on its own."
|
|
exit 0
|
|
fi
|
|
sleep 30
|
|
done
|
|
echo "::warning::Pyxis has not marked both architectures certified after 10 minutes. Check https://connect.redhat.com/component/view/${COMPONENT_ID}/images"
|
|
|
|
release_ui:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
release_ui_artifact_url: ${{ steps.upload_release_ui.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-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@v7
|
|
with:
|
|
node-version: '22'
|
|
|
|
- name: Set up pnpm
|
|
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
|
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 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@b66839b # v1.7.0
|
|
- 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@5daf1e915a5f0af01ddbcd89a43b8061ff4f1a89 # v7.2.2
|
|
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 }}
|
|
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
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
|
|
with:
|
|
name: release-ui
|
|
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
|
|
# Restore-only from the release_ui cache written by trusted runs; the
|
|
# module cache is identical (same go.sum) and stale build-cache
|
|
# entries just miss.
|
|
uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
|
|
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@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version: '22'
|
|
|
|
- name: Set up pnpm
|
|
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.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.
|
|
# -tags gtk3: the CLI links the wails runtime's cgo packages, and the
|
|
# default tags request gtk4/webkitgtk-6.0 pkg-config entries that do
|
|
# not exist on ubuntu-22.04.
|
|
run: |
|
|
WAILS_VERSION=$(go list -m -f '{{.Version}}' github.com/wailsapp/wails/v3)
|
|
go install -tags gtk3 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:
|
|
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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
with:
|
|
fetch-depth: 0 # It is required for GoReleaser to work properly
|
|
persist-credentials: false
|
|
- 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-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@v7
|
|
with:
|
|
node-version: '22'
|
|
- name: Set up pnpm
|
|
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
|
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@5daf1e915a5f0af01ddbcd89a43b8061ff4f1a89 # v7.2.2
|
|
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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
|
|
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: Checkout
|
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Parse semver string
|
|
id: semver_parser
|
|
uses: netbirdio/shared-actions/actions/parse-semver@be5df6047383da2236e02243cceb857d8567c27e # v0.0.2
|
|
|
|
- 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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: release
|
|
path: release
|
|
|
|
- name: Download UI release artifacts
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
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
|
|
id: download-wintun
|
|
uses: netbirdio/shared-actions/actions/win-download-and-verify@be5df6047383da2236e02243cceb857d8567c27e # v0.0.2
|
|
with:
|
|
url: https://pkgs.netbird.io/wintun/wintun-0.14.1.zip
|
|
destination: ${{ env.downloadPath }}\wintun.zip
|
|
sha256: 07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51
|
|
|
|
- name: Decompress wintun files
|
|
run: tar -xvf "${{ env.downloadPath }}\wintun.zip" -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: netbirdio/shared-actions/actions/win-download-and-verify@be5df6047383da2236e02243cceb857d8567c27e # v0.0.2
|
|
with:
|
|
url: https://pkgs.netbird.io/nsis/EnVar_plugin.zip
|
|
destination: ${{ github.workspace }}\envar_plugin.zip
|
|
sha256: e9aa92de351345ed82795251d838f1ae9041ba35af9d381a5780c7843b01f56a
|
|
|
|
- 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)
|
|
if: matrix.arch == 'amd64'
|
|
uses: netbirdio/shared-actions/actions/win-download-and-verify@be5df6047383da2236e02243cceb857d8567c27e # v0.0.2
|
|
with:
|
|
url: https://pkgs.netbird.io/nsis/ShellExecAsUser_amd64-Unicode.7z
|
|
destination: ${{ github.workspace }}\ShellExecAsUser_amd64-Unicode.7z
|
|
sha256: 0a55ea25c7330a92cec028eda8afcaf1b1a7092e0dfb77c21c8f654564b4ff9d
|
|
|
|
- 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@v6
|
|
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
|
|
shell: pwsh
|
|
env:
|
|
APPVER: ${{ steps.semver_parser.outputs.major }}.${{ steps.semver_parser.outputs.minor }}.${{ steps.semver_parser.outputs.patch }}.${{ github.run_id }}
|
|
run: |
|
|
$nsisPluginDir = "C:\Program Files (x86)\NSIS\Plugins\x86-unicode"
|
|
$srcPlugins = "${{ github.workspace }}\NSIS_Plugins\Plugins"
|
|
Get-ChildItem -Path $srcPlugins -Recurse -Filter *.dll |
|
|
Copy-Item -Destination $nsisPluginDir -Force
|
|
& "C:\Program Files (x86)\NSIS\makensis.exe" /V4 "/DARCH=${{ matrix.arch }}" client\installer.nsis
|
|
if ($LASTEXITCODE -ne 0) { throw "makensis failed with exit code $LASTEXITCODE" }
|
|
|
|
- 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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
|
|
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_gtk3, 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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
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:
|
|
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 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],
|
|
];
|
|
|
|
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_gtk3, release_ui_darwin, test_windows_installer]
|
|
if: startsWith(github.ref, 'refs/tags/')
|
|
steps:
|
|
- name: Trigger binaries sign pipelines
|
|
uses: benc-uk/workflow-dispatch@31e2b3319479a63f0ab15bf800eff9e913504e26 # v1.3.2
|
|
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 }'
|