Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Repository instructions

## Building

- Always use `./build.ps1` from the repository root to build or compile the project.
- Do not invoke Cargo directly for build validation, including `cargo build` or `cargo check`.
- For targeted builds, use `./build.ps1 -Project <project-name>` instead of a package-scoped Cargo command.
- Use the relevant `build.ps1` switches for linting, testing, release builds, documentation, auditing, and other supported validation.
5 changes: 4 additions & 1 deletion .github/instructions/instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ For a quick build and test on your current platform:

## Building the Project

The main build script is `build.ps1`, which orchestrates the entire build process.
The main build script is `build.ps1`, which orchestrates the entire build process. Always use
`./build.ps1` from the repository root to build or compile the project. Do not invoke Cargo
directly for build validation, including `cargo build` or `cargo check`. For targeted builds,
use `./build.ps1 -Project <project-name>`.

### Basic Build

Expand Down
28 changes: 28 additions & 0 deletions lib/dsc-lib/locales/en-us.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ dependencyTypeMismatch = "'dependsOn' resource type '%{resource_type}' does not
resourceNotInOrder = "Resource not found in order"
dependencyNotInOrder = "Dependency not found in order"
circularDependency = "Circular dependency detected for resource named '%{resource}'"
gettingInvocationOrder = "Getting resource invocation order"
invocationOrder = "Resource invocation order"

[configure.export_filter]
Expand Down Expand Up @@ -105,6 +106,10 @@ metadataRefreshEnvNoContext = "Resource returned '_refreshEnv' which indicates e
copyDeprecated = "Copy for loop '%{name}' is deprecated and will be removed in a future release. See https://github.com/PowerShell/DSC/issues/1429 for more details."
secureStringMustBeString = "Secure string parameter '%{name}' must be a string"
secureObjectMustBeObject = "Secure object parameter '%{name}' must be an object"
prefixUserPath = "Prefixing user PATH '%{value}' to system PATH '%{system_path}'"
systemPathNotFound = "System PATH not found, using user PATH '%{value}' as PATH"
resourceType = "resource_type %{resource_type}"
validatingParameter = "Validating parameter '%{name}'"

[configure.parameters]
importingParametersFromComplexInput = "Importing parameters from complex input"
Expand Down Expand Up @@ -153,6 +158,20 @@ conditionNotMet = "Condition '%{condition}' not met, skipping manifest at '%{pat
adaptedResourcePathNotFound = "Adapted resource '%{resource}' path not found: %{path}"
invalidManifestFileName = "Invalid manifest file name '%{path}'"
failedLoadManifest = "Failed to load manifest: %{err}"
restrictedPath = "DSC_RESTRICTED_PATH: %{path}"
resourcePath = "DSC_RESOURCE_PATH: %{path}"
adapterNameFilter = "Using type name filter '%{filter}' as filter for adapter name"
adapterNameFilterForDiscovery = "Using type name filter '%{filter}' for adapter name"
resourceNameFilter = "Using type name filter '%{filter}' as filter for resource name"
adapterListFailed = "Adapter failed to list resources with exit code %{exit_code}: %{stderr}"
failedParseResource = "Failed to parse resource: %{line} -> %{err}"
adapterListedResources = "Adapter '%{adapter}' listed %{count} resources"
savingLookupTable = "Saving lookup table with %{count} items to %{path}"
failedWriteLookupTable = "Unable to write lookup_table file %{path}"
failedCreateLookupTableDirectory = "Unable to create parent directories of the lookup_table file %{path}"
failedGetLookupTableDirectory = "Unable to get directory of the lookup_table file %{path}"
failedSerializeLookupTable = "Unable to serialize lookup_table to json"
readLookupTable = "Read %{count} items into lookup table from %{path}"

[dscresources.commandResource]
invokeGet = "Invoking get for '%{resource}'"
Expand Down Expand Up @@ -210,6 +229,9 @@ noAdaptedContent = "No adapted content available for resource '%{resource}'"
invalidAdaptedContent = "Invalid adapted content for resource '%{resource}': %{error}"
exportFilteringNotSupported = "Resource '%{resource}' does not support export filtering"
retrievedSchemaFromCache = "Retrieved schema for resource '%{resource}' with version '%{version}' from cache"
writingStdin = "Writing to command STDIN: %{input}"
processTrace = "PID %{process_id}: %{message}"
processTraceWithTarget = "PID %{process_id}: %{target}: %{line_number}: %{message}"

