Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 65 additions & 11 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,25 @@ on:
installer_base_name:
required: true
type: string
linux_arch:
required: false
type: string
default: all

permissions:
contents: "read"

jobs:
build-linux:
strategy:
fail-fast: false
matrix: ${{ fromJSON(inputs.linux_arch == 'arm64' && '{"include":[{"arch":"arm64","runner":"ubuntu-24.04-arm"}]}' || inputs.linux_arch == 'amd64' && '{"include":[{"arch":"amd64","runner":"ubuntu-latest"}]}' || '{"include":[{"arch":"amd64","runner":"ubuntu-latest"},{"arch":"arm64","runner":"ubuntu-24.04-arm"}]}') }}
env:
BUILD_TYPE: ${{ inputs.build_type }}
VERSION: ${{ inputs.version }}
runs-on: ubuntu-latest
TARGET_ARCH: ${{ matrix.arch }}
FULL_INSTALLER_NAME: ${{ inputs.installer_base_name }}${{ inputs.build_type != 'production' && format('-{0}', inputs.build_type) || '' }}${{ matrix.arch == 'arm64' && '-arm64' || '' }}
runs-on: ${{ matrix.runner }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand Down Expand Up @@ -52,9 +64,9 @@ jobs:
continue-on-error: true
with:
path: ~/.cache/apt/archives
key: ${{ runner.os }}-apt-archives-v1-${{ hashFiles('.github/workflows/build-linux.yml') }}
key: ${{ runner.os }}-${{ matrix.arch }}-apt-archives-v1-${{ hashFiles('.github/workflows/build-linux.yml') }}
restore-keys: |
${{ runner.os }}-apt-archives-v1-
${{ runner.os }}-${{ matrix.arch }}-apt-archives-v1-

- name: Install Linux dependencies
run: |
Expand Down Expand Up @@ -95,13 +107,36 @@ jobs:
- name: Install the ninja build tool
uses: seanmiddleditch/gha-setup-ninja@master

- name: Install Flutter
- name: Load Flutter version
shell: bash
run: |
set -euo pipefail
FLUTTER_VERSION=$(grep -E '^[[:space:]]*flutter:' .github/flutter-version.yaml | sed -E 's/.*"([^"]+)".*/\1/')
if [[ -z "$FLUTTER_VERSION" ]]; then
echo "Failed to parse Flutter version from .github/flutter-version.yaml" >&2
exit 1
fi
echo "FLUTTER_VERSION=$FLUTTER_VERSION" >> "$GITHUB_ENV"

- name: Install Flutter (amd64)
uses: subosito/flutter-action@v2.22.0
if: ${{ matrix.arch == 'amd64' }}
with:
channel: stable
flutter-version-file: .github/flutter-version.yaml
cache: true

- name: Install Flutter (arm64 fallback)
if: ${{ matrix.arch == 'arm64' }}
shell: bash
run: |
set -euo pipefail
FLUTTER_HOME="$HOME/flutter"
git clone --depth 1 --branch "$FLUTTER_VERSION" https://github.com/flutter/flutter.git "$FLUTTER_HOME"
echo "$FLUTTER_HOME/bin" >> "$GITHUB_PATH"
export PATH="$FLUTTER_HOME/bin:$PATH"
flutter --version

- name: Install dependencies
run: make install-linux-deps

Expand All @@ -122,16 +157,19 @@ jobs:
BUILD_TYPE: ${{ inputs.build_type }}
VERSION: ${{ inputs.version }}
INSTALLER_NAME: ${{ inputs.installer_base_name }}
LINUX_TARGET_ARCH: ${{ matrix.arch }}
LINUX_CC_AMD64: gcc
LINUX_CC_ARM64: gcc

- name: Verify Linux package contents
run: |
./scripts/ci/verify_linux_package.sh "${{ inputs.installer_base_name }}${{ inputs.build_type != 'production' && format('-{0}', inputs.build_type) || '' }}.deb"
./scripts/ci/verify_linux_package.sh "./${{ env.FULL_INSTALLER_NAME }}.deb" "${{ matrix.arch }}"

- name: Install .deb and verify postinst started daemon
shell: bash
run: |
set -euxo pipefail
deb="./${{ inputs.installer_base_name }}${{ inputs.build_type != 'production' && format('-{0}', inputs.build_type) || '' }}.deb"
deb="./${{ env.FULL_INSTALLER_NAME }}.deb"
test -f "$deb"
sudo apt-get install -y "$deb"

Expand Down Expand Up @@ -180,9 +218,25 @@ jobs:
run: |
set -euxo pipefail
TEST_START_UTC="$(date -u '+%Y-%m-%d %H:%M:%S')"
sg lantern -c "env PATH=$PATH HOME=$HOME xvfb-run -a flutter test integration_test/vpn/linux_connect_smoke_test.dart -d linux --dart-define=DISABLE_SYSTEM_TRAY=true --dart-define=ENABLE_IP_CHECK=true"
ENABLE_IP_CHECK=false
if [[ "${TARGET_ARCH}" == "amd64" ]]; then
ENABLE_IP_CHECK=true
fi
set +e
sg lantern -c "env PATH=$PATH HOME=$HOME timeout --signal=TERM --kill-after=30s 8m xvfb-run -a flutter test integration_test/vpn/linux_connect_smoke_test.dart -d linux --reporter=expanded --dart-define=DISABLE_SYSTEM_TRAY=true --dart-define=ENABLE_IP_CHECK=${ENABLE_IP_CHECK}"
SMOKE_EXIT=$?
set -e

sudo journalctl -u lanternd.service --since "$TEST_START_UTC" --no-pager > /tmp/lanternd-journal-ui-smoke.log
if [[ "$SMOKE_EXIT" -eq 124 ]]; then
echo "Linux UI connect/disconnect smoke timed out after 8m"
tail -n 200 /tmp/lanternd-journal-ui-smoke.log || true
exit 1
fi
if [[ "$SMOKE_EXIT" -ne 0 ]]; then
tail -n 200 /tmp/lanternd-journal-ui-smoke.log || true
exit "$SMOKE_EXIT"
fi

if ! grep -Eq 'IPC request.*path=/service/start' /tmp/lanternd-journal-ui-smoke.log; then
echo "Missing /service/start IPC request in lanternd journal"
Expand All @@ -199,13 +253,13 @@ jobs:
- name: Upload Linux build
uses: actions/upload-artifact@v4
with:
name: lantern-installer-rpm
path: ${{ inputs.installer_base_name }}${{ inputs.build_type != 'production' && format('-{0}', inputs.build_type) || '' }}.rpm
name: lantern-installer-rpm-${{ matrix.arch }}
path: ${{ env.FULL_INSTALLER_NAME }}.rpm
retention-days: 2

- name: Upload Linux build
uses: actions/upload-artifact@v4
with:
name: lantern-installer-deb
path: ${{ inputs.installer_base_name }}${{ inputs.build_type != 'production' && format('-{0}', inputs.build_type) || '' }}.deb
name: lantern-installer-deb-${{ matrix.arch }}
path: ${{ env.FULL_INSTALLER_NAME }}.deb
retention-days: 2
26 changes: 26 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ on:
required: false
type: string
default: "linux"
linux_arch:
description: "Linux arch to build when Linux is included"
required: false
type: choice
options:
- all
- amd64
- arm64
default: all
cleanup_on_failure:
description: "Delete draft release on failure"
required: false
Expand Down Expand Up @@ -49,6 +58,7 @@ jobs:
version: ${{ steps.meta.outputs.version }}
installer_base_name: ${{ steps.meta.outputs.installer_base_name }}
platform: ${{ steps.meta.outputs.platform }}
linux_arch: ${{ steps.meta.outputs.linux_arch }}
is_test_run: ${{ steps.meta.outputs.is_test_run }}
steps:
- name: Checkout repo
Expand All @@ -72,12 +82,14 @@ jobs:
VERSION=$(./scripts/ci/version.sh generate nightly)
RELEASE_TAG="v${VERSION}"
PLATFORM="all"
LINUX_ARCH="all"
;;

workflow_dispatch)
# Manual trigger
BUILD_TYPE="${{ github.event.inputs.build_type }}"
PLATFORM="${{ github.event.inputs.platforms }}"
LINUX_ARCH="${{ github.event.inputs.linux_arch }}"

