mirror of https://github.com/astral-sh/uv
Add initial work
This commit is contained in:
parent
9f51dca37c
commit
fc5d5e871c
|
|
@ -34,6 +34,7 @@ use uv_platform_tags::Tags;
|
||||||
use uv_preview::Preview;
|
use uv_preview::Preview;
|
||||||
use uv_pypi_types::{Conflicts, ResolverMarkerEnvironment};
|
use uv_pypi_types::{Conflicts, ResolverMarkerEnvironment};
|
||||||
use uv_python::{PythonEnvironment, PythonInstallation};
|
use uv_python::{PythonEnvironment, PythonInstallation};
|
||||||
|
use uv_redacted::DisplaySafeUrl;
|
||||||
use uv_requirements::{
|
use uv_requirements::{
|
||||||
GroupsSpecification, LookaheadResolver, NamedRequirementsResolver, RequirementsSource,
|
GroupsSpecification, LookaheadResolver, NamedRequirementsResolver, RequirementsSource,
|
||||||
RequirementsSpecification, SourceTree, SourceTreeResolver,
|
RequirementsSpecification, SourceTree, SourceTreeResolver,
|
||||||
|
|
@ -454,6 +455,20 @@ impl ChangedDist {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn version(&self) -> Option<&Version> {
|
||||||
|
match self {
|
||||||
|
Self::Local(dist) => Some(dist.installed_version().version()),
|
||||||
|
Self::Remote(dist) => dist.version(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn url(&self) -> Option<&DisplaySafeUrl> {
|
||||||
|
match self {
|
||||||
|
Self::Local(dist) => dist.installed_version().url(),
|
||||||
|
Self::Remote(dist) => dist.version_or_url().url().map(|url| &**url),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A summary of the changes made to the environment during an installation.
|
/// A summary of the changes made to the environment during an installation.
|
||||||
|
|
|
||||||
|
|
@ -375,7 +375,7 @@ pub(crate) async fn remove(
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(()) => {}
|
Ok(_) => {}
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
||||||
.report(err)
|
.report(err)
|
||||||
|
|
|
||||||
|
|
@ -339,7 +339,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(()) => {}
|
Ok(_) => {}
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::native_tls(
|
||||||
client_builder.is_native_tls(),
|
client_builder.is_native_tls(),
|
||||||
|
|
@ -867,7 +867,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(()) => {}
|
Ok(_) => {}
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(
|
return diagnostics::OperationDiagnostic::native_tls(
|
||||||
client_builder.is_native_tls(),
|
client_builder.is_native_tls(),
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ use uv_configuration::{
|
||||||
use uv_dispatch::BuildDispatch;
|
use uv_dispatch::BuildDispatch;
|
||||||
use uv_distribution::LoweredExtraBuildDependencies;
|
use uv_distribution::LoweredExtraBuildDependencies;
|
||||||
use uv_distribution_types::{
|
use uv_distribution_types::{
|
||||||
DirectorySourceDist, Dist, Index, Requirement, Resolution, ResolvedDist, SourceDist,
|
DirectorySourceDist, Dist, Index, Name, Requirement, Resolution, ResolvedDist, SourceDist,
|
||||||
};
|
};
|
||||||
use uv_fs::{PortablePathBuf, Simplified};
|
use uv_fs::{PortablePathBuf, Simplified};
|
||||||
use uv_installer::{InstallationStrategy, SitePackages};
|
use uv_installer::{InstallationStrategy, SitePackages};
|
||||||
|
|
@ -37,16 +37,16 @@ use uv_workspace::pyproject::Source;
|
||||||
use uv_workspace::{DiscoveryOptions, MemberDiscovery, VirtualProject, Workspace, WorkspaceCache};
|
use uv_workspace::{DiscoveryOptions, MemberDiscovery, VirtualProject, Workspace, WorkspaceCache};
|
||||||
|
|
||||||
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger, InstallLogger};
|
use crate::commands::pip::loggers::{DefaultInstallLogger, DefaultResolveLogger, InstallLogger};
|
||||||
use crate::commands::pip::operations::Modifications;
|
use crate::commands::pip::operations::{ChangedDist, Changelog, Modifications};
|
||||||
use crate::commands::pip::resolution_markers;
|
use crate::commands::pip::resolution_markers;
|
||||||
use crate::commands::pip::{operations, resolution_tags};
|
use crate::commands::pip::{operations, resolution_tags};
|
||||||
use crate::commands::project::install_target::InstallTarget;
|
use crate::commands::project::install_target::InstallTarget;
|
||||||
use crate::commands::project::lock::{LockMode, LockOperation, LockResult};
|
use crate::commands::project::lock::{LockMode, LockOperation, LockResult};
|
||||||
use crate::commands::project::lock_target::LockTarget;
|
use crate::commands::project::lock_target::LockTarget;
|
||||||
use crate::commands::project::{
|
use crate::commands::project::{
|
||||||
PlatformState, ProjectEnvironment, ProjectError, ScriptEnvironment, UniversalState,
|
EnvironmentUpdate, PlatformState, ProjectEnvironment, ProjectError, ScriptEnvironment,
|
||||||
default_dependency_groups, detect_conflicts, script_extra_build_requires, script_specification,
|
UniversalState, default_dependency_groups, detect_conflicts, script_extra_build_requires,
|
||||||
update_environment,
|
script_specification, update_environment,
|
||||||
};
|
};
|
||||||
use crate::commands::{ExitStatus, diagnostics};
|
use crate::commands::{ExitStatus, diagnostics};
|
||||||
use crate::printer::Printer;
|
use crate::printer::Printer;
|
||||||
|
|
@ -207,11 +207,12 @@ pub(crate) async fn sync(
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
let sync_report = SyncReport {
|
let mut sync_report = SyncReport {
|
||||||
dry_run: dry_run.enabled(),
|
dry_run: dry_run.enabled(),
|
||||||
environment: EnvironmentReport::from(&environment),
|
environment: EnvironmentReport::from(&environment),
|
||||||
action: SyncAction::from(&environment),
|
action: SyncAction::from(&environment),
|
||||||
target: TargetName::from(&target),
|
target: TargetName::from(&target),
|
||||||
|
packages: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Show the intermediate results if relevant
|
// Show the intermediate results if relevant
|
||||||
|
|
@ -292,7 +293,8 @@ pub(crate) async fn sync(
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(..) => {
|
Ok(EnvironmentUpdate { changelog, .. }) => {
|
||||||
|
sync_report.packages = PackageChangeReport::from_changelog(&changelog);
|
||||||
// Generate a report for the script without a lockfile
|
// Generate a report for the script without a lockfile
|
||||||
let report = Report {
|
let report = Report {
|
||||||
schema: SchemaReport::default(),
|
schema: SchemaReport::default(),
|
||||||
|
|
@ -387,27 +389,13 @@ pub(crate) async fn sync(
|
||||||
writeln!(printer.stderr(), "{message}")?;
|
writeln!(printer.stderr(), "{message}")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let report = Report {
|
|
||||||
schema: SchemaReport::default(),
|
|
||||||
target: TargetName::from(&target),
|
|
||||||
project: target.project().map(ProjectReport::from),
|
|
||||||
script: target.script().map(ScriptReport::from),
|
|
||||||
sync: sync_report,
|
|
||||||
lock: Some(lock_report),
|
|
||||||
dry_run: dry_run.enabled(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(output) = report.format(output_format) {
|
|
||||||
writeln!(printer.stdout_important(), "{output}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identify the installation target.
|
// Identify the installation target.
|
||||||
let sync_target = identify_installation_target(&target, outcome.lock(), all_packages, &package);
|
let sync_target = identify_installation_target(&target, outcome.lock(), all_packages, &package);
|
||||||
|
|
||||||
let state = state.fork();
|
let state = state.fork();
|
||||||
|
|
||||||
// Perform the sync operation.
|
// Perform the sync operation.
|
||||||
match do_sync(
|
let changelog = match do_sync(
|
||||||
sync_target,
|
sync_target,
|
||||||
&environment,
|
&environment,
|
||||||
&extras,
|
&extras,
|
||||||
|
|
@ -430,13 +418,29 @@ pub(crate) async fn sync(
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(()) => {}
|
Ok(changelog) => changelog,
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
||||||
.report(err)
|
.report(err)
|
||||||
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
.map_or(Ok(ExitStatus::Failure), |err| Err(err.into()));
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
sync_report.packages = PackageChangeReport::from_changelog(&changelog);
|
||||||
|
|
||||||
|
let report = Report {
|
||||||
|
schema: SchemaReport::default(),
|
||||||
|
target: TargetName::from(&target),
|
||||||
|
project: target.project().map(ProjectReport::from),
|
||||||
|
script: target.script().map(ScriptReport::from),
|
||||||
|
sync: sync_report,
|
||||||
|
lock: Some(lock_report),
|
||||||
|
dry_run: dry_run.enabled(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(output) = report.format(output_format) {
|
||||||
|
writeln!(printer.stdout_important(), "{output}")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
match outcome {
|
match outcome {
|
||||||
|
|
@ -614,7 +618,7 @@ pub(super) async fn do_sync(
|
||||||
dry_run: DryRun,
|
dry_run: DryRun,
|
||||||
printer: Printer,
|
printer: Printer,
|
||||||
preview: Preview,
|
preview: Preview,
|
||||||
) -> Result<(), ProjectError> {
|
) -> Result<Changelog, ProjectError> {
|
||||||
// Extract the project settings.
|
// Extract the project settings.
|
||||||
let InstallerSettingsRef {
|
let InstallerSettingsRef {
|
||||||
index_locations,
|
index_locations,
|
||||||
|
|
@ -825,7 +829,7 @@ pub(super) async fn do_sync(
|
||||||
let site_packages = SitePackages::from_environment(venv)?;
|
let site_packages = SitePackages::from_environment(venv)?;
|
||||||
|
|
||||||
// Sync the environment.
|
// Sync the environment.
|
||||||
operations::install(
|
let changelog = operations::install(
|
||||||
&resolution,
|
&resolution,
|
||||||
site_packages,
|
site_packages,
|
||||||
InstallationStrategy::Strict,
|
InstallationStrategy::Strict,
|
||||||
|
|
@ -850,7 +854,7 @@ pub(super) async fn do_sync(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(changelog)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Filter out any virtual workspace members.
|
/// Filter out any virtual workspace members.
|
||||||
|
|
@ -1254,6 +1258,9 @@ struct SyncReport {
|
||||||
environment: EnvironmentReport,
|
environment: EnvironmentReport,
|
||||||
/// The action performed during the sync, e.g., what was done to the environment.
|
/// The action performed during the sync, e.g., what was done to the environment.
|
||||||
action: SyncAction,
|
action: SyncAction,
|
||||||
|
/// The packages that changed during the sync.
|
||||||
|
#[serde(default)]
|
||||||
|
packages: Vec<PackageChangeReport>,
|
||||||
|
|
||||||
// We store these fields so the report can format itself self-contained, but the outer
|
// We store these fields so the report can format itself self-contained, but the outer
|
||||||
// [`Report`] is intended to include these in user-facing output
|
// [`Report`] is intended to include these in user-facing output
|
||||||
|
|
@ -1276,6 +1283,7 @@ impl SyncReport {
|
||||||
let Self {
|
let Self {
|
||||||
environment,
|
environment,
|
||||||
action,
|
action,
|
||||||
|
packages: _,
|
||||||
dry_run,
|
dry_run,
|
||||||
target,
|
target,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
@ -1294,6 +1302,77 @@ impl SyncReport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A summary of a single package change performed during sync.
|
||||||
|
#[derive(Serialize, Debug, Clone)]
|
||||||
|
struct PackageChangeReport {
|
||||||
|
/// The normalized package name.
|
||||||
|
name: String,
|
||||||
|
/// The resolved version of the package.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
version: Option<uv_pep440::Version>,
|
||||||
|
/// The source for URL-based requirements.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
source: Option<PackageChangeSourceReport>,
|
||||||
|
/// The action that was taken for the package.
|
||||||
|
action: PackageChangeAction,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackageChangeReport {
|
||||||
|
fn from_changelog(changelog: &Changelog) -> Vec<Self> {
|
||||||
|
let mut changes: Vec<_> = changelog
|
||||||
|
.uninstalled
|
||||||
|
.iter()
|
||||||
|
.map(|dist| Self::from_dist(dist, PackageChangeAction::Removed))
|
||||||
|
.chain(
|
||||||
|
changelog
|
||||||
|
.installed
|
||||||
|
.iter()
|
||||||
|
.map(|dist| Self::from_dist(dist, PackageChangeAction::Added)),
|
||||||
|
)
|
||||||
|
.chain(
|
||||||
|
changelog
|
||||||
|
.reinstalled
|
||||||
|
.iter()
|
||||||
|
.map(|dist| Self::from_dist(dist, PackageChangeAction::Reinstalled)),
|
||||||
|
)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
changes.sort_by(|a, b| {
|
||||||
|
a.name
|
||||||
|
.cmp(&b.name)
|
||||||
|
.then_with(|| a.action.cmp(&b.action))
|
||||||
|
.then_with(|| a.version.cmp(&b.version))
|
||||||
|
});
|
||||||
|
changes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_dist(dist: &ChangedDist, action: PackageChangeAction) -> Self {
|
||||||
|
Self {
|
||||||
|
name: dist.name().to_string(),
|
||||||
|
version: dist.version().cloned(),
|
||||||
|
source: dist.url().map(|url| PackageChangeSourceReport {
|
||||||
|
url: url.to_string(),
|
||||||
|
}),
|
||||||
|
action,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The action taken on an individual package during sync.
|
||||||
|
#[derive(Serialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
enum PackageChangeAction {
|
||||||
|
Removed,
|
||||||
|
Added,
|
||||||
|
Reinstalled,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The source for a package change, when it originated from a URL requirement.
|
||||||
|
#[derive(Serialize, Debug, Clone)]
|
||||||
|
struct PackageChangeSourceReport {
|
||||||
|
url: String,
|
||||||
|
}
|
||||||
|
|
||||||
/// The report for a lock operation.
|
/// The report for a lock operation.
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
struct LockReport {
|
struct LockReport {
|
||||||
|
|
|
||||||
|
|
@ -680,7 +680,7 @@ async fn lock_and_sync(
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(()) => {}
|
Ok(_) => {}
|
||||||
Err(ProjectError::Operation(err)) => {
|
Err(ProjectError::Operation(err)) => {
|
||||||
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
return diagnostics::OperationDiagnostic::native_tls(client_builder.is_native_tls())
|
||||||
.report(err)
|
.report(err)
|
||||||
|
|
|
||||||
|
|
@ -420,7 +420,14 @@ fn sync_json() -> Result<()> {
|
||||||
"implementation": "cpython"
|
"implementation": "cpython"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"action": "check"
|
"action": "check",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "iniconfig",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"action": "added"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"lock": {
|
"lock": {
|
||||||
"path": "[TEMP_DIR]/uv.lock",
|
"path": "[TEMP_DIR]/uv.lock",
|
||||||
|
|
@ -464,7 +471,8 @@ fn sync_json() -> Result<()> {
|
||||||
"implementation": "cpython"
|
"implementation": "cpython"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"action": "check"
|
"action": "check",
|
||||||
|
"packages": []
|
||||||
},
|
},
|
||||||
"lock": {
|
"lock": {
|
||||||
"path": "[TEMP_DIR]/uv.lock",
|
"path": "[TEMP_DIR]/uv.lock",
|
||||||
|
|
@ -503,7 +511,8 @@ fn sync_json() -> Result<()> {
|
||||||
"implementation": "cpython"
|
"implementation": "cpython"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"action": "check"
|
"action": "check",
|
||||||
|
"packages": []
|
||||||
},
|
},
|
||||||
"lock": {
|
"lock": {
|
||||||
"path": "[TEMP_DIR]/uv.lock",
|
"path": "[TEMP_DIR]/uv.lock",
|
||||||
|
|
@ -569,7 +578,8 @@ fn sync_json() -> Result<()> {
|
||||||
"implementation": "cpython"
|
"implementation": "cpython"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"action": "check"
|
"action": "check",
|
||||||
|
"packages": []
|
||||||
},
|
},
|
||||||
"lock": {
|
"lock": {
|
||||||
"path": "[TEMP_DIR]/uv.lock",
|
"path": "[TEMP_DIR]/uv.lock",
|
||||||
|
|
@ -629,7 +639,14 @@ fn sync_dry_json() -> Result<()> {
|
||||||
"implementation": "cpython"
|
"implementation": "cpython"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"action": "create"
|
"action": "create",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "iniconfig",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"action": "added"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"lock": {
|
"lock": {
|
||||||
"path": "[TEMP_DIR]/uv.lock",
|
"path": "[TEMP_DIR]/uv.lock",
|
||||||
|
|
@ -6856,7 +6873,24 @@ fn sync_active_script_environment_json() -> Result<()> {
|
||||||
"implementation": "cpython"
|
"implementation": "cpython"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"action": "create"
|
"action": "create",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "anyio",
|
||||||
|
"version": "4.3.0",
|
||||||
|
"action": "added"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "idna",
|
||||||
|
"version": "3.6",
|
||||||
|
"action": "added"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sniffio",
|
||||||
|
"version": "1.3.1",
|
||||||
|
"action": "added"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"lock": null,
|
"lock": null,
|
||||||
"dry_run": false
|
"dry_run": false
|
||||||
|
|
@ -6902,7 +6936,24 @@ fn sync_active_script_environment_json() -> Result<()> {
|
||||||
"implementation": "cpython"
|
"implementation": "cpython"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"action": "create"
|
"action": "create",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "anyio",
|
||||||
|
"version": "4.3.0",
|
||||||
|
"action": "added"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "idna",
|
||||||
|
"version": "3.6",
|
||||||
|
"action": "added"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sniffio",
|
||||||
|
"version": "1.3.1",
|
||||||
|
"action": "added"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"lock": null,
|
"lock": null,
|
||||||
"dry_run": false
|
"dry_run": false
|
||||||
|
|
@ -6961,7 +7012,24 @@ fn sync_active_script_environment_json() -> Result<()> {
|
||||||
"implementation": "cpython"
|
"implementation": "cpython"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"action": "update"
|
"action": "update",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "anyio",
|
||||||
|
"version": "4.3.0",
|
||||||
|
"action": "added"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "idna",
|
||||||
|
"version": "3.6",
|
||||||
|
"action": "added"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sniffio",
|
||||||
|
"version": "1.3.1",
|
||||||
|
"action": "added"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"lock": null,
|
"lock": null,
|
||||||
"dry_run": false
|
"dry_run": false
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue