123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- const tslib_1 = require("tslib");
- const chalk_1 = require("chalk");
- const cli_utils_1 = require("@ionic/cli-utils");
- const command_1 = require("@ionic/cli-utils/lib/command");
- const errors_1 = require("@ionic/cli-utils/lib/errors");
- const CHOICE_CREATE_NEW_APP = 'createNewApp';
- const CHOICE_NEVERMIND = 'nevermind';
- const CHOICE_IONIC = 'ionic';
- const CHOICE_GITHUB = 'github';
- const CHOICE_MASTER_ONLY = 'master';
- const CHOICE_SPECIFIC_BRANCHES = 'specific';
- let LinkCommand = class LinkCommand extends command_1.Command {
- preRun(inputs, options) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const { create } = options;
- if (inputs[0] && create) {
- throw new errors_1.FatalException(`Sorry--cannot use both ${chalk_1.default.green('app_id')} and ${chalk_1.default.green('--create')}. You must either link an existing app or create a new one.`);
- }
- let proAppId = options['pro-id'] || '';
- const config = yield this.env.config.load();
- if (proAppId) {
- if (config.backend !== cli_utils_1.BACKEND_PRO) {
- yield this.env.runCommand(['config', 'set', '-g', 'backend', 'pro'], { showExecution: false });
- this.env.log.nl();
- this.env.log.info(`${chalk_1.default.bold(chalk_1.default.blue.underline('Welcome to Ionic Pro!') + ' The CLI is now set up to use Ionic Pro services.')}\n` +
- `You can revert back to Ionic Cloud (legacy) services at any time:\n\n` +
- `${chalk_1.default.green('ionic config set -g backend legacy')}\n`);
- }
- inputs[0] = proAppId;
- }
- });
- }
- run(inputs, options) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const { promptToLogin } = yield Promise.resolve().then(() => require('@ionic/cli-utils/lib/session'));
- let [appId] = inputs;
- let { create, name } = options;
- const config = yield this.env.config.load();
- const project = yield this.env.project.load();
- const appUtil = yield this.getAppClient();
- if (project.app_id) {
- if (project.app_id === appId) {
- this.env.log.info(`Already linked with app ${chalk_1.default.green(appId)}.`);
- return;
- }
- const msg = appId ?
- `Are you sure you want to link it to ${chalk_1.default.green(appId)} instead?` :
- `Would you like to link it to a different app?`;
- const confirm = yield this.env.prompt({
- type: 'confirm',
- name: 'confirm',
- message: `App ID ${chalk_1.default.green(project.app_id)} is already set up with this app. ${msg}`,
- });
- if (!confirm) {
- this.env.log.info('Not linking.');
- return;
- }
- }
- if (!(yield this.env.session.isLoggedIn())) {
- yield promptToLogin(this.env);
- }
- if (appId) {
- this.env.tasks.next(`Looking up app ${chalk_1.default.green(appId)}`);
- yield appUtil.load(appId);
- this.env.tasks.end();
- }
- else if (!create) {
- this.env.tasks.next(`Looking up your apps`);
- let apps = [];
- const paginator = yield appUtil.paginate();
- for (let r of paginator) {
- const res = yield r;
- apps = apps.concat(res.data);
- }
- this.env.tasks.end();
- const createAppChoice = {
- name: 'Create a new app',
- id: CHOICE_CREATE_NEW_APP,
- };
- const neverMindChoice = {
- name: 'Nevermind',
- id: CHOICE_NEVERMIND,
- };
- const linkedApp = yield this.env.prompt({
- type: 'list',
- name: 'linkedApp',
- message: `Which app would you like to link`,
- choices: [createAppChoice, ...apps, neverMindChoice].map((app) => ({
- name: [CHOICE_CREATE_NEW_APP, CHOICE_NEVERMIND].includes(app.id) ? chalk_1.default.bold(app.name) : `${app.name} (${app.id})`,
- value: app.id
- }))
- });
- appId = linkedApp;
- }
- if (create || appId === CHOICE_CREATE_NEW_APP) {
- if (config.backend === cli_utils_1.BACKEND_PRO) {
- if (!name) {
- name = yield this.env.prompt({
- type: 'input',
- name: 'name',
- message: 'Please enter a name for your new app:',
- });
- }
- const app = yield appUtil.create({ name: String(name) });
- appId = app.id;
- yield this.linkApp(app);
- }
- else {
- const opn = yield Promise.resolve().then(() => require('opn'));
- const dashUrl = yield this.env.config.getDashUrl();
- const token = yield this.env.session.getUserToken();
- opn(`${dashUrl}/?user_token=${token}`, { wait: false });
- this.env.log.info(`Rerun ${chalk_1.default.green(`ionic link`)} to link to the new app.`);
- }
- }
- else if (appId === CHOICE_NEVERMIND) {
- this.env.log.info('Not linking app.');
- }
- else {
- const app = yield appUtil.load(appId);
- yield this.linkApp(app);
- }
- yield Promise.all([this.env.config.save(), this.env.project.save()]);
- });
- }
- getAppClient() {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const { App } = yield Promise.resolve().then(() => require('@ionic/cli-utils/lib/app'));
- const token = yield this.env.session.getUserToken();
- return new App(token, this.env.client);
- });
- }
- getUserClient() {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const { UserClient } = yield Promise.resolve().then(() => require('@ionic/cli-utils/lib/user'));
- const token = yield this.env.session.getUserToken();
- return new UserClient({ token, client: this.env.client });
- });
- }
- linkApp(app) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- // TODO: load connections
- // TODO: check for git availability before this
- this.env.log.nl();
- this.env.log.info(`${chalk_1.default.bold(`Ionic Pro uses a git-based workflow to manage app updates.`)}\n` +
- `You will be prompted to set up the git host and repository for this new app. See the docs${chalk_1.default.bold('[1]')} for more information.\n\n` +
- `${chalk_1.default.bold('[1]')}: ${chalk_1.default.cyan('https://ionicframework.com/docs/pro/basics/git/')}`);
- const service = yield this.env.prompt({
- type: 'list',
- name: 'gitService',
- message: 'Which git host would you like to use?',
- choices: [
- {
- name: 'GitHub',
- value: CHOICE_GITHUB,
- },
- {
- name: 'Ionic Pro',
- value: CHOICE_IONIC,
- },
- ],
- });
- let githubUrl;
- if (service === CHOICE_IONIC) {
- const config = yield this.env.config.load();
- if (!config.git.setup) {
- yield this.env.runCommand(['ssh', 'setup']);
- }
- yield this.env.runCommand(['config', 'set', 'app_id', `"${app.id}"`, '--json']);
- yield this.env.runCommand(['git', 'remote']);
- }
- else {
- if (service === CHOICE_GITHUB) {
- githubUrl = yield this.linkGithub(app);
- }
- yield this.env.runCommand(['config', 'set', 'app_id', `"${app.id}"`, '--json']);
- }
- this.env.log.ok(`Project linked with app ${chalk_1.default.green(app.id)}!`);
- if (service === CHOICE_GITHUB) {
- this.env.log.info(`Here are some additional links that can help you with you first push to GitHub:\n` +
- `${chalk_1.default.bold('Adding GitHub as a remote')}:\n\t${chalk_1.default.cyan('https://help.github.com/articles/adding-a-remote/')}\n\n` +
- `${chalk_1.default.bold('Pushing to a remote')}:\n\t${chalk_1.default.cyan('https://help.github.com/articles/pushing-to-a-remote/')}\n\n` +
- `${chalk_1.default.bold('Working with branches')}:\n\t${chalk_1.default.cyan('https://guides.github.com/introduction/flow/')}\n\n` +
- `${chalk_1.default.bold('More comfortable with a GUI? Try GitHub Desktop!')}\n\t${chalk_1.default.cyan('https://desktop.github.com/')}`);
- if (githubUrl) {
- this.env.log.info(`${chalk_1.default.bold('You can now push to one of your branches on GitHub to trigger a build in Ionic Pro!')}\n` +
- `If you haven't added GitHub as your origin you can do so by running:\n\n` +
- `${chalk_1.default.green('git remote add origin ' + githubUrl)}\n\n` +
- `You can find additional links above to help if you're having issues.`);
- }
- }
- });
- }
- linkGithub(app) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const { id } = yield this.env.session.getUser();
- const userClient = yield this.getUserClient();
- const user = yield userClient.load(id, { fields: ['oauth_identities'] });
- if (!user.oauth_identities || !user.oauth_identities.github) {
- yield this.oAuthProcess(id);
- }
- if (yield this.needsAssociation(app, user.id)) {
- yield this.confirmGithubRepoExists();
- const repoId = yield this.selectGithubRepo();
- const branches = yield this.selectGithubBranches(repoId);
- return this.connectGithub(app, repoId, branches);
- }
- });
- }
- confirmGithubRepoExists() {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- let confirm = false;
- this.env.log.nl();
- this.env.log.info(chalk_1.default.bold(`In order to link to a GitHub repository the repository must already exist on GitHub.`));
- this.env.log.info(`${chalk_1.default.bold('If the repository does not exist please create one now before continuing.')}\n` +
- `If you're not familiar with Git you can learn how to set it up with GitHub here:\n\n` +
- chalk_1.default.cyan(`https://help.github.com/articles/set-up-git/ \n\n`) +
- `You can find documentation on how to create a repository on GitHub and push to it here:\n\n` +
- chalk_1.default.cyan(`https://help.github.com/articles/create-a-repo/`));
- confirm = yield this.env.prompt({
- type: 'confirm',
- name: 'confirm',
- message: 'Does the repository exist on GitHub?',
- });
- if (!confirm) {
- throw new errors_1.FatalException('Repo Must exist on GitHub in order to link.');
- }
- });
- }
- oAuthProcess(userId) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const opn = yield Promise.resolve().then(() => require('opn'));
- const userClient = yield this.getUserClient();
- let confirm = false;
- this.env.log.nl();
- this.env.log.info(`${chalk_1.default.bold('GitHub OAuth setup required.')}\n` +
- `To continue, we need you to authorize Ionic Pro with your GitHub account. ` +
- `A browser will open and prompt you to complete the authorization request. ` +
- `When finished, please return to the CLI to continue linking your app.`);
- confirm = yield this.env.prompt({
- type: 'confirm',
- name: 'ready',
- message: 'Open browser:',
- });
- if (!confirm) {
- throw new errors_1.FatalException('Aborting.');
- }
- const url = yield userClient.oAuthGithubLogin(userId);
- opn(url, { wait: false });
- confirm = yield this.env.prompt({
- type: 'confirm',
- name: 'ready',
- message: 'Authorized and ready to continue:',
- });
- if (!confirm) {
- throw new errors_1.FatalException('Aborting.');
- }
- });
- }
- needsAssociation(app, userId) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const appClient = yield this.getAppClient();
- if (app.association && app.association.repository.html_url) {
- this.env.log.msg(`App ${chalk_1.default.green(app.id)} already connected to ${chalk_1.default.bold(app.association.repository.html_url)}`);
- const confirm = yield this.env.prompt({
- type: 'confirm',
- name: 'confirm',
- message: 'Would you like to connect a different repo?',
- });
- if (!confirm) {
- return false;
- }
- try {
- // TODO: maybe we can use a PUT instead of DELETE now + POST later?
- yield appClient.deleteAssociation(app.id);
- }
- catch (e) {
- if (cli_utils_1.isSuperAgentError(e)) {
- if (e.response.status === 401) {
- yield this.oAuthProcess(userId);
- yield appClient.deleteAssociation(app.id);
- return true;
- }
- else if (e.response.status === 404) {
- return true;
- }
- }
- throw e;
- }
- }
- return true;
- });
- }
- connectGithub(app, repoId, branches) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const appClient = yield this.getAppClient();
- try {
- const association = yield appClient.createAssociation(app.id, { repoId, type: 'github', branches });
- this.env.log.ok(`App ${chalk_1.default.green(app.id)} connected to ${chalk_1.default.bold(association.repository.html_url)}`);
- return association.repository.clone_url;
- }
- catch (e) {
- if (cli_utils_1.isSuperAgentError(e) && e.response.status === 403) {
- throw new errors_1.FatalException(e.response.body.error.message);
- }
- else {
- throw e;
- }
- }
- });
- }
- formatRepoName(fullName) {
- const [org, name] = fullName.split('/');
- return `${chalk_1.default.dim(`${org} /`)} ${name}`;
- }
- selectGithubRepo() {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- const user = yield this.env.session.getUser();
- const userClient = yield this.getUserClient();
- this.env.tasks.next('Looking up your GitHub repositories');
- const paginator = userClient.paginateGithubRepositories(user.id);
- const repos = [];
- try {
- for (const r of paginator) {
- const res = yield r;
- repos.push(...res.data);
- this.env.tasks.updateMsg(`Looking up your GitHub repositories: ${chalk_1.default.bold(String(repos.length))} found`);
- }
- }
- catch (e) {
- this.env.tasks.fail();
- if (cli_utils_1.isSuperAgentError(e) && e.response.status === 401) {
- yield this.oAuthProcess(user.id);
- return this.selectGithubRepo();
- }
- throw e;
- }
- this.env.tasks.end();
- const repoId = yield this.env.prompt({
- type: 'list',
- name: 'githubRepo',
- message: 'Which GitHub repository would you like to link?',
- choices: repos.map(repo => ({
- name: this.formatRepoName(repo.full_name),
- value: String(repo.id),
- })),
- });
- return Number(repoId);
- });
- }
- selectGithubBranches(repoId) {
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
- this.env.log.nl();
- this.env.log.info(chalk_1.default.bold(`By default Ionic Pro links only to the ${chalk_1.default.green('master')} branch.`));
- this.env.log.info(`${chalk_1.default.bold('If you\'d like to link to another branch or multiple branches you\'ll need to select each branch to connect to.')}\n` +
- `If you're not familiar with on working with branches in GitHub you can read about them here:\n\n` +
- chalk_1.default.cyan(`https://guides.github.com/introduction/flow/ \n\n`));
- const choice = yield this.env.prompt({
- type: 'list',
- name: 'githubMultipleBranches',
- message: 'Which would you like to do?',
- choices: [
- {
- name: `Link to master branch only`,
- value: CHOICE_MASTER_ONLY,
- },
- {
- name: `Link to specific branches`,
- value: CHOICE_SPECIFIC_BRANCHES,
- },
- ],
- });
- switch (choice) {
- case CHOICE_MASTER_ONLY:
- return ['master'];
- case CHOICE_SPECIFIC_BRANCHES:
- // fall through and begin prompting to choose branches
- break;
- default:
- throw new errors_1.FatalException('Aborting. No branch choice specified.');
- }
- const user = yield this.env.session.getUser();
- const userClient = yield this.getUserClient();
- const paginator = userClient.paginateGithubBranches(user.id, repoId);
- this.env.tasks.next('Looking for available branches');
- const availableBranches = [];
- try {
- for (const r of paginator) {
- const res = yield r;
- availableBranches.push(...res.data);
- this.env.tasks.updateMsg(`Looking up the available branches on your GitHub repository: ${chalk_1.default.bold(String(availableBranches.length))} found`);
- }
- }
- catch (e) {
- this.env.tasks.fail();
- throw e;
- }
- this.env.tasks.end();
- const choices = availableBranches.map(branch => ({
- name: branch.name,
- value: branch.name,
- checked: branch.name === 'master',
- }));
- if (choices.length === 0) {
- this.env.log.warn('No branches found for the repository...linking to master branch.');
- return ['master'];
- }
- const selectedBranches = yield this.env.prompt({
- type: 'checkbox',
- name: 'githubBranches',
- message: 'Which branch would you like to link?',
- choices: choices,
- default: ['master'],
- });
- return selectedBranches;
- });
- }
- };
- LinkCommand = tslib_1.__decorate([
- command_1.CommandMetadata({
- name: 'link',
- type: 'project',
- backends: [cli_utils_1.BACKEND_LEGACY, cli_utils_1.BACKEND_PRO],
- description: 'Connect your local app to Ionic',
- longDescription: `
- If you have an app on Ionic, you can link it to this local Ionic project with this command.
-
- Excluding the ${chalk_1.default.green('app_id')} argument looks up your apps on Ionic and prompts you to select one.
-
- This command simply sets the ${chalk_1.default.bold('app_id')} property in ${chalk_1.default.bold('ionic.config.json')} for other commands to read.
- `,
- exampleCommands: ['', 'a1b2c3d4'],
- inputs: [
- {
- name: 'app_id',
- description: `The ID of the app to link (e.g. ${chalk_1.default.green('a1b2c3d4')})`,
- required: false,
- },
- ],
- options: [
- {
- name: 'name',
- description: 'The app name to use during the linking of a new app',
- },
- {
- name: 'create',
- description: 'Create a new app on Ionic and link it with this local Ionic project',
- backends: [cli_utils_1.BACKEND_PRO],
- type: Boolean,
- visible: false,
- },
- {
- name: 'pro-id',
- description: 'Specify an app ID from the Ionic Dashboard to link',
- visible: false,
- },
- ],
- })
- ], LinkCommand);
- exports.LinkCommand = LinkCommand;
|