[dscresources.dscresource]
invokeGet = "Invoking get for '%{resource}'"
Expand Down Expand Up @@ -250,6 +272,9 @@ adapterDoesNotSupportDelete = "Adapter '%{adapter}' does not support delete oper
validatingAgainstSchema = "Validating against resource schema"
deprecationMessage = "Resource '%{resource}' is deprecated: %{message}"
retrievedSchemaFromCache = "Retrieved schema for resource '%{resource}' with version '%{version}' from cache"
exportResult = "Export result: %{result}"
json = "JSON: %{json}"
schema = "Schema: %{schema}"

[dscresources.resource_manifest]
resourceManifestSchemaTitle = "Resource manifest schema URI"
Expand All @@ -272,6 +297,7 @@ secretMultipleLinesReturned = "Extension '%{extension}' returned multiple lines
importProcessingOutput = "Processing output from extension '%{extension}'"
deprecationMessage = "Extension '%{extension}' is deprecated: %{message}"
failedLoadManifest = "Extension '%{extension}' failed to load manifest: %{err}"
manifestImported = "Manifest imported from extension %{extension}"

[extensions.extension_manifest]
extensionManifestSchemaTitle = "Extension manifest schema URI"
Expand Down Expand Up @@ -575,6 +601,7 @@ syntax = "map( <array>, <lambda> )"

[functions.max]
description = "Returns the largest number from a list of numbers"
invoked = "max function"
syntax = "max( <number>, [number], ... )"
emptyArray = "Array cannot be empty"
integersOnly = "Array must contain only integers"
Expand All @@ -590,6 +617,7 @@ noMin = "Unable to find min value"

[functions.mod]
description = "Divides the first number by the second and returns the remainder"
invoked = "mod function"
syntax = "mod( <number>, <number> )"
divideByZero = "Cannot divide by zero"

