///|
/// Why a command stopped.
priv suberror CliError {
/// The command line is wrong; the usage hint is printed.
Usage(String)
/// The command could not do its job.
Failed(String)
/// The package was read but failed an integrity check.
Mismatch(String)
}
///|
const USAGE : String =
#|linuxpkg - build Debian, RPM and Arch Linux packages without dpkg-deb or rpmbuild
#|
#|Usage:
#| linuxpkg build [--format deb|rpm|arch|apk|all] [--compression C] [--sign-key KEY] [--out DIR]
#| linuxpkg inspect [--json] (.deb, .rpm, .apk or .pkg.tar.*)
#| linuxpkg verify [--keyring KEY]
#| linuxpkg repo [--format deb|rpm|all] [--sign-key KEY]
#| linuxpkg elf [--json]
#| linuxpkg convert [--format deb|rpm|apk|arch] [-o linuxpkg.json]
#| linuxpkg key apk-pub|info [--name NAME] [--out DIR]
#| linuxpkg init [PATH] [--force]
#| linuxpkg init --moon [DIR] [--bin PACKAGE] [--name NAME] [--arch ARCH]
#| linuxpkg --version
#|
#|Commands:
#| build Build packages described by a linuxpkg.json file.
#| inspect Show the metadata and files of a package.
#| verify Check the checksums of a package.
#| repo Write apt and dnf repository metadata for the packages in a directory.
#| convert Turn an nFPM configuration into a linuxpkg.json (build also reads nfpm.yaml).
#| elf Show the architecture, linking, interpreter and libraries of an ELF file.
#| key Export the Alpine public key (apk-pub) of an RSA signing key, or show
#| which key a file holds (info).
#| init Write an example linuxpkg.json to edit, or (--moon) one for the
#| native executable of the MoonBit project in DIR.
#|
#|Options:
#| -f, --format FORMAT Package format to build or index: deb, rpm, arch, apk or all
#| (default; build skips arch and apk where they do not fit).
#| -z, --compression C Payload compression: none, gzip, xz or zstd. Overrides
#| the configuration (default: gzip).
#| -o, --out DIR Directory for the packages (default: current directory).
#| --sign-key KEY Sign with this OpenPGP private key (ASCII armor or binary, RSA or
#| Ed25519, no passphrase): rpm packages, Arch packages (a .sig
#| file next to them; build) and apt / dnf metadata (repo).
#| --apk-key-name N Name of the Alpine public key in apk signatures (build; default
#| linuxpkg-.rsa.pub).
#| --keyring KEY Public key(s) to check signatures against (verify): ASCII armor
#| or binary. Packages and repositories that are not signed fail.
#| --allow-arch-mismatch Build even if an ELF file is for another CPU than "arch" (build).
#| --json Print machine readable JSON (inspect, elf).
#| --force Overwrite an existing file (init).
#| -h, --help Show this help.
#| -V, --version Show the version.
#|
#|Set SOURCE_DATE_EPOCH to a Unix timestamp to make builds reproducible when
#|the configuration does not set "mtime".
#|
#|Exit status: 0 on success, 1 for usage errors, unreadable or unsupported
#|input, 2 when verify finds a damaged or tampered package.
///|
fn run(args : Array[String]) -> Int32 {
match args {
[] => {
eprintln(USAGE)
1
}
["--help" | "-h" | "help", ..] => {
println(USAGE)
0
}
["--version" | "-V" | "version", ..] => {
println("linuxpkg \{@linuxpkg.VERSION}")
0
}
[command, .. rest] =>
try dispatch(command, rest.to_owned()) catch {
Usage(message) => {
eprintln("linuxpkg: \{message}")
eprintln("Run `linuxpkg --help` for usage.")
1
}
Failed(message) => {
eprintln("linuxpkg: \{message}")
1
}
Mismatch(message) => {
eprintln("linuxpkg: \{message}")
2
}
} noraise {
code => code
}
}
}
///|
fn dispatch(command : String, args : Array[String]) -> Int32 raise CliError {
match command {
"build" => cmd_build(args)
"init" => cmd_init(args)
"inspect" => cmd_inspect(args)
"verify" => cmd_verify(args)
"repo" => cmd_repo(args)
"key" => cmd_key(args)
"elf" => cmd_elf(args)
"convert" => cmd_convert(args)
_ => raise Usage("unknown command \"\{command}\"")
}
}
///|
fn main {
let argv = @env.args()
// The first element is the program itself; on the js backend it is node
// followed by the script path.
let skip = if argv.length() > 1 && argv[1].has_suffix(".js") { 2 } else { 1 }
let args = if argv.length() >= skip { argv[skip:].to_owned() } else { [] }
let code = run(args)
if code != 0 {
@sys.exit(code)
}
}