Skip to content

Make validate all validate all available options#23228

Closed
steveny91 wants to merge 4 commits intomasterfrom
sy/ddev-validate-all
Closed

Make validate all validate all available options#23228
steveny91 wants to merge 4 commits intomasterfrom
sy/ddev-validate-all

Conversation

@steveny91
Copy link
Copy Markdown
Contributor

@steveny91 steveny91 commented Apr 8, 2026

What does this PR do?

The current validate all doesn't run all validation that we need in CI. This change ports over validate all to ddev and add additional validations to mimic what CI does. Also introduces --sync to it.

e.g.:

ddev validate all istio -s
ddev validate all istio --sync

@datadog-datadog-prod-us1-2
Copy link
Copy Markdown

datadog-datadog-prod-us1-2 bot commented Apr 8, 2026

⚠️ Tests

Fix all issues with BitsAI or with Cursor

⚠️ Other Violations

🧪 2 Tests failed

test_e2e_metrics from test_e2e.py   View in Datadog   (Fix with Cursor)
Metadata assertion errors using metadata.csv:
	- Expect \`prefect.server.task_runs.completed.count\` from metadata.csv but not submitted.
	- Expect \`prefect.server.task_runs.crashed.count\` from metadata.csv but not submitted.
	- Expect \`prefect.server.task_runs.failed.count\` from metadata.csv but not submitted.
test_e2e_profile_cisco_asa from test_e2e_core_vs_python.py   View in Datadog   (Fix with Cursor)
[s6-init] making user provided files available at /var/run/s6/etc...exited 0.
[s6-init] ensuring user provided files have correct perms...exited 0.
[fix-attrs.d] applying ownership & permissions fixes...
[fix-attrs.d] done.
[cont-init.d] executing container initialization scripts...
[cont-init.d] 01-check-apikey.sh: executing... 
[cont-init.d] 01-check-apikey.sh: exited 0.
[cont-init.d] 50-ci.sh: executing... 
[cont-init.d] 50-ci.sh: exited 0.
[cont-init.d] 50-ecs-managed.sh: executing... 
...

ℹ️ Info

No other issues found (see more)

❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: f2190c9 | Docs | Datadog PR Page | Was this helpful? React with 👍/👎 or give us feedback!

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 8, 2026

Codecov Report

❌ Patch coverage is 62.79070% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.33%. Comparing base (3223e87) to head (f2190c9).
⚠️ Report is 67 commits behind head on master.

Additional details and impacted files
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@steveny91 steveny91 marked this pull request as ready for review April 8, 2026 19:47
@steveny91 steveny91 requested a review from a team as a code owner April 8, 2026 19:47
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f2190c91b2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

(licenses, ('core',), 'repo', 'sync'),
(metadata, None, 'integrations', 'sync'),
(models, None, 'check', 'sync'),
(openmetrics, None, 'integrations', None),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restrict openmetrics validator to supported repos

VALIDATORS currently runs openmetrics for every repo (repos=None), but ddev/src/ddev/cli/validate/openmetrics.py aborts when the repo is not core or extras. In practice, ddev validate all on marketplace (and other unsupported repo contexts) will always record openmetrics as a failed validation even when nothing is wrong, which breaks the goal of mirroring CI behavior where this validator is only enabled when requested for compatible repos.

Useful? React with 👍 / 👎.

VALIDATORS: tuple[tuple[click.BaseCommand, tuple[str, ...] | None, str, str | None], ...] = (
(agent_reqs, ('core',), 'check', None),
(ci, None, 'repo', 'sync'),
(codeowners, ('extras',), 'repo', None),
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 Run codeowners validation for core repos

The registry limits codeowners to extras, so ddev validate all skips this check in core. However, .github/workflows/validate.yml enables codeowners: true for repo: core, and the codeowners command contains core-specific validation (logs ownership), so this omission means local validate all can miss failures that CI will catch.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

@steveny91 steveny91 left a comment

Choose a reason for hiding this comment

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

test

(licenses, ('core',), 'repo', 'sync'),
(metadata, None, 'integrations', 'sync'),
(models, None, 'check', 'sync'),
(openmetrics, None, 'integrations', None),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

test

Copy link
Copy Markdown
Contributor Author

@steveny91 steveny91 left a comment

Choose a reason for hiding this comment

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

test

(licenses, ('core',), 'repo', 'sync'),
(metadata, None, 'integrations', 'sync'),
(models, None, 'check', 'sync'),
(openmetrics, None, 'integrations', None),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Request: openmetrics is registered with applicable_repos=None (runs on all repos), but the validator internally calls app.abort() with exit code 1 when the repo is not core or extras. On marketplace or internal repos, ddev validate all will catch this as click.exceptions.Exit(1) and record openmetrics as a failed validation — a false positive.

Fix:

(openmetrics, ('core', 'extras'), 'integrations', None),

Copy link
Copy Markdown
Contributor Author

@steveny91 steveny91 left a comment

Choose a reason for hiding this comment

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

test

VALIDATORS: tuple[tuple[click.BaseCommand, tuple[str, ...] | None, str, str | None], ...] = (
(agent_reqs, ('core',), 'check', None),
(ci, None, 'repo', 'sync'),
(codeowners, ('extras',), 'repo', None),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Suggestion: codeowners is restricted to ('extras',), but CI runs codeowners: true for the core repo too. The validator performs a log-asset ownership check on core (line 118 of codeowners.py). This is a pre-existing gap inherited from the old ALL_VALIDATIONS, but since the PR's goal is to "mimic what CI does," consider widening to ('core', 'extras').

Copy link
Copy Markdown
Contributor Author

@steveny91 steveny91 left a comment

Choose a reason for hiding this comment

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

test

if sync and fix_param is not None:
kwargs[fix_param] = True

try:
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Suggestion: Only SystemExit and click.exceptions.Exit are caught. An unexpected exception (e.g., KeyError, FileNotFoundError) from any validator will halt the entire run, preventing remaining validators from executing and suppressing the failure summary. For an orchestrator whose job is running all validators, a broad except Exception with error logging would be more resilient:

except Exception as e:
    failed.append(func.name)
    app.display_error(f'{func.name} raised unexpected error: {e}')

Praise: Catching both SystemExit (from legacy validators using sys.exit()) and click.exceptions.Exit (from ddev validators using app.abort()) with distinct truthiness checks is the right approach. Collecting failures rather than short-circuiting gives users a complete picture in one run.

Copy link
Copy Markdown
Contributor Author

@steveny91 steveny91 left a comment

Choose a reason for hiding this comment

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

test

# 'integrations' — ddev validators that take an `integrations` tuple argument
# 'repo' — repo-level validators that take no check/integration argument
# fix_param: the keyword argument name for auto-fixing ('sync', 'fix'), or None if not supported
VALIDATORS: tuple[tuple[click.BaseCommand, tuple[str, ...] | None, str, str | None], ...] = (
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Suggestion: The VALIDATORS tuple uses positional fields with arg_mode typed as bare str. A typo like 'integration' instead of 'integrations' silently falls through both branches and invokes the validator with no arguments. A NamedTuple with Literal['check', 'integrations', 'repo'] for arg_mode would make entries self-documenting and catch misconfigurations at type-check time:

from typing import Literal, NamedTuple

class ValidatorSpec(NamedTuple):
    func: click.BaseCommand
    repos: tuple[str, ...] | None
    arg_mode: Literal['check', 'integrations', 'repo']
    fix_param: str | None

Copy link
Copy Markdown
Contributor Author

@steveny91 steveny91 left a comment

Choose a reason for hiding this comment

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

test

If `check` is specified, only the check will be validated, if check value is 'changed' will only apply to changed
checks, an 'all' or empty `check` value will validate all README files.
"""
warnings.warn(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Suggestion: DeprecationWarning is silently suppressed by default in non-test, non--Wd Python environments. CLI users invoking the legacy path will see no deprecation message. Since this is a CLI command (not a library API), a visible click.echo warning or FutureWarning (which is shown by default) would actually reach the user.

Copy link
Copy Markdown
Contributor Author

@steveny91 steveny91 left a comment

Choose a reason for hiding this comment

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

test

return {func.name for func, _, _, _ in VALIDATORS}


def test_validators_match_ci_workflow():
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Suggestion: The single test validates name-set parity between the VALIDATORS registry and the CI workflow — a valuable contract check. But the core behaviors introduced by this PR are untested: repo filtering, --sync flag propagation, and failure collection. The repo already has ddev fixture infrastructure and fake_repo/fake_marketplace_repo helpers that would enable integration-level tests. At minimum, a test verifying that repo-restricted validators are skipped on non-matching repos would catch regressions.

Copy link
Copy Markdown
Contributor Author

@steveny91 steveny91 left a comment

Choose a reason for hiding this comment

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

test

)
@click.pass_context
def all(ctx: click.Context, check: str | None, sync: bool) -> None:
"""Run all CI validations for a repo.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nit: The docstring says "If check is specified, only that check will be validated" — but this isn't true for 'repo'-mode validators (ci, dep, labeler, licenses, codeowners), which always run repo-wide regardless of the check argument. Also, "An empty or 'all' value" is slightly misleading since check defaults to None, not an empty string.

Copy link
Copy Markdown
Contributor Author

@steveny91 steveny91 left a comment

Choose a reason for hiding this comment

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

test


from ddev.cli.validate.all import VALIDATORS

REPO_ROOT = Path(__file__).resolve().parents[4]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nit: REPO_ROOT = Path(__file__).resolve().parents[4] hardcodes the assumption the test lives exactly 4 levels below the repo root. If the file is ever relocated, this silently resolves to the wrong directory. An assertion guard like assert REPO_ROOT.joinpath('.github').is_dir() would make failures self-explanatory.

Copy link
Copy Markdown
Contributor Author

@steveny91 steveny91 left a comment

Choose a reason for hiding this comment

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

Hi @steveny91, this is a first quick review powered by Claude Code. This uses a team of agents reviewing different factors of the code changes such as code quality, functionality, correctness and other aspects I find important. Rules are tailored following my personal recommendations and the review has been first approved by me.

Please take a look at the comments and decide whether they should be implemented or not. When deciding not to implement a comment make sure to say why, I will be reviewing both the code and your comments personally. This is a first iteration trying to catch the most important things.

My Feedback Legend

Here is a quick guide to the prefixes I use in my comments:

praise: no action needed, just celebrate!
note: just a comment/information, no need to take any action.
question: I need clarification or I am seeking to understand your approach.
nit: A minor, non-blocking issue (e.g., style, typo). Feel free to ignore.
suggestion: I am proposing an improvement. This is optional but recommended.
request: A change I believe is necessary before this can be merged.

The only blocking comments are request, any other type of comment can be applied at discretion of the developer.

(licenses, ('core',), 'repo', 'sync'),
(metadata, None, 'integrations', 'sync'),
(models, None, 'check', 'sync'),
(openmetrics, None, 'integrations', None),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Request: openmetrics is registered with applicable_repos=None (runs on all repos), but the validator internally calls app.abort() with exit code 1 when the repo is not core or extras. On marketplace or internal repos, ddev validate all will catch this as click.exceptions.Exit(1) and record openmetrics as a failed validation — a false positive.

Fix:

(openmetrics, ('core', 'extras'), 'integrations', None),

VALIDATORS: tuple[tuple[click.BaseCommand, tuple[str, ...] | None, str, str | None], ...] = (
(agent_reqs, ('core',), 'check', None),
(ci, None, 'repo', 'sync'),
(codeowners, ('extras',), 'repo', None),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Suggestion: codeowners is restricted to ('extras',), but CI runs codeowners: true for the core repo too. The validator performs a log-asset ownership check on core (line 118 of codeowners.py). This is a pre-existing gap inherited from the old ALL_VALIDATIONS, but since the PR's goal is to "mimic what CI does," consider widening to ('core', 'extras').

if sync and fix_param is not None:
kwargs[fix_param] = True

try:
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Suggestion: Only SystemExit and click.exceptions.Exit are caught. An unexpected exception (e.g., KeyError, FileNotFoundError) from any validator will halt the entire run, preventing remaining validators from executing and suppressing the failure summary. For an orchestrator whose job is running all validators, a broad except Exception with error logging would be more resilient:

except Exception as e:
    failed.append(func.name)
    app.display_error(f'{func.name} raised unexpected error: {e}')

Praise: Catching both SystemExit (from legacy validators using sys.exit()) and click.exceptions.Exit (from ddev validators using app.abort()) with distinct truthiness checks is the right approach. Collecting failures rather than short-circuiting gives users a complete picture in one run.

# 'integrations' — ddev validators that take an `integrations` tuple argument
# 'repo' — repo-level validators that take no check/integration argument
# fix_param: the keyword argument name for auto-fixing ('sync', 'fix'), or None if not supported
VALIDATORS: tuple[tuple[click.BaseCommand, tuple[str, ...] | None, str, str | None], ...] = (
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Suggestion: The VALIDATORS tuple uses positional fields with arg_mode typed as bare str. A typo like 'integration' instead of 'integrations' silently falls through both branches and invokes the validator with no arguments. A NamedTuple with Literal['check', 'integrations', 'repo'] for arg_mode would make entries self-documenting and catch misconfigurations at type-check time:

from typing import Literal, NamedTuple

class ValidatorSpec(NamedTuple):
    func: click.BaseCommand
    repos: tuple[str, ...] | None
    arg_mode: Literal['check', 'integrations', 'repo']
    fix_param: str | None

If `check` is specified, only the check will be validated, if check value is 'changed' will only apply to changed
checks, an 'all' or empty `check` value will validate all README files.
"""
warnings.warn(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Suggestion: DeprecationWarning is silently suppressed by default in non-test, non--Wd Python environments. CLI users invoking the legacy path will see no deprecation message. Since this is a CLI command (not a library API), a visible click.echo warning or FutureWarning (which is shown by default) would actually reach the user.

return {func.name for func, _, _, _ in VALIDATORS}


def test_validators_match_ci_workflow():
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Suggestion: The single test validates name-set parity between the VALIDATORS registry and the CI workflow — a valuable contract check. But the core behaviors introduced by this PR are untested: repo filtering, --sync flag propagation, and failure collection. The repo already has ddev fixture infrastructure and fake_repo/fake_marketplace_repo helpers that would enable integration-level tests. At minimum, a test verifying that repo-restricted validators are skipped on non-matching repos would catch regressions.

)
@click.pass_context
def all(ctx: click.Context, check: str | None, sync: bool) -> None:
"""Run all CI validations for a repo.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nit: The docstring says "If check is specified, only that check will be validated" — but this isn't true for 'repo'-mode validators (ci, dep, labeler, licenses, codeowners), which always run repo-wide regardless of the check argument. Also, "An empty or 'all' value" is slightly misleading since check defaults to None, not an empty string.


from ddev.cli.validate.all import VALIDATORS

REPO_ROOT = Path(__file__).resolve().parents[4]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nit: REPO_ROOT = Path(__file__).resolve().parents[4] hardcodes the assumption the test lives exactly 4 levels below the repo root. If the file is ever relocated, this silently resolves to the wrong directory. An assertion guard like assert REPO_ROOT.joinpath('.github').is_dir() would make failures self-explanatory.

@steveny91 steveny91 closed this Apr 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant