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
17 changes: 17 additions & 0 deletions .aspect/axl.axl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ load("@aspect//lib/repro_commands.axl", "build_aspect_command", "dedup_and_attri
load("@aspect//lib/result_text.axl", "severity_for_status")
load("@aspect//lib/runnable.axl", "apparent_label", "runnable")
load("@aspect//lib/sarif.axl", "get_sarif_summary", "parse_sarif", "parse_sarif_diagnostics", "sarif_to_review_comments")
load("@aspect//lib/template_styles.axl", "resolve_style")
load("@aspect//lib/tips.axl", "tips")
load("@aspect//lint.axl", "file_uri_to_path", "filter_all", "filter_changeset", "linter_error_message")
load("@demo//answer.axl", "ANSWER")
Expand Down Expand Up @@ -3378,6 +3379,22 @@ def test_bazel_render_snippets(ctx: TaskContext, tc: int, temp_dir: str) -> int:
tc = test_case(tc, "…\nline 45" in out3["text"], "render: dropped-head truncation marker (leading …)")
tc = test_case(tc, "line 49" in out3["text"], "render: windowed snippet keeps the tail")

# Strata style: same fixture `r` (snippets + per-failure repros), rendered
# through the Strata templates. The DX layout leads with a verdict heading +
# KPI strip, shows each failure snippet open with its repro beneath it, and
# keeps the whole-run command out of the combined Reproduce section.
_strata = resolve_style("strata", {})
_st = {"summary": _strata["summary"], "details": _strata["details"]}
out_s = render_check_output(ctx, r, "failed", render_ctx, links, templates = _st, snippet_options = opts, max_snippets = max_snips)
tc = test_case(tc, "### ❌ Failed" in out_s["text"], "strata: verdict heading")
tc = test_case(tc, "### 💥 What broke" in out_s["text"], "strata: what-broke section")
tc = test_case(tc, "FAILED: expected 1 got 2" in out_s["text"], "strata: inlines failed-test snippet")
tc = test_case(tc, "no member named 'foo'" in out_s["text"], "strata: inlines failed-action snippet")
tc = test_case(tc, "aspect test -- //pkg:fail_test" in out_s["text"], "strata: per-failure aspect repro inline")
tc = test_case(tc, "aspect build -- //pkg:broken" in out_s["text"], "strata: per-failure action repro inline")
_s_repro = out_s["text"][out_s["text"].find("🔁 Reproduce"):] if "🔁 Reproduce" in out_s["text"] else ""
tc = test_case(tc, "//pkg:fail_test" not in _s_repro, "strata: combined Reproduce excludes per-failure commands")

ctx.std.fs.remove_dir_all(d)
return tc

Expand Down
6 changes: 6 additions & 0 deletions .aspect/config.axl
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ load("@aspect//lib/runnable_test.axl", "runnable_tests")
load("@aspect//lib/runner_job_history_test.axl", "runner_job_history_tests")
load("@aspect//lib/sandbox_recovery_test.axl", "sandbox_recovery_tests")
load("@aspect//lib/tar_test.axl", "tar_tests")
load("@aspect//lib/template_styles_test.axl", "template_styles_tests")
load("@aspect//lib/tips_test.axl", "tips_tests")
load("@aspect//lib/wrapper_test.axl", "wrapper_tests")
load("@aspect//tips.axl", "TASK_SCREENS", "TIP_SUGGESTION", "TIP_TEMPLATE_RAW", "add_tip")
Expand Down Expand Up @@ -272,6 +273,11 @@ def config(ctx: ConfigContext):
# Run with: aspect dev test-tar
ctx.tasks.add(tar_tests)

# Template-style machinery: resolve_style selection + override layering,
# compute_strata_kpi verdict/thresholds/slow-detection.
# Run with: aspect dev test-template-styles
ctx.tasks.add(template_styles_tests)

# CircleCI S3 error parsing: expired-credential 400 → legible reason.
# Run with: aspect dev test-circleci
ctx.tasks.add(circleci_tests)
Expand Down
1 change: 1 addition & 0 deletions crates/aspect-cli/src/builtins/aspect/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,5 +218,6 @@ Behavior notes:
| [lib/format_results.axl](lib/format_results.axl) | `init_data`, `render_check_output`, `format_summary_title` | format |
| [lib/gazelle_results.axl](lib/gazelle_results.axl) | `init_data`, `render_check_output`, `gazelle_summary_title` | gazelle |
| [lib/delivery_results.axl](lib/delivery_results.axl) | `init_data`, `add_result`, `render_check_output`, `delivery_summary_title` | delivery |
| [lib/template_styles.axl](lib/template_styles.axl) | `resolve_style`, `builtin_style`, `STYLE_NAMES`, `STYLE_ARG_DESCRIPTION` (Strata strings in [lib/strata_templates.axl](lib/strata_templates.axl)) | every status feature (the `style` arg) |

Each `*_results.axl` derives its `init_data()` from `bazel_results.init_data()` (so `process_event` can populate the full bazel state) and appends `SHARED_DETAILS_BODY_TEMPLATE` to its task-specific top section so the rendered details body has the same Targets / Build Metrics / Invocation / Workflows Runner / Workspace Status / Build Metadata / Options-parsed sections everywhere.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ load("../lib/environment.axl", "feature_logger", "workflows_results_url")
load("../lib/lifecycle.axl", "TaskLifecycleTrait", "TaskUpdate")
load("../lib/rate_limit.axl", "usage_footer")
load("../lib/result_text.axl", "severity_for_status")
load("../lib/template_styles.axl", "STYLE_ARG_DESCRIPTION", "STYLE_NAMES", "resolve_style")
load("../lib/tips.axl", "SURFACE_BUILDKITE_ANNOTATIONS", "collect_tips_sorted")

# ─── Style selection ──────────────────────────────────────────────────────────
Expand Down Expand Up @@ -205,6 +206,10 @@ def _buildkite_annotations(ctx: FeatureContext):
mode = ctx.args.mode
is_bk = bool(ctx.std.env.var("BUILDKITE"))

# Resolve the named style into the {summary, details} the renderer consumes.
_style = resolve_style(ctx.args.style, ctx.args.templates or {})
_templates = {"summary": _style["summary"], "details": _style["details"]}

_LOG.trace("starting (mode=%s BUILDKITE=%r is_bk=%s)" %
(mode, ctx.std.env.var("BUILDKITE") or "", is_bk))

Expand Down Expand Up @@ -322,7 +327,7 @@ def _buildkite_annotations(ctx: FeatureContext):
status,
make_render_ctx(_state, tips = collect_tips_sorted(ctx, surface = SURFACE_BUILDKITE_ANNOTATIONS), api_usage = usage_footer(ctx), last_updated_at = format_run_date(ctx, now_ms(ctx))),
_make_links(data),
None,
_templates,
None,
snippet_options = _snippet_opts,
max_snippets = _max_snippets,
Expand Down Expand Up @@ -357,5 +362,18 @@ BuildkiteAnnotations = feature(
"less-responsive live annotations. The task's terminal result and phase " +
"boundaries always land immediately regardless of this setting.",
),
"style": args.string(
default = "strata",
values = STYLE_NAMES,
description = STYLE_ARG_DESCRIPTION,
),
"templates": args.custom(
dict,
default = {},
description = "Map of template overrides for the rendered annotation body. " +
"Keys: \"summary\", \"details\". Each value is a Jinja2 template string. " +
"Empty dict (default) uses the selected `style`'s templates. Overrides " +
"layer on top of `style`.",
),
},
)
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ load(
"should_emit_phase_change",
"usage_footer",
)
load("../lib/template_styles.axl", "STYLE_ARG_DESCRIPTION", "STYLE_NAMES", "resolve_style")
load("../lib/tips.axl", "SURFACE_GITHUB_STATUS_CHECKS", "collect_tips_sorted")
load("../lint.axl", "LintTrait")

