Skip to content

feat(xtest): Allow selecting sdk versions for xtest#446

Open
dmihalcik-virtru wants to merge 13 commits intomainfrom
DSPX-1115-sdkversions
Open

feat(xtest): Allow selecting sdk versions for xtest#446
dmihalcik-virtru wants to merge 13 commits intomainfrom
DSPX-1115-sdkversions

Conversation

@dmihalcik-virtru
Copy link
Copy Markdown
Member

@dmihalcik-virtru dmihalcik-virtru commented May 4, 2026

Summary

  • Adds version-qualified SDK specifiers (go@main, go@v0.18.0, go@*) to --sdks, --sdks-encrypt, and --sdks-decrypt pytest options
  • Replaces the static sdk: [go, java, js] CI matrix with a dynamic per-sdk@version matrix, so each released or head version of each SDK gets its own test job
  • Skips round-trip test pairs where all SDKs involved are released artifacts when running non-dispatch CI, to avoid redundant old-vs-old coverage

Motivation

Version-qualified SDK selection

Previously the matrix ran one job per SDK type, always using whatever versions happened to be installed. This PR resolves each SDK to explicit versions (via otdf-sdk-mgr versions resolve) and parametrizes the matrix over sdk@version entries, making it possible to test multiple released versions of the same SDK in a single run and to pin exact versions in CI artifacts.

Skip released-only pairs

With multiple SDK versions in the matrix, the cross-product of (encrypt_sdk, decrypt_sdk) pairs grows quickly. Most of those new pairs are combinations of two already-shipped artifacts — they test code that hasn't changed and add no signal on a PR.

This is analogous to the existing --focus <sdk> mechanism, which skips pairs that don't include the focused SDK. The new --skip-released-pairs flag (set automatically in CI) skips any round-trip test where both encrypt_sdk and decrypt_sdk are semver-tagged releases (v\d+\.\d+\.\d+).

The flag is not set when:

  • The event is workflow_dispatch — the user explicitly requested a full run
  • The platform under test is itself a head build (e.g. main) — in that case, testing released SDKs against a new platform IS meaningful for backward-compatibility coverage (covers PRs and cron jobs from opentdf/platform)

Implementation

  • SDK.is_released() in tdfs.py — semver regex on the version string
  • --skip-released-pairs option + pytest_runtest_setup hook in conftest.py — auto-skips without touching individual test files
  • SKIP_RELEASED_PAIRS env var in the CI matrix job, gated on event type and platform head status

Summary by CodeRabbit

  • New Features

    • Added a --skip-released-pairs option to conditionally skip tests for released SDK pairs.
    • Tests can now target specific SDK@version entries resolved at runtime.
  • Chores

    • CI workflow improved for clearer test-result naming, conditional steps, and aggregate failure handling.
    • Test tooling and validators enhanced for stricter SDK spec parsing and lint/type-tool config updated.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 4, 2026

📝 Walkthrough

Walkthrough

Refactors SDK-spec handling across tests and CI: adds typed SDK spec parsing and release detection in xtest/tdfs.py, validates and processes SDK-spec CLI options plus a --skip-released-pairs pytest flag in xtest/conftest.py, updates GitHub Actions xtest workflow to produce and consume a sdk-version matrix and conditionally skip/upload artifacts, and adjusts lint/pyright config.

Changes

SDK Version Specification & Conditional Testing