Expand Down
2 changes: 1 addition & 1 deletion lib/dsc-lib/src/configure/depends_on.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use tracing::{debug, warn};
///
/// * `DscError::Validation` - The configuration is invalid
pub fn get_resource_invocation_order(config: &Configuration, parser: &mut Statement, context: &mut Context) -> Result<Vec<Resource>, DscError> {
debug!("Getting resource invocation order");
debug!("{}", t!("configure.dependsOn.gettingInvocationOrder"));
let mut order: Vec<Resource> = Vec::new();
for resource in &config.resources {
// validate that the resource isn't specified more than once in the config
Expand Down
12 changes: 6 additions & 6 deletions lib/dsc-lib/src/configure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,10 +433,10 @@ fn get_metadata_from_result(mut context: Option<&mut Context>, properties: &mut
for (key, value) in user_env_vars {
if key == "PATH" {
if let Some(system_path) = env_vars.get(&key) {
trace!("Prefixing user PATH '{value}' to system PATH '{system_path}'");
trace!("{}", t!("configure.mod.prefixUserPath", value = value, system_path = system_path));
env_vars.insert(key, format!("{};{}", value, system_path));
} else {
trace!("System PATH not found, using user PATH '{value}' as PATH");
trace!("{}", t!("configure.mod.systemPathNotFound", value = value));
env_vars.insert(key, value.to_string());
}
} else {
Expand Down Expand Up @@ -684,7 +684,7 @@ impl Configurator {
let adapter = get_require_adapter_from_directive(&resource.directives);
find_resource_or_error!(dsc_resource, discovery, resource, adapter);
let properties = self.get_properties(&resource, &dsc_resource.kind)?;
debug!("resource_type {}", &resource.resource_type);
debug!("{}", t!("configure.mod.resourceType", resource_type = &resource.resource_type));
// see if the properties contains `_exist` and is false
let exist = match &properties {
Some(property_map) => {
Expand Down Expand Up @@ -873,7 +873,7 @@ impl Configurator {
let adapter = get_require_adapter_from_directive(&resource.directives);
find_resource_or_error!(dsc_resource, discovery, resource, adapter);
let properties = self.get_properties(&resource, &dsc_resource.kind)?;
debug!("resource_type {}", &resource.resource_type);
debug!("{}", t!("configure.mod.resourceType", resource_type = &resource.resource_type));
let expected = add_metadata(dsc_resource, properties, resource.metadata.clone())?;
trace!("{}", t!("configure.mod.expectedState", state = expected));
let start_datetime = chrono::Local::now();
Expand Down Expand Up @@ -963,7 +963,7 @@ impl Configurator {
let adapter = get_require_adapter_from_directive(&resource.directives);
find_resource_or_error!(dsc_resource, discovery, resource, adapter);
let properties = self.get_properties(resource, &dsc_resource.kind)?;
debug!("resource_type {}", &resource.resource_type);
debug!("{}", t!("configure.mod.resourceType", resource_type = &resource.resource_type));
let input = add_metadata(dsc_resource, properties, resource.metadata.clone())?;
trace!("{}", t!("configure.mod.exportInput", input = input));
let export_result = match add_resource_export_results_to_configuration(
Expand Down Expand Up @@ -1100,7 +1100,7 @@ impl Configurator {
let input_parameters: HashMap<String, Value> = import_parameters(parameters_input)?;
for (name, value) in input_parameters {
if let Some(constraint) = parameters.get(&name) {
debug!("Validating parameter '{name}'");
debug!("{}", t!("configure.mod.validatingParameter", name = name));
check_length(&name, &value, constraint)?;
check_allowed_values(&name, &value, constraint)?;
check_number_limits(&name, &value, constraint)?;
Expand Down
28 changes: 14 additions & 14 deletions lib/dsc-lib/src/discovery/command_discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ impl CommandDiscovery {
let dsc_restricted_path = env::var_os("DSC_RESTRICTED_PATH");
let dsc_resource_path = env::var_os("DSC_RESOURCE_PATH");
if resource_path_setting.allow_env_override && let Some(restricted_path) = &dsc_restricted_path {
debug!("DSC_RESTRICTED_PATH: {:?}", restricted_path.to_string_lossy());
debug!("{}", t!("discovery.commandDiscovery.restrictedPath", path = restricted_path.to_string_lossy() : {:?}));
paths.append(&mut env::split_paths(&restricted_path).collect::<Vec<_>>());

// when using restricted path, intent is to isolate the search of manifests and executables to the restricted path
Expand All @@ -152,7 +152,7 @@ impl CommandDiscovery {
return Err(DscError::Operation(t!("discovery.commandDiscovery.failedJoinRestrictedPath").to_string()));
}
} else if resource_path_setting.allow_env_override && let Some(resource_path) = &dsc_resource_path {
debug!("DSC_RESOURCE_PATH: {:?}", resource_path.to_string_lossy());
debug!("{}", t!("discovery.commandDiscovery.resourcePath", path = resource_path.to_string_lossy() : {:?}));
paths.append(&mut env::split_paths(&resource_path).collect::<Vec<_>>());

// just add exe home to PATH env var if not already in PATH env var
Expand Down Expand Up @@ -238,7 +238,7 @@ impl ResourceDiscovery for CommandDiscovery {

info!("{}", t!("discovery.commandDiscovery.discoverResources", kind = kind : {:?}, filter = filter.to_string()));

debug!("Using type name filter '{filter}' for adapter name");
debug!("{}", t!("discovery.commandDiscovery.adapterNameFilterForDiscovery", filter = filter));
let mut progress = ProgressBar::new(1, self.progress_format)?;
match kind {
DiscoveryKind::Resource => {
Expand Down Expand Up @@ -376,9 +376,9 @@ impl ResourceDiscovery for CommandDiscovery {
}

let adapters = locked_clone!(ADAPTERS);
debug!("Using type name filter '{adapter_filter}' as filter for adapter name");
debug!("{}", t!("discovery.commandDiscovery.adapterNameFilter", filter = adapter_filter));

debug!("Using type name filter '{name_filter}' as filter for resource name");
debug!("{}", t!("discovery.commandDiscovery.resourceNameFilter", filter = name_filter));

let mut progress = ProgressBar::new(adapters.len() as u64, self.progress_format)?;
progress.write_activity("Searching for adapted resources");
Expand Down Expand Up @@ -416,7 +416,7 @@ impl ResourceDiscovery for CommandDiscovery {

if exit_code != 0 {
// in case of failure, log and continue
warn!("Adapter failed to list resources with exit code {exit_code}: {stderr}");
warn!("{}", t!("discovery.commandDiscovery.adapterListFailed", exit_code = exit_code, stderr = stderr));
continue;
}

Expand All @@ -434,14 +434,14 @@ impl ResourceDiscovery for CommandDiscovery {
}
},
Result::Err(err) => {
warn!("Failed to parse resource: {line} -> {err}");
warn!("{}", t!("discovery.commandDiscovery.failedParseResource", line = line, err = err));
}
}
}
}

adapter_progress.write_increment(1);
debug!("Adapter '{}' listed {} resources", adapter_name, adapter_resources_count);
debug!("{}", t!("discovery.commandDiscovery.adapterListedResources", adapter = adapter_name, count = adapter_resources_count));
}
}

Expand Down Expand Up @@ -1058,22 +1058,22 @@ fn save_adapted_resources_lookup_table(lookup_table: &HashMap<String, String>)
{
if let Ok(lookup_table_json) = serde_json::to_string(&lookup_table) {
let file_path = get_lookup_table_file_path();
debug!("Saving lookup table with {} items to {:?}", lookup_table.len(), file_path);
debug!("{}", t!("discovery.commandDiscovery.savingLookupTable", count = lookup_table.len(), path = file_path : {:?}));

let path = std::path::Path::new(&file_path);
if let Some(prefix) = path.parent() {
if create_dir_all(prefix).is_ok() {
if write(file_path.clone(), lookup_table_json).is_err() {
info!("Unable to write lookup_table file {file_path:?}");
info!("{}", t!("discovery.commandDiscovery.failedWriteLookupTable", path = file_path : {:?}));
}
} else {
info!("Unable to create parent directories of the lookup_table file {file_path:?}");
info!("{}", t!("discovery.commandDiscovery.failedCreateLookupTableDirectory", path = file_path : {:?}));
}
} else {
info!("Unable to get directory of the lookup_table file {file_path:?}");
info!("{}", t!("discovery.commandDiscovery.failedGetLookupTableDirectory", path = file_path : {:?}));
}
} else {
info!("Unable to serialize lookup_table to json");
info!("{}", t!("discovery.commandDiscovery.failedSerializeLookupTable"));
}
}

Expand All @@ -1086,7 +1086,7 @@ fn load_adapted_resources_lookup_table() -> HashMap<String, String>
Err(_) => { HashMap::new() }
};

debug!("Read {} items into lookup table from {:?}", lookup_table.len(), file_path);
debug!("{}", t!("discovery.commandDiscovery.readLookupTable", count = lookup_table.len(), path = file_path : {:?}));
lookup_table
}

Expand Down
30 changes: 20 additions & 10 deletions lib/dsc-lib/src/dscresources/command_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -865,7 +865,7 @@ async fn run_process_async(executable: &str, args: Option<Vec<String>>, input: O
let mut stderr_reader = BufReader::new(stderr).lines();

if let Some(input) = input {
trace!("Writing to command STDIN: {input}");
trace!("{}", t!("dscresources.commandResource.writingStdin", input = input));
let Some(mut stdin) = child.stdin.take() else {
return Err(DscError::CommandOperation(t!("dscresources.commandResource.processChildStdin").to_string(), executable.to_string()));
};
Expand Down Expand Up @@ -1286,9 +1286,19 @@ pub fn log_stderr_line<'a>(process_id: &u32, trace_line: &'a str) -> &'a str
0
};
let trace_message = if include_target {
format!("PID {process_id}: {target}: {line_number}: {}", trace_object.fields.message)
t!(
"dscresources.commandResource.processTraceWithTarget",
process_id = process_id,
target = target,
line_number = line_number,
message = trace_object.fields.message
).to_string()
} else {
format!("PID {process_id}: {}", trace_object.fields.message)
t!(
"dscresources.commandResource.processTrace",
process_id = process_id,
message = trace_object.fields.message
).to_string()
};
match trace_object.level {
TraceLevel::Error => {
Expand All @@ -1310,23 +1320,23 @@ pub fn log_stderr_line<'a>(process_id: &u32, trace_line: &'a str) -> &'a str
}
else if let Ok(json_obj) = serde_json::from_str::<Value>(trace_line) {
if let Some(msg) = json_obj.get("error") {
error!("PID {process_id}: {}", msg.as_str().unwrap_or_default());
error!("{}", t!("dscresources.commandResource.processTrace", process_id = process_id, message = msg.as_str().unwrap_or_default()));
} else if let Some(msg) = json_obj.get("warn") {
warn!("PID {process_id}: {}", msg.as_str().unwrap_or_default());
warn!("{}", t!("dscresources.commandResource.processTrace", process_id = process_id, message = msg.as_str().unwrap_or_default()));
} else if let Some(msg) = json_obj.get("info") {
info!("PID {process_id}: {}", msg.as_str().unwrap_or_default());
info!("{}", t!("dscresources.commandResource.processTrace", process_id = process_id, message = msg.as_str().unwrap_or_default()));
} else if let Some(msg) = json_obj.get("debug") {
debug!("PID {process_id}: {}", msg.as_str().unwrap_or_default());
debug!("{}", t!("dscresources.commandResource.processTrace", process_id = process_id, message = msg.as_str().unwrap_or_default()));
} else if let Some(msg) = json_obj.get("trace") {
trace!("PID {process_id}: {}", msg.as_str().unwrap_or_default());
trace!("{}", t!("dscresources.commandResource.processTrace", process_id = process_id, message = msg.as_str().unwrap_or_default()));
} else {
// the line is a valid json, but not one of standard trace lines - return it as filtered stderr_line
trace!("PID {process_id}: {trace_line}");
trace!("{}", t!("dscresources.commandResource.processTrace", process_id = process_id, message = trace_line));
return trace_line;
}
} else {
// the line is not a valid json - return it as filtered stderr_line
trace!("PID {process_id}: {}", trace_line);
trace!("{}", t!("dscresources.commandResource.processTrace", process_id = process_id, message = trace_line));
return trace_line;
}
}
Expand Down
Loading