Skip to main content

max / makenotwork

Answer --version and --help before loading config The server loaded Config::from_env() before it looked at its arguments, so `makenotwork --version` panicked with MissingDatabaseUrl on any box without the unit's EnvironmentFile sourced. That is the one check that says which binary is executing rather than which artifact was placed, and it was the one service in the tree that could not answer it. Classification is a pure function over the arguments, so the flag set is tested without spawning a process. --help documents the env-var modes the deploy tooling drives (MNW_CHECK_CONFIG, MNW_CHECK_DOCS, SANDO_BOOT_SMOKE).
Author: Max Johnson <me@maxj.phd> · 2026-08-23 01:47 UTC
Signed with PGP, not checked
Commit: 7fb12e060b646e5daea487c1b2f16712ca39f255
Parent: e558838
1 file changed, +111 insertions, -0 deletions
@@ -23,6 +23,16 @@
23 23
24 24 #[tokio::main]
25 25 async fn main() {
26 + // Inspection flags first, before anything that reads the environment.
27 + // `--version` is how a deploy is verified: the controller's matrix says
28 + // which artifact was placed, only the running binary can say which one is
29 + // executing. Loading `Config` first made that check impossible on a box
30 + // without the unit's `EnvironmentFile` sourced, since a missing
31 + // `DATABASE_URL` panicked before the flag was ever looked at.
32 + if let Some(code) = handle_inspection_flags() {
33 + std::process::exit(code);
34 + }
35 +
26 36 dotenvy::dotenv().ok();
27 37
28 38 // Before any TLS client is built; see the function's own docs for why this
@@ -803,6 +813,107 @@
803 813 "hard-exit must outlast the bounded request + pool drains, with margin"
804 814 );
805 815
816 + /// What an inspection flag asks for. Both answers are printable without a
817 + /// database, an env file, or anything else the deployed environment supplies.
818 + #[derive(Debug, PartialEq, Eq)]
819 + enum Inspection {
820 + Version,
821 + Help,
822 + }
823 +
824 + /// Classify the arguments, without printing. Unknown arguments are left alone:
825 + /// the binary takes other flags (`--seed-examples`) later in startup, and this
826 + /// pass is not an argument parser.
827 + fn inspection_request<I, S>(args: I) -> Option<Inspection>
828 + where
829 + I: IntoIterator<Item = S>,
830 + S: AsRef<str>,
831 + {
832 + args.into_iter().find_map(|arg| match arg.as_ref() {
833 + "--version" | "-V" => Some(Inspection::Version),
834 + "--help" | "-h" => Some(Inspection::Help),
835 + _ => None,
836 + })
837 + }
838 +
839 + /// `name version (git sha)`, the same string `/health` reports, so the two can
840 + /// be compared directly when verifying a release.
841 + fn version_line() -> String {
842 + match option_env!("GIT_HASH") {
843 + Some(hash) if !hash.is_empty() => {
844 + format!("makenotwork {} ({hash})", env!("CARGO_PKG_VERSION"))
845 + }
846 + _ => format!("makenotwork {}", env!("CARGO_PKG_VERSION")),
847 + }
848 + }
849 +
850 + const HELP: &str = "\
851 + makenotwork, the MNW server.
852 +
853 + Usage: makenotwork [FLAGS]
854 +
855 + Flags:
856 + -V, --version Print the version and exit
857 + -h, --help Print this help and exit
858 + --seed-examples Seed the staging example marketplace, then exit. Refuses
859 + against production; see makenotwork::seed.
860 +
861 + The server takes its configuration from the environment (see .env.example).
862 + Deployment tooling drives these modes by environment variable rather than by
863 + flag, because each one runs against a target with its env already sourced:
864 +
865 + MNW_CHECK_CONFIG=1 Load the config, print a sentinel line, exit. No database.
866 + MNW_CHECK_DOCS=1 Report broken internal doc links, exit. No database.
867 + SANDO_BOOT_SMOKE=1 Serve a minimal /health on SANDO_BOOT_SMOKE_PORT and idle.
868 +
869 + Running with no flags starts the server: config, pool, migrations, then serve.";
870 +
871 + /// Answer `--version` and `--help` before the process touches its environment.
872 + /// Returns the exit code when a flag was handled, `None` to carry on booting.
873 + fn handle_inspection_flags() -> Option<i32> {
874 + match inspection_request(std::env::args().skip(1))? {
875 + Inspection::Version => println!("{}", version_line()),
876 + Inspection::Help => println!("{HELP}"),
877 + }
878 + Some(0)
879 + }
880 +
881 + #[cfg(test)]
882 + mod inspection_tests {
883 + use super::*;
884 +
885 + #[test]
886 + fn version_flags_are_recognised() {
887 + assert_eq!(inspection_request(["--version"]), Some(Inspection::Version));
888 + assert_eq!(inspection_request(["-V"]), Some(Inspection::Version));
889 + }
890 +
891 + #[test]
892 + fn help_flags_are_recognised() {
893 + assert_eq!(inspection_request(["--help"]), Some(Inspection::Help));
894 + assert_eq!(inspection_request(["-h"]), Some(Inspection::Help));
895 + }
896 +
897 + #[test]
898 + fn boot_arguments_are_not_inspection_requests() {
899 + assert_eq!(inspection_request(Vec::<String>::new()), None);
900 + assert_eq!(inspection_request(["--seed-examples"]), None);
901 + }
902 +
903 + #[test]
904 + fn the_first_inspection_flag_wins() {
905 + assert_eq!(
906 + inspection_request(["--seed-examples", "--version", "--help"]),
907 + Some(Inspection::Version)
908 + );
909 + }
910 +
911 + #[test]
912 + fn version_line_carries_the_crate_version() {
913 + assert!(version_line().starts_with(&format!("makenotwork {}", env!("CARGO_PKG_VERSION"))));
914 + }
915 + }
916 +
806 917 /// Wait for SIGINT (Ctrl-C) or SIGTERM, then return to trigger graceful shutdown.
807 918 /// In-flight requests get up to `SHUTDOWN_REQUEST_DRAIN_SECS` to complete, then
808 919 /// the post-serve drains get `SHUTDOWN_POOL_DRAIN_SECS`; the hard-exit timer