Layer / File(s) Summary
Data Shape / Types
xtest/tdfs.py
Adds is_sdk_type() type guard, parse_sdk_spec(spec) -> list[SDK], and SDK.is_released(); annotates SDK.version.
Core Parsing Logic
xtest/tdfs.py
parse_sdk_spec() supports "<sdk>", "<sdk>@*", "<sdk>@<version>"; all_versions_of() refactored to list-comprehension.
CLI Validation & Hooks
xtest/conftest.py
Adds sdk_spec_type() validator; updates --sdks, --sdks-decrypt, --sdks-encrypt to use it; adds --skip-released-pairs; pytest_generate_tests() now parses specs with tdfs.parse_sdk_spec() and raises pytest.UsageError on parse errors.
Test Skipping
xtest/conftest.py
Adds pytest_runtest_setup() to skip tests when both encrypt/decrypt SDKs are released and --skip-released-pairs is enabled.
CI: Version Resolution & Matrix
.github/workflows/xtest.yml
resolve-versions step builds sdk-version-list (excluding platform, only successful entries) and sets it as job output. xct job matrix uses sdk-version from that output; ENCRYPT_SDK and other envs switch to matrix.sdk-version.
CI: Conditional Steps & Artifacts
.github/workflows/xtest.yml
Step conditions changed to startsWith(matrix.sdk-version, 'go@') / java@; multiple pytest steps conditionally append --skip-released-pairs based on SKIP_RELEASED_PAIRS; artifact names sanitized from ENCRYPT_SDK and use sdk-version and platform-tag; aggregator treats skipped as failure.
Tooling Config
xtest/pyproject.toml
Ruff enables PERF; Pyright venv settings added (venvPath=".", venv=".venv").
Small Refactor
xtest/audit_logs.py
Switched several context string assemblies in _raise_assertion_error from loops to list comprehensions (no behavior change).

Sequence Diagram

sequenceDiagram
    actor Developer
    participant GHA as GitHub Actions
    participant RV as resolve-versions Job
    participant Parser as tdfs.parse_sdk_spec
    participant XCT as xct Job (matrix)
    participant Pytest as pytest
    participant Hook as pytest_runtest_setup

    Developer->>GHA: push / trigger workflow
    GHA->>RV: run version resolution
    RV->>Parser: build sdkVersionList from resolved tags
    Parser-->>RV: SDK strings like "go@v1.2.3"
    RV->>GHA: set job output sdk-version-list (JSON)
    GHA->>XCT: expand matrix from sdk-version-list
    XCT->>Pytest: invoke pytest with matrix.sdk-version envs
    Pytest->>Parser: parse CLI --sdks* via parse_sdk_spec()
    Parser-->>Pytest: resolved SDK param sets
    Pytest->>Hook: for each test, run pytest_runtest_setup
    Hook->>Hook: if both SDKs .is_released() && skip flag -> skip test
    alt skipped
        Hook-->>Pytest: test skipped
    else
        Hook-->>Pytest: test runs
    end
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested Reviewers

  • pflynn-virtru

Poem

🐰 Specs now hop in tidy rows,
parse and guard where version flows,
released pairs kindly step aside,
CI matrix marches, artifacts tied,
a rabbit cheers the trimmed test stride.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding version-qualified SDK selection to xtest, which is the core feature across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch DSPX-1115-sdkversions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for version-qualified SDK specifiers (e.g., 'go:main', 'go:*') in the test suite. Key changes include the addition of sdk_spec_type for CLI option validation in conftest.py, a new parse_sdk_spec utility in tdfs.py, and updates to pyright configuration. Feedback focuses on improving consistency by using the is_sdk_type helper and replacing assert statements with ValueError to ensure robust runtime validation.

Comment thread xtest/conftest.py Outdated
Comment thread xtest/tdfs.py Outdated
Comment thread xtest/tdfs.py Outdated
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

X-Test Failure Report

1 similar comment
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

X-Test Failure Report

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 5, 2026

X-Test Failure Report

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 5, 2026

X-Test Failure Report

dmihalcik-virtru and others added 11 commits May 5, 2026 09:45
- Add `tdfs.parse_sdk_spec()` supporting `go`, `go:*`, `go:main`,
  `go:v0.18.0` specifiers
- Update `--sdks`, `--sdks-encrypt`, `--sdks-decrypt` CLI options to
  accept version-qualified values via new `sdk_spec_type` validator
- Replace `defaulted_list_opt`/`all_versions_of` expansion in
  `pytest_generate_tests` with `sdk_specs_opt` + `parse_sdk_spec`
- Emit `sdk-version-list` (e.g. `["go:main","java:latest"]`) from
  `resolve-versions` job and drive `xct` matrix from it instead of a
  hardcoded `["go","java","js"]`; each SDK version now gets its own
  matrix cell