case "$BUILD_TYPE" in
nightly|beta)
Expand Down Expand Up @@ -135,6 +147,11 @@ jobs:
all) ;;
*) VERSION="${VERSION%-$PLATFORM}" ;;
esac

case "$PLATFORM" in
all|linux) LINUX_ARCH="all" ;;
*) LINUX_ARCH="amd64" ;;
esac
;;
esac

Expand All @@ -147,6 +164,7 @@ jobs:
echo " Release Tag: $RELEASE_TAG"
echo " Version: $VERSION"
echo " Platform: $PLATFORM"
echo " Linux Arch: $LINUX_ARCH"
echo " Installer: $INSTALLER_BASE_NAME"

# Test run: workflow_dispatch from non-default branch
Expand All @@ -163,6 +181,7 @@ jobs:
echo "release_tag=$RELEASE_TAG" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "platform=$PLATFORM" >> $GITHUB_OUTPUT
echo "linux_arch=$LINUX_ARCH" >> $GITHUB_OUTPUT
echo "installer_base_name=$INSTALLER_BASE_NAME" >> $GITHUB_OUTPUT
echo "is_test_run=$IS_TEST_RUN" >> $GITHUB_OUTPUT

Expand Down Expand Up @@ -266,6 +285,7 @@ jobs:
version: ${{ needs.set-metadata.outputs.version }}
build_type: ${{ needs.set-metadata.outputs.build_type }}
installer_base_name: ${{ needs.set-metadata.outputs.installer_base_name }}
linux_arch: ${{ needs.set-metadata.outputs.linux_arch }}

build-ios:
needs: [set-metadata, release-create, release-approval]
Expand Down Expand Up @@ -365,6 +385,7 @@ jobs:
RELEASE_TAG: ${{ needs.set-metadata.outputs.release_tag }}
INSTALLER_BASE_NAME: ${{ needs.set-metadata.outputs.installer_base_name }}
PLATFORM: ${{ needs.set-metadata.outputs.platform }}
LINUX_ARCH: ${{ needs.set-metadata.outputs.linux_arch }}
BUCKET: ${{ vars.S3_RELEASES_BUCKET }}
GITHUB_REF_NAME: ${{ github.ref_name }}
steps:
Expand Down Expand Up @@ -445,6 +466,7 @@ jobs:
RELEASE_TAG: ${{ needs.set-metadata.outputs.release_tag }}
INSTALLER_BASE_NAME: ${{ needs.set-metadata.outputs.installer_base_name }}
PLATFORM: ${{ needs.set-metadata.outputs.platform }}
LINUX_ARCH: ${{ needs.set-metadata.outputs.linux_arch }}
BUCKET: ${{ vars.S3_RELEASES_BUCKET }}
GITHUB_REF_NAME: ${{ github.ref_name }}
steps:
Expand Down Expand Up @@ -487,6 +509,10 @@ jobs:
upload_if_exists "lantern-installer-dmg/${FULL_NAME}.dmg"
upload_if_exists "lantern-installer-exe/${FULL_NAME}.exe"
upload_if_exists "lantern-installer-apk/${FULL_NAME}.apk"
upload_if_exists "lantern-installer-deb-amd64/${FULL_NAME}.deb"
upload_if_exists "lantern-installer-rpm-amd64/${FULL_NAME}.rpm"
upload_if_exists "lantern-installer-deb-arm64/${FULL_NAME}-arm64.deb"
upload_if_exists "lantern-installer-rpm-arm64/${FULL_NAME}-arm64.rpm"
upload_if_exists "lantern-installer-deb/${FULL_NAME}.deb"
upload_if_exists "lantern-installer-rpm/${FULL_NAME}.rpm"
upload_if_exists "lantern-installer-ipa/${FULL_NAME}.ipa"
Expand Down
57 changes: 37 additions & 20 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,20 @@ LINUX_LIB := $(LANTERN_LIB_NAME).so
LINUX_LIB_AMD64 := $(BIN_DIR)/linux-amd64/$(LANTERN_LIB_NAME).so
LINUX_LIB_ARM64 := $(BIN_DIR)/linux-arm64/$(LANTERN_LIB_NAME).so
LINUX_LIB_BUILD := $(BIN_DIR)/linux/$(LINUX_LIB)
LINUX_INSTALLER_DEB := $(INSTALLER_NAME)$(if $(filter-out production,$(BUILD_TYPE)),-$(BUILD_TYPE)).deb
LINUX_INSTALLER_RPM := $(INSTALLER_NAME)$(if $(filter-out production,$(BUILD_TYPE)),-$(BUILD_TYPE)).rpm
LINUX_INSTALLER_ARCH := $(INSTALLER_NAME)$(if $(filter-out production,$(BUILD_TYPE)),-$(BUILD_TYPE)).pkg.tar.zst
LINUX_TARGET_ARCH ?= amd64
LINUX_PACKAGE_ARCH_SUFFIX := $(if $(filter amd64,$(LINUX_TARGET_ARCH)),,-$(LINUX_TARGET_ARCH))
LINUX_INSTALLER_DEB := $(INSTALLER_NAME)$(if $(filter-out production,$(BUILD_TYPE)),-$(BUILD_TYPE))$(LINUX_PACKAGE_ARCH_SUFFIX).deb
LINUX_INSTALLER_RPM := $(INSTALLER_NAME)$(if $(filter-out production,$(BUILD_TYPE)),-$(BUILD_TYPE))$(LINUX_PACKAGE_ARCH_SUFFIX).rpm
LINUX_INSTALLER_ARCH := $(INSTALLER_NAME)$(if $(filter-out production,$(BUILD_TYPE)),-$(BUILD_TYPE))$(LINUX_PACKAGE_ARCH_SUFFIX).pkg.tar.zst
LINUX_SERVICE_NAME := lanternd
LINUX_SERVICE_SRC := $(RADIANCE_REPO)/cmd/lanternd
LINUX_SERVICE_BUILD_AMD64 := $(BIN_DIR)/linux-amd64/$(LINUX_SERVICE_NAME)
LINUX_SERVICE_BUILD_ARM64 := $(BIN_DIR)/linux-arm64/$(LINUX_SERVICE_NAME)
LINUX_SERVICE_BUILD_TARGET := $(BIN_DIR)/linux-$(LINUX_TARGET_ARCH)/$(LINUX_SERVICE_NAME)
LINUX_BUNDLE_DIR_X64 := build/linux/x64/release/bundle
LINUX_BUNDLE_DIR_ARM64 := build/linux/arm64/release/bundle
LINUX_CC_AMD64 ?= x86_64-linux-gnu-gcc
LINUX_CC_ARM64 ?= aarch64-linux-gnu-gcc
LINUX_PKG_ROOT := linux/packaging
LINUX_SERVICE_DST := $(LINUX_PKG_ROOT)/usr/sbin
LINUX_PKG_SYSTEMD_DIR := $(LINUX_PKG_ROOT)/usr/lib/systemd/system
Expand Down Expand Up @@ -260,20 +267,20 @@ install-linux-deps:
linux-arm64: $(LINUX_LIB_ARM64)

$(LINUX_LIB_ARM64): $(GO_SOURCES)
CC=aarch64-linux-gnu-gcc GOARCH=arm64 LIB_NAME=$@ make desktop-lib
CC=$(LINUX_CC_ARM64) GOOS=linux GOARCH=arm64 LIB_NAME=$@ $(MAKE) desktop-lib
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The arm64 library build writes output to bin/linux-arm64/... but never creates that directory first. On clean builds this will fail with a "no such file or directory" error when go build -o tries to write the shared library. [resource leak]

Severity Level: Critical 🚨
- ❌ Linux arm64 release build can fail immediately.
- ❌ CI pipeline blocks Linux artifact production.
Suggested change
CC=$(LINUX_CC_ARM64) GOOS=linux GOARCH=arm64 LIB_NAME=$@ $(MAKE) desktop-lib
$(call MKDIR_P,$(dir $@))
Steps of Reproduction ✅
1. Trigger Linux CI build at `.github/workflows/build-linux.yml:155`, which runs `make
linux-release-ci` with `LINUX_TARGET_ARCH=${{ matrix.arch }}` (`:160`), and matrix
includes `arm64` (`:27`).

2. In `Makefile`, `linux-release-ci` (`:318`) depends on `linux` (`:279`), which resolves
to `linux-arm64` and executes target rule `$(LINUX_LIB_ARM64):` at `:269-270`.

