Skip to content

Commit cc8aa9f

Browse files
RobPasMuepyansys-ci-botgithub-advanced-security[bot]
authored
feat: verifying backwards compatibility (#2124)
Co-authored-by: pyansys-ci-bot <[email protected]> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
1 parent f9ce7dd commit cc8aa9f

File tree

20 files changed

+606
-16
lines changed

20 files changed

+606
-16
lines changed
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
name: Backwards compatibility tests
2+
on:
3+
pull_request:
4+
workflow_dispatch:
5+
push:
6+
tags:
7+
- "*"
8+
branches:
9+
- main
10+
11+
env:
12+
MAIN_PYTHON_VERSION: '3.13'
13+
ANSRV_GEO_IMAGE: 'ghcr.io/ansys/geometry'
14+
ANSRV_GEO_PORT: 700
15+
ANSRV_GEO_LICENSE_SERVER: ${{ secrets.LICENSE_SERVER }}
16+
GEO_CONT_NAME: ans_geo
17+
IS_WORKFLOW_RUNNING: True
18+
19+
concurrency:
20+
group: ${{ github.workflow }}-${{ github.ref }}
21+
cancel-in-progress: true
22+
23+
permissions:
24+
contents: read
25+
packages: read
26+
id-token: write
27+
28+
jobs:
29+
30+
# =================================================================================================
31+
# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv RUNNING ON SELF-HOSTED RUNNER vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
32+
# =================================================================================================
33+
34+
testing-backwards-compatibility-windows:
35+
name: Windows ${{ matrix.backend-version }}
36+
runs-on: [self-hosted, Windows, pygeometry]
37+
strategy:
38+
fail-fast: false
39+
matrix:
40+
include:
41+
- image-tag: "windows-24.1"
42+
backend-version: "24.1"
43+
- image-tag: "windows-24.2"
44+
backend-version: "24.2"
45+
- image-tag: "windows-25.1"
46+
backend-version: "25.1"
47+
- image-tag: "core-windows-25.2"
48+
backend-version: "25.2"
49+
steps:
50+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
51+
52+
- name: Set up Python
53+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
54+
with:
55+
python-version: ${{ env.MAIN_PYTHON_VERSION }}
56+
57+
- name: Set up headless display
58+
uses: pyvista/setup-headless-display-action@7d84ae825e6d9297a8e99bdbbae20d1b919a0b19 # v4.2
59+
60+
- name: Create Python venv
61+
run: |
62+
python -m venv .venv
63+
.\.venv\Scripts\Activate.ps1
64+
65+
- name: Install packages for testing
66+
run: |
67+
.\.venv\Scripts\Activate.ps1
68+
python -m pip install --upgrade pip
69+
pip install --upgrade build wheel
70+
pip install .[tests]
71+
72+
- name: Login to GitHub Container Registry
73+
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
74+
with:
75+
registry: ghcr.io
76+
username: ${{ github.actor }}
77+
password: ${{ secrets.GITHUB_TOKEN }}
78+
79+
- name: Download Geometry service container (if needed)
80+
run: docker pull ${{ env.ANSRV_GEO_IMAGE }}:${{ matrix.image-tag }}
81+
82+
- name: Check location of self-hosted runner and define license server accordingly
83+
if: runner.name == 'pygeometry-ci-2'
84+
run:
85+
echo "ANSRV_GEO_LICENSE_SERVER=${{ secrets.INTERNAL_LICENSE_SERVER }}" | Out-File -FilePath $env:GITHUB_ENV -Append
86+
87+
- name: Stop any running containers
88+
run: |
89+
$dockerContainers = docker ps -a -q
90+
if (-not [string]::IsNullOrEmpty($dockerContainers)) {
91+
docker stop $dockerContainers
92+
docker rm $dockerContainers
93+
}
94+
95+
- name: Start Geometry service and verify start
96+
run: |
97+
.\.venv\Scripts\Activate.ps1
98+
docker run --detach --name ${{ env.GEO_CONT_NAME }} -e LICENSE_SERVER=${{ env.ANSRV_GEO_LICENSE_SERVER }} -p ${{ env.ANSRV_GEO_PORT }}:50051 ${{ env.ANSRV_GEO_IMAGE }}:${{ matrix.image-tag }}
99+
python -c "from ansys.geometry.core.connection.validate import validate; validate()"
100+
101+
- name: Testing
102+
run: |
103+
.\.venv\Scripts\Activate.ps1
104+
pytest -v --backwards-compatibility=yes --backend-version=${{ matrix.backend-version }} -rf
105+
106+
- name: Upload coverage to Codecov
107+
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
108+
env:
109+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
110+
with:
111+
files: .cov/xml
112+
113+
- name: Stop the Geometry service
114+
if: always()
115+
run: |
116+
docker stop ${{ env.GEO_CONT_NAME }}
117+
docker logs ${{ env.GEO_CONT_NAME }}
118+
docker rm ${{ env.GEO_CONT_NAME }}
119+
120+
- name: Stop any remaining containers
121+
if: always()
122+
run: |
123+
$dockerContainers = docker ps -a -q
124+
if (-not [string]::IsNullOrEmpty($dockerContainers)) {
125+
docker stop $dockerContainers
126+
docker rm $dockerContainers
127+
}
128+
129+
# =================================================================================================
130+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUNNING ON SELF-HOSTED RUNNER ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
131+
# =================================================================================================

.github/workflows/ci_cd.yml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,10 @@ jobs:
320320
docker rm $dockerContainers
321321
}
322322
323+
# =================================================================================================
324+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUNNING ON SELF-HOSTED RUNNER ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
325+
# =================================================================================================
326+
323327
docs:
324328
name: Documentation
325329
needs: [docs-style]
@@ -356,11 +360,6 @@ jobs:
356360
docker logs ${{ env.GEO_CONT_NAME }}
357361
docker rm ${{ env.GEO_CONT_NAME }}
358362
359-
360-
# =================================================================================================
361-
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUNNING ON SELF-HOSTED RUNNER ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
362-
# =================================================================================================
363-
364363
testing-linux:
365364
name: Testing and coverage (Linux)
366365
needs: [smoke-tests, manifests]

