Skip to content

Fix external plugin migration pipeline issues#185

Open
cs-raj wants to merge 6 commits into
feat/migrate-external-cli-plugins-v2from
fix/external-plugin-migration
Open

Fix external plugin migration pipeline issues#185
cs-raj wants to merge 6 commits into
feat/migrate-external-cli-plugins-v2from
fix/external-plugin-migration

Conversation

@cs-raj
Copy link
Copy Markdown
Contributor

@cs-raj cs-raj commented May 27, 2026

fix: CI unit test failures across migrated external plugins

Summary

This PR fixes a series of CI failures discovered after migrating external CLI plugins into the monorepo. All failures were silent in CI because they either threw before tests ran, produced exit codes that masked the real error, or targeted wrong module instances due to the build/test environment mismatch.


Fixes

1. contentstack-apps-cli — CI crash: Region not configured at module load time

Problem:
Several test files (create.test.ts, deploy.test.ts, reinstall.test.ts, common-utils.test.ts, etc.) called configHandler.get("region") and getDeveloperHubUrl() at the top level — outside of any describe/it block. In CI, no region is pre-configured, so getDeveloperHubUrl() threw "Region not configured. Please set the region..." before a single test could register. This caused mocha to crash entirely.

Fix (test/helpers/init.js):
Added a mock NA region via configHandler.set() in the shared mocha init file. This file is required before any test file is loaded, ensuring the region is available at module-load time in CI without affecting real CLI configuration.

const { configHandler } = require('@contentstack/cli-utilities')
if (!configHandler.get('region')) {
  configHandler.set('region', {
    name: 'NA',
    cma: 'https://api.contentstack.io',
    cda: 'https://cdn.contentstack.io',
  })
}

2. contentstack-apps-cli — 5 deploy tests failing in CI (exit code 5)

Problem:
deploy.test.ts (and previously update.test.ts) used a try/catch pattern to load stubs from lib/ if that directory existed, falling back to src/ otherwise:

try {
  BaseCommandToStub = require("lib/base-command").BaseCommand;  // succeeds in CI
} catch {
  BaseCommandToStub = BaseCommand;  // only used locally
}

The CI workflow runs pnpm build before tests, so lib/ always exists in CI. This meant stubs were applied to lib/ class instances. However, oclif's tsPath mechanism (activated by NODE_ENV=development in init.js) redirects all command loading from lib/src/ at runtime. The actual command ran against src/ module instances — completely different objects from those being stubbed — so stubs had no effect, real HTTP calls went out, and all 5 tests failed.

The exit code 5 matched the 5 failing tests (mocha exits with the number of failures), and the --reporter json flag in the test:unit:report:json script suppressed terminal output, hiding the actual errors.

Fix (deploy.test.ts and update.test.ts):
Removed the try/catch lib/ loading pattern entirely. Both files now import directly from src/ — the same module instances oclif loads via tsPath — making stubs work correctly regardless of whether lib/ exists.

// Before (broken in CI when lib/ is built)
try {
  LibDeploy = require("lib/commands/app/deploy").default;
} catch { LibDeploy = Deploy; }

// After (always targets src/, matching what runCommand loads)
const LibDeploy = Deploy;
import * as libCommonUtils from "../../../../src/util/common-utils";
import * as libInquirer from "../../../../src/util/inquirer";

Also fixed two TypeScript type errors introduced by switching to typed imports:

  • selectProject stub changed from .resolves(null) to .resolves(undefined) (return type is LaunchProjectRes | undefined, not null)
  • Added missing developerHubAppUid: null to a mock LaunchProjectRes object

3. contentstack-content-typeCannot find module 'cli-ux'

Problem:
compare.test.ts mocked cli-ux for the open browser call, but the source (compare.ts) had already been migrated to use the standalone open package directly. The mock was targeting a package that was no longer imported, so the real open() call executed in tests.

Fix (compare.ts + compare.test.ts):
Updated the source to import from open and updated the test mock to match:

// compare.ts
- import cli from 'cli-ux'
+ import open from 'open'
- await cli.open(path)
+ await open(path)

// compare.test.ts
- jest.mock('cli-ux', () => ({ default: { open: jest.fn() } }))
+ jest.mock('open', () => jest.fn().mockResolvedValue(undefined))

4. contentstack-cli-cm-regex-validate — ESLint v8 + eslint-config-oclif v6 incompatibility

Problem:
The package used ESLint v8 with eslint-config-oclif v6. eslint-config-oclif v6 is ESM-only flat config — it cannot be loaded by ESLint v8's CommonJS require(). The posttest lint step failed immediately with a config resolution error.

Additionally, the old .eslintrc config bundled its own @typescript-eslint/eslint-plugin and eslint-plugin-unicorn versions, which conflicted with the versions brought in by eslint-config-oclif.

Fix (package.json + new eslint.config.mjs):
Upgraded to ESLint v9 (which supports flat config natively), removed the now-redundant standalone plugin packages, created a new eslint.config.mjs flat config file matching the pattern used by other packages in the monorepo, and updated the lint/posttest scripts to use the new flat config invocation.

// package.json
- "eslint": "^8.57.1",
+ "eslint": "^9.26.0",
- "@typescript-eslint/eslint-plugin": "^8.59.2",   // removed
- "eslint-plugin-unicorn": "^48.0.1",               // removed
- "posttest": "eslint . --ext .ts --config .eslintrc",
+ "posttest": "eslint src/**/*.ts",

5. contentstack-cli-cm-regex-validate — Source code refactored for ESLint v9 compliance

Problem:
With the upgraded ESLint v9 + flat config, the existing source files had violations the old config was silently allowing (import ordering, arrow function style, semicolons, spacing).

Fix (multiple src/ files):
Applied consistent formatting fixes across validate-regex.ts, generate-output.ts, interactive.ts, connect-stack.ts, process-stack.ts, and safe-regex.ts — import ordering, arrow functions, semicolon removal, object spacing — to satisfy the new lint rules. Also reordered static examples before static flags in the command class to match oclif convention.


6. contentstack-cli-tsgen — Integration tests running in the unit test workflow

Problem:
The unit-test.yml workflow ran npm run test for the tsgen plugin. The test script runs jest --testPathPattern=tests, which matches tests/integration/ — the only test directory. Integration tests throw at module load time if TOKEN_ALIAS env var is not set:

if (!tokenAlias) {
  throw new Error("TOKEN_ALIAS environment variable is not set");
}

TOKEN_ALIAS is a delivery token alias that must be registered in the csdx CLI config via csdx auth:tokens:add. The unit test workflow has no access to these secrets. A dedicated tsgen-integration-test.yml workflow already exists that properly sets up the CLI, registers the token, and provides TOKEN_ALIAS from GitHub Secrets.

Fix (unit-test.yml):
Removed the tsgen step from unit-test.yml entirely. Integration tests are already handled by the dedicated tsgen-integration-test.yml workflow on pull requests. Additionally updated tsgen-integration-test.yml to use npm run test (which runs all tests including integration) instead of test:integration to ensure the full test suite runs in the right environment.


7. Dependency updates across packages