Expand Down Expand Up @@ -93,7 +94,12 @@ def _collect_ci_links(ctx, results_base_url):
def _github_status_checks(ctx: FeatureContext):
lifecycle = ctx.traits[TaskLifecycleTrait]

templates = ctx.args.templates or {}
# Resolve the named style (kilgore default) into its template bundle, then
# layer the per-key `templates` overrides on top (a custom variant). The
# resolved {summary, details} pair is what the renderer consumes; selecting
# the default style is byte-identical to the pre-style behavior.
_style = resolve_style(ctx.args.style, ctx.args.templates or {})
templates = {"summary": _style["summary"], "details": _style["details"]}
Comment on lines +101 to +102

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 Badge Keep Strata templates from hiding non-Bazel details

This resolved template bundle is passed later to whichever renderer_for_kind(update.kind) is selected; check_dispatch includes lint_results, format_results, gazelle_results, and delivery_results, but STRATA_DETAILS_TEMPLATE only renders the Bazel shared fields and never the non-Bazel renderers' actionable groups/file_list/delivery sections. With style = "strata", a failing or warning format/lint/gazelle/delivery check therefore loses the details users need to fix it, so either gate this template to Bazel renderers or add per-kind Strata details templates.

Useful? React with 👍 / 👎.

metadata_keys = list(ctx.args.metadata_keys or [])

