Skip to content

feat(config): add --merge and --dry-run flags to zowe config import#2712

Draft
chipset wants to merge 2 commits into
zowe:masterfrom
chipset:feature/config-import-merge-dryrun
Draft

feat(config): add --merge and --dry-run flags to zowe config import#2712
chipset wants to merge 2 commits into
zowe:masterfrom
chipset:feature/config-import-merge-dryrun

Conversation

@chipset

@chipset chipset commented Apr 8, 2026

Copy link
Copy Markdown

Summary

  • --merge (-mg): Merges properties from an imported config into the existing zowe.config.json instead of overwriting it. Existing values win on all conflicts — profiles are deep-merged, defaults use existing keys, plugins are appended without duplication, and autoStore is only set if not already defined.
  • --dry-run (-dr): Previews the resulting config without writing anything to disk. Works standalone (preview a full overwrite) or combined with --merge (preview a safe merge). Schema download is skipped during dry runs.
  • Updated the "file already exists" skip message to advertise --merge and --dry-run alongside the existing --overwrite hint.
  • Added CHANGES.md documenting the new behaviour, merge semantics, and a behaviour matrix.

Motivation

zowe config import previously offered only two modes: skip (if the target file already existed) or overwrite (--overwrite). Users with an existing zowe.config.json had no safe way to adopt new defaults or profiles from a shared/team configuration without losing their own settings. This PR closes that gap.

Behaviour matrix

Flag(s) File exists? Result
(none) No Write imported config as-is
(none) Yes Skip (no change)
--overwrite Yes Overwrite existing file
--merge No Write imported config as-is
--merge Yes Deep-merge; existing values win
--dry-run Any Print result to console, no disk write
--merge --dry-run Yes Print merge preview, no disk write
--merge --overwrite Any Error (mutually exclusive)

Merge semantics (field-by-field)

Field Rule
profiles Deep-merged via lodash.mergeWith; existing property values and arrays are preserved
defaults Shallow spread — existing keys are never overwritten; only missing keys are added
plugins Additive — new plugin names appended only if not already in the list
autoStore Only set from imported config if the target does not already define it

Test plan

  • 10 new unit tests in import.handler.unit.test.ts covering all flag combinations, mutual exclusion, skip message text, dry-run output, and field-level merge correctness
  • 5 new integration tests in the CLI config import subtest file covering help output, --dry-run, --merge --dry-run, --merge on existing file, and --merge first-time import
  • New fixture test.config.for.merge.json with overlapping and new profiles/defaults used by integration tests
  • Manual smoke test: zowe config import <url> --merge --dry-run against a live profile

Files changed

File Change
src/config/cmd/import/import.definition.ts Added --merge and --dry-run options and three new usage examples
src/config/cmd/import/import.handler.ts Implemented safe merge logic, dry-run preview, mutual exclusion guard, updated skip message
src/config/cmd/import/CHANGES.md New — documents all changes
__tests__/config/cmd/import/import.handler.unit.test.ts 10 new unit tests
__tests__/__integration__/.../cli/config/import/cli.imperative-test-cli.config.import.integration.subtest.ts 5 new integration tests + updated help assertion
__tests__/__integration__/.../cli/config/import/__resources__/test.config.for.merge.json New integration test fixture

Made with Cursor

tm633717 added 2 commits April 8, 2026 13:22
- --merge adds missing properties from the imported config into an
  existing zowe.config.json without overwriting any existing values.
  Existing values win on all conflicts across profiles, defaults,
  plugins, and autoStore.
- --dry-run previews what the resulting config would look like without
  writing anything to disk. Works standalone or combined with --merge.
- Updated skip message to advertise --merge and --dry-run alongside
  the existing --overwrite hint.
- Added CHANGES.md documenting the new behaviour and merge semantics.
Closes #<issue-number>

Made-with: Cursor
…e and --dry-run

- Unit tests: 10 new cases covering --merge (existing values win,
  new profiles/defaults/plugins added, autoStore not overwritten,
  no duplicate plugins, first-time import, dry-run preview),
  --dry-run (no writes, no schema download), mutual exclusion of
  --merge and --overwrite, and updated skip message text.
- Integration tests: 5 new cases covering --dry-run (no file created,
  no schema downloaded, preview without write), --merge (field-level
  correctness on disk, first-time import as-is), and help output
  asserting --merge and --dry-run flags are listed.
- New fixture: test.config.for.merge.json with overlapping and new
  profiles/defaults used to verify merge behaviour end-to-end.

Made-with: Cursor
@github-project-automation github-project-automation Bot moved this to New Issues in Zowe CLI Squad Apr 8, 2026
@zowe-robot zowe-robot moved this from New Issues to Review/QA in Zowe CLI Squad Apr 8, 2026
@chipset

chipset commented Apr 8, 2026

Copy link
Copy Markdown
Author

I tested this on my own system based upon a large customer's need to manage multiple configuration files for their developers. Reach out to me if you need the names.

Test fixtures are included.

@gejohnston gejohnston 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.

Thanks for this effort. It looks like a viable, useful enhancement. With a pull request from a forked branch, the effort to confirm or fix the full set of unit, integration, and system tests, and to reach code coverage test objectives falls on the Zowe team. Our product management team will prioritize such an effort. Completion of this pull request will be undertaken based on that prioritization.

In the meantime, I posted a number of comments/questions that can be considered when this work is undertaken.

: config.api.layers.get();

// profiles: deep-merge with existing winning — lodash.mergeWith(existing, incoming)
target.properties.profiles = lodash.mergeWith(

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.

It appears that this logic will work for nexted configs. Has it been tested with nested configs?
Like having both of these profiles in a config:

target.properties.profiles.QA.profiles.zosmf
target.properties.profiles.DEV.profiles.zosmf

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I tested it with nested configs on my box.

target.properties.profiles, // existing (higher priority, applied second — wins)
(existingVal: any, _importedVal: any) => {
// For arrays, keep the existing array as-is
if (lodash.isArray(existingVal)) { return existingVal; }

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.

If an imported config adds a new secure property (like port), that secure port property will not added. Further, if port was previously a plain text property, the plain text port property would not be removed. These choices are understandable and consistent, but:

  • Would a site think that this behavior is a bug?
  • Can we explain our behavior somewhere?

...target.properties.defaults // existing overrides (wins on conflict)
};

// plugins: add any new plugins from the import not already present

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.

We add plugin names from the imported config to the target config.
My guess is that the function which we call later:

config.api.layers.set(target.properties)

stores the plugin names into plugins.json. Would that action have any real effect if we do not actually install the new plugins?

Further, since the value of the imported configJson.plugins property probably comes from the local plugins.json file (not from anything from the imported config file itself), does copying and storing the plugins really do anything at all? If not, we might choose to remove the entire plugin paragraph.

// Print a preview of the would-be result without touching disk
params.response.console.log(
TextUtils.chalk.yellow("[Dry Run]") +
` The following config would be written to ${layer.path}:\n`

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.

We attempt to update plugins at line 100, but dryRun displays no indication that we will update the set of plugins. We should add a statement that we will update plugins. Of course, if we remove our attempt to update plugins (see earlier comment), No change is needed here.

@zFernand0 zFernand0 marked this pull request as draft May 4, 2026 15:08
@zowe-robot zowe-robot moved this from Review/QA to In Progress in Zowe CLI Squad May 4, 2026
@JTonda JTonda moved this from In Progress to Release Backlog in Zowe CLI Squad May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Release Backlog

Development

Successfully merging this pull request may close these issues.

5 participants