Codex refactor update transactions

Codex did this refactor.

Batch update installs, dependency installs, conflict removals, and replacement removals under one update transaction hook boundary, following pacman's sync transaction model.

Remove the unused pdf-extract dependency and refresh related dependency versions.
This commit is contained in:
2026-07-08 16:35:09 -05:00
parent 98eb1fe246
commit 279a7e0521
5 changed files with 615 additions and 517 deletions
Generated
+18 -327
View File
@@ -8,26 +8,6 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "adobe-cmap-parser"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae8abfa9a4688de8fc9f42b3f013b6fffec18ed8a554f5f113577e0b9b3212a3"
dependencies = [
"pom",
]
[[package]]
name = "aes"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher 0.4.4",
"cpufeatures 0.2.17",
]
[[package]]
name = "aes"
version = "0.9.1"
@@ -109,9 +89,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.102"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
[[package]]
name = "ar"
@@ -198,27 +178,12 @@ dependencies = [
"zeroize",
]
[[package]]
name = "block-padding"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "bytecount"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
[[package]]
name = "bytes"
version = "1.11.1"
@@ -234,15 +199,6 @@ dependencies = [
"libbz2-rs-sys",
]
[[package]]
name = "cbc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
dependencies = [
"cipher 0.4.4",
]
[[package]]
name = "cc"
version = "1.2.63"
@@ -255,12 +211,6 @@ dependencies = [
"shlex",
]
[[package]]
name = "cff-parser"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f5b6e9141c036f3ff4ce7b2f7e432b0f00dee416ddcd4f17741d189ddc2e9d"
[[package]]
name = "cfg-if"
version = "1.0.4"
@@ -328,9 +278,9 @@ dependencies = [
[[package]]
name = "clap_complete"
version = "4.6.5"
version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7a9bfdb35811f9e59832f0f05975114d2251b415fb534108e6f34060fd772"
checksum = "db8b397918185f0161ff3d6fcaa9e4bfc09b8367caf6e1d4a2848e5477ed027b"
dependencies = [
"clap",
]
@@ -376,9 +326,9 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
name = "console"
version = "0.16.3"
version = "0.16.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87"
checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c"
dependencies = [
"encode_unicode",
"libc",
@@ -531,7 +481,7 @@ checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2"
[[package]]
name = "depot"
version = "0.50.0"
version = "0.60.0"
dependencies = [
"anyhow",
"ar",
@@ -550,7 +500,6 @@ dependencies = [
"md5",
"minisign",
"nix",
"pdf-extract",
"petgraph",
"reqwest",
"rusqlite",
@@ -653,15 +602,6 @@ version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
[[package]]
name = "ecb"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a8bfa975b1aec2145850fcaa1c6fe269a16578c44705a532ae3edc92b8881c7"
dependencies = [
"cipher 0.4.4",
]
[[package]]
name = "encode_unicode"
version = "1.0.0"
@@ -693,15 +633,6 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "euclid"
version = "0.20.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bb7ef65b3777a325d1eeefefab5b6d4959da54747e33bd6258e789640f307ad"
dependencies = [
"num-traits",
]
[[package]]
name = "fallible-iterator"
version = "0.3.0"
@@ -832,19 +763,6 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
[[package]]
name = "futures-lite"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"parking",
"pin-project-lite",
]
[[package]]
name = "futures-sink"
version = "0.3.32"
@@ -1260,9 +1178,9 @@ dependencies = [
[[package]]
name = "indicatif"
version = "0.18.4"
version = "0.18.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb"
checksum = "9433806cd6b4ec1aba79c021c7e4c58fb4c3b9977c085062e611ac929998fb0c"
dependencies = [
"console",
"portable-atomic",
@@ -1277,7 +1195,6 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"block-padding",
"generic-array",
]
@@ -1469,34 +1386,6 @@ version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
[[package]]
name = "lopdf"
version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7184fdea2bc3cd272a1acec4030c321a8f9875e877b3f92a53f2f6033fdc289"
dependencies = [
"aes 0.8.4",
"bitflags",
"cbc",
"ecb",
"encoding_rs",
"flate2",
"getrandom 0.3.4",
"indexmap",
"itoa",
"log",
"md-5",
"nom",
"nom_locate",
"rand",
"rangemap",
"sha2 0.10.9",
"stringprep",
"thiserror 2.0.18",
"ttf-parser",
"weezl",
]
[[package]]
name = "lz4_flex"
version = "0.13.1"
@@ -1508,9 +1397,9 @@ dependencies = [
[[package]]
name = "lzma-rust2"
version = "0.16.4"
version = "0.16.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce716bf1a316f47a280fc76295f6495b5bea4752bca01c3b3885e101b1c23c02"
checksum = "ca93e534d1142d1d0dcca6d25fe302508a5dfb40b302802904577725ea0b695b"
dependencies = [
"sha2 0.11.0",
]
@@ -1526,16 +1415,6 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "md-5"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
dependencies = [
"cfg-if",
"digest 0.10.7",
]
[[package]]
name = "md5"
version = "0.8.0"
@@ -1617,26 +1496,6 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
dependencies = [
"memchr",
]
[[package]]
name = "nom_locate"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b577e2d69827c4740cba2b52efaad1c4cc7c73042860b199710b3575c68438d"
dependencies = [
"bytecount",
"memchr",
"nom",
]
[[package]]
name = "num-conv"
version = "0.2.2"
@@ -1717,12 +1576,6 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "parking"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]]
name = "parking_lot"
version = "0.12.5"
@@ -1766,23 +1619,6 @@ dependencies = [
"hmac 0.13.0",
]
[[package]]
name = "pdf-extract"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e28ba1758a3d3f361459645780e09570b573fc3c82637449e9963174c813a98"
dependencies = [
"adobe-cmap-parser",
"cff-parser",
"encoding_rs",
"euclid",
"log",
"lopdf",
"postscript",
"type1-encoding-parser",
"unicode-normalization",
]
[[package]]
name = "percent-encoding"
version = "2.3.2"
@@ -1813,24 +1649,12 @@ version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
[[package]]
name = "pom"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60f6ce597ecdcc9a098e7fddacb1065093a3d66446fa16c675e7e71d1b5c28e6"
[[package]]
name = "portable-atomic"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "postscript"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78451badbdaebaf17f053fd9152b3ffb33b516104eacb45e7864aaa9c712f306"
[[package]]
name = "potential_utf"
version = "0.1.5"
@@ -1852,15 +1676,6 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "prettyplease"
version = "0.2.37"
@@ -1901,41 +1716,6 @@ version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
dependencies = [
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
dependencies = [
"getrandom 0.3.4",
]
[[package]]
name = "rangemap"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68"
[[package]]
name = "redox_syscall"
version = "0.5.18"
@@ -2364,17 +2144,6 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "stringprep"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1"
dependencies = [
"unicode-bidi",
"unicode-normalization",
"unicode-properties",
]
[[package]]
name = "strsim"
version = "0.11.1"
@@ -2389,12 +2158,11 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "suppaftp"
version = "8.0.4"
version = "10.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cf00e4d8418c477a8cb3c13ae5396a68d31658e760c74280bdbd34926e3b94b"
checksum = "81b8286a5639930b8fc0400f8a5111439ffb57e5fba6e8276a8bce857ec47b18"
dependencies = [
"chrono",
"futures-lite",
"lazy-regex",
"log",
"thiserror 2.0.18",
@@ -2519,9 +2287,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.49"
version = "0.3.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469"
checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50"
dependencies = [
"deranged",
"js-sys",
@@ -2540,9 +2308,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
[[package]]
name = "time-macros"
version = "0.2.29"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d"
checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f"
dependencies = [
"num-conv",
"time-core",
@@ -2558,21 +2326,6 @@ dependencies = [
"zerovec",
]
[[package]]
name = "tinyvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.52.3"
@@ -2718,27 +2471,12 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "ttf-parser"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
[[package]]
name = "twox-hash"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
[[package]]
name = "type1-encoding-parser"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa10c302f5a53b7ad27fd42a3996e23d096ba39b5b8dd6d9e683a05b01bee749"
dependencies = [
"pom",
]
[[package]]
name = "typed-path"
version = "0.12.3"
@@ -2751,33 +2489,12 @@ version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "unicode-bidi"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-normalization"
version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-properties"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
[[package]]
name = "unicode-segmentation"
version = "1.13.3"
@@ -2990,12 +2707,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "weezl"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "winapi"
version = "0.3.9"
@@ -3327,26 +3038,6 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zerocopy"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zerofrom"
version = "0.1.8"
@@ -3413,7 +3104,7 @@ version = "8.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b"
dependencies = [
"aes 0.9.1",
"aes",
"bzip2",
"constant_time_eq 0.4.2",
"crc32fast",
+7 -8
View File
@@ -1,6 +1,6 @@
[package]
name = "depot"
version = "0.50.0"
version = "0.60.0"
edition = "2024"
[lints.rust]
@@ -19,13 +19,13 @@ static-except-libc = [
]
[dependencies]
anyhow = "1.0.102"
anyhow = "1.0.103"
ar = "0.9.0"
bzip2 = "0.6.1"
clap = { version = "4.6.0", features = ["derive"] }
flate2 = "1.1.9"
git2 = "0.21.0"
indicatif = "0.18.4"
indicatif = "0.18.6"
nix = { version = "0.31.3", features = ["user"] }
rusqlite = "0.40.1"
semver = "1.0.28"
@@ -43,22 +43,21 @@ zip = "8.5.0"
zstd = { version = "0.13.3", features = ["zstdmt"] }
inquire = "0.9.4"
md5 = "0.8.0"
suppaftp = "8.0.4"
suppaftp = "10.0.0"
minisign = "0.9.1"
petgraph = "0.8.3"
fd-lock = "4.0.4"
reqwest = { version = "0.13.4", default-features = false, features = ["blocking", "native-tls"] }
filetime = "0.2.29"
clap_complete = "4.6.5"
clap_complete = "4.6.7"
sys-mount = { version = "3.1.0", default-features = false }
time = { version = "0.3.49", features = ["formatting", "parsing"] }
time = { version = "0.3.53", features = ["formatting", "parsing"] }
b2sum-rust = "0.3.0"
serde_ignored = "0.1.14"
lz4_flex = "0.13.1"
lzma-rust2 = "0.16.4"
lzma-rust2 = "0.16.5"
signal-hook = "0.4.4"
sha1 = "0.11.0"
pdf-extract = "0.10.0"
[dev-dependencies]
tempfile = "=3.27.0"
+437 -138
View File
@@ -735,6 +735,30 @@ struct PlannedPackageInstall {
staged: PlannedStagedInstall,
}
#[derive(Debug, Clone)]
struct PlannedInstalledRemoval {
package: String,
affected_paths: Vec<String>,
}
struct PreparedDirectInstallResources<'a> {
_staging_dir: Option<tempfile::TempDir>,
_source_cleanup_guard: SourceBuildCleanupGuard<'a>,
}
struct PreparedDirectInstall<'a> {
plans: Vec<PlannedPackageInstall>,
resources: PreparedDirectInstallResources<'a>,
}
struct DirectInstallPreparationOptions<'a> {
build_dir: &'a Path,
clean_sources_before_build: bool,
suppress_output: bool,
confirm_installation: bool,
resolve_installed_conflicts: bool,
}
#[derive(Clone, Copy)]
struct PendingLifecycleHook {
hook: install::scripts::Hook,
@@ -946,6 +970,26 @@ pub(crate) fn remove_installed_package_with_hooks(
affected_paths: &affected_paths,
},
)?;
remove_installed_package_without_transaction_hooks(package, rootfs, config, &affected_paths)?;
install::hooks::run_transaction_hooks(
rootfs,
&install::hooks::HookExecutionContext {
phase: install::hooks::HookPhase::Post,
operation: install::hooks::HookOperation::Remove,
package,
affected_paths: &affected_paths,
},
)?;
Ok(())
}
fn remove_installed_package_without_transaction_hooks(
package: &str,
rootfs: &Path,
config: &config::Config,
_affected_paths: &[String],
) -> Result<()> {
let db_path = config.installed_db_path(rootfs);
let script_dir = install::scripts::installed_scripts_dir(rootfs, package);
let _ = install::scripts::run_hook_if_present(
&script_dir,
@@ -963,34 +1007,25 @@ pub(crate) fn remove_installed_package_with_hooks(
let cleanup_scripts = install::scripts::remove_installed_scripts(rootfs, package);
post_remove?;
cleanup_scripts?;
install::hooks::run_transaction_hooks(
rootfs,
&install::hooks::HookExecutionContext {
phase: install::hooks::HookPhase::Post,
operation: install::hooks::HookOperation::Remove,
package,
affected_paths: &affected_paths,
},
)?;
ui::success(format!("Successfully removed {}", package));
Ok(())
}
fn resolve_installed_conflicts_for_subjects(
fn prompt_installed_conflict_removals_for_subjects(
subjects: &[InstallConflictSubject],
rootfs: &Path,
config: &config::Config,
dry_run: bool,
) -> Result<()> {
) -> Result<Vec<String>> {
if subjects.is_empty() {
return Ok(());
return Ok(Vec::new());
}
let db_path = config.installed_db_path(rootfs);
let installed = collect_installed_conflict_packages(&db_path)?;
let removals = collect_conflicting_installed_packages(subjects, &installed)?;
if removals.is_empty() {
return Ok(());
return Ok(Vec::new());
}
let prompt_entries: Vec<String> = removals
@@ -1009,16 +1044,27 @@ fn resolve_installed_conflicts_for_subjects(
"Dry run: would remove conflicting installed package(s): {}",
prompt_entries.join(", ")
));
return Ok(());
return Ok(Vec::new());
}
if !ui::prompt_package_action("conflict removal", &prompt_entries, true)? {
anyhow::bail!("Aborted");
}
for package in removals.keys() {
Ok(removals.keys().cloned().collect())
}
fn resolve_installed_conflicts_for_subjects(
subjects: &[InstallConflictSubject],
rootfs: &Path,
config: &config::Config,
dry_run: bool,
) -> Result<()> {
for package in
prompt_installed_conflict_removals_for_subjects(subjects, rootfs, config, dry_run)?
{
ui::info(format!("Removing conflicting package: {}", package));
remove_installed_package_with_hooks(package, rootfs, config)?;
remove_installed_package_with_hooks(&package, rootfs, config)?;
}
Ok(())
@@ -1325,6 +1371,15 @@ fn install_planned_packages_to_rootfs(
plans: &[PlannedPackageInstall],
rootfs: &Path,
config: &config::Config,
) -> Result<()> {
install_planned_packages_to_rootfs_with_pre_removed(plans, rootfs, config, &HashSet::new())
}
fn install_planned_packages_to_rootfs_with_pre_removed(
plans: &[PlannedPackageInstall],
rootfs: &Path,
config: &config::Config,
pre_removed_packages: &HashSet<String>,
) -> Result<()> {
let mut removed_replacements = HashSet::new();
let mut pending_post_hooks = Vec::new();
@@ -1338,6 +1393,9 @@ fn install_planned_packages_to_rootfs(
plan.spec.package.revision
));
for package in &plan.staged.replacement_removals {
if pre_removed_packages.contains(package) {
continue;
}
if removed_replacements.insert(package.clone()) {
remove_installed_package_with_hooks(package, rootfs, config)?;
}
@@ -2082,104 +2140,31 @@ impl Drop for SourceBuildCleanupGuard<'_> {
}
}
fn run_direct_install_request(
fn prepare_direct_install_request<'a>(
options: DirectInstallOptions<'_>,
config: &config::Config,
mut spec_path: PathBuf,
) -> Result<bool> {
let mut install_lock = locking::open_lock(config)?;
let install_lock_path = locking::lock_path(config);
let _install_lock_guard = locking::try_write(&mut install_lock, &install_lock_path, "install")?;
// Repo clone dir is available via `config.repo_clone_dir` and
// is passed explicitly to index builders below.
// If the provided path doesn't exist, treat it as a package name and
// try to locate a spec under configured repo dir or local packages/.
if !spec_path.exists() {
let name = spec_path.to_string_lossy().to_string();
ui::info(format!("Looking up package '{}' in local indexes...", name));
let pkg_index =
index::PackageIndex::build_with_repo_dir(Some(config.repo_clone_dir.clone()));
if let Some(found) = pkg_index.find(&name) {
spec_path = found;
} else {
let host_arch = std::env::consts::ARCH;
let mut binary_repos: Vec<_> = config
.binary_repos
.iter()
.filter(|(_, repo)| repo.enabled && repo.supports_arch(host_arch))
.collect();
binary_repos.sort_by(|a, b| a.1.priority.cmp(&b.1.priority).then_with(|| a.0.cmp(b.0)));
for (repo_name, repo_cfg) in binary_repos {
match db::repo::find_binary_repo_package(
repo_name,
repo_cfg,
options.rootfs,
&config.package_cache_dir,
&name,
) {
Ok(Some(rec)) => {
let archive = db::repo::fetch_binary_package_archive(
repo_name,
repo_cfg,
options.rootfs,
&rec,
&config.package_cache_dir,
)?;
ui::info(format!(
"Resolved '{}' from binary repo '{}' as {}-{} (package {}) ({} bytes){} -> {}",
name,
repo_name,
rec.version,
rec.revision,
rec.name,
rec.size,
rec.description
.as_ref()
.map(|d| format!(" [{}]", d))
.unwrap_or_default(),
archive.display()
));
spec_path = archive;
break;
}
Ok(None) => {}
Err(e) => {
crate::log_warn!("Binary repo '{}': {}", repo_name, e);
}
}
}
}
}
let suppress_output = suppress_nested_install_output();
if !suppress_output {
ui::info(format!("Installing package from: {}", spec_path.display()));
}
config: &'a config::Config,
spec_path: &Path,
preparation: DirectInstallPreparationOptions<'_>,
) -> Result<PreparedDirectInstall<'a>> {
let (mut pkg_spec, staging_dir): (package::PackageSpec, Option<tempfile::TempDir>) =
if spec_path.to_string_lossy().ends_with(".tar.zst") {
// Install from archive
let (spec, tmp_dir) = load_package_archive_into_staging(config, &spec_path)?;
let (spec, tmp_dir) = load_package_archive_into_staging(config, spec_path)?;
(spec, Some(tmp_dir))
} else {
// Install from spec (normal build)
let mut pkg_spec = package::PackageSpec::from_file(&spec_path)?;
let mut pkg_spec = package::PackageSpec::from_file(spec_path)?;
pkg_spec.apply_config(config);
pkg_spec.build.flags.rootfs = build_cmd::build_env_rootfs(options.rootfs);
(pkg_spec, None)
};
let built_from_source = staging_dir.is_none();
let _source_cleanup_guard = SourceBuildCleanupGuard::new(config, built_from_source);
let source_cleanup_guard = SourceBuildCleanupGuard::new(config, built_from_source);
if options.lib32_only && staging_dir.is_some() {
anyhow::bail!("--lib32-only is only supported when installing from a package spec");
}
let lib32_only = effective_lib32_only(&pkg_spec, options.lib32_only);
if staging_dir.is_none() && !suppress_output {
if staging_dir.is_none() && !preparation.suppress_output {
ui::info(format!(
"Package: {} v{}-{}",
pkg_spec.package.name, pkg_spec.package.version, pkg_spec.package.revision
@@ -2204,30 +2189,37 @@ fn run_direct_install_request(
if staging_dir.is_some() {
conflict_subjects = install_conflict_subjects_for_spec(&pkg_spec, true, false);
}
resolve_installed_conflicts_for_subjects(
&conflict_subjects,
options.rootfs,
config,
options.dry_run,
)?;
if preparation.resolve_installed_conflicts {
resolve_installed_conflicts_for_subjects(
&conflict_subjects,
options.rootfs,
config,
options.dry_run,
)?;
}
if options.dry_run {
ui::info("Dry run enabled, stopping before install/build work.");
return Ok(false);
return Ok(PreparedDirectInstall {
plans: Vec::new(),
resources: PreparedDirectInstallResources {
_staging_dir: staging_dir,
_source_cleanup_guard: source_cleanup_guard,
},
});
}
let install_targets = vec![format!(
"{} v{}-{}",
pkg_spec.package.name, pkg_spec.package.version, pkg_spec.package.revision
)];
if !suppress_output && !ui::prompt_package_action("installation", &install_targets, true)? {
if preparation.confirm_installation
&& !preparation.suppress_output
&& !ui::prompt_package_action("installation", &install_targets, true)?
{
anyhow::bail!("Aborted");
}
let _snapper_pre_install_snapshot_todo: fn() -> ! =
|| todo!("snapper: create pre-install snapshot before install work starts");
// Ensure database directory exists
std::fs::create_dir_all(&config.db_dir).with_context(|| {
format!(
"Failed to create database directory: {}",
@@ -2257,14 +2249,12 @@ fn run_direct_install_request(
}
}
// Check dependencies and prompt for auto-install if needed
if !options.no_deps {
let missing_required = merge_missing_dependencies(
deps::check_build_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?,
deps::check_runtime_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?,
);
if !missing_required.is_empty() {
// Check for dependency cycles via DEPOT_DEPCHAIN env var
let dep_chain = std::env::var("DEPOT_DEPCHAIN").unwrap_or_default();
let chain_set: std::collections::HashSet<&str> =
dep_chain.split(',').filter(|s| !s.is_empty()).collect();
@@ -2302,11 +2292,9 @@ fn run_direct_install_request(
dep_plan_packages
};
if ui::prompt_package_action("dependency installation", &dep_prompt_packages, true)? {
// Build package index for fast lookups
let pkg_index =
index::PackageIndex::build_with_repo_dir(Some(config.repo_clone_dir.clone()));
// Build new dep chain
let new_chain = if dep_chain.is_empty() {
pkg_spec.package.name.clone()
} else {
@@ -2315,7 +2303,6 @@ fn run_direct_install_request(
let mut dep_spec_paths = Vec::new();
for dep in missing_required {
// Use package index for O(1) lookup
let candidate = pkg_index.find(&dep);
if let Some(dep_spec_path) = candidate {
@@ -2348,7 +2335,6 @@ fn run_direct_install_request(
}
}
// Enforce required dependencies before building/installing.
deps::require_build_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?;
deps::require_runtime_deps_for_outputs(&pkg_spec, &db_path, requested_outputs)?;
if should_install_test_deps(&pkg_spec, options.install_test_deps, requested_outputs) {
@@ -2466,10 +2452,11 @@ fn run_direct_install_request(
let destdir = if let Some(dir) = &staging_dir {
dir.path().to_path_buf()
} else {
// 1-2. Fetch + extract sources (supports archives and git URL#rev)
clean_build_source_dirs(config)?;
if preparation.clean_sources_before_build {
clean_build_source_dirs(config)?;
}
source::preflight_manual_sources(&pkg_spec, &config.cache_dir)?;
let src_dir = source::prepare(&pkg_spec, &config.cache_dir, &config.build_dir)?;
let src_dir = source::prepare(&pkg_spec, &config.cache_dir, preparation.build_dir)?;
built_src_dir = Some(src_dir.clone());
let host_build_dir = builder::ensure_host_build(
&pkg_spec,
@@ -2482,11 +2469,14 @@ fn run_direct_install_request(
pkg_spec.build.flags.host_build_dir = Some(host_dir.to_string_lossy().into_owned());
}
// 3. Build
let destdir = config
let destdir = preparation
.build_dir
.join("destdir")
.join(&pkg_spec.package.name);
if destdir.exists() {
fs::remove_dir_all(&destdir)
.with_context(|| format!("Failed to clean destdir: {}", destdir.display()))?;
}
if !lib32_only {
builder::build(
@@ -2498,7 +2488,6 @@ fn run_direct_install_request(
host_build_dir.as_deref(),
)?;
// 3.1 Copy license files into staged tree
staging::add_licenses(&src_dir, &destdir, &pkg_spec.package.name)?;
install::scripts::stage_scripts_from_spec_dir(&pkg_spec, &destdir)?;
builder::stage_generated_lifecycle_scripts(&pkg_spec, &destdir)?;
@@ -2511,16 +2500,12 @@ fn run_direct_install_request(
if !lib32_only {
if staging_dir.is_none() {
// Source-build path: apply staging transforms (strip/compress/static cleanup).
staging::process(&destdir, &pkg_spec)?;
if let Some(src_dir) = built_src_dir.as_deref() {
staging::stage_split_package_licenses(src_dir, &destdir, &pkg_spec)?;
}
} else {
// Binary archive path: install as-packaged without post-build transformations.
if !suppress_output {
ui::info("Installing binary archive payload");
}
} else if !preparation.suppress_output {
ui::info("Installing binary archive payload");
}
let output_plans =
@@ -2546,20 +2531,334 @@ fn run_direct_install_request(
});
}
run_transaction_hooks_for_plans(
options.rootfs,
install::hooks::HookPhase::Pre,
&transaction_plans,
Ok(PreparedDirectInstall {
plans: transaction_plans,
resources: PreparedDirectInstallResources {
_staging_dir: staging_dir,
_source_cleanup_guard: source_cleanup_guard,
},
})
}
fn install_direct_transaction(
plans: &[PlannedPackageInstall],
rootfs: &Path,
config: &config::Config,
) -> Result<()> {
run_transaction_hooks_for_plans(rootfs, install::hooks::HookPhase::Pre, plans)?;
install_planned_packages_to_rootfs(plans, rootfs, config)?;
run_transaction_hooks_for_plans(rootfs, install::hooks::HookPhase::Post, plans)?;
Ok(())
}
fn install_requests_for_plan(
plan: &planner::ExecutionPlan,
config: &config::Config,
rootfs: &Path,
) -> Result<Vec<PathBuf>> {
let mut requests = Vec::new();
for step in plan.actionable_steps() {
match &step.origin {
planner::PlanOrigin::Source { path, .. } => {
requests.push(path.clone());
}
planner::PlanOrigin::Binary { repo_name, record } => {
let repo_cfg = config
.binary_repos
.get(repo_name)
.with_context(|| format!("Binary repo '{}' not found in config", repo_name))?;
let archive = db::repo::fetch_binary_package_archive(
repo_name,
repo_cfg,
rootfs,
record,
&config.package_cache_dir,
)
.with_context(|| {
format!(
"Failed to fetch binary package '{}' from repo '{}'",
record.filename, repo_name
)
})?;
requests.push(archive);
}
planner::PlanOrigin::Installed => {}
}
}
Ok(requests)
}
fn planned_installed_removals(
rootfs: &Path,
config: &config::Config,
packages: impl IntoIterator<Item = String>,
) -> Result<Vec<PlannedInstalledRemoval>> {
let db_path = config.installed_db_path(rootfs);
let installed = db::get_installed_packages(&db_path)?;
let mut unique = BTreeSet::new();
for package in packages {
if installed.contains(&package) {
unique.insert(package);
}
}
unique
.into_iter()
.map(|package| {
let affected_paths = db::get_package_files(&db_path, &package)?;
Ok(PlannedInstalledRemoval {
package,
affected_paths,
})
})
.collect()
}
fn transaction_contexts_for_update(
removals: &[PlannedInstalledRemoval],
plans: &[PlannedPackageInstall],
) -> Vec<install::hooks::HookExecutionContextOwned> {
let mut contexts = Vec::with_capacity(removals.len() + plans.len());
contexts.extend(
removals
.iter()
.map(|removal| install::hooks::HookExecutionContextOwned {
operation: install::hooks::HookOperation::Remove,
package: removal.package.clone(),
affected_paths: removal.affected_paths.clone(),
}),
);
contexts.extend(plans.iter().map(|plan| plan.staged.hook_context.clone()));
contexts
}
fn install_update_transaction(
plans: &[PlannedPackageInstall],
removals: &[PlannedInstalledRemoval],
rootfs: &Path,
config: &config::Config,
) -> Result<()> {
let contexts = transaction_contexts_for_update(removals, plans);
install::hooks::run_transaction_hooks_batch(rootfs, install::hooks::HookPhase::Pre, &contexts)?;
for removal in removals {
ui::info(format!(
"Removing package for update transaction: {}",
removal.package
));
remove_installed_package_without_transaction_hooks(
&removal.package,
rootfs,
config,
&removal.affected_paths,
)?;
}
let pre_removed_packages: HashSet<String> = removals
.iter()
.map(|removal| removal.package.clone())
.collect();
install_planned_packages_to_rootfs_with_pre_removed(
plans,
rootfs,
config,
&pre_removed_packages,
)?;
install::hooks::run_transaction_hooks_batch(
rootfs,
install::hooks::HookPhase::Post,
&contexts,
)?;
Ok(())
}
fn run_direct_install_request(
options: DirectInstallOptions<'_>,
config: &config::Config,
mut spec_path: PathBuf,
) -> Result<bool> {
let mut install_lock = locking::open_lock(config)?;
let install_lock_path = locking::lock_path(config);
let _install_lock_guard = locking::try_write(&mut install_lock, &install_lock_path, "install")?;
// Repo clone dir is available via `config.repo_clone_dir` and
// is passed explicitly to index builders below.
// If the provided path doesn't exist, treat it as a package name and
// try to locate a spec under configured repo dir or local packages/.
if !spec_path.exists() {
let name = spec_path.to_string_lossy().to_string();
ui::info(format!("Looking up package '{}' in local indexes...", name));
let pkg_index =
index::PackageIndex::build_with_repo_dir(Some(config.repo_clone_dir.clone()));
if let Some(found) = pkg_index.find(&name) {
spec_path = found;
} else {
let host_arch = std::env::consts::ARCH;
let mut binary_repos: Vec<_> = config
.binary_repos
.iter()
.filter(|(_, repo)| repo.enabled && repo.supports_arch(host_arch))
.collect();
binary_repos.sort_by(|a, b| a.1.priority.cmp(&b.1.priority).then_with(|| a.0.cmp(b.0)));
for (repo_name, repo_cfg) in binary_repos {
match db::repo::find_binary_repo_package(
repo_name,
repo_cfg,
options.rootfs,
&config.package_cache_dir,
&name,
) {
Ok(Some(rec)) => {
let archive = db::repo::fetch_binary_package_archive(
repo_name,
repo_cfg,
options.rootfs,
&rec,
&config.package_cache_dir,
)?;
ui::info(format!(
"Resolved '{}' from binary repo '{}' as {}-{} (package {}) ({} bytes){} -> {}",
name,
repo_name,
rec.version,
rec.revision,
rec.name,
rec.size,
rec.description
.as_ref()
.map(|d| format!(" [{}]", d))
.unwrap_or_default(),
archive.display()
));
spec_path = archive;
break;
}
Ok(None) => {}
Err(e) => {
crate::log_warn!("Binary repo '{}': {}", repo_name, e);
}
}
}
}
}
let suppress_output = suppress_nested_install_output();
if !suppress_output {
ui::info(format!("Installing package from: {}", spec_path.display()));
}
let _snapper_pre_install_snapshot_todo: fn() -> ! =
|| todo!("snapper: create pre-install snapshot before install work starts");
let _snapper_post_install_snapshot_todo: fn() -> ! =
|| todo!("snapper: create post-install snapshot after install commit succeeds");
install_planned_packages_to_rootfs(&transaction_plans, options.rootfs, config)?;
run_transaction_hooks_for_plans(
options.rootfs,
install::hooks::HookPhase::Post,
&transaction_plans,
)?;
let prepared = prepare_direct_install_request(
options,
config,
&spec_path,
DirectInstallPreparationOptions {
build_dir: &config.build_dir,
clean_sources_before_build: true,
suppress_output,
confirm_installation: true,
resolve_installed_conflicts: true,
},
)?;
if options.dry_run {
return Ok(false);
}
let _resources = prepared.resources;
install_direct_transaction(&prepared.plans, options.rootfs, config)?;
Ok(true)
}
fn isolated_update_build_dir(config: &config::Config, idx: usize) -> PathBuf {
config
.build_dir
.join("update-tx")
.join(format!("{:04}", idx + 1))
}
fn run_update_transaction_install_requests(
options: DirectInstallOptions<'_>,
config: &config::Config,
requests: &[PathBuf],
) -> Result<bool> {
if requests.is_empty() {
return Ok(false);
}
let mut install_lock = locking::open_lock(config)?;
let install_lock_path = locking::lock_path(config);
let _install_lock_guard = locking::try_write(&mut install_lock, &install_lock_path, "update")?;
let suppress_output = suppress_nested_install_output();
if requests
.iter()
.any(|request| !is_archive_install_request(request))
{
clean_build_source_dirs(config)?;
}
let mut transaction_plans = Vec::new();
let mut resources = Vec::with_capacity(requests.len());
for (idx, request) in requests.iter().enumerate() {
if !suppress_output {
ui::info(format!(
"[{}/{}] preparing update payload from {}",
idx + 1,
requests.len(),
request.display()
));
}
let build_dir = isolated_update_build_dir(config, idx);
let prepared = prepare_direct_install_request(
options,
config,
request,
DirectInstallPreparationOptions {
build_dir: &build_dir,
clean_sources_before_build: false,
suppress_output,
confirm_installation: false,
resolve_installed_conflicts: false,
},
)
.with_context(|| {
format!(
"Failed to prepare update payload from {}",
request.display()
)
})?;
transaction_plans.extend(prepared.plans);
resources.push(prepared.resources);
}
if options.dry_run {
return Ok(false);
}
let conflict_subjects: Vec<_> = transaction_plans
.iter()
.flat_map(|plan| install_conflict_subjects_for_output_spec(&plan.spec))
.collect();
validate_no_transaction_conflicts(&conflict_subjects)?;
let mut removal_packages = prompt_installed_conflict_removals_for_subjects(
&conflict_subjects,
options.rootfs,
config,
false,
)?;
for plan in &transaction_plans {
removal_packages.extend(plan.staged.replacement_removals.iter().cloned());
}
let removals = planned_installed_removals(options.rootfs, config, removal_packages)?;
install_update_transaction(&transaction_plans, &removals, options.rootfs, config)?;
drop(resources);
Ok(true)
}
+125
View File
@@ -41,6 +41,42 @@ fn lib32_args() -> Lib32Args {
Lib32Args { lib32_only: false }
}
fn write_basic_binary_archive(
archive_path: &Path,
package_name: &str,
version: &str,
revision: u32,
payload_path: &str,
payload: &[u8],
) -> Result<()> {
let file = fs::File::create(archive_path)
.with_context(|| format!("Failed to create {}", archive_path.display()))?;
let encoder =
zstd::stream::write::Encoder::new(file, 3).context("Failed to create zstd encoder")?;
let mut tar = tar::Builder::new(encoder);
let mut payload_header = tar::Header::new_gnu();
payload_header.set_path(payload_path)?;
payload_header.set_size(payload.len() as u64);
payload_header.set_mode(0o755);
payload_header.set_cksum();
tar.append(&payload_header, payload)?;
let metadata = format!(
"name = \"{package_name}\"\nversion = \"{version}\"\nrevision = {revision}\ndescription = \"test\"\nhomepage = \"https://example.test\"\nlicense = \"MIT\"\n[dependencies]\nruntime = []\noptional = []\n"
);
let mut meta_header = tar::Header::new_gnu();
meta_header.set_path(".metadata.toml")?;
meta_header.set_size(metadata.len() as u64);
meta_header.set_mode(0o644);
meta_header.set_cksum();
tar.append(&meta_header, metadata.as_bytes())?;
let encoder = tar.into_inner()?;
encoder.finish()?;
Ok(())
}
#[test]
fn build_env_rootfs_uses_selected_non_live_rootfs() {
let tmp = tempfile::tempdir().unwrap();
@@ -691,6 +727,95 @@ fn direct_archive_install_rejects_conflicting_archives_in_same_batch() -> Result
Ok(())
}
#[test]
fn update_transaction_runs_matching_transaction_hook_once_for_batch() -> Result<()> {
let rootfs = tempfile::tempdir().context("Failed to create temp rootfs")?;
let pkg_dir = tempfile::tempdir().context("Failed to create temp package dir")?;
let old_alpha = pkg_dir.path().join("alpha-1.0-1-x86_64.depot.pkg.tar.zst");
let old_beta = pkg_dir.path().join("beta-1.0-1-x86_64.depot.pkg.tar.zst");
let new_alpha = pkg_dir.path().join("alpha-2.0-1-x86_64.depot.pkg.tar.zst");
let new_beta = pkg_dir.path().join("beta-2.0-1-x86_64.depot.pkg.tar.zst");
write_basic_binary_archive(&old_alpha, "alpha", "1.0", 1, "usr/bin/alpha", b"alpha-old")?;
write_basic_binary_archive(&old_beta, "beta", "1.0", 1, "usr/bin/beta", b"beta-old")?;
write_basic_binary_archive(&new_alpha, "alpha", "2.0", 1, "usr/bin/alpha", b"alpha-new")?;
write_basic_binary_archive(&new_beta, "beta", "2.0", 1, "usr/bin/beta", b"beta-new")?;
let mut cfg = config::Config::for_rootfs(rootfs.path());
cfg.build_dir = rootfs.path().join("var/cache/depot/build");
cfg.db_dir = rootfs.path().join("var/lib/depot");
run_direct_archive_install_requests(
DirectInstallOptions {
rootfs: rootfs.path(),
no_deps: true,
no_flags: false,
cross_prefix: None,
clean: false,
dry_run: false,
lib32_only: false,
install_test_deps: false,
},
&cfg,
&[old_alpha, old_beta],
false,
)?;
let hooks_dir = install::hooks::transaction_hooks_dir(rootfs.path());
fs::create_dir_all(&hooks_dir)?;
fs::write(
hooks_dir.join("90-update-batch.toml"),
r#"
[hook]
name = "update batch recorder"
[when]
phase = "post"
operation = ["update"]
paths = ["usr/bin/*"]
[exec]
command = "printf '%s:%s\n' \"$DEPOT_ACTION\" \"$DEPOT_PACKAGE\" >> \"$DEPOT_ROOTFS/hook-runs\"; cat >> \"$DEPOT_ROOTFS/hook-targets\""
needs_paths = true
"#,
)?;
let updated = run_update_transaction_install_requests(
DirectInstallOptions {
rootfs: rootfs.path(),
no_deps: true,
no_flags: false,
cross_prefix: None,
clean: false,
dry_run: false,
lib32_only: false,
install_test_deps: false,
},
&cfg,
&[new_alpha, new_beta],
)?;
assert!(updated);
assert_eq!(
fs::read_to_string(rootfs.path().join("usr/bin/alpha"))?,
"alpha-new"
);
assert_eq!(
fs::read_to_string(rootfs.path().join("usr/bin/beta"))?,
"beta-new"
);
let hook_runs = fs::read_to_string(rootfs.path().join("hook-runs"))?;
assert_eq!(hook_runs.lines().collect::<Vec<_>>(), vec!["update:alpha"]);
let hook_targets: BTreeSet<_> = fs::read_to_string(rootfs.path().join("hook-targets"))?
.lines()
.map(str::to_string)
.collect();
assert_eq!(
hook_targets,
BTreeSet::from(["usr/bin/alpha".to_string(), "usr/bin/beta".to_string()])
);
Ok(())
}
#[test]
fn collect_conflicting_installed_packages_matches_by_name_and_provide() -> Result<()> {
let removals = collect_conflicting_installed_packages(
+28 -44
View File
@@ -747,29 +747,6 @@ pub(crate) fn collect_missing_update_dependencies(
Ok(missing)
}
fn run_update_install_command(
program: &Path,
request: &Path,
options: UpdateCommandOptions<'_>,
) -> Result<()> {
super::super::run_install_command_with_program(
program,
&[request.to_path_buf()],
options.rootfs,
super::super::ChildInstallCommandOptions {
no_deps: true,
assume_yes: true,
no_flags: options.no_flags,
cross_prefix: options.cross_prefix,
clean: options.clean,
lib32_only: false,
install_test_deps: options.install_test_deps,
install_context: Some(super::super::INSTALL_CONTEXT_UPDATE),
dep_chain: None,
},
)
}
fn confirm_update_replacements(candidates: &[UpdateCandidate]) -> Result<()> {
for candidate in candidates {
if !candidate.replaces_installed {
@@ -865,13 +842,16 @@ pub(crate) fn run_update_command(
confirm_update_replacements(&updates)?;
}
super::super::resolve_installed_conflicts_for_subjects(
&conflict_subjects,
options.rootfs,
config,
options.dry_run,
)?;
if options.dry_run {
super::super::resolve_installed_conflicts_for_subjects(
&conflict_subjects,
options.rootfs,
config,
true,
)?;
}
let mut transaction_requests = Vec::new();
if !missing_deps.is_empty() {
let dep_plan = planner::build_dependency_install_plan(
config,
@@ -892,29 +872,19 @@ pub(crate) fn run_update_command(
return Ok(());
}
crate::commands::execute_install_plan_with_child_commands(
transaction_requests.extend(super::super::install_requests_for_plan(
&dep_plan,
options.rootfs,
config,
crate::commands::InstallPlanExecutionOptions {
no_flags: options.no_flags,
cross_prefix: options.cross_prefix,
clean: options.clean,
dry_run: false,
confirm_installation: false,
lib32_only_requested_specs: false,
install_test_deps: options.install_test_deps,
},
)?;
options.rootfs,
)?);
} else if options.dry_run {
ui::info("Dry run enabled, stopping before update.");
return Ok(());
}
let exe = std::env::current_exe().context("Failed to locate depot executable")?;
for (idx, candidate) in updates.iter().enumerate() {
ui::info(format!(
"[{}/{}] updating {} v{}-{} -> {} v{}-{}",
"[{}/{}] staging update {} v{}-{} -> {} v{}-{}",
idx + 1,
updates.len(),
candidate.installed_package,
@@ -925,8 +895,22 @@ pub(crate) fn run_update_command(
candidate.candidate_revision
));
let request = candidate_request_path(candidate, config, options.rootfs)?;
run_update_install_command(&exe, &request, options)?;
transaction_requests.push(request);
}
super::super::run_update_transaction_install_requests(
super::super::DirectInstallOptions {
rootfs: options.rootfs,
no_deps: true,
no_flags: options.no_flags,
cross_prefix: options.cross_prefix,
clean: options.clean,
dry_run: false,
lib32_only: false,
install_test_deps: options.install_test_deps,
},
config,
&transaction_requests,
)?;
if options.clean {
crate::commands::clean_build_workspace(config)?;