no more Docker!

* use `termion` rather than `ncurses` to limit runtime deps
* cross-compile with `cross` instead of our own dockerfiles/scripts
* update instructions
* update release procedure and GitHub actions to match
* prep changelog for `v0.7.8`

Fixes #160
Closes #265
This commit is contained in:
Scott Lamb 2023-10-14 15:52:06 -07:00
parent faf0201b52
commit ee98bf5236
23 changed files with 364 additions and 890 deletions

View File

@ -24,8 +24,7 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem.
**Server (please complete the following information):**
- If using Docker: `docker ps` + `docker images`
- If building from git: `moonfire-nvr --version`
- `moonfire-nvr --version`
- Attach a [log file](https://github.com/scottlamb/moonfire-nvr/blob/master/guide/troubleshooting.md#viewing-moonfire-nvrs-logs). Run with the `RUST_BACKTRACE=1` environment variable set if possible.
**Camera (please complete the following information):**

View File

@ -1,6 +1,10 @@
name: CI
on: [push, pull_request]
defaults:
run:
shell: bash
env:
CARGO_TERM_COLOR: always
MOONFIRE_COLOR: always
@ -37,11 +41,7 @@ jobs:
restore-keys: |
cargo-${{ matrix.rust }}-
cargo-
- name: Install dependencies
# The retry here is to work around "Unable to connect to azure.archive.ubuntu.com" errors.
# https://github.com/actions/runner-images/issues/6894
run: sudo apt-get --option=APT::Acquire::Retries=3 update && sudo apt-get --option=APT::Acquire::Retries=3 install libncurses-dev libsqlite3-dev pkgconf
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Install Rust
@ -51,13 +51,10 @@ jobs:
toolchain: ${{ matrix.rust }}
override: true
components: ${{ matrix.extra_components }}
- run: cd ui && npm ci
- run: cd ui && npm run build
- name: Test
run: |
cd server
cargo test ${{ matrix.extra_args }} --all
cargo test --features=bundled-ui ${{ matrix.extra_args }} --all
cargo test --features=rusqlite/bundled ${{ matrix.extra_args }} --all
continue-on-error: ${{ matrix.rust == 'nightly' }}
- name: Check formatting
if: matrix.rust == 'stable'
@ -70,7 +67,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- run: cd ui && npm ci
@ -83,5 +80,5 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- run: find . -type f -print0 | xargs -0 .github/workflows/check-license.py

View File

@ -1,19 +1,109 @@
name: Release
defaults:
run:
shell: bash
on:
push:
tags:
- v[0-9]+.*
jobs:
create-release:
base:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: taiki-e/create-gh-release-action@v1
- name: Checkout
uses: actions/checkout@v4
- uses: taiki-e/install-action@v2
with:
# (Optional) Path to changelog.
changelog: CHANGELOG.md
tool: parse-changelog
- name: Generate changelog
run: |
VERSION_MINUS_V=${GITHUB_REF_NAME/#v/}
parse-changelog CHANGELOG.md $VERSION_MINUS_V > CHANGELOG-$GITHUB_REF_NAME.md
- uses: actions/setup-node@v3
with:
node-version: 18
- run: cd ui && npm ci
- run: cd ui && npm run build
- run: cd ui && npm run test
# Upload the UI and changelog as *job* artifacts (not *release* artifacts), used below.
- uses: actions/upload-artifact@v3
with:
name: moonfire-nvr-ui-${{ github.ref_name }}
path: ui/build
if-no-files-found: error
- uses: actions/upload-artifact@v3
with:
name: CHANGELOG-${{ github.ref_name }}
path: CHANGELOG-${{ github.ref_name }}.md
if-no-files-found: error
cross:
needs: base # for bundled ui
strategy:
matrix:
include:
- target: x86_64-unknown-linux-musl
- target: aarch64-unknown-linux-musl
- target: armv7-unknown-linux-musleabihf
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download UI
uses: actions/download-artifact@v3
with:
name: moonfire-nvr-ui-${{ github.ref_name }}
path: ui/build
# actions-rust-cross doesn't actually use cross for x86_64.
# Install the needed musl-tools in the host.
- name: Install musl-tools
run: sudo apt-get --option=APT::Acquire::Retries=3 update && sudo apt-get --option=APT::Acquire::Retries=3 install musl-tools
if: matrix.target == 'x86_64-unknown-linux-musl'
- name: Build
uses: houseabsolute/actions-rust-cross@v0
env:
# (Required) GitHub token for creating GitHub Releases.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
UI_BUILD_DIR: ../ui/build
# cross doesn't install `git` within its Docker container, so plumb
# the version through rather than try `git describe` from `build.rs`.
VERSION: ${{ github.ref_name }}
with:
working-directory: server
target: ${{ matrix.target }}
command: build
args: --release --features bundled
# Upload as a *job* artifact (not *release* artifact), used below.
- name: Upload
uses: actions/upload-artifact@v3
with:
name: moonfire-nvr-${{ github.ref_name }}-${{ matrix.target }}
path: server/target/${{ matrix.target }}/release/moonfire-nvr
if-no-files-found: error
release:
needs: [ base, cross ]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v3
with:
path: artifacts
- name: ls before rearranging
run: find . -ls
- name: Rearrange
run: |
(cd artifacts/moonfire-nvr-ui-${GITHUB_REF_NAME} && zip -r ../../moonfire-nvr-ui-${GITHUB_REF_NAME}.zip .)
(cd artifacts; for i in moonfire-nvr-*/moonfire-nvr; do mv $i "../$(dirname $i)"; done)
- name: ls after rearranging
run: find . -ls
- name: Create GitHub release
uses: softprops/action-gh-release@v1
with:
body_path: artifacts/CHANGELOG-${{ github.ref_name }}/CHANGELOG-${{ github.ref_name }}.md
files: |
moonfire-nvr-*

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
.DS_Store
*.swp
/release-*
/server/target
.idea

View File

@ -1,17 +1,22 @@
# Moonfire NVR change log
Below are some highlights in each release. For a full description of all
changes, see Git history.
Each release is tagged in Git and on the Docker repository
[`scottlamb/moonfire-nvr`](https://hub.docker.com/r/scottlamb/moonfire-nvr).
changes, see Git history. Each release is tagged in git.
Backwards-incompatible database schema changes happen on on major version
upgrades, e.g. `0.6.x` -> `0.7.x`. The config file format and
upgrades, e.g. `v0.6.x` -> `v0.7.x`. The config file format and
[API](ref/api.md) currently have no stability guarantees, so they may change
even on minor releases, e.g. `0.7.5` -> `0.7.6`.
even on minor releases, e.g. `v0.7.5` -> `v0.7.6`.
## 0.7.7 (2023-08-03)
## v0.7.8 (2023-10-18)
* release as self-contained Linux binaries (for `x86_64`, `aarch64`, and
`armv8` architectures) rather than Docker images. This minimizes hassle and
total download size. Along the way, we switched libc to from `glibc` to
`musl` in the process. Please report any problems with the build or
instructions!
## v0.7.7 (2023-08-03)
* fix [#289](https://github.com/scottlamb/moonfire-nvr/issues/289): crash on
pressing the `Add` button in the sample file directory dialog
@ -19,7 +24,7 @@ even on minor releases, e.g. `0.7.5` -> `0.7.6`.
* experimental (off by default) support for bundling UI files into the
executable.
## 0.7.6 (2023-07-08)
## v0.7.6 (2023-07-08)
* new log formats using `tracing`. This will allow richer context information.
* bump minimum Rust version to 1.70.
@ -53,7 +58,7 @@ even on minor releases, e.g. `0.7.5` -> `0.7.6`.
* improvements to `moonfire-nvr config`,
thanks to [@sky1e](https://github.com/sky1e).
## 0.7.5 (2022-05-09)
## v0.7.5 (2022-05-09)
* [#219](https://github.com/scottlamb/moonfire-nvr/issues/219): fix
live stream failing with `ws close: 1006` on URLs with port numbers.
@ -63,7 +68,7 @@ even on minor releases, e.g. `0.7.5` -> `0.7.6`.
Retina 0.3.10, improving compatibility with OMNY M5S2A 2812 cameras that
send invalid `rtptime` values.
## 0.7.4 (2022-04-13)
## v0.7.4 (2022-04-13)
* upgrade to Retina 0.3.9, improving camera interop and diagnostics.
Fixes [#213](https://github.com/scottlamb/moonfire-nvr/issues/213),
@ -74,13 +79,13 @@ even on minor releases, e.g. `0.7.5` -> `0.7.6`.
* [#206](https://github.com/scottlamb/moonfire-nvr/issues/206#issuecomment-1086442543):
fix `teardown Sender shouldn't be dropped: RecvError(())` errors on shutdown.
## 0.7.3 (2022-03-22)
## v0.7.3 (2022-03-22)
* security fix: check the `Origin` header on live stream WebSocket requests
to avoid cross-site WebSocket hijacking (CSWSH).
* RTSP connections always use the Retina library rather than FFmpeg.
## 0.7.2 (2022-03-16)
## v0.7.2 (2022-03-16)
* introduce a configuration file `/etc/moonfire-nvr.toml`; you will need
to create one when upgrading.
@ -97,13 +102,13 @@ even on minor releases, e.g. `0.7.5` -> `0.7.6`.
* progress on [#70](https://github.com/scottlamb/moonfire-nvr/issues/184):
shrink the binary from 154 MiB to 70 MiB by reducing debugging information.
## 0.7.1 (2021-10-27)
## v0.7.1 (2021-10-27)
* bugfix: editing a camera from `nvr config` would erroneously clear the
sample file directory associated with its streams.
* RTSP transport (TCP or UDP) can be set per-stream from `nvr config`.
## 0.7.0 (2021-10-27)
## v0.7.0 (2021-10-27)
* [schema version 7](guide/schema.md#version-7)
* Changes to the [API](guide/api.md):
@ -130,7 +135,7 @@ even on minor releases, e.g. `0.7.5` -> `0.7.6`.
currently may be either absent or the string `record`.
* Added `POST /api/users/<id>` for altering a user's UI preferences.
## 0.6.7 (2021-10-20)
## v0.6.7 (2021-10-20)
* trim whitespace when detecting time zone by reading `/etc/timezone`.
* (Retina 0.3.2) better `TEARDOWN` handling with the default
@ -142,7 +147,7 @@ even on minor releases, e.g. `0.7.5` -> `0.7.6`.
`--rtsp-library=retina` (see
[scottlamb/retina#25](https://github.com/scottlamb/retina/25)).
## 0.6.6 (2021-09-23)
## v0.6.6 (2021-09-23)
* fix [#146](https://github.com/scottlamb/moonfire-nvr/issues/146): "init
segment fetch error" when browsers have cached data from `v0.6.4` and
@ -167,7 +172,7 @@ even on minor releases, e.g. `0.7.5` -> `0.7.6`.
impatient to get fast results with ctrl-C when running interactively, rather
than having to use `SIGKILL` from another terminal.
## 0.6.5 (2021-08-13)
## v0.6.5 (2021-08-13)
* UI: improve video aspect ratio handling. Live streams formerly worked
around a Firefox pixel aspect ratio bug by forcing all videos to 16:9, which
@ -181,7 +186,7 @@ even on minor releases, e.g. `0.7.5` -> `0.7.6`.
`GET_PARAMETERS` as a RTSP keepalive. GW Security cameras would ignored
the latter, causing Moonfire NVR to drop the connection every minute.
## 0.6.4 (2021-06-28)
## v0.6.4 (2021-06-28)
* Default to a new pure-Rust RTSP library, `retina`. If you hit problems, you
can switch back via `--rtsp-library=ffmpeg`. Please report a bug if this
@ -189,7 +194,7 @@ even on minor releases, e.g. `0.7.5` -> `0.7.6`.
* Correct the pixel aspect ratio of 9:16 sub streams (eg a standard 16x9
camera rotated 90 degrees) in the same way as 16:9 sub streams.
## 0.6.3 (2021-03-31)
## v0.6.3 (2021-03-31)
* New user interface! Besides a more modern appearance, it has better
error handling and an experimental live view UI.
@ -199,7 +204,7 @@ even on minor releases, e.g. `0.7.5` -> `0.7.6`.
not calculated properly there might be unexpected gaps or overlaps in
playback.
## 0.6.2 (2021-03-12)
## v0.6.2 (2021-03-12)
* Fix panics when a stream's PTS has extreme jumps
([#113](https://github.com/scottlamb/moonfire-nvr/issues/113))
@ -209,7 +214,7 @@ even on minor releases, e.g. `0.7.5` -> `0.7.6`.
`moonfire-nvr check --delete-orphan-rows` command from actually deleting
rows.
## 0.6.1 (2021-02-16)
## v0.6.1 (2021-02-16)
* Improve the server's error messages on the console and in logs.
* Switch the UI build from the `yarn` package manager to `npm`.
@ -221,7 +226,7 @@ even on minor releases, e.g. `0.7.5` -> `0.7.6`.
* Fix mangled favicons
([#105](https://github.com/scottlamb/moonfire-nvr/issues/105))
## 0.6.0 (2021-01-22)
## v0.6.0 (2021-01-22)
This is the first tagged version and first Docker image release. I chose the
version number 0.6.0 to match the current schema version 6.

View File

@ -1,75 +0,0 @@
# syntax=docker/dockerfile:1.2.1
# This file is part of Moonfire NVR, a security camera network video recorder.
# Copyright (C) 2021 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
# SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
# See documentation here:
# https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md
# "dev-common" is the portion of "dev" (see below) which isn't specific to the
# target arch. It's sufficient for building the non-arch-specific webpack.
FROM --platform=$BUILDPLATFORM ubuntu:20.04 AS dev-common
LABEL maintainer="slamb@slamb.org"
ARG BUILD_UID=1000
ARG BUILD_GID=1000
ARG INVALIDATE_CACHE_DEV_COMMON=
ENV LC_ALL=C.UTF-8
COPY docker/dev-common.bash /
RUN --mount=type=cache,id=var-cache-apt,target=/var/cache/apt,sharing=locked \
/dev-common.bash
CMD [ "/bin/bash", "--login" ]
# "dev" is a full development environment, suitable for shelling into or
# using with the VS Code container plugin.
FROM --platform=$BUILDPLATFORM dev-common AS dev
LABEL maintainer="slamb@slamb.org"
ARG BUILDARCH
ARG TARGETARCH
ARG INVALIDATE_CACHE_DEV=
COPY docker/dev.bash /
RUN --mount=type=cache,id=var-cache-apt,target=/var/cache/apt,sharing=locked \
/dev.bash
USER moonfire-nvr:moonfire-nvr
WORKDIR /var/lib/moonfire-nvr
# Build the UI with node_modules and ui-dist outside the src dir.
FROM --platform=$BUILDPLATFORM dev-common AS build-ui
ARG INVALIDATE_CACHE_BUILD_UI=
LABEL maintainer="slamb@slamb.org"
WORKDIR /var/lib/moonfire-nvr/src/ui
COPY docker/build-ui.bash /
COPY ui /var/lib/moonfire-nvr/src/ui
RUN --mount=type=tmpfs,target=/var/lib/moonfire-nvr/src/ui/node_modules \
/build-ui.bash
# Build the Rust components. Note that dev.sh set up an environment variable
# in .buildrc that similarly changes the target dir path.
FROM --platform=$BUILDPLATFORM dev AS build-server
LABEL maintainer="slamb@slamb.org"
ARG INVALIDATE_CACHE_BUILD_SERVER=
COPY docker/build-server.bash /
COPY --from=build-ui /var/lib/moonfire-nvr/src/ui/build /var/lib/moonfire-nvr/src/ui/build
RUN --mount=type=cache,id=target,target=/var/lib/moonfire-nvr/target,sharing=locked,mode=777 \
--mount=type=cache,id=cargo,target=/cargo-cache,sharing=locked,mode=777 \
--mount=type=bind,source=server,target=/var/lib/moonfire-nvr/src/server,readonly \
/build-server.bash
# Deployment environment, now in the target platform.
FROM --platform=$TARGETPLATFORM ubuntu:20.04 AS deploy
LABEL maintainer="slamb@slamb.org"
ARG INVALIDATE_CACHE_BUILD_DEPLOY=
ENV LC_ALL=C.UTF-8
COPY docker/deploy.bash /
RUN --mount=type=cache,id=var-cache-apt,target=/var/cache/apt,sharing=locked \
/deploy.bash
COPY --from=dev-common /docker-build-debug/dev-common/ /docker-build-debug/dev-common/
COPY --from=dev /docker-build-debug/dev/ /docker-build-debug/dev/
COPY --from=build-server /docker-build-debug/build-server/ /docker-build-debug/build-server/
COPY --from=build-server /usr/local/bin/moonfire-nvr /usr/local/bin/moonfire-nvr
COPY --from=build-ui /docker-build-debug/build-ui /docker-build-debug/build-ui
# The install instructions say to use --user in the docker run commandline.
# Specify a non-root user just in case someone forgets.
USER 10000:10000
WORKDIR /var/lib/moonfire-nvr
ENTRYPOINT [ "/usr/local/bin/moonfire-nvr" ]

View File

@ -1,37 +0,0 @@
#!/bin/bash
# This file is part of Moonfire NVR, a security camera network video recorder.
# Copyright (C) 2021 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
# SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
# Build the "build-server" target. See Dockerfile.
set -o errexit
set -o pipefail
set -o xtrace
mkdir /docker-build-debug/build-server
exec > >(tee -i /docker-build-debug/build-server/output) 2>&1
date
uname -a
find /cargo-cache -ls > /docker-build-debug/build-server/cargo-cache-before
find ~/target -ls > /docker-build-debug/build-server/target-before
source ~/.buildrc
# The "mode" argument to cache mounts doesn't seem to work reliably
# (as of Docker version 20.10.5, build 55c4c88, using a docker-container
# builder), thus the chmod command.
sudo chmod 777 /cargo-cache /var/lib/moonfire-nvr/target
mkdir -p /cargo-cache/{git,registry}
ln -s /cargo-cache/{git,registry} ~/.cargo
build_profile=release-lto
cd src/server
time cargo test --features=bundled-ui
time cargo build --features=bundled-ui --profile=$build_profile
find /cargo-cache -ls > /docker-build-debug/build-server/cargo-cache-after
find ~/target -ls > /docker-build-debug/build-server/target-after
sudo install -m 755 \
~/platform-target/$build_profile/moonfire-nvr \
/usr/local/bin/moonfire-nvr
date

View File

@ -1,24 +0,0 @@
#!/bin/bash
# This file is part of Moonfire NVR, a security camera network video recorder.
# Copyright (C) 2021 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
# SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
# Build the "build-ui" target. See Dockerfile.
set -o errexit
set -o pipefail
set -o xtrace
mkdir /docker-build-debug/build-ui
exec > >(tee -i /docker-build-debug/build-ui/output) 2>&1
date
uname -a
node --version
npm --version
time npm ci
time npm run build
find /var/lib/moonfire-nvr/src/ui/node_modules -ls \
> /docker-build-debug/build-ui/node_modules-after
date

View File

@ -1,32 +0,0 @@
#!/bin/bash
# This file is part of Moonfire NVR, a security camera network video recorder.
# Copyright (C) 2021 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
# SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
# Build the "deploy" target. See Dockerfile.
set -o errexit
set -o pipefail
set -o xtrace
mkdir -p /docker-build-debug/deploy
exec > >(tee -i /docker-build-debug/deploy/output) 2>&1
find /var/cache/apt -ls > /docker-build-debug/deploy/var-cache-apt-before
date
uname -a
export DEBIAN_FRONTEND=noninteractive
time apt-get update
time apt-get install --assume-yes --no-install-recommends \
libncurses6 \
libncursesw6 \
locales \
sudo \
sqlite3 \
tzdata \
vim-nox
rm -rf /var/lib/apt/lists/*
ln -s moonfire-nvr /usr/local/bin/nvr
find /var/cache/apt -ls > /docker-build-debug/deploy/var-cache-apt-after
date

View File

@ -1,82 +0,0 @@
#!/bin/bash
# This file is part of Moonfire NVR, a security camera network video recorder.
# Copyright (C) 2021 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
# SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
# Build the "dev" target. See Dockerfile.
set -o errexit
set -o pipefail
set -o xtrace
mkdir --mode=1777 /docker-build-debug
mkdir /docker-build-debug/dev-common
exec > >(tee -i /docker-build-debug/dev-common/output) 2>&1
date
uname -a
find /var/cache/apt -ls > /docker-build-debug/dev-common/var-cache-apt-before
export DEBIAN_FRONTEND=noninteractive
# This file cleans apt caches after every invocation. Instead, we use a
# buildkit cachemount to avoid putting them in the image while still allowing
# some reuse.
rm /etc/apt/apt.conf.d/docker-clean
packages=()
# Install all packages necessary for building (and some for testing/debugging).
packages+=(
build-essential
curl
pkgconf
locales
sudo
sqlite3
tzdata
vim-nox
)
time apt-get update
time apt-get install --assume-yes --no-install-recommends "${packages[@]}"
# Install a more recent nodejs/npm than in the universe repository.
time curl -sL https://deb.nodesource.com/setup_14.x | bash -
time apt-get install nodejs
# Create the user. On the dev environment, allow sudo.
groupadd \
--gid="${BUILD_GID}" \
moonfire-nvr
useradd \
--no-log-init \
--home-dir=/var/lib/moonfire-nvr \
--uid="${BUILD_UID}" \
--gid=moonfire-nvr \
--shell=/bin/bash \
--create-home \
moonfire-nvr
echo 'moonfire-nvr ALL=(ALL) NOPASSWD: ALL' >>/etc/sudoers
# Install Rust. Note curl was already installed for yarn above.
time su moonfire-nvr -lc "
curl --proto =https --tlsv1.2 -sSf https://sh.rustup.rs |
sh -s - -y"
# Put configuration for the Rust build into a new ".buildrc" which is used
# both (1) interactively from ~/.bashrc when logging into the dev container
# and (2) from a build-server RUN command. In particular, the latter can't
# use ~/.bashrc because that script immediately exits when run from a
# non-interactive shell.
echo 'source $HOME/.buildrc' >> /var/lib/moonfire-nvr/.bashrc
cat >> /var/lib/moonfire-nvr/.buildrc <<EOF
source \$HOME/.cargo/env
# Set the target directory to be outside the src bind mount.
# https://doc.rust-lang.org/cargo/reference/config.html#buildtarget-dir
export CARGO_BUILD_TARGET_DIR=/var/lib/moonfire-nvr/target
EOF
chown moonfire-nvr:moonfire-nvr /var/lib/moonfire-nvr/.buildrc
find /var/cache/apt -ls > /docker-build-debug/dev-common/var-cache-apt-after
date

View File

@ -1,122 +0,0 @@
#!/bin/bash
# This file is part of Moonfire NVR, a security camera network video recorder.
# Copyright (C) 2021 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
# SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
# Build the "dev" target. See Dockerfile.
set -o errexit
set -o pipefail
set -o xtrace
mkdir /docker-build-debug/dev
exec > >(tee -i /docker-build-debug/dev/output) 2>&1
date
uname -a
find /var/cache/apt -ls > /docker-build-debug/dev/var-cache-apt-before
export DEBIAN_FRONTEND=noninteractive
packages=()
if [[ "${BUILDARCH}" != "${TARGETARCH}" ]]; then
# Set up cross compilation.
case "${TARGETARCH}" in
arm64)
dpkg_arch=arm64
gcc_target=aarch64-linux-gnu
rust_target=aarch64-unknown-linux-gnu
target_is_port=1
;;
arm)
dpkg_arch=armhf
gcc_target=arm-linux-gnueabihf
rust_target=arm-unknown-linux-gnueabihf
target_is_port=1
;;
amd64)
dpkg_arch=amd64
gcc_target=x86_64-linux-gnu
rust_target=x86_64-unknown-linux-gnu
target_is_port=0
;;
*)
echo "Unsupported cross-compile target ${TARGETARCH}." >&2
exit 1
esac
apt_target_suffix=":${dpkg_arch}"
case "${BUILDARCH}" in
amd64|386) host_is_port=0 ;;
*) host_is_port=1 ;;
esac
time dpkg --add-architecture "${dpkg_arch}"
if [[ $target_is_port -ne $host_is_port ]]; then
# Ubuntu stores non-x86 architectures at a different URL, so futz the
# sources file to allow installing both host and target.
# See https://github.com/rust-embedded/cross/blob/master/docker/common.sh
perl -pi.bak -e '
s{^deb (http://.*.ubuntu.com/ubuntu/) (.*)}
{deb [arch=amd64,i386] \1 \2\ndeb [arch-=amd64,i386] http://ports.ubuntu.com/ubuntu-ports \2};
s{^deb (http://ports.ubuntu.com/ubuntu-ports/) (.*)}
{deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu \2\ndeb [arch-=amd64,i386] \1 \2}' \
/etc/apt/sources.list
cat /etc/apt/sources.list
fi
packages+=(
g++-${gcc_target/_/-}
libc6-dev-${dpkg_arch}-cross
qemu-user
)
fi
time apt-get update
# Install the packages for the target architecture.
packages+=(
libncurses-dev"${apt_target_suffix}"
libsqlite3-dev"${apt_target_suffix}"
)
time apt-get update
time apt-get install --assume-yes --no-install-recommends "${packages[@]}"
# Set environment variables for cross-compiling.
# Also set up a symlink that points to the output platform's target dir, because
# the target directory layout varies when cross-compiling, as described here:
# https://doc.rust-lang.org/cargo/guide/build-cache.html
if [[ -n "${rust_target}" ]]; then
su moonfire-nvr -lc "rustup target install ${rust_target} &&
ln -s target/${rust_target} platform-target"
underscore_rust_target="${rust_target//-/_}"
uppercase_underscore_rust_target="${underscore_rust_target^^}"
cat >> /var/lib/moonfire-nvr/.buildrc <<EOF
# https://doc.rust-lang.org/cargo/reference/config.html
export CARGO_BUILD_TARGET=${rust_target}
export CARGO_TARGET_${uppercase_underscore_rust_target}_LINKER=${gcc_target}-gcc
# Use a pkg-config wrapper for the target architecture. This wrapper was
# automatically created on "dpkg --add-architecture"; see
# /etc/dpkg/dpkg.cfg.d/pkgconf-hook-config.
#
# https://github.com/rust-lang/pkg-config-rs uses the "PKG_CONFIG"
# variable to to select the pkg-config binary to use. As of pkg-config 0.3.19,
# it unfortunately doesn't understand the <target>_ prefix that the README.md
# describes for other vars. Fortunately Moonfire NVR doesn't have any host tools
# that need pkg-config.
export PKG_CONFIG=${gcc_target}-pkg-config
# https://github.com/alexcrichton/cc-rs uses these variables to decide what
# compiler to invoke.
export CC_${underscore_rust_target}=${gcc_target}-gcc
export CXX_${underscore_rust_target}=${gcc_target}-g++
EOF
else
su moonfire-nvr -lc "ln -s target platform-target"
fi
find /var/cache/apt -ls > /docker-build-debug/dev/var-cache-apt-after
date

View File

@ -2,7 +2,7 @@
This document has notes for software developers on building Moonfire NVR from
source code for development. If you just want to install precompiled
binaries, see the [Docker installation instructions](install.md) instead.
binaries, see the [installation instructions](install.md) instead.
This document doesn't spell out as many details as the installation
instructions. Please ask on Moonfire NVR's [issue
@ -11,11 +11,9 @@ tracker](https://github.com/scottlamb/moonfire-nvr/issues) or
stuck. Please also send pull requests to improve this doc.
* [Downloading](#downloading)
* [Docker builds](#docker-builds)
* [Release procedure](#release-procedure)
* [Non-Docker setup](#non-docker-setup)
* [Building](#building)
* [Running interactively straight from the working copy](#running-interactively-straight-from-the-working-copy)
* [Running as a `systemd` service](#running-as-a-systemd-service)
* [Release procedure](#release-procedure)
## Downloading
@ -28,152 +26,16 @@ $ git clone https://github.com/scottlamb/moonfire-nvr.git
$ cd moonfire-nvr
```
## Docker builds
## Building
This command should prepare a deployment image for your local machine:
```console
$ sudo docker buildx build --load --tag=moonfire-nvr -f docker/Dockerfile .
```
<details>
<summary>Common errors</summary>
* `docker: 'buildx' is not a docker command.`: You shouldn't see this with
Docker 20.10. With Docker version 19.03 you'll need to prepend
`DOCKER_CLI_EXPERIMENTAL=enabled` to `docker buildx build` commands. If
your Docker version is older than 19.03, you'll need to upgrade.
* `At least one invalid signature was encountered.`: this is likely
due to an error in `libseccomp`, as described [in this askubuntu.com answer](https://askubuntu.com/a/1264921/1365248).
Try running in a privileged builder. As described in [`docker buildx build` documentation](https://docs.docker.com/engine/reference/commandline/buildx_build/#allow),
run this command once:
```console
$ sudo docker buildx create --use --name insecure-builder --buildkitd-flags '--allow-insecure-entitlement security.insecure'
```
then add `--allow security.insecure` to your `docker buildx build` commandlines.
</details>
If you want to iterate on code changes, doing a full Docker build from
scratch every time will be painfully slow. You will likely find it more
helpful to use the `dev` target. This is a self-contained developer environment
which you can use from its shell via `docker run` or via something like
Visual Studio Code's Docker plugin.
```console
$ sudo docker buildx build \
--load --tag=moonfire-dev --target=dev -f docker/Dockerfile .
...
$ sudo docker run \
--rm --interactive=true --tty \
--mount=type=bind,source=$(pwd),destination=/var/lib/moonfire-nvr/src \
moonfire-dev
```
The development image overrides cargo's output directory to
`/var/lib/moonfire-nvr/target`. (See `~moonfire-nvr/.buildrc`.) This avoids
using a bind filesystem for build products, which can be slow on macOS. It
also means that if you sometimes compile directly on the host and sometimes
within Docker, they don't trip over each other's target directories.
You can also cross-compile to a different architecture. Adding a
`--platform=linux/arm64/v8,linux/arm/v7,linux/amd64` argument will compile
Moonfire NVR for all supported platforms. (Note: this has been used
successfully for building on x86-64 and compiling to arm but not the
reverse.) For the `dev` target, this prepares a build which executes on your
local architecture and is capable of building a binary for your desired target
architecture.
On the author's macOS machine with Docker desktop 3.0.4, building for
multiple platforms at once will initially fail with the following error:
```console
$ sudo docker buildx build ... --platform=linux/arm64/v8,linux/arm/v7,linux/amd64
[+] Building 0.0s (0/0)
error: multiple platforms feature is currently not supported for docker driver. Please switch to a different driver (eg. "docker buildx create --use")
```
Running `docker buildx create --use` once solves this problem, with a couple
caveats:
* you'll need to specify an additional `--load` argument to make builds
available to run locally.
* the `--load` argument only works for one platform at a time. With multiple
platforms, it gives an error like the following:
```
error: failed to solve: rpc error: code = Unknown desc = docker exporter does not currently support exporting manifest lists
```
[A comment on docker/buildx issue
#59](https://github.com/docker/buildx/issues/59#issuecomment-667548900)
suggests a workaround of building all three then using caching to quickly
load the one of immediate interest:
```
$ sudo docker buildx build --platform=linux/arm64/v8,linux/arm/v7,linux/amd64 ...
$ sudo docker buildx build --load --platform=arm64/v8 ...
```
On Linux hosts (as opposed to when using Docker Desktop on macOS/Windows),
you'll likely see errors like the ones below. The solution is to [install
emulators](https://github.com/tonistiigi/binfmt#installing-emulators).
You may need to reinstall emulators on each boot of the host.
```
Exec format error
Error while loading /usr/sbin/dpkg-split: No such file or directory
Error while loading /usr/sbin/dpkg-deb: No such file or directory
```
Moonfire NVR's `Dockerfile` has some built-in debugging tools:
* Each stage saves some debug info to `/docker-build-debug/<stage>`, and
the `deploy` stage preserves the output from previous stages. The debug
info includes:
* output (stdout + stderr) from the build script, running long operations
through the `time` command.
* `find -ls` output on cache mounts before and after.
* Each stage accepts a `INVALIDATE_CACHE_<stage>` argument. You can use eg
`--build-arg=INVALIDATE_CACHE_BUILD_SERVER=$(date +%s)` to force the
`build-server` stage to be rebuilt rather than use cached Docker layers.
### Release procedure
Releases are currently a bit manual. From a completely clean git work tree,
1. manually verify the current commit is pushed to github's master branch and
has a green checkmark indicating CI passed.
2. update version in `CHANGELOG.md`.
3. run commands:
```bash
VERSION=x.y.z
git commit -am "prepare version ${VERSION}"
git tag -a "v${VERSION}" -m "version ${VERSION}"
./release.bash
git push
git push origin "v${VERSION}"
```
The `release.bash` script needs [`jq`](https://stedolan.github.io/jq/)
installed to work.
## Non-Docker setup
You may prefer building without Docker on the host. Moonfire NVR should run
natively on any Unix-like system. It's been tested on Linux and macOS.
(In theory [Windows Subsystem for
Moonfire NVR should run natively on any Unix-like system. It's been tested on
Linux, macOS, and FreeBSD. (In theory [Windows Subsystem for
Linux](https://docs.microsoft.com/en-us/windows/wsl/about) should also work.
Please speak up if you try it.)
On macOS systems native builds may be noticeably faster than using Docker's
Linux VM and filesystem overlay.
To build the server, you will need the following C libraries installed:
* [SQLite3](https://www.sqlite.org/), at least version 3.8.2.
(You can skip this if you compile with `--features=bundled` and
don't mind the `moonfire-nvr sql` command not working.)
* [`ncursesw`](https://www.gnu.org/software/ncurses/), the UTF-8 version of
the `ncurses` library.
To build the server, you will need [SQLite3](https://www.sqlite.org/). You
can skip this if compiling with `--features=rusqlite/bundled` and don't
mind the `moonfire-nvr sql` command not working.
To build the UI, you'll need a [nodejs](https://nodejs.org/en/download/) release
in "Maintenance" or "LTS" status: currently v14, v16, or v18.
@ -184,7 +46,6 @@ most non-Rust dependencies:
```console
$ sudo apt-get install \
build-essential \
libncurses-dev \
libsqlite3-dev \
pkgconf \
sqlite3 \
@ -253,72 +114,24 @@ $ nvr run
with `cargo build` rather than `cargo build --release`, for a faster build
cycle and slower performance.)
Note this `nvr` is a little different than the `nvr` shell script you create
when following the [install instructions](install.md). With that shell wrapper,
`nvr run` will create and run a detached Docker container with some extra
arguments specified in the script. This `nvr run` will directly run from the
terminal, with no extra arguments, until you abort with Ctrl-C. Likewise,
some of the shell script's subcommands that wrap Docker (`start`, `stop`, and
`logs`) have no parallel with this `nvr`.
## Release procedure
### Running as a `systemd` service
Releases are currently a bit manual. From a completely clean git work tree,
If you want to deploy a non-Docker build on Linux, you may want to use
`systemd`. Create `/etc/systemd/system/moonfire-nvr.service`:
1. manually verify the current commit is pushed to github's master branch and
has a green checkmark indicating CI passed.
2. update versions:
* update `server/Cargo.toml` version by hand; run `cargo test --workspace`
to update `Cargo.lock`.
* ensure `README.md` and `CHANGELOG.md` refer to the new version.
3. run commands:
```bash
VERSION=x.y.z
git commit -am "prepare version ${VERSION}"
git tag -a "v${VERSION}" -m "version ${VERSION}"
git push origin "v${VERSION}"
```
```ini
[Unit]
Description=Moonfire NVR
After=network-online.target
[Service]
ExecStart=/usr/local/bin/moonfire-nvr run
Environment=TZ=:/etc/localtime
Environment=MOONFIRE_FORMAT=google-systemd
Environment=MOONFIRE_LOG=info
Environment=RUST_BACKTRACE=1
Type=simple
User=moonfire-nvr
Restart=on-failure
CPUAccounting=true
MemoryAccounting=true
BlockIOAccounting=true
[Install]
WantedBy=multi-user.target
```
You'll also need a `/etc/moonfire-nvr.toml`:
```toml
[[binds]]
ipv4 = "0.0.0.0:8080"
allowUnauthenticatedPermissions = { viewVideo = true }
[[binds]]
unix = "/var/lib/moonfire-nvr/sock"
ownUidIsPrivileged = true
```
Note this configuration is insecure. You can change that via replacing the
`allowUnauthenticatedPermissions` here as described in [Securing Moonfire NVR
and exposing it to the Internet](secure.md).
See [ref/config.md](../ref/config.md) for more about `/etc/moonfire-nvr.toml`.
Some handy commands:
```console
$ sudo systemctl daemon-reload # reload configuration files
$ sudo systemctl start moonfire-nvr # start the service now
$ sudo systemctl stop moonfire-nvr # stop the service now (but don't wait for it finish stopping)
$ sudo systemctl status moonfire-nvr # show if the service is running and the last few log lines
$ sudo systemctl enable moonfire-nvr # start the service on boot
$ sudo systemctl disable moonfire-nvr # don't start the service on boot
$ sudo journalctl --unit=moonfire-nvr --since='-5 min' --follow # look at recent logs and await more
```
See the [systemd](http://www.freedesktop.org/wiki/Software/systemd/)
documentation for more information. The [manual
pages](http://www.freedesktop.org/software/systemd/man/) for `systemd.service`
and `systemctl` may be of particular interest.
The rest should happen automatically—the tag push will fire off a GitHub
Actions workflow which creates a release, cross-compiles statically compiled
binaries for three different platforms, and uploads them to the release.

View File

@ -1,14 +1,14 @@
# Installing Moonfire NVR <!-- omit in toc -->
* [Downloading, installing, and configuring Moonfire NVR with Docker](#downloading-installing-and-configuring-moonfire-nvr-with-docker)
* [Downloading, installing, and configuring Moonfire NVR](#downloading-installing-and-configuring-moonfire-nvr)
* [Dedicated hard drive setup](#dedicated-hard-drive-setup)
* [Completing configuration through the UI](#completing-configuration-through-the-ui)
* [Starting it up](#starting-it-up)
## Downloading, installing, and configuring Moonfire NVR with Docker
## Downloading, installing, and configuring Moonfire NVR
This document describes how to download, install, and configure Moonfire NVR
via the prebuilt Docker images available for x86-64, arm64, and arm. If you
via the prebuilt Linux binaries available for x86-64, arm64, and arm. If you
instead want to build Moonfire NVR yourself, see the [Build
instructions](build.md).
@ -18,17 +18,16 @@ left, and pick the [latest tagged version](https://github.com/scottlamb/moonfire
![Selecting a version of install instructions](install-version.png)
Next, install [Docker](https://www.docker.com/) if you haven't already,
and verify `sudo docker run --rm hello-world` works.
Download the binary for your platform from the matching GitHub release.
Install it as `/usr/local/bin/moonfire-nvr` and ensure it is executable, e.g.
for version `v0.7.8` on Intel machines:
<details>
<summary><tt>sudo</tt> or not?</summary>
If you prefer to save typing by not prefixing all `docker` and `nvr` commands
with `sudo`, see [Docker docs: Manage Docker as a non-root
user](https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user).
Note `docker` access is equivalent to root access security-wise.
</details>
```console
$ VERSION=v0.7.8
$ ARCH=x86_64-unknown-linux-musl
$ curl -OL "https://github.com/scottlamb/moonfire-nvr/releases/download/$VERSION/moonfire-nvr-$VERSION-$ARCH"
$ sudo install -m 755 "moonfire-nvr-$VERSION-$ARCH" /usr/local/bin/moonfire-nvr
```
Next, you'll need to set up your filesystem and the Moonfire NVR user.
@ -49,22 +48,12 @@ On most Linux systems, you can create the user as follows:
$ sudo useradd --user-group --create-home --home /var/lib/moonfire-nvr moonfire-nvr
```
and create a script called `nvr` to run Moonfire NVR as the intended host user.
This script supports running Moonfire NVR's various administrative commands interactively
and managing a long-lived Docker container for its web interface.
As you set up this script, adjust the `tz` variable as appropriate for your
time zone.
Use your favorite editor to create `/etc/moonfire-nvr.toml` and
`/usr/local/bin/nvr`, starting from the configurations below:
Use your favorite editor to create `/etc/moonfire-nvr.toml`,
starting from the configurations below:
```console
$ sudo nano /etc/moonfire-nvr.toml
(see below for contents)
$ sudo nano /usr/local/bin/nvr
(see below for contents)
$ sudo chmod a+rx /usr/local/bin/nvr
```
`/etc/moonfire-nvr.toml` (see [ref/config.md](../ref/config.md) for more explanation):
@ -78,79 +67,10 @@ unix = "/var/lib/moonfire-nvr/sock"
ownUidIsPrivileged = true
```
`/usr/local/bin/nvr`:
```bash
#!/bin/bash -e
# Set your timezone here.
tz="America/Los_Angeles"
image_name="scottlamb/moonfire-nvr:v0.7.7"
container_name="moonfire-nvr"
common_docker_run_args=(
--mount=type=bind,source=/var/lib/moonfire-nvr,destination=/var/lib/moonfire-nvr
--mount=type=bind,source=/etc/moonfire-nvr.toml,destination=/etc/moonfire-nvr.toml
# Add additional mount lines here for each sample file directory
# outside of /var/lib/moonfire-nvr, e.g.:
# --mount=type=bind,source=/media/nvr/sample,destination=/media/nvr/sample
--user="$(id -u moonfire-nvr):$(id -g moonfire-nvr)"
# This avoids errors with broken seccomp on older 32-bit hosts.
# https://github.com/moby/moby/issues/40734
--security-opt=seccomp:unconfined
# This is the simplest way of configuring networking, although
# you can use e.g. --publish=8080:8080 in the run) case below if you
# prefer.
--network=host
# docker's default log driver won't rotate logs properly, and will throw
# away logs when you destroy and recreate the container. Using journald
# solves these problems.
# https://docs.docker.com/config/containers/logging/configure/
--log-driver=journald
--log-opt="tag=moonfire-nvr"
--env=RUST_BACKTRACE=1
--env=TZ=":${tz}"
)
case "$1" in
run)
shift
exec docker run \
--detach=true \
--restart=unless-stopped \
"${common_docker_run_args[@]}" \
--name="${container_name}" \
"${image_name}" \
run \
"$@"
;;
start|stop|logs|rm)
exec docker "$@" "${container_name}"
;;
pull)
exec docker pull "${image_name}"
;;
*)
exec docker run \
--interactive=true \
--tty \
--rm \
"${common_docker_run_args[@]}" \
"${image_name}" \
"$@"
;;
esac
```
then try it out by initializing the database:
Then initialize the database:
```console
$ sudo nvr init
$ sudo -u moonfire-nvr moonfire-nvr init
```
This will create a directory `/var/lib/moonfire-nvr/db` with a SQLite3 database
@ -189,10 +109,6 @@ system will boot successfully even when the hard drive is unavailable (such as
when your external USB storage is unmounted). This can be helpful when
recovering from problems.
Add a new `--mount` line to your Docker wrapper script `/usr/local/bin/nvr`
to expose the new sample directory `/media/nvr/sample` to the Docker container,
right where a comment mentions "Additional mount lines".
### Completing configuration through the UI
Once your system is set up, it's time to initialize an empty database
@ -200,16 +116,17 @@ and add the cameras and sample directories. You can do this
by using the `moonfire-nvr` binary's text-based configuration tool.
```console
$ sudo nvr config 2>debug-log
$ sudo -u moonfire-nvr moonfire-nvr config 2>debug-log
```
<details>
<summary>Did it return without doing anything?</summary>
If `nvr config` returns you to the console prompt right away, look in the
`debug-log` file for why. One common reason is that you have Moonfire NVR
running; you'll need to shut it down first. Try `nvr stop` before `nvr config`
and `nvr start` afterward.
If `moonfire-nvr config` returns you to the console prompt right away, look in
the `debug-log` file for why. One common reason is that you have Moonfire NVR
running; you'll need to shut it down first. If you are running a systemd
service as described below, try `sudo systemctl stop moonfire-nvr` before
editing the config and `sudo systemctl start moonfire-nvr` after.
</details>
In the user interface,
@ -277,15 +194,58 @@ starting it in this configuration to try it out, particularly if the machine
it's running on is behind a home router's firewall. You might not; in that case
read through [secure the system](secure.md) first.
This command will start a detached Docker container for the web interface.
It will automatically restart when your system does.
Assuming you want to proceed, you can launch Moonfire NVR through `systemd`.
Create `/etc/systemd/system/moonfire-nvr.service`:
```console
$ sudo nvr run
```ini
[Unit]
Description=Moonfire NVR
After=network-online.target
# If you use an external hard drive, uncomment this with a reference to the
# mount point as written in `/etc/fstab`.
# RequiresMountsFor=/media/nvr
[Service]
ExecStart=/usr/local/bin/moonfire-nvr run
Environment=TZ=:/etc/localtime
Environment=MOONFIRE_FORMAT=systemd
Environment=MOONFIRE_LOG=info
Environment=RUST_BACKTRACE=1
Type=simple
User=moonfire-nvr
Restart=on-failure
CPUAccounting=true
MemoryAccounting=true
BlockIOAccounting=true
[Install]
WantedBy=multi-user.target
```
You can temporarily disable the service via `nvr stop` and restart it later via
`nvr start`. You'll need to do this before and after using `nvr config`.
Then start it up as follows:
```console
$ sudo systemctl daemon-reload # read in the new config file
$ sudo systemctl enable --now moonfire-nvr # start the service now and on boot
```
Some handy commands:
```console
$ sudo systemctl daemon-reload # reload configuration files
$ sudo systemctl start moonfire-nvr # start the service now without enabling on boot
$ sudo systemctl stop moonfire-nvr # stop the service now (but don't wait for it finish stopping)
$ sudo systemctl status moonfire-nvr # show if the service is running and the last few log lines
$ sudo systemctl enable moonfire-nvr # start the service on boot
$ sudo systemctl disable moonfire-nvr # don't start the service on boot
$ sudo journalctl --unit=moonfire-nvr --since='-5 min' --follow # look at recent logs and await more
```
See the [systemd](http://www.freedesktop.org/wiki/Software/systemd/)
documentation for more information. The [manual
pages](http://www.freedesktop.org/software/systemd/man/) for `systemd.service`
and `systemctl` may be of particular interest.
The HTTP interface is accessible on port 8080; if your web browser is running
on the same machine, you can access it at

View File

@ -62,11 +62,11 @@ SQLite database:
no longer in the dangerous mode.
Next ensure Moonfire NVR is not running and does not automatically restart if
the system is rebooted during the upgrade. If you followed the Docker
the system is rebooted during the upgrade. If you followed the standard
instructions, you can do this as follows:
```console
$ sudo nvr stop
$ sudo systemctl disable --now moonfire-nvr
```
Then back up your SQLite database. If you are using the default path, you can
@ -92,34 +92,27 @@ manual for write-ahead logging](https://www.sqlite.org/wal.html):
Run the upgrade procedure using the new software binary.
```console
$ sudo nvr pull # updates the docker image to the latest binary
$ sudo nvr upgrade # runs the upgrade
```
As a rule of thumb, on a Raspberry Pi 4 with a 1 GiB database, an upgrade might
take about four minutes for each schema version and for the final vacuum.
Next, you can run the system in read-only mode, although you'll find this only
works in the "insecure" setup. (Authorization requires writing the database.)
To just run directly within the console until you hit ctrl-C, use the following
command:
```console
$ sudo nvr rm
$ sudo nvr run --read-only
$ sudo -u moonfire-nvr moonfire-nvr run --read-only
```
Go to the web interface and ensure the system is operating correctly. If
you detect a problem now, you can copy the old database back over the new one
and edit your `nvr` script to use the corresponding older Docker image. If
you detect a problem after enabling read-write operation, a restore will be
more complicated.
and go back to the prior release. If you detect a problem after enabling
read-write operation, a restore will be more complicated.
Once you're satisfied, restart the system in read-write mode:
Once you're satisfied, ctrl-C and start the system in read-write mode:
```console
$ sudo nvr stop
$ sudo nvr rm
$ sudo nvr run
$ sudo systemctl enable --now moonfire-nvr
```
Hopefully your system is functioning correctly. If not, there are two options
@ -137,7 +130,8 @@ for restore; neither are easy:
* undo the changes by hand. There's no documentation on this; you'll need
to read the code and come up with a reverse transformation.
The `nvr check` command will show you what problems exist on your system.
The `sudo -u moonfire-nvr moonfire-nvr check` command will show you what
problems exist on your system.
### Unversioned to version 0

View File

@ -161,8 +161,8 @@ your browser. See [How to secure Nginx with Let's Encrypt on Ubuntu
## 6. Reconfigure Moonfire NVR
If you follow the recommended Docker setup, your `/etc/moonfire-nvr.toml`
will contain this line:
If you follow the recommended setup, your `/etc/moonfire-nvr.toml` will contain
this line:
```toml
allowUnauthenticatedPermissions = { viewVideo = true }
@ -185,18 +185,16 @@ This change has two effects:
See also [ref/config.md](../ref/config.md) for more about the configuration file.
If the webserver is running on the same machine as Moonfire NVR, you might
also change `--publish=8080:8080` to `--publish=127.0.0.1:8080:8080` in your
`/usr/local/bin/nvr` script, preventing other machines on the network from
impersonating the proxy, effectively allowing them to lie about the client's IP
and protocol.
also change the `ipv4 = "0.0.0.0:8080"` line in `/etc/moonfire-nvr/toml` to
`ipv4 = "127.0.0.1:8080"`, so that only the local host can directly connect to
Moonfire NVR. If other machines can connect directly, they can impersonate
the proxy, which would effectively allow them to lie about the client's IP and
protocol.
To make this take effect, you'll need to stop the running Docker container,
delete it, and create/run a new one:
To make this take effect, you'll need to restart Moonfire NVR:
```console
$ sudo nvr stop
$ sudo nvr rm
$ sudo nvr run
$ sudo systemctl restart moonfire-nvr
```
## 7. Configure the webserver

View File

@ -11,7 +11,6 @@ need more help.
* [Camera stream errors](#camera-stream-errors)
* [Problems](#problems)
* [Server errors](#server-errors)
* [`clock_gettime failed: EPERM: Operation not permitted`](#clock_gettime-failed-eperm-operation-not-permitted)
* [`Error: pts not monotonically increasing; got 26615520 then 26539470`](#error-pts-not-monotonically-increasing-got-26615520-then-26539470)
* [Out of disk space](#out-of-disk-space)
* [Database or filesystem corruption errors](#database-or-filesystem-corruption-errors)
@ -29,9 +28,8 @@ While Moonfire NVR is running, logs will be written to stderr.
* When running the configuration UI, you typically should redirect stderr
to a text file to avoid poor interaction between the interactive stdout
output and the logging. If you use the recommended
`nvr config 2>debug-log` command, output will be in the `debug-log` file.
* When running detached through Docker, Docker saves the logs for you.
Try `nvr logs` or `docker logs moonfire-nvr`.
`moonfire-nvr config 2>debug-log` command, output will be in the
`debug-log` file.
* When running through systemd, stderr will be redirected to the journal.
Try `sudo journalctl --unit moonfire-nvr` to view the logs. You also
likely want to set `MOONFIRE_FORMAT=systemd` to format logs as
@ -56,8 +54,6 @@ Logging options are controlled by environment variables:
consumption.
* Errors include a backtrace if `RUST_BACKTRACE=1` is set.
If you use Docker, set these via Docker's `--env` argument.
With `MOONFIRE_FORMAT` left unset, log events look as follows:
```text
@ -191,23 +187,6 @@ quickly enough. In the latter case, you'll likely see a
### Server errors
#### `clock_gettime failed: EPERM: Operation not permitted`
If commands fail with an error like the following, you're likely running
Docker with an overly restrictive `seccomp` setup. [This stackoverflow
answer](https://askubuntu.com/questions/1263284/apt-update-throws-signature-error-in-ubuntu-20-04-container-on-arm/1264921#1264921) describes the
problem in more detail. The simplest solution is to add
`--security-opt=seccomp:unconfined` to your Docker commandline.
If you are using the recommended `/usr/local/bin/nvr` wrapper script,
add this option to the `common_docker_run_args` section.
```console
$ sudo docker run --rm -it moonfire-nvr:latest
clock_gettime failed: EPERM: Operation not permitted
This indicates a broken environment. See the troubleshooting guide.
```
#### `Error: pts not monotonically increasing; got 26615520 then 26539470`
If your streams cut out and you see error messages like this one in Moonfire
@ -322,8 +301,7 @@ mechanism to fix old timestamps after the fact. Ideas and help welcome; see
#### `moonfire-nvr config` displays garbage
This happens if you're not using the premade Docker containers and have
configured your machine is configured to a non-UTF-8 locale, due to
This may happen if your machine is configured to a non-UTF-8 locale, due to
gyscos/Cursive#13. As a workaround, try setting the environment variable
`LC_ALL=C.UTF-8`.

View File

@ -1,51 +0,0 @@
#!/bin/bash
# This file is part of Moonfire NVR, a security camera network video recorder.
# Copyright (C) 2021 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
# SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception
# Pushes a release to Docker. See guides/build.md#release-procedure.
set -o errexit
set -o pipefail
set -o xtrace
set_latest() {
# Our images are manifest lists (for multiple architectures).
# "docker tag" won't copy those. The technique below is adopted from here:
# https://github.com/docker/buildx/issues/459#issuecomment-750011371
local image="$1"
local hashes=($(docker manifest inspect "${image}:${version}" |
jq --raw-output '.manifests[].digest'))
time docker manifest rm "${image}:latest" || true
time docker manifest create \
"${image}:latest" \
"${hashes[@]/#/${image}@}"
time docker manifest push "${image}:latest"
}
build_and_push() {
local image="$1"
local target="$2"
time docker buildx build \
--push \
--tag="${image}:${version}" \
--target="${target}" \
--platform=linux/amd64,linux/arm64/v8,linux/arm/v7 \
-f docker/Dockerfile .
}
version="$(git describe --dirty)"
if [[ ! "${version}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Expected a vX.Y.Z version tag, got ${version}." >&2
exit 1
fi
if [[ -n "$(git status --porcelain)" ]]; then
echo "git status says there's extra stuff in this directory." >&2
exit 1
fi
build_and_push scottlamb/moonfire-nvr deploy
build_and_push scottlamb/moonfire-dev dev
set_latest scottlamb/moonfire-nvr
set_latest scottlamb/moonfire-dev

46
server/Cargo.lock generated
View File

@ -303,10 +303,8 @@ dependencies = [
"lazy_static",
"libc",
"log",
"maplit",
"ncurses",
"signal-hook",
"term_size",
"termion",
"unicode-segmentation",
"unicode-width",
]
@ -982,12 +980,6 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]]
name = "matchers"
version = "0.1.0"
@ -1198,17 +1190,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96a1fe2275b68991faded2c80aa4a33dba398b77d276038b8f50701a22e55918"
[[package]]
name = "ncurses"
version = "5.101.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e2c5d34d72657dc4b638a1c25d40aae81e4f1c699062f72f467237920752032"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "nix"
version = "0.26.1"
@ -1338,6 +1319,12 @@ dependencies = [
"libc",
]
[[package]]
name = "numtoa"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
[[package]]
name = "odds"
version = "0.4.0"
@ -1581,6 +1568,15 @@ dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_termios"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
dependencies = [
"redox_syscall",
]
[[package]]
name = "reffers"
version = "0.7.0"
@ -1994,13 +1990,15 @@ dependencies = [
]
[[package]]
name = "term_size"
version = "0.3.2"
name = "termion"
version = "1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e"
dependencies = [
"libc",
"winapi",
"numtoa",
"redox_syscall",
"redox_termios",
]
[[package]]

View File

@ -35,7 +35,7 @@ bpaf = { version = "0.9.1", features = ["autocomplete", "bright-color", "derive"
bytes = "1"
byteorder = "1.0"
chrono = "0.4.23"
cursive = "0.20.0"
cursive = { version = "0.20.0", default-features = false, features = ["termion-backend"] }
db = { package = "moonfire-db", path = "db" }
futures = "0.3"
fnv = "1.0"

14
server/Cross.toml Normal file
View File

@ -0,0 +1,14 @@
[build.env]
volumes = [
# For the (optional) `bundled-ui` feature.
"UI_BUILD_DIR",
# For tests which use the `America/Los_Angeles` zone.
"ZONEINFO=/usr/share/zoneinfo",
]
passthrough = [
# Cross's default docker image doesn't install `git`, so `git_version!` doesn't work.
# Allow passing through the version via this environment variable.
"VERSION",
]

View File

@ -6,13 +6,17 @@
use std::fmt::Write;
use std::path::{Path, PathBuf};
use std::process::ExitCode;
use std::process::Command;
const UI_DIR: &str = "../ui/build";
const UI_BUILD_DIR_ENV_VAR: &str = "UI_BUILD_DIR";
const DEFAULT_UI_BUILD_DIR: &str = "../ui/build";
type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
fn ensure_link(original: &Path, link: &Path) {
match std::fs::read_link(link) {
Ok(dst) if dst == original => return,
Ok(_) => std::fs::remove_file(link).expect("removing stale symlink should succeed"),
Err(e) if e.kind() != std::io::ErrorKind::NotFound => {
panic!("couldn't create link {link:?} to original path {original:?}: {e}")
}
@ -67,37 +71,30 @@ fn stringify_files(files: &FileMap) -> Result<String, std::fmt::Error> {
Ok(buf)
}
fn main() -> ExitCode {
// Explicitly declare dependencies, so this doesn't re-run if other source files change.
println!("cargo:rerun-if-changed=build.rs");
fn handle_bundled_ui() -> Result<(), BoxError> {
// Nothing to do if the feature is off. cargo will re-run if features change.
if !cfg!(feature = "bundled-ui") {
return ExitCode::SUCCESS;
return Ok(());
}
let ui_dir =
std::env::var(UI_BUILD_DIR_ENV_VAR).unwrap_or_else(|_| DEFAULT_UI_BUILD_DIR.to_owned());
// If the feature is on, also re-run if the actual UI files change.
println!("cargo:rerun-if-changed={UI_DIR}");
println!("cargo:rerun-if-env-changed={UI_BUILD_DIR_ENV_VAR}");
println!("cargo:rerun-if-changed={ui_dir}");
let out_dir: PathBuf = std::env::var_os("OUT_DIR")
.expect("cargo should set OUT_DIR")
.into();
let abs_ui_dir = std::fs::canonicalize(UI_DIR)
.expect("ui dir should be accessible. Did you run `npm run build` first?");
let abs_ui_dir = std::fs::canonicalize(&ui_dir).map_err(|e| format!("ui dir {ui_dir:?} should be accessible. Did you run `npm run build` first?\n\ncaused by:\n{e}"))?;
let mut files = FileMap::default();
for entry in walkdir::WalkDir::new(&abs_ui_dir) {
let entry = match entry {
Ok(e) => e,
Err(e) => {
eprintln!(
"walkdir failed. Did you run `npm run build` first?\n\n\
caused by:\n{e}"
);
return ExitCode::FAILURE;
}
};
let entry = entry.map_err(|e| {
format!("walkdir failed. Did you run `npm run build` first?\n\ncaused by:\n{e}")
})?;
if !entry.file_type().is_file() {
continue;
}
@ -135,6 +132,13 @@ fn main() -> ExitCode {
);
}
if !files.contains_key("index.html") {
return Err(format!(
"No `index.html` within {ui_dir:?}. Did you run `npm run build` first?"
)
.into());
}
let files = stringify_files(&files).expect("write to String should succeed");
let mut out_rs_path = std::path::PathBuf::new();
out_rs_path.push(&out_dir);
@ -145,5 +149,49 @@ fn main() -> ExitCode {
out_link_path.push(&out_dir);
out_link_path.push("ui_files");
ensure_link(&abs_ui_dir, &out_link_path);
return ExitCode::SUCCESS;
Ok(())
}
fn handle_version() -> Result<(), BoxError> {
println!("cargo:rerun-if-env-changed=VERSION");
if std::env::var("VERSION").is_ok() {
return Ok(());
}
// Get version from `git describe`. Inspired by the `git-version` crate.
// We don't use that directly because `cross`'s default docker image doesn't install `git`,
// and thus we need the environment variable pass-through above.
// Avoid reruns when the output doesn't meaningfully change. I don't think this is quite right:
// it won't recognize toggling between `-dirty` and not. But it'll do.
let dir = Command::new("git")
.arg("rev-parse")
.arg("--git-dir")
.output()?
.stdout;
let dir = String::from_utf8(dir).unwrap();
let dir = dir.strip_suffix('\n').unwrap();
println!("cargo:rerun-if-changed={dir}/logs/HEAD");
println!("cargo:rerun-if-changed={dir}/index");
// Plumb the version through.
let version = Command::new("git")
.arg("describe")
.arg("--always")
.arg("--dirty")
.output()?
.stdout;
let version = String::from_utf8(version).unwrap();
let version = version.strip_suffix('\n').unwrap();
println!("cargo:rustc-env=VERSION={version}");
Ok(())
}
fn main() -> Result<(), BoxError> {
// Explicitly declare dependencies, so this doesn't re-run if other source files change.
println!("cargo:rerun-if-changed=build.rs");
handle_bundled_ui()?;
handle_version()?;
Ok(())
}

View File

@ -22,6 +22,7 @@ struct BuildFile {
encoding: FileEncoding,
}
#[allow(unused)] // it's valid for a UI to have all uncompressed files or vice versa.
#[derive(Copy, Clone)]
enum FileEncoding {
Uncompressed,

View File

@ -25,7 +25,8 @@ mod bundled_ui;
const DEFAULT_DB_DIR: &str = "/var/lib/moonfire-nvr/db";
const VERSION: &str = git_version::git_version!(args = ["--always", "--dirty"]);
// This is either in the environment when `cargo` is invoked or set from within `build.rs`.
const VERSION: &str = env!("VERSION");
/// Moonfire NVR: security camera network video recorder.
#[derive(Bpaf, Debug)]