Skip to content

Commit d445700

Browse files
committed
cli: Add lockfile mode
1 parent 74a97b0 commit d445700

File tree

3 files changed

+113
-70
lines changed

3 files changed

+113
-70
lines changed

README.md

+25-34
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,11 @@ Commands:
114114
help Print this message or the help of the given subcommand(s)
115115

116116
Options:
117-
-d, --directory <FOLDER> Base folder for sources.json and the boilerplate default.nix [env: NPINS_DIRECTORY=] [default: npins]
118-
-v, --verbose Print debug messages
119-
-h, --help Print help
120-
-V, --version Print version
117+
-d, --directory <FOLDER> Base folder for sources.json and the boilerplate default.nix [env: NPINS_DIRECTORY=] [default: npins]
118+
--lock-file <LOCK_FILE> Specifies the path to the sources.json and activates lockfile mode. In lockfile mode, no default.nix will be generated and --directory will be ignored
119+
-v, --verbose Print debug messages
120+
-h, --help Print help
121+
-V, --version Print version
121122
```
122123

123124
### Initialization
@@ -137,10 +138,9 @@ Intializes the npins directory. Running this multiple times will restore/upgrade
137138
Usage: npins init [OPTIONS]
138139

139140
Options:
140-
--bare Don't add an initial `nixpkgs` entry
141-
-d, --directory <FOLDER> Base folder for sources.json and the boilerplate default.nix [env: NPINS_DIRECTORY=] [default: npins]
142-
-v, --verbose Print debug messages
143-
-h, --help Print help
141+
--bare Don't add an initial `nixpkgs` entry
142+
-v, --verbose Print debug messages
143+
-h, --help Print help
144144
```
145145

146146
### Migrate from Niv
@@ -166,10 +166,9 @@ Arguments:
166166
[PATH] [default: nix/sources.json]
167167

168168
Options:
169-
-d, --directory <FOLDER> Base folder for sources.json and the boilerplate default.nix [env: NPINS_DIRECTORY=] [default: npins]
170-
-n, --name <NAME> Only import one entry from Niv
171-
-v, --verbose Print debug messages
172-
-h, --help Print help
169+
-n, --name <NAME> Only import one entry from Niv
170+
-v, --verbose Print debug messages
171+
-h, --help Print help
173172
```
174173

175174
### Adding dependencies
@@ -208,11 +207,10 @@ Commands:
208207
help Print this message or the help of the given subcommand(s)
209208

210209
Options:
211-
-d, --directory <FOLDER> Base folder for sources.json and the boilerplate default.nix [env: NPINS_DIRECTORY=] [default: npins]
212-
--name <NAME> Add the pin with a custom name. If a pin with that name already exists, it will be overwritten
213-
-n, --dry-run Don't actually apply the changes
214-
-v, --verbose Print debug messages
215-
-h, --help Print help
210+
--name <NAME> Add the pin with a custom name. If a pin with that name already exists, it will be overwritten
211+
-n, --dry-run Don't actually apply the changes
212+
-v, --verbose Print debug messages
213+
-h, --help Print help
216214
```
217215

218216
There are several options for tracking git branches, releases and tags:
@@ -229,16 +227,14 @@ Arguments:
229227
Options:
230228
-b, --branch <BRANCH>
231229
Track a branch instead of a release
232-
-d, --directory <FOLDER>
233-
Base folder for sources.json and the boilerplate default.nix [env: NPINS_DIRECTORY=] [default: npins]
234230
--name <NAME>
235231
Add the pin with a custom name. If a pin with that name already exists, it will be overwritten
236232
--at <tag or rev>
237233
Use a specific commit/release instead of the latest. This may be a tag name, or a git revision when --branch is set
238-
-v, --verbose
239-
Print debug messages
240234
--pre-releases
241235
Also track pre-releases. Conflicts with the --branch option
236+
-v, --verbose
237+
Print debug messages
242238
--upper-bound <version>
243239
Bound the version resolution. For example, setting this to "2" will restrict updates to 1.X versions. Conflicts with the --branch option
244240
--release-prefix <RELEASE_PREFIX>
@@ -261,9 +257,8 @@ Arguments:
261257
<NAME>
262258

263259
Options:
264-
-d, --directory <FOLDER> Base folder for sources.json and the boilerplate default.nix [env: NPINS_DIRECTORY=] [default: npins]
265-
-v, --verbose Print debug messages
266-
-h, --help Print help
260+
-v, --verbose Print debug messages
261+
-h, --help Print help
267262
```
268263

269264
### Show current entries
@@ -277,9 +272,8 @@ Lists the current pin entries
277272
Usage: npins show [OPTIONS]
278273

279274
Options:
280-
-d, --directory <FOLDER> Base folder for sources.json and the boilerplate default.nix [env: NPINS_DIRECTORY=] [default: npins]
281-
-v, --verbose Print debug messages
282-
-h, --help Print help
275+
-v, --verbose Print debug messages
276+
-h, --help Print help
283277
```
284278

