Skip to content

feat: add template versioning with tags support for JS and Python SDKs - #1080

Merged
dobrac merged 43 commits into
mainfrom
feat/template-versioning-tags
Jan 27, 2026
Merged

feat: add template versioning with tags support for JS and Python SDKs#1080
dobrac merged 43 commits into
mainfrom
feat/template-versioning-tags

Conversation

@dobrac

@dobrac dobrac commented Jan 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Add template versioning support using tags to the JS and Python SDKs. Templates can now be built with multiple tags (in name:tag format), and tags can be assigned or deleted from existing builds.

Features

Template Building with Tags

Build templates with one or more tags to enable versioning and environment-based deployments.

  • Supports single string, array of strings, or options object
  • Multiple tags can reference the same build artifact
  • Backward compatible with existing alias parameter

Tag Management

Manage tags on existing template builds:

  • assignTag - Assign new tag(s) to an existing build
  • removeTag - Remove a tag from a template

SDK Usage

JavaScript/TypeScript

// Build with single tag
await Template.build(tmpl, "web-server:v1.0")

// Build with multiple tags
await Template.build(tmpl, "web-server", { tags: ["v1.1.0", "stable"] })

// Assign tag(s) to existing build
await Template.assignTags("web-server:v1.1.0", "production")
await Template.assignTags("web-server:v1.1.0", ["production", "latest"])

// Remove a tag
await Template.removeTags("web-server", "production")

Python

# Build with single tag
Template.build(config, "web-server:v1.0")

# Build with multiple tags
Template.build(config, "web-server", tags=["v1.1.0", "stable"])

# Assign tag(s) to existing build
Template.assign_tags("web-server:v1.1.0", "production")
Template.assign_tags("web-server:v1.1.0", tags=["production", "default"])

# Remove a tag
Template.remove_tags("web-server", "production")

Based on infra PR #1524


Note

Introduces tag-based template versioning across JS and Python SDKs and updates the build API.

  • Template builds now accept name or name:tag; multiple tags supported; alias remains for backward compatibility (normalized internally)
  • New tag management APIs: Template.assignTags()/assign_tags() and Template.removeTags()/remove_tags(); adds /templates/tags endpoints and generated models/types
  • JS SDK: refactors build request to name/tags, maps build status/reason/log entries to new types, exposes tag helpers
  • Python SDK: parallel changes in async/sync APIs, adds status/reason dataclasses, tag helpers, and argument normalization
  • CLI templates/tests updated to new Template.build(template, "name") signature; extensive unit/integration tests added
  • OpenAPI schema/codegen updated to include tags and deprecations (e.g., alias in v3 request), scripts include tags spec

Written by Cursor Bugbot for commit c8f54f9. This will update automatically on new commits. Configure here.

- Add Template.assignTag() and Template.deleteTag() methods
- Update Template.build() to accept names as second parameter (string or array)
- Support 'alias:tag' format for template versioning
- Maintain backward compatibility with deprecated alias option
- Add TagInfo type for tag assignment responses
- Update BuildInfo to include names array
- Add tests for new tag methods
- Regenerate API clients with tags endpoints

Based on infra PR #1524

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@dobrac dobrac added the feature New feature or request label Jan 13, 2026
@changeset-bot

changeset-bot Bot commented Jan 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c8f54f9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@e2b/cli Patch
e2b Minor
@e2b/python-sdk Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

dobrac and others added 3 commits January 13, 2026 13:54
- Add Template.assignTag() and Template.deleteTag() methods
- Update Template.build() to accept names as second parameter (string or array)
- Support 'alias:tag' format for template versioning
- Maintain backward compatibility with deprecated alias option
- Add TagInfo type for tag assignment responses
- Update BuildInfo to include names array
- Add tests for new tag methods
- Regenerate API clients with tags endpoints

Based on infra PR #1524

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…dation

Let the API validate the mutual exclusivity of names and alias fields
instead of having the SDK do the conversion/validation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The API rejects null values for optional fields. Use UNSET from
the generated client to omit fields instead of sending null.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Three stacktrace tests were using `alias` property which is not
recognized by the buildTemplate helper function, causing them to
use random UUIDs instead of expected template names.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- JS SDK: Added normalizeBuildArguments() helper for build/buildInBackground
- Python SDK: Added normalize_names() helper in types.py for build methods

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- JS SDK: tests/integration/tags.test.ts
  - Build template with tags
  - Assign additional tags
  - Delete tags
  - Verify error on non-existent tag deletion

- Python SDK: sync and async integration tests
  - tests/sync/template_sync/test_tags_integration.py
  - tests/async/template_async/test_tags_integration.py

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move integration tests from separate files into the existing
tags.test.ts and test_tags.py files. Add pytest integration
marker support.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update test fixtures to use `name` instead of `alias` parameter
- Pass `skip_cache` through fixtures to SDK instead of ignoring it
- Update stacktrace tests mock functions to use `name` parameter
- Update all test calls to use `name=` instead of `alias=`

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@dobrac
dobrac force-pushed the feat/template-versioning-tags branch from f43619a to f4f37ec Compare January 13, 2026 14:11
The API returns just the tag portion (e.g., 'production') instead of
the full alias:tag format. Updated integration tests to expect the
correct response format.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The API returns success (204) for deleting nonexistent tags (idempotent
behavior), so integration tests should not expect an error in this case.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The SDK's request_build function is called with names= and alias=
keyword arguments, not name=.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

@mishushakov mishushakov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

reorder some stuff, improve python tests, add more explicits checks for name length, logs array