Bumped minor/patch versions of shared dependencies to resolve lockfile conflicts introduced during the monorepo migration:

  • axios: ^1.15.0^1.16.1 (apps-cli)
  • oclif: ^4.23.0^4.23.8 (apps-cli, regex-validate, tsgen, and others)
  • Various @contentstack/* peer dependency alignments across contentstack-audit, contentstack-bootstrap, contentstack-clone, contentstack-export, contentstack-import, contentstack-migrate-rte, contentstack-migration, contentstack-query-export, contentstack-seed, contentstack-variants, and others
  • Updated pnpm-lock.yaml to reflect all of the above

Packages affected

Package Change type
contentstack-apps-cli Test infra fix (region mock, stub target, deploy tests)
contentstack-content-type Source + test fix (cli-uxopen)
contentstack-cli-cm-regex-validate ESLint v8→v9 upgrade, source lint fixes
contentstack-cli-tsgen Removed from unit test workflow
.github/workflows/unit-test.yml Removed tsgen step, corrected apps-cli script name
.github/workflows/tsgen-integration-test.yml Fixed test script reference
Multiple packages Dependency version bumps + lockfile update

Test plan

  • All steps in unit-test.yml pass in CI (no TOKEN_ALIAS, no built lib/ conflict)
  • tsgen-integration-test.yml runs correctly on PRs with secrets configured
  • contentstack-apps-cli: 61 unit tests pass (npm run test:unit:report)
  • contentstack-content-type: 76 unit tests pass (npm run test:unit)
  • contentstack-cli-cm-regex-validate: 22 unit tests pass + ESLint clean (npm run test:unit)
  • No regressions in any other package listed above

@cs-raj cs-raj requested a review from a team as a code owner May 27, 2026 19:01
@github-actions
Copy link
Copy Markdown

🔒 Security Scan Results

ℹ️ Note: Only vulnerabilities with available fixes (upgrades or patches) are counted toward thresholds.

Check Type Count (with fixes) Without fixes Threshold Result
🔴 Critical Severity 0 0 10 ✅ Passed
🟠 High Severity 3 56 25 ✅ Passed
🟡 Medium Severity 0 2 500 ✅ Passed
🔵 Low Severity 0 0 1000 ✅ Passed

⏱️ SLA Breach Summary

⚠️ Warning: The following vulnerabilities have exceeded their SLA thresholds (days since publication).

Severity Breaches (with fixes) Breaches (no fixes) SLA Threshold (with/no fixes) Status
🔴 Critical 0 0 15 / 30 days ✅ Passed
🟠 High 0 0 30 / 120 days ✅ Passed
🟡 Medium 0 1 90 / 365 days ⚠️ Warning
🔵 Low 0 0 180 / 365 days ✅ Passed

ℹ️ Vulnerabilities Without Available Fixes (Informational Only)

The following vulnerabilities were detected but do not have fixes available (no upgrade or patch). These are excluded from failure thresholds:

  • Critical without fixes: 0
  • High without fixes: 56
  • Medium without fixes: 2
  • Low without fixes: 0

⚠️ BUILD PASSED WITH WARNINGS - SLA breaches detected for issues without available fixes

Consider reviewing these vulnerabilities when fixes become available.

@github-actions
Copy link
Copy Markdown

🔒 Security Scan Results

ℹ️ Note: Only vulnerabilities with available fixes (upgrades or patches) are counted toward thresholds.

Check Type Count (with fixes) Without fixes Threshold Result
🔴 Critical Severity 0 0 10 ✅ Passed
🟠 High Severity 3 56 25 ✅ Passed
🟡 Medium Severity 0 2 500 ✅ Passed
🔵 Low Severity 0 0 1000 ✅ Passed

⏱️ SLA Breach Summary

⚠️ Warning: The following vulnerabilities have exceeded their SLA thresholds (days since publication).

Severity Breaches (with fixes) Breaches (no fixes) SLA Threshold (with/no fixes) Status
🔴 Critical 0 0 15 / 30 days ✅ Passed
🟠 High 0 0 30 / 120 days ✅ Passed
🟡 Medium 0 1 90 / 365 days ⚠️ Warning
🔵 Low 0 0 180 / 365 days ✅ Passed

ℹ️ Vulnerabilities Without Available Fixes (Informational Only)

The following vulnerabilities were detected but do not have fixes available (no upgrade or patch). These are excluded from failure thresholds:

  • Critical without fixes: 0
  • High without fixes: 56
  • Medium without fixes: 2
  • Low without fixes: 0

⚠️ BUILD PASSED WITH WARNINGS - SLA breaches detected for issues without available fixes

Consider reviewing these vulnerabilities when fixes become available.

@cs-raj cs-raj changed the title Fix/external plugin migration Fix external plugin migration pipeline issues May 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant