@hubspot/cli 8.6.0-beta.1 → 8.7.0-beta.0
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/api/releases.d.ts +36 -0
- package/api/releases.js +41 -0
- package/bin/cli.js +3 -3
- package/commands/account/auth.js +2 -2
- package/commands/account/clean.js +2 -2
- package/commands/account/createOverride.js +2 -2
- package/commands/account/info.js +2 -2
- package/commands/account/link.js +2 -2
- package/commands/account/list.js +2 -2
- package/commands/account/remove.js +2 -2
- package/commands/account/removeOverride.js +2 -2
- package/commands/account/rename.js +2 -2
- package/commands/account/unlink.js +2 -2
- package/commands/account/use.js +2 -2
- package/commands/api.js +2 -2
- package/commands/app/migrate.js +2 -2
- package/commands/app/secret/add.js +2 -2
- package/commands/app/secret/delete.js +2 -2
- package/commands/app/secret/list.js +2 -2
- package/commands/app/secret/update.js +2 -2
- package/commands/auth.js +2 -2
- package/commands/cms/app/create.js +2 -2
- package/commands/cms/convertFields.js +2 -2
- package/commands/cms/delete.js +2 -2
- package/commands/cms/fetch.js +2 -2
- package/commands/cms/function/create.js +2 -2
- package/commands/cms/function/deploy.js +2 -2
- package/commands/cms/function/list.js +2 -2
- package/commands/cms/function/logs.js +2 -2
- package/commands/cms/function/server.js +2 -2
- package/commands/cms/getReactModule.js +2 -2
- package/commands/cms/lighthouseScore.js +2 -2
- package/commands/cms/lint.js +2 -2
- package/commands/cms/list.js +2 -2
- package/commands/cms/module/create.js +8 -2
- package/commands/cms/module/marketplace-validate.js +2 -2
- package/commands/cms/mv.js +2 -2
- package/commands/cms/template/create.js +2 -2
- package/commands/cms/theme/create.js +2 -2
- package/commands/cms/theme/generate-selectors.js +2 -2
- package/commands/cms/theme/marketplace-validate.js +2 -2
- package/commands/cms/theme/preview.js +2 -2
- package/commands/cms/upload.js +2 -2
- package/commands/cms/watch.js +2 -2
- package/commands/cms/webpack/create.js +2 -2
- package/commands/completion.js +2 -2
- package/commands/config/migrate.js +2 -2
- package/commands/config/set.js +2 -2
- package/commands/customObject/create.js +2 -2
- package/commands/customObject/createSchema.js +2 -2
- package/commands/customObject/deleteSchema.js +2 -2
- package/commands/customObject/fetchAllSchemas.js +2 -2
- package/commands/customObject/fetchSchema.js +2 -2
- package/commands/customObject/listSchemas.js +2 -2
- package/commands/customObject/updateSchema.js +2 -2
- package/commands/doctor.js +2 -2
- package/commands/feedback.js +2 -2
- package/commands/filemanager/fetch.js +2 -2
- package/commands/filemanager/upload.js +2 -2
- package/commands/getStarted.js +2 -2
- package/commands/hubdb/clear.js +2 -2
- package/commands/hubdb/create.js +2 -2
- package/commands/hubdb/delete.js +2 -2
- package/commands/hubdb/fetch.js +2 -2
- package/commands/hubdb/list.js +2 -2
- package/commands/init.js +2 -2
- package/commands/mcp/setup.js +2 -2
- package/commands/mcp/start.js +2 -2
- package/commands/open.js +2 -2
- package/commands/project/add.js +2 -2
- package/commands/project/appInstallStatus.d.ts +2 -2
- package/commands/project/appInstallStatus.js +3 -2
- package/commands/project/create.js +2 -2
- package/commands/project/delete.js +2 -2
- package/commands/project/deploy.js +2 -2
- package/commands/project/dev/index.js +2 -2
- package/commands/project/download.js +2 -2
- package/commands/project/info.d.ts +2 -2
- package/commands/project/info.js +3 -2
- package/commands/project/installDeps.js +2 -2
- package/commands/project/lint.js +7 -5
- package/commands/project/list.d.ts +2 -2
- package/commands/project/list.js +3 -2
- package/commands/project/listBuilds.js +2 -2
- package/commands/project/logs.js +2 -2
- package/commands/project/migrate.js +2 -2
- package/commands/project/open.js +2 -2
- package/commands/project/profile/add.js +2 -2
- package/commands/project/profile/delete.js +2 -2
- package/commands/project/release/create.d.ts +7 -0
- package/commands/project/release/create.js +159 -0
- package/commands/project/release/info.d.ts +6 -0
- package/commands/project/release/info.js +147 -0
- package/commands/project/release/list.d.ts +6 -0
- package/commands/project/release/list.js +111 -0
- package/commands/project/release.d.ts +3 -0
- package/commands/project/release.js +20 -0
- package/commands/project/updateDeps.js +2 -2
- package/commands/project/upload.d.ts +3 -0
- package/commands/project/upload.js +77 -9
- package/commands/project/validate.js +2 -2
- package/commands/project/watch.js +2 -2
- package/commands/project.js +2 -0
- package/commands/sandbox/create.js +2 -2
- package/commands/sandbox/delete.js +2 -2
- package/commands/secret/addSecret.js +2 -2
- package/commands/secret/deleteSecret.js +2 -2
- package/commands/secret/listSecret.js +2 -2
- package/commands/secret/updateSecret.js +2 -2
- package/commands/testAccount/create.js +2 -2
- package/commands/testAccount/createConfig.js +2 -2
- package/commands/testAccount/delete.js +2 -2
- package/commands/testAccount/importData.js +2 -2
- package/commands/upgrade.js +2 -2
- package/lang/en.d.ts +92 -0
- package/lang/en.js +92 -0
- package/lib/api/usageTracking.d.ts +29 -0
- package/lib/api/usageTracking.js +28 -0
- package/lib/commonOpts.js +0 -1
- package/lib/constants.d.ts +1 -0
- package/lib/constants.js +1 -0
- package/lib/projects/localDev/helpers/project.js +1 -1
- package/lib/projects/npmAuditOnUpload.d.ts +10 -0
- package/lib/projects/npmAuditOnUpload.js +73 -0
- package/lib/projects/pollProjectBuildAndDeploy.d.ts +5 -1
- package/lib/projects/pollProjectBuildAndDeploy.js +3 -2
- package/lib/projects/preview.d.ts +7 -0
- package/lib/projects/preview.js +48 -0
- package/lib/projects/uieLinting.d.ts +4 -0
- package/lib/projects/uieLinting.js +36 -1
- package/lib/projects/upload.d.ts +3 -1
- package/lib/projects/upload.js +26 -6
- package/lib/projects/validateLintConfigOnUpload.d.ts +9 -0
- package/lib/projects/validateLintConfigOnUpload.js +45 -0
- package/lib/projects/workspaces.d.ts +11 -1
- package/lib/projects/workspaces.js +27 -12
- package/lib/usageTracking.d.ts +7 -17
- package/lib/usageTracking.js +43 -29
- package/lib/yargs/makeWrappedYargsHandler.d.ts +3 -0
- package/lib/yargs/{makeYargsHandlerWithUsageTracking.js → makeWrappedYargsHandler.js} +29 -3
- package/mcp-server/tools/cms/HsCreateFunctionTool.js +2 -2
- package/mcp-server/tools/cms/HsCreateModuleTool.js +4 -5
- package/mcp-server/tools/cms/HsCreateTemplateTool.js +2 -2
- package/mcp-server/tools/cms/HsFunctionLogsTool.js +2 -3
- package/mcp-server/tools/cms/HsListFunctionsTool.js +2 -3
- package/mcp-server/tools/cms/HsListTool.js +2 -2
- package/mcp-server/utils/toolUsageTracking.js +10 -6
- package/package.json +4 -4
- package/lib/yargs/makeYargsHandlerWithUsageTracking.d.ts +0 -3
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { runNpmAuditJson } from '@hubspot/ui-extensions-dev-server';
|
|
3
|
+
import { lib } from '../../lang/en.js';
|
|
4
|
+
import { uiLogger } from '../ui/logger.js';
|
|
5
|
+
export function summarizeNpmAuditJson(source) {
|
|
6
|
+
try {
|
|
7
|
+
const data = JSON.parse(source);
|
|
8
|
+
const errorMessage = data.error?.message ?? data.error?.summary;
|
|
9
|
+
if (errorMessage) {
|
|
10
|
+
return errorMessage;
|
|
11
|
+
}
|
|
12
|
+
const v = data.metadata?.vulnerabilities;
|
|
13
|
+
if (!v) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const total = v.total ?? 0;
|
|
17
|
+
if (total === 0) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const severityOrder = [
|
|
21
|
+
'critical',
|
|
22
|
+
'high',
|
|
23
|
+
'moderate',
|
|
24
|
+
'low',
|
|
25
|
+
'info',
|
|
26
|
+
];
|
|
27
|
+
const parts = severityOrder
|
|
28
|
+
.filter(severity => (v[severity] ?? 0) > 0)
|
|
29
|
+
.map(severity => `${v[severity]} ${severity}`);
|
|
30
|
+
return `${total} total (${parts.join(', ')})`;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function runNpmAuditsBeforeProjectUpload({ srcDir, projectDir, parsedPackageJsons, isLegacyPlatform, }) {
|
|
37
|
+
const auditRoots = new Set();
|
|
38
|
+
if (isLegacyPlatform) {
|
|
39
|
+
auditRoots.add(srcDir);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
for (const { dir } of parsedPackageJsons) {
|
|
43
|
+
auditRoots.add(dir);
|
|
44
|
+
}
|
|
45
|
+
if (auditRoots.size === 0) {
|
|
46
|
+
auditRoots.add(srcDir);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const auditRootArray = [...auditRoots];
|
|
50
|
+
const results = await Promise.all(auditRootArray.map(auditRoot => runNpmAuditJson(auditRoot)));
|
|
51
|
+
for (let i = 0; i < auditRootArray.length; i++) {
|
|
52
|
+
const auditRoot = auditRootArray[i];
|
|
53
|
+
const result = results[i];
|
|
54
|
+
if (result.skipped) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const relativeRoot = path.relative(projectDir, auditRoot) || '.';
|
|
58
|
+
const summary = summarizeNpmAuditJson(result.source);
|
|
59
|
+
if (result.exitCode === 127) {
|
|
60
|
+
uiLogger.warn(lib.projectUpload.handleProjectUpload.npmAuditNpmUnavailable(relativeRoot));
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (summary) {
|
|
64
|
+
uiLogger.warn(lib.projectUpload.handleProjectUpload.npmAuditIssues(relativeRoot, summary));
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (result.exitCode !== 0) {
|
|
68
|
+
uiLogger.warn(lib.projectUpload.handleProjectUpload.npmAuditNonZeroExit(relativeRoot, result.exitCode));
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
uiLogger.success(lib.projectUpload.handleProjectUpload.npmAuditClean(relativeRoot));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -6,5 +6,9 @@ type PollTaskStatusFunction<T extends ProjectTask> = (accountId: number, taskNam
|
|
|
6
6
|
export declare const pollBuildStatus: PollTaskStatusFunction<Build>;
|
|
7
7
|
export declare const pollDeployStatus: PollTaskStatusFunction<Deploy>;
|
|
8
8
|
export declare function displayWarnLogs(accountId: number, projectName: string, taskId: number, isDeploy?: boolean): Promise<void>;
|
|
9
|
-
|
|
9
|
+
type PollOptions = {
|
|
10
|
+
silenceLogs?: boolean;
|
|
11
|
+
skipDeploy?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare function pollProjectBuildAndDeploy(accountId: number, projectConfig: ProjectConfig, tempFile: FileResult, buildId: number, options?: PollOptions): Promise<ProjectPollResult>;
|
|
10
14
|
export {};
|
|
@@ -291,7 +291,8 @@ export async function displayWarnLogs(accountId, projectName, taskId, isDeploy =
|
|
|
291
291
|
});
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
|
-
export async function pollProjectBuildAndDeploy(accountId, projectConfig, tempFile, buildId,
|
|
294
|
+
export async function pollProjectBuildAndDeploy(accountId, projectConfig, tempFile, buildId, options = {}) {
|
|
295
|
+
const { silenceLogs = false, skipDeploy = false } = options;
|
|
295
296
|
let buildStatus = await pollBuildStatus(accountId, projectConfig.name, buildId, null, silenceLogs);
|
|
296
297
|
if (!silenceLogs) {
|
|
297
298
|
uiLine();
|
|
@@ -306,7 +307,7 @@ export async function pollProjectBuildAndDeploy(accountId, projectConfig, tempFi
|
|
|
306
307
|
result.succeeded = false;
|
|
307
308
|
return result;
|
|
308
309
|
}
|
|
309
|
-
else if (buildStatus.isAutoDeployEnabled) {
|
|
310
|
+
else if (buildStatus.isAutoDeployEnabled && !skipDeploy) {
|
|
310
311
|
if (!silenceLogs) {
|
|
311
312
|
uiLogger.log(lib.projectBuildAndDeploy.pollProjectBuildAndDeploy.buildSucceededAutomaticallyDeploying(buildId, uiAccountDescription(accountId)));
|
|
312
313
|
await displayWarnLogs(accountId, projectConfig.name, buildId);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { triggerAutoRelease, getAutoReleaseStatus, } from '../../api/releases.js';
|
|
2
|
+
import { PREVIEW_POLL_TIMEOUT } from '../constants.js';
|
|
3
|
+
import { poll } from '../polling.js';
|
|
4
|
+
import SpinniesManager from '../ui/SpinniesManager.js';
|
|
5
|
+
import { logError, ApiErrorContext } from '../errorHandlers/index.js';
|
|
6
|
+
import { lib } from '../../lang/en.js';
|
|
7
|
+
export async function triggerAndPollPreview(accountId, projectId, buildId, targetPortalId) {
|
|
8
|
+
let triggerResponse;
|
|
9
|
+
SpinniesManager.add('preview', {
|
|
10
|
+
text: lib.projectPreview.triggeringPreview(buildId, targetPortalId),
|
|
11
|
+
succeedColor: 'white',
|
|
12
|
+
});
|
|
13
|
+
try {
|
|
14
|
+
const { data } = await triggerAutoRelease(accountId, projectId, buildId, targetPortalId);
|
|
15
|
+
triggerResponse = data;
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
SpinniesManager.fail('preview', {
|
|
19
|
+
text: lib.projectPreview.triggerFailed,
|
|
20
|
+
});
|
|
21
|
+
logError(e, new ApiErrorContext({
|
|
22
|
+
accountId,
|
|
23
|
+
request: 'preview trigger',
|
|
24
|
+
}));
|
|
25
|
+
return { succeeded: false };
|
|
26
|
+
}
|
|
27
|
+
const { releaseTag, appId } = triggerResponse;
|
|
28
|
+
SpinniesManager.update('preview', {
|
|
29
|
+
text: lib.projectPreview.pollingStatus(releaseTag, targetPortalId),
|
|
30
|
+
});
|
|
31
|
+
try {
|
|
32
|
+
await poll(() => getAutoReleaseStatus(accountId, projectId, targetPortalId, releaseTag, appId), { successStates: ['COMPLETE'], errorStates: [] }, PREVIEW_POLL_TIMEOUT);
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
SpinniesManager.fail('preview', {
|
|
36
|
+
text: lib.projectPreview.pollFailed,
|
|
37
|
+
});
|
|
38
|
+
logError(e, new ApiErrorContext({
|
|
39
|
+
accountId,
|
|
40
|
+
request: 'preview status',
|
|
41
|
+
}));
|
|
42
|
+
return { succeeded: false, releaseTag, appId };
|
|
43
|
+
}
|
|
44
|
+
SpinniesManager.succeed('preview', {
|
|
45
|
+
text: lib.projectPreview.succeeded(releaseTag, targetPortalId),
|
|
46
|
+
});
|
|
47
|
+
return { succeeded: true, releaseTag, appId };
|
|
48
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { LoadedProjectConfig } from './config.js';
|
|
1
2
|
export declare const REQUIRED_PACKAGES_AND_MIN_VERSIONS: {
|
|
2
3
|
readonly eslint: "9.0.0";
|
|
3
4
|
readonly '@eslint/js': "9.0.0";
|
|
@@ -23,6 +24,9 @@ export declare function hasEslintConfig(directory: string): boolean;
|
|
|
23
24
|
export declare function hasDeprecatedEslintConfig(directory: string): boolean;
|
|
24
25
|
export declare function getDeprecatedEslintConfigFiles(directory: string): string[];
|
|
25
26
|
export declare function createEslintConfig(directory: string, platformVersion?: string | null): Promise<string>;
|
|
27
|
+
export declare function getUieLintablePackageJsonLocations(projectConfig: LoadedProjectConfig): Promise<string[]>;
|
|
28
|
+
export declare const HUBSPOT_UI_EXTENSIONS_RULE_PREFIX = "@hubspot/ui-extensions/";
|
|
29
|
+
export declare function isHubSpotEslintConfigActive(directory: string): Promise<boolean>;
|
|
26
30
|
export declare function lintPackagesInDirectory(directory: string, projectDir?: string): Promise<{
|
|
27
31
|
success: boolean;
|
|
28
32
|
output: string;
|
|
@@ -10,7 +10,8 @@ import { uiLogger } from '../ui/logger.js';
|
|
|
10
10
|
import { clearPackageJsonCache, safeGetPackageJsonCached, } from '../npm/packageJson.js';
|
|
11
11
|
import { debugError } from '../errorHandlers/index.js';
|
|
12
12
|
import { isLegacyProject } from '@hubspot/project-parsing-lib/projects';
|
|
13
|
-
import {
|
|
13
|
+
import { DEFAULT_PROJECT_TEMPLATE_BRANCH, HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH, } from '../constants.js';
|
|
14
|
+
import { CARDS_KEY, Components, PAGES_KEY, SETTINGS_KEY, } from '@hubspot/project-parsing-lib/constants';
|
|
14
15
|
export const REQUIRED_PACKAGES_AND_MIN_VERSIONS = {
|
|
15
16
|
eslint: '9.0.0',
|
|
16
17
|
'@eslint/js': '9.0.0',
|
|
@@ -40,6 +41,11 @@ const DEPRECATED_ESLINT_CONFIG_FILES = [
|
|
|
40
41
|
'.eslintrc.json',
|
|
41
42
|
'.eslintrc',
|
|
42
43
|
];
|
|
44
|
+
const UIE_COMPONENTS = [
|
|
45
|
+
Components[CARDS_KEY],
|
|
46
|
+
Components[SETTINGS_KEY],
|
|
47
|
+
Components[PAGES_KEY],
|
|
48
|
+
];
|
|
43
49
|
export const LINT_SCRIPTS = {
|
|
44
50
|
lint: 'eslint .',
|
|
45
51
|
'lint:fix': 'eslint . --fix',
|
|
@@ -163,6 +169,35 @@ export async function createEslintConfig(directory, platformVersion) {
|
|
|
163
169
|
throw error;
|
|
164
170
|
}
|
|
165
171
|
}
|
|
172
|
+
export async function getUieLintablePackageJsonLocations(projectConfig) {
|
|
173
|
+
if (!projectConfig.projectDir || !projectConfig.projectConfig?.srcDir) {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
const srcDirAbsolute = path.resolve(projectConfig.projectDir, projectConfig.projectConfig.srcDir);
|
|
177
|
+
const uiePackageDirPrefixes = UIE_COMPONENTS.map(component => path.join(srcDirAbsolute, component.parentComponent
|
|
178
|
+
? Components[component.parentComponent].dir
|
|
179
|
+
: '', component.dir));
|
|
180
|
+
const allLocations = await getProjectPackageJsonLocations(projectConfig.projectDir);
|
|
181
|
+
return allLocations.filter(location => {
|
|
182
|
+
const resolvedLocation = path.resolve(location);
|
|
183
|
+
return uiePackageDirPrefixes.some(prefix => resolvedLocation.startsWith(prefix));
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
export const HUBSPOT_UI_EXTENSIONS_RULE_PREFIX = '@hubspot/ui-extensions/';
|
|
187
|
+
export async function isHubSpotEslintConfigActive(directory) {
|
|
188
|
+
const exec = util.promisify(execAsync);
|
|
189
|
+
try {
|
|
190
|
+
const { stdout } = await exec('npx eslint --print-config ./Component.tsx', {
|
|
191
|
+
cwd: directory,
|
|
192
|
+
});
|
|
193
|
+
const config = JSON.parse(stdout);
|
|
194
|
+
const rules = config.rules ?? {};
|
|
195
|
+
return Object.keys(rules).some(rule => rule.startsWith(HUBSPOT_UI_EXTENSIONS_RULE_PREFIX));
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
166
201
|
export async function lintPackagesInDirectory(directory, projectDir) {
|
|
167
202
|
const displayPath = projectDir
|
|
168
203
|
? path.relative(projectDir, directory)
|
package/lib/projects/upload.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ type ProjectUploadResult<T> = {
|
|
|
5
5
|
result?: T;
|
|
6
6
|
uploadError?: unknown;
|
|
7
7
|
projectNotFound?: boolean;
|
|
8
|
+
projectId?: number;
|
|
8
9
|
};
|
|
9
10
|
type HandleProjectUploadArg<T> = {
|
|
10
11
|
accountId: number;
|
|
@@ -16,9 +17,10 @@ type HandleProjectUploadArg<T> = {
|
|
|
16
17
|
isUploadCommand?: boolean;
|
|
17
18
|
sendIR?: boolean;
|
|
18
19
|
skipValidation?: boolean;
|
|
20
|
+
skipNpmAudit?: boolean;
|
|
19
21
|
profile?: string;
|
|
20
22
|
};
|
|
21
|
-
export declare function handleProjectUpload<T>({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage, forceCreate, isUploadCommand, sendIR, skipValidation, }: HandleProjectUploadArg<T>): Promise<ProjectUploadResult<T>>;
|
|
23
|
+
export declare function handleProjectUpload<T>({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage, forceCreate, isUploadCommand, sendIR, skipValidation, skipNpmAudit, }: HandleProjectUploadArg<T>): Promise<ProjectUploadResult<T>>;
|
|
22
24
|
export declare function validateSourceDirectory(srcDir: string, projectConfig: ProjectConfig, projectDir: string): Promise<void>;
|
|
23
25
|
export declare function validateNoHSMetaMismatch(srcDir: string, projectConfig: ProjectConfig): Promise<void>;
|
|
24
26
|
type HandleTranslateArg = {
|
package/lib/projects/upload.js
CHANGED
|
@@ -18,6 +18,8 @@ import { walk } from '@hubspot/local-dev-lib/fs';
|
|
|
18
18
|
import { LEGACY_CONFIG_FILES } from '../constants.js';
|
|
19
19
|
import { archiveWorkspacesAndDependencies, getPackageJsonPathsToUpdate, getLockfilePathsToUpdate, } from './workspaces.js';
|
|
20
20
|
import { isLegacyProject } from '@hubspot/project-parsing-lib/projects';
|
|
21
|
+
import { validateLintConfigOnUpload } from './validateLintConfigOnUpload.js';
|
|
22
|
+
import { runNpmAuditsBeforeProjectUpload } from './npmAuditOnUpload.js';
|
|
21
23
|
async function uploadProjectFiles(accountId, projectName, filePath, uploadMessage, platformVersion, intermediateRepresentation) {
|
|
22
24
|
const accountIdentifier = uiAccountDescription(accountId) || `${accountId}`;
|
|
23
25
|
SpinniesManager.add('upload', {
|
|
@@ -44,7 +46,7 @@ async function uploadProjectFiles(accountId, projectName, filePath, uploadMessag
|
|
|
44
46
|
}
|
|
45
47
|
return { buildId, error };
|
|
46
48
|
}
|
|
47
|
-
export async function handleProjectUpload({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage = '', forceCreate = false, isUploadCommand = false, sendIR = false, skipValidation = false, }) {
|
|
49
|
+
export async function handleProjectUpload({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage = '', forceCreate = false, isUploadCommand = false, sendIR = false, skipValidation = false, skipNpmAudit = false, }) {
|
|
48
50
|
const srcDir = path.resolve(projectDir, projectConfig.srcDir);
|
|
49
51
|
await validateSourceDirectory(srcDir, projectConfig, projectDir);
|
|
50
52
|
await validateNoHSMetaMismatch(srcDir, projectConfig);
|
|
@@ -54,11 +56,28 @@ export async function handleProjectUpload({ accountId, projectConfig, projectDir
|
|
|
54
56
|
// Versions <= 2025.1 do not support the new npm workspaces bundling behavior.
|
|
55
57
|
let workspaceMappings = [];
|
|
56
58
|
let fileDependencyMappings = [];
|
|
59
|
+
let parsedPackageJsons = [];
|
|
57
60
|
if (!isLegacyProject(projectConfig.platformVersion)) {
|
|
58
|
-
|
|
61
|
+
parsedPackageJsons = await findAndParsePackageJsonFiles(srcDir);
|
|
59
62
|
workspaceMappings = await collectWorkspaceDirectories(parsedPackageJsons);
|
|
60
63
|
fileDependencyMappings = await collectFileDependencies(parsedPackageJsons);
|
|
61
64
|
}
|
|
65
|
+
if (isUploadCommand && !skipValidation) {
|
|
66
|
+
await validateLintConfigOnUpload({
|
|
67
|
+
srcDir,
|
|
68
|
+
projectDir,
|
|
69
|
+
parsedPackageJsons,
|
|
70
|
+
isLegacyPlatform: isLegacyProject(projectConfig.platformVersion),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (isUploadCommand && !skipNpmAudit) {
|
|
74
|
+
await runNpmAuditsBeforeProjectUpload({
|
|
75
|
+
srcDir,
|
|
76
|
+
projectDir,
|
|
77
|
+
parsedPackageJsons,
|
|
78
|
+
isLegacyPlatform: isLegacyProject(projectConfig.platformVersion),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
62
81
|
const output = fs.createWriteStream(tempFile.name);
|
|
63
82
|
const archive = archiver('zip');
|
|
64
83
|
const result = new Promise((resolve, reject) => output.on('close', async function () {
|
|
@@ -79,7 +98,7 @@ export async function handleProjectUpload({ accountId, projectConfig, projectDir
|
|
|
79
98
|
return resolve({ uploadError: e });
|
|
80
99
|
}
|
|
81
100
|
}
|
|
82
|
-
const { projectExists } = await ensureProjectExists(accountId, projectConfig.name, {
|
|
101
|
+
const { projectExists, project } = await ensureProjectExists(accountId, projectConfig.name, {
|
|
83
102
|
forceCreate,
|
|
84
103
|
uploadCommand: isUploadCommand,
|
|
85
104
|
noLogs: true,
|
|
@@ -88,13 +107,14 @@ export async function handleProjectUpload({ accountId, projectConfig, projectDir
|
|
|
88
107
|
uiLogger.log(lib.projectUpload.handleProjectUpload.projectDoesNotExist(accountId));
|
|
89
108
|
return resolve({ projectNotFound: true });
|
|
90
109
|
}
|
|
110
|
+
const projectId = project?.id;
|
|
91
111
|
const { buildId, error } = await uploadProjectFiles(accountId, projectConfig.name, tempFile.name, uploadMessage, projectConfig.platformVersion, intermediateRepresentation);
|
|
92
112
|
if (error) {
|
|
93
|
-
resolve({ uploadError: error });
|
|
113
|
+
resolve({ uploadError: error, projectId });
|
|
94
114
|
}
|
|
95
115
|
else if (callbackFunc) {
|
|
96
116
|
const uploadResult = await callbackFunc(accountId, projectConfig, tempFile, buildId);
|
|
97
|
-
resolve({ result: uploadResult });
|
|
117
|
+
resolve({ result: uploadResult, projectId });
|
|
98
118
|
}
|
|
99
119
|
}
|
|
100
120
|
catch (e) {
|
|
@@ -123,7 +143,7 @@ export async function handleProjectUpload({ accountId, projectConfig, projectDir
|
|
|
123
143
|
return ignored ? false : file;
|
|
124
144
|
});
|
|
125
145
|
// Archive workspaces and file: dependencies
|
|
126
|
-
await archiveWorkspacesAndDependencies(archive, srcDir,
|
|
146
|
+
await archiveWorkspacesAndDependencies(archive, srcDir, workspaceMappings, fileDependencyMappings);
|
|
127
147
|
archive.finalize();
|
|
128
148
|
return result;
|
|
129
149
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ParsedPackageJson } from '@hubspot/project-parsing-lib/workspaces';
|
|
2
|
+
type ValidateLintConfigOnUploadArgs = {
|
|
3
|
+
srcDir: string;
|
|
4
|
+
projectDir: string;
|
|
5
|
+
parsedPackageJsons: ParsedPackageJson[];
|
|
6
|
+
isLegacyPlatform: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare function validateLintConfigOnUpload({ srcDir, projectDir, parsedPackageJsons, isLegacyPlatform, }: ValidateLintConfigOnUploadArgs): Promise<void>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { lib } from '../../lang/en.js';
|
|
3
|
+
import { uiLogger } from '../ui/logger.js';
|
|
4
|
+
import { areAllLintPackagesInstalled, hasEslintConfig, isHubSpotEslintConfigActive, } from './uieLinting.js';
|
|
5
|
+
export async function validateLintConfigOnUpload({ srcDir, projectDir, parsedPackageJsons, isLegacyPlatform, }) {
|
|
6
|
+
const lintRoots = new Set();
|
|
7
|
+
if (isLegacyPlatform) {
|
|
8
|
+
lintRoots.add(srcDir);
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
for (const { dir } of parsedPackageJsons) {
|
|
12
|
+
lintRoots.add(dir);
|
|
13
|
+
}
|
|
14
|
+
if (lintRoots.size === 0) {
|
|
15
|
+
lintRoots.add(srcDir);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
let hasAnyOutput = false;
|
|
19
|
+
for (const lintRoot of lintRoots) {
|
|
20
|
+
const relativeRoot = path.relative(projectDir, lintRoot) || '.';
|
|
21
|
+
let warnMessage;
|
|
22
|
+
if (!areAllLintPackagesInstalled(lintRoot)) {
|
|
23
|
+
warnMessage =
|
|
24
|
+
lib.projectUpload.handleProjectUpload.lintPackagesNotConfigured(relativeRoot);
|
|
25
|
+
}
|
|
26
|
+
else if (!hasEslintConfig(lintRoot)) {
|
|
27
|
+
warnMessage =
|
|
28
|
+
lib.projectUpload.handleProjectUpload.lintConfigNotFound(relativeRoot);
|
|
29
|
+
}
|
|
30
|
+
else if (!(await isHubSpotEslintConfigActive(lintRoot))) {
|
|
31
|
+
warnMessage =
|
|
32
|
+
lib.projectUpload.handleProjectUpload.lintHubSpotRulesNotActive(relativeRoot);
|
|
33
|
+
}
|
|
34
|
+
if (warnMessage) {
|
|
35
|
+
if (!hasAnyOutput) {
|
|
36
|
+
uiLogger.log('');
|
|
37
|
+
hasAnyOutput = true;
|
|
38
|
+
}
|
|
39
|
+
uiLogger.warn(warnMessage);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (hasAnyOutput) {
|
|
43
|
+
uiLogger.log('');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -12,6 +12,16 @@ export type WorkspaceArchiveResult = {
|
|
|
12
12
|
* Uses SHA256 truncated to 8 hex characters (4 billion possibilities).
|
|
13
13
|
*/
|
|
14
14
|
export declare function shortHash(input: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Converts native path separators to POSIX forward slashes.
|
|
17
|
+
*
|
|
18
|
+
* Zip entry names and npm workspace globs are POSIX-only. On Windows,
|
|
19
|
+
* `path.relative` returns backslash-separated paths; archiver normalizes
|
|
20
|
+
* its appended entry names to forward slashes but its filter callback
|
|
21
|
+
* receives forward-slashed names too. Without this normalization, lookups
|
|
22
|
+
* in our exclusion Sets miss on Windows and a file gets archived twice.
|
|
23
|
+
*/
|
|
24
|
+
export declare function toPosixPath(p: string): string;
|
|
15
25
|
/**
|
|
16
26
|
* Determines the archive path for an external workspace or file: dependency.
|
|
17
27
|
* Produces `_workspaces/<basename>-<hash>` with no subdirectory.
|
|
@@ -39,4 +49,4 @@ export declare function getLockfilePathsToUpdate(srcDir: string, workspaceMappin
|
|
|
39
49
|
* Main orchestration function that handles archiving of workspaces and file dependencies.
|
|
40
50
|
* This is the clean integration point for upload.ts.
|
|
41
51
|
*/
|
|
42
|
-
export declare function archiveWorkspacesAndDependencies(archive: archiver.Archiver, srcDir: string,
|
|
52
|
+
export declare function archiveWorkspacesAndDependencies(archive: archiver.Archiver, srcDir: string, workspaceMappings: WorkspaceMapping[], fileDependencyMappings: FileDependencyMapping[]): Promise<WorkspaceArchiveResult>;
|
|
@@ -12,6 +12,21 @@ import { lib } from '../../lang/en.js';
|
|
|
12
12
|
export function shortHash(input) {
|
|
13
13
|
return crypto.createHash('sha256').update(input).digest('hex').slice(0, 8);
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Converts native path separators to POSIX forward slashes.
|
|
17
|
+
*
|
|
18
|
+
* Zip entry names and npm workspace globs are POSIX-only. On Windows,
|
|
19
|
+
* `path.relative` returns backslash-separated paths; archiver normalizes
|
|
20
|
+
* its appended entry names to forward slashes but its filter callback
|
|
21
|
+
* receives forward-slashed names too. Without this normalization, lookups
|
|
22
|
+
* in our exclusion Sets miss on Windows and a file gets archived twice.
|
|
23
|
+
*/
|
|
24
|
+
export function toPosixPath(p) {
|
|
25
|
+
if (path.sep === path.posix.sep) {
|
|
26
|
+
return p;
|
|
27
|
+
}
|
|
28
|
+
return p.replaceAll(path.sep, path.posix.sep);
|
|
29
|
+
}
|
|
15
30
|
/**
|
|
16
31
|
* Determines the archive path for an external workspace or file: dependency.
|
|
17
32
|
* Produces `_workspaces/<basename>-<hash>` with no subdirectory.
|
|
@@ -20,7 +35,7 @@ export function shortHash(input) {
|
|
|
20
35
|
export function computeExternalArchivePath(absolutePath) {
|
|
21
36
|
const resolved = path.resolve(absolutePath);
|
|
22
37
|
const name = path.basename(resolved);
|
|
23
|
-
return path.join('_workspaces', `${name}-${shortHash(resolved)}`);
|
|
38
|
+
return path.posix.join('_workspaces', `${name}-${shortHash(resolved)}`);
|
|
24
39
|
}
|
|
25
40
|
/**
|
|
26
41
|
* Returns true if dir is inside srcDir (i.e. it will already be included
|
|
@@ -71,7 +86,7 @@ async function archiveWorkspaceDirectories(archive, srcDir, workspaceMappings) {
|
|
|
71
86
|
if (isInsideSrcDir(workspaceDir, srcDir)) {
|
|
72
87
|
// Internal: already in archive from srcDir walk.
|
|
73
88
|
// Store the relative path from the package.json directory so npm can resolve it.
|
|
74
|
-
const relPath = path.relative(path.dirname(sourcePackageJsonPath), path.resolve(workspaceDir));
|
|
89
|
+
const relPath = toPosixPath(path.relative(path.dirname(sourcePackageJsonPath), path.resolve(workspaceDir)));
|
|
75
90
|
packageWorkspaceEntries.get(sourcePackageJsonPath).push(relPath);
|
|
76
91
|
}
|
|
77
92
|
else {
|
|
@@ -89,7 +104,7 @@ async function archiveWorkspaceDirectories(archive, srcDir, workspaceMappings) {
|
|
|
89
104
|
externalsToArchive.push({ dir: workspaceDir, archivePath });
|
|
90
105
|
}
|
|
91
106
|
const relPkgJsonDir = path.relative(srcDir, path.dirname(sourcePackageJsonPath));
|
|
92
|
-
const relativeEntry = path.relative(relPkgJsonDir, archivePath);
|
|
107
|
+
const relativeEntry = toPosixPath(path.relative(relPkgJsonDir, archivePath));
|
|
93
108
|
packageWorkspaceEntries.get(sourcePackageJsonPath).push(relativeEntry);
|
|
94
109
|
}
|
|
95
110
|
}
|
|
@@ -130,7 +145,7 @@ async function archiveFileDependencies(archive, srcDir, fileDependencyMappings,
|
|
|
130
145
|
packageFileDeps.set(sourcePackageJsonPath, new Map());
|
|
131
146
|
}
|
|
132
147
|
const relPkgJsonDir = path.relative(srcDir, path.dirname(sourcePackageJsonPath));
|
|
133
|
-
const relativeArchivePath = path.relative(relPkgJsonDir, archivePath);
|
|
148
|
+
const relativeArchivePath = toPosixPath(path.relative(relPkgJsonDir, archivePath));
|
|
134
149
|
packageFileDeps
|
|
135
150
|
.get(sourcePackageJsonPath)
|
|
136
151
|
.set(packageName, relativeArchivePath);
|
|
@@ -172,7 +187,7 @@ export async function updatePackageJsonInArchive(archive, srcDir, packageWorkspa
|
|
|
172
187
|
if (!fs.existsSync(packageJsonPath)) {
|
|
173
188
|
continue;
|
|
174
189
|
}
|
|
175
|
-
const relativePackageJsonPath = path.relative(srcDir, packageJsonPath);
|
|
190
|
+
const relativePackageJsonPath = toPosixPath(path.relative(srcDir, packageJsonPath));
|
|
176
191
|
let rawContent;
|
|
177
192
|
try {
|
|
178
193
|
rawContent = fs.readFileSync(packageJsonPath, 'utf8');
|
|
@@ -261,11 +276,11 @@ export function rewriteLockfileForExternalDeps(lockfileContent, pathMappings) {
|
|
|
261
276
|
export function getPackageJsonPathsToUpdate(srcDir, workspaceMappings, fileDependencyMappings) {
|
|
262
277
|
const paths = new Set();
|
|
263
278
|
for (const { sourcePackageJsonPath } of workspaceMappings) {
|
|
264
|
-
paths.add(path.relative(srcDir, sourcePackageJsonPath));
|
|
279
|
+
paths.add(toPosixPath(path.relative(srcDir, sourcePackageJsonPath)));
|
|
265
280
|
}
|
|
266
281
|
for (const { localPath, sourcePackageJsonPath } of fileDependencyMappings) {
|
|
267
282
|
if (!isInsideSrcDir(localPath, srcDir)) {
|
|
268
|
-
paths.add(path.relative(srcDir, sourcePackageJsonPath));
|
|
283
|
+
paths.add(toPosixPath(path.relative(srcDir, sourcePackageJsonPath)));
|
|
269
284
|
}
|
|
270
285
|
}
|
|
271
286
|
return paths;
|
|
@@ -290,7 +305,7 @@ export function getLockfilePathsToUpdate(srcDir, workspaceMappings, fileDependen
|
|
|
290
305
|
for (const dir of dirsWithExternalDeps) {
|
|
291
306
|
const lockfilePath = path.join(dir, 'package-lock.json');
|
|
292
307
|
if (fs.existsSync(lockfilePath)) {
|
|
293
|
-
paths.add(path.relative(srcDir, lockfilePath));
|
|
308
|
+
paths.add(toPosixPath(path.relative(srcDir, lockfilePath)));
|
|
294
309
|
}
|
|
295
310
|
}
|
|
296
311
|
return paths;
|
|
@@ -319,12 +334,12 @@ async function rewriteLockfilesInArchive(archive, srcDir, externalArchivePaths,
|
|
|
319
334
|
const pathMappings = [];
|
|
320
335
|
for (const [absoluteExternalPath, archivePath] of externalArchivePaths) {
|
|
321
336
|
pathMappings.push({
|
|
322
|
-
oldPath: path.relative(dir, absoluteExternalPath),
|
|
323
|
-
newPath: path.relative(dir, path.join(srcDir, archivePath)),
|
|
337
|
+
oldPath: toPosixPath(path.relative(dir, absoluteExternalPath)),
|
|
338
|
+
newPath: toPosixPath(path.relative(dir, path.join(srcDir, archivePath))),
|
|
324
339
|
});
|
|
325
340
|
}
|
|
326
341
|
const rewritten = rewriteLockfileForExternalDeps(lockfileContent, pathMappings);
|
|
327
|
-
const relativeLockfilePath = path.relative(srcDir, lockfilePath);
|
|
342
|
+
const relativeLockfilePath = toPosixPath(path.relative(srcDir, lockfilePath));
|
|
328
343
|
uiLogger.debug(lib.projectUpload.handleProjectUpload.updatingLockfile(relativeLockfilePath));
|
|
329
344
|
archive.append(JSON.stringify(rewritten, null, 2), {
|
|
330
345
|
name: relativeLockfilePath,
|
|
@@ -336,7 +351,7 @@ async function rewriteLockfilesInArchive(archive, srcDir, externalArchivePaths,
|
|
|
336
351
|
* Main orchestration function that handles archiving of workspaces and file dependencies.
|
|
337
352
|
* This is the clean integration point for upload.ts.
|
|
338
353
|
*/
|
|
339
|
-
export async function archiveWorkspacesAndDependencies(archive, srcDir,
|
|
354
|
+
export async function archiveWorkspacesAndDependencies(archive, srcDir, workspaceMappings, fileDependencyMappings) {
|
|
340
355
|
// Archive workspace directories (internal ones are skipped, externals are copied)
|
|
341
356
|
const { externalArchivePaths, packageWorkspaceEntries } = await archiveWorkspaceDirectories(archive, srcDir, workspaceMappings);
|
|
342
357
|
// Archive external file: dependencies (internals are skipped)
|
package/lib/usageTracking.d.ts
CHANGED
|
@@ -1,29 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
os?: string;
|
|
4
|
-
nodeVersion?: string;
|
|
5
|
-
nodeMajorVersion?: string;
|
|
6
|
-
version?: string;
|
|
7
|
-
command?: string;
|
|
8
|
-
authType?: string;
|
|
9
|
-
step?: string;
|
|
10
|
-
assetType?: string;
|
|
11
|
-
mode?: string;
|
|
12
|
-
type?: string | number;
|
|
13
|
-
file?: boolean;
|
|
14
|
-
successful?: boolean;
|
|
15
|
-
};
|
|
1
|
+
import { type UsageTrackingMeta, type ConfigType, type ExecutionSource } from './api/usageTracking.js';
|
|
2
|
+
export type { UsageTrackingMeta, ConfigType, ExecutionSource, } from './api/usageTracking.js';
|
|
16
3
|
export declare const EventClass: {
|
|
17
4
|
USAGE: string;
|
|
18
5
|
INTERACTION: string;
|
|
19
6
|
VIEW: string;
|
|
20
7
|
ACTIVATION: string;
|
|
21
8
|
};
|
|
22
|
-
export declare function
|
|
9
|
+
export declare function getExecutionEnvironmentMeta(): {
|
|
10
|
+
os: string;
|
|
23
11
|
nodeVersion: string;
|
|
24
12
|
nodeMajorVersion: string;
|
|
13
|
+
version: string;
|
|
14
|
+
configType?: ConfigType;
|
|
15
|
+
executionSource: ExecutionSource;
|
|
25
16
|
};
|
|
26
|
-
export declare function getPlatform(): string;
|
|
27
17
|
export declare function trackCommandUsage(command: string, meta?: UsageTrackingMeta, accountId?: number): Promise<void>;
|
|
28
18
|
export declare function trackHelpUsage(command: string): Promise<void>;
|
|
29
19
|
export declare function trackConvertFieldsUsage(command: string): Promise<void>;
|