Skip to content

Commit

Permalink
feat: lockfiles
Browse files Browse the repository at this point in the history
  • Loading branch information
jdx committed Oct 27, 2024
1 parent ec4f463 commit 4193b98
Show file tree
Hide file tree
Showing 24 changed files with 275 additions and 49 deletions.
2 changes: 1 addition & 1 deletion docs/.vitepress/cli_commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const commands: { [key: string]: Command } = {
},
},
current: {
hide: false,
hide: true,
},
deactivate: {
hide: false,
Expand Down
1 change: 0 additions & 1 deletion docs/cli/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ Answer yes to all confirmation prompts
* [`mise config get [-f --file <FILE>] [KEY]`](/cli/config/get.md)
* [`mise config ls [--no-header] [-J --json]`](/cli/config/ls.md)
* [`mise config set [-f --file <FILE>] [-t --type <TYPE>] <KEY> <VALUE>`](/cli/config/set.md)
* [`mise current [PLUGIN]`](/cli/current.md)
* [`mise deactivate`](/cli/deactivate.md)
* [`mise direnv <SUBCOMMAND>`](/cli/direnv.md)
* [`mise direnv activate`](/cli/direnv/activate.md)
Expand Down
2 changes: 2 additions & 0 deletions docs/cli/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ By default, this keeps the range specified in mise.toml. So if you have node@20
upgrade to the latest 20.x.x version available. See the `--bump` flag to use the latest version
and bump the version in mise.toml.

This will update mise.lock if it is enabled, see <https://mise.jdx.dev/configuration/settings.html#lockfile>

## Arguments

### `[TOOL@VERSION]...`
Expand Down
19 changes: 19 additions & 0 deletions e2e/lockfile/test_lockfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bash

export MISE_LOCKFILE=1
export MISE_EXPERIMENTAL=1

mise install [email protected]
mise use tiny@1 --trace
mise install [email protected]
assert "mise config get -f .mise.toml tools.tiny" "1"
assert "mise ls tiny --json --current | jq -r '.[0].requested_version'" "1"
assert "mise ls tiny --json --current | jq -r '.[0].version'" "1.0.0"
assert "cat .mise.lock" '[tools]
tiny = "1.0.0"'

mise use tiny@1
assert "cat .mise.lock" '[tools]
tiny = "1.1.0"'
assert "mise ls tiny --json --current | jq -r '.[0].requested_version'" "1"
assert "mise ls tiny --json --current | jq -r '.[0].version'" "1.1.0"
3 changes: 0 additions & 3 deletions man/man1/mise.1
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,6 @@ Generate shell completions
mise\-config(1)
Manage config files
.TP
mise\-current(1)
Shows current active and installed runtime versions
.TP
mise\-deactivate(1)
Disable mise for current shell session
.TP
Expand Down
6 changes: 4 additions & 2 deletions mise.usage.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ cmd "config" help="Manage config files" {
arg "<VALUE>" help="The value to set the key to"
}
}
cmd "current" help="Shows current active and installed runtime versions" {
cmd "current" hide=true help="Shows current active and installed runtime versions" {
long_help r"Shows current active and installed runtime versions

This is similar to `mise ls --current`, but this only shows the runtime
Expand Down Expand Up @@ -1333,7 +1333,9 @@ cmd "upgrade" help="Upgrades outdated tools" {

By default, this keeps the range specified in mise.toml. So if you have node@20 set, it will
upgrade to the latest 20.x.x version available. See the `--bump` flag to use the latest version
and bump the version in mise.toml."
and bump the version in mise.toml.

This will update mise.lock if it is enabled, see https://mise.jdx.dev/configuration/settings.html#lockfile"
after_long_help r"Examples:

# Upgrades node to the latest version matching the range in mise.toml
Expand Down
5 changes: 5 additions & 0 deletions schema/mise.json
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,11 @@
"description": "Use libgit2 for git operations, set to false to shell out to git.",
"type": "boolean"
},
"lockfile": {
"default": false,
"description": "Create and read lockfiles for tool versions.",
"type": "boolean"
},
"log_level": {
"default": "info",
"description": "Show more/less output.",
Expand Down
19 changes: 19 additions & 0 deletions settings.toml
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,25 @@ Use libgit2 for git operations. This is generally faster but may not be as compa
system's libgit2 is not the same version as the one used by mise.
"""

[lockfile]
env = "MISE_LOCKFILE"
type = "Bool"
default = false
description = "Create and read lockfiles for tool versions."
docs = """
Create and read lockfiles for tool versions. This is useful when you'd like to have loose versions in mise.toml like this:
```toml
[tools]
node = "22"
gh = "latest"
```
But you'd like the versions installed to be consistent within a project. When this is enabled, mise will automatically
create mise.lock files next to mise.toml files containing pinned versions.
When installing tools, mise will reference this lockfile if it exists and this setting is enabled to resolve versions.
"""

[log_level]
env = "MISE_LOG_LEVEL"
type = "String"
Expand Down
2 changes: 1 addition & 1 deletion src/cli/current.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::toolset::{Toolset, ToolsetBuilder};
/// This is similar to `mise ls --current`, but this only shows the runtime
/// and/or version. It's designed to fit into scripts more easily.
#[derive(Debug, clap::Args)]
#[clap(verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)]
#[clap(verbatim_doc_comment, hide = true, after_long_help = AFTER_LONG_HELP)]
pub struct Current {
/// Plugin to show versions of
/// e.g.: ruby, node, cargo:eza, npm:prettier, etc.
Expand Down
11 changes: 8 additions & 3 deletions src/cli/install.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::cli::args::{BackendArg, ToolArg};
use crate::config::Config;
use crate::lockfile;
use crate::toolset::{InstallOptions, ToolRequest, ToolSource, ToolVersion, Toolset};
use crate::ui::multi_progress_report::MultiProgressReport;
use eyre::Result;
use eyre::{Result, WrapErr};
use itertools::Itertools;
use std::collections::HashSet;

Expand Down Expand Up @@ -64,7 +65,9 @@ impl Install {
warn!("specify a version with `mise install <PLUGIN>@<VERSION>`");
return Ok(vec![]);
}
ts.install_versions(config, tool_versions, &mpr, &self.install_opts())
let versions = ts.install_versions(config, tool_versions, &mpr, &self.install_opts())?;
lockfile::update_lockfiles(&versions).wrap_err("failed to update lockfiles")?;
Ok(versions)
}

fn install_opts(&self) -> InstallOptions {
Expand Down Expand Up @@ -125,7 +128,9 @@ impl Install {
}
let mpr = MultiProgressReport::get();
let mut ts = Toolset::from(trs.clone());
ts.install_versions(config, versions, &mpr, &self.install_opts())
let versions = ts.install_versions(config, versions, &mpr, &self.install_opts())?;
lockfile::update_lockfiles(&versions).wrap_err("failed to update lockfiles")?;
Ok(versions)
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/cli/uninstall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::cli::args::ToolArg;
use crate::config::Config;
use crate::toolset::{ToolRequest, ToolSource, ToolVersion, ToolsetBuilder};
use crate::ui::multi_progress_report::MultiProgressReport;
use crate::{backend, runtime_symlinks, shims};
use crate::{backend, lockfile, runtime_symlinks, shims};

/// Removes installed tool versions
///
Expand Down Expand Up @@ -67,6 +67,7 @@ impl Uninstall {
}
}

lockfile::update_lockfiles(&[]).wrap_err("failed to update lockfiles")?;
let ts = ToolsetBuilder::new().build(&config)?;
shims::reshim(&ts, false).wrap_err("failed to reshim")?;
runtime_symlinks::rebuild(&config)?;
Expand Down
7 changes: 5 additions & 2 deletions src/cli/upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::file::display_path;
use crate::toolset::{InstallOptions, OutdatedInfo, ToolVersion, ToolsetBuilder};
use crate::ui::multi_progress_report::MultiProgressReport;
use crate::ui::progress_report::SingleReport;
use crate::{runtime_symlinks, shims, ui};
use crate::{lockfile, runtime_symlinks, shims, ui};
use demand::DemandOption;
use eyre::{Context, Result};

Expand All @@ -13,6 +13,8 @@ use eyre::{Context, Result};
/// By default, this keeps the range specified in mise.toml. So if you have node@20 set, it will
/// upgrade to the latest 20.x.x version available. See the `--bump` flag to use the latest version
/// and bump the version in mise.toml.
///
/// This will update mise.lock if it is enabled, see https://mise.jdx.dev/configuration/settings.html#lockfile
#[derive(Debug, clap::Args)]
#[clap(visible_alias = "up", verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)]
pub struct Upgrade {
Expand Down Expand Up @@ -142,7 +144,7 @@ impl Upgrade {
latest_versions: true,
};
let new_versions = outdated.iter().map(|o| o.tool_request.clone()).collect();
ts.install_versions(config, new_versions, &mpr, &opts)?;
let versions = ts.install_versions(config, new_versions, &mpr, &opts)?;

for (o, bump, mut cf) in config_file_updates {
cf.replace_versions(o.tool_request.backend(), &[bump])?;
Expand All @@ -154,6 +156,7 @@ impl Upgrade {
self.uninstall_old_version(&o.tool_version, pr.as_ref())?;
}

lockfile::update_lockfiles(&versions).wrap_err("failed to update lockfiles")?;
let ts = ToolsetBuilder::new().with_args(&self.tool).build(config)?;
shims::reshim(&ts, false).wrap_err("failed to reshim")?;
runtime_symlinks::rebuild(config)?;
Expand Down
14 changes: 11 additions & 3 deletions src/cli/use.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::path::{Path, PathBuf};

use console::style;
use eyre::Result;
use eyre::{Result, WrapErr};
use itertools::Itertools;

use crate::cli::args::{BackendArg, ToolArg};
Expand All @@ -13,7 +13,7 @@ use crate::env::{
use crate::file::display_path;
use crate::toolset::{InstallOptions, ToolRequest, ToolSource, ToolVersion, ToolsetBuilder};
use crate::ui::multi_progress_report::MultiProgressReport;
use crate::{env, file};
use crate::{env, file, lockfile};

/// Installs a tool and adds the version it to mise.toml.
///
Expand Down Expand Up @@ -97,7 +97,7 @@ impl Use {
None => ToolRequest::new(t.backend, "latest", ToolSource::Argument),
})
.collect::<Result<_>>()?;
let versions = ts.install_versions(
let mut versions = ts.install_versions(
&config,
versions.clone(),
&mpr,
Expand Down Expand Up @@ -133,6 +133,14 @@ impl Use {
cf.remove_plugin(plugin_name)?;
}
cf.save()?;

for tv in &mut versions {
// update the source so the lockfile is updated correctly
tv.request.set_source(cf.source());
}

lockfile::update_lockfiles(&versions).wrap_err("failed to update lockfiles")?;

self.render_success_message(cf.as_ref(), &versions)?;
Ok(())
}
Expand Down
40 changes: 15 additions & 25 deletions src/cli/where.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,39 +30,29 @@ pub struct Where {
impl Where {
pub fn run(self) -> Result<()> {
let config = Config::try_get()?;
let runtime = match self.tool.tvr {
let tvr = match self.tool.tvr {
Some(tvr) => tvr,
None => match self.asdf_version {
Some(version) => self.tool.with_version(&version),
Some(version) => self.tool.with_version(&version).tvr.unwrap(),
None => {
let ts = ToolsetBuilder::new()
.with_args(&[self.tool.clone()])
.build(&config)?;
let v = ts
.versions
let ts = ToolsetBuilder::new().build(&config)?;
ts.versions
.get(&self.tool.backend)
.and_then(|v| v.requests.first())
.map(|r| r.version());
self.tool.with_version(&v.unwrap_or(String::from("latest")))
.and_then(|tvr| tvr.requests.first().cloned())
.unwrap_or_else(|| self.tool.with_version("latest").tvr.unwrap())
}
},
_ => self.tool,
};

let plugin = backend::get(&runtime.backend);
let ba = tvr.backend();
let backend = backend::get(ba);
let tv = tvr.resolve(backend.as_ref(), false)?;

match runtime
.tvr
.as_ref()
.map(|tvr| tvr.resolve(plugin.as_ref(), false))
{
Some(Ok(tv)) if plugin.is_version_installed(&tv, true) => {
miseprintln!("{}", tv.install_path().to_string_lossy());
Ok(())
}
_ => Err(VersionNotInstalled(
runtime.backend.to_string(),
runtime.tvr.map(|tvr| tvr.version()).unwrap_or_default(),
))?,
if backend.is_version_installed(&tv, true) {
miseprintln!("{}", tv.install_path().to_string_lossy());
Ok(())
} else {
Err(VersionNotInstalled(ba.to_string(), tvr.version()))?
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/config/config_file/legacy_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ impl ConfigFile for LegacyVersionFile {
unimplemented!()
}

fn source(&self) -> ToolSource {
ToolSource::LegacyVersionFile(self.path.clone())
}

fn to_tool_request_set(&self) -> Result<ToolRequestSet> {
Ok(self.tools.clone())
}
Expand Down
4 changes: 4 additions & 0 deletions src/config/config_file/mise_toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,10 @@ impl ConfigFile for MiseToml {
Ok(self.doc()?.to_string())
}

fn source(&self) -> ToolSource {
ToolSource::MiseToml(self.path.clone())
}

fn to_tool_request_set(&self) -> eyre::Result<ToolRequestSet> {
let source = ToolSource::MiseToml(self.path.clone());
let mut trs = ToolRequestSet::new();
Expand Down
1 change: 1 addition & 0 deletions src/config/config_file/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ pub trait ConfigFile: Debug + Send + Sync {
fn replace_versions(&mut self, fa: &BackendArg, versions: &[String]) -> eyre::Result<()>;
fn save(&self) -> eyre::Result<()>;
fn dump(&self) -> eyre::Result<String>;
fn source(&self) -> ToolSource;
fn to_toolset(&self) -> eyre::Result<Toolset> {
Ok(self.to_tool_request_set()?.into())
}
Expand Down
4 changes: 4 additions & 0 deletions src/config/config_file/tool_versions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ impl ConfigFile for ToolVersions {
Ok(s.trim_end().to_string() + "\n")
}

fn source(&self) -> ToolSource {
ToolSource::ToolVersions(self.path.clone())
}

fn to_tool_request_set(&self) -> eyre::Result<ToolRequestSet> {
Ok(self.tools.clone())
}
Expand Down
5 changes: 1 addition & 4 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,7 @@ impl Config {
.cloned()
.collect_vec();
time!("load config_filenames");
let config_paths = load_config_paths(&config_filenames)
.into_iter()
.unique_by(|p| p.canonicalize().unwrap_or_else(|_| p.clone()))
.collect_vec();
let config_paths = load_config_paths(&config_filenames);
time!("load config_paths");
trace!("config_paths: {config_paths:?}");
let config_files = load_all_config_files(&config_paths, &legacy_files)?;
Expand Down
Loading

0 comments on commit 4193b98

Please sign in to comment.