@puls-atlas/cli
Version:
The Puls Atlas CLI tool for managing Atlas projects
159 lines • 4.39 kB
JavaScript
import chalk from 'chalk';
import inquirer from 'inquirer';
import { warnIfNotReleaseBranch } from '../../utils/git.js';
import { requireFirebaseJson, resolveFirestoreRulesSourceLabel, selectProject } from '../../utils/firebase.js';
import { logger } from '../../utils/logger.js';
import { execSync } from '../../utils/index.js';
const FIRESTORE_RULES_DEPLOY_TARGET = 'firestore:rules';
const outputDeploymentSummary = ({
loggerImpl = logger,
projectId,
rulesSourceLabel
}) => {
loggerImpl.summary('Deployment plan', [{
label: 'Consumer project',
value: projectId,
tone: 'warning'
}, {
label: 'Deploy target',
value: 'Firestore rules',
tone: 'accent'
}, {
label: 'Source',
value: rulesSourceLabel,
tone: 'warning'
}], {
spacing: 'after'
});
};
const outputDeploymentOutcomeSummary = ({
loggerImpl = logger,
projectId,
rulesSourceLabel
}) => {
loggerImpl.summary('Deployment outcome', [{
label: 'Consumer project',
value: projectId,
tone: 'warning'
}, {
label: 'Deploy target',
value: 'Firestore rules',
tone: 'accent'
}, {
label: 'Source',
value: rulesSourceLabel,
tone: 'warning'
}, {
label: 'Status',
value: 'deployed',
tone: 'success'
}], {
spacing: 'after'
});
};
const confirmRulesDeployment = async ({
projectId,
promptImpl = inquirer.prompt,
rulesSourceLabel
}) => {
const {
isConfirmed
} = await promptImpl([{
type: 'confirm',
name: 'isConfirmed',
default: false,
message: `Deploy Firestore rules from ${chalk.cyan(rulesSourceLabel)} ` + `to consumer project ${chalk.bold(projectId)}?`
}]);
return isConfirmed;
};
export const createDeployRulesHandler = (dependencies = {}) => {
const {
execSyncImpl = execSync,
loggerImpl = logger,
promptImpl = inquirer.prompt,
requireFirebaseJsonImpl = requireFirebaseJson,
resolveFirestoreRulesSourceLabelImpl = resolveFirestoreRulesSourceLabel,
selectProjectImpl = selectProject,
warnIfNotReleaseBranchImpl = warnIfNotReleaseBranch
} = dependencies;
return async (options = {}) => {
try {
warnIfNotReleaseBranchImpl({
loggerImpl
});
const {
firestore,
storage
} = options;
if (storage && !firestore) {
loggerImpl.warning('Storage rules are not yet supported.');
return false;
}
const {
projectId
} = await selectProjectImpl('.firebaserc', {
promptMessage: 'Select a Google Cloud consumer project to deploy Firestore rules to:'
});
const firebaseJson = requireFirebaseJsonImpl();
const rulesSourceLabel = resolveFirestoreRulesSourceLabelImpl(firebaseJson);
if (!rulesSourceLabel) {
loggerImpl.error('No Firestore rules configuration found in firebase.json.', {
exit: true,
exitCode: 1
});
return false;
}
outputDeploymentSummary({
loggerImpl,
projectId,
rulesSourceLabel
});
const isConfirmed = await confirmRulesDeployment({
projectId,
promptImpl,
rulesSourceLabel
});
if (!isConfirmed) {
loggerImpl.warning('Deployment cancelled by the user.');
return false;
}
const spinner = loggerImpl.spinner('Deploying Firestore rules...');
try {
execSyncImpl('firebase deploy', {
only: FIRESTORE_RULES_DEPLOY_TARGET,
project: projectId,
stdio: 'inherit'
});
spinner.succeed('Firestore rules deployed successfully.');
} catch (error) {
spinner.fail('Deployment failed. See the error above for details.');
loggerImpl.error(error, {
exit: true,
exitCode: 1
});
return false;
}
outputDeploymentOutcomeSummary({
loggerImpl,
projectId,
rulesSourceLabel
});
loggerImpl.success('Done!');
return true;
} catch (error) {
if (error instanceof Error && error.name === 'ExitPromptError') {
loggerImpl.error('Deployment cancelled by the user.', {
exit: true,
exitCode: 130
});
return false;
}
loggerImpl.error(error, {
exit: true,
exitCode: 1
});
return false;
}
};
};
export default createDeployRulesHandler();