Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
3f66ba3
feat: app deploy command
aman19K Jun 11, 2024
d783fe2
update launch version
aman19K Jun 11, 2024
c380787
Merge pull request #252 from contentstack/main
cs-raj Jun 11, 2024
5335578
Merge pull request #253 from contentstack/staging
cs-raj Jun 11, 2024
5df4ce5
fix org type def issue
aman19K Jun 11, 2024
43b0570
Merge pull request #250 from contentstack/feat/DX-541
aman19K Jun 11, 2024
3683642
feat: config based launch project creation
aman19K Jun 17, 2024
b2bda01
Merge pull request #254 from contentstack/feat/DX-785
aman19K Jun 17, 2024
8403398
refactor: updated examples
aman19K Jun 18, 2024
e4bfc23
updated readme
aman19K Jun 18, 2024
b4950f2
fix: snyk issues
aman19K Jun 18, 2024
dd1e7dc
updated utilities version
aman19K Jun 18, 2024
83b96dc
version bump
aman19K Jun 18, 2024
fabe102
Merge pull request #255 from contentstack/feat/app-deploy-command
aman19K Jun 18, 2024
09754e4
fix: deploy command
aman19K Jun 19, 2024
335f067
refactor: launch project flag
aman19K Jun 19, 2024
2f923bc
refactor: app disconnect
aman19K Jun 19, 2024
cef7d91
Merge pull request #256 from contentstack/fix/deploy-command
aman19K Jun 19, 2024
8eba9b7
fix: launch project query, removed duplicate yes flag & updated readm…
aman19K Jun 19, 2024
b9cf987
Merge pull request #257 from contentstack/fix/launch-project-query
aman19K Jun 19, 2024
4cd7cc6
fix: app uid issue
aman19K Jun 19, 2024
99f75d8
added check for app-uid
aman19K Jun 19, 2024
e008e13
updated reademe file
aman19K Jun 19, 2024
5c9d2ca
Merge branch 'development' into fix/app-uid-issue
aman19K Jun 19, 2024
f327429
Merge pull request #258 from contentstack/fix/app-uid-issue
aman19K Jun 19, 2024
75dfe08
build: updated oclif/test version to 2.5.6
harshithad0703 Jun 24, 2024
6b18665
test: deploy command unit test
harshithad0703 Jun 24, 2024
57afe40
test: :adhesive_bandage: deploy command unit test update
harshithad0703 Jun 24, 2024
57af772
chore: updated test script in package.json
harshithad0703 Jun 24, 2024
d342cb5
refactor: deploy command ui text
aman19K Jun 26, 2024
b9ed523
Merge pull request #261 from contentstack/refactor/deploy-command-ui-…
aman19K Jun 26, 2024
6f7b5f5
test: added hosting with launch test case for deploy
harshithad0703 Jun 26, 2024
43e2bb6
refactor: deploy command ui text
aman19K Jun 26, 2024
cf74caa
test: added mock data for deploy unit test
harshithad0703 Jun 26, 2024
9d7bb0f
Merge branch 'development' into test/DX-543-deploy-command-unit-tests
harshithad0703 Jun 27, 2024
2feb7d0
test: added 1 more test case using flags in command
harshithad0703 Jun 27, 2024
6afde49
Merge pull request #260 from contentstack/test/DX-543-deploy-command-…
harshithad0703 Jun 28, 2024
4fd4f88
Merge pull request #262 from contentstack/development
harshithad0703 Jun 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: config based launch project creation
  • Loading branch information
aman19K committed Jun 17, 2024
commit 36836425d6b550839b577a37d1a33765b3a2dbf6
9 changes: 9 additions & 0 deletions examples/create-launch-project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "app name",
"type": "GitHub",
"environment": "Default",
"framework": "NextJs",
"build-command": "npm run build",
"out-dir": "./.next",
"branch": "master"
}
103 changes: 59 additions & 44 deletions src/commands/app/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { ApolloClient } from "@apollo/client/core";
import { Flags, FlagInput } from "@contentstack/cli-utilities";
import { Flags, FlagInput, cliux } from "@contentstack/cli-utilities";
import config from "@contentstack/cli-launch/dist/config";
import { GraphqlApiClient } from "@contentstack/cli-launch/dist/util";
import Launch from "@contentstack/cli-launch/dist/commands/launch/index";

import { UpdateHostingParams } from "../../types";
import { commonMsg, deployAppMsg } from "../../messages";
import { AppCLIBaseCommand } from "../../app-cli-base-command";
import {
Expand All @@ -19,8 +21,8 @@ import {
setupConfig,
disconnectApp,
formatUrl,
handleProjectNameConflict,
} from "../../util";
import { UpdateHostingParams } from "../../types";

export default class Deploy extends AppCLIBaseCommand {
static description = "Deploy an app";
Expand All @@ -47,10 +49,10 @@ export default class Deploy extends AppCLIBaseCommand {
description: deployAppMsg.FORCE_DISCONNECT,
default: false,
}),
"project-type": Flags.string({
"launch-project-type": Flags.string({
multiple: false,
options: ["existing-project", "new-project"],
description: deployAppMsg.PROJECT_TYPE,
description: deployAppMsg.LAUNCH_PROJECT_TYPE,
}),
config: Flags.string({
char: "c",
Expand Down Expand Up @@ -88,15 +90,19 @@ export default class Deploy extends AppCLIBaseCommand {
return;
}

await updateApp(
flags,
this.sharedConfig.org,
{
managementSdk: this.managementAppSdk,
log: this.log,
},
updateHostingPayload
);
if (flags["app-url"]) {
const spinner = cliux.loaderV2("Updating App...");
await updateApp(
flags,
this.sharedConfig.org,
{
managementSdk: this.managementAppSdk,
log: this.log,
},
updateHostingPayload
);
cliux.loaderV2("done", spinner);
}

this.log(
this.$t(deployAppMsg.APP_DEPLOYED, {
Expand All @@ -106,7 +112,6 @@ export default class Deploy extends AppCLIBaseCommand {
);
this.log(`App URL: ${flags["app-url"]}`, "info");
} catch (error: any) {
console.log("error", error);
this.log(error?.errorMessage || error?.message || error, "error");
this.exit(1);
}
Expand Down Expand Up @@ -187,20 +192,18 @@ export default class Deploy extends AppCLIBaseCommand {
if (isProjectConnected?.length) {
this.flags["yes"] = this.flags["yes"] || (await askConfirmation());
if (!this.flags["yes"]) {
this.log(
deployAppMsg.LAUNCH_PROJECT_SKIP_MSG,
"info"
);
return;
throw new Error(deployAppMsg.APP_UPDATE_TERMINATION_MSG);
}
const spinner = cliux.loaderV2("Disconnecting launch project...");
await disconnectApp(
this.flags,
this.sharedConfig.org,
this.developerHubBaseUrl
);
cliux.loaderV2("disconnected...", spinner);
}
this.flags["project-type"] =
this.flags["project-type"] || (await askProjectType());
this.flags["launch-project-type"] =
this.flags["launch-project-type"] || (await askProjectType());
await this.handleProjectType(config, updateHostingPayload, projects);
}

Expand All @@ -218,9 +221,13 @@ export default class Deploy extends AppCLIBaseCommand {
): Promise<void> {
let url: string = "";

if (this.flags["project-type"] === "existing-project") {
if (this.flags["launch-project-type"] === "existing-project") {
url = await this.handleExistingProject(updateHostingPayload, projects);
} else if (this.flags["project-type"] === "new-project") {
} else if (this.flags["launch-project-type"] === "new-project") {
config["name"] = await handleProjectNameConflict(
config["name"],
projects
);
url = await this.handleNewProject(config, updateHostingPayload);
} else {
this.log("Invalid project type", "error");
Expand Down Expand Up @@ -252,34 +259,42 @@ export default class Deploy extends AppCLIBaseCommand {

/**
* Handles the deployment of a new project.
*
*
* @param config - The configuration object containing project details.
* @param updateHostingPayload - The payload for updating hosting parameters.
* @returns A Promise that resolves to a string.
* @returns The URL of the deployed project.
*/
async handleNewProject(
config: Record<string, string>,
updateHostingPayload: UpdateHostingParams
): Promise<string> {
await Launch.run([
"--org",
this.sharedConfig.org,
"--name",
config["name"],
"--type",
config["type"],
"--environment",
config["environment"],
"--framework",
config["framework"],
"--build-command",
config["build-command"],
"--out-dir",
config["out-dir"],
"--branch",
config["branch"],
]);
updateHostingPayload["deployment_url"] = this.flags["app-url"];
const args = [];
const configMappings = {
org: this.sharedConfig.org,
name: config["name"],
type: config["type"],
environment: config["environment"],
framework: config["framework"],
"build-command": config["build-command"],
"out-dir": config["out-dir"],
branch: config["branch"],
};

for (const [key, value] of Object.entries(configMappings)) {
if (config[key]) {
args.push(`--${key}`, value);
}
}

await Launch.run(args);
const apolloClient = await this.getApolloClient();
const projects = await getProjects(apolloClient);
const project = projects.find((project) => project.name === config["name"]);
if (project) {
updateHostingPayload["environment_uid"] = project.environmentUid;
updateHostingPayload["project_uid"] = project.uid;
return project.url || "";
}
return "";
}
}
9 changes: 5 additions & 4 deletions src/messages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,16 @@ const reinstallAppMsg = {
}

const deployAppMsg = {
APP_UID: "Provide the app UID of an existing app to be reinstalled.",
APP_DEPLOYED: "{app} deployed successfully.",
FORCE_DISCONNECT: "Force disconnect launch project by skipping the confirmation",
PROJECT_TYPE: "Project Type",
LAUNCH_PROJECT_TYPE: "Launch Project Type",
APP_URL: "App URL",
HOSTING_TYPE: "Hosting Type",
CONFIG_FILE: "[optional] path of config file",
LAUNCH_PROJECT_SKIP_MSG: "Launch Project connected! Skipping deployment process",
DISCONNECT_PROJECT: "Are you sure you want to disconnect launch Project ?"
APP_UPDATE_TERMINATION_MSG: "Launch Project connected! Skipping app hosting updates process...",
DISCONNECT_PROJECT: "Are you sure you want to disconnect launch Project ?",
PROJECT_NOT_FOUND: "Project not found!",
PROJECT_NAME_CONFLICT_FAILED: "Project name conflict resolution failed!"
}

const messages: typeof errors &
Expand Down
27 changes: 22 additions & 5 deletions src/util/common-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
LogFn,
UpdateHostingParams,
} from "../types";
import { askProjectName } from "./inquirer";
import { deployAppMsg } from "../messages";

export type CommonOptions = {
log: LogFn;
Expand Down Expand Up @@ -304,7 +306,7 @@ async function getProjects(
const projects: any = await apolloClient
.query({ query: projectsQuery, variables: { query: {} } })
.then(({ data: { projects } }: { data: { projects: any } }) => projects);

return map(
projects.edges,
({
Expand All @@ -315,16 +317,14 @@ async function getProjects(
deployment: { url },
environment: { uid: environmentUid },
},
integrations: {
developerHubApp: { uid: developerHubAppUid },
},
integrations: { developerHubApp },
},
}) => ({
name,
uid,
url,
environmentUid,
developerHubAppUid,
developerHubAppUid: developerHubApp?.uid || null,
})
);
}
Expand Down Expand Up @@ -384,6 +384,22 @@ function formatUrl(url: string): string {
return url ? (url.startsWith("https") ? url : `https://${url}`) : "";
}

const handleProjectNameConflict = async (
projectName: string,
projects: any[],
retry = 1
): Promise<string> => {
if (retry > 3) {
throw new Error(deployAppMsg.PROJECT_NAME_CONFLICT_FAILED);
}
const project = projects.find((project) => project.name === projectName);
if (project) {
projectName = await askProjectName(projectName);
return handleProjectNameConflict(projectName, projects, retry + 1);
}
return projectName;
};

export {
getOrganizations,
getOrgAppUiLocation,
Expand All @@ -404,4 +420,5 @@ export {
setupConfig,
disconnectApp,
formatUrl,
handleProjectNameConflict,
};
37 changes: 31 additions & 6 deletions src/util/inquirer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import {
import { Installation } from "@contentstack/management/types/app/installation";
import { AppTarget } from "@contentstack/management/types/app/index";

import messages, { $t, deployAppMsg, errors, uninstallAppMsg } from "../messages";
import messages, {
$t,
deployAppMsg,
errors,
uninstallAppMsg,
} from "../messages";
import {
CommonOptions,
getOrganizations,
Expand Down Expand Up @@ -342,13 +347,13 @@ async function selectProject(
}

const askProjectType = async (): Promise<string> => {
return cliux.inquire<string>({
return await cliux.inquire<string>({
type: "list",
name: "selectedProject",
message: "Project type",
name: "selected_project_type",
message: "Launch Project type",
choices: [
{ name: "Existing project", value: "existing-project" },
{ name: "New Project", value: "new-project" },
{ name: "Existing", value: "existing-project" },
{ name: "New", value: "new-project" },
],
});
};
Expand All @@ -361,6 +366,25 @@ async function askConfirmation(): Promise<boolean> {
});
}

const askProjectName = async (
projectName: string,
): Promise<string> => {
return await cliux.inquire({
type: "input",
name: "name",
validate: inquireRequireValidation,
message: `${projectName} project already exist. Enter a new name to create a project.?`,
});
};

function inquireRequireValidation(input: any): string | boolean {
if (isEmpty(input)) {
return "This field can't be empty.";
}

return true;
}

export {
getOrg,
getAppName,
Expand All @@ -375,4 +399,5 @@ export {
askProjectType,
askConfirmation,
selectProject,
askProjectName
};