From ca02ea3f4cf0f6ee244bc0888616b3ad1abc1813 Mon Sep 17 00:00:00 2001 From: wobrschalek Date: Sun, 4 Oct 2026 07:47:15 -0700 Subject: [PATCH 1/2] updates for pypi publishing --- .github/workflows/package.yml | 41 +++++++++++ .github/workflows/publish.yml | 38 +++++++++++ .../workflows/test-sysmlv2-python-client.yml | 61 ++++++----------- .gitignore | 4 +- CHANGELOG.md | 6 ++ MANIFEST.in | 1 + README.md | 32 +++++---- RELEASING.md | 68 +++++++++++++++++++ pyproject.toml | 36 ++++++++-- src/sysml_api/api_lib.py | 1 - tests/test_client.py | 2 +- 11 files changed, 229 insertions(+), 61 deletions(-) create mode 100644 .github/workflows/package.yml create mode 100644 .github/workflows/publish.yml create mode 100644 CHANGELOG.md create mode 100644 MANIFEST.in create mode 100644 RELEASING.md diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml new file mode 100644 index 0000000..da40b33 --- /dev/null +++ b/.github/workflows/package.yml @@ -0,0 +1,41 @@ +name: Validate distributions +on: + workflow_call: +permissions: + contents: read +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - run: python -m pip install build twine + - run: python -m build + - run: python -m twine check --strict dist/* + - name: Check release tag matches package version + if: startsWith(github.ref, 'refs/tags/') + run: | + python - <<'PY' + import ast, os + from pathlib import Path + tree = ast.parse(Path('src/sysmlv2_client/__init__.py').read_text()) + version = next(ast.literal_eval(node.value) for node in tree.body + if isinstance(node, ast.Assign) + and any(isinstance(t, ast.Name) and t.id == '__version__' for t in node.targets)) + assert os.environ['GITHUB_REF_NAME'] == f'v{version}', 'Tag must match package version' + PY + - name: Test installed wheel outside checkout + run: | + python -m venv /tmp/wheel-test + /tmp/wheel-test/bin/python -m pip install dist/*.whl pytest requests-mock + cd /tmp + /tmp/wheel-test/bin/python -c "import sysml_api; from sysmlv2_client import SysMLV2Client" + /tmp/wheel-test/bin/python -m pytest --import-mode=importlib "$GITHUB_WORKSPACE/tests" + - name: Build wheel from source distribution + run: python -m pip wheel --no-deps dist/*.tar.gz --wheel-dir /tmp/sdist-wheels + - uses: actions/upload-artifact@v4 + with: + name: distributions + path: dist/* diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..6fa7509 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,38 @@ +name: Publish package +on: + release: + types: [published] + workflow_dispatch: +permissions: + contents: read +jobs: + build: + uses: ./.github/workflows/package.yml + testpypi: + if: github.event_name == 'workflow_dispatch' + needs: build + runs-on: ubuntu-latest + environment: testpypi + permissions: + id-token: write + steps: + - uses: actions/download-artifact@v4 + with: + name: distributions + path: dist/ + - uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + pypi: + if: github.event_name == 'release' && startsWith(github.ref, 'refs/tags/v') + needs: build + runs-on: ubuntu-latest + environment: pypi + permissions: + id-token: write + steps: + - uses: actions/download-artifact@v4 + with: + name: distributions + path: dist/ + - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/test-sysmlv2-python-client.yml b/.github/workflows/test-sysmlv2-python-client.yml index 2ab44a3..e6fd345 100644 --- a/.github/workflows/test-sysmlv2-python-client.yml +++ b/.github/workflows/test-sysmlv2-python-client.yml @@ -1,47 +1,28 @@ -name: Test sysmlv2-python-client - +name: Test sysml-api-client on: push: - branches: [ main, develop ] + branches: [main, develop] pull_request: - branches: [ main, develop ] - + branches: [main, develop] +permissions: + contents: read jobs: test: runs-on: ubuntu-latest - + strategy: + matrix: + python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - id: setup-python - uses: actions/setup-python@v4 - with: - python-version: '3.12' - - - name: Cache pip dependencies - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('pyproject.toml') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e . - pip install pytest pytest-cov requests-mock - - - name: Run tests with coverage - run: | - mkdir -p test-results - pytest -v --tb=short --junitxml=test-results/results.xml --cov=src/sysmlv2_client --cov-report=xml:test-results/coverage.xml --cov-report=html:test-results/htmlcov - - - name: Upload test results - uses: actions/upload-artifact@v4 - if: always() - with: - name: test-results-sysmlv2-python-client - path: test-results/ + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - run: python -m pip install -e ".[test]" + - run: python -m pytest -v --tb=short --junitxml=test-results/results.xml --cov=sysmlv2_client --cov-report=xml:test-results/coverage.xml + - uses: actions/upload-artifact@v4 + if: always() + with: + name: test-results-${{ matrix.python-version }} + path: test-results/ + package: + uses: ./.github/workflows/package.yml diff --git a/.gitignore b/.gitignore index 3d8c3b0..5a75f93 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ __pycache__/ # Virtual Environments venv/ .venv/ +.venv-wheel/ env/ # Jupyter Notebook Checkpoints @@ -23,6 +24,7 @@ env/ # Flexo Setup Sensitive Files flexo-setup/docker-compose/env/*.env /build -/src/sysmlv2_python_client.egg-info +/dist/ +*.egg-info/ /test-results coverage.xml diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..dec9c8f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +## 0.1.0 + +- Initial PyPI packaging as `sysml-api-client`. +- Includes the existing `sysmlv2_client` client, exceptions, and `sysml_api` helpers. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..27854f7 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include CHANGELOG.md RELEASING.md diff --git a/README.md b/README.md index d0ea63b..a8548e0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,20 @@ -# SysML v2 Python Client +# sysml-api-client -A basic Python client library for interacting with a SysML v2 API server, specifically tested against the OpenMBEE Flexo implementation. +A Python client for the [OMG Systems Modeling API and Services](https://www.omg.org/spec/SystemsModelingAPI), specifically tested against the OpenMBEE Flexo implementation. The PyPI name is `sysml-api-client`; Python imports remain `sysmlv2_client` and `sysml_api`. + +## Installation + +Requires Python 3.10 or newer. Once published on PyPI: + +```bash +python -m pip install sysml-api-client +``` + +```python +from sysmlv2_client import SysMLV2Client +``` + +See [RELEASING.md](RELEASING.md) for build validation and publisher setup. ## Features @@ -19,7 +33,7 @@ A basic Python client library for interacting with a SysML v2 API server, specif This client is designed to work with a running instance of the OpenMBEE Flexo SysMLv2 service. * **Prerequisites:** Docker and Docker Compose installed. -* **Get Setup Files:** The necessary `docker-compose.yml` and configuration files are located in the `flexo-setup/docker-compose/` directory within this project (downloaded from `Open-MBEE/flexo-mms-sysmlv2`). +* **Get Setup Files:** Obtain the Docker Compose setup from [Open-MBEE/flexo-mms-sysmlv2](https://github.com/Open-MBEE/flexo-mms-sysmlv2). The paths below assume you have copied it into `flexo-setup/` locally; it is not included in this package. * **Start Services:** Navigate to the `flexo-setup/docker-compose/` directory in your terminal and run: ```bash docker compose up -d @@ -29,13 +43,8 @@ This client is designed to work with a running instance of the OpenMBEE Flexo Sy ### 2. Install Client (Development) -```python -# Example: Add src to path if running scripts/notebooks from project root -import sys -import os -sys.path.insert(0, os.path.abspath('./src')) - -from sysmlv2_client import SysMLV2Client +```bash +python -m pip install -e ".[test]" ``` ## Basic Usage @@ -104,7 +113,6 @@ except Exception as e: ``` -See the [examples/basic_usage.ipynb](examples/basic_usage.ipynb) Jupyter Notebook for more detailed examples covering commits, branches, tags, and element retrieval/modification via commits. ## Running Tests @@ -112,7 +120,7 @@ Unit tests are implemented using `pytest` and `requests-mock`. 1. **Install Dependencies:** ```bash - pip install pytest requests requests-mock + python -m pip install -e ".[test]" ``` 2. **Run Tests:** Navigate to the project root directory in your terminal and run: ```bash diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..f3acdad --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,68 @@ +# Releasing sysml-api-client + +The distribution includes `sysmlv2_client` and `sysml_api`. The version comes +from `src/sysmlv2_client/__init__.py` (initial release: `0.1.0`). + +## One-time setup + +Register a pending Trusted Publisher from your PyPI organization's **Publishing** +page to create the project under organization ownership on the first upload. +If you manually create the project first, add a normal Trusted Publisher to +that project instead. + +| Publisher field | PyPI | TestPyPI | +| --- | --- | --- | +| Project name | `sysml-api-client` | `sysml-api-client` | +| GitHub owner | `Open-MBEE` | `Open-MBEE` | +| Repository | `sysmlv2-python-client` | `sysmlv2-python-client` | +| Workflow filename | `publish.yml` | `publish.yml` | +| Environment | `pypi` | `testpypi` | + +TestPyPI needs its own account and publisher configuration. Create GitHub +environments `pypi` and `testpypi`; configure required reviewers for `pypi` +and restrict it to release tags as appropriate. No PyPI token secret is needed. +Check that PyPI accepts the project name; a pending publisher does not reserve it. + +References: [organization pending publishers](https://blog.pypi.org/posts/2025-11-10-trusted-publishers-coming-to-orgs/), +[existing project publishers](https://docs.pypi.org/trusted-publishers/adding-a-publisher/). + +## Local validation + +Create and activate a virtual environment, then run: + +```bash +python -m pip install -e ".[test]" build twine +python -m pytest +python -m build +python -m twine check --strict dist/* +``` + +Start with an empty `dist/` directory. CI also tests the installed wheel outside +the checkout, imports both packages, and builds a wheel from the source archive. + +## Release + +1. Update `__version__` and `CHANGELOG.md`; each upload needs a new version. +2. Push the changes and wait for the test matrix and package checks to pass. +3. Run **Publish package** manually from the desired commit to upload to TestPyPI. + For repeated rehearsals use new versions, such as `0.1.0rc1`, `0.1.0rc2`. +4. In a clean virtual environment, verify the candidate (substitute its version): + + ```bash + python -m pip install requests + python -m pip install --index-url https://test.pypi.org/simple/ --no-deps sysml-api-client==0.1.0 + python -c "import sysml_api; from sysmlv2_client import SysMLV2Client" + ``` + +5. Publish a GitHub release tagged `v0.1.0` at the tested commit, substituting + the actual version. This triggers production publishing. Approve the `pypi` + environment deployment if configured. The workflow rejects mismatched tags. +6. Verify production installation in a fresh environment: + + ```bash + python -m pip install sysml-api-client==0.1.0 + python -c "import sysml_api; from sysmlv2_client import SysMLV2Client" + ``` + +Published files cannot be replaced on PyPI or TestPyPI. Increment the version +to correct a release. diff --git a/pyproject.toml b/pyproject.toml index 4b37c46..4a9b9b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,34 @@ [project] -name = "sysmlv2-python-client" -version = "0.1.0" -description = "Python client for SysML v2 services" -authors = [{name = "Your Name", email = "you@example.com"}] -dependencies = [] +name = "sysml-api-client" +dynamic = ["version"] +description = "Python client for the OMG Systems Modeling API and Services" +readme = "README.md" +requires-python = ">=3.10" +license = "Apache-2.0" +license-files = ["LICENSE"] +dependencies = ["requests>=2.28,<3"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", +] + +[project.urls] +Repository = "https://github.com/Open-MBEE/sysmlv2-python-client" +Issues = "https://github.com/Open-MBEE/sysmlv2-python-client/issues" +Specification = "https://www.omg.org/spec/SystemsModelingAPI" + +[project.optional-dependencies] +test = ["pytest>=7", "pytest-cov>=4", "requests-mock>=1.11"] + +[tool.setuptools.packages.find] +where = ["src"] +include = ["sysmlv2_client*", "sysml_api*"] +namespaces = false + +[tool.setuptools.dynamic] +version = {attr = "sysmlv2_client.__version__"} [build-system] -requires = ["setuptools>=61.0"] +requires = ["setuptools>=77.0.3"] build-backend = "setuptools.build_meta" diff --git a/src/sysml_api/api_lib.py b/src/sysml_api/api_lib.py index 2269efb..c3be767 100644 --- a/src/sysml_api/api_lib.py +++ b/src/sysml_api/api_lib.py @@ -1,4 +1,3 @@ -import numpy as np from sysmlv2_client import SysMLV2Client, SysMLV2Error, SysMLV2NotFoundError from typing import Any, Dict diff --git a/tests/test_client.py b/tests/test_client.py index b355b82..f7a4595 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -4,7 +4,7 @@ import requests_mock import requests -from src.sysmlv2_client import ( +from sysmlv2_client import ( SysMLV2Client, SysMLV2Error, SysMLV2AuthError, From 465ab8c49c4336af098b55998071be3d00811a92 Mon Sep 17 00:00:00 2001 From: wobrschalek Date: Sun, 4 Oct 2026 07:51:18 -0700 Subject: [PATCH 2/2] Update .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 3d8c3b0..c8fc5a3 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ flexo-setup/docker-compose/env/*.env /src/sysmlv2_python_client.egg-info /test-results coverage.xml +/.venv-wheel +/src/sysml_api_client.egg-info +/dist