doc/changelog.d/2124.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Verifying backwards compatibility

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ tests = [
7373
"pytest-cov==6.2.1",
7474
"pytest-pyvista==0.2.0",
7575
"pyvista[jupyter]==0.45.3",
76+
"pyyaml==6.0.2",
7677
"requests==2.32.4",
7778
"scipy==1.15.3",
7879
"semver==3.0.4",
@@ -83,6 +84,7 @@ tests-minimal = [
8384
"pytest==8.4.1",
8485
"pytest-cov==6.2.1",
8586
"pytest-pyvista==0.2.0",
87+
"pyyaml==6.0.2",
8688
]
8789
doc = [
8890
"ansys-sphinx-theme[autoapi]==1.5.3",

src/ansys/geometry/core/_grpc/_services/v0/bodies.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from ..base.bodies import GRPCBodyService
3131
from ..base.conversions import from_measurement_to_server_angle, from_measurement_to_server_length
3232
from .conversions import (
33+
_nurbs_curves_compatibility,
3334
build_grpc_id,
3435
from_frame_to_grpc_frame,
3536
from_grpc_material_to_material,
@@ -102,6 +103,10 @@ def create_extruded_body(self, **kwargs) -> dict: # noqa: D102
102103
),
103104
)
104105

106+
# HACK: we should inform the user that NURBS curve sketches are not supported
107+
# prior to 26R1... and if passed, raise an error
108+
_nurbs_curves_compatibility(kwargs["backend_version"], request.geometries)
109+
105110
# Call the gRPC service
106111
resp = self.stub.CreateExtrudedBody(request=request)
107112

@@ -128,6 +133,10 @@ def create_sweeping_profile_body(self, **kwargs) -> dict: # noqa: D102
128133
path=[from_trimmed_curve_to_grpc_trimmed_curve(tc) for tc in kwargs["path"]],
129134
)
130135

