@contrail/cli 3.5.5-alpha.960e382 → 3.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ $ npm install -g @contrail/cli
|
|
|
17
17
|
$ contrail COMMAND
|
|
18
18
|
running command...
|
|
19
19
|
$ contrail (--version)
|
|
20
|
-
@contrail/cli/3.5.5
|
|
20
|
+
@contrail/cli/3.5.5 linux-x64 node-v24.21.0
|
|
21
21
|
$ contrail --help [COMMAND]
|
|
22
22
|
USAGE
|
|
23
23
|
$ contrail COMMAND
|
|
@@ -34,5 +34,9 @@ export declare function uploadFeatureFlagsIfPermitted(dir: string): Promise<void
|
|
|
34
34
|
*
|
|
35
35
|
* Failures are collected and rethrown so an import never reports success while the org is
|
|
36
36
|
* missing flags.
|
|
37
|
+
*
|
|
38
|
+
* A file that lists no flags at all is skipped rather than applied: reconciling against an
|
|
39
|
+
* empty list would disable every flag in the target org, which is almost never what a copy
|
|
40
|
+
* is meant to do. Flags that should be off in the target have to be disabled by hand.
|
|
37
41
|
*/
|
|
38
42
|
export declare function uploadFeatureFlags(dir: string): Promise<void>;
|
|
@@ -107,6 +107,10 @@ exports.uploadFeatureFlagsIfPermitted = uploadFeatureFlagsIfPermitted;
|
|
|
107
107
|
*
|
|
108
108
|
* Failures are collected and rethrown so an import never reports success while the org is
|
|
109
109
|
* missing flags.
|
|
110
|
+
*
|
|
111
|
+
* A file that lists no flags at all is skipped rather than applied: reconciling against an
|
|
112
|
+
* empty list would disable every flag in the target org, which is almost never what a copy
|
|
113
|
+
* is meant to do. Flags that should be off in the target have to be disabled by hand.
|
|
110
114
|
*/
|
|
111
115
|
async function uploadFeatureFlags(dir) {
|
|
112
116
|
const filePath = path.join(dir, export_1.FEATURE_FLAGS_FILE);
|
|
@@ -114,11 +118,16 @@ async function uploadFeatureFlags(dir) {
|
|
|
114
118
|
logger_1.default.info(`${chalk_1.default.yellow.bold('FEATURE FLAGS')} No feature flags found in ${dir}. Skipping.`);
|
|
115
119
|
return;
|
|
116
120
|
}
|
|
117
|
-
const data = (0, file_helpers_1.loadData)(filePath);
|
|
121
|
+
const data = (0, file_helpers_1.loadData)(filePath) ?? [];
|
|
118
122
|
if (!Array.isArray(data)) {
|
|
119
123
|
throw new Error(`${filePath} must contain a YAML list of feature flags`);
|
|
120
124
|
}
|
|
121
125
|
const featureNamesToEnable = new Set(data.map(toFeatureName).filter(Boolean));
|
|
126
|
+
if (featureNamesToEnable.size === 0) {
|
|
127
|
+
logger_1.default.info(`${chalk_1.default.yellow.bold('FEATURE FLAGS')} ${filePath} lists no feature flags. Skipping, so ` +
|
|
128
|
+
'the flags currently enabled in this org are left as they are.');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
122
131
|
const existingFlags = (await (0, feature_flags_1.getFeatureFlags)()) ?? [];
|
|
123
132
|
const enabledFeatureNames = new Set(existingFlags.map((flag) => flag?.featureName));
|
|
124
133
|
const toCreate = [...featureNamesToEnable].filter((name) => !enabledFeatureNames.has(name));
|
|
@@ -58,6 +58,26 @@ describe('uploadFeatureFlags', () => {
|
|
|
58
58
|
expect(mocked(feature_flags_1.getFeatureFlags)).not.toHaveBeenCalled();
|
|
59
59
|
expect(mocked(feature_flags_1.createFeatureFlag)).not.toHaveBeenCalled();
|
|
60
60
|
});
|
|
61
|
+
it('skips an export that lists no flags instead of disabling everything in the target org', async () => {
|
|
62
|
+
mocked(file_helpers_1.loadData).mockReturnValue([]);
|
|
63
|
+
mocked(feature_flags_1.getFeatureFlags).mockResolvedValue([
|
|
64
|
+
{ id: '1', featureName: 'SHOWROOM' },
|
|
65
|
+
{ id: '2', featureName: 'DEV_TABS' },
|
|
66
|
+
]);
|
|
67
|
+
const infoSpy = jest.spyOn(logger_1.default, 'info').mockImplementation(() => undefined);
|
|
68
|
+
await (0, import_1.uploadFeatureFlags)('export-dir');
|
|
69
|
+
expect(mocked(feature_flags_1.deleteFeatureFlag)).not.toHaveBeenCalled();
|
|
70
|
+
expect(mocked(feature_flags_1.createFeatureFlag)).not.toHaveBeenCalled();
|
|
71
|
+
expect(infoSpy).toHaveBeenCalledWith(expect.stringContaining('lists no feature flags'));
|
|
72
|
+
infoSpy.mockRestore();
|
|
73
|
+
});
|
|
74
|
+
it('treats a blank feature-flags.yml the same as an empty list', async () => {
|
|
75
|
+
mocked(file_helpers_1.loadData).mockReturnValue(undefined);
|
|
76
|
+
mocked(feature_flags_1.getFeatureFlags).mockResolvedValue([{ id: '1', featureName: 'SHOWROOM' }]);
|
|
77
|
+
await (0, import_1.uploadFeatureFlags)('export-dir');
|
|
78
|
+
expect(mocked(feature_flags_1.getFeatureFlags)).not.toHaveBeenCalled();
|
|
79
|
+
expect(mocked(feature_flags_1.deleteFeatureFlag)).not.toHaveBeenCalled();
|
|
80
|
+
});
|
|
61
81
|
it('enables every flag from the export that the target org is missing', async () => {
|
|
62
82
|
mocked(file_helpers_1.loadData).mockReturnValue([{ featureName: 'SHOWROOM' }, { featureName: 'PROMOTE' }]);
|
|
63
83
|
mocked(feature_flags_1.getFeatureFlags).mockResolvedValue([{ id: '1', featureName: 'SHOWROOM' }]);
|
package/oclif.manifest.json
CHANGED