Skip to content
Merged
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
6 changes: 1 addition & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,14 @@
npm-publish-workflow-check \
check-parity-inventory \
check-cli-metadata \
check-todo-args \
check-compatibility-dashboard \
check-upstream-test-coverage \
upstream-compatibility

RUST_MANIFEST := cmd/devcontainer/Cargo.toml
RELEASE_BINARY := ./cmd/devcontainer/target/release/devcontainer

tests: rust-fmt rust-clippy rust-check rust-tests build-release standalone-artifact-smoke pypi-wheel-smoke native-only-startup-contract acceptance-fixtures-check command-matrix-drift-check schema-drift-check parity-harness no-node-runtime npm-wrapper-check npm-publish-script-check npm-package-smoke homebrew-distribution-check npm-publish-workflow-check check-parity-inventory check-cli-metadata check-todo-args check-compatibility-dashboard check-upstream-test-coverage upstream-compatibility
tests: rust-fmt rust-clippy rust-check rust-tests build-release standalone-artifact-smoke pypi-wheel-smoke native-only-startup-contract acceptance-fixtures-check command-matrix-drift-check schema-drift-check parity-harness no-node-runtime npm-wrapper-check npm-publish-script-check npm-package-smoke homebrew-distribution-check npm-publish-workflow-check check-parity-inventory check-cli-metadata check-compatibility-dashboard check-upstream-test-coverage upstream-compatibility

rust-fmt:
cargo fmt --manifest-path $(RUST_MANIFEST) --all -- --check
Expand Down Expand Up @@ -101,9 +100,6 @@ check-parity-inventory:
check-cli-metadata:
node build/generate-cli-metadata.js --check

check-todo-args:
node build/generate-todo-args.js --check

check-compatibility-dashboard:
node build/generate-compatibility-dashboard.js --check

Expand Down
7 changes: 0 additions & 7 deletions TODO_ARGS.md

This file was deleted.

85 changes: 0 additions & 85 deletions build/check-repo-branding.js

This file was deleted.

95 changes: 0 additions & 95 deletions build/generate-todo-args.js

This file was deleted.

2 changes: 1 addition & 1 deletion cmd/devcontainer/src/cli_metadata.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"upstreamCommit": "2d81ee3c9ed96a7312c18c7513a17933f8f66d41",
"upstreamCommit": "6293ce5879399316f06287e42e710c0f8e5edfef",
"sourcePath": "upstream/src/spec-node/devContainersSpecCLI.ts",
"root": {
"lines": [
Expand Down
8 changes: 8 additions & 0 deletions cmd/devcontainer/src/commands/configuration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,14 @@ pub(crate) fn ensure_native_lockfile(
upgrade::ensure_native_lockfile(args, config_file, configuration)
}

pub(crate) fn validate_native_lockfile(
args: &[String],
config_file: &std::path::Path,
configuration: &Value,
) -> Result<(), String> {
upgrade::validate_native_lockfile(args, config_file, configuration)
}

pub(crate) fn should_use_native_read_configuration(args: &[String]) -> bool {
read::should_use_native_read_configuration(args)
}
Expand Down
36 changes: 34 additions & 2 deletions cmd/devcontainer/src/commands/configuration/tests/upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ fn ensure_native_lockfile_uses_shared_lockfile_format() {
.expect("lockfile write");

let lockfile = fs::read_to_string(root.join(".devcontainer-lock.json")).expect("lockfile");
assert!(!lockfile.ends_with('\n'));
assert!(lockfile.ends_with('\n'));
let _ = fs::remove_dir_all(root);
}

Expand All @@ -230,11 +230,43 @@ fn upgrade_lockfile_uses_shared_lockfile_format() {
.expect("lockfile payload");

let lockfile = fs::read_to_string(root.join(".devcontainer-lock.json")).expect("lockfile");
assert!(!lockfile.ends_with('\n'));
assert!(lockfile.ends_with('\n'));

let _ = fs::remove_dir_all(root);
}

#[test]
fn ensure_native_lockfile_rejects_corrupt_existing_lockfile_when_generating() {
let root = unique_temp_dir();
fs::create_dir_all(&root).expect("failed to create root");
let config_file = root.join(".devcontainer.json");
let lockfile_path = root.join(".devcontainer-lock.json");
fs::write(&lockfile_path, "this is not json").expect("corrupt lockfile");

let error = ensure_native_lockfile(
&[
"--workspace-folder".to_string(),
root.display().to_string(),
"--experimental-lockfile".to_string(),
],
&config_file,
&json!({
"image": "debian:bookworm",
"features": {
"ghcr.io/devcontainers/features/github-cli": {}
}
}),
)
.expect_err("corrupt lockfile error");

assert!(error.contains("line 1 column"), "{error}");
assert_eq!(
fs::read_to_string(lockfile_path).expect("lockfile"),
"this is not json"
);
let _ = fs::remove_dir_all(root);
}

#[test]
fn ensure_native_lockfile_reports_missing_frozen_lockfile() {
let root = unique_temp_dir();
Expand Down
52 changes: 47 additions & 5 deletions cmd/devcontainer/src/commands/configuration/upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,7 @@ pub(super) fn ensure_native_lockfile(
config_file: &Path,
configuration: &Value,
) -> Result<(), String> {
let wants_lockfile = common::has_flag(args, "--experimental-lockfile")
|| common::has_flag(args, "--experimental-frozen-lockfile");
if !wants_lockfile {
if !wants_native_lockfile(args) {
return Ok(());
}

Expand All @@ -84,8 +82,8 @@ pub(super) fn ensure_native_lockfile(
.or_else(|| config_file.parent().map(Path::to_path_buf));
let generated = generate_lockfile(configuration, workspace_folder.as_deref())?;
let path = lockfile_path(config_file);
let existing = existing_native_lockfile(args, &path)?;
if common::has_flag(args, "--experimental-frozen-lockfile") {
let existing = read_lockfile(path.clone())?;
let Some(existing) = existing else {
return Err("Lockfile does not exist.".to_string());
};
Expand All @@ -103,8 +101,52 @@ pub(super) fn ensure_native_lockfile(
Ok(())
}

pub(super) fn validate_native_lockfile(
args: &[String],
config_file: &Path,
configuration: &Value,
) -> Result<(), String> {
if !wants_native_lockfile(args) {
return Ok(());
}

let path = lockfile_path(config_file);
let existing = existing_native_lockfile(args, &path)?;
if common::has_flag(args, "--experimental-frozen-lockfile") {
let Some(existing) = existing else {
return Err("Lockfile does not exist.".to_string());
};
let workspace_folder = common::parse_option_value(args, "--workspace-folder")
.map(PathBuf::from)
.or_else(|| config_file.parent().map(Path::to_path_buf));
let generated = generate_lockfile(configuration, workspace_folder.as_deref())?;
if existing != generated {
return Err(format!(
"Lockfile at {} is out of date for the current feature configuration",
path.display()
));
}
}
Ok(())
}

fn wants_native_lockfile(args: &[String]) -> bool {
common::has_flag(args, "--experimental-lockfile")
|| common::has_flag(args, "--experimental-frozen-lockfile")
}

fn existing_native_lockfile(args: &[String], path: &Path) -> Result<Option<Lockfile>, String> {
if path.exists() || common::has_flag(args, "--experimental-frozen-lockfile") {
read_lockfile(path.to_path_buf())
} else {
Ok(None)
}
}

fn serialized_lockfile(lockfile: &Lockfile) -> Result<String, String> {
serde_json::to_string_pretty(lockfile).map_err(|error| error.to_string())
serde_json::to_string_pretty(lockfile)
.map(|json| format!("{json}\n"))
.map_err(|error| error.to_string())
}

#[cfg(test)]
Expand Down
10 changes: 10 additions & 0 deletions cmd/devcontainer/src/runtime/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ pub(crate) fn build_image(resolved: &ResolvedConfig, args: &[String]) -> Result<
.to_string()
})?;
return if let Some(feature_support) = feature_support {
configuration::validate_native_lockfile(
args,
&resolved.config_file,
&resolved.configuration,
)?;
let image_name = common::parse_option_value(args, "--image-name")
.unwrap_or_else(|| default_image_name(&resolved.workspace_folder));
let built =
Expand All @@ -79,6 +84,11 @@ pub(crate) fn build_image(resolved: &ResolvedConfig, args: &[String]) -> Result<
let image_name = common::parse_option_value(args, "--image-name")
.unwrap_or_else(|| default_image_name(&resolved.workspace_folder));
if let Some(feature_support) = feature_support {
configuration::validate_native_lockfile(
args,
&resolved.config_file,
&resolved.configuration,
)?;
let base_image = format!("{image_name}-base");
build_base_image(resolved, args, &base_image)?;
let built = build_feature_image(
Expand Down
Loading
Loading