Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2ffcbd7
feat(otdf-sdk-mgr): add SDK version management CLI and refactor scripts
dmihalcik-virtru Feb 13, 2026
20c4075
cleanups
dmihalcik-virtru Feb 18, 2026
3baeb44
Update otdfctl.sh
dmihalcik-virtru Feb 18, 2026
e1e8afb
remove old scripts
dmihalcik-virtru Feb 18, 2026
c60a687
Update xtest.yml
dmihalcik-virtru Feb 18, 2026
9faa0d6
Update xtest.yml
dmihalcik-virtru Feb 18, 2026
c5d2feb
Update xtest.yml
dmihalcik-virtru Feb 18, 2026
d8d0a88
cleanups
dmihalcik-virtru Feb 18, 2026
d40d84e
chore(xtest): Removes dead code and comments to it
dmihalcik-virtru Feb 18, 2026
dc32ae9
fix(otdf-sdk-mgr): address PR #410 review issues
dmihalcik-virtru Feb 18, 2026
a665d5e
chore(xtest): fmt
dmihalcik-virtru Feb 18, 2026
5ef4548
chore(otdf-sdk-mgr): address PR #410 review issues (round 2)
dmihalcik-virtru Feb 18, 2026
f1e54db
fix(xtest): split space-separated ref aliases before spawnSync call
dmihalcik-virtru Feb 18, 2026
f8f8642
chore(xtest): restore ci lint check
dmihalcik-virtru Feb 18, 2026
d8a3b0b
chore(docs) update path
dmihalcik-virtru Feb 18, 2026
205aa3d
Update checkout.py
dmihalcik-virtru Feb 18, 2026
f2d2127
fix(resolve): resolve JS refs via npm; support pre-release and dist-tags
dmihalcik-virtru Feb 18, 2026
616b4e7
feat(ci): add Artifact column to Versions under Test summary table
dmihalcik-virtru Feb 18, 2026
971c988
Update resolve.py
dmihalcik-virtru Feb 18, 2026
c0b665d
fix(setup-cli-tool): fix tag sanitization for pre-release version env…
dmihalcik-virtru Feb 18, 2026
5db2709
test(otdf-sdk-mgr): add pytest coverage for semver and resolve modules
dmihalcik-virtru Feb 19, 2026
085b329
fix(test): remove unused variable in test_resolve.py
dmihalcik-virtru Feb 19, 2026
20562ea
chore(xtest) ruff format
dmihalcik-virtru Feb 19, 2026
3426af7
fix(test): resolve pyright type errors in test_resolve.py
dmihalcik-virtru Feb 19, 2026
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
8 changes: 8 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,11 @@ jobs:
uv run pyright
uv run pytest --maxfail=1 --disable-warnings -v --tb=short -m "not integration"
working-directory: otdf-local
- name: Lint and test otdf-sdk-mgr
run: |
uv sync
uv run ruff check .
uv run ruff format --check .
uv run pyright
uv run pytest --maxfail=1 --disable-warnings -v --tb=short -m "not integration"
working-directory: otdf-sdk-mgr
72 changes: 53 additions & 19 deletions .github/workflows/xtest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,27 @@ on:
required: false
type: string
default: main
description: "The branch or commit to use for the platform"
description: "platform ref: branch, tag (e.g. service/v0.12.0), commit SHA, 'latest', or 'lts'"
otdfctl-ref:
required: false
type: string
default: main
description: "The branch or commit to use for otdfctl"
description: "otdfctl ref: branch, tag (e.g. v0.29.0), commit SHA, 'latest', or 'lts'"
js-ref:
required: false
type: string
default: main
description: "The branch or commit to use for the web-sdk"
description: "web-sdk ref: branch, tag (e.g. sdk/0.9.0 or 0.9.0-beta.84), commit SHA, 'latest', or 'lts'"
java-ref:
required: false
type: string
default: main
description: "The branch or commit to use for the java-sdk"
description: "java-sdk ref: branch, tag (e.g. v0.12.0), commit SHA, 'latest', or 'lts'"
focus-sdk:
required: false
type: string
default: all
description: "The SDK to focus on (go, js, java, all)"
description: "SDK to focus on (go, js, java, all)"
workflow_call:
inputs:
platform-ref:
Expand Down Expand Up @@ -113,13 +113,13 @@ jobs:
path: otdf-sdk
persist-credentials: false
repository: opentdf/tests
sparse-checkout: xtest/sdk
sparse-checkout: |
xtest/sdk
otdf-sdk-mgr
- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b
with:
python-version: "3.14"
- run: |-
pip install -r scripts/requirements.txt
working-directory: otdf-sdk/xtest/sdk
- uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7.2.0
- id: version-info
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
with:
Expand All @@ -131,11 +131,10 @@ jobs:
.replace(/"/g, """)
.replace(/'/g, "'");
}
const { execSync } = require('child_process');
const { spawnSync } = require('child_process');
const path = require('path');

const workingDir = path.join(process.env.GITHUB_WORKSPACE, 'otdf-sdk/xtest/sdk');
const resolveVersionScript = path.join(workingDir, 'scripts/resolve-version.py');
const sdkMgrDir = path.join(process.env.GITHUB_WORKSPACE, 'otdf-sdk', 'otdf-sdk-mgr');
const defaultTags = process.env.DEFAULT_TAGS || 'main';
core.setOutput('default-tags', defaultTags);

Expand All @@ -150,7 +149,12 @@ jobs:

for (const [sdkType, ref] of Object.entries(refs)) {
try {
const output = execSync(`python3 ${resolveVersionScript} ${sdkType} ${ref}`, { cwd: workingDir }).toString();
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' });
if (result.status !== 0) {
throw new Error(result.stderr || `Process exited with code ${result.status}`);
}
const output = result.stdout;
const ojson = JSON.parse(output);
if (!!ojson.err) {
throw new Error(ojson.err);
Expand All @@ -166,10 +170,28 @@ jobs:

core.summary.addHeading('Versions under Test', 3);

function artifactLink(sdkType, tag, release, head) {
if (head || !release) return '';
const v = tag.replace(/^v/, '');
if (sdkType === 'js') {
const url = `https://www.npmjs.com/package/@opentdf/ctl/v/${encodeURIComponent(v)}`;
return `<a href="${htmlEscape(url)}">npmjs</a>`;
}
if (sdkType === 'java') {
const url = `https://central.sonatype.com/artifact/io.opentdf.platform/sdk/${encodeURIComponent(v)}`;
return `<a href="${htmlEscape(url)}">Maven Central</a>`;
}
if (sdkType === 'go') {
const url = `https://pkg.go.dev/github.com/opentdf/otdfctl@${encodeURIComponent(tag)}`;
return `<a href="${htmlEscape(url)}">pkg.go.dev</a>`;
}
return '';
}

let errorCount = 0;
const table = [];
const th = (data) => ({ data, header: true });
table.push([th('Library'), th('Tag'), th('SHA'), th('Alias'), th('Error')]);
table.push([th('Library'), th('Tag'), th('SHA'), th('Alias'), th('Artifact'), th('Error')]);

for (const [sdkType, refInfo] of Object.entries(versionData)) {
const tagList = [];
Expand All @@ -181,9 +203,10 @@ jobs:
const sdkLink = `<a href="${htmlEscape(sdkRepoUrl)}">${htmlEscape(sdkType)}</a>`;
const commitLink = sha ? `<a href="${htmlEscape(`${sdkRepoUrl}/commit/${encodeURIComponent(sha)}`)}">${htmlEscape(sha.substring(0, 7))}</a>` : ' . ';
const tagLink = (release && tag)
? `<a href="${htmlEscape(`${sdkRepoUrl}/releases/tag/${release}`)}">${htmlEscape(tag)}</a>`
? `<a href="${htmlEscape(`${sdkRepoUrl}/releases/tag/${release}`)}">${htmlEscape(tag)}</a>`
: tag ? htmlEscape(tag) : 'N/A';
table.push([sdkLink, tagLink, commitLink, alias || 'N/A', err || 'N/A']);
const artifactCell = artifactLink(sdkType, tag, release, head);
table.push([sdkLink, tagLink, commitLink, alias || 'N/A', artifactCell || 'N/A', err || 'N/A']);
if (err) {
errorCount += 1;
continue;
Expand Down Expand Up @@ -269,13 +292,15 @@ jobs:

######### CHECKOUT JS CLI #############
- name: Configure js-sdk
id: configure-js
uses: ./otdftests/xtest/setup-cli-tool
with:
path: otdftests/xtest/sdk
sdk: js
version-info: "${{ needs.resolve-versions.outputs.js }}"

- name: Cache npm
if: fromJson(steps.configure-js.outputs.heads)[0] != null
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
with:
path: ~/.npm
Expand All @@ -286,6 +311,7 @@ jobs:
######## SETUP THE JS CLI #############
- name: build and setup the web-sdk cli
id: build-web-sdk
if: fromJson(steps.configure-js.outputs.heads)[0] != null
run: |
make
working-directory: otdftests/xtest/sdk/js
Expand All @@ -300,6 +326,7 @@ jobs:
version-info: "${{ needs.resolve-versions.outputs.go }}"

- name: Cache Go modules
if: fromJson(steps.configure-go.outputs.heads)[0] != null
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
with:
path: |
Expand All @@ -311,13 +338,14 @@ jobs:

- name: Resolve otdfctl heads
id: resolve-otdfctl-heads
if: fromJson(steps.configure-go.outputs.heads)[0] != null
run: |-
echo "OTDFCTL_HEADS=$OTDFCTL_HEADS" >> "$GITHUB_ENV"
env:
OTDFCTL_HEADS: ${{ steps.configure-go.outputs.heads }}

- name: Replace otdfctl go.mod packages, but only at head version of platform
if: env.FOCUS_SDK == 'go' && contains(fromJSON(needs.resolve-versions.outputs.heads), matrix.platform-tag)
if: fromJson(steps.configure-go.outputs.heads)[0] != null && env.FOCUS_SDK == 'go' && contains(fromJSON(needs.resolve-versions.outputs.heads), matrix.platform-tag)
env:
PLATFORM_WORKING_DIR: ${{ steps.run-platform.outputs.platform-working-dir }}
run: |-
Expand All @@ -337,20 +365,23 @@ jobs:

######## SETUP THE GO CLI #############
- name: Prepare go cli
if: fromJson(steps.configure-go.outputs.heads)[0] != null
run: |-
make
working-directory: otdftests/xtest/sdk/go

####### CHECKOUT JAVA SDK ##############

- name: Configure java-sdk
id: configure-java
uses: ./otdftests/xtest/setup-cli-tool
with:
path: otdftests/xtest/sdk
sdk: java
version-info: "${{ needs.resolve-versions.outputs.java }}"

- name: Cache Maven repository
if: fromJson(steps.configure-java.outputs.heads)[0] != null
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
with:
path: ~/.m2/repository
Expand All @@ -359,8 +390,10 @@ jobs:
maven-${{ runner.os }}-

- name: pre-release protocol buffers for java-sdk
if: |
(env.FOCUS_SDK == 'go' || env.FOCUS_SDK == 'java') && contains(fromJSON(needs.resolve-versions.outputs.heads), matrix.platform-tag)
if: >-
fromJson(steps.configure-java.outputs.heads)[0] != null
&& (env.FOCUS_SDK == 'go' || env.FOCUS_SDK == 'java')
&& contains(fromJSON(needs.resolve-versions.outputs.heads), matrix.platform-tag)
run: |-
echo "Replacing .env files for java-sdk..."
echo "Platform tag: $platform_tag"
Expand All @@ -382,6 +415,7 @@ jobs:

####### SETUP JAVA CLI ##############
- name: Prepare java cli
if: fromJson(steps.configure-java.outputs.heads)[0] != null
run: |
make
working-directory: otdftests/xtest/sdk/java
Expand Down
14 changes: 13 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,22 @@ This guide provides essential knowledge for AI agents performing updates, refact

### Structure
- **Test Directory**: `xtest/` - pytest-based integration tests
- **SDK Distributions**: `xtest/sdk/{go,java,js}/` - SDK checkout, build, and CLI wrappers
- **SDK Distributions**: `xtest/sdk/{go,java,js}/dist/` - built SDK distributions with CLI wrappers
- **SDK Configuration**: `otdf-sdk-mgr install` - installs SDK CLIs from released artifacts or delegates to source builds
- **SDK Version Lookup**: `otdf-sdk-mgr versions list` - lists released artifacts across registries (Go git tags, npm, Maven Central, GitHub Releases)
- **Platform**: `platform/` - OpenTDF platform service
- **Test Runner**: pytest with custom CLI options

### Configuring SDK Artifacts

Use `otdf-sdk-mgr` (uv-managed CLI in `otdf-sdk-mgr/`) to install SDK CLIs from released artifacts or source. See `otdf-sdk-mgr/README.md` for full command reference.

```bash
cd otdf-sdk-mgr && uv tool install --editable .
otdf-sdk-mgr install stable # Latest stable releases (recommended)
otdf-sdk-mgr install tip go # Build from source
```

### Running Tests

```bash
Expand Down
85 changes: 85 additions & 0 deletions otdf-sdk-mgr/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# otdf-sdk-mgr

SDK artifact management CLI for OpenTDF cross-client tests. Installs SDK CLIs from **released artifacts** (fast, deterministic) or **source** (for branch/PR testing). Both modes produce the same `sdk/{go,java,js}/dist/{version}/` directory structure.

## Installation

```bash
cd otdf-sdk-mgr && uv tool install --editable .
```

## Commands

### install

```bash
# Install latest stable releases for all SDKs (recommended for local testing)
otdf-sdk-mgr install stable

# Install LTS versions
otdf-sdk-mgr install lts

# Install specific released versions
otdf-sdk-mgr install release go:v0.24.0 js:0.4.0 java:v0.9.0

# Install from tip of main branch (source build)
otdf-sdk-mgr install tip
otdf-sdk-mgr install tip go # Single SDK

# Install a published version with optional dist name (defaults to version tag)
otdf-sdk-mgr install artifact --sdk go --version v0.24.0
otdf-sdk-mgr install artifact --sdk go --version v0.24.0 --dist-name my-tag
```

### versions

```bash
# List available versions
otdf-sdk-mgr versions list go --stable --latest 3 --table

# Resolve version tags to SHAs
otdf-sdk-mgr versions resolve go main latest
```

### Other commands

```bash
# Checkout SDK source
otdf-sdk-mgr checkout go main

# Clean dist and source directories
otdf-sdk-mgr clean

# Fix Java pom.xml after source checkout
otdf-sdk-mgr java-fixup
```

## How Release Installs Work

- **Go**: Writes a `.version` file; `cli.sh`/`otdfctl.sh` use `go run github.com/opentdf/otdfctl@{version}` (no local compilation needed, Go caches the binary)
- **JS**: Runs `npm install @opentdf/ctl@{version}` into the dist directory; `cli.sh` uses `npx` from local `node_modules/`
- **Java**: Downloads `cmdline.jar` from GitHub Releases; `cli.sh` uses `java -jar cmdline.jar`

## Source Builds

Source builds (`tip` mode) check out source to `sdk/{lang}/src/` and compile via `make` to `sdk/{lang}/dist/`.

After changes to SDK source, rebuild:

```bash
otdf-sdk-mgr install tip go # or java, js

# Or manually: checkout + make
cd sdk/go # or sdk/java, sdk/js
make

# Verify build worked
ls -la dist/main/cli.sh
```

## Manual SDK Operations

```bash
sdk/go/dist/main/cli.sh encrypt input.txt output.tdf --attr <fqn>
sdk/go/dist/main/cli.sh decrypt output.tdf decrypted.txt
```
28 changes: 28 additions & 0 deletions otdf-sdk-mgr/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[project]
name = "otdf-sdk-mgr"
version = "0.1.0"
description = "SDK artifact management CLI for OpenTDF cross-client tests"
requires-python = ">=3.11"
dependencies = [
"gitpython>=3.1.46",
"rich>=13.7.0",
"typer>=0.12.0",
]

[dependency-groups]
dev = [
"pyright>=1.1.408",
"pytest>=8.0.0",
"ruff>=0.9.0",
]

[project.scripts]
otdf-sdk-mgr = "otdf_sdk_mgr.cli:app"

[build-system]
requires = ["uv_build>=0.9.28"]
build-backend = "uv_build"

[tool.ruff]
target-version = "py311"
line-length = 100
3 changes: 3 additions & 0 deletions otdf-sdk-mgr/src/otdf_sdk_mgr/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""SDK artifact management CLI for OpenTDF cross-client tests."""

__version__ = "0.1.0"
5 changes: 5 additions & 0 deletions otdf-sdk-mgr/src/otdf_sdk_mgr/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Allow running as `python -m otdf_sdk_mgr`."""

from otdf_sdk_mgr.cli import app

app()
Loading
Loading