Comment thread packages/js-sdk/src/template/index.ts Outdated
Comment thread packages/js-sdk/src/template/buildApi.ts
Comment thread packages/js-sdk/src/template/index.ts Outdated
Comment thread packages/js-sdk/src/template/index.ts
Comment thread packages/js-sdk/src/template/buildApi.ts
Comment thread packages/python-sdk/tests/async/template_async/test_tags.py
Comment thread packages/python-sdk/tests/async/template_async/test_tags.py Outdated
Comment thread packages/python-sdk/e2b/template/utils.py Outdated
Comment thread packages/js-sdk/src/template/utils.ts Outdated
- Reuse BuildLogLevel type from types.ts in logger.ts to avoid duplication
- Refactor Python tag tests to use unittest.mock (AsyncMock/Mock) instead of manual capture lists
- Remove unnecessary cleanup calls in integration tests
- Remove duplicate BuildLogEntry and BuildLogLevel types
- Add step field to LogEntry class to support build step info
- Update mapLogEntry to return LogEntry instances
- Use LogEntryLevel as single source of truth for log levels
- Fix Python f-string formatting for tags message

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is ON. A Cloud Agent has been kicked off to fix the reported issue.

Comment thread packages/js-sdk/src/template/buildApi.ts
@cursor

cursor Bot commented Jan 26, 2026

Copy link
Copy Markdown
Contributor

Bugbot Autofix prepared a fix for the bug found in the latest run.

  • ✅ Fixed: LogEntry step field lost when forwarding build logs
    • Added logEntry.step as the 4th argument to LogEntry constructor to preserve step information when forwarding build logs.

View PR

Preview (5a7e357)
diff --git a/packages/js-sdk/src/template/buildApi.ts b/packages/js-sdk/src/template/buildApi.ts
--- a/packages/js-sdk/src/template/buildApi.ts
+++ b/packages/js-sdk/src/template/buildApi.ts
@@ -293,7 +293,8 @@ export async function waitForBuildFinish(
         new LogEntry(
           logEntry.timestamp,
           logEntry.level,
-          stripAnsi(logEntry.message)
+          stripAnsi(logEntry.message),
+          logEntry.step
         )
       )
     )

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
@dobrac
dobrac force-pushed the feat/template-versioning-tags branch from 7f300d9 to 61eaa9d Compare January 26, 2026 23:02
@dobrac
dobrac force-pushed the feat/template-versioning-tags branch from 61eaa9d to d819f77 Compare January 26, 2026 23:14
@dobrac
dobrac requested a review from mishushakov January 27, 2026 09:05
Comment thread packages/js-sdk/src/template/index.ts
Comment thread packages/js-sdk/src/template/index.ts Outdated
Comment thread packages/js-sdk/src/template/logger.ts Outdated
Comment thread packages/python-sdk/e2b/template_async/main.py
Comment thread packages/python-sdk/e2b/template_async/main.py Outdated
Comment thread packages/python-sdk/e2b/template_sync/build_api.py Outdated
Comment thread packages/python-sdk/tests/conftest.py

@mishushakov mishushakov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

minor nits, but overall looks good, happy to merge

…consistency

- Fix JSDoc param order in Template.build and buildInBackground (template, name, options)
- Reorder Python SDK parameters: move tags after alias in build methods
- Update request_build function signature to match main.py ordering
- Add error handling with build info capture to JS test setup matching Python conftest.py
The step field on LogEntry was set from API responses but never used by consumers.
BuildStatusReason.step (used for stack trace resolution) is preserved.

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is ON. A Cloud Agent has been kicked off to fix the reported issue.

Comment thread packages/python-sdk/e2b/template_async/build_api.py Outdated
@cursor

cursor Bot commented Jan 27, 2026

Copy link
Copy Markdown
Contributor

Bugbot Autofix prepared a fix for the bug found in the latest run.

  • ✅ Fixed: Unset log_entries in reason may cause iteration error
    • Updated log_entries handling to explicitly check for Unset type before iteration, matching the pattern used for the step field.

View PR

Preview (984da47)
diff --git a/packages/python-sdk/e2b/template_async/build_api.py b/packages/python-sdk/e2b/template_async/build_api.py
--- a/packages/python-sdk/e2b/template_async/build_api.py
+++ b/packages/python-sdk/e2b/template_async/build_api.py
@@ -158,7 +158,7 @@ def _map_build_status_reason(reason) -> Optional[BuildStatusReason]:
     return BuildStatusReason(
         message=reason.message,
         step=reason.step if not isinstance(reason.step, Unset) else None,
-        log_entries=[_map_log_entry(e) for e in (reason.log_entries or [])],
+        log_entries=[_map_log_entry(e) for e in (reason.log_entries if not isinstance(reason.log_entries, Unset) and reason.log_entries else [])],
     )
 
 

diff --git a/packages/python-sdk/e2b/template_sync/build_api.py b/packages/python-sdk/e2b/template_sync/build_api.py
--- a/packages/python-sdk/e2b/template_sync/build_api.py
+++ b/packages/python-sdk/e2b/template_sync/build_api.py
@@ -157,7 +157,7 @@ def _map_build_status_reason(reason) -> Optional[BuildStatusReason]:
     return BuildStatusReason(
         message=reason.message,
         step=reason.step if not isinstance(reason.step, Unset) else None,
-        log_entries=[_map_log_entry(e) for e in (reason.log_entries or [])],
+        log_entries=[_map_log_entry(e) for e in (reason.log_entries if not isinstance(reason.log_entries, Unset) and reason.log_entries else [])],
     )

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Jakub Dobry <jakub.dobry8@gmail.com>
@dobrac
dobrac enabled auto-merge (squash) January 27, 2026 13:30
@dobrac
dobrac merged commit 10abab8 into main Jan 27, 2026
11 checks passed
@dobrac
dobrac deleted the feat/template-versioning-tags branch January 27, 2026 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants