diff --git a/docs/command-graph.html b/docs/command-graph.html
index e20f2ffe..48f54b22 100644
--- a/docs/command-graph.html
+++ b/docs/command-graph.html
@@ -471,7 +471,7 @@
options: ["-p, --profile ", "--json", "--flavors ", "--withDependencies", "--packageKeys ", "--keysByVersion ", "--variableValue ", "--variableType ", "--branches (default: false)", "--staging (default: false)", "-h, --help"] },
{ id: "t2tc_package_export", label: "export", group: "command", path: "t2tc package export",
description: "Export one or more packages into a Team-to-Team Copy archive. The archive is consumed by 't2tc package import' / 't2tc package diff'. To export a single standalone package, use 'config package export'.",
- options: ["-p, --profile ", "--packageKeys ", "--keysByVersion ", "--withDependencies", "--unzip", "--gitProfile ", "--gitBranch ", "-h, --help"] },
+ options: ["-p, --profile ", "--packageKeys ", "--keysByVersion ", "--withDependencies", "--unzip", "--gitProfile ", "--gitBranch ", "--staging (only with --packageKeys)", "-h, --help"] },
{ id: "t2tc_package_import", label: "import", group: "command", path: "t2tc package import",
description: "Import packages from a Team-to-Team Copy archive produced by 't2tc package export'. To import a single standalone package, use 'config package import'.",
options: ["-p, --profile ", "--overwrite", "--validate (default: false)", "--gitProfile ", "--gitBranch ", "-f, --file ", "-d, --directory ", "-h, --help"] },
diff --git a/docs/user-guide/t2tc-commands.md b/docs/user-guide/t2tc-commands.md
index b31abe55..740f02ad 100644
--- a/docs/user-guide/t2tc-commands.md
+++ b/docs/user-guide/t2tc-commands.md
@@ -93,6 +93,11 @@ content-cli t2tc package export -p --packageKeys key1 ... keyN
The `--keysByVersion` option can be used to export packages by specific version. You can specify multiple packages with version separated by spaces, in the format of 'packageKey.version'.
The `--withDependencies` option can be used to also export dependencies of the given packages.
The `--unzip` option can be used to unzip the exported packages into the current working directory.
+The `--staging` option exports the current staging state of the given `--packageKeys` instead of their latest deployed version. Branch package keys (`@`) are supported. Staging packages appear as `_staging.zip`, use `staging` as their version in `manifest.json` and `variables.json`, and their staging variable values are exported. With `--withDependencies`, dependencies are exported at the published versions the staging configuration references. `--staging` cannot be combined with `--keysByVersion`.
+
+```bash
+content-cli t2tc package export -p --packageKeys projectKey@branchKey --staging --withDependencies
+```
Depending on the `--unzip` option used, a zip file, or a directory containing the exported packages, will be created in the current working directory containing:
diff --git a/src/commands/t2tc/api/t2tc-package-api.ts b/src/commands/t2tc/api/t2tc-package-api.ts
index 27e4cf0b..15cea3e1 100644
--- a/src/commands/t2tc/api/t2tc-package-api.ts
+++ b/src/commands/t2tc/api/t2tc-package-api.ts
@@ -74,6 +74,16 @@ export class T2tcPackageApi {
});
}
+ public async exportStagingPackages(packageKeys: string[], withDependencies: boolean): Promise {
+ const queryParams = new URLSearchParams();
+ packageKeys.forEach(packageKey => queryParams.append("packageKeys", packageKey));
+ queryParams.set("withDependencies", withDependencies.toString());
+
+ return this.httpClient().getFile(`/package-manager/api/core/staging/packages/export/batch?${queryParams.toString()}`).catch(e => {
+ throw new FatalError(`Problem exporting staging packages: ${e}`);
+ });
+ }
+
public async exportPackagesByVersions(packageKeysWithVersion: string[], withDependencies: boolean): Promise {
const queryParams = new URLSearchParams();
packageKeysWithVersion.forEach(packageKeyByVersion => queryParams.append("packageKeysWithVersion", packageKeyByVersion));
diff --git a/src/commands/t2tc/batch-export-import.constants.ts b/src/commands/t2tc/batch-export-import.constants.ts
index 534543c6..92ca2144 100644
--- a/src/commands/t2tc/batch-export-import.constants.ts
+++ b/src/commands/t2tc/batch-export-import.constants.ts
@@ -7,5 +7,6 @@ export enum BatchExportImportConstants {
ZIP_EXTENSION = ".zip",
JSON_EXTENSION = ".json",
NODES_FOLDER_NAME = "nodes/",
- SCENARIO_NODE = "SCENARIO"
+ SCENARIO_NODE = "SCENARIO",
+ STAGING_VERSION = "staging"
}
diff --git a/src/commands/t2tc/module.ts b/src/commands/t2tc/module.ts
index 7da3374d..f89aa688 100644
--- a/src/commands/t2tc/module.ts
+++ b/src/commands/t2tc/module.ts
@@ -33,6 +33,7 @@ class Module extends IModule {
.option("--unzip", "Unzip the exported file", "")
.option("--gitProfile ", "Git profile which you want to use for the Git operations")
.option("--gitBranch ", "Git branch in which you want to push the exported file")
+ .option("--staging", "Export the staging state of the given --packageKeys (including branch packages) instead of the latest deployed version", false)
.action(this.batchExportPackages);
t2tcPackageCommand.command("import")
@@ -81,8 +82,11 @@ class Module extends IModule {
if (options.gitProfile && !options.gitBranch) {
throw new Error("Please specify a branch using --gitBranch when using a Git profile.");
}
+ if (options.staging && options.keysByVersion) {
+ throw new Error("Staging parameter is only supported with --packageKeys, not --keysByVersion.");
+ }
options.withDependencies = options.withDependencies ?? false;
- await new T2tcCommandService(context).batchExportPackages(options.packageKeys, options.keysByVersion, options.withDependencies, options.gitBranch, options.unzip);
+ await new T2tcCommandService(context).batchExportPackages(options.packageKeys, options.keysByVersion, options.withDependencies, options.gitBranch, options.unzip, options.staging);
}
private async batchImportPackages(context: Context, command: Command, options: OptionValues): Promise {
diff --git a/src/commands/t2tc/t2tc-command.service.ts b/src/commands/t2tc/t2tc-command.service.ts
index eb75e313..fd665c2d 100644
--- a/src/commands/t2tc/t2tc-command.service.ts
+++ b/src/commands/t2tc/t2tc-command.service.ts
@@ -39,8 +39,8 @@ export class T2tcCommandService {
}
}
- public batchExportPackages(packageKeys: string[], packageKeysByVersion: string[], withDependencies: boolean, gitBranch: string, unzip: boolean): Promise {
- return this.t2tcPackageService.batchExportPackages(packageKeys, packageKeysByVersion, withDependencies, gitBranch, unzip);
+ public batchExportPackages(packageKeys: string[], packageKeysByVersion: string[], withDependencies: boolean, gitBranch: string, unzip: boolean, staging: boolean = false): Promise {
+ return this.t2tcPackageService.batchExportPackages(packageKeys, packageKeysByVersion, withDependencies, gitBranch, unzip, staging);
}
public batchImportPackages(file: string, directory: string, overwrite: boolean, gitBranch: string, performValidation: boolean = false): Promise {
diff --git a/src/commands/t2tc/t2tc-package.service.ts b/src/commands/t2tc/t2tc-package.service.ts
index 35301ede..31fcc985 100644
--- a/src/commands/t2tc/t2tc-package.service.ts
+++ b/src/commands/t2tc/t2tc-package.service.ts
@@ -16,6 +16,7 @@ import { parse, stringify } from "../../core/utils/json";
import { PackageApi } from "../studio/api/package-api";
import { T2tcPackageApi } from "./api/t2tc-package-api";
import { VariableApi } from "../configuration-management/api/variable-api";
+import { StagingPackageVariablesApi } from "../configuration-management/api/staging-package-variables-api";
import { fixConnectionVariables } from "../configuration-management/connection-variable.helper";
import { StudioService } from "./studio.service";
import { GitService } from "../../core/git-profile/git/git.service";
@@ -26,6 +27,7 @@ export class T2tcPackageService {
private t2tcPackageApi: T2tcPackageApi;
private variableApi: VariableApi;
+ private stagingPackageVariablesApi: StagingPackageVariablesApi;
private studioPackageApi: PackageApi;
private studioService: StudioService;
@@ -35,6 +37,7 @@ export class T2tcPackageService {
constructor(context: Context) {
this.t2tcPackageApi = new T2tcPackageApi(context);
this.variableApi = new VariableApi(context);
+ this.stagingPackageVariablesApi = new StagingPackageVariablesApi(context);
this.studioPackageApi = new PackageApi(context);
this.studioService = new StudioService(context);
@@ -72,9 +75,11 @@ export class T2tcPackageService {
});
}
- public async batchExportPackages(packageKeys: string[], packageKeysByVersion: string[], withDependencies: boolean, gitBranch: string, unzip: boolean): Promise {
+ public async batchExportPackages(packageKeys: string[], packageKeysByVersion: string[], withDependencies: boolean, gitBranch: string, unzip: boolean, staging: boolean = false): Promise {
let exportedPackagesData: Buffer;
- if (packageKeys) {
+ if (staging) {
+ exportedPackagesData = await this.t2tcPackageApi.exportStagingPackages(packageKeys, withDependencies);
+ } else if (packageKeys) {
exportedPackagesData = await this.t2tcPackageApi.exportPackages(packageKeys, withDependencies);
} else {
exportedPackagesData = await this.t2tcPackageApi.exportPackagesByVersions(packageKeysByVersion, withDependencies);
@@ -184,10 +189,15 @@ export class T2tcPackageService {
return versionsByPackageKey;
}
- private getVersionedVariablesForPackagesWithKeys(versionsByPackageKey: Map): Promise {
+ private async getVersionedVariablesForPackagesWithKeys(versionsByPackageKey: Map): Promise {
const variableExportRequest: PackageKeyAndVersionPair[] = [];
+ const stagingPackageKeys: string[] = [];
versionsByPackageKey?.forEach((versions, key) => {
versions?.forEach(version => {
+ if (version === BatchExportImportConstants.STAGING_VERSION) {
+ stagingPackageKeys.push(key);
+ return;
+ }
variableExportRequest.push({
packageKey: key,
version: version,
@@ -195,7 +205,21 @@ export class T2tcPackageService {
})
});
- return this.variableApi.findVariablesWithValuesByPackageKeysAndVersion(variableExportRequest)
+ const versionedVariables = await this.variableApi.findVariablesWithValuesByPackageKeysAndVersion(variableExportRequest);
+ const stagingVariables = stagingPackageKeys.length
+ ? await this.getStagingVariablesForPackagesWithKeys(stagingPackageKeys)
+ : [];
+
+ return [...versionedVariables, ...stagingVariables];
+ }
+
+ private async getStagingVariablesForPackagesWithKeys(packageKeys: string[]): Promise {
+ const stagingVariables = await this.stagingPackageVariablesApi.findAllByPackageKeys(packageKeys);
+ return stagingVariables.map(stagingVariable => ({
+ packageKey: stagingVariable.packageKey,
+ version: BatchExportImportConstants.STAGING_VERSION,
+ variables: stagingVariable.variables ?? [],
+ }));
}
private buildBodyForImport(configs: AdmZip, sourcePath: string, variablesManifests: VariableManifestTransport[]): FormData {
diff --git a/tests/commands/t2tc/module.spec.ts b/tests/commands/t2tc/module.spec.ts
index 270adfdb..bca5a738 100644
--- a/tests/commands/t2tc/module.spec.ts
+++ b/tests/commands/t2tc/module.spec.ts
@@ -154,6 +154,39 @@ describe("T2TC Module", () => {
undefined,
false,
undefined,
+ true,
+ undefined
+ );
+ });
+
+ it("throws when --staging is combined with --keysByVersion", async () => {
+ const options: OptionValues = {
+ keysByVersion: ["package1.1.0.0"],
+ staging: true,
+ };
+
+ await expect(
+ (module as any).batchExportPackages(testContext, mockCommand, options)
+ ).rejects.toThrow("Staging parameter is only supported with --packageKeys, not --keysByVersion.");
+
+ expect(mockT2tcCommandService.batchExportPackages).not.toHaveBeenCalled();
+ });
+
+ it("delegates to T2tcCommandService with staging enabled", async () => {
+ const options: OptionValues = {
+ packageKeys: ["project@my-branch"],
+ withDependencies: true,
+ staging: true,
+ };
+
+ await (module as any).batchExportPackages(testContext, mockCommand, options);
+
+ expect(mockT2tcCommandService.batchExportPackages).toHaveBeenCalledWith(
+ ["project@my-branch"],
+ undefined,
+ true,
+ undefined,
+ undefined,
true
);
});
diff --git a/tests/commands/t2tc/t2tc-package-export.spec.ts b/tests/commands/t2tc/t2tc-package-export.spec.ts
index 7cf81c0d..5c708ddf 100644
--- a/tests/commands/t2tc/t2tc-package-export.spec.ts
+++ b/tests/commands/t2tc/t2tc-package-export.spec.ts
@@ -1210,4 +1210,50 @@ describe("Config export", () => {
expect(getDownloadedFileName()).not.toBeNull();
})
-})
\ No newline at end of file
+ it("Should export staging packages from the staging endpoint with staging variables", async () => {
+ const manifest: PackageManifestTransport[] = [];
+ manifest.push(ConfigUtils.buildManifestForKeyAndFlavorAndVersion("project@my-branch", "TEST", null, {
+ [BatchExportImportConstants.STAGING_VERSION]: [],
+ } as unknown as Map));
+ manifest.push(ConfigUtils.buildManifestForKeyAndFlavorAndVersion("dependency-key", "TEST", "1.0.0", {
+ "1.0.0": [],
+ } as unknown as Map));
+ const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, []);
+
+ const stagingVariable = {
+ key: "staging-variable",
+ type: PackageManagerVariableType.PLAIN_TEXT,
+ value: "staging-value",
+ metadata: {},
+ };
+ const versionedVariables: VariableManifestTransport[] = [{
+ packageKey: "dependency-key",
+ version: "1.0.0",
+ variables: [],
+ }];
+
+ mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/staging/packages/export/batch?packageKeys=project%40my-branch&withDependencies=true", exportedPackagesZip.toBuffer());
+ mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", versionedVariables);
+ mockAxiosPost("https://myTeam.celonis.cloud/pacman/api/core/staging/packages/variables/by-package-keys", [{
+ packageKey: "project@my-branch",
+ variables: [stagingVariable],
+ }]);
+
+ await new T2tcCommandService(testContext).batchExportPackages(["project@my-branch"], undefined, true, null, null, true);
+
+ const versionedVariablesRequest = parse(mockedPostRequestBodyByUrl.get("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments"));
+ expect(versionedVariablesRequest).toEqual([{packageKey: "dependency-key", version: "1.0.0"}]);
+
+ const stagingVariablesRequest = parse(mockedPostRequestBodyByUrl.get("https://myTeam.celonis.cloud/pacman/api/core/staging/packages/variables/by-package-keys"));
+ expect(stagingVariablesRequest).toEqual(["project@my-branch"]);
+
+ const actualZip = new AdmZip(getDownloadedFileName());
+ const exportedVariables: VariableManifestTransport[] = parse(actualZip.getEntry(BatchExportImportConstants.VARIABLES_FILE_NAME).getData().toString());
+ expect(exportedVariables).toContainEqual({
+ packageKey: "project@my-branch",
+ version: BatchExportImportConstants.STAGING_VERSION,
+ variables: [stagingVariable],
+ });
+ expect(exportedVariables).toContainEqual(versionedVariables[0]);
+ })
+})