136+
# HACK: we should inform the user that NURBS curve sketches are not supported
137+
# prior to 26R1... and if passed, raise an error
138+
_nurbs_curves_compatibility(kwargs["backend_version"], request.geometries)
139+
131140
# Call the gRPC service
132141
resp = self.stub.CreateSweepingProfile(request=request)
133142

@@ -229,6 +238,10 @@ def create_planar_body(self, **kwargs) -> dict: # noqa: D102
229238
),
230239
)
231240

241+
# HACK: we should inform the user that NURBS curve sketches are not supported
242+
# prior to 26R1... and if passed, raise an error
243+
_nurbs_curves_compatibility(kwargs["backend_version"], request.geometries)
244+
232245
# Call the gRPC service
233246
resp = self.stub.CreatePlanarBody(request=request)
234247

src/ansys/geometry/core/_grpc/_services/v0/conversions.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@
5555
TrimmedCurve as GRPCTrimmedCurve,
5656
TrimmedSurface as GRPCTrimmedSurface,
5757
)
58+
from ansys.geometry.core.errors import GeometryRuntimeError
5859
from ansys.geometry.core.misc.checks import graphics_required
5960

6061
if TYPE_CHECKING: # pragma: no cover
6162
import pyvista as pv
63+
import semver
6264

6365
from ansys.geometry.core.connection.backend import BackendType
6466
from ansys.geometry.core.designer.design import DesignFileFormat
@@ -1136,3 +1138,26 @@ def from_material_to_grpc_material(
11361138
for property in material.properties.values()
11371139
],
11381140
)
1141+
1142+
1143+
def _nurbs_curves_compatibility(backend_version: "semver.Version", grpc_geometries: GRPCGeometries):
1144+
"""Check if the backend version is compatible with NURBS curves in sketches.
1145+
1146+
Parameters
1147+
----------
1148+
backend_version : semver.Version
1149+
The version of the backend.
1150+
grpc_geometries : GRPCGeometries
1151+
The gRPC geometries potentially containing NURBS curves.
1152+
1153+
Raises
1154+
------
1155+
GeometryRuntimeError
1156+
If the backend version is lower than 26.1.0 and NURBS curves are present.
1157+
"""
1158+
if grpc_geometries.nurbs_curves and backend_version < (26, 1, 0):
1159+
raise GeometryRuntimeError(
1160+
"The usage of NURBS in sketches requires a minimum Ansys release version of " # noqa: E501
1161+
+ "26.1.0, but the current version used is "
1162+
+ f"{backend_version}."
1163+
)

src/ansys/geometry/core/_grpc/_services/v0/measurement_tools.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,19 @@ def min_distance_between_objects(self, **kwargs) -> dict: # noqa: D102
5656
# Request is different based on backend_version (25.2 vs. earlier)
5757
if kwargs["backend_version"] < (25, 2, 0):
5858
request = MinDistanceBetweenObjectsRequest(bodies=kwargs["selection"])
59+
60+
# Call the gRPC service
61+
response = self.stub.MinDistanceBetweenObjects(request)
62+
5963
else:
6064
from ansys.api.dbu.v0.dbumodels_pb2 import EntityIdentifier
6165

6266
request = MinDistanceBetweenObjectsRequest(
6367
selection=[EntityIdentifier(id=item) for item in kwargs["selection"]]
6468
)
6569

66-
# Call the gRPC service
67-
response = self.stub.MinDistanceBetweenSelectionObjects(request)
70+
# Call the gRPC service
71+
response = self.stub.MinDistanceBetweenSelectionObjects(request)
6872

6973
# Return the response - formatted as a dictionary
7074
return {"distance": to_distance(response.gap.distance)}

src/ansys/geometry/core/designer/component.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,14 @@ def set_name(self, name: str) -> None:
299299
self._name = name
300300

301301
@property
302+
@min_backend_version(25, 1, 0)
302303
def instance_name(self) -> str:
303-
"""Name of the component instance."""
304+
"""Name of the component instance.
305+
306+
Warnings
307+
--------
308+
This method is only available starting on Ansys release 25R1.
309+
"""
304310
return self._instance_name
305311

