diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f7014c35..a7130553 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.11.0" + ".": "0.12.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index e93723a0..f0b85f43 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 193 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-3dcdbd68ce4b336149d28d17ab08f211538ed6630112ae4883af2f6680643159.yml -openapi_spec_hash: 7e4333995b65cf32663166801e2444bb -config_hash: 8d7b241284195a8c51f5d670fbbe0ab4 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-803e9a382bb3f4d9b6ef7b1dd12c5541bf93608d3f4c240e99054929130f260b.yml +openapi_spec_hash: e2eae7e0a5a1fc83f3af40662b1ffbd1 +config_hash: d73914a733b27d121d59aa43bc7c710e diff --git a/CHANGELOG.md b/CHANGELOG.md index ddc444e4..8c4ff484 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 0.12.0 (2026-04-10) + +Full Changelog: [v0.11.0...v0.12.0](https://github.com/gitpod-io/gitpod-sdk-python/compare/v0.11.0...v0.12.0) + +### Features + +* **api:** add old_path field to ContentGitChangedFile ([d79d4d4](https://github.com/gitpod-io/gitpod-sdk-python/commit/d79d4d49ef41a889a81a0d484616fca442849356)) +* **api:** remove terminal field from RunsOn type ([faca2b2](https://github.com/gitpod-io/gitpod-sdk-python/commit/faca2b27b88f17a759bd91c305e5ea2a856a1e2c)) + + +### Bug Fixes + +* **client:** preserve hardcoded query params when merging with user params ([b7f0b1d](https://github.com/gitpod-io/gitpod-sdk-python/commit/b7f0b1d27ef51872bf81541dd7f81a8101f856af)) +* ensure file data are only sent as 1 parameter ([5c02854](https://github.com/gitpod-io/gitpod-sdk-python/commit/5c02854efdc2874535d3d7867823048d5e8d4693)) + + +### Documentation + +* **api:** update trigger usage note in AutomationTrigger ([5a292cb](https://github.com/gitpod-io/gitpod-sdk-python/commit/5a292cb1ef87a0dd73d93445707412d25c0e95e0)) + ## 0.11.0 (2026-04-02) Full Changelog: [v0.10.0...v0.11.0](https://github.com/gitpod-io/gitpod-sdk-python/compare/v0.10.0...v0.11.0) diff --git a/pyproject.toml b/pyproject.toml index 5d1671eb..0528316d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "gitpod-sdk" -version = "0.11.0" +version = "0.12.0" description = "The official Python library for the gitpod API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/gitpod/_base_client.py b/src/gitpod/_base_client.py index cc7f8af2..a9da58f9 100644 --- a/src/gitpod/_base_client.py +++ b/src/gitpod/_base_client.py @@ -540,6 +540,10 @@ def _build_request( files = cast(HttpxRequestFiles, ForceMultipartDict()) prepared_url = self._prepare_url(options.url) + # preserve hard-coded query params from the url + if params and prepared_url.query: + params = {**dict(prepared_url.params.items()), **params} + prepared_url = prepared_url.copy_with(raw_path=prepared_url.raw_path.split(b"?", 1)[0]) if "_" in prepared_url.host: # work around https://github.com/encode/httpx/discussions/2880 kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} diff --git a/src/gitpod/_utils/_utils.py b/src/gitpod/_utils/_utils.py index eec7f4a1..63b8cd60 100644 --- a/src/gitpod/_utils/_utils.py +++ b/src/gitpod/_utils/_utils.py @@ -86,8 +86,9 @@ def _extract_items( index += 1 if is_dict(obj): try: - # We are at the last entry in the path so we must remove the field - if (len(path)) == index: + # Remove the field if there are no more dict keys in the path, + # only "" traversal markers or end. + if all(p == "" for p in path[index:]): item = obj.pop(key) else: item = obj[key] diff --git a/src/gitpod/_version.py b/src/gitpod/_version.py index 7504df3f..6521e93a 100644 --- a/src/gitpod/_version.py +++ b/src/gitpod/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "gitpod" -__version__ = "0.11.0" # x-release-please-version +__version__ = "0.12.0" # x-release-please-version diff --git a/src/gitpod/types/environment_status.py b/src/gitpod/types/environment_status.py index 45ad12ca..d5ed5c65 100644 --- a/src/gitpod/types/environment_status.py +++ b/src/gitpod/types/environment_status.py @@ -91,6 +91,12 @@ class ContentGitChangedFile(BaseModel): ] = FieldInfo(alias="changeType", default=None) """ChangeType is the type of change that happened to the file""" + old_path: Optional[str] = FieldInfo(alias="oldPath", default=None) + """ + old_path is the previous path of the file before a rename or copy. Only set when + change_type is RENAMED or COPIED. + """ + path: Optional[str] = None """path is the path of the file""" diff --git a/src/gitpod/types/shared/automation_trigger.py b/src/gitpod/types/shared/automation_trigger.py index 8ffd732b..3d1fafb8 100644 --- a/src/gitpod/types/shared/automation_trigger.py +++ b/src/gitpod/types/shared/automation_trigger.py @@ -20,7 +20,7 @@ class AutomationTrigger(BaseModel): The `prebuild` field starts the automation during a prebuild of an environment. This phase does not have user secrets available. The `before_snapshot` field triggers the automation after all prebuild tasks complete but before the snapshot is taken. This is used for tasks that need to run last during prebuilds, such as IDE warmup. - Note: The prebuild and before_snapshot triggers can only be used with tasks, not services. + Note: The before_snapshot trigger can only be used with tasks, not services. """ before_snapshot: Optional[bool] = FieldInfo(alias="beforeSnapshot", default=None) diff --git a/src/gitpod/types/shared/runs_on.py b/src/gitpod/types/shared/runs_on.py index 5f26ef47..33cf1bd5 100644 --- a/src/gitpod/types/shared/runs_on.py +++ b/src/gitpod/types/shared/runs_on.py @@ -18,9 +18,3 @@ class RunsOn(BaseModel): machine: Optional[object] = None """Machine runs the service/task directly on the VM/machine level.""" - - terminal: Optional[object] = None - """ - Terminal runs the service inside a managed PTY terminal in the devcontainer. - Users can attach to the terminal interactively via the terminal API. - """ diff --git a/src/gitpod/types/shared_params/automation_trigger.py b/src/gitpod/types/shared_params/automation_trigger.py index 27dc462b..1020f005 100644 --- a/src/gitpod/types/shared_params/automation_trigger.py +++ b/src/gitpod/types/shared_params/automation_trigger.py @@ -20,7 +20,7 @@ class AutomationTrigger(TypedDict, total=False): The `prebuild` field starts the automation during a prebuild of an environment. This phase does not have user secrets available. The `before_snapshot` field triggers the automation after all prebuild tasks complete but before the snapshot is taken. This is used for tasks that need to run last during prebuilds, such as IDE warmup. - Note: The prebuild and before_snapshot triggers can only be used with tasks, not services. + Note: The before_snapshot trigger can only be used with tasks, not services. """ before_snapshot: Annotated[bool, PropertyInfo(alias="beforeSnapshot")] diff --git a/src/gitpod/types/shared_params/runs_on.py b/src/gitpod/types/shared_params/runs_on.py index 0182710a..8fc0fd3c 100644 --- a/src/gitpod/types/shared_params/runs_on.py +++ b/src/gitpod/types/shared_params/runs_on.py @@ -20,9 +20,3 @@ class RunsOn(TypedDict, total=False): machine: object """Machine runs the service/task directly on the VM/machine level.""" - - terminal: object - """ - Terminal runs the service inside a managed PTY terminal in the devcontainer. - Users can attach to the terminal interactively via the terminal API. - """ diff --git a/tests/api_resources/environments/automations/test_services.py b/tests/api_resources/environments/automations/test_services.py index 3a80cc01..3d1ccf73 100644 --- a/tests/api_resources/environments/automations/test_services.py +++ b/tests/api_resources/environments/automations/test_services.py @@ -75,7 +75,6 @@ def test_method_create_with_all_params(self, client: Gitpod) -> None: "image": "x", }, "machine": {}, - "terminal": {}, }, "session": "session", "spec_version": "specVersion", @@ -188,7 +187,6 @@ def test_method_update_with_all_params(self, client: Gitpod) -> None: "image": "x", }, "machine": {}, - "terminal": {}, }, }, status={ @@ -437,7 +435,6 @@ async def test_method_create_with_all_params(self, async_client: AsyncGitpod) -> "image": "x", }, "machine": {}, - "terminal": {}, }, "session": "session", "spec_version": "specVersion", @@ -550,7 +547,6 @@ async def test_method_update_with_all_params(self, async_client: AsyncGitpod) -> "image": "x", }, "machine": {}, - "terminal": {}, }, }, status={ diff --git a/tests/api_resources/environments/automations/test_tasks.py b/tests/api_resources/environments/automations/test_tasks.py index 29599ec1..242dc25e 100644 --- a/tests/api_resources/environments/automations/test_tasks.py +++ b/tests/api_resources/environments/automations/test_tasks.py @@ -71,7 +71,6 @@ def test_method_create_with_all_params(self, client: Gitpod) -> None: "image": "x", }, "machine": {}, - "terminal": {}, }, }, ) @@ -178,7 +177,6 @@ def test_method_update_with_all_params(self, client: Gitpod) -> None: "image": "x", }, "machine": {}, - "terminal": {}, }, }, ) @@ -377,7 +375,6 @@ async def test_method_create_with_all_params(self, async_client: AsyncGitpod) -> "image": "x", }, "machine": {}, - "terminal": {}, }, }, ) @@ -484,7 +481,6 @@ async def test_method_update_with_all_params(self, async_client: AsyncGitpod) -> "image": "x", }, "machine": {}, - "terminal": {}, }, }, ) diff --git a/tests/api_resources/environments/test_automations.py b/tests/api_resources/environments/test_automations.py index bed1aa5a..15f3fe3a 100644 --- a/tests/api_resources/environments/test_automations.py +++ b/tests/api_resources/environments/test_automations.py @@ -44,7 +44,6 @@ def test_method_upsert_with_all_params(self, client: Gitpod) -> None: "image": "x", }, "machine": {}, - "terminal": {}, }, "triggered_by": ["postDevcontainerStart"], } @@ -61,7 +60,6 @@ def test_method_upsert_with_all_params(self, client: Gitpod) -> None: "image": "x", }, "machine": {}, - "terminal": {}, }, "triggered_by": ["postEnvironmentStart"], } @@ -126,7 +124,6 @@ async def test_method_upsert_with_all_params(self, async_client: AsyncGitpod) -> "image": "x", }, "machine": {}, - "terminal": {}, }, "triggered_by": ["postDevcontainerStart"], } @@ -143,7 +140,6 @@ async def test_method_upsert_with_all_params(self, async_client: AsyncGitpod) -> "image": "x", }, "machine": {}, - "terminal": {}, }, "triggered_by": ["postEnvironmentStart"], } diff --git a/tests/test_client.py b/tests/test_client.py index 92b0eea3..c105b4d9 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -438,6 +438,30 @@ def test_default_query_option(self) -> None: client.close() + def test_hardcoded_query_params_in_url(self, client: Gitpod) -> None: + request = client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/foo?beta=true", + params={"limit": "10", "page": "abc"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/files/a%2Fb?beta=true", + params={"limit": "10"}, + ) + ) + assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" + def test_request_extra_json(self, client: Gitpod) -> None: request = client._build_request( FinalRequestOptions( @@ -1363,6 +1387,30 @@ async def test_default_query_option(self) -> None: await client.close() + async def test_hardcoded_query_params_in_url(self, async_client: AsyncGitpod) -> None: + request = async_client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true"} + + request = async_client._build_request( + FinalRequestOptions( + method="get", + url="/foo?beta=true", + params={"limit": "10", "page": "abc"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} + + request = async_client._build_request( + FinalRequestOptions( + method="get", + url="/files/a%2Fb?beta=true", + params={"limit": "10"}, + ) + ) + assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" + def test_request_extra_json(self, client: Gitpod) -> None: request = client._build_request( FinalRequestOptions( diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py index 0ca5a8dc..a9d8b167 100644 --- a/tests/test_extract_files.py +++ b/tests/test_extract_files.py @@ -35,6 +35,15 @@ def test_multiple_files() -> None: assert query == {"documents": [{}, {}]} +def test_top_level_file_array() -> None: + query = {"files": [b"file one", b"file two"], "title": "hello"} + assert extract_files(query, paths=[["files", ""]]) == [ + ("files[]", b"file one"), + ("files[]", b"file two"), + ] + assert query == {"title": "hello"} + + @pytest.mark.parametrize( "query,paths,expected", [