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
15 changes: 8 additions & 7 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

set -eu

manifest_path="cmd/devcontainer/Cargo.toml"
echo "Running make rust-fmt..."
make rust-fmt

echo "Running cargo fmt -- --check..."
cargo fmt --manifest-path "$manifest_path" --all -- --check
echo "Running make rust-clippy..."
make rust-clippy

echo "Running cargo clippy -- -D warnings..."
cargo clippy --manifest-path "$manifest_path" -- -D warnings
echo "Running make rust-check..."
make rust-check

echo "Running cargo check..."
cargo check --manifest-path "$manifest_path"
echo "Running make rust-doc..."
make rust-doc
15 changes: 15 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,18 @@ updates:
directory: "/"
schedule:
interval: "weekly"

- package-ecosystem: "cargo"
directory: "/cmd/devcontainer"
schedule:
interval: "weekly"

- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"

- package-ecosystem: "npm"
directory: "/tap"
schedule:
interval: "weekly"
2 changes: 1 addition & 1 deletion .github/workflows/devcontainer-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
if [[ -z "$latest_tag" ]]; then
base_version="0.0.0"
else
base_version="${latest_tag#${TAG_PREFIX}}"
base_version="${latest_tag#"$TAG_PREFIX"}"
fi

IFS='.' read -r major minor patch <<< "$base_version"
Expand Down
25 changes: 20 additions & 5 deletions .github/workflows/rust-port-convergence.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,32 @@ jobs:
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov

- name: Install cargo-deny
uses: taiki-e/install-action@cargo-deny

- name: Workflow lint
run: make actionlint-check

- name: Shell lint
run: make shellcheck

- name: Rust fmt
run: cargo fmt --manifest-path cmd/devcontainer/Cargo.toml --all -- --check
run: make rust-fmt

- name: Rust clippy
run: cargo clippy --manifest-path cmd/devcontainer/Cargo.toml -- -D warnings
run: make rust-clippy

- name: Rust check
run: cargo check --manifest-path cmd/devcontainer/Cargo.toml
run: make rust-check

- name: Rust doc
run: make rust-doc

- name: Rust tests
run: cargo test --manifest-path cmd/devcontainer/Cargo.toml
run: make rust-tests

- name: Dependency policy
run: make cargo-deny-check

- name: Build release binary
run: cargo build --release --manifest-path cmd/devcontainer/Cargo.toml
Expand All @@ -57,7 +72,7 @@ jobs:
run: make pypi-wheel-smoke

- name: Rust coverage
run: cargo llvm-cov --manifest-path cmd/devcontainer/Cargo.toml --lcov --output-path cmd/devcontainer/target/coverage.lcov
run: make rust-coverage

- name: Native-only startup contract
run: node build/check-native-only.js
Expand Down
47 changes: 42 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
rust-fmt \
rust-clippy \
rust-check \
rust-doc \
rust-tests \
cargo-deny-check \
rust-coverage \
actionlint-check \
shellcheck \
build-release \
real-engine-lifecycle-smoke \
real-engine-lifecycle-smoke-docker \
Expand All @@ -11,7 +16,9 @@
pypi-wheel-smoke \
native-only-startup-contract \
acceptance-fixtures-check \
check-upstream-submodule \
command-matrix-drift-check \
check-cli-reference \
schema-drift-check \
parity-harness \
no-node-runtime \
Expand All @@ -25,27 +32,48 @@
check-cli-metadata \
check-compatibility-dashboard \
check-upstream-test-coverage \
check-devcontainer-config \
upstream-compatibility

RUST_MANIFEST := cmd/devcontainer/Cargo.toml
RELEASE_BINARY := ./cmd/devcontainer/target/release/devcontainer
CARGO_LLVM_COV ?= cargo llvm-cov
COVERAGE_LINE_THRESHOLD := 88
ACTIONLINT := uv tool run --from actionlint-py actionlint
SHELLCHECK := uv tool run --from shellcheck-py shellcheck
SHELLCHECK_FILES := $(shell git ls-files -- '*.sh' '.githooks/pre-commit' ':(exclude)upstream/**' ':(exclude)spec/**' ':(exclude)target/**' ':(exclude)node_modules/**')

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 tap-check homebrew-distribution-check npm-publish-workflow-check check-parity-inventory check-cli-metadata check-compatibility-dashboard check-upstream-test-coverage upstream-compatibility
tests: rust-fmt rust-tests rust-clippy rust-check rust-doc rust-coverage cargo-deny-check actionlint-check shellcheck build-release standalone-artifact-smoke pypi-wheel-smoke native-only-startup-contract acceptance-fixtures-check check-upstream-submodule command-matrix-drift-check check-cli-reference schema-drift-check parity-harness no-node-runtime npm-wrapper-check npm-publish-script-check npm-package-smoke tap-check homebrew-distribution-check npm-publish-workflow-check check-parity-inventory check-cli-metadata check-compatibility-dashboard check-upstream-test-coverage check-devcontainer-config upstream-compatibility

rust-fmt:
cargo fmt --manifest-path $(RUST_MANIFEST) --all -- --check

rust-clippy:
cargo clippy --manifest-path $(RUST_MANIFEST) -- -D warnings
cargo clippy --manifest-path $(RUST_MANIFEST) --locked --all-targets --all-features -- -D warnings

rust-check:
cargo check --manifest-path $(RUST_MANIFEST)
cargo check --manifest-path $(RUST_MANIFEST) --locked --all-targets --all-features

rust-doc:
cargo doc --manifest-path $(RUST_MANIFEST) --locked --no-deps --document-private-items

rust-tests:
cargo test --manifest-path $(RUST_MANIFEST)
cargo test --manifest-path $(RUST_MANIFEST) --locked

cargo-deny-check:
cargo deny --manifest-path $(RUST_MANIFEST) check -A license-not-encountered

rust-coverage:
$(CARGO_LLVM_COV) --manifest-path $(RUST_MANIFEST) --locked --all-features --workspace --fail-under-lines $(COVERAGE_LINE_THRESHOLD)

actionlint-check:
$(ACTIONLINT) .github/workflows/*.yml

shellcheck:
$(SHELLCHECK) $(SHELLCHECK_FILES)

build-release:
cargo build --release --manifest-path $(RUST_MANIFEST)
cargo build --release --manifest-path $(RUST_MANIFEST) --locked

real-engine-lifecycle-smoke: real-engine-lifecycle-smoke-docker real-engine-lifecycle-smoke-podman

Expand All @@ -67,9 +95,15 @@ native-only-startup-contract:
acceptance-fixtures-check:
node build/check-acceptance-fixtures.js

check-upstream-submodule:
node build/check-upstream-submodule.js

command-matrix-drift-check:
node build/generate-command-matrix.js --check

check-cli-reference:
node build/generate-cli-reference.js --check

schema-drift-check:
node build/check-spec-drift.js

Expand Down Expand Up @@ -110,5 +144,8 @@ check-compatibility-dashboard:
check-upstream-test-coverage:
node build/check-upstream-test-coverage.js

check-devcontainer-config:
node build/check-devcontainer-config.js

upstream-compatibility:
node build/check-upstream-compatibility.js
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,35 @@ If `upstream/` or `spec/` is missing or uninitialized, run the same command agai

## Local development

Run the complete local gate before pushing:

```bash
make tests
```

Rust validation:

```bash
cargo fmt --manifest-path cmd/devcontainer/Cargo.toml --all -- --check
cargo clippy --manifest-path cmd/devcontainer/Cargo.toml -- -D warnings
cargo test --manifest-path cmd/devcontainer/Cargo.toml
cargo clippy --manifest-path cmd/devcontainer/Cargo.toml --all-targets --all-features -- -D warnings
cargo check --manifest-path cmd/devcontainer/Cargo.toml --all-targets --all-features
cargo doc --manifest-path cmd/devcontainer/Cargo.toml --no-deps --document-private-items
cargo test --manifest-path cmd/devcontainer/Cargo.toml --locked
cargo deny --manifest-path cmd/devcontainer/Cargo.toml check -A license-not-encountered
```

CI also enforces the current Rust line coverage baseline:

```bash
cargo llvm-cov --manifest-path cmd/devcontainer/Cargo.toml --all-features --workspace --fail-under-lines 88
```

Compatibility/tooling validation:

```bash
npm test
make actionlint-check
make shellcheck
```

Manual acceptance suite shape:
Expand Down
14 changes: 14 additions & 0 deletions cmd/devcontainer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ serde_yaml = "0.9"
sha2 = "0.10"
tar = "0.4"

[lints.rust]
rust_2018_idioms = { level = "deny", priority = -1 }
unsafe_code = "forbid"
unreachable_pub = "warn"

[lints.rustdoc]
bare_urls = "deny"
broken_intra_doc_links = "deny"

[lints.clippy]
dbg_macro = "deny"
todo = "deny"
unimplemented = "deny"

[package.metadata.dist]
dist = true

Expand Down
16 changes: 9 additions & 7 deletions cmd/devcontainer/src/commands/collections/tests/feature_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,19 @@ use crate::commands::collections::feature_tests::{

const DEFAULT_FEATURE_TEST_BASE_IMAGE: &str = "docker.io/library/debian:bookworm-slim";

type ExecCall = (
String,
PathBuf,
Option<String>,
Vec<(String, String)>,
String,
);

#[derive(Default)]
struct FakeFeatureTestRuntime {
build_calls: Vec<(String, PathBuf, PathBuf)>,
start_calls: Vec<(String, PathBuf)>,
exec_calls: Vec<(
String,
PathBuf,
Option<String>,
Vec<(String, String)>,
String,
)>,
exec_calls: Vec<ExecCall>,
remove_calls: Vec<String>,
}

Expand Down
26 changes: 13 additions & 13 deletions cmd/devcontainer/src/commands/configuration/catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,18 @@ impl VersionSelector {
}
}

impl Ord for ParsedVersion {
fn cmp(&self, other: &Self) -> Ordering {
(self.major, self.minor, self.patch).cmp(&(other.major, other.minor, other.patch))
}
}

impl PartialOrd for ParsedVersion {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

#[cfg(test)]
mod tests {
use std::fs;
Expand Down Expand Up @@ -561,7 +573,7 @@ mod tests {
let metadata = json!({
"id": "published-feature",
"version": version,
"dependsOn": depends_on.map(|entries| entries.iter().copied().collect::<Vec<_>>()),
"dependsOn": depends_on.map(<[_]>::to_vec),
});
let manifest = json!({
"schemaVersion": 2,
Expand Down Expand Up @@ -731,15 +743,3 @@ mod tests {
let _ = fs::remove_dir_all(workspace);
}
}

impl Ord for ParsedVersion {
fn cmp(&self, other: &Self) -> Ordering {
(self.major, self.minor, self.patch).cmp(&(other.major, other.minor, other.patch))
}
}

impl PartialOrd for ParsedVersion {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ fn write_workspace_layout_version(
let metadata = json!({
"id": "published-feature",
"version": version,
"dependsOn": depends_on.map(|entries| entries.iter().copied().collect::<Vec<_>>()),
"dependsOn": depends_on.map(<[_]>::to_vec),
});
let manifest = json!({
"schemaVersion": 2,
Expand Down
35 changes: 15 additions & 20 deletions cmd/devcontainer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![forbid(unsafe_code)]

//! Crate entry points and shared module wiring for the native devcontainer CLI.

use std::env;
Expand All @@ -18,16 +20,15 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION");

pub fn native_only_mode_enabled() -> bool {
env::var(NATIVE_ONLY_ENV_VAR)
.map(|value| {
let normalized = value.trim().to_ascii_lowercase();
!normalized.is_empty()
&& normalized != "0"
&& normalized != "false"
&& normalized != "no"
})
.map(|value| native_only_mode_value_enabled(&value))
.unwrap_or(false)
}

fn native_only_mode_value_enabled(value: &str) -> bool {
let normalized = value.trim().to_ascii_lowercase();
!normalized.is_empty() && normalized != "0" && normalized != "false" && normalized != "no"
}

pub fn run_from_env() -> ExitCode {
run(env::args().skip(1).collect())
}
Expand Down Expand Up @@ -110,21 +111,15 @@ pub fn run(raw_args: Vec<String>) -> ExitCode {

#[cfg(test)]
mod tests {
use super::native_only_mode_enabled;
use super::native_only_mode_value_enabled;

#[test]
fn native_only_mode_uses_environment_switch() {
let original = std::env::var("DEVCONTAINER_NATIVE_ONLY").ok();
std::env::set_var("DEVCONTAINER_NATIVE_ONLY", "1");
assert!(native_only_mode_enabled());

std::env::set_var("DEVCONTAINER_NATIVE_ONLY", "false");
assert!(!native_only_mode_enabled());

if let Some(value) = original {
std::env::set_var("DEVCONTAINER_NATIVE_ONLY", value);
} else {
std::env::remove_var("DEVCONTAINER_NATIVE_ONLY");
}
assert!(native_only_mode_value_enabled("1"));
assert!(native_only_mode_value_enabled("yes"));
assert!(!native_only_mode_value_enabled(""));
assert!(!native_only_mode_value_enabled("0"));
assert!(!native_only_mode_value_enabled("false"));
assert!(!native_only_mode_value_enabled("no"));
}
}
Loading
Loading