Add --codegen-version to apimatic sdk publish
Summary
apimatic sdk generate supports both the v3 and v4 code generators via --codegen-version. apimatic sdk publish does not — it hardcodes v3. This adds the same flag to publish so a v4 SDK can be generated and published.
The change is a flag plus parameter threading through four files, plus one stability fix the v4 path turned out to require. No service changes, no prompt changes, no changes to GenerateAction.
Background — why this is small
The v4 generation path already exists and works. GenerateAction (src/actions/sdk/generate.ts:109) branches on codeGenVersion and calls PortalService.generateV4Sdk. The only reason sdk publish can't reach it is that SdkPublishAction passes hardcoded literals:
// src/actions/sdk/publish.ts (before)
CodeGenerationVersion.V3,
Stability.STABLE,
It's also worth stating why publishing needs no changes at all. The CLI talks to two independent services:
| Stage |
Service |
Endpoint |
| Generation |
api.apimatic.io |
POST /sdk (v3) or POST /sdk/v2 (v4) |
| Publishing |
api.package-publishing.apimatic.io |
POST /publish/{profileId}/{language} |
The CLI generates the SDK itself, then uploads the finished zip to the publishing service (src/infrastructure/services/publishing-api-service.ts:46-78) along with packageVersion and publishType. The publishing service never learns which generator produced the zip.
This also settles the package-version question: --version is sent to the publishing service as an explicit form field (publishing-api-service.ts:67), and that is the service that performs the registry push. The v3 generator additionally receives it (portal-service.ts:117) while the v4 generator has no such parameter, but that's redundant rather than load-bearing.
Stability is not independent of codegen version
The original scoping treated --stability as an unrelated knob and excluded it. That was wrong, and a live run proved it:
The V4 Code Generator currently supports SDK generation for 'csharp' only in 'beta'.
Choosing v4 constrains which stability values the server accepts. Because SdkPublishAction pinned Stability.STABLE, --codegen-version v4 could never generate anything at all.
Note also that stability only exists on the v4 path — portal-service.ts:111 (generateSdk, v3) takes no stability parameter, while portal-service.ts:172 (generateV4Sdk) requires one. So this is inert for v3.
Resolution: the v4 path asks for beta; v3 keeps stable. No new flag.
A --stability flag was considered and rejected. v4 is csharp-only in practice, this is a temporary server-side restriction, and a flag would force users to pass two flags that always go together. The conditional is keyed on v4 rather than on csharp, so if another language joins v4 later it also starts in beta without anyone editing a language check.
Out of scope
- A
--stability flag — see above; the v4 path selects beta automatically.
- An interactive prompt for codegen version — see below.
- A guard for builds with tracked SDK customizations — see Known limitations.
- Anything touching
--version / SemVersion — unrelated.
Interactive mode needs no special handling
src/commands/sdk/publish.ts:86 decides the mode with:
const interactive = this.argv.length === 0;
Interactive mode means no flags were passed at all, so codegenVersion is always its default (v3) on that path. Threading the parsed value through both branches uniformly is correct and requires no hardcoding or prompting.
Consequence: the wizard cannot produce a v4 SDK. This is deliberate for now — v4 publishing targets CI/CD and scripted use. If interactive v4 is wanted later, it is additive: the parameter is already threaded, so only the mode check changes (drop --codegen-version from the argv count, handling both --codegen-version v4 and --codegen-version=v4). Nothing in this change would be undone.
Passing --codegen-version v4 alone routes to the non-interactive path, which reports the missing required flags and prints interactiveModeNotice() — "You can run the command in interactive mode by not passing any flags" (src/prompts/sdk/publish/non-interactive.ts:32-34). Same as passing any single flag today.
Change 1 — src/commands/sdk/publish.ts
1a. Import
// before
import { Language } from '../../types/sdk/generate.js';
// after
import { CodeGenerationVersion, Language } from '../../types/sdk/generate.js';
1b. Flag
Add as the last entry in static flags, after 'dry-run':
'dry-run': Flags.boolean({
default: false,
description: 'Generate the SDK locally for review without publishing.'
}),
'codegen-version': Flags.string({
description: 'Version of the code generator to use',
options: Object.values(CodeGenerationVersion).map((v) => v.valueOf()),
default: CodeGenerationVersion.V3
})
Name, description, options and default are copied verbatim from src/commands/sdk/generate.ts:46-50 so the two commands stay consistent.
1c. Example
Add one v4 example to static examples:
`${SdkPublish.cmdTxt} ${format.flag('profile-id', 'd4e5f6a1b2c3d4e5f6a1b2c3')} ${format.flag(
'language',
'typescript'
)} ${format.flag('version', '1.0.0')} ${format.flag(
'publish-type',
PublishType.PackagePublishing
)} ${format.flag('codegen-version', 'v4')}`
test/commands/examples-parse.test.ts parses every command's examples against its own flag definitions, so this is validated automatically.
1d. Destructure
const {
flags: {
'profile-id': profileId,
version,
destination,
language,
force,
input,
'publish-type': publishType,
'dry-run': dryRun,
'codegen-version': codegenVersion
}
} = await this.parse(SdkPublish);
1e. Telemetry payload
Add the new flag to the non-interactive failure event so a failed v4 publish is distinguishable from a failed v3 one:
new SdkPublishValidationFailedEvent(errorMessage, SdkPublish.id, {
'profile-id': profileId,
version,
language,
...(force && { force }),
'publish-type': publishTypes,
'codegen-version': codegenVersion
}),
1f. Pass to both actions
intro('Publish SDK');
const result = interactive
? await new SdkPublishInteractiveAction(configDir, commandMetadata).execute(
workingDirectory,
codegenVersion as CodeGenerationVersion,
onPublishSdkError
)
: await new SdkPublishNonInteractiveAction(configDir, commandMetadata).execute(
buildDirectory,
sdkDirectory,
language as Language,
publishTypes,
force,
dryRun,
codegenVersion as CodeGenerationVersion,
onPublishSdkError,
profileId,
version
);
outro(result);
The as CodeGenerationVersion cast matches how language as Language is already handled here and how sdk generate does it.
Change 2 — src/actions/sdk/publish/non-interactive.ts
2a. Import
// before
import { Language } from '../../../types/sdk/generate.js';
// after
import { CodeGenerationVersion, Language } from '../../../types/sdk/generate.js';
2b. Signature
codegenVersion goes after dryRun, keeping all required parameters ahead of the callback and the trailing optionals:
public readonly execute = async (
buildDirectory: DirectoryPath,
sdkDirectory: DirectoryPath,
language: Language,
publishTypes: PublishType[],
force: boolean,
dryRun: boolean,
codegenVersion: CodeGenerationVersion,
onPublishSdkError: (errorMessage: string) => void,
profileId?: string,
version?: string
): Promise<ActionResult> => {
2c. Forward to SdkPublishAction
const publishResult = await new SdkPublishAction(this.configDir, this.commandMetadata).execute(
buildDirectory,
outputDir,
language,
publishTypes,
force,
publishingProfileId,
semVersion,
publishingProfile,
dryRun,
codegenVersion,
onPublishSdkError
);
Everything above this line — directory checks, missing-flag collection, version and profile-id validation, profile lookup, language and publish-type checks — is unchanged.
Change 3 — src/actions/sdk/publish/interactive.ts
3a. Import
This file has no import from types/sdk/generate.js yet; add one:
import { CodeGenerationVersion } from '../../../types/sdk/generate.js';
3b. Signature
public readonly execute = async (
defaultBuildDirectory: DirectoryPath,
codegenVersion: CodeGenerationVersion,
onPublishSdkError: (errorMessage: string) => void
): Promise<ActionResult> => {
3c. Forward to SdkPublishAction
const publishResult = await new SdkPublishAction(this.configDir, this.commandMetadata).execute(
buildDirectory,
sdkDirectory,
language,
publishTypes,
false,
publishingProfileId,
version,
publishingProfile,
false,
codegenVersion,
onPublishSdkError
);
No new prompt, no new wizard step. The seven existing steps are untouched.
Change 4 — src/actions/sdk/publish.ts
CodeGenerationVersion and Stability are already imported here (line 11), so no import change.
4a. Signature
public readonly execute = async (
buildDirectory: DirectoryPath,
outputDirectory: DirectoryPath,
language: Language,
publishType: PublishType[],
force: boolean,
profileId: ProfileId,
semVersion: SemVersion,
publishingProfile: PublishingProfile,
dryRun: boolean,
codegenVersion: CodeGenerationVersion,
onPublishSdkError: (errorMessage: string) => void
): Promise<ActionResult<PublishingInfo>> => {
4b. Use it — the actual fix
Both hardcoded literals go. The codegen version becomes the caller's, and stability follows from it:
const sdkGenerateAction = new GenerateAction(this.configDir, this.commandMetadata);
const sdkGenerationResult = await sdkGenerateAction.execute(
buildDirectory,
outputDirectory,
language,
force,
false,
false,
false,
codegenVersion,
// The V4 generator is only offered in beta today, so asking for a stable V4 SDK is
// rejected server-side. Drop this once V4 reaches general availability.
codegenVersion === CodeGenerationVersion.V4 ? Stability.BETA : Stability.STABLE,
undefined,
semVersion,
packageSettingsDirectory
);
Package-settings writing, the dry-run branch, zipping, the publish call and the error path are all untouched.
Verified against the live API
A real run against api.apimatic.io confirmed the full CLI path:
◇ Profile search complete.
▲ The V4 Code Generator does not currently support SDK customizations. ← v4 branch taken
◇ SDK generated successfully. ← beta stability accepted
● The generated SDK can be found at '...\sdk\csharp'.
◇ Publishing initiated. ← zip uploaded
The warning on line 2 exists only inside GenerateAction's v4 branch (generate.ts:110), so its presence in a publish run proves the value threaded through all four layers and reached POST /sdk/v2. Before the stability fix, the same run failed at generation with the beta error quoted above.
Known limitations
1. v4 and tracked SDK customizations. On the v4 path, GenerateAction prints sdkCustomizationsNotSupportedForV4() and returns at generate.ts:131, before the MergeSourceTreeAction block at line 167. A build containing sdk-source-tree/.{language} therefore generates — and now publishes — an SDK without those changes, with only a warning line as the signal. Pre-existing property of the v4 path, accepted here. If we later guard it, the check must run against the versioned build context (generate.ts:98), not the root one.
Add
--codegen-versiontoapimatic sdk publishSummary
apimatic sdk generatesupports both the v3 and v4 code generators via--codegen-version.apimatic sdk publishdoes not — it hardcodes v3. This adds the same flag topublishso a v4 SDK can be generated and published.The change is a flag plus parameter threading through four files, plus one stability fix the v4 path turned out to require. No service changes, no prompt changes, no changes to
GenerateAction.Background — why this is small
The v4 generation path already exists and works.
GenerateAction(src/actions/sdk/generate.ts:109) branches oncodeGenVersionand callsPortalService.generateV4Sdk. The only reasonsdk publishcan't reach it is thatSdkPublishActionpasses hardcoded literals:It's also worth stating why publishing needs no changes at all. The CLI talks to two independent services:
api.apimatic.ioPOST /sdk(v3) orPOST /sdk/v2(v4)api.package-publishing.apimatic.ioPOST /publish/{profileId}/{language}The CLI generates the SDK itself, then uploads the finished zip to the publishing service (
src/infrastructure/services/publishing-api-service.ts:46-78) along withpackageVersionandpublishType. The publishing service never learns which generator produced the zip.This also settles the package-version question:
--versionis sent to the publishing service as an explicit form field (publishing-api-service.ts:67), and that is the service that performs the registry push. The v3 generator additionally receives it (portal-service.ts:117) while the v4 generator has no such parameter, but that's redundant rather than load-bearing.Stability is not independent of codegen version
The original scoping treated
--stabilityas an unrelated knob and excluded it. That was wrong, and a live run proved it:Choosing v4 constrains which stability values the server accepts. Because
SdkPublishActionpinnedStability.STABLE,--codegen-version v4could never generate anything at all.Note also that stability only exists on the v4 path —
portal-service.ts:111(generateSdk, v3) takes no stability parameter, whileportal-service.ts:172(generateV4Sdk) requires one. So this is inert for v3.Resolution: the v4 path asks for beta; v3 keeps stable. No new flag.
A
--stabilityflag was considered and rejected. v4 is csharp-only in practice, this is a temporary server-side restriction, and a flag would force users to pass two flags that always go together. The conditional is keyed on v4 rather than on csharp, so if another language joins v4 later it also starts in beta without anyone editing a language check.Out of scope
--stabilityflag — see above; the v4 path selects beta automatically.--version/SemVersion— unrelated.Interactive mode needs no special handling
src/commands/sdk/publish.ts:86decides the mode with:Interactive mode means no flags were passed at all, so
codegenVersionis always its default (v3) on that path. Threading the parsed value through both branches uniformly is correct and requires no hardcoding or prompting.Consequence: the wizard cannot produce a v4 SDK. This is deliberate for now — v4 publishing targets CI/CD and scripted use. If interactive v4 is wanted later, it is additive: the parameter is already threaded, so only the mode check changes (drop
--codegen-versionfrom the argv count, handling both--codegen-version v4and--codegen-version=v4). Nothing in this change would be undone.Passing
--codegen-version v4alone routes to the non-interactive path, which reports the missing required flags and printsinteractiveModeNotice()— "You can run the command in interactive mode by not passing any flags" (src/prompts/sdk/publish/non-interactive.ts:32-34). Same as passing any single flag today.Change 1 —
src/commands/sdk/publish.ts1a. Import
1b. Flag
Add as the last entry in
static flags, after'dry-run':Name, description,
optionsanddefaultare copied verbatim fromsrc/commands/sdk/generate.ts:46-50so the two commands stay consistent.1c. Example
Add one v4 example to
static examples:test/commands/examples-parse.test.tsparses every command's examples against its own flag definitions, so this is validated automatically.1d. Destructure
1e. Telemetry payload
Add the new flag to the non-interactive failure event so a failed v4 publish is distinguishable from a failed v3 one:
1f. Pass to both actions
The
as CodeGenerationVersioncast matches howlanguage as Languageis already handled here and howsdk generatedoes it.Change 2 —
src/actions/sdk/publish/non-interactive.ts2a. Import
2b. Signature
codegenVersiongoes afterdryRun, keeping all required parameters ahead of the callback and the trailing optionals:2c. Forward to
SdkPublishActionEverything above this line — directory checks, missing-flag collection, version and profile-id validation, profile lookup, language and publish-type checks — is unchanged.
Change 3 —
src/actions/sdk/publish/interactive.ts3a. Import
This file has no import from
types/sdk/generate.jsyet; add one:3b. Signature
3c. Forward to
SdkPublishActionNo new prompt, no new wizard step. The seven existing steps are untouched.
Change 4 —
src/actions/sdk/publish.tsCodeGenerationVersionandStabilityare already imported here (line 11), so no import change.4a. Signature
4b. Use it — the actual fix
Both hardcoded literals go. The codegen version becomes the caller's, and stability follows from it:
Package-settings writing, the dry-run branch, zipping, the publish call and the error path are all untouched.
Verified against the live API
A real run against
api.apimatic.ioconfirmed the full CLI path:The warning on line 2 exists only inside
GenerateAction's v4 branch (generate.ts:110), so its presence in a publish run proves the value threaded through all four layers and reachedPOST /sdk/v2. Before the stability fix, the same run failed at generation with the beta error quoted above.Known limitations
1. v4 and tracked SDK customizations. On the v4 path,
GenerateActionprintssdkCustomizationsNotSupportedForV4()and returns atgenerate.ts:131, before theMergeSourceTreeActionblock at line 167. A build containingsdk-source-tree/.{language}therefore generates — and now publishes — an SDK without those changes, with only a warning line as the signal. Pre-existing property of the v4 path, accepted here. If we later guard it, the check must run against the versioned build context (generate.ts:98), not the root one.