UNPKG

@amplitude/ampli

Version:

Amplitude CLI

289 lines (288 loc) 16.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IS_LATEST_IF_NOT_DEFAULT_BRANCH = exports.IS_LATEST = exports.IS_MERGED = exports.SKIP_UPDATE_ON_DEFAULT_BRANCH = void 0; const chalk_1 = require("chalk"); const lodash_1 = require("lodash"); const base_1 = require("./base"); const generated_1 = require("../graphql/generated"); const errors_1 = require("../errors"); const sentry_1 = require("../sentry"); const verifier_1 = require("../verifier"); const compareByName_1 = require("../util/sort/compareByName"); const itly_reference_1 = require("../verifier/base/itly-reference"); const EventVerificationInfoReduced_1 = require("../components/EventVerificationInfoReduced"); const pull_1 = require("./pull"); const semver_1 = require("../util/semver"); const icons_1 = require("../ui/icons"); const string_1 = require("../util/string"); const configure_1 = require("./configure"); const { bold } = chalk_1.default; exports.SKIP_UPDATE_ON_DEFAULT_BRANCH = 'skip-update-on-default-branch'; exports.IS_MERGED = 'is-merged'; exports.IS_LATEST = 'is-latest'; exports.IS_LATEST_IF_NOT_DEFAULT_BRANCH = 'is-latest-if-not-default-branch'; class StatusAction extends base_1.default { constructor(flags, args, config, setBranchMappedToMain) { super(flags, args, config); this.setBranchMappedToMain = setBranchMappedToMain; } async run() { var _a; let missedEventCount = 0; const settings = this.getSettings(); let sourceId = settings.getSourceId(); if (!sourceId) { await new pull_1.default(Object.assign(Object.assign({}, this.flags), { version: undefined, path: undefined, omitApiKeys: undefined, [configure_1.DEPRECATED_RUNTIMES]: undefined }), { source: undefined }, this.config, () => { }).run(); this.println(); sourceId = settings.getSourceId(); } if (!sourceId) { this.error(new errors_1.UserFixableError(errors_1.USER_ERROR_MESSAGES.runPullBefore('status')), 1); } let branchName = settings.getBranch(); let defaultBranch; let mergedVersion; let versionNumber = settings.getVersion(); let versionId; let isLatestVersion; const originalVersionNumber = versionNumber; const originalBranchName = branchName; let originalBranch; let branch; const orgId = settings.getOrgId(); const workspaceId = settings.getWorkspaceId(); let org; let workspace; this.startSpinner(`Verifying event tracking implementation in source code`); try { const graphql = await this.getAmpliGraphQLClient(); const branchesResponse = await this.getOrgWorkspaceBranches(orgId, workspaceId); const { branches } = branchesResponse; ({ org, workspace, defaultBranch } = branchesResponse); branch = branchName ? branches.find(b => b.name === branchName) : defaultBranch; originalBranch = branch; let expectedBranch = this.flags.branch; versionId = await this.getVersionIdFromSettings(settings, branch); if (!branch) { if (versionId) { mergedVersion = await this.getMergedVersionByVersionId(orgId, workspaceId, versionId); } else { mergedVersion = await this.getMergedVersionByVersionSemVer(orgId, workspaceId, branchName, versionNumber); } if (mergedVersion != null) { if (expectedBranch === branchName) { expectedBranch = defaultBranch.name; } branchName = defaultBranch.name; versionNumber = mergedVersion.mainSemVer; versionId = mergedVersion.mainVersionId; branch = defaultBranch; this.setBranchMappedToMain(true); } } if (expectedBranch && expectedBranch !== branchName) { this.error(new errors_1.UserFixableError(errors_1.USER_ERROR_MESSAGES.branchDoesNotMatchExpected(expectedBranch, originalBranchName)), 1); } if (!branch) { this.error(new Error(errors_1.USER_ERROR_MESSAGES.branchDoesNotExist(branchName)), 1); } if (!versionId) { this.error(new Error(errors_1.USER_ERROR_MESSAGES.versionDoesNotExist(versionNumber)), 1); } const sources = await this.getSources(orgId, workspaceId, defaultBranch, branch, versionId, sourceId); const source = sources[0]; if (!source) { this.error(new Error(errors_1.USER_ERROR_MESSAGES.sourceDoesNotExist(sourceId)), 1); } sentry_1.default.captureSource(source); isLatestVersion = versionId === branch.currentVersionId; const versionResponse = await graphql.versions({ orgId, workspaceId, branchId: branch.id, versionId }); if (versionResponse.orgs.length === 0) { this.error(new errors_1.UserFixableError(errors_1.USER_ERROR_MESSAGES.userDoesntHaveAccessToWorkspace), 1); } const [version] = versionResponse.orgs[0].workspaces[0].branches[0].versions; if (!version) { this.error(new Error(errors_1.USER_ERROR_MESSAGES.versionNotFoundOnServer(versionId)), 1); } this.debug(`runtime: ${source.runtime.id}\n`); let runtimeId = this.flags.runtime || source.runtime.id; const runtimes = await this.getRuntimes(); const sourceRuntime = runtimes.find(r => r.id === source.runtime.id); const settingRuntime = settings.getRuntime(runtimes); if (!this.flags.runtime) { const runtime = this.compareSettingsAndActualRuntimes(settings, settingRuntime, sourceRuntime); if (!runtime) { return { missedEventCount, exitCode: 1 }; } runtimeId = runtime.id; } if (this.flags.instanceNames != null) { settings.setInstanceNames((this.flags.instanceNames.length === 1 && this.flags.instanceNames[0] === '') ? [] : this.flags.instanceNames); } const instanceNames = settings.getInstanceNames(); const verifiers = verifier_1.VERIFIERS_BY_RUNTIME[runtimeId].map(factory => factory(instanceNames)); if (lodash_1.isEmpty(verifiers)) { this.error(new Error(errors_1.USER_ERROR_MESSAGES.unsupportedRuntime(runtimeId)), 1); } const events = version.events .filter(e => !e.isDeleted && e.sources.some(s => s.id === source.id)) .sort(compareByName_1.default); if (this.flags.sourceDirs != null) { settings.setSourceDirs((this.flags.sourceDirs.length === 1 && [this.flags.projectDir, ''].includes(this.flags.sourceDirs[0])) ? [] : this.flags.sourceDirs); } const sourceDirs = (_a = settings.getSourceDirs()) !== null && _a !== void 0 ? _a : [this.flags.projectDir]; const results = []; for (let i = 0; i < verifiers.length; i += 1) { results.push(await verifiers[i].verify(events.map(e => e.name), sourceDirs)); } const verifierResults = results.reduce((acc, cur) => ({ references: acc.references.concat(cur.references), errors: acc.errors.concat(cur.errors), })); sentry_1.default.captureCommandContext(this.id, { referenceCount: verifierResults.references.length, errorCount: verifierResults.errors.length, errors: verifierResults.errors.map(e => e.message), }); const skipUpdate = this.flags[exports.SKIP_UPDATE_ON_DEFAULT_BRANCH] && branch.default; const eventStatusInputMap = itly_reference_1.toEventStatusInputMap(verifierResults.references); let editEventStatusesError; if (this.flags.update && !skipUpdate && events.length > 0) { try { const branchId = branch.id; await graphql.editEventStatuses({ input: events.map(e => { const eventStatus = eventStatusInputMap.get(e.name) || []; const instrumentationStatus = eventStatus.length === 0 ? generated_1.InstrumentationStatus.Absent : generated_1.InstrumentationStatus.Instrumented; return { branchId, versionId: version.id, sourceId: source.id, eventId: e.id, status: instrumentationStatus, input: eventStatus, }; }), }); } catch (error) { editEventStatusesError = error; } } const { errors } = verifierResults; if (errors && !lodash_1.isEmpty(errors)) { this.error(new errors_1.VerificationError(errors_1.USER_ERROR_MESSAGES.verifyResultsContainErrors(errors)), 1); } missedEventCount = events .map(e => (eventStatusInputMap.get(e.name) ? 0 : 1)) .reduce((acc, cur) => acc + cur, 0); this.stopSpinner(missedEventCount === 0); EventVerificationInfoReduced_1.default({ events, eventStatusInputMap, }); if (this.flags.update && events.length > 0) { const branchInfo = `on branch ${bold(branchName)}`; if (skipUpdate) { this.println(`\ ${icons_1.ICON_WARNING_W_TEXT} Implementation status not updated in tracking plan ${branchInfo} \ due to ${bold(`--${exports.SKIP_UPDATE_ON_DEFAULT_BRANCH}`)} option.`); } else if (editEventStatusesError) { const message = (editEventStatusesError.response && editEventStatusesError.response.errors && editEventStatusesError.response.errors.length > 0) ? editEventStatusesError.response.errors[0].message : editEventStatusesError.message; this.println(`${icons_1.ICON_ERROR_W_TEXT} Implementation status not updated in tracking plan ${branchInfo} \ due to server error: ${bold(message)}`); } else { this.println(`${icons_1.ICON_SUCCESS} Tracking plan successfully updated with current implementation status ${branchInfo}.`); } } } finally { this.stopSpinner(false); } if (mergedVersion != null) { this.println(`${icons_1.ICON_WARNING_W_TEXT} \ The current branch ${bold(mergedVersion.branchName)} version ${bold(mergedVersion.featureSemVer)} has already \ been merged into ${bold(defaultBranch === null || defaultBranch === void 0 ? void 0 : defaultBranch.name)}. \ Verified implementation against ${bold(defaultBranch === null || defaultBranch === void 0 ? void 0 : defaultBranch.name)} version ${bold(mergedVersion.mainSemVer)}. We recommend running ${bold('ampli pull')} to update to ${bold(defaultBranch === null || defaultBranch === void 0 ? void 0 : defaultBranch.name)} version ${bold(mergedVersion.mainSemVer)}.`); } let exitCode; if (this.flags[exports.IS_MERGED]) { if (mergedVersion != null) { this.println(`${icons_1.ICON_SUCCESS} Branch ${bold(mergedVersion.branchName)} version ${bold(originalVersionNumber)} is merged.`); } else if (originalBranch && originalBranch.default) { this.println(`${icons_1.ICON_SUCCESS} You are on the default branch ${bold(originalBranch.name)}. No merge necessary.`); } else { this.println(`${icons_1.ICON_ERROR_W_TEXT} Branch ${bold(branchName)} version ${bold(originalVersionNumber)} is not merged.`); exitCode = 1; } } await this.checkIsLatestVersion(orgId, org, workspaceId, workspace, branch, originalBranch, versionId, versionNumber, isLatestVersion); return { missedEventCount, exitCode }; } async checkIsLatestVersion(orgId, org, workspaceId, workspace, branch, originalBranch, versionId, versionNumber, isLatestVersion) { if (this.flags[exports.IS_LATEST] || this.flags[exports.IS_LATEST_IF_NOT_DEFAULT_BRANCH]) { if (isLatestVersion) { this.println(`${icons_1.ICON_SUCCESS} You are on the latest version of ${bold(branch.name)}`); } else if (this.flags[exports.IS_LATEST_IF_NOT_DEFAULT_BRANCH] && (originalBranch === null || originalBranch === void 0 ? void 0 : originalBranch.default)) { this.println(`${icons_1.ICON_SUCCESS} You are on default branch ${bold(branch.name)}`); } else { const currentVersion = branch ? await this.getBranchVersion(orgId, workspaceId, branch.id, branch.currentVersionId) : undefined; const latestSemVer = currentVersion ? semver_1.default.fromAmpliVersion(currentVersion).toString() : 'unknown'; this.error(new errors_1.UserFixableError(errors_1.USER_ERROR_MESSAGES.versionIsNotLatest(branch.name, latestSemVer, versionNumber)), 1); } } else if (isLatestVersion) { if (branch.stagingVersionId) { const graphql = await this.getAmpliGraphQLClient(); const response = await graphql.versionChangeCount({ orgId, workspaceId, branchId: branch.id, versionId: branch.stagingVersionId, }); if (response.orgs.length === 0) { this.error(new errors_1.UserFixableError(errors_1.USER_ERROR_MESSAGES.userDoesntHaveAccessToWorkspace), 1); } const { changeCount } = response.orgs[0].workspaces[0].branches[0].versions[0].changes.forward; if (changeCount > 0) { const is = string_1.pluralize(changeCount, 'are', 'is'); const s = string_1.pluralizeS(changeCount); const branchUrl = this.getBranchUrl(org.url, workspace.name, branch.name); this.println(` ${icons_1.ICON_WARNING_W_TEXT} There ${is} ${bold(`${changeCount} pending change${s}`)} in the tracking plan. Save changes on ${branchUrl} to include them in code generation.`); return; } } this.println(`${icons_1.ICON_SUCCESS} Your branch is up to date.`); } else { const sortedVersions = await this.getBranchVersions(orgId, workspaceId, branch === null || branch === void 0 ? void 0 : branch.id); const versionIndex = sortedVersions.findIndex(v => v.id === versionId); const latestVersionIndex = sortedVersions.findIndex(v => v.id === (branch === null || branch === void 0 ? void 0 : branch.currentVersionId)); const deltaVersions = new Set(); if (versionIndex >= 0 && latestVersionIndex > versionIndex) { const currentVersionNumber = semver_1.default.fromAmpliVersion(sortedVersions[versionIndex]).toString(); for (let i = versionIndex + 1; i <= latestVersionIndex; i += 1) { const deltaVersionNumber = semver_1.default.fromAmpliVersion(sortedVersions[i]).toString(); if (deltaVersionNumber !== currentVersionNumber) { deltaVersions.add(deltaVersionNumber); } } } this.println(`${icons_1.ICON_WARNING_W_TEXT} Your local branch is ${deltaVersions.size} versions behind. Run ${bold('ampli pull')} to get the latest event definitions.`); } } } exports.default = StatusAction;