Skip to content

Bump OMG Pilot

Bump OMG Pilot #85

Workflow file for this run

name: PR
# Pull requests only: pushes and tags are covered by the CircleCI config, and
# this workflow deliberately mirrors only its build-test workflow, not release.
on:
pull_request:
permissions:
contents: read
concurrency:
group: pr-${{ github.event.pull_request.number }}
cancel-in-progress: true
env:
# The OMG training corpus is not vendored. Requiring it here turns "corpus
# absent" into a test failure instead of a skip, so the gate cannot pass green
# without actually running (see internal/core/model/training_examples_test.go).
OPENSYSML_REQUIRE_TRAINING_CORPUS: "1"
# Same for the three pinned OMG pilot corpora, which are downloaded below and
# gated by internal/core/model/pilot_corpora_test.go.
OPENSYSML_REQUIRE_PILOT_CORPORA: "1"
# z3 is installed below, so "no solver, therefore skip" would exercise nothing:
# this turns an absent solver into a failure (see internal/core/solve).
OPENSYSML_REQUIRE_SMT: "1"
jobs:
# Which areas the pull request touches. A path no area claims turns every area
# on, so a new directory is over-tested rather than untested.
changes:
name: Changed areas
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
go: ${{ steps.areas.outputs.go }}
docs: ${{ steps.areas.outputs.docs }}
node: ${{ steps.areas.outputs.node }}
python: ${{ steps.areas.outputs.python }}
java: ${{ steps.areas.outputs.java }}
rust: ${{ steps.areas.outputs.rust }}
vscode: ${{ steps.areas.outputs.vscode }}
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Test the area filter
run: scripts/ci-changed-areas-test.sh
- name: Decide which areas to run
id: areas
env:
BASE_REF: ${{ github.base_ref }}
run: scripts/ci-changed-areas.sh "origin/$BASE_REF" HEAD | tee -a "$GITHUB_OUTPUT"
race-tests:
name: Go race tests
needs: changes
if: needs.changes.outputs.go == 'true'
runs-on: ubuntu-latest
timeout-minutes: 40
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Download Go modules
run: go mod download
# Pinned by digest, so a substituted asset is not run. The release binary rather
# than `go run` keeps buf's own module tree out of the checksum-database path.
- name: Install buf
run: |
curl -fsSL -o /tmp/buf \
https://github.com/bufbuild/buf/releases/download/v1.57.2/buf-Linux-x86_64
echo "644e6fa4a62afc2b0d425d3ac9538d2f512947c0f2e82fc95c41a5eab6729414 /tmp/buf" | sha256sum --check --strict
sudo install -m 0755 /tmp/buf /usr/local/bin/buf
buf --version
# One buf template generates both the Go stubs and the Java client's message
# classes, so without this check the committed stubs can drift from the schema.
- name: Verify the committed Go and Java stubs are current
run: |
make proto-buf BUF=buf
git diff --exit-code api/proto \
clients/java/opensysml-client/src/main/java/io/opensysml/proto
# The solver is an external process, never linked in: the solver-dependent
# tests need one on PATH to run rather than skip.
- name: Install Z3
run: |
sudo apt-get update
sudo apt-get install -y z3
z3 --version
# The second verified backend, for the portability gate below: cvc5 has no
# apt package, so the official static release is what CI runs, pinned by
# digest so a substituted asset is not run.
- name: Install cvc5
run: |
curl -fsSL -o /tmp/cvc5.zip \
https://github.com/cvc5/cvc5/releases/download/cvc5-1.3.4/cvc5-Linux-x86_64-static.zip
echo "dcdbfada0ce493ee98259c0816e0daafc561c223aadb3af298c2968e73ea39c6 /tmp/cvc5.zip" | sha256sum --check --strict
unzip -p /tmp/cvc5.zip '*/bin/cvc5' > /tmp/cvc5-bin
sudo install -m 0755 /tmp/cvc5-bin /usr/local/bin/cvc5
cvc5 --version | head -1
# Keyed on the download script and the shared pilot pin, so a change to the
# pinned tag or the layout invalidates the cache. A restored cache is still
# verified below.
- name: Cache the OMG training corpus
uses: actions/cache@v4
with:
path: examples/sysml-v2-training
key: training-corpus-${{ hashFiles('scripts/download-training-examples.sh', 'scripts/pilot-pin.sh') }}
- name: Download the OMG training corpus
run: ./scripts/download-training-examples.sh
- name: Verify the OMG training corpus
run: |
count=$(find examples/sysml-v2-training -name '*.sysml' | wc -l)
echo "training corpus: $count .sysml files"
if [ "$count" -eq 0 ]; then
echo "error: the training corpus is empty; the corpus gate would not run" >&2
exit 1
fi
# Same keying as the training corpus: the download script plus the shared
# pilot pin. A restored cache is verified below rather than trusted.
- name: Cache the OMG pilot corpora
uses: actions/cache@v4
with:
path: examples/pilot-corpora
key: pilot-corpora-${{ hashFiles('scripts/download-pilot-corpora.sh', 'scripts/pilot-pin.sh') }}
- name: Download the OMG pilot corpora
run: ./scripts/download-pilot-corpora.sh
- name: Verify the OMG pilot corpora
run: |
for root in sysml-examples sysml-validation kerml-examples; do
count=$(find "examples/pilot-corpora/$root" -type f \( -name '*.sysml' -o -name '*.kerml' \) | wc -l)
echo "pilot corpus $root: $count model files"
if [ "$count" -eq 0 ]; then
echo "error: pilot corpus $root is empty; the corpora gate would not run" >&2
exit 1
fi
done
# Per-package timeout: under -race, passes and model run within 1% of go's 10m
# default. Matches `make test`.
- name: Run Go race tests
run: make test
- name: Upload coverage artifact
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage.txt
if-no-files-found: error
static-and-integrity:
name: Go static and integrity checks
needs: changes
if: needs.changes.outputs.go == 'true'
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Download Go modules
run: go mod download
- name: Install Z3
run: |
sudo apt-get update
sudo apt-get install -y z3
z3 --version
- name: Install cvc5
run: |
curl -fsSL -o /tmp/cvc5.zip \
https://github.com/cvc5/cvc5/releases/download/cvc5-1.3.4/cvc5-Linux-x86_64-static.zip
echo "dcdbfada0ce493ee98259c0816e0daafc561c223aadb3af298c2968e73ea39c6 /tmp/cvc5.zip" | sha256sum --check --strict
unzip -p /tmp/cvc5.zip '*/bin/cvc5' > /tmp/cvc5-bin
sudo install -m 0755 /tmp/cvc5-bin /usr/local/bin/cvc5
cvc5 --version | head -1
- name: Cache the OMG training corpus
uses: actions/cache@v4
with:
path: examples/sysml-v2-training
key: training-corpus-${{ hashFiles('scripts/download-training-examples.sh', 'scripts/pilot-pin.sh') }}
- name: Download the OMG training corpus
run: ./scripts/download-training-examples.sh
- name: Verify the OMG training corpus
run: |
count=$(find examples/sysml-v2-training -name '*.sysml' | wc -l)
echo "training corpus: $count .sysml files"
if [ "$count" -eq 0 ]; then
echo "error: the training corpus is empty; the corpus gate would not run" >&2
exit 1
fi
- name: Cache the OMG pilot corpora
uses: actions/cache@v4
with:
path: examples/pilot-corpora
key: pilot-corpora-${{ hashFiles('scripts/download-pilot-corpora.sh', 'scripts/pilot-pin.sh') }}
- name: Download the OMG pilot corpora
run: ./scripts/download-pilot-corpora.sh
- name: Verify the OMG pilot corpora
run: |
for root in sysml-examples sysml-validation kerml-examples; do
count=$(find "examples/pilot-corpora/$root" -type f \( -name '*.sysml' -o -name '*.kerml' \) | wc -l)
echo "pilot corpus $root: $count model files"
if [ "$count" -eq 0 ]; then
echo "error: pilot corpus $root is empty; the corpora gate would not run" >&2
exit 1
fi
done
- name: Check Go formatting
run: |
unformatted=$(gofmt -l .)
if [ -n "$unformatted" ]; then
echo "The following files are not gofmt'd:"
echo "$unformatted"
exit 1
fi
- name: Run Go vet
run: go vet ./...
- name: Run static analysis (staticcheck + gosec)
run: make lint
- name: Check grpc-go stays out of production code
run: ./scripts/check-grpc-imports.sh
# Re-run the corpus gate on its own so its verdict is legible in the log
# and a skip is impossible to miss. TestCorpusGates is the shared
# cache-independence case over all four OMG roots, which skips with them.
- name: Run training corpus gate
run: |
set -o pipefail
go test -count=1 -v ./internal/core/model -run 'TestTrainingExamples|TestCorpusGates' | tee corpus-gate.log
grep -E 'training files clean' corpus-gate.log
if grep -qE '^\s*--- SKIP' corpus-gate.log; then
echo "error: the corpus gate skipped" >&2
exit 1
fi
# The pilot corpora gate on its own, for the same reason: its per-root
# verdict lines are the reviewable output, and a skip must not pass.
# It gates our own diagnostics only; the comparison against the reference
# implementation (cmd/pilot-diff) needs Java validators and is not run here.
- name: Run pilot corpora gate
run: |
set -o pipefail
go test -count=1 -v ./internal/core/model -run 'TestPilotCorpora' | tee pilot-corpora-gate.log
grep -E 'pilot corpus files clean' pilot-corpora-gate.log
if grep -qE '^\s*--- SKIP' pilot-corpora-gate.log; then
echo "error: the pilot corpora gate skipped" >&2
exit 1
fi
# Re-run the solver gate on its own, as the corpus gate is, so a skip
# cannot hide behind a green suite.
- name: Run solver gate (Z3)
run: |
set -o pipefail
go test -count=1 -v ./internal/core/solve ./internal/repl -run 'TestSolver|TestDiscovery|TestSolved|TestDivisor|TestCheck|TestSolve|TestConfigure|TestSynthesis|TestPinned|TestFixed|TestOptimize|TestOptimum' | tee solver-gate.log
if grep -qE '^\s*--- SKIP' solver-gate.log; then
echo "error: a solver-dependent test skipped" >&2
exit 1
fi
# The differential gate compares the solver against the normative
# evaluator; its summary counts make coverage drift reviewable in the log.
- name: Run solver differential gate
run: |
set -o pipefail
go test -count=1 -v ./internal/core/solve -run TestDifferential | tee differential-gate.log
grep -E 'differential gate .*: .* elements:' differential-gate.log
if grep -qE '^\s*--- SKIP' differential-gate.log; then
echo "error: a differential gate skipped" >&2
exit 1
fi
# Portability: the same subset run against both verified backends, so a
# feature one of them refuses is reported rather than discovered by a user.
# OPENSYSML_REQUIRE_SMT makes an absent solver a failure, not a skip.
- name: Run solver portability gate
run: |
set -o pipefail
for solver in z3 cvc5; do
OPENSYSML_SMT="$solver" go test -count=1 -v ./internal/core/solve \
-run 'TestPortability' | tee "portability-$solver.log"
grep -E "portability of $solver" "portability-$solver.log"
if grep -qE '^\s*--- SKIP' "portability-$solver.log"; then
echo "error: the portability gate skipped for $solver" >&2
exit 1
fi
done
# The solver gate again against the second backend, not only the
# portability subset: what z3 answers, cvc5 must answer too.
- name: Run solver gate (cvc5)
run: |
set -o pipefail
OPENSYSML_SMT=cvc5 go test -count=1 -v ./internal/core/solve ./internal/repl \
-run 'TestSolver|TestDiscovery|TestSolved|TestDivisor|TestCheck|TestSolve|TestConfigure|TestSynthesis|TestPinned|TestFixed|TestExplain|TestCore' \
| tee cvc5-gate.log
if grep -qE '^\s*--- SKIP' cvc5-gate.log; then
echo "error: a solver-dependent test skipped under cvc5" >&2
exit 1
fi
# The other half of the contract: with no solver to be found, every
# solver-dependent test must skip with a reason and the absent-solver paths
# must still report one. OPENSYSML_SMT names an executable that does not
# exist, which is discovery's absent case, and the requirement flag is
# dropped so a skip is the expected outcome here.
- name: Run solver-absent gate
run: |
set -o pipefail
env -u OPENSYSML_REQUIRE_SMT OPENSYSML_SMT=/nonexistent/no-such-solver \
go test -count=1 -v ./internal/core/solve ./internal/repl | tee no-solver-gate.log
for name in TestCheckReportsAnAbsentSolver TestExplainReportsAnAbsentSolver \
TestSolveReportsAnAbsentSolver TestConfigureReportsAnAbsentSolver \
TestOptimizeReportsAnAbsentSolver; do
grep -qE "^\s*--- PASS: $name" no-solver-gate.log || {
echo "error: $name did not run without a solver" >&2
exit 1
}
done
# The client jobs download this binary rather than building one, so it runs
# whenever the service or any client does.
build:
name: Build binaries
needs: changes
if: |
needs.changes.outputs.go == 'true' ||
needs.changes.outputs.node == 'true' ||
needs.changes.outputs.python == 'true' ||
needs.changes.outputs.java == 'true' ||
needs.changes.outputs.rust == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Download Go modules
run: go mod download
- name: Build binaries
run: |
make build \
VERSION="pr-${{ github.event.pull_request.number }}" \
COMMIT="$(git rev-parse --short HEAD)" \
BUILD_TIME="$(date -u '+%Y-%m-%d_%H:%M:%S')" \
GO_VERSION="$(go version | awk '{print $3}')"
- name: Verify binaries
run: |
./bin/sysml --version
./bin/sysml-lsp --version
ls -lh bin/
- name: Upload binary artifacts
uses: actions/upload-artifact@v4
with:
name: binaries
path: bin/
if-no-files-found: error
# A gated job that did not run reports `skipped`, which this gate accepts; a job
# skipped by a failed dependency is caught through that dependency's own result.
build-and-test:
name: Build and test
if: always()
needs:
- changes
- build
- docs
- python-test
- race-tests
- rust-test
- static-and-integrity
- vscode-extension
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Verify required checks
env:
CHANGES_RESULT: ${{ needs.changes.result }}
BUILD_RESULT: ${{ needs.build.result }}
DOCS_RESULT: ${{ needs.docs.result }}
PYTHON_TEST_RESULT: ${{ needs.python-test.result }}
RACE_TESTS_RESULT: ${{ needs.race-tests.result }}
RUST_TEST_RESULT: ${{ needs.rust-test.result }}
STATIC_AND_INTEGRITY_RESULT: ${{ needs.static-and-integrity.result }}
VSCODE_EXTENSION_RESULT: ${{ needs.vscode-extension.result }}
run: |
ok() { [ "$1" = success ] || [ "$1" = skipped ]; }
if [ "$CHANGES_RESULT" != success ] ||
! ok "$BUILD_RESULT" ||
! ok "$DOCS_RESULT" ||
! ok "$PYTHON_TEST_RESULT" ||
! ok "$RACE_TESTS_RESULT" ||
! ok "$RUST_TEST_RESULT" ||
! ok "$STATIC_AND_INTEGRITY_RESULT" ||
! ok "$VSCODE_EXTENSION_RESULT"; then
echo "changes=$CHANGES_RESULT"
echo "build=$BUILD_RESULT"
echo "docs=$DOCS_RESULT"
echo "python-test=$PYTHON_TEST_RESULT"
echo "race-tests=$RACE_TESTS_RESULT"
echo "rust-test=$RUST_TEST_RESULT"
echo "static-and-integrity=$STATIC_AND_INTEGRITY_RESULT"
echo "vscode-extension=$VSCODE_EXTENSION_RESULT"
exit 1
fi
vscode-extension:
name: VS Code extension
needs: changes
if: needs.changes.outputs.vscode == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: editors/vscode/package-lock.json
# Type-check, bundle and package. The generated TextMate grammars are
# gated by go test ./editors/... in the job above.
- name: Package the VS Code extension
run: make vscode-package
- name: Upload VS Code extension artifact
uses: actions/upload-artifact@v4
with:
name: vscode-extension
path: editors/vscode/opensysml-sysml.vsix
if-no-files-found: error
docs:
name: Documentation site
needs: changes
if: needs.changes.outputs.docs == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: pip
cache-dependency-path: docs-requirements.txt
- name: Install documentation toolchain
run: make docs-install
# --strict: a link or anchor a moved page left behind fails here rather than
# publishing a 404 from main.
- name: Build documentation site
run: make docs
node-test:
name: Node client tests
needs: [changes, build-and-test]
if: needs.changes.outputs.node == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: binaries
path: bin
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: clients/node/package-lock.json
# The tests and the conformance runner start private children of this
# binary; without it they would build one from the checkout, needing Go.
- name: Install the sysml-grpc binary
run: |
chmod +x bin/sysml-grpc
echo "OPENSYSML_BINARY=$(pwd)/bin/sysml-grpc" >> "$GITHUB_ENV"
- name: Install the client's dependencies
working-directory: clients/node
run: npm ci
- name: Build, typecheck and lint
working-directory: clients/node
run: |
npm run build
npm run typecheck
npm run lint
- name: Run the tests
working-directory: clients/node
run: npm test
# --allow-skips because v1 covers 5 of the 15 RPCs; the skipped count is
# reported and the artifact records which scenarios they were.
- name: Run the conformance suite through the client
working-directory: clients/node
run: |
npm run conformance -- --binary "$OPENSYSML_BINARY" --allow-skips \
--report node-conformance.json
# A runner that passes against a deliberately broken client tests nothing.
- name: Check the conformance runner catches a broken client
working-directory: clients/node
run: |
for mutation in hide-capability drop-diagnostics blank-symbol-kind shift-integer drop-feature-values; do
if npm run conformance -- --binary "$OPENSYSML_BINARY" --allow-skips \
--protocols connect --mutate "$mutation" > /dev/null 2>&1; then
echo "error: the suite passed with the $mutation mutation applied" >&2
exit 1
fi
echo "ok: $mutation is caught"
done
- uses: actions/upload-artifact@v4
with:
name: node-conformance
path: clients/node/node-conformance.json
# Pinned by digest, as cvc5 is above, so a substituted asset is not run.
- name: Install buf
run: |
curl -fsSL -o /tmp/buf \
https://github.com/bufbuild/buf/releases/download/v1.57.2/buf-Linux-x86_64
echo "644e6fa4a62afc2b0d425d3ac9538d2f512947c0f2e82fc95c41a5eab6729414 /tmp/buf" | sha256sum --check --strict
sudo install -m 0755 /tmp/buf /usr/local/bin/buf
buf --version
# Committed stubs must be what buf generates, or the checked-in client and
# the schema can drift apart silently.
- name: Verify the committed TypeScript stubs are current
run: |
make proto-ts BUF=buf
git diff --exit-code clients/node/src/generated
python-test:
name: Python client tests
needs: [changes, build]
if: needs.changes.outputs.python == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Download binary artifacts
uses: actions/download-artifact@v4
with:
name: binaries
path: bin
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install sysml-grpc
run: |
mkdir -p ~/.opensysml/bin
cp bin/sysml-grpc ~/.opensysml/bin/
chmod +x ~/.opensysml/bin/sysml-grpc
- name: Install Python client
run: |
make python-install
pip install pytest pytest-mock psutil
# The integration tests connect to a service on the standard port with
# auto_start=False, the explicit opt-in to one the client does not manage;
# without it they skip, exercising nothing, so one runs here for the rest of
# the job. Tests naming no address start a private child of their own.
- name: Start sysml-grpc
run: nohup ~/.opensysml/bin/sysml-grpc -port 50051 > /tmp/sysml-grpc.log 2>&1 &
- name: Wait for sysml-grpc
run: |
python3 - <<'PY'
import socket, sys, time
for _ in range(30):
with socket.socket() as probe:
probe.settimeout(1)
if probe.connect_ex(("localhost", 50051)) == 0:
print("sysml-grpc is listening on 50051")
sys.exit(0)
time.sleep(1)
sys.exit("sysml-grpc never listened on 50051")
PY
# OPENSYSML_REQUIRE_SERVICE turns "no service, so skip" into a failure: a
# service is provided here, so its absence is the bug, not a pass.
- name: Run Python client tests
env:
OPENSYSML_REQUIRE_SERVICE: 1
run: make python-test
- name: Verify Python client import
run: python3 -c "import opensysml; print(f'opensysml {opensysml.__version__} imported successfully')"
rust-test:
name: Rust client tests
needs: [changes, build]
if: needs.changes.outputs.rust == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Reuse the service built by the Go job rather than installing Go again
# in this Rust-focused gate.
- uses: actions/download-artifact@v4
with:
name: binaries
path: bin
# The artifact is a zip, which does not carry the executable bit.
- name: Install the sysml-grpc binary
run: chmod +x bin/sysml-grpc
- name: Install Rust 1.83
run: |
rustup toolchain install 1.83.0 --profile minimal
rustup default 1.83.0
rustup component add rustfmt clippy --toolchain 1.83.0
rustc --version
cargo --version
- name: Check Rust formatting
working-directory: rust
run: cargo fmt --all -- --check
- name: Run Rust clippy
working-directory: rust
run: cargo clippy --all-targets -- -D warnings
# A missing service must fail rather than silently skipping integration
# tests, because the downloaded binary is required by this job.
- name: Run Rust tests
working-directory: rust
env:
OPENSYSML_GRPC_BINARY: ${{ github.workspace }}/bin/sysml-grpc
OPENSYSML_REQUIRE_SERVICE: 1
run: cargo test --workspace
# Invoke the runner directly so this job does not rebuild the service
# through make conformance-rust, which would require Go here.
- name: Run Rust conformance
env:
OPENSYSML_GRPC_BINARY: ${{ github.workspace }}/bin/sysml-grpc
run: |
cargo run --manifest-path rust/Cargo.toml -p opensysml-conformance -- \
-binary "$GITHUB_WORKSPACE/bin/sysml-grpc" \
-report bin/conformance-report-rust.json
- name: Package Rust client
working-directory: rust
run: cargo package -p opensysml
# The digest pins the generator binary so a substituted release cannot
# change the committed Rust artifacts.
- name: Install buf
run: |
curl -fsSL -o /tmp/buf \
https://github.com/bufbuild/buf/releases/download/v1.57.2/buf-Linux-x86_64
echo "644e6fa4a62afc2b0d425d3ac9538d2f512947c0f2e82fc95c41a5eab6729414 /tmp/buf" | sha256sum --check --strict
chmod +x /tmp/buf
/tmp/buf --version
- name: Verify Rust generated artifacts
run: |
make BUF=/tmp/buf proto-rust
git diff --exit-code rust/opensysml/src/proto rust/conformance/sysml.descriptor.binpb
- uses: actions/upload-artifact@v4
with:
name: conformance-report-rust
path: bin/conformance-report-rust.json
# Mirrors the CircleCI java-test job: unit tests plus the conformance suite over
# both Connect encodings, on the client's Java 17 baseline.
java-test:
name: Java client tests
needs: [changes, build]
if: needs.changes.outputs.java == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: binaries
path: bin
# 17 is the client's compile target, so a 21-only construct fails here.
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
cache: maven
- name: Make the service binary executable
run: chmod +x bin/sysml-grpc
# opensysml.requireService turns "no binary, so skip" into a failure, and
# `install` builds the sources and javadoc jars a release needs on every commit.
- name: Test and install the client
run: mvn -B -f clients/java/pom.xml install -Dopensysml.requireService=true
- name: Run the conformance suite through the client
run: |
mvn -B -q -f clients/java/pom.xml -pl opensysml-conformance \
dependency:build-classpath -Dmdep.outputFile=/tmp/cp.txt
java -cp "clients/java/opensysml-conformance/target/classes:$(cat /tmp/cp.txt)" \
io.opensysml.conformance.Main \
-binary bin/sysml-grpc -protocols connect,connect-json -allow-skips \
-report conformance-report-java.json
- uses: actions/upload-artifact@v4
if: always()
with:
name: java-conformance-report
path: conformance-report-java.json
conformance-pkg:
name: Conformance through pkg/opensysml
needs: changes
if: needs.changes.outputs.go == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Download modules
run: go mod download
# The runner builds and starts sysml-grpc itself; the pkg protocol runs
# in-process and pkg-connect dials that service, so this gate proves the
# public Go API answers as the wire does. Scenarios outside its v1
# surface are counted skips, not hidden.
- name: Conformance suite through pkg/opensysml
run: make conformance-pkg
# if: always() — the report matters most when the gate failed and halted the job.
- uses: actions/upload-artifact@v4
if: always()
with:
name: conformance-pkg-report
path: bin/conformance-pkg-report.json