Skip to content
Merged
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
44 changes: 44 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
build:
strategy:
matrix:
include:
- os: macos-14
target: daemon-mac
# - os: windows-latest
# target: daemon-win
# - os: ubuntu-latest
# target: firmware

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4

# --- daemon-mac ---
- name: Install dependencies (macOS)
if: matrix.target == 'daemon-mac'
run: brew install libwebsockets cjson openssl@3

- name: Build daemon (macOS)
if: matrix.target == 'daemon-mac'
working-directory: host/linkctl-daemon
run: |
mkdir -p build && cd build
cmake ..
make -j$(sysctl -n hw.ncpu)

- name: Upload build artifact
if: matrix.target == 'daemon-mac'
uses: actions/upload-artifact@v4
with:
name: linkctl-daemon-${{ matrix.os }}
path: host/linkctl-daemon/build/linkctl-daemon
81 changes: 81 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Release

on:
push:
tags:
- 'v*.*.*'

permissions:
contents: write

jobs:
build-release:
strategy:
matrix:
include:
- os: macos-14
target: daemon-mac
arch: arm64
# - os: windows-latest
# target: daemon-win
# arch: x64

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4

- name: Extract version from tag
id: version
run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"

# --- daemon-mac ---
- name: Install dependencies (macOS)
if: matrix.target == 'daemon-mac'
run: brew install libwebsockets cjson openssl@3

- name: Build daemon (macOS)
if: matrix.target == 'daemon-mac'
working-directory: host/linkctl-daemon
run: |
mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(sysctl -n hw.ncpu)

- name: Package release (macOS)
if: matrix.target == 'daemon-mac'
id: package
run: |
STAGING="linkctl-daemon-${{ steps.version.outputs.VERSION }}-macos-${{ matrix.arch }}"
mkdir -p "$STAGING"
cp host/linkctl-daemon/build/linkctl-daemon "$STAGING/"
cp host/linkctl-daemon/install.sh "$STAGING/"
chmod +x "$STAGING/install.sh"
cp README.md "$STAGING/"
tar czf "${STAGING}.tar.gz" "$STAGING"
echo "ARCHIVE=${STAGING}.tar.gz" >> "$GITHUB_OUTPUT"

- name: Upload release artifact
uses: actions/upload-artifact@v4
with:
name: release-${{ matrix.target }}
path: ${{ steps.package.outputs.ARCHIVE }}

create-release:
needs: build-release
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Download all release artifacts
uses: actions/download-artifact@v4
with:
pattern: release-*
merge-multiple: true

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: "*.tar.gz"
generate_release_notes: true
94 changes: 94 additions & 0 deletions host/linkctl-daemon/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/bin/bash
# install.sh — Install linkctl-daemon from a release archive.
# Run from inside the extracted release directory.
set -euo pipefail

INSTALL_DIR="/usr/local/bin"
BINARY="linkctl-daemon"
LABEL="com.linkctl.daemon"
DOMAIN="gui/$(id -u)"
PLIST_DIR="$HOME/Library/LaunchAgents"
PLIST="$PLIST_DIR/$LABEL.plist"
LOG_FILE="$HOME/Library/Logs/linkctl-daemon.log"

echo "linkctl-daemon installer"
echo "========================"
echo

# Check we're on macOS
if [ "$(uname)" != "Darwin" ]; then
echo "Error: this installer is for macOS only."
exit 1
fi

# Check the binary is present
if [ ! -f "$BINARY" ]; then
echo "Error: $BINARY not found in current directory."
echo "Run this script from inside the extracted release archive."
exit 1
fi

# Check Homebrew dependencies
echo "Checking dependencies..."
MISSING=()
for dep in libwebsockets cjson openssl@3; do
if ! brew list --formula "$dep" &>/dev/null; then
MISSING+=("$dep")
fi
done

if [ ${#MISSING[@]} -gt 0 ]; then
echo "Missing Homebrew packages: ${MISSING[*]}"
echo "Install with: brew install ${MISSING[*]}"
exit 1
fi
echo " All dependencies found."

# Install binary
echo "Installing $BINARY to $INSTALL_DIR..."
sudo install -m 755 "$BINARY" "$INSTALL_DIR/$BINARY"
echo " Done."

# Create launchd plist
echo "Setting up launchd service..."
mkdir -p "$PLIST_DIR"
cat > "$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>$LABEL</string>
<key>ProgramArguments</key>
<array>
<string>$INSTALL_DIR/$BINARY</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>ThrottleInterval</key>
<integer>10</integer>
<key>StandardOutPath</key>
<string>$LOG_FILE</string>
<key>StandardErrorPath</key>
<string>$LOG_FILE</string>
</dict>
</plist>
EOF

# Load service (unload first if already running)
launchctl bootout "$DOMAIN/$LABEL" 2>/dev/null || true
launchctl bootstrap "$DOMAIN" "$PLIST"
echo " Service started."

echo
echo "Installation complete!"
echo " Binary: $INSTALL_DIR/$BINARY"
echo " Service: $LABEL (running)"
echo " Logs: $LOG_FILE"
echo
echo "To uninstall:"
echo " launchctl bootout $DOMAIN/$LABEL"
echo " sudo rm $INSTALL_DIR/$BINARY"
echo " rm $PLIST"
1 change: 1 addition & 0 deletions src/serial_cli.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class SerialCLI {
// Config setters (used by captive portal)
void setWifiConfig(const String& ssid, const String& password);
void setServerConfig(const String& host, uint16_t port);
void setJoyCenter(int centerX, int centerY);

// Request portal start
bool portalRequested() const { return _portalRequested; }
Expand Down
Loading