# Check-run URL is published below via `checkrun.set_url`; siblings
Expand Down Expand Up @@ -490,12 +496,19 @@ GithubStatusChecks = feature(
default = True,
description = "Restrict lint check-run annotations to the displayed hunk regions of changed files (added lines + surrounding context from `detect_changed_files`). When false, off-region annotations are still posted but GitHub only renders them on the check-run detail page and commit page, not in Files Changed. Skipped silently when no changed-files context is available.",
),
"style": args.string(
default = "strata",
values = STYLE_NAMES,
description = STYLE_ARG_DESCRIPTION,
),
"templates": args.custom(
dict,
default = {},
description = "Map of template overrides for the rendered check-run output. " +
"Keys: \"summary\", \"details\". Each value is a Jinja2 template " +
"string. Empty dict (default) uses the feature's built-in templates.",
"string. Empty dict (default) uses the selected `style`'s templates. " +
"Overrides layer on top of `style`, so you can pick a style and replace " +
"just one slot.",
),
"metadata_keys": args.string_list(
default = [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ load(
load("../lib/repro_commands.axl", "ASPECT_CLI_INSTALL_HINT_MARKDOWN", "dedup_and_attribute", "rehydrate_commands")
load("../lib/result_text.axl", "severity_for_status")
load("../lib/tar.axl", "bsdtar")
load("../lib/template_styles.axl", "STYLE_ARG_DESCRIPTION", "STYLE_NAMES", "resolve_style")
load(
"../lib/tips.axl",
"SURFACE_GITHUB_PR_SUMMARY",
Expand Down Expand Up @@ -1264,9 +1265,13 @@ def _github_status_comments(ctx: FeatureContext):
min_poll_interval_seconds = ctx.args.min_poll_interval_seconds
artifact_expires_minutes = ctx.args.artifact_expires_minutes

# Resolve the named style (kilgore default), then layer the per-key
# `templates` overrides. The Kilgore bundle carries no `body` (the body
# template lives in this feature), so it falls back to _DEFAULT_BODY_TEMPLATE;
# Strata supplies its own body; a `templates["body"]` override wins over both.
# args.custom(dict, ...) hands None at runtime, not the declared default.
templates = ctx.args.templates or {}
body_template = templates.get("body") or _DEFAULT_BODY_TEMPLATE
_style = resolve_style(ctx.args.style, ctx.args.templates or {})
body_template = _style["body"] or _DEFAULT_BODY_TEMPLATE
status_badges = dict(_DEFAULT_STATUS_BADGES)
status_badges.update(ctx.args.status_badges or {})

Expand Down Expand Up @@ -1988,6 +1993,11 @@ GithubStatusComments = feature(
"Set to 0 to disable (no expiry).",
),
# Config-only (set via configure(..., feature_args = {...}) in config.axl).
"style": args.string(
default = "strata",
values = STYLE_NAMES,
description = STYLE_ARG_DESCRIPTION,
),
"templates": args.custom(
dict,
default = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ load(
load("../lib/bazel_results.axl", "compute_reproducer_command")
load("../lib/github.axl", "github")
load("../lib/repro_commands.axl", "rehydrate_commands")
load("../lib/template_styles.axl", "resolve_style")
load("../lib/tips.axl", "tips")

def _eq(label, got, want):
Expand Down Expand Up @@ -2332,8 +2333,29 @@ def _test_impl(ctx):
print(body)
print("")

# Strata style: render a subset through the Strata body template so any
# Jinja/data-shape regression in the aggregated comment surfaces here.
strata = resolve_style("strata", {})
for (label, raw_entries) in scenarios:
prepared = prepare_for_render(raw_entries)
sections = bucket_entries(prepared)
body = render_body(
ctx,
MARKER_FMT % 1234,
prepared,
"Fri May 10 07:16:05 UTC 2024",
strata["body"],
DEFAULT_STATUS_BADGES,
sections,
)
print(sep)
print("SCENARIO: strata · " + label)
print(sep)
print(body)
print("")

print(sep)
print("All %d PR-comment scenarios rendered without error." % len(scenarios))
print("All %d PR-comment scenarios rendered without error (Kilgore + Strata)." % len(scenarios))
return 0

def _aborted(data, reason):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ load(
"effective_throttle_factor",
"should_emit_phase_change",
)
load("../lib/template_styles.axl", "STYLE_ARG_DESCRIPTION", "STYLE_NAMES", "resolve_style")

_LOG = feature_logger("GitLab commit statuses")

Expand Down Expand Up @@ -152,7 +153,9 @@ def _state_for(status, final):
def _gitlab_commit_statuses(ctx: FeatureContext):
lifecycle = ctx.traits[TaskLifecycleTrait]

templates = ctx.args.templates or {}
# Resolve the named style (kilgore default) + per-key `templates` overrides.
_style = resolve_style(ctx.args.style, ctx.args.templates or {})
templates = {"summary": _style["summary"], "details": _style["details"]}
metadata_keys = list(ctx.args.metadata_keys or [])

mode = ctx.args.mode
Expand Down Expand Up @@ -428,12 +431,18 @@ GitlabCommitStatuses = feature(
"or the task runs longer. The task's terminal result always lands immediately " +
"so the final state shows up without delay.",
),
"style": args.string(
default = "strata",
values = STYLE_NAMES,
description = STYLE_ARG_DESCRIPTION,
),
"templates": args.custom(
dict,
default = {},
description = "Map of template overrides for the rendered status output. " +
"Keys: \"summary\", \"details\". Each value is a Jinja2 template " +
"string. Empty dict (default) uses the feature's built-in templates. " +
"string. Empty dict (default) uses the selected `style`'s templates; " +
"overrides layer on top of `style`. " +
"Note: only the rendered `title` lands on the commit status (in " +
"`description`, truncated to 255 chars) — the full body is rendered " +
"into the GitlabStatusComments rolling note.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ load(
"epoch_now_s",
"usage_footer",
)
load("../lib/template_styles.axl", "STYLE_ARG_DESCRIPTION", "STYLE_NAMES", "resolve_style")

_LOG = feature_logger("GitLab status comments")

Expand Down Expand Up @@ -153,8 +154,11 @@ def _gitlab_status_comments(ctx: FeatureContext):
ci_run_url = _ci_run_url(env)
min_poll_interval_seconds = max(1, ctx.args.min_poll_interval_seconds)

templates = ctx.args.templates or {}
body_template = templates.get("body") or DEFAULT_BODY_TEMPLATE
# Resolve the named style, then layer per-key `templates` overrides. Kilgore
# carries no body (falls back to the shared GitHub body template); Strata
# supplies its own; a `templates["body"]` override wins over both.
_style = resolve_style(ctx.args.style, ctx.args.templates or {})
body_template = _style["body"] or DEFAULT_BODY_TEMPLATE
status_badges = dict(DEFAULT_STATUS_BADGES)
status_badges.update(ctx.args.status_badges or {})

Expand Down Expand Up @@ -442,13 +446,18 @@ GitlabStatusComments = feature(
"the cost stays flat regardless of how many run in parallel. Terminal task " +
"verdicts always land immediately.",
),
"style": args.string(
default = "strata",
values = STYLE_NAMES,
description = STYLE_ARG_DESCRIPTION,
),
"templates": args.custom(
dict,
default = {},
description = "Per-section Jinja2 template overrides. Keys: 'body'. " +
"Vars available match the GithubStatusComments template contract " +
"(see feature/github_status_comments.axl). Empty dict → built-in default. " +
"config.axl only.",
"(see feature/github_status_comments.axl). Empty dict → selected `style`'s " +
"body. Overrides layer on top of `style`. config.axl only.",
),
"status_badges": args.custom(
dict,
Expand Down
Loading
Loading