Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(cli): don't prompt for providers during convert #3431

Merged
merged 3 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 23 additions & 7 deletions packages/cdktf-cli/src/bin/cmds/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import path from "path";
import os from "os";
import { readPackageJson, projectRootPath } from "./helper/utilities";
import { readSchema } from "@cdktf/provider-schema";
import { editor } from "@inquirer/prompts";

const chalkColour = new chalk.Instance();
const config = readConfigSync();
Expand Down Expand Up @@ -109,10 +110,24 @@ export async function convert({
experimentalProviderSchemaCachePath
);

const input = await readStreamAsString(
process.stdin,
"No stdin was passed, please use it like this: cat main.tf | cdktf convert > imported.ts"
);
let input: string | undefined = undefined;
try {
input = await readStreamAsString(process.stdin);
} catch (e) {
logger.debug(`No TTY stream passed to convert, using interactive input`);
try {
input = await editor({
message:
"Enter your Terrafrom code to convert to cdktf (or run the command again and pass it as stdin):",
postfix: ".tf",
waitForUseInput: true,
});
} catch (err) {
throw Errors.Usage(
"No Terraform code to convert was provided. Please provide Terraform code to convert as stdin or run the command again and let the CLI open the editor."
);
}
}

const needsProject = language !== "typescript";

Expand All @@ -128,7 +143,7 @@ export async function convert({
logger.setLevel("ERROR");
await init({
template: "typescript",
providers: provider,
providers: provider || [],
projectName: path.basename(tempDir),
projectDescription: "Temporary project for conversion",
local: true,
Expand All @@ -137,6 +152,7 @@ export async function convert({
dist: pkg.version === "0.0.0" ? dist : undefined,
cdktfVersion: pkg.version,
silent: true,
nonInteractive: true,
});
logger.useDefaultLevel();
}
Expand Down Expand Up @@ -401,7 +417,7 @@ export async function login(argv: { tfeHostname: string }) {
const terraformLogin = new TerraformLogin(argv.tfeHostname);
let token = "";
try {
token = await readStreamAsString(process.stdin, "No stdin was passed");
token = await readStreamAsString(process.stdin);
} catch (e) {
logger.debug(`No TTY stream passed to login`);
}
Expand All @@ -413,7 +429,7 @@ export async function login(argv: { tfeHostname: string }) {
if (token) {
await terraformLogin.saveTerraformCredentials(sanitizedToken);
} else {
token = await terraformLogin.askToLogin();
token = await terraformLogin.askToLogin(false);
if (token === "") {
throw Errors.Usage(`No Terraform Cloud token was provided.`);
}
Expand Down
44 changes: 35 additions & 9 deletions packages/cdktf-cli/src/bin/cmds/helper/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ type Options = {
enableCrashReporting?: boolean;
tfeHostname?: string;
silent?: boolean;
nonInteractive?: boolean;
};

export async function runInit(argv: Options) {
Expand All @@ -93,7 +94,7 @@ export async function runInit(argv: Options) {
// without a token and set up the project.

const terraformLogin = new TerraformLogin(terraformRemoteHostname);
token = await terraformLogin.askToLogin();
token = await terraformLogin.askToLogin(argv.nonInteractive ?? false);
} else {
if (!argv.silent) {
console.log(chalkColour`{yellow Note: By supplying '--local' option you have chosen local storage mode for storing the state of your stack.
Expand All @@ -116,9 +117,21 @@ This means that your Terraform state file will be stored locally on disk in a fi
}

// Gather information about the template and the project
const templateInfo = await getTemplate(template);
const templateInfo = await getTemplate(
template,
argv.nonInteractive ?? false
);
telemetryData.template = templateInfo.Name;

if (!argv.projectName && argv.nonInteractive) {
throw Errors.Usage(
"You are trying to initialize a project without specifying a project name in non-interactive mode. This can also happen when running cdktf convert against a project not using Typescript, since we need to create a temporary cdktf project for an accurate translation. If this happens using convert, please report it as a bug. Please specify a project name using the --project-name option."
);
}
if (!argv.projectDescription && argv.nonInteractive) {
("You are trying to initialize a project without specifying a project description in non-interactive mode. This can also happen when running cdktf convert against a project not using Typescript, since we need to create a temporary cdktf project for an accurate translation. If this happens using convert, please report it as a bug. Please specify a project name using the --project-description option.");
}

const { projectInfo, useTerraformCloud } = await gatherInfo(
token,
terraformRemoteHostname,
Expand All @@ -131,7 +144,9 @@ This means that your Terraform state file will be stored locally on disk in a fi
let fromTerraformProject = argv.fromTerraformProject || undefined;
if (!fromTerraformProject) {
if (templateInfo.Name === "typescript") {
fromTerraformProject = await getTerraformProject();
fromTerraformProject = await getTerraformProject(
argv.nonInteractive ?? false
);
}
} else if (fromTerraformProject === "no") {
fromTerraformProject = undefined;
Expand Down Expand Up @@ -159,9 +174,10 @@ This means that your Terraform state file will be stored locally on disk in a fi
const sendCrashReports =
argv.enableCrashReporting ??
(ci ? false : await askForCrashReportingConsent());
const providers = argv.providers?.length
? argv.providers
: await askForProviders();
const providers =
argv.providers?.length || argv.nonInteractive
? argv.providers
: await askForProviders();

let convertResult, importPath;
if (fromTerraformProject) {
Expand Down Expand Up @@ -422,8 +438,10 @@ You can create one here: https://${terraformRemoteHostname}/app/organizations/ne
};
}

async function getTerraformProject(): Promise<string | undefined> {
if (!isInteractiveTerminal()) {
async function getTerraformProject(
nonInteractive: boolean
): Promise<string | undefined> {
if (!isInteractiveTerminal() || nonInteractive) {
return Promise.resolve(undefined);
}
const shouldUseTerraformProject = await confirm({
Expand Down Expand Up @@ -461,8 +479,16 @@ async function getTerraformProject(): Promise<string | undefined> {
*
* @param templateName either the name of built-in templates or an url pointing to a zip archive
*/
async function getTemplate(templateName: string): Promise<Template> {
async function getTemplate(
templateName: string,
nonInteractive: boolean
): Promise<Template> {
if (templateName == "") {
if (nonInteractive) {
throw Errors.Usage(
"You are trying to initialize a project without specifying a template in non-interactive mode. This can also happen when running cdktf convert against a project not using Typescript, since we need to create a temporary cdktf project for an accurate translation. Please specify a template using the --template option in init or --language in convert."
);
}
const templateOptionRemote = "<remote zip file>";
const options = [...templates, templateOptionRemote];
// Prompt for template
Expand Down
15 changes: 13 additions & 2 deletions packages/cdktf-cli/src/bin/cmds/helper/terraform-login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ import { confirm, password } from "@inquirer/prompts";
import * as open from "open";
import * as chalk from "chalk";
import * as terraformCloudClient from "./terraform-cloud-client";
import { logger } from "@cdktf/commons";
import { Errors, logger } from "@cdktf/commons";

const chalkColour = new chalk.Instance();
const homedir = require("os").homedir();
const terraformCredentialsFilePath = `${homedir}/.terraform.d/credentials.tfrc.json`;

const nonIteractiveLoginWithNoTokenError = (invalid: boolean) =>
Errors.Usage(
`You are trying to use terraform init in a non-interactive mode while ${
invalid ? "having an invalid token for" : "not being logged in to"
} Terraform Cloud. This can also happen when running cdktf convert against a project not using Typescript, since we need to create a temporary cdktf project for an accurate translation. Please run 'cdktf login' to log in.`
);

DanielMSchmidt marked this conversation as resolved.
Show resolved Hide resolved
export interface Hostname {
token: string;
}
Expand Down Expand Up @@ -147,7 +154,7 @@ the following file for use by subsequent Terraform commands:
}
}

public async askToLogin(): Promise<string> {
public async askToLogin(nonInteractive: boolean): Promise<string> {
const hasToken = await this.checkIfTerraformCredentialsExist();
const token: string | null = hasToken
? await this.getTokenFromTerraformCredentialsFile()
Expand All @@ -157,6 +164,10 @@ the following file for use by subsequent Terraform commands:
return token;
}

// if we are not interactive, we need to abort
if (nonInteractive) {
throw nonIteractiveLoginWithNoTokenError(Boolean(token));
}
// we either have no token or not a valid one
const shouldContinue = await this.askToContinue();
if (shouldContinue) {
Expand Down
5 changes: 2 additions & 3 deletions packages/cdktf-cli/src/bin/cmds/helper/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,11 @@ export const requireHandlers = () => {
};

export function readStreamAsString(
stream: typeof process.stdin,
noTTYErrorMessage: string
stream: typeof process.stdin
): Promise<string> {
return new Promise((ok, ko) => {
if (stream.isTTY) {
ko(noTTYErrorMessage);
ko();
} else {
let string = "";
stream.on("data", (data) => (string += data.toString()));
Expand Down
Loading