diff --git a/.github/workflows/cross-build-rpi.yml b/.github/workflows/cross-build-rpi.yml new file mode 100644 index 0000000..693b59d --- /dev/null +++ b/.github/workflows/cross-build-rpi.yml @@ -0,0 +1,56 @@ +name: Cross-build Raspberry Pi + +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] + +jobs: + cross-build: + name: Build ${{ matrix.target.name }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + - name: linux-armv6 + goos: linux + goarch: arm + goarm: "6" + output: logdy_linux_armv6 + - name: linux-armv7 + goos: linux + goarch: arm + goarm: "7" + output: logdy_linux_armv7 + - name: linux-arm64 + goos: linux + goarch: arm64 + goarm: "" + output: logdy_linux_arm64 + + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.23.2" + + - name: Download dependencies + run: go mod download + + - name: Build target + env: + GOOS: ${{ matrix.target.goos }} + GOARCH: ${{ matrix.target.goarch }} + GOARM: ${{ matrix.target.goarm }} + CGO_ENABLED: "0" + run: go build -trimpath -ldflags "-X 'main.Version=ci'" -o "bin/${{ matrix.target.output }}" . + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.target.output }} + path: bin/${{ matrix.target.output }} diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..bbedcca --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,64 @@ +name: Docker Publish (GHCR) + +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] + tags: + - "v*" + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + docker: + name: Build and publish container image + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=tag + type=sha,prefix=sha- + flavor: | + latest=auto + + - name: Build and push image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm64,linux/arm/v7 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/README.md b/README.md index 5f9b982..5b5b9a4 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ Logdy is a lightweight, single-binary log viewer that works just like `grep`, `a * Multiple input modes (files, stdin, sockets, REST API) * Custom parsers and columns with TypeScript support (code editor with types support) * Go library integration +* Cross-build support for Raspberry Pi (ARMv6/ARMv7/ARM64) ### Standalone use ```bash @@ -91,11 +92,45 @@ brew install logdy Navigate to [releases](https://github.com/logdyhq/logdy-core/releases) Github page and download the latest release for your architecture. ```bash -wget https://github.com/logdyhq/logdy-core/releases/download/v0.16.0/logdy_linux_amd64; -mv logdy_linux_amd64 logdy; +wget https://github.com/logdyhq/logdy-core/releases/download/vX.Y.Z/logdy_linux_arm64; +mv logdy_linux_arm64 logdy; chmod +x logdy; ``` Additionally, you can [add the binary to your PATH](https://logdy.dev/docs/how-tos#how-to-add-logdy-to-path) for easier access. + +## Raspberry Pi + +`logdy` runs well on Raspberry Pi with 32-bit and 64-bit OS images. + +### Build Raspberry Pi binaries +Use the dedicated script to generate binaries for: +- `linux/armv6` (Pi Zero / Pi 1) +- `linux/armv7` (Pi 2 / Pi 3 with 32-bit OS) +- `linux/arm64` (Pi 3/4/5 with 64-bit OS) + +```bash +./scripts/build_rpi.sh X.Y.Z +``` + +Generated files: +- `bin/logdy_linux_armv6` +- `bin/logdy_linux_armv7` +- `bin/logdy_linux_arm64` + +### Install as a service on Raspberry Pi (systemd) +This repository includes a hardened service unit at `packaging/systemd/logdy.service`. + +Example installation flow on Raspberry Pi: +```bash +sudo install -m 0755 ./bin/logdy_linux_arm64 /usr/local/bin/logdy +sudo ./scripts/install_rpi_service.sh /usr/local/bin/logdy +``` + +By default, service mode starts: +- `logdy --ui-ip 0.0.0.0 --port 8080 stdin` + +Then access the UI from your network: +- `http://:8080` ## Quick start Whatever the below command will produce to the output, will be forwarded to a Web UI. ```bash @@ -181,12 +216,11 @@ go build ``` ## Releasing -For a cross architecture build use `gox`. This will generate multiple binaries (in `bin/` dir) for specific architectures, don't forget to update `main.Version` tag. +For cross-architecture builds use the repository scripts below. This generates binaries in `bin/` and includes Raspberry Pi targets. + ```bash -gox \ - -ldflags "-X 'main.Version=x.x.x'" \ - -output="bin/{{.Dir}}_{{.OS}}_{{.Arch}}" \ - -osarch="linux/amd64 windows/386 windows/amd64 darwin/amd64 darwin/arm64 linux/arm64" +./build_across x.x.x +./scripts/build_rpi.sh x.x.x ``` Once it's ready, publish the binaries in a new Github release. Again, don't forget to update the version. diff --git a/build_across b/build_across index 9988ccf..c51f19f 100755 --- a/build_across +++ b/build_across @@ -1 +1,23 @@ -gox -ldflags "-X 'main.Version=0.17.1'" -output="bin/logdy_{{.OS}}_{{.Arch}}" -osarch="linux/amd64 linux/arm64 windows/386 windows/amd64 darwin/amd64 darwin/arm64" +#!/usr/bin/env bash + +set -euo pipefail + +VERSION="${1:-dev}" +OUT_DIR="${2:-bin}" +LDFLAGS="-X 'main.Version=${VERSION}'" + +mkdir -p "${OUT_DIR}" + +echo "Building common release targets..." +GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -ldflags "${LDFLAGS}" -o "${OUT_DIR}/logdy_linux_amd64" . +GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -trimpath -ldflags "${LDFLAGS}" -o "${OUT_DIR}/logdy_linux_arm64" . +GOOS=windows GOARCH=386 CGO_ENABLED=0 go build -trimpath -ldflags "${LDFLAGS}" -o "${OUT_DIR}/logdy_windows_386.exe" . +GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -ldflags "${LDFLAGS}" -o "${OUT_DIR}/logdy_windows_amd64.exe" . +GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -ldflags "${LDFLAGS}" -o "${OUT_DIR}/logdy_darwin_amd64" . +GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build -trimpath -ldflags "${LDFLAGS}" -o "${OUT_DIR}/logdy_darwin_arm64" . + +echo "Building Raspberry Pi targets..." +GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=0 go build -trimpath -ldflags "${LDFLAGS}" -o "${OUT_DIR}/logdy_linux_armv6" . +GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=0 go build -trimpath -ldflags "${LDFLAGS}" -o "${OUT_DIR}/logdy_linux_armv7" . + +echo "Done. Binaries are in ${OUT_DIR}/" diff --git a/packaging/systemd/logdy.service b/packaging/systemd/logdy.service new file mode 100644 index 0000000..04a6546 --- /dev/null +++ b/packaging/systemd/logdy.service @@ -0,0 +1,21 @@ +[Unit] +Description=Logdy log viewer service +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=logdy +Group=logdy +WorkingDirectory=/var/lib/logdy +ExecStart=/usr/local/bin/logdy --ui-ip 0.0.0.0 --port 8080 stdin +Restart=always +RestartSec=3 +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=full +ProtectHome=true +ReadWritePaths=/var/lib/logdy + +[Install] +WantedBy=multi-user.target diff --git a/scripts/build_rpi.sh b/scripts/build_rpi.sh new file mode 100755 index 0000000..359033d --- /dev/null +++ b/scripts/build_rpi.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +set -euo pipefail + +VERSION="${1:-dev}" +OUT_DIR="${2:-bin}" +APP_NAME="logdy" +LDFLAGS="-X 'main.Version=${VERSION}'" + +mkdir -p "${OUT_DIR}" + +build_target() { + local arch="$1" + local goarm="${2:-}" + local output_suffix="linux_${arch}" + + if [[ -n "${goarm}" ]]; then + output_suffix="${output_suffix}v${goarm}" + fi + + echo "Building ${APP_NAME}_${output_suffix}" + GOOS=linux GOARCH="${arch}" CGO_ENABLED=0 GOARM="${goarm}" \ + go build -trimpath -ldflags "${LDFLAGS}" -o "${OUT_DIR}/${APP_NAME}_${output_suffix}" . +} + +# Raspberry Pi Zero / 1 (ARMv6, 32-bit) +build_target arm 6 + +# Raspberry Pi 2 / 3 (ARMv7, 32-bit) +build_target arm 7 + +# Raspberry Pi 3 / 4 / 5 (ARM64, 64-bit OS) +build_target arm64 + +echo "Raspberry Pi binaries available in ${OUT_DIR}/" diff --git a/scripts/install_rpi_service.sh b/scripts/install_rpi_service.sh new file mode 100755 index 0000000..6857ca9 --- /dev/null +++ b/scripts/install_rpi_service.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +set -euo pipefail + +BIN_PATH="${1:-/usr/local/bin/logdy}" +SERVICE_PATH="/etc/systemd/system/logdy.service" +SERVICE_USER="${LOGDY_USER:-logdy}" +SERVICE_GROUP="${LOGDY_GROUP:-logdy}" + +if [[ ! -x "${BIN_PATH}" ]]; then + echo "Binary not found or not executable: ${BIN_PATH}" + echo "Build/copy logdy first, then run this script with sudo." + exit 1 +fi + +if ! id -u "${SERVICE_USER}" >/dev/null 2>&1; then + useradd --system --home /var/lib/logdy --shell /usr/sbin/nologin "${SERVICE_USER}" +fi + +if ! getent group "${SERVICE_GROUP}" >/dev/null 2>&1; then + groupadd --system "${SERVICE_GROUP}" +fi + +mkdir -p /var/lib/logdy +chown "${SERVICE_USER}:${SERVICE_GROUP}" /var/lib/logdy + +tmp_service="$(mktemp)" +cp packaging/systemd/logdy.service "${tmp_service}" +sed -i "s/^User=.*/User=${SERVICE_USER}/" "${tmp_service}" +sed -i "s/^Group=.*/Group=${SERVICE_GROUP}/" "${tmp_service}" +sed -i "s|^ExecStart=.*|ExecStart=${BIN_PATH} --ui-ip 0.0.0.0 --port 8080 stdin|" "${tmp_service}" + +install -m 0644 "${tmp_service}" "${SERVICE_PATH}" +rm -f "${tmp_service}" + +systemctl daemon-reload +systemctl enable --now logdy.service +systemctl status --no-pager logdy.service