306312
@property
@@ -597,6 +603,7 @@ def extrude_sketch(
597603
sketch=sketch,
598604
distance=distance,
599605
direction=direction.get_multiplier(),
606+
backend_version=self._grpc_client.backend_version,
600607
)
601608
created_body = self.__build_body_from_response(response)
602609

@@ -656,6 +663,7 @@ def sweep_sketch(
656663
parent_id=self.id,
657664
sketch=sketch,
658665
path=path,
666+
backend_version=self._grpc_client.backend_version,
659667
)
660668
return self.__build_body_from_response(response)
661669

@@ -811,7 +819,7 @@ def extrude_face(
811819

812820
@check_input_types
813821
@ensure_design_is_active
814-
@min_backend_version(24, 2, 0)
822+
@min_backend_version(25, 1, 0)
815823
def create_sphere(self, name: str, center: Point3D, radius: Distance) -> Body:
816824
"""Create a sphere body defined by the center point and the radius.
817825
@@ -831,7 +839,7 @@ def create_sphere(self, name: str, center: Point3D, radius: Distance) -> Body:
831839
832840
Warnings
833841
--------
834-
This method is only available starting on Ansys release 24R2.
842+
This method is only available starting on Ansys release 25R1.
835843
"""
836844
self._grpc_client.log.debug(f"Creating a sphere body on {self.id}.")
837845
response = self._grpc_client.services.bodies.create_sphere_body(
@@ -925,7 +933,10 @@ def create_surface(self, name: str, sketch: Sketch) -> Body:
925933
f"Creating planar surface from sketch provided on {self.id}. Creating body..."
926934
)
927935
response = self._grpc_client.services.bodies.create_planar_body(
928-
name=name, parent_id=self.id, sketch=sketch
936+
name=name,
937+
parent_id=self.id,
938+
sketch=sketch,
939+
backend_version=self._grpc_client.backend_version,
929940
)
930941

931942
return self.__build_body_from_response(response)

src/ansys/geometry/core/designer/design.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1126,7 +1126,9 @@ def __read_existing_design(self) -> None:
11261126
mp = MaterialProperty(mp_type, property.display_name, mp_quantity)
11271127
properties.append(mp)
11281128
if mp.type == MaterialPropertyType.DENSITY:
1129-
density = mp.quantity
1129+
density = (
1130+
mp.quantity if isinstance(mp.quantity, Quantity) else Quantity(mp.quantity)
1131+
)
11301132

11311133
m = Material(material.name, density, properties)
11321134
self.materials.append(m)

src/ansys/geometry/core/designer/geometry_commands.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,8 @@ def rename_object(
585585
)
586586
return result.success
587587

588+
@protect_grpc
589+
@min_backend_version(25, 2, 0)
588590
def create_linear_pattern(
589591
self,
590592
selection: Union["Face", list["Face"]],
@@ -618,6 +620,10 @@ def create_linear_pattern(
618620
-------
619621
bool
620622
``True`` when successful, ``False`` when failed.
623+
624+
Warnings
625+
--------
626+
This method is only available starting on Ansys release 25R2.
621627
"""
622628
from ansys.geometry.core.designer.face import Face
623629

@@ -1173,6 +1179,8 @@ def revolve_faces_by_helix(
11731179
self._grpc_client.log.info("Failed to revolve faces.")
11741180
return []
11751181

1182+
@protect_grpc
1183+
@min_backend_version(25, 2, 0)
11761184
def replace_face(
11771185
self,
11781186
target_selection: Union["Face", list["Face"]],
@@ -1191,6 +1199,10 @@ def replace_face(
11911199
-------
11921200
bool
11931201
``True`` when successful, ``False`` when failed.
1202+
1203+
Warnings
1204+
--------
1205+
This method is only available starting on Ansys release 25R2.
11941206
"""
11951207
target_selection: list["Face"] = (
11961208
target_selection if isinstance(target_selection, list) else [target_selection]

0 commit comments

Comments
 (0)