285279
### Updating dependencies
@@ -296,16 +290,14 @@ Arguments:
296290
[NAMES]... Updates only the specified pins
297291

298292
Options:
299-
-d, --directory <FOLDER>
300-
Base folder for sources.json and the boilerplate default.nix [env: NPINS_DIRECTORY=] [default: npins]
301293
-p, --partial
302294
Don't update versions, only re-fetch hashes
303295
-f, --full
304296
Re-fetch hashes even if the version hasn't changed. Useful to make sure the derivations are in the Nix store
305-
-v, --verbose
306-
Print debug messages
307297
-n, --dry-run
308298
Print the diff, but don't write back the changes
299+
-v, --verbose
300+
Print debug messages
309301
--max-concurrent-downloads <MAX_CONCURRENT_DOWNLOADS>
310302
Maximum number of simultaneous downloads [default: 5]
311303
-h, --help
@@ -323,9 +315,8 @@ Upgrade the sources.json and default.nix to the latest format version. This may
323315
Usage: npins upgrade [OPTIONS]
324316

325317
Options:
326-
-d, --directory <FOLDER> Base folder for sources.json and the boilerplate default.nix [env: NPINS_DIRECTORY=] [default: npins]
327-
-v, --verbose Print debug messages
328-
-h, --help Print help
318+
-v, --verbose Print debug messages
319+
-h, --help Print help
329320
```
330321

331322
### Using private GitLab repositories

src/cli.rs

+60-36
Original file line numberDiff line numberDiff line change
@@ -534,14 +534,18 @@ fn print_diff(name: &str, diff: impl AsRef<[diff::DiffEntry]>) {
534534
pub struct Opts {
535535
/// Base folder for sources.json and the boilerplate default.nix
536536
#[arg(
537-
global = true,
538537
short = 'd',
539538
long = "directory",
540539
default_value = "npins",
541540
env = "NPINS_DIRECTORY"
542541
)]
543542
folder: std::path::PathBuf,
544543

544+
/// Specifies the path to the sources.json and activates lockfile mode.
545+
/// In lockfile mode, no default.nix will be generated and --directory will be ignored.
546+
#[arg(long)]
547+
lock_file: Option<std::path::PathBuf>,
548+
545549
/// Print debug messages.
546550
#[arg(global = true, short = 'v', long = "verbose")]
547551
pub verbose: bool,
@@ -552,7 +556,11 @@ pub struct Opts {
552556

553557
impl Opts {
554558
fn read_pins(&self) -> Result<NixPins> {
555-
let path = self.folder.join("sources.json");
559+
let path = if let Some(lock_file) = self.lock_file.as_ref() {
560+
lock_file.to_owned()
561+
} else {
562+
self.folder.join("sources.json")
563+
};
556564
let fh = std::io::BufReader::new(std::fs::File::open(&path).with_context(move || {
557565
format!(
558566
"Failed to open {}. You must initialize npins before you can show current pins.",
@@ -564,10 +572,14 @@ impl Opts {
564572
}
565573

566574
fn write_pins(&self, pins: &NixPins) -> Result<()> {
567-
if !self.folder.exists() {
568-
std::fs::create_dir(&self.folder)?;
569-
}
570-
let path = self.folder.join("sources.json");
575+
let path = if let Some(lock_file) = &self.lock_file {
576+
lock_file.to_owned()
577+
} else {
578+
if !self.folder.exists() {
579+
std::fs::create_dir(&self.folder)?;
580+
}
581+
self.folder.join("sources.json")
582+
};
571583
let mut fh = std::fs::File::create(&path)
572584
.with_context(move || format!("Failed to open {} for writing.", path.display()))?;
573585
serde_json::to_writer_pretty(&mut fh, &versions::to_value_versioned(pins))?;
@@ -577,23 +589,27 @@ impl Opts {
577589

578590
async fn init(&self, o: &InitOpts) -> Result<()> {
579591
log::info!("Welcome to npins!");
580-
let default_nix = DEFAULT_NIX;
581-
if !self.folder.exists() {
582-
log::info!("Creating `{}` directory", self.folder.display());
583-
std::fs::create_dir(&self.folder).context("Failed to create npins folder")?;
584-
}
585-
log::info!("Writing default.nix");
586-
let p = self.folder.join("default.nix");
587-
let mut fh = std::fs::File::create(&p).context("Failed to create npins default.nix")?;
588-
fh.write_all(default_nix.as_bytes())?;
589592

590-
// Only create the pins if the file isn't there yet
591-
if self.folder.join("sources.json").exists() {
592-
log::info!(
593-
"The file '{}' already exists; nothing to do.",
594-
self.folder.join("pins.json").display()
595-
);
596-
return Ok(());
593+
// Skip the entire default.nix and convenience creating folders bit in lockfile mode
594+
if self.lock_file.is_none() {
595+
let default_nix = DEFAULT_NIX;
596+
if !self.folder.exists() {
597+
log::info!("Creating `{}` directory", self.folder.display());
598+
std::fs::create_dir(&self.folder).context("Failed to create npins folder")?;
599+
}
600+
log::info!("Writing default.nix");
601+
let p = self.folder.join("default.nix");
602+
let mut fh = std::fs::File::create(&p).context("Failed to create npins default.nix")?;
603+
fh.write_all(default_nix.as_bytes())?;
604+
605+
// Only create the pins if the file isn't there yet
606+
if self.folder.join("sources.json").exists() {
607+
log::info!(
608+
"The file '{}' already exists; nothing to do.",
609+
self.folder.join("pins.json").display()
610+
);
611+
return Ok(());
612+
}
597613
}
598614

599615
let initial_pins = if o.bare {
@@ -610,7 +626,7 @@ impl Opts {
610626
self.write_pins(&initial_pins)?;
611627
log::info!(
612628
"Successfully written initial files to '{}'.",
613-
self.folder.display()
629+
self.lock_file.as_ref().unwrap_or(&self.folder).display()
614630
);
615631
Ok(())
616632
}
@@ -761,19 +777,22 @@ impl Opts {
761777
}
762778

763779
fn upgrade(&self) -> Result<()> {
764-
anyhow::ensure!(
765-
self.folder.exists(),
766-
"Could not find npins folder at {}",
767-
self.folder.display(),
768-
);
780+
if self.lock_file.is_none() {
781+
anyhow::ensure!(
782+
self.folder.exists(),
783+
"Could not find npins folder at {}",
784+
self.folder.display(),
785+
);
769786

770-
let nix_path = self.folder.join("default.nix");
771-
let nix_file = DEFAULT_NIX;
772-
if std::fs::read_to_string(&nix_path)? == nix_file {
773-
log::info!("default.nix is already up to date");
774-
} else {
775-
log::info!("Replacing default.nix with an up to date version");
776-
std::fs::write(&nix_path, nix_file).context("Failed to create npins default.nix")?;
787+
let nix_path = self.folder.join("default.nix");
788+
let nix_file = DEFAULT_NIX;
789+
if std::fs::read_to_string(&nix_path)? == nix_file {
790+
log::info!("default.nix is already up to date");
791+
} else {
792+
log::info!("Replacing default.nix with an up to date version");
793+
std::fs::write(&nix_path, nix_file)
794+
.context("Failed to create npins default.nix")?;
795+
}
777796
}
778797

779798
log::info!("Upgrading sources.json to the newest format version");
@@ -791,7 +810,9 @@ impl Opts {
791810
let pins_raw_new = versions::upgrade(pins_raw.clone()).context("Upgrading failed")?;
792811
let pins: NixPins = serde_json::from_value(pins_raw_new.clone())?;
793812
if pins_raw_new != serde_json::Value::Object(pins_raw) {
794-
log::info!("Done. It is recommended to at least run `update --partial` afterwards.");
813+
log::info!(
814+
"Done. It is recommended to at least run `npins update --partial` afterwards."
815+
);
795816
}
796817
self.write_pins(&pins)
797818
}
@@ -969,6 +990,9 @@ impl Opts {
969990
}
970991

971992
pub async fn run(&self) -> Result<()> {
993+
if self.lock_file.is_some() && &*self.folder != std::path::Path::new("npins") {
994+
anyhow::bail!("If --lock-file is set, --directory will be ignored and thus should not be set to a non-default value (which is \"npins\")");
995+
}
972996
match &self.command {
973997
Command::Init(o) => self.init(o).await?,
974998
Command::Show => self.show()?,

test.nix

+28
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,34 @@ let
277277
'';
278278
in
279279
{
280+
initNoDefaultNix = mkGitTest {
281+
name = "init-no-default-nix";
282+
repositories."foo" = gitRepo;
283+
commands = ''
284+
npins --lock-file sources.json init --bare
285+
# Setting a custom directory should fail in lockfile mode
286+
! npins --lock-file sources.json -d npins2 show
287+
npins --lock-file sources.json -d npins show
288+
test -e npins/default.nix && exit 1
289+
V=$(jq -r .pins sources.json)
290+
[[ "$V" = "{}" ]]
291+
'';
292+
};
293+
294+
addInLockfileMode = mkGitTest rec {
295+
name = "add-in-lockfile-mode";
296+
repositories."foo" = gitRepo;
297+
commands = ''
298+
npins --lock-file sources2.json init --bare
299+
npins --lock-file sources2.json add git http://localhost:8000/foo -b test-branch
300+
301+
# Check version and url
302+
eq "$(jq -r .pins.foo.version sources2.json)" "null"
303+
eq "$(jq -r .pins.foo.revision sources2.json)" "$(resolveGitCommit ${repositories."foo"} HEAD)"
304+
eq "$(jq -r .pins.foo.url sources2.json)" "null"
305+
'';
306+
};
307+
280308
addDryRun = mkGitTest {
281309
name = "add-dry-run";
282310
repositories."foo" = gitRepo;

0 commit comments

Comments
 (0)