Skip to content

Commit

Permalink
feat(cli): implement deploy hook for compute environment (#172)
Browse files Browse the repository at this point in the history
  • Loading branch information
Milad Imen authored Sep 11, 2021
1 parent 73c10a9 commit 025731b
Show file tree
Hide file tree
Showing 17 changed files with 501 additions and 262 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@salesforce/core": "3.3.1",
"@salesforce/plugin-org": "^1.6.7",
"@salesforce/plugin-project-utils": "^0.0.6",
"@salesforce/sf-plugins-core": "^0.0.15",
"@salesforce/ts-sinon": "^1.3.18",
"@salesforce/ts-types": "^1.5.5",
"axios": "^0.21.1",
Expand Down Expand Up @@ -100,7 +101,7 @@
"bin": "sf",
"topicSeparator": " ",
"hooks": {
"project:findDeployers": "./lib/hooks/findDeployers"
"sf:deploy": "./lib/hooks/deploy"
},
"plugins": [
"@oclif/plugin-not-found"
Expand Down
81 changes: 10 additions & 71 deletions src/commands/deploy/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import * as path from 'path';
import { URL } from 'url';
import herokuColor from '@heroku-cli/color';
import { Messages } from '@salesforce/core';
import { Flags } from '@oclif/core';
Expand All @@ -20,11 +18,11 @@ import {
filterProjectReferencesToRemove,
FullNameReference,
splitFullName,
resolveFunctionReferences,
} from '../../lib/function-reference-utils';
import Git from '../../lib/git';
import { resolveFunctionsPaths } from '../../lib/path-utils';
import { parseProjectToml } from '../../lib/project-toml';
import { ComputeEnvironment, FunctionReference, SfdxProjectConfig } from '../../lib/sfdc-types';
import { ComputeEnvironment, FunctionReference } from '../../lib/sfdc-types';
import { fetchAppForProject, fetchOrg, fetchSfdxProject } from '../../lib/utils';

const debug = debugFactory('deploy:functions');

Expand All @@ -51,65 +49,6 @@ export default class DeployFunctions extends Command {
}),
};

async getCurrentBranch() {
const statusString = await this.git?.status();

return statusString!.split('\n')[0].replace('On branch ', '');
}

async gitRemote(app: ComputeEnvironment) {
const externalApiKey = process.env.SALESFORCE_FUNCTIONS_API_KEY;
const url = new URL(app.git_url!);

if (externalApiKey) {
url.password = externalApiKey;
url.username = '';

return url.toString();
}

const username = this.username;
const token = this.auth;

if (!username || !token) {
this.error('No login found. Please log in using the `login:functions` command.');
}

url.username = username;
url.password = token;

return url.toString();
}

async resolveFunctionReferences(project: SfdxProjectConfig) {
// Locate functions directory and grab paths for all function names, error if not in project or no
// functions found
const fnPaths = await resolveFunctionsPaths();

// Create function reference objects
return Promise.all(
fnPaths.map(async (fnPath) => {
const projectTomlPath = path.join(fnPath, 'project.toml');
const projectToml: any = await parseProjectToml(projectTomlPath);
const fnName = projectToml.com.salesforce.id;

const fnReference: FunctionReference = {
fullName: `${project.name}-${fnName}`,
label: fnName,
description: projectToml.com.salesforce.description,
};

const permissionSet = projectToml._.metadata?.permissionSet;

if (permissionSet) {
fnReference.permissionSet = permissionSet;
}

return fnReference;
})
);
}

async run() {
const { flags } = await this.parse(DeployFunctions);

Expand All @@ -128,17 +67,17 @@ export default class DeployFunctions extends Command {

// Heroku side: Fetch git remote URL and push working branch to Heroku git server
cli.action.start('Pushing changes to functions');
const org = await this.fetchOrg(flags['connected-org']);
const project = await this.fetchSfdxProject();
const org = await fetchOrg(flags['connected-org']);
const project = await fetchSfdxProject();

// FunctionReferences: create function reference using info from function.toml and project info
// we do this early on because we don't want to bother with anything else if it turns out
// there are no functions to deploy
const references = await this.resolveFunctionReferences(project);
const references = await resolveFunctionReferences(project);

let app: ComputeEnvironment;
try {
app = await this.fetchAppForProject(project.name, flags['connected-org']);
app = await fetchAppForProject(this.client, project.name, flags['connected-org']);
} catch (error) {
if (error.body.message?.includes("Couldn't find that app")) {
this.error(
Expand All @@ -153,13 +92,13 @@ export default class DeployFunctions extends Command {
this.error('You cannot use the `--force` flag with a production org.');
}

const remote = await this.gitRemote(app);
const remote = await this.git.getRemote(app, redactedToken, this.username);

debug('pushing to git server');

const currentBranch = await this.getCurrentBranch();
const currentBranch = await this.git.getCurrentBranch();

const pushCommand = ['push', remote, `${flags.branch ?? currentBranch}:master`];
const pushCommand = ['push', remote, `${flags.branch || currentBranch}:master`];

// Since we error out if they try to use `--force` with a production org, we don't check for
// a production org here since this code would be unreachable in that scenario
Expand Down
7 changes: 4 additions & 3 deletions src/commands/env/create/compute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { format } from 'date-fns';
import Command from '../../../lib/base';
import { FunctionsFlagBuilder } from '../../../lib/flags';
import pollForResult from '../../../lib/poll-for-result';
import { fetchAppForProject, fetchOrg, fetchSfdxProject } from '../../../lib/utils';

interface FunctionConnectionRecord {
Id: string;
Expand Down Expand Up @@ -43,7 +44,7 @@ export default class EnvCreateCompute extends Command {
const alias = flags.setalias;

// if `--connected-org` is null here, fetchOrg will pull the default org from the surrounding environment
const org = await this.fetchOrg(flags['connected-org']);
const org = await fetchOrg(flags['connected-org']);
const orgId = org.getOrgId();

if (!(await this.isFunctionsEnabled(org))) {
Expand All @@ -62,7 +63,7 @@ export default class EnvCreateCompute extends Command {

cli.action.start(`Creating compute environment for org ID ${orgId}`);

const project = await this.fetchSfdxProject();
const project = await fetchSfdxProject();
const projectName = project.name;

if (!projectName) {
Expand Down Expand Up @@ -161,7 +162,7 @@ export default class EnvCreateCompute extends Command {
// we want to fetch the existing environment so that we can point the user to it
if (error.body?.message?.includes(DUPLICATE_PROJECT_MESSAGE)) {
cli.action.stop('error!');
const app = await this.fetchAppForProject(projectName, org.getUsername());
const app = await fetchAppForProject(this.client, projectName, org.getUsername());

this.log(`${DUPLICATE_PROJECT_MESSAGE}:`);
this.log(`Compute Environment ID: ${app.name}`);
Expand Down
3 changes: 2 additions & 1 deletion src/commands/env/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
import { FunctionsFlagBuilder, confirmationFlag } from '../../lib/flags';
import Command from '../../lib/base';
import batchCall from '../../lib/batch-call';
import { fetchSfdxProject } from '../../lib/utils';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/plugin-functions', 'env.delete');
Expand Down Expand Up @@ -97,7 +98,7 @@ export default class EnvDelete extends Command {
// environment while the org still exists, so we need to delete all the function references
// from the org as part of the cleanup process
if (org) {
const project = await this.fetchSfdxProject();
const project = await fetchSfdxProject();
const connection = org.getConnection();
let refList = await connection.metadata.list({ type: 'FunctionReference' });
refList = ensureArray(refList);
Expand Down
3 changes: 2 additions & 1 deletion src/commands/env/display.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ComputeEnvironment, Dictionary } from '../../lib/sfdc-types';
import { FunctionsFlagBuilder } from '../../lib/flags';
import herokuVariant from '../../lib/heroku-variant';
import { ensureArray } from '../../lib/function-reference-utils';
import { fetchSfdxProject } from '../../lib/utils';

interface EnvDisplayTable {
alias?: string;
Expand Down Expand Up @@ -101,7 +102,7 @@ export default class EnvDisplay extends Command {
});
const salesOrgId = app.sales_org_connection?.sales_org_id;
const org = await this.resolveOrg(salesOrgId);
const project = await this.fetchSfdxProject();
const project = await fetchSfdxProject();
const connection = org.getConnection();

const refList = await connection.metadata.list({ type: 'FunctionReference' });
Expand Down
3 changes: 2 additions & 1 deletion src/commands/env/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Command from '../../lib/base';
import herokuVariant from '../../lib/heroku-variant';
import { ComputeEnvironment, Dictionary } from '../../lib/sfdc-types';
import { environmentType } from '../../lib/flags';
import { fetchSfdxProject } from '../../lib/utils';

type EnvironmentType = 'org' | 'scratchorg' | 'compute';

Expand Down Expand Up @@ -246,7 +247,7 @@ export default class EnvList extends Command {

if (!flags.all) {
try {
const project = await this.fetchSfdxProject();
const project = await fetchSfdxProject();

if (!flags.json) {
this.log(`Current environments for project ${project.name}\n`);
Expand Down
3 changes: 2 additions & 1 deletion src/commands/login/functions/jwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import axios from 'axios';
import { cli } from 'cli-ux';
import Command from '../../../lib/base';
import { herokuVariant } from '../../../lib/heroku-variant';
import { fetchSfdxProject } from '../../../lib/utils';

// This is a public Oauth client created expressly for the purpose of headless auth in the functions CLI.
// It does not require a client secret, is marked as public in the database and scoped accordingly
Expand Down Expand Up @@ -73,7 +74,7 @@ export default class JwtLogin extends Command {
};

if (!loginUrl) {
const project = await this.fetchSfdxProject();
const project = await fetchSfdxProject();
// If the user passes an instance URL, we always want to defer that over trying to read their
// project config or defaulting to the basic salesforce login URL.
loginUrl = getString(project, 'sfdcLoginUrl', 'https://login.salesforce.com');
Expand Down
Loading

0 comments on commit 025731b

Please sign in to comment.