- Replace `env.FOCUS_SDK == 'go'/'java'` step conditions with
  `startsWith(matrix.sdk-version, 'go:')` / `'java:'`
- Update artifact names to include the full `sdk-version` specifier

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Colons are not allowed in GitHub Actions artifact names and are
problematic in file names; '@' is the idiomatic version separator
(npm, Go module proxy, etc.).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fail xtest capstone when xct matrix is skipped (empty sdk-version-list
  would previously produce a green run with zero tests executed)
- Correct --sdks help text examples from ':' to '@' separator
- Reject empty version strings (e.g. 'go@') and whitespace-only input
  in sdk_spec_type at argument-parse time with a clear error message
- Wrap e_sdks/d_sdks comprehensions in pytest_generate_tests to convert
  FileNotFoundError/ValueError into pytest.UsageError

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add SDK.is_released() to detect semver-tagged versions, a
--skip-released-pairs pytest flag, and a pytest_runtest_setup hook that
automatically skips any round-trip test where both encrypt_sdk and
decrypt_sdk are released artifacts. The workflow sets SKIP_RELEASED_PAIRS
on all non-workflow_dispatch triggers so PR runs skip redundant
old-vs-old pairs without requiring changes to individual test files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
If the platform under test is a head build (e.g. main), all SDK pairs
are meaningful for backward-compat coverage, so --skip-released-pairs
must not fire. Only skip when both the platform tag is a released
artifact and the event is not a workflow_dispatch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@dmihalcik-virtru dmihalcik-virtru force-pushed the DSPX-1115-sdkversions branch from 5b25db8 to 404c3ff Compare May 5, 2026 13:45
@dmihalcik-virtru dmihalcik-virtru changed the title feat(xtest): DSPX-1115 Allow selecting sdk versions for xtest feat(xtest): Allow selecting sdk versions for xtest May 5, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 5, 2026

X-Test Failure Report

@dmihalcik-virtru dmihalcik-virtru marked this pull request as ready for review May 5, 2026 15:12
@dmihalcik-virtru dmihalcik-virtru requested review from a team as code owners May 5, 2026 15:12
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/xtest.yml:
- Around line 717-725: Sanitize matrix.sdk-version before using it in artifact
names: update the two upload-artifact steps (the test-results upload and the
"Upload audit logs on failure" step) so the name interpolation uses a sanitized
value like replace(matrix.sdk-version, '/', '-') (e.g., ${{
replace(matrix.sdk-version, '/', '-') }}-${{ matrix.platform-tag }}), ensuring
any forward slashes are replaced (or otherwise removed) so artifact names are
valid for actions/upload-artifact.

In `@xtest/tdfs.py`:
- Around line 387-388: is_released currently only recognizes versions starting
with "v", so expand the detection to match tags like "sdk/0.9.0", bare semver
"0.9.0" and semver with prerelease/build metadata "0.9.0-beta.84". Update the
is_released method (refer to is_released and self.version) to use a broader
regex that optionally allows "v" or "sdk/" prefixes and accepts semver with
optional prerelease/build suffixes (for example by matching
(?:v|sdk/)?\d+\.\d+\.\d+(?:[-+].+)? across the string) so --skip-released-pairs
correctly identifies released JS versions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 062f64a9-22a1-490d-a6b3-d442fed86167

📥 Commits

Reviewing files that changed from the base of the PR and between 7e76234 and 404c3ff.

📒 Files selected for processing (4)
  • .github/workflows/xtest.yml
  • xtest/conftest.py
  • xtest/pyproject.toml
  • xtest/tdfs.py

Comment thread .github/workflows/xtest.yml Outdated
Comment thread xtest/tdfs.py Outdated
…rtifact names

- Broaden `SDK.is_released()` regex to recognize JS-style tags (`sdk/0.9.0`,
  `0.9.0-beta.84`) in addition to `v`-prefixed semver; use `fullmatch` to
  avoid partial matches so `--skip-released-pairs` correctly prunes JS pairs
