diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b12333d..863a558 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -25,7 +25,7 @@ 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: `git describe --dirty` + `moonfire-nvr --version` + - If building from git: `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):** diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a6e8ee..ce524ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,10 +17,15 @@ jobs: extra_args: "--features nightly --benches" - rust: stable extra_components: rustfmt - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 + with: + # `git describe` output gets baked into the binary for `moonfire-nvr --version`. + # Fetch all revs so it can see tag history. + fetch-depth: 0 + filter: 'tree:0' - name: Cache uses: actions/cache@v2 with: @@ -62,9 +67,9 @@ jobs: strategy: matrix: node: [ "14", "16", "18" ] - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions/setup-node@v2 with: node-version: ${{ matrix.node }} diff --git a/README.md b/README.md index db0c8f6..6804c8c 100644 --- a/README.md +++ b/README.md @@ -35,10 +35,9 @@ There's no support yet for motion detection, no https/TLS support (you'll need a proxy server, as described [here](guide/secure.md)), and only a console-based (rather than web-based) configuration UI. -Moonfire NVR is currently at version 0.7.7. Until version 1.0, there will be no -compatibility guarantees: configuration and storage formats may change from -version to version. There is an [upgrade procedure](guide/schema.md) but it is -not for the faint of heart. +Moonfire NVR is pre-1.0, with will be no compatibility guarantees: +configuration and storage formats may change from version to version. There is +an [upgrade procedure](guide/schema.md) but it is not for the faint of heart. I hope to add features such as video analytics. In time, we can build a full-featured hobbyist-oriented multi-camera NVR that requires nothing but diff --git a/guide/build.md b/guide/build.md index c477ee8..d1c016b 100644 --- a/guide/build.md +++ b/guide/build.md @@ -141,10 +141,7 @@ 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 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. +2. update version in `CHANGELOG.md`. 3. run commands: ```bash VERSION=x.y.z diff --git a/server/Cargo.lock b/server/Cargo.lock index 0a2b3a2..0082dba 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -621,6 +621,28 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "git-version" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b0decc02f4636b9ccad390dcbe77b722a77efedfa393caf8379a51d5c61899" +dependencies = [ + "git-version-macro", + "proc-macro-hack", +] + +[[package]] +name = "git-version-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe69f1cbdb6e28af2bac214e943b99ce8a0a06b447d15d3e61161b0423139f3f" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.107", +] + [[package]] name = "h2" version = "0.3.15" @@ -1092,7 +1114,7 @@ dependencies = [ [[package]] name = "moonfire-nvr" -version = "0.7.7" +version = "0.0.0" dependencies = [ "base64", "blake3", @@ -1104,6 +1126,7 @@ dependencies = [ "flate2", "fnv", "futures", + "git-version", "h264-reader", "http", "http-serve", @@ -1429,6 +1452,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + [[package]] name = "proc-macro2" version = "1.0.63" diff --git a/server/Cargo.toml b/server/Cargo.toml index 76ceaf4..e9e7852 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "moonfire-nvr" -version = "0.7.7" +version = "0.0.0" authors = ["Scott Lamb "] edition = "2021" resolver = "2" @@ -73,6 +73,7 @@ ulid = "1.0.0" url = "2.1.1" uuid = { version = "1.1.2", features = ["serde", "std", "v4"] } flate2 = "1.0.26" +git-version = "0.3.5" [build-dependencies] blake3 = "1.0.0" diff --git a/server/db/check.rs b/server/db/check.rs index 70c1e7f..0f3065c 100644 --- a/server/db/check.rs +++ b/server/db/check.rs @@ -54,7 +54,10 @@ pub fn run(conn: &mut rusqlite::Connection, opts: &Options) -> Result Result<(), Error> { } /// Gets the schema version from the given database connection. -/// A fully initialized database will return `Ok(Some(version))` where `version` is an integer that -/// can be compared to `EXPECTED_VERSION`. An empty database will return `Ok(None)`. A partially -/// initialized database (in particular, one without a version row) will return some error. +/// A fully initialized database will return `Ok(Some(schema_version))` where `schema_version` is +/// an integer that can be compared to `EXPECTED_SCHEMA_VERSION`. An empty database will return +/// `Ok(None)`. A partially initialized database (in particular, one without a version row) will +/// return some error. pub fn get_schema_version(conn: &rusqlite::Connection) -> Result, Error> { let ver_tables: i32 = conn.query_row_and_then( "select count(*) from sqlite_master where name = 'version'", @@ -2282,11 +2283,11 @@ pub(crate) fn check_schema_version(conn: &rusqlite::Connection) -> Result<(), Er ."), ) }; - match ver.cmp(&EXPECTED_VERSION) { + match ver.cmp(&EXPECTED_SCHEMA_VERSION) { std::cmp::Ordering::Less => bail!( FailedPrecondition, msg( - "database schema version {ver} is too old (expected {EXPECTED_VERSION}); \ + "database schema version {ver} is too old (expected {EXPECTED_SCHEMA_VERSION}); \ see upgrade instructions in guide/upgrade.md" ), ), @@ -2294,7 +2295,7 @@ pub(crate) fn check_schema_version(conn: &rusqlite::Connection) -> Result<(), Er std::cmp::Ordering::Greater => bail!( FailedPrecondition, msg( - "database schema version {ver} is too new (expected {EXPECTED_VERSION}); \ + "database schema version {ver} is too new (expected {EXPECTED_SCHEMA_VERSION}); \ must use a newer binary to match" ), ), diff --git a/server/db/upgrade/mod.rs b/server/db/upgrade/mod.rs index 4d61ddb..77e3591 100644 --- a/server/db/upgrade/mod.rs +++ b/server/db/upgrade/mod.rs @@ -6,7 +6,7 @@ //! //! See `guide/schema.md` for more information. -use crate::db::{self, EXPECTED_VERSION}; +use crate::db::{self, EXPECTED_SCHEMA_VERSION}; use base::{bail, Error}; use nix::NixPath; use rusqlite::params; @@ -23,8 +23,6 @@ mod v4_to_v5; mod v5_to_v6; mod v6_to_v7; -const UPGRADE_NOTES: &str = concat!("upgraded using moonfire-db ", env!("CARGO_PKG_VERSION")); - #[derive(Debug)] pub struct Args<'a> { pub sample_file_dir: Option<&'a std::path::Path>, @@ -46,7 +44,12 @@ fn set_journal_mode(conn: &rusqlite::Connection, requested: &str) -> Result<(), Ok(()) } -fn upgrade(args: &Args, target_ver: i32, conn: &mut rusqlite::Connection) -> Result<(), Error> { +fn upgrade( + args: &Args, + target_schema_ver: i32, + sw_version: &str, + conn: &mut rusqlite::Connection, +) -> Result<(), Error> { let upgraders = [ v0_to_v1::run, v1_to_v2::run, @@ -58,25 +61,31 @@ fn upgrade(args: &Args, target_ver: i32, conn: &mut rusqlite::Connection) -> Res ]; { - assert_eq!(upgraders.len(), db::EXPECTED_VERSION as usize); - let old_ver = conn.query_row("select max(id) from version", params![], |row| row.get(0))?; - if old_ver > EXPECTED_VERSION { + assert_eq!(upgraders.len(), db::EXPECTED_SCHEMA_VERSION as usize); + let old_schema_ver = + conn.query_row("select max(id) from version", params![], |row| row.get(0))?; + if old_schema_ver > EXPECTED_SCHEMA_VERSION { bail!( FailedPrecondition, - msg("database is at version {old_ver}, later than expected {EXPECTED_VERSION}"), + msg("database is at version {old_schema_ver}, \ + later than expected {EXPECTED_SCHEMA_VERSION}"), ); - } else if old_ver < 0 { + } else if old_schema_ver < 0 { bail!( FailedPrecondition, - msg("Database is at negative version {old_ver}!") + msg("Database is at negative version {old_schema_ver}!") ); } info!( - "Upgrading database from version {} to version {}...", - old_ver, target_ver + "Upgrading database from schema version {} to schema version {}...", + old_schema_ver, target_schema_ver ); - for ver in old_ver..target_ver { - info!("...from version {} to version {}", ver, ver + 1); + for ver in old_schema_ver..target_schema_ver { + info!( + "...from schema version {} to schema version {}", + ver, + ver + 1 + ); let tx = conn.transaction()?; upgraders[ver as usize](args, &tx)?; tx.execute( @@ -84,7 +93,7 @@ fn upgrade(args: &Args, target_ver: i32, conn: &mut rusqlite::Connection) -> Res insert into version (id, unix_time, notes) values (?, cast(strftime('%s', 'now') as int32), ?) "#, - params![ver + 1, UPGRADE_NOTES], + params![ver + 1, format!("Upgraded using moonfire-nvr {sw_version}")], )?; tx.commit()?; } @@ -93,11 +102,11 @@ fn upgrade(args: &Args, target_ver: i32, conn: &mut rusqlite::Connection) -> Res Ok(()) } -pub fn run(args: &Args, conn: &mut rusqlite::Connection) -> Result<(), Error> { +pub fn run(args: &Args, sw_version: &str, conn: &mut rusqlite::Connection) -> Result<(), Error> { db::check_sqlite_version()?; db::set_integrity_pragmas(conn)?; set_journal_mode(conn, args.preset_journal)?; - upgrade(args, EXPECTED_VERSION, conn)?; + upgrade(args, EXPECTED_SCHEMA_VERSION, sw_version, conn)?; // As in "moonfire-nvr init": try for page_size=16384 and wal for the reasons explained there. // @@ -291,9 +300,10 @@ mod tests { no_vacuum: false, }, *ver, + "test", &mut upgraded, ) - .map_err(|e| err!(e, msg("upgrade to version {ver} failed")))?; + .map_err(|e| err!(e, msg("upgrade to schema version {ver} failed")))?; if let Some(f) = fresh_sql { compare(&upgraded, *ver, f)?; } diff --git a/server/src/cmds/upgrade/mod.rs b/server/src/cmds/upgrade/mod.rs index fc5056a..fa5a063 100644 --- a/server/src/cmds/upgrade/mod.rs +++ b/server/src/cmds/upgrade/mod.rs @@ -39,6 +39,7 @@ pub fn run(args: Args) -> Result { preset_journal: &args.preset_journal, no_vacuum: args.no_vacuum, }, + crate::VERSION, &mut conn, )?; Ok(0) diff --git a/server/src/main.rs b/server/src/main.rs index 2e222df..13ae1f8 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -25,9 +25,11 @@ mod bundled_ui; const DEFAULT_DB_DIR: &str = "/var/lib/moonfire-nvr/db"; +const VERSION: &str = git_version::git_version!(args = ["--always", "--dirty"]); + /// Moonfire NVR: security camera network video recorder. #[derive(Bpaf, Debug)] -#[bpaf(options, version)] +#[bpaf(options, version(VERSION))] enum Args { // See docstrings of `cmds::*::Args` structs for a description of the respective subcommands. Check(#[bpaf(external(cmds::check::args))] cmds::check::Args),