Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 57 additions & 4 deletions packages/cli/src/commands/template/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,62 @@ import {
Language,
languageDisplay,
} from './generators'
import { listSandboxTemplates } from './list'
import { resolveTeamId } from '../../api'

/**
* Resolve the human-readable template alias used as `Template.build(..., name)`.
*
* `template_id` is an opaque internal ID. Passing it as `name` makes the API
* try to register that string as a *new* alias, which collides with the
* existing template → 409 (#1478). Prefer `template_name`, then the first
* alias returned by the templates API for this ID, and only fall back to
* `template_id` with a loud warning when nothing else is available.
*/
async function resolveMigrateBuildName(
config: E2BConfig,
nameOverride?: string
): Promise<string> {
Comment on lines +30 to +33

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add a changeset for the CLI fix

The repo-root AGENTS.md says, “Generate a changeset when updating packages/cli…”, but this commit changes packages/cli source/tests without adding or updating a .changeset entry for @e2b/cli; as a result this user-facing CLI fix can be omitted from the next package version/changelog. Please add a changeset describing the migrate alias fix.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@cla-bot check

if (nameOverride) {
return nameOverride
}
if (config.template_name) {
return config.template_name
}
if (!config.template_id) {
throw new Error('Template name or ID is required')
}

try {
const templates = await listSandboxTemplates({
teamID: resolveTeamId(undefined, config.team_id),
})
const match = templates.find((tpl) => tpl.templateID === config.template_id)
const alias = match?.aliases?.find((a) => typeof a === 'string' && a.length > 0)
if (alias) {
console.log(
`Resolved template_id ${asLocal(config.template_id)} to alias ${asPrimary(alias)}`
)
return alias
}
} catch (err) {
console.warn(
`Could not list templates to resolve alias for ${asLocal(config.template_id)}: ${
err instanceof Error ? err.message : String(err)
}`
)
}

console.warn(
`\n⚠️ No template_name/alias found for template_id ${asLocal(
config.template_id
)}. Using the ID as the build name may cause a 409 alias collision.`
)
console.warn(
` Prefer: e2b template migrate --name <existing-alias> (or set template_name in e2b.toml)\n`
)
return config.template_id
}

/**
* Migrate Dockerfile to a specific target language using SDK
Expand Down Expand Up @@ -68,10 +124,7 @@ async function migrateToLanguage(
parsedTemplate = baseTemplate.setReadyCmd(config.ready_cmd)
}

const name = nameOverride || config.template_name || config.template_id
if (!name) {
throw new Error('Template name or ID is required')
}
const name = await resolveMigrateBuildName(config, nameOverride)

// Generate code for the target language using shared functionality
await generateAndWriteTemplateFiles(
Expand Down
31 changes: 31 additions & 0 deletions packages/cli/tests/commands/template/migrate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,4 +348,35 @@ dockerfile = "e2b.Dockerfile"`
expect(files).toContain('build.prod.ts')
})
})
describe('Build name resolution (#1478)', () => {
test('uses template_name over template_id in generated build_prod', async () => {
const dockerfile = 'FROM node:18'
await fs.writeFile(path.join(testDir, 'e2b.Dockerfile'), dockerfile)

// Opaque ID-looking value must NOT appear as the build name when a
// human-readable template_name is present (would 409 on rebuild).
const config = `template_id = "abc1234567890xyz"
template_name = "my-template"
dockerfile = "e2b.Dockerfile"`
await fs.writeFile(path.join(testDir, 'e2b.toml'), config)

execSync(`node ${cliPath} template migrate --language python-async`, {
cwd: testDir,
})

const buildProd = await fs.readFile(
path.join(testDir, 'build_prod.py'),
'utf-8'
)
expect(buildProd).toContain('"my-template"')
expect(buildProd).not.toContain('"abc1234567890xyz"')

const buildDev = await fs.readFile(
path.join(testDir, 'build_dev.py'),
'utf-8'
)
expect(buildDev).toContain('"my-template-dev"')
expect(buildDev).not.toContain('"abc1234567890xyz-dev"')
})
})
})