3. That rule directly calls `desktop-lib` at `Makefile:270` without creating `$(dir $@)`.
`desktop-lib` runs `go build ... -o $(LIB_NAME)` at `Makefile:177`.

4. On a clean checkout (`actions/checkout` at workflow `:35`), `bin/linux-arm64/` does not
exist, so writing `-o bin/linux-arm64/liblantern.so` fails with missing-directory error.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** Makefile
**Line:** 270:270
**Comment:**
	*Resource Leak: The arm64 library build writes output to `bin/linux-arm64/...` but never creates that directory first. On clean builds this will fail with a "no such file or directory" error when `go build -o` tries to write the shared library.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

Comment on lines 269 to +270
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "Linux lib target recipes:"
nl -ba Makefile | sed -n '266,281p'

echo
echo "desktop-lib recipe:"
nl -ba Makefile | sed -n '175,181p'

echo
echo "clean recipe:"
nl -ba Makefile | sed -n '618,624p'

Repository: coderabbit-test/lantern

Length of output: 152


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "=== Linux lib target recipes (lines 266-281) ==="
sed -n '266,281p' Makefile | cat -n

echo
echo "=== desktop-lib recipe (lines 175-181) ==="
sed -n '175,181p' Makefile | cat -n

echo
echo "=== clean recipe (lines 618-624) ==="
sed -n '618,624p' Makefile | cat -n

echo
echo "=== Checking for MKDIR_P definition ==="
grep -n "MKDIR_P" Makefile | head -20

Repository: coderabbit-test/lantern

Length of output: 2123


🏁 Script executed:

#!/bin/bash
echo "=== Searching for LINUX_LIB_ARM64 and LINUX_LIB_AMD64 definitions ==="
grep -n "LINUX_LIB_ARM64\|LINUX_LIB_AMD64" Makefile | grep -v "^\s*#" | head -30

echo
echo "=== Searching for BIN_DIR definition ==="
grep -n "^BIN_DIR" Makefile

echo
echo "=== Searching for LINUX_LIB definition ==="
grep -n "^LINUX_LIB" Makefile | head -10

Repository: coderabbit-test/lantern

Length of output: 750


Create the per-arch lib output directories before calling desktop-lib.

These targets write to nested paths like bin/linux-amd64/liblantern.so and bin/linux-arm64/liblantern.so, but neither recipe creates the parent directories. After a clean checkout or after make clean removes bin/*, the nested output paths will be missing before the build even starts, causing the go build step to fail. This pattern is already correctly implemented in other platform targets (Windows and Darwin lib builds use $(call MKDIR_P,$(dir $@))), but is missing here.

🛠️ Proposed fix
 $(LINUX_LIB_ARM64): $(GO_SOURCES)
+	$(call MKDIR_P,$(dir $@))
 	CC=$(LINUX_CC_ARM64) GOOS=linux GOARCH=arm64 LIB_NAME=$@ $(MAKE) desktop-lib

 $(LINUX_LIB_AMD64): $(GO_SOURCES)
+	$(call MKDIR_P,$(dir $@))
 	CC=$(LINUX_CC_AMD64) GOOS=linux GOARCH=amd64 LIB_NAME=$@ $(MAKE) desktop-lib
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Makefile` around lines 269 - 270, The recipe for the LINUX_LIB_ARM64 target
currently invokes the desktop-lib make target without ensuring the output
directory exists; update the LINUX_LIB_ARM64 recipe to create the parent
directory for $@ before calling desktop-lib (use the same pattern as the
Windows/Darwin targets, e.g. call the MKDIR_P macro on $(dir $@)) so the nested
path (e.g. bin/linux-arm64/) is created prior to running CC=$(LINUX_CC_ARM64)
GOOS=linux GOARCH=arm64 LIB_NAME=$@ $(MAKE) desktop-lib.


.PHONY: linux-amd64
linux-amd64: $(LINUX_LIB_AMD64)

$(LINUX_LIB_AMD64): $(GO_SOURCES)
CC=x86_64-linux-gnu-gcc GOARCH=amd64 LIB_NAME=$@ make desktop-lib
CC=$(LINUX_CC_AMD64) GOOS=linux GOARCH=amd64 LIB_NAME=$@ $(MAKE) desktop-lib
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The amd64 library build has the same missing-directory issue: it writes to bin/linux-amd64/... without ensuring the directory exists first. This breaks clean CI/local runs where that path has not been created yet. [possible bug]

Severity Level: Critical 🚨
- ❌ Linux amd64 release build can fail immediately.
- ❌ Primary CI release workflow becomes unstable.
Suggested change
CC=$(LINUX_CC_AMD64) GOOS=linux GOARCH=amd64 LIB_NAME=$@ $(MAKE) desktop-lib
$(call MKDIR_P,$(dir $@))
Steps of Reproduction ✅
1. Trigger Linux CI build at `.github/workflows/build-linux.yml:155` with
`matrix.arch=amd64` (`:27`, passed to `LINUX_TARGET_ARCH` at `:160`).

2. `linux-release-ci` (`Makefile:318`) depends on `linux` (`:279`), which resolves to
`linux-amd64` and runs rule `$(LINUX_LIB_AMD64):` at `:275-276`.

3. Rule line `Makefile:276` invokes `desktop-lib` directly; `desktop-lib` writes output
via `go build ... -o $(LIB_NAME)` (`Makefile:177`) and does not create parent directories.

4. In clean CI workspaces, `bin/linux-amd64/` is absent, so writing
`bin/linux-amd64/liblantern.so` can fail with "no such file or directory".
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** Makefile
**Line:** 276:276
**Comment:**
	*Possible Bug: The amd64 library build has the same missing-directory issue: it writes to `bin/linux-amd64/...` without ensuring the directory exists first. This breaks clean CI/local runs where that path has not been created yet.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎


.PHONY: linux
linux: linux-amd64
linux: linux-$(LINUX_TARGET_ARCH)
mkdir -p $(BIN_DIR)/linux
cp $(LINUX_LIB_AMD64) $(LINUX_LIB_BUILD)
cp $(BIN_DIR)/linux-$(LINUX_TARGET_ARCH)/$(LINUX_LIB) $(LINUX_LIB_BUILD)

.PHONY: linux-service-amd64 linux-service-arm64 stage-linux-service
.PHONY: linux-service-amd64 linux-service-arm64 linux-service stage-linux-service

linux-service-amd64: $(GO_SOURCES)
$(call MKDIR_P,$(dir $(LINUX_SERVICE_BUILD_AMD64)))
Expand All @@ -291,10 +298,12 @@ linux-service-arm64: $(GO_SOURCES)
-o $(LINUX_SERVICE_BUILD_ARM64) $(LINUX_SERVICE_SRC)
@echo "Built Linux service: $(LINUX_SERVICE_BUILD_ARM64)"

stage-linux-service: linux-service-amd64
linux-service: linux-service-$(LINUX_TARGET_ARCH)

stage-linux-service: linux-service
@echo "Staging systemd unit + service binary $(LINUX_PKG_ROOT)..."
$(call MKDIR_P,$(LINUX_SERVICE_DST))
$(call COPY_FILE,$(LINUX_SERVICE_BUILD_AMD64),$(LINUX_SERVICE_DST)/$(LINUX_SERVICE_NAME))
$(call COPY_FILE,$(LINUX_SERVICE_BUILD_TARGET),$(LINUX_SERVICE_DST)/$(LINUX_SERVICE_NAME))
$(call MKDIR_P,$(LINUX_PKG_SYSTEMD_DIR))
$(call COPY_FILE,$(LINUX_SYSTEMD_UNIT_SRC),$(LINUX_SYSTEMD_UNIT_DST))

Expand All @@ -309,21 +318,29 @@ linux-release: clean linux-release-ci
linux-release-ci: linux pubget gen
@echo "Building Flutter app (release) for Linux..."
flutter build linux --release $(DART_DEFINES)

cp $(LINUX_LIB_BUILD) build/linux/x64/release/bundle
$(MAKE) stage-linux-service
patchelf --set-rpath '$$ORIGIN/lib' build/linux/x64/release/bundle/lantern || true

@echo "Packaging deb, rpm, and archlinux with nfpm..."
VERSION=$(APP_VERSION) SYSTEMD_UNIT_SRC=$(LINUX_SYSTEMD_UNIT_DST) \
@if [ "$(LINUX_TARGET_ARCH)" = "arm64" ]; then \
BUNDLE_DIR="$(LINUX_BUNDLE_DIR_ARM64)"; \
else \
BUNDLE_DIR="$(LINUX_BUNDLE_DIR_X64)"; \
fi; \
if [ ! -d "$$BUNDLE_DIR" ]; then \
echo "Expected Linux bundle dir not found: $$BUNDLE_DIR"; \
exit 1; \
fi; \
echo "Using Linux bundle dir: $$BUNDLE_DIR"; \
cp "$(LINUX_LIB_BUILD)" "$$BUNDLE_DIR"; \
patchelf --set-rpath '$$ORIGIN/lib' "$$BUNDLE_DIR/lantern" || true; \
VERSION=$(APP_VERSION) GOARCH=$(LINUX_TARGET_ARCH) LINUX_BUNDLE_SRC="$$BUNDLE_DIR/" SYSTEMD_UNIT_SRC=$(LINUX_SYSTEMD_UNIT_DST) \
LANTERND_SRC=$(LINUX_SERVICE_DST)/$(LINUX_SERVICE_NAME) LANTERND_DST=/usr/sbin/$(LINUX_SERVICE_NAME) \
nfpm package -f $(LINUX_PKG_ROOT)/nfpm.yaml -p deb -t $(LINUX_INSTALLER_DEB)
VERSION=$(APP_VERSION) SYSTEMD_UNIT_SRC=$(LINUX_SYSTEMD_UNIT_DST) \
nfpm package -f $(LINUX_PKG_ROOT)/nfpm.yaml -p deb -t $(LINUX_INSTALLER_DEB); \
VERSION=$(APP_VERSION) GOARCH=$(LINUX_TARGET_ARCH) LINUX_BUNDLE_SRC="$$BUNDLE_DIR/" SYSTEMD_UNIT_SRC=$(LINUX_SYSTEMD_UNIT_DST) \
LANTERND_SRC=$(LINUX_SERVICE_DST)/$(LINUX_SERVICE_NAME) LANTERND_DST=/usr/sbin/$(LINUX_SERVICE_NAME) \
nfpm package -f $(LINUX_PKG_ROOT)/nfpm.yaml -p rpm -t $(LINUX_INSTALLER_RPM)
VERSION=$(APP_VERSION) SYSTEMD_UNIT_SRC=$(LINUX_SYSTEMD_UNIT_DST) \
nfpm package -f $(LINUX_PKG_ROOT)/nfpm.yaml -p rpm -t $(LINUX_INSTALLER_RPM); \
Comment on lines +337 to +340
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The three packaging commands are chained with ;, so failure in the deb/rpm packaging steps can be silently ignored if the final archlinux command succeeds. This can produce partially broken release artifacts while the target still exits successfully. [logic error]

Severity Level: Major ⚠️
- ❌ RPM artifact can be missing while build reports success.
- ⚠️ Partial packaging failures become harder to detect.
Suggested change
nfpm package -f $(LINUX_PKG_ROOT)/nfpm.yaml -p deb -t $(LINUX_INSTALLER_DEB); \
VERSION=$(APP_VERSION) GOARCH=$(LINUX_TARGET_ARCH) LINUX_BUNDLE_SRC="$$BUNDLE_DIR/" SYSTEMD_UNIT_SRC=$(LINUX_SYSTEMD_UNIT_DST) \
LANTERND_SRC=$(LINUX_SERVICE_DST)/$(LINUX_SERVICE_NAME) LANTERND_DST=/usr/sbin/$(LINUX_SERVICE_NAME) \
nfpm package -f $(LINUX_PKG_ROOT)/nfpm.yaml -p rpm -t $(LINUX_INSTALLER_RPM)
VERSION=$(APP_VERSION) SYSTEMD_UNIT_SRC=$(LINUX_SYSTEMD_UNIT_DST) \
nfpm package -f $(LINUX_PKG_ROOT)/nfpm.yaml -p rpm -t $(LINUX_INSTALLER_RPM); \
nfpm package -f $(LINUX_PKG_ROOT)/nfpm.yaml -p deb -t $(LINUX_INSTALLER_DEB) && \
VERSION=$(APP_VERSION) GOARCH=$(LINUX_TARGET_ARCH) LINUX_BUNDLE_SRC="$$BUNDLE_DIR/" SYSTEMD_UNIT_SRC=$(LINUX_SYSTEMD_UNIT_DST) \
LANTERND_SRC=$(LINUX_SERVICE_DST)/$(LINUX_SERVICE_NAME) LANTERND_DST=/usr/sbin/$(LINUX_SERVICE_NAME) \
nfpm package -f $(LINUX_PKG_ROOT)/nfpm.yaml -p rpm -t $(LINUX_INSTALLER_RPM) && \
Steps of Reproduction ✅
1. CI executes `make linux-release-ci` at `.github/workflows/build-linux.yml:155`, running
packaging recipe at `Makefile:335-343`.

2. The recipe chains three `nfpm package` commands with `;` (`Makefile:337`, `:340`,
`:343`) in one shell line.

3. `Makefile` does not set global shell fail-fast (`.ONESHELL`/`SHELLFLAGS -e` not
defined; only unrelated local `@set -e` appears in iOS block at `Makefile:555`), so the
shell returns the status of the last command.

4. If `deb` or `rpm` packaging fails but `archlinux` succeeds, this line exits success; CI
can proceed and artifact upload for rpm (`.github/workflows/build-linux.yml:256-257`) may
silently publish incomplete outputs.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** Makefile
**Line:** 337:340
**Comment:**
	*Logic Error: The three packaging commands are chained with `;`, so failure in the deb/rpm packaging steps can be silently ignored if the final archlinux command succeeds. This can produce partially broken release artifacts while the target still exits successfully.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

VERSION=$(APP_VERSION) GOARCH=$(LINUX_TARGET_ARCH) LINUX_BUNDLE_SRC="$$BUNDLE_DIR/" SYSTEMD_UNIT_SRC=$(LINUX_SYSTEMD_UNIT_DST) \
LANTERND_SRC=$(LINUX_SERVICE_DST)/$(LINUX_SERVICE_NAME) LANTERND_DST=/usr/bin/$(LINUX_SERVICE_NAME) \
nfpm package -f $(LINUX_PKG_ROOT)/nfpm.yaml -p archlinux -t $(LINUX_INSTALLER_ARCH)
nfpm package -f $(LINUX_PKG_ROOT)/nfpm.yaml -p archlinux -t $(LINUX_INSTALLER_ARCH)
Comment on lines +335 to +343
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The three nfpm commands are very similar and repeat a long list of environment variables. This repetition can make the recipe harder to read and maintain. You can define a shell variable to hold the common environment variables to reduce duplication and improve clarity.

	NFPM_ENV="VERSION=$(APP_VERSION) GOARCH=$(LINUX_TARGET_ARCH) LINUX_BUNDLE_SRC='$$BUNDLE_DIR/' SYSTEMD_UNIT_SRC=$(LINUX_SYSTEMD_UNIT_DST) LANTERND_SRC=$(LINUX_SERVICE_DST)/$(LINUX_SERVICE_NAME)"; \
	$$NFPM_ENV LANTERND_DST=/usr/sbin/$(LINUX_SERVICE_NAME) \
		nfpm package -f $(LINUX_PKG_ROOT)/nfpm.yaml -p deb -t $(LINUX_INSTALLER_DEB); \
	$$NFPM_ENV LANTERND_DST=/usr/sbin/$(LINUX_SERVICE_NAME) \
		nfpm package -f $(LINUX_PKG_ROOT)/nfpm.yaml -p rpm -t $(LINUX_INSTALLER_RPM); \
	$$NFPM_ENV LANTERND_DST=/usr/bin/$(LINUX_SERVICE_NAME) \
		nfpm package -f $(LINUX_PKG_ROOT)/nfpm.yaml -p archlinux -t $(LINUX_INSTALLER_ARCH)


.PHONY: verify-linux-package
verify-linux-package:
Expand Down
Loading