- Sanitize `matrix.sdk-version` before using it in artifact names: JS tags
  like `sdk/0.9.0` produce names with `/` which `actions/upload-artifact`
  rejects; new step outputs a `/`→`-` sanitized value used by both upload steps

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 5, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@xtest/tdfs.py`:
- Around line 387-392: The current is_released() regex rejects semver strings
that include both prerelease and build metadata (e.g. "0.18.0-rc.1+build.7");
update the pattern in is_released to allow an optional prerelease group and an
optional build metadata group separately (for example replace the existing final
part with something like (?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?), keeping the
existing optional "sdk/" and "v" prefixes and using re.fullmatch against
self.version.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 990144b1-809b-45c0-a49a-d13463240507

📥 Commits

Reviewing files that changed from the base of the PR and between 404c3ff and 7bc0f72.

📒 Files selected for processing (4)
  • .github/workflows/xtest.yml
  • xtest/audit_logs.py
  • xtest/pyproject.toml
  • xtest/tdfs.py
✅ Files skipped from review due to trivial changes (2)
  • xtest/audit_logs.py
  • xtest/pyproject.toml

Comment thread xtest/tdfs.py
Comment on lines +387 to +392
def is_released(self) -> bool:
return bool(
re.fullmatch(
r"(?:sdk/)?v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?", self.version
)
)
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 5, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Allow prerelease and build metadata in is_released().

This regex rejects valid semver tags like 0.18.0-rc.1+build.7, so pytest_runtest_setup() will treat those SDKs as unreleased and keep running released-only pairs unnecessarily. Split prerelease and build into separate optional groups.

Suggested fix
     def is_released(self) -> bool:
         return bool(
             re.fullmatch(
-                r"(?:sdk/)?v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?", self.version
+                r"(?:sdk/)?v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?",
+                self.version,
             )
         )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@xtest/tdfs.py` around lines 387 - 392, The current is_released() regex
rejects semver strings that include both prerelease and build metadata (e.g.
"0.18.0-rc.1+build.7"); update the pattern in is_released to allow an optional
prerelease group and an optional build metadata group separately (for example
replace the existing final part with something like
(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?), keeping the existing optional "sdk/"
and "v" prefixes and using re.fullmatch against self.version.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

prereleased and build-tagged SDKs are not released, hence the intentional regex rejection

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@jakedoublev, that makes complete sense — prerelease (-rc.1) and build-tagged (+build.7) versions are not stable releases, so intentionally excluding them from is_released() is the right design. My suggestion was incorrect. Thanks for the clarification!


✏️ Learnings added
Learnt from: jakedoublev
Repo: opentdf/tests PR: 446
File: xtest/tdfs.py:387-392
Timestamp: 2026-05-05T21:39:53.321Z
Learning: In `xtest/tdfs.py`, the `SDK.is_released()` method intentionally rejects prerelease (e.g., `-rc.1`) and build-metadata (e.g., `+build.7`) tagged versions. Only clean semver tags (e.g., `v0.18.0`) or those with an optional combined suffix are considered "released." Do not suggest expanding the regex to accept prerelease/build metadata tags as released artifacts.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

for (const [sdkType, ref] of Object.entries(refs)) {
try {
const refArgs = ref.trim().split(/\s+/).filter(Boolean);
const result = spawnSync('uv', ['run', '--project', sdkMgrDir, 'otdf-sdk-mgr', 'versions', 'resolve', sdkType, ...refArgs], { cwd: sdkMgrDir, encoding: 'utf-8' });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

JavaScript, in YAML, that shells out to invoke python. Don't you love github actions?

FOCUS_SDK: ${{ inputs.focus-sdk || 'all' }}
ENCRYPT_SDK: ${{ matrix.sdk }}
ENCRYPT_SDK: ${{ matrix.sdk-version }}
SKIP_RELEASED_PAIRS: ${{ github.event_name != 'workflow_dispatch' && !contains(fromJSON(needs.resolve-versions.outputs.heads), matrix.platform-tag) }}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A comment here explaining this condition like in your PR description could be useful. 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants