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
68 changes: 48 additions & 20 deletions src/command_install.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,24 @@
#include "configuration.h"
#include "error.h"
#include "logger.h"
#include "print.h"
#include "resolver.h"

namespace {

auto padded_label(const std::string_view label) -> std::string {
using sourcemeta::core::TerminalStyle;

constexpr auto PROGRESS_STYLE{TerminalStyle::Bold | TerminalStyle::Cyan};
constexpr auto SUCCESS_STYLE{TerminalStyle::Bold | TerminalStyle::Green};
constexpr auto WARNING_STYLE{TerminalStyle::Bold | TerminalStyle::Yellow};
constexpr auto FAILURE_STYLE{TerminalStyle::Bold | TerminalStyle::Red};

auto padded_label(const std::string_view label,
const sourcemeta::core::TerminalStyle style =
sourcemeta::core::TerminalStyle::None) -> std::string {
assert(label.size() <= 14);
std::string result{label};
auto result{sourcemeta::jsonschema::paint(
label, style, sourcemeta::core::TerminalStream::Stderr)};
result.append(14 - label.size(), ' ');
result += " : ";
return result;
Expand Down Expand Up @@ -160,30 +171,41 @@ auto make_on_event(const sourcemeta::core::Options &options,
if (is_json) {
emit_json(events_array, "fetching", "uri", event.uri);
} else {
std::cerr << padded_label("Fetching") << event.uri << "\n";
std::cerr << padded_label("Fetching", PROGRESS_STYLE) << event.uri
<< "\n";
}

break;
case Type::FetchEnd:
break;
case Type::BundleStart:
case Type::BundleStart: {
const auto effective_style{is_json ? TerminalStyle::None
: PROGRESS_STYLE};
sourcemeta::jsonschema::LOG_VERBOSE(options)
<< padded_label("Bundling") << event.uri << "\n";
<< padded_label("Bundling", effective_style) << event.uri
<< "\n";
break;
}
case Type::BundleEnd:
break;
case Type::WriteStart:
case Type::WriteStart: {
const auto effective_style{is_json ? TerminalStyle::None
: PROGRESS_STYLE};
sourcemeta::jsonschema::LOG_VERBOSE(options)
<< padded_label("Writing") << event.path.generic_string()
<< "\n";
<< padded_label("Writing", effective_style)
<< event.path.generic_string() << "\n";
break;
}
case Type::WriteEnd:
break;
case Type::VerifyStart:
case Type::VerifyStart: {
const auto effective_style{is_json ? TerminalStyle::None
: PROGRESS_STYLE};
sourcemeta::jsonschema::LOG_VERBOSE(options)
<< padded_label("Verifying") << event.path.generic_string()
<< "\n";
<< padded_label("Verifying", effective_style)
<< event.path.generic_string() << "\n";
break;
}
case Type::VerifyEnd:
if (is_json) {
auto json_event{sourcemeta::core::JSON::make_object()};
Expand All @@ -193,7 +215,7 @@ auto make_on_event(const sourcemeta::core::Options &options,
"path", sourcemeta::core::JSON{event.path.generic_string()});
events_array.push_back(std::move(json_event));
} else {
std::cerr << padded_label("Installed")
std::cerr << padded_label("Installed", SUCCESS_STYLE)
<< event.path.generic_string() << "\n";
}

Expand All @@ -202,7 +224,8 @@ auto make_on_event(const sourcemeta::core::Options &options,
if (is_json) {
emit_json(events_array, "up-to-date", "uri", event.uri);
} else {
std::cerr << padded_label("Up to date") << event.uri << "\n";
std::cerr << padded_label("Up to date", SUCCESS_STYLE)
<< event.uri << "\n";
}

break;
Expand All @@ -211,7 +234,7 @@ auto make_on_event(const sourcemeta::core::Options &options,
emit_json(events_array, "file-missing", "path",
event.path.generic_string());
} else {
std::cerr << padded_label("File missing")
std::cerr << padded_label("File missing", WARNING_STYLE)
<< event.path.generic_string() << "\n";
}

Expand All @@ -221,7 +244,7 @@ auto make_on_event(const sourcemeta::core::Options &options,
emit_json(events_array, "mismatched", "path",
event.path.generic_string());
} else {
std::cerr << padded_label("Mismatched")
std::cerr << padded_label("Mismatched", WARNING_STYLE)
<< event.path.generic_string() << "\n";
Comment on lines +247 to 248

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Frozen mismatches are failures, but this label remains yellow. Select FAILURE_STYLE when orphaned_behavior == OrphanedBehavior::Error and keep WARNING_STYLE for automatically repaired mismatches.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/command_install.cc, line 247:

<comment>Frozen mismatches are failures, but this label remains yellow. Select `FAILURE_STYLE` when `orphaned_behavior == OrphanedBehavior::Error` and keep `WARNING_STYLE` for automatically repaired mismatches.</comment>

<file context>
@@ -221,7 +244,7 @@ auto make_on_event(const sourcemeta::core::Options &options,
                         event.path.generic_string());
             } else {
-              std::cerr << padded_label("Mismatched")
+              std::cerr << padded_label("Mismatched", WARNING_STYLE)
                         << event.path.generic_string() << "\n";
             }
</file context>
Suggested change
std::cerr << padded_label("Mismatched", WARNING_STYLE)
<< event.path.generic_string() << "\n";
std::cerr << padded_label(
"Mismatched", orphaned_behavior == OrphanedBehavior::Error
? FAILURE_STYLE
: WARNING_STYLE)
<< event.path.generic_string() << "\n";

}

Expand All @@ -230,7 +253,8 @@ auto make_on_event(const sourcemeta::core::Options &options,
if (is_json) {
emit_json(events_array, "path-mismatch", "uri", event.uri);
} else {
std::cerr << padded_label("Path mismatch") << event.uri << "\n";
std::cerr << padded_label("Path mismatch", WARNING_STYLE)
<< event.uri << "\n";
}

break;
Expand All @@ -239,15 +263,19 @@ auto make_on_event(const sourcemeta::core::Options &options,
if (is_json) {
emit_json(events_array, "untracked", "uri", event.uri);
} else {
std::cerr << padded_label("Untracked") << event.uri << "\n";
std::cerr << padded_label("Untracked", FAILURE_STYLE) << event.uri
<< "\n";
}

break;
case Type::Orphaned:
if (is_json) {
emit_json(events_array, "orphaned", "uri", event.uri);
} else {
std::cerr << padded_label("Orphaned") << event.uri << "\n";
const auto style{orphaned_behavior == OrphanedBehavior::Delete
? WARNING_STYLE
: FAILURE_STYLE};
std::cerr << padded_label("Orphaned", style) << event.uri << "\n";
}

if (orphaned_behavior == OrphanedBehavior::Delete) {
Expand Down Expand Up @@ -381,8 +409,8 @@ auto sourcemeta::jsonschema::install(const sourcemeta::core::Options &options)
json_event.assign("path", sourcemeta::core::JSON{relative_target});
events_array.push_back(std::move(json_event));
} else {
std::cerr << padded_label("Adding") << dependency_uri << " -> "
<< relative_target << "\n";
std::cerr << padded_label("Adding", PROGRESS_STYLE) << dependency_uri
<< " -> " << relative_target << "\n";
}
}

Expand Down
9 changes: 9 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,15 @@ add_jsonschema_test(install/pass_frozen_idempotent)
add_jsonschema_test(install/pass_frozen_subdir)
add_jsonschema_test(install/pass_frozen_mixed_status)
add_jsonschema_test(install/pass_frozen_resolve_mapping)
add_jsonschema_test(install/pass_color_always_verbose)
add_jsonschema_test(install/pass_up_to_date_color_always)
add_jsonschema_test(install/pass_add_dependency_color_always)
add_jsonschema_test(install/pass_remove_orphaned_color_always)
add_jsonschema_test(install/fail_frozen_mismatch_color_always)
add_jsonschema_test(install/fail_frozen_untracked_color_always)
add_jsonschema_test(install/fail_frozen_orphaned_color_always)
add_jsonschema_test(install/pass_json_color_always)
add_jsonschema_test(install/pass_color_never_verbose)

# Upgrade
add_jsonschema_test(upgrade/pass_default_target)
Expand Down
56 changes: 56 additions & 0 deletions test/install/fail_frozen_mismatch_color_always.clitest
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
MAKE DIRECTORY source

MAKE DIRECTORY project

WRITE source/schema.json UNTIL EOF
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
}
EOF

WRITE project/jsonschema.json UNTIL EOF
{
"dependencies": {
"[CWD_URI]/source/schema.json": "./vendor/schema.json"
}
}
EOF

REPLACE '[CWD_URI]' WITH $CWD_URI IN project/jsonschema.json

RUN install STDIN /dev/null IN project INTO result_0.txt EXPECTING 0

REPLACE $CWD_URI WITH '[CWD_URI]' IN result_0.txt
REPLACE $CWD WITH '[CWD]' IN result_0.txt

WRITE expected_0.txt UNTIL EOF
2> Fetching : [CWD_URI]/source/schema.json
2> Installed : [CWD]/project/vendor/schema.json
EOF

COMPARE result_0.txt AGAINST expected_0.txt

WRITE project/vendor/schema.json UNTIL EOF
TAMPERED CONTENT
EOF

COPY project/jsonschema.lock.json TO lock_before.json

// Validation failure with forced color
RUN install --frozen --color always STDIN /dev/null IN project INTO result_1.txt EXPECTING 2

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: These new frozen failure tests cover only human-readable output, leaving JSON failure events unverified. Add a matching --json --color always run and compare its structured events for all three fail_frozen_*_color_always cases.

(Based on your team's feedback about JSON variants for failure tests.) [8987a25a-4f41-4a67-914e-ada0d55918a7]

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At test/install/fail_frozen_mismatch_color_always.clitest, line 41:

<comment>These new frozen failure tests cover only human-readable output, leaving JSON failure events unverified. Add a matching `--json --color always` run and compare its structured events for all three `fail_frozen_*_color_always` cases.

(Based on your team's feedback about JSON variants for failure tests.) [8987a25a-4f41-4a67-914e-ada0d55918a7]</comment>

<file context>
@@ -0,0 +1,56 @@
+COPY project/jsonschema.lock.json TO lock_before.json
+
+// Validation failure with forced color
+RUN install --frozen --color always STDIN /dev/null IN project INTO result_1.txt EXPECTING 2
+
+REPLACE $CWD_URI WITH '[CWD_URI]' IN result_1.txt
</file context>


REPLACE $CWD_URI WITH '[CWD_URI]' IN result_1.txt
REPLACE $CWD WITH '[CWD]' IN result_1.txt
REPLACE MATCHING '\x1B\[1;33m' WITH '[ANSI_BOLD_YELLOW]' IN result_1.txt
REPLACE MATCHING '\x1B\[0m' WITH '[ANSI_RESET]' IN result_1.txt

WRITE expected_1.txt UNTIL EOF
2> [ANSI_BOLD_YELLOW]Mismatched[ANSI_RESET] : [CWD]/project/vendor/schema.json
2> error: File hash does not match lock file in frozen mode
2> at uri [CWD_URI]/source/schema.json
EOF

COMPARE result_1.txt AGAINST expected_1.txt

COMPARE project/jsonschema.lock.json AGAINST lock_before.json
71 changes: 71 additions & 0 deletions test/install/fail_frozen_orphaned_color_always.clitest
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
MAKE DIRECTORY source

MAKE DIRECTORY project

WRITE source/schema.json UNTIL EOF
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
}
EOF

WRITE source/other.json UNTIL EOF
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "number"
}
EOF

WRITE project/jsonschema.json UNTIL EOF
{
"dependencies": {
"[CWD_URI]/source/schema.json": "./vendor/schema.json",
"[CWD_URI]/source/other.json": "./vendor/other.json"
}
}
EOF

REPLACE '[CWD_URI]' WITH $CWD_URI IN project/jsonschema.json

RUN install STDIN /dev/null IN project INTO result_0.txt EXPECTING 0

REPLACE $CWD_URI WITH '[CWD_URI]' IN result_0.txt
REPLACE $CWD WITH '[CWD]' IN result_0.txt

WRITE expected_0.txt UNTIL EOF
2> Fetching : [CWD_URI]/source/other.json
2> Installed : [CWD]/project/vendor/other.json
2> Fetching : [CWD_URI]/source/schema.json
2> Installed : [CWD]/project/vendor/schema.json
EOF

COMPARE result_0.txt AGAINST expected_0.txt

WRITE project/jsonschema.json UNTIL EOF
{
"dependencies": {
"[CWD_URI]/source/schema.json": "./vendor/schema.json"
}
}
EOF

REPLACE '[CWD_URI]' WITH $CWD_URI IN project/jsonschema.json

COPY project/jsonschema.lock.json TO lock_before.json

// Validation failure with forced color
RUN install --frozen --color always STDIN /dev/null IN project INTO result_1.txt EXPECTING 2

REPLACE $CWD_URI WITH '[CWD_URI]' IN result_1.txt
REPLACE MATCHING '\x1B\[1;32m' WITH '[ANSI_BOLD_GREEN]' IN result_1.txt
REPLACE MATCHING '\x1B\[1;31m' WITH '[ANSI_BOLD_RED]' IN result_1.txt
REPLACE MATCHING '\x1B\[0m' WITH '[ANSI_RESET]' IN result_1.txt

WRITE expected_1.txt UNTIL EOF
2> [ANSI_BOLD_GREEN]Up to date[ANSI_RESET] : [CWD_URI]/source/schema.json
2> [ANSI_BOLD_RED]Orphaned[ANSI_RESET] : [CWD_URI]/source/other.json
EOF

COMPARE result_1.txt AGAINST expected_1.txt

COMPARE project/jsonschema.lock.json AGAINST lock_before.json
62 changes: 62 additions & 0 deletions test/install/fail_frozen_untracked_color_always.clitest
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
MAKE DIRECTORY source

MAKE DIRECTORY project

WRITE source/schema.json UNTIL EOF
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
}
EOF

WRITE project/jsonschema.json UNTIL EOF
{
"dependencies": {
"[CWD_URI]/source/schema.json": "./vendor/schema.json"
}
}
EOF

REPLACE '[CWD_URI]' WITH $CWD_URI IN project/jsonschema.json

RUN install STDIN /dev/null IN project INTO result_0.txt EXPECTING 0

REPLACE $CWD_URI WITH '[CWD_URI]' IN result_0.txt
REPLACE $CWD WITH '[CWD]' IN result_0.txt

WRITE expected_0.txt UNTIL EOF
2> Fetching : [CWD_URI]/source/schema.json
2> Installed : [CWD]/project/vendor/schema.json
EOF

COMPARE result_0.txt AGAINST expected_0.txt

WRITE project/jsonschema.json UNTIL EOF
{
"dependencies": {
"[CWD_URI]/source/schema.json": "./vendor/schema.json",
"file:///zzz/fake/new.json": "./vendor/new.json"
}
}
EOF

REPLACE '[CWD_URI]' WITH $CWD_URI IN project/jsonschema.json

COPY project/jsonschema.lock.json TO lock_before.json

// Validation failure with forced color
RUN install --frozen --color always STDIN /dev/null IN project INTO result_1.txt EXPECTING 2

REPLACE $CWD_URI WITH '[CWD_URI]' IN result_1.txt
REPLACE MATCHING '\x1B\[1;32m' WITH '[ANSI_BOLD_GREEN]' IN result_1.txt
REPLACE MATCHING '\x1B\[1;31m' WITH '[ANSI_BOLD_RED]' IN result_1.txt
REPLACE MATCHING '\x1B\[0m' WITH '[ANSI_RESET]' IN result_1.txt

WRITE expected_1.txt UNTIL EOF
2> [ANSI_BOLD_GREEN]Up to date[ANSI_RESET] : [CWD_URI]/source/schema.json
2> [ANSI_BOLD_RED]Untracked[ANSI_RESET] : file:///zzz/fake/new.json
EOF

COMPARE result_1.txt AGAINST expected_1.txt

COMPARE project/jsonschema.lock.json AGAINST lock_before.json
Loading
Loading