Skip to content

feat: add public release capsule/pipeline API - #75

Open
spirpinias wants to merge 3 commits into
mainfrom
add-public-release-capsule-pipeline-api-sc-198066
Open

feat: add public release capsule/pipeline API#75
spirpinias wants to merge 3 commits into
mainfrom
add-public-release-capsule-pipeline-api-sc-198066

Conversation

@spirpinias

Copy link
Copy Markdown
Contributor

Add release_capsule / release_pipeline SDK methods wrapping the new POST /capsules/{id}/release and POST /pipelines/{id}/release endpoints, plus CapsuleReleaseResults / ReleaseVersion models. Bump version to 0.17.0 and MIN_SERVER_VERSION to 4.8.0.

Add release_capsule / release_pipeline SDK methods wrapping the new
POST /capsules/{id}/release and POST /pipelines/{id}/release endpoints,
plus CapsuleReleaseResults / ReleaseVersion models. Bump version to
0.17.0 and MIN_SERVER_VERSION to 4.8.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@spirpinias
spirpinias requested a review from zvikagart August 13, 2026 13:27

@zvikagart zvikagart left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is idiomatic and matches the existing sync_capsule / sync_pipeline pattern, but it models a backend contract that no longer exists: it tracks the first BE commit (OceanCodes/backend#278) and was never updated for the refinements in OceanCodes/backend#288 (same story, sc-198066).

Blockers

  1. The 200 body is a job, not a flags object — the spec returns CapsuleReleaseJob (job_id + status required, plus started/duration/error and, on completion only, release_capsule/release_version).
  2. GET /{capsules,pipelines}/{id}/release/{job_id} is missing from the SDK — that is the actual polling mechanism (routes.go, pipelines twin).
  3. The unmet-requirement flags now come back as 403 CapsuleReleaseValidationIssues with renamed, inverted, issue-only fields — unmodeled here, and the names in this PR are the pre-rename ones.

Details inline.

Comment thread src/codeocean/models/capsule.py Outdated

@dataclass_json
@dataclass(frozen=True)
class CapsuleReleaseResults:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocker — this is not the 200 response shape. POST /release returns CapsuleReleaseJob:

field type required
job_id string yes
status CapsuleReleaseJobStatus (created/started/completed/failed/canceled/canceling) yes
started int64
duration int
release_capsule string
release_version Version
error string

None of job_id, status, started, duration, error are modeled here, and the ten validation flags were removed from the success body entirely. Against a real 4.8 server every flag deserializes to None, and release_capsule/release_version are also empty — the handler only populates them once the job completes, not on the POST. So release_capsule() returns an all-None object and silently drops the one field that matters.

Also needed: a CapsuleReleaseJobStatus StrEnum alongside CapsuleStatus, and the class should be renamed CapsuleReleaseJob to match the spec.

Second blocker, same model: the unmet-requirement flags now arrive as 403 CapsuleReleaseValidationIssues (handler), present only when true, with problem-oriented names: missing_reproducible_run, uncommitted_files, missing_metadata, non_default_branch, git_out_of_sync, unreleased_pipeline_capsules, missing_release_functionality, invalid_app_panel, unreleased_post_run_capsule. The names here are all pre-rename and inverted in polarity. Since _error_handler raises on 403, they're currently reachable only as an untyped Error.data dict — worth a typed model on the error path.

Comment thread src/codeocean/models/capsule.py Outdated
default=None,
metadata={"description": "Whether the required metadata is present"},
)
no_credentials: Optional[bool] = dataclass_field(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no_credentials was dropped outright in OceanCodes/backend#288 — it is not a release blocker, and no equivalent exists in CapsuleReleaseValidationIssues.

Comment thread src/codeocean/models/capsule.py Outdated

@dataclass_json
@dataclass(frozen=True)
class ReleaseVersion:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This duplicates the spec's shared Version schema, which is also what Capsule.versions holds — currently typed Optional[list[dict]] above. Name it Version after the spec and reuse it for both, rather than adding a release-only twin.

Also, the spec marks major_version / minor_version / release_time as required; default=0 here makes them optional. Capsule models required fields without defaults — follow that.

Comment thread src/codeocean/capsule.py Outdated

return GitSyncResults.from_dict(res.json())

def release_capsule(self, capsule_id: str) -> CapsuleReleaseResults:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocker — the polling story in this docstring isn't the contract. "poll the release capsule and watch for a version higher than the returned release_version" was the pre-#288 design. The real mechanism is GET /capsules/{capsule_id}/release/{job_id} (route), and it isn't wrapped here at all — so with the current return type there's no job_id and the caller cannot poll.

Please add get_release_job(capsule_id, job_id) (plus the pipeline delegate), and ideally a wait_until_completed mirroring Computations.wait_until_completed.

Worth documenting too: a 400 when the capsule was never released (which the docstring covers) and a 403 carrying the validation issues (which it doesn't).

Comment thread src/codeocean/capsule.py Outdated
)
# Re-exports for backward compatibility
from codeocean.models.capsule import ( # noqa: F401
ReleaseVersion,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block is # Re-exports for backward compatibility — names that predate the models/ split. A brand-new type belongs in the regular import list above it.

Comment thread src/codeocean/pipeline.py Outdated
"""Sync a pipeline with its linked external Git repository."""
return self._capsules.sync_capsule(pipeline_id)

def release_pipeline(self, pipeline_id: str) -> CapsuleReleaseResults:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same stale polling advice as release_capsule — the pipeline twin is GET /pipelines/{pipeline_id}/release/{job_id}, which needs a delegate here.

Comment thread tests/test_release.py Outdated
return session

def test_release_capsule_returns_results(self):
"""release_capsule posts to the capsule release route and parses the results."""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style matches test_git_sync.py nicely — but these assert the stale contract, so they pass green while the client is wrong against a 4.8 server. After the model rewrite they should cover the job response (job_id + status), the completed-job GET carrying release_capsule/release_version, and a 403 validation-issues case.

Comment thread CHANGELOG.md Outdated
=========

## 0.17.0 (2026-08-13)
- feat: add release support for capsules and pipelines (`release_capsule` / `release_pipeline`)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing the PR link that every prior entry carries, e.g. - [#75](https://github.com/codeocean/codeocean-sdk-python/pull/75) feat: ....

Address PR review: the release API returns an asynchronous CapsuleReleaseJob
(job_id + status), not a flags object, and is polled via
GET .../release/{job_id}. Unmet requirements come back as 403
CapsuleReleaseValidationIssues.

- Replace CapsuleReleaseResults/ReleaseVersion with CapsuleReleaseJob,
  CapsuleReleaseJobStatus, and a shared Version model (now also used by
  Capsule.versions); drop the removed no_credentials check.
- Add CapsuleReleaseValidationIssues for the 403 error-path body.
- Add get_release_job and wait_until_release_completed (plus pipeline
  delegates); release_capsule/release_pipeline now return the job.
- Move new types out of the backward-compat re-export block.
- Rewrite tests for the job response, completed-job GET, and validation issues.
- Add PR link to the CHANGELOG entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread src/codeocean/models/capsule.py Outdated
release_time: int = dataclass_field(
metadata={"description": "Release time (int64 timestamp, seconds)"},
)
doi: Optional[str] = dataclass_field(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost certain DOI is not in use in VPCs so we can remove

Comment thread src/codeocean/models/capsule.py Outdated
versions: Optional[list[Version]] = dataclass_field(
default=None,
metadata={
"description": "Capsule versions with major_version, minor_version, release_time, and DOI"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"description": "Capsule versions with major_version, minor_version, release_time, and DOI"
"description": "Capsule versions with major and minor version, and release time"

Comment thread CHANGELOG.md Outdated
CHANGELOG
=========

## 0.17.0 (2026-08-13)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We hold off adding the change log until we release, but if already added:

Suggested change
## 0.17.0 (2026-08-13)
## 0.17.0 (TBD)

Comment thread CHANGELOG.md Outdated
=========

## 0.17.0 (2026-08-13)
- [#75](https://github.com/codeocean/codeocean-sdk-python/pull/75) feat: add release support for capsules and pipelines (`release_capsule` / `release_pipeline`, `get_release_job`, `wait_until_release_completed`)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR name

Suggested change
- [#75](https://github.com/codeocean/codeocean-sdk-python/pull/75) feat: add release support for capsules and pipelines (`release_capsule` / `release_pipeline`, `get_release_job`, `wait_until_release_completed`)
- [#75](https://github.com/codeocean/codeocean-sdk-python/pull/75) feat: add public release capsule/pipeline API

- Remove doi from Version (not used in VPCs, per review) and drop it from
  the Capsule.versions description; update the release-job test accordingly.
- CHANGELOG: mark 0.17.0 as (TBD) and use the PR title for the entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants