Skip to content

Commit 2a14b6e

Browse files
authored
Merge pull request #274 from helsing-ai/vsiles/mtime
Keeping files "last modified" time to enable change detection in build.rs
2 parents e4c0c77 + c920bbc commit 2a14b6e

File tree

6 files changed

+210
-79
lines changed

6 files changed

+210
-79
lines changed

flake.nix

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
rust-overlay = {
77
url = "github:oxalica/rust-overlay";
88
inputs.nixpkgs.follows = "nixpkgs";
9-
inputs.flake-utils.follows = "flake-utils";
109
};
1110
advisory-db = {
1211
url = "github:rustsec/advisory-db";

src/command.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ pub async fn package(
232232
directory: impl AsRef<Path>,
233233
dry_run: bool,
234234
version: Option<Version>,
235+
preserve_mtime: bool,
235236
) -> miette::Result<()> {
236237
let mut manifest = Manifest::read().await?;
237238
let store = PackageStore::current().await?;
@@ -248,7 +249,7 @@ pub async fn package(
248249
store.populate(pkg).await?;
249250
}
250251

251-
let package = store.release(&manifest).await?;
252+
let package = store.release(&manifest, preserve_mtime).await?;
252253

253254
if dry_run {
254255
return Ok(());
@@ -275,6 +276,7 @@ pub async fn publish(
275276
#[cfg(feature = "git")] allow_dirty: bool,
276277
dry_run: bool,
277278
version: Option<Version>,
279+
preserve_mtime: bool,
278280
) -> miette::Result<()> {
279281
#[cfg(feature = "git")]
280282
async fn git_statuses() -> miette::Result<Vec<String>> {
@@ -345,7 +347,7 @@ pub async fn publish(
345347
store.populate(pkg).await?;
346348
}
347349

348-
let package = store.release(&manifest).await?;
350+
let package = store.release(&manifest, preserve_mtime).await?;
349351

350352
if dry_run {
351353
tracing::warn!(":: aborting upload due to dry run");
@@ -356,7 +358,9 @@ pub async fn publish(
356358
}
357359

358360
/// Installs dependencies
359-
pub async fn install() -> miette::Result<()> {
361+
///
362+
/// if [preserve_mtime] is true, local dependencies will keep their modification time
363+
pub async fn install(preserve_mtime: bool) -> miette::Result<()> {
360364
let manifest = Manifest::read().await?;
361365
let lockfile = Lockfile::read_or_default().await?;
362366
let store = PackageStore::current().await?;
@@ -371,10 +375,15 @@ pub async fn install() -> miette::Result<()> {
371375
tracing::info!(":: installed {}@{}", pkg.name, pkg.version);
372376
}
373377

374-
let dependency_graph =
375-
DependencyGraph::from_manifest(&manifest, &lockfile, &credentials.into(), &cache)
376-
.await
377-
.wrap_err(miette!("dependency resolution failed"))?;
378+
let dependency_graph = DependencyGraph::from_manifest(
379+
&manifest,
380+
&lockfile,
381+
&credentials.into(),
382+
&cache,
383+
preserve_mtime,
384+
)
385+
.await
386+
.wrap_err(miette!("dependency resolution failed"))?;
378387

379388
let mut locked = Vec::new();
380389

src/main.rs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ enum Command {
9494
/// Note: This overrides the version in the manifest.
9595
#[clap(long)]
9696
set_version: Option<Version>,
97+
/// Indicate whether access time information is preserved when creating a package.
98+
/// Default is 'true'
99+
#[clap(long)]
100+
preserve_mtime: Option<bool>,
97101
},
98102

99103
/// Packages and uploads this api to the registry
@@ -115,10 +119,19 @@ enum Command {
115119
/// Note: This overrides the version in the manifest.
116120
#[clap(long)]
117121
set_version: Option<Version>,
122+
/// Indicate whether access time information is preserved when creating a package.
123+
/// Default is 'true'
124+
#[clap(long)]
125+
preserve_mtime: Option<bool>,
118126
},
119127

120128
/// Installs dependencies
121-
Install,
129+
Install {
130+
/// Indicate whether access time information is preserved when installing a local.
131+
/// Default is 'true'
132+
#[clap(long)]
133+
preserve_local_mtime: Option<bool>,
134+
},
122135
/// Uninstalls dependencies
123136
Uninstall,
124137

@@ -229,23 +242,31 @@ async fn main() -> miette::Result<()> {
229242
output_directory,
230243
dry_run,
231244
set_version,
232-
} => command::package(output_directory, dry_run, set_version)
233-
.await
234-
.wrap_err(miette!(
235-
"failed to export `{package}` into the buffrs package format"
236-
)),
245+
preserve_mtime,
246+
} => command::package(
247+
output_directory,
248+
dry_run,
249+
set_version,
250+
preserve_mtime.unwrap_or(true),
251+
)
252+
.await
253+
.wrap_err(miette!(
254+
"failed to export `{package}` into the buffrs package format"
255+
)),
237256
Command::Publish {
238257
registry,
239258
repository,
240259
allow_dirty,
241260
dry_run,
242261
set_version,
262+
preserve_mtime,
243263
} => command::publish(
244264
registry.to_owned(),
245265
repository.to_owned(),
246266
allow_dirty,
247267
dry_run,
248268
set_version,
269+
preserve_mtime.unwrap_or(true),
249270
)
250271
.await
251272
.wrap_err(miette!(
@@ -255,7 +276,9 @@ async fn main() -> miette::Result<()> {
255276
"failed to lint protocol buffers in `{}`",
256277
PackageStore::PROTO_PATH
257278
)),
258-
Command::Install => command::install()
279+
Command::Install {
280+
preserve_local_mtime,
281+
} => command::install(preserve_local_mtime.unwrap_or(true))
259282
.await
260283
.wrap_err(miette!("failed to install dependencies for `{package}`")),
261284
Command::Uninstall => command::uninstall()

src/package/compressed.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use std::{
1616
collections::BTreeMap,
1717
io::{self, Cursor, Read, Write},
1818
path::{Path, PathBuf},
19+
time::UNIX_EPOCH,
1920
};
2021

2122
use bytes::{Buf, Bytes};
@@ -32,6 +33,8 @@ use crate::{
3233
ManagedFile,
3334
};
3435

36+
use super::store::Entry;
37+
3538
/// An in memory representation of a `buffrs` package
3639
#[derive(Clone, Debug, PartialEq, Eq)]
3740
pub struct Package {
@@ -46,7 +49,11 @@ impl Package {
4649
///
4750
/// This intentionally uses a [`BTreeMap`] to ensure that the list of files is sorted
4851
/// lexicographically. This ensures a reproducible output.
49-
pub fn create(mut manifest: Manifest, files: BTreeMap<PathBuf, Bytes>) -> miette::Result<Self> {
52+
pub fn create(
53+
mut manifest: Manifest,
54+
files: BTreeMap<PathBuf, Entry>,
55+
preserve_mtime: bool,
56+
) -> miette::Result<Self> {
5057
if manifest.edition == Edition::Unknown {
5158
manifest = Manifest::new(manifest.package, manifest.dependencies);
5259
}
@@ -88,8 +95,23 @@ impl Package {
8895
.into_diagnostic()
8996
.wrap_err(miette!("failed to add manifest to release"))?;
9097

91-
for (name, contents) in &files {
98+
for (name, entry) in &files {
9299
let mut header = tar::Header::new_gnu();
100+
101+
let Entry { contents, metadata } = entry;
102+
103+
if preserve_mtime {
104+
let mtime = metadata
105+
.as_ref()
106+
.and_then(|metadata| metadata.modified().ok())
107+
.and_then(|modified| modified.duration_since(UNIX_EPOCH).ok())
108+
.map(|duration| duration.as_secs());
109+
110+
if let Some(mtime) = mtime {
111+
header.set_mtime(mtime);
112+
}
113+
}
114+
93115
header.set_mode(0o444);
94116
header.set_size(contents.len() as u64);
95117
archive

src/package/store.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use std::{
1818
path::{Path, PathBuf},
1919
};
2020

21+
use bytes::Bytes;
2122
use miette::{bail, ensure, miette, Context, IntoDiagnostic};
2223
use tokio::fs;
2324
use walkdir::WalkDir;
@@ -151,7 +152,11 @@ impl PackageStore {
151152
}
152153

153154
/// Packages a release from the local file system state
154-
pub async fn release(&self, manifest: &Manifest) -> miette::Result<Package> {
155+
pub async fn release(
156+
&self,
157+
manifest: &Manifest,
158+
preserve_mtime: bool,
159+
) -> miette::Result<Package> {
155160
for dependency in manifest.dependencies.iter() {
156161
let resolved = self.resolve(&dependency.package).await?;
157162

@@ -171,10 +176,17 @@ impl PackageStore {
171176
for entry in self.collect(&pkg_path, false).await {
172177
let path = entry.strip_prefix(&pkg_path).into_diagnostic()?;
173178
let contents = tokio::fs::read(&entry).await.unwrap();
174-
entries.insert(path.into(), contents.into());
179+
180+
entries.insert(
181+
path.into(),
182+
Entry {
183+
contents: contents.into(),
184+
metadata: tokio::fs::metadata(&entry).await.ok(),
185+
},
186+
);
175187
}
176188

177-
let package = Package::create(manifest.clone(), entries)?;
189+
let package = Package::create(manifest.clone(), entries, preserve_mtime)?;
178190

179191
tracing::info!(":: packaged {}@{}", package.name(), package.version());
180192

@@ -260,6 +272,13 @@ impl PackageStore {
260272
}
261273
}
262274

275+
pub struct Entry {
276+
/// Actual bytes of the file
277+
pub contents: Bytes,
278+
/// File metadata, like mtime, ...
279+
pub metadata: Option<std::fs::Metadata>,
280+
}
281+
263282
#[test]
264283
fn can_get_proto_path() {
265284
assert_eq!(

0 commit comments

Comments
 (0)