@amplitude/ampli
Version:
Amplitude CLI
289 lines (288 loc) • 16.4 kB
JavaScript
;
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;