Cleanup autorest cli and fix using core version from config file (#3749)

This commit is contained in:
Timothee Guerin 2020-12-17 11:37:48 -08:00 коммит произвёл GitHub
Родитель 334b242cb9
Коммит 410d193ab7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
22 изменённых файлов: 655 добавлений и 467 удалений

Просмотреть файл

@ -32,10 +32,6 @@ rules:
"@typescript-eslint/consistent-type-assertions":
- warn
- assertionStyle: "angle-bracket"
"@typescript-eslint/array-type":
- warn
- default: generic
no-undef: "off"
no-unused-vars: "off"
linebreak-style:

1
.gitignore поставляемый
Просмотреть файл

@ -207,7 +207,6 @@ src/generator/AutoRest.Go.Tests/src/tests/glide.lock
src/client/**/*
src/extension/old/**/*
*.d.ts
src/bootstrapper
src/extension/out

Просмотреть файл

@ -1,430 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
declare const isDebuggerEnabled;
const cwd = process.cwd();
import { isFile, readdir, rmdir, isDirectory } from "@azure-tools/async-io";
import { Exception, LazyPromise } from "@azure-tools/tasks";
import { homedir } from "os";
import chalk from "chalk";
import { join, dirname } from "path";
import { gt } from "semver";
import {
availableVersions,
newCorePackage,
oldCorePackage,
ensureAutorestHome,
extensionManager,
installedCores,
networkEnabled,
pkgVersion,
resolvePathForLocalVersion,
rootFolder,
selectVersion,
tryRequire,
resolveEntrypoint,
runCoreOutOfProc,
} from "./autorest-as-a-service";
import { color } from "./coloring";
import { tmpdir } from "os";
import * as vm from "vm";
import { ResolveUri, ReadUri, EnumerateFiles } from "@azure-tools/uri";
const launchCore = isDebuggerEnabled ? tryRequire : runCoreOutOfProc;
// aliases, round one.
if (process.argv.indexOf("--no-upgrade-check") !== -1) {
process.argv.push("--skip-upgrade-check");
}
if (process.argv.indexOf("--json") !== -1) {
process.argv.push("--message-format=json");
}
if (process.argv.indexOf("--yaml") !== -1) {
process.argv.push("--message-format=yaml");
}
function parseArgs(autorestArgs: Array<string>): any {
const result: any = {};
for (const arg of autorestArgs) {
const match = /^--([^=:]+)([=:](.+))?$/g.exec(arg);
if (match) {
const key = match[1];
let rawValue = match[3] || "true";
if (rawValue.startsWith(".")) {
// starts with a . or .. -> this is a relative path to current directory
rawValue = join(cwd, rawValue);
}
// untildify!
if (/^~[/|\\]/g.exec(rawValue)) {
rawValue = join(homedir(), rawValue.substring(2));
}
let value;
try {
value = JSON.parse(rawValue);
// restrict allowed types (because with great type selection comes great responsibility)
if (typeof value !== "string" && typeof value !== "boolean") {
value = rawValue;
}
} catch (e) {
value = rawValue;
}
result[key] = value;
}
}
return result;
}
const args = parseArgs(process.argv);
(<any>global).__args = args;
// aliases
args["info"] = args["info"] || args["list-installed"];
args["preview"] = args["preview"] || args["prerelease"];
if (args["v3"] && !args["version"]) {
// --v3 without --version infers --version:~3.0.6212 +
args["version"] = "~3.0.6212";
}
// Suppress the banner if the message-format is set to something other than regular.
if (!args["message-format"] || args["message-format"] === "regular") {
console.log(
chalk.green.bold.underline(
`AutoRest code generation utility [cli version: ${chalk.white.bold(pkgVersion)}; node: ${chalk.white.bold(
process.version,
)}, max-memory: ${
Math.round(require("v8").getHeapStatistics().heap_size_limit / (1024 * 1024)) & 0xffffffff00
} MB]`,
),
);
console.log(color("(C) 2018 **Microsoft Corporation.**"));
console.log(chalk.blue.bold.underline("https://aka.ms/autorest"));
}
// argument tweakin'
const preview: boolean = args.preview;
args.info = args.version === "" || args.version === true || args.info; // show --info if they use unparameterized --version.
const listAvailable: boolean = args["list-available"] || false;
const force = args.force || false;
/** Check if there is an update for the bootstrapper available. */
const checkBootstrapper = new LazyPromise(async () => {
if ((await networkEnabled) && !args["skip-upgrade-check"]) {
try {
const pkg = await (await extensionManager).findPackage("autorest", preview ? "preview" : "latest");
if (gt(pkg.version, pkgVersion)) {
console.log(
color(
`\n## There is a new version of AutoRest available (${
pkg.version
}).\n > You can install the newer version with with \`npm install -g autorest@${
preview ? "preview" : "latest"
}\`\n`,
),
);
}
} catch (e) {
// no message then.
}
}
});
/** Shows the valid available autorest core packages. */
async function showAvailableCores(): Promise<number> {
let table = "";
let max = 10;
const cores = await availableVersions();
for (const v of cores) {
max--;
table += `\n ${chalk.cyan.bold(newCorePackage.padEnd(30, " "))} ${chalk.grey.bold(v.padEnd(14, " "))} `;
if (!max) {
break;
}
}
if (args.json) {
console.log(JSON.stringify(cores, null, " "));
} else {
if (table) {
console.log(
`${chalk.green.bold.underline(" Extension Name".padEnd(30, " "))} ${chalk.green.bold.underline(
"Version".padEnd(14, " "),
)}\n${table}`,
);
}
}
return 0;
}
/** Shows all the autorest extensions that are installed. */
async function showInstalledExtensions(): Promise<number> {
const extensions = await (await extensionManager).getInstalledExtensions();
let table = "";
if (extensions.length > 0) {
for (const extension of extensions) {
table += `\n ${chalk.cyan(
(extension.name === newCorePackage || extension.name === oldCorePackage ? "core" : "extension").padEnd(10),
)} ${chalk.cyan.bold(extension.name.padEnd(40))} ${chalk.cyan(extension.version.padEnd(12))} ${chalk.cyan(
extension.location,
)}`;
}
}
if (args.json) {
console.log(JSON.stringify(extensions, null, " "));
} else {
if (table) {
console.log(
color(
`\n\n# Showing All Installed Extensions\n\n ${chalk.underline("Type".padEnd(10))} ${chalk.underline(
"Extension Name".padEnd(40),
)} ${chalk.underline("Version".padEnd(12))} ${chalk.underline("Location")} ${table}\n\n`,
),
);
} else {
console.log(color("\n\n# Showing All Installed Extensions\n\n > No Extensions are currently installed.\n\n"));
}
}
return 0;
}
/** clears out all autorest-temp folders from the temp folder*/
async function clearTempData() {
const all = [];
const tmp = tmpdir();
for (const each of await readdir(tmp)) {
if (each.startsWith("autorest")) {
const name = join(tmp, each);
if (await isDirectory(name)) {
all.push(rmdir(name));
}
}
}
if (all.length > 0) {
console.log(chalk.grey(`Clearing ${all.length} autorest temp data folders...`));
}
await Promise.all(all);
}
async function configurationSpecifiedVersion(selectedVersion: any) {
try {
// we can either have a selectedVerison object or a path. See if we can find the AutoRest API
const autorestApi = await resolveEntrypoint(
typeof selectedVersion === "string" ? selectedVersion : await selectedVersion.modulePath,
"main",
);
// things we need in the sandbox.
const sandbox = {
require,
console,
rfs: {
EnumerateFileUris: async (folderUri: string): Promise<Array<string>> => {
return EnumerateFiles(folderUri, ["readme.md"]);
},
ReadFile: async (uri: string): Promise<string> => {
return ReadUri(uri);
},
WriteFile: async (uri: string, content: string): Promise<void> => {
//return WriteString(uri, content);
},
},
cfgfile: ResolveUri(cwd, args.configFileOrFolder || "."),
switches: args,
};
// *sigh* ... there's a bug in most versions of autorest-core that to use the API you have to
// have the current directory set to the package location. We'll fix this in the future versions.
process.chdir(dirname(autorestApi));
const configSpecifiedVersion = await vm.runInNewContext(
`
async function go() {
// load the autorest api library
const r = require('${autorestApi}');
const api = new r.AutoRest(rfs,cfgfile);
// don't let the version from the cmdline affect this!
delete switches.version;
api.AddConfiguration(switches);
// resolve the configuration and return the version if there is one.
return (await api.view).rawConfig.version;
}
go();
`,
sandbox,
);
// if we got back a result, lets return that.
if (configSpecifiedVersion) {
selectedVersion = await selectVersion(configSpecifiedVersion, false);
console.log(
chalk.yellow(
`NOTE: AutoRest core version selected from configuration: ${chalk.yellow.bold(configSpecifiedVersion)}.`,
),
);
}
return selectedVersion;
} catch {
return undefined;
}
}
/** Main Entrypoint for AutoRest Bootstrapper */
async function main() {
try {
// did they ask for what is available?
if (listAvailable) {
process.exit(await showAvailableCores());
}
// show what we have.
if (args.info) {
process.exit(await showInstalledExtensions());
}
try {
/* make sure we have a .autorest folder */
await ensureAutorestHome();
if (args.reset || args["clear-temp"]) {
// clear out all the temp-data too
await clearTempData();
}
// if we have an autorest home folder, --reset may mean something.
// if it's not there, --reset won't do anything.
if (args.reset) {
if (args.debug) {
console.log(`Resetting autorest extension folder '${rootFolder}'`);
}
try {
await (await extensionManager).reset();
console.log(
color(
"\n\n## Cleared the AutoRest extension folder.\nOn the next run, extensions will be reacquired from the repository.",
),
);
process.exit(0);
} catch (e) {
console.log(
color(
"\n\n## The AutoRest extension folder appears to be locked.\nDo you have a process that is currently using AutoRest (perhaps the vscode extension?).\n\nUnable to reset the extension folder, exiting.",
),
);
process.exit(10);
}
}
} catch {
// We have a chance to fail again later if this proves problematic.
}
let requestedVersion: string =
args.version || (args.latest && "latest") || (args.preview && "preview") || "latest-installed";
// check to see if local installed core is available.
let localVersion = resolvePathForLocalVersion(args.version ? requestedVersion : null);
if (!args.version && localVersion) {
// they never specified a version on the cmdline, but we might have one in configuration
const cfgVersion = (await configurationSpecifiedVersion(localVersion))?.version;
// if we got one back, we're going to set the requestedVersion to whatever they asked for.
if (cfgVersion) {
args.version = requestedVersion = cfgVersion;
// and not use the local version
localVersion = undefined;
}
}
// if this is still valid, then we're not overriding it from configuration.
if (localVersion) {
process.chdir(cwd);
if (await launchCore(localVersion, "app.js")) {
return;
}
}
// if the resolved local version is actually a file, we'll try that as a package when we get there.
if (await isFile(localVersion)) {
// this should try to install the file.
if (args.debug) {
console.log(`Found local core package file: '${localVersion}'`);
}
requestedVersion = localVersion;
}
// failing that, we'll continue on and see if NPM can do something with the version.
if (args.debug) {
console.log(`Network Enabled: ${await networkEnabled}`);
}
// wait for the bootstrapper check to finish.
await checkBootstrapper;
// logic to resolve and optionally install a autorest core package.
// will throw if it's not doable.
let selectedVersion = await selectVersion(requestedVersion, force);
// let's strip the extra stuff from the command line before we require the core module.
const oldArgs = process.argv;
const newArgs = new Array<string>();
for (const each of process.argv) {
let keep = true;
for (const discard of [
"--version",
"--list-installed",
"--list-available",
"--reset",
"--latest",
"--latest-release",
"--runtime-id",
]) {
if (each === discard || each.startsWith(`${discard}=`) || each.startsWith(`${discard}:`)) {
keep = false;
}
}
if (keep) {
newArgs.push(each);
}
}
process.argv = newArgs;
// use this to make the core aware that this run may be legal even without any inputs
// this is a valid scenario for "preparation calls" to autorest like `autorest --reset` or `autorest --latest`
if (args.reset || args.latest || args.version == "latest") {
// if there is *any* other argument left, that's an indicator that the core is supposed to do something
process.argv.push("--allow-no-input");
}
// if they never said the version on the command line, we should make a check for the config version.
if (!args.version) {
selectedVersion = (await configurationSpecifiedVersion(selectedVersion)) || selectedVersion;
}
if (args.debug) {
console.log(`Starting ${newCorePackage} from ${await selectedVersion.location}`);
}
// reset the working folder to the correct place.
process.chdir(cwd);
const result = await launchCore(await selectedVersion.modulePath, "app.js");
if (!result) {
throw new Error(`Unable to start AutoRest Core from ${await selectedVersion.modulePath}`);
}
} catch (exception) {
console.log(chalk.redBright("Failure:"));
console.error(chalk.bold(exception));
console.error(chalk.bold((<Error>exception).stack));
process.exit(1);
}
}
main();

Просмотреть файл

@ -157,7 +157,6 @@ declare module "autorest-core" {
* Event: Signals when a message is generated
*/
Message: IEvent<MessageEmitter, Message>;
constructor();
}
export interface Directive {

Просмотреть файл

Просмотреть файл

@ -1,45 +1,62 @@
#!/usr/bin/env node
global.isDebuggerEnabled =
!!require('inspector').url()
|| global.v8debug
|| /--debug|--inspect/.test(process.execArgv.join(' '));
!!require("inspector").url() || global.v8debug || /--debug|--inspect/.test(process.execArgv.join(" "));
const maxMemorySizeArg = process.argv.join(' ').match(/--max-memory-size=(\w+)/);
const maxMemorySizeArg = process.argv.join(" ").match(/--max-memory-size=(\w+)/);
const maxMemorySize = maxMemorySizeArg && parseInt(maxMemorySizeArg[1]);
if (isNaN(maxMemorySize)) {
console.error(`\nWarning: --max-memory-size parameter '${maxMemorySizeArg[1]}' is not an integer, ignoring it.\n`);
}
// if the process was started with a low heap size (and we're not debugging!) then respawn with a bigger heap size.
if (maxMemorySize && !isDebuggerEnabled && (require('v8').getHeapStatistics()).heap_size_limit < (maxMemorySize * 1024000)) {
process.env['NODE_OPTIONS'] = `${process.env['NODE_OPTIONS'] || ''} --max-old-space-size=${maxMemorySize} --no-warnings`;
const argv = process.argv.indexOf('--break') === -1 ? process.argv.slice(1) : ['--inspect-brk', ...process.argv.slice(1).filter(each => each !== '--break')];
require('child_process').spawn(process.execPath, argv, { argv0: 'node', stdio: 'inherit' }).on('close', code => { process.exit(code); });
if (
maxMemorySize &&
!isDebuggerEnabled &&
require("v8").getHeapStatistics().heap_size_limit < maxMemorySize * 1024000
) {
process.env["NODE_OPTIONS"] = `${
process.env["NODE_OPTIONS"] || ""
} --max-old-space-size=${maxMemorySize} --no-warnings`;
const argv =
process.argv.indexOf("--break") === -1
? process.argv.slice(1)
: ["--inspect-brk", ...process.argv.slice(1).filter((each) => each !== "--break")];
require("child_process")
.spawn(process.execPath, argv, { argv0: "node", stdio: "inherit" })
.on("close", (code) => {
process.exit(code);
});
} else {
// load modules from static linker filesystem.
if (isDebuggerEnabled) {
try {
// try to let source maps resolve
require('source-map-support').install();
require("source-map-support").install();
} catch (e) {
// no worries
}
}
try {
const v = process.versions.node.split('.');
if (v[0] < 10 || v[0] === 10 && v[1] < 12) {
console.error('\nFATAL: Node v10 or higher (v10.12.x minimum, v14.x LTS recommended) is required for AutoRest.\n');
const v = process.versions.node.split(".");
if (v[0] < 10 || (v[0] === 10 && v[1] < 12)) {
console.error(
"\nFATAL: Node v10 or higher (v10.12.x minimum, v14.x LTS recommended) is required for AutoRest.\n",
);
process.exit(1);
}
if (v[0] > 14) {
console.error('\nWARNING: AutoRest has not been tested with Node versions greater than v14.\n');
console.error("\nWARNING: AutoRest has not been tested with Node versions greater than v14.\n");
}
if (process.argv.indexOf('--no-static-loader') === -1 && process.env['no-static-loader'] === undefined && require('fs').existsSync(`${__dirname}/../dist/static-loader.js`)) {
if (
process.argv.indexOf("--no-static-loader") === -1 &&
process.env["no-static-loader"] === undefined &&
require("fs").existsSync(`${__dirname}/../dist/static-loader.js`)
) {
require(`${__dirname}/../dist/static-loader.js`).load(`${__dirname}/../dist/static_modules.fs`);
}
require(`${__dirname}/../dist/app.js`);
require(`${__dirname}/../dist/src/app.js`);
} catch (e) {
console.error(e);
}

Просмотреть файл

@ -22,25 +22,24 @@
"bugs": {
"url": "https://github.com/Azure/autorest/issues"
},
"main": "./dist/exports.js",
"main": "./dist/src/exports.js",
"bin": {
"autorest": "./entrypoints/app.js"
},
"scripts": {
"start": "node ./dist/app.js",
"start": "node ./dist/src/app.js",
"test": "jest --coverage=false",
"test:ci": "jest --ci",
"build": "npm run before-build && tsc -p .",
"watch": "npm run before-build && tsc -p . --watch",
"build": "tsc -p .",
"watch": "tsc -p . --watch",
"fix": "eslint . --fix --ext .ts",
"lint": "eslint . --ext .ts --max-warnings=0",
"static-link": "static-link --force --no-node-modules",
"preinstall": "node ./.scripts/preinstall-check",
"prepack": "npm run static-link && npm run build",
"clean": "rimraf ./dist",
"before-build": "mkdirp ./dist/interfaces && cpy ./interfaces/*.d.ts ./dist/interfaces"
"clean": "rimraf ./dist"
},
"typings": "./dist/main.d.ts",
"typings": "./dist/src/main.d.ts",
"devDependencies": {
"@types/commonmark": "^0.27.0",
"@types/node": "12.7.2",

Просмотреть файл

@ -0,0 +1,32 @@
import { Package } from "@azure-tools/extension";
import { gt } from "semver";
import { AutorestArgs } from "../args";
import { extensionManager, networkEnabled, pkgVersion } from "../autorest-as-a-service";
import { color } from "../coloring";
/**
* Check if there is any updates to the autorest package and display message to use if there is.
* @param args Autorest cli args.
*/
export const checkForAutoRestUpdate = async (args: AutorestArgs) => {
if ((await networkEnabled) && !args["skip-upgrade-check"]) {
try {
const npmTag = args.preview ? "preview" : "latest";
const newVersion = await isAutorestUpdateAvailable(npmTag);
if (newVersion) {
console.log(
color(
`\n## There is a new version of AutoRest available (${newVersion.version}).\n > You can install the newer version with with \`npm install -g autorest@${npmTag}\`\n`,
),
);
}
} catch (e) {
// no message then.
}
}
};
const isAutorestUpdateAvailable = async (npmTag: string): Promise<Package | undefined> => {
const pkg = await (await extensionManager).findPackage("autorest", npmTag);
return gt(pkg.version, pkgVersion) ? pkg : undefined;
};

Просмотреть файл

@ -0,0 +1,24 @@
import { isDirectory, readdir, rmdir } from "@azure-tools/async-io";
import chalk from "chalk";
import { tmpdir } from "os";
import { join } from "path";
/**
* Clears out all autorest-temp folders from the temp folder.
*/
export const clearTempData = async () => {
const all = [];
const tmp = tmpdir();
for (const each of await readdir(tmp)) {
if (each.startsWith("autorest")) {
const name = join(tmp, each);
if (await isDirectory(name)) {
all.push(rmdir(name));
}
}
}
if (all.length > 0) {
console.log(chalk.grey(`Clearing ${all.length} autorest temp data folders...`));
}
await Promise.all(all);
};

Просмотреть файл

@ -0,0 +1,2 @@
export * from "./check-autorest-update";
export * from "./clear-temp-data";

Просмотреть файл

@ -0,0 +1,208 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
declare const isDebuggerEnabled: boolean;
const cwd = process.cwd();
import { isFile } from "@azure-tools/async-io";
import chalk from "chalk";
import {
newCorePackage,
ensureAutorestHome,
networkEnabled,
pkgVersion,
resolvePathForLocalVersion,
selectVersion,
tryRequire,
runCoreOutOfProc,
} from "./autorest-as-a-service";
import { color } from "./coloring";
import { parseArgs } from "./args";
import { resetAutorest, showAvailableCoreVersions, showInstalledExtensions } from "./commands";
import { checkForAutoRestUpdate, clearTempData } from "./actions";
import { configurationSpecifiedVersion, getRequestedCoreVersion } from "./core-version-utils";
const launchCore = isDebuggerEnabled ? tryRequire : runCoreOutOfProc;
// aliases, round one.
if (process.argv.indexOf("--no-upgrade-check") !== -1) {
process.argv.push("--skip-upgrade-check");
}
if (process.argv.indexOf("--json") !== -1) {
process.argv.push("--message-format=json");
}
if (process.argv.indexOf("--yaml") !== -1) {
process.argv.push("--message-format=yaml");
}
const args = parseArgs(process.argv);
(<any>global).__args = args;
// aliases
args["info"] = args["info"] || args["list-installed"];
args["preview"] = args["preview"] || args["prerelease"];
if (args["v3"] && !args["version"]) {
// --v3 without --version infers --version:~3.0.6212 +
args["version"] = "~3.0.6212";
}
// Suppress the banner if the message-format is set to something other than regular.
if (!args["message-format"] || args["message-format"] === "regular") {
console.log(
chalk.green.bold.underline(
`AutoRest code generation utility [cli version: ${chalk.white.bold(pkgVersion)}; node: ${chalk.white.bold(
process.version,
)}, max-memory: ${
Math.round(require("v8").getHeapStatistics().heap_size_limit / (1024 * 1024)) & 0xffffffff00
} MB]`,
),
);
console.log(color("(C) 2018 **Microsoft Corporation.**"));
console.log(chalk.blue.bold.underline("https://aka.ms/autorest"));
}
// argument tweakin'
args.info = args.version === "" || args.info; // show --info if they use unparameterized --version.
const listAvailable: boolean = args["list-available"] || false;
const force = args.force || false;
/** Main Entrypoint for AutoRest Bootstrapper */
async function main() {
try {
// did they ask for what is available?
if (listAvailable) {
process.exit(await showAvailableCoreVersions(args));
}
// show what we have.
if (args.info) {
process.exit(await showInstalledExtensions(args));
}
try {
/* make sure we have a .autorest folder */
await ensureAutorestHome();
if (args.reset || args["clear-temp"]) {
// clear out all the temp-data too
await clearTempData();
}
// if we have an autorest home folder, --reset may mean something.
// if it's not there, --reset won't do anything.
if (args.reset) {
process.exit(await resetAutorest(args));
}
} catch {
// We have a chance to fail again later if this proves problematic.
}
let requestedVersion: string = getRequestedCoreVersion(args);
// check to see if local installed core is available.
let localVersion = resolvePathForLocalVersion(args.version ? requestedVersion : null);
if (!args.version && localVersion) {
// they never specified a version on the cmdline, but we might have one in configuration
const cfgVersion = (await configurationSpecifiedVersion(args, localVersion))?.version;
// if we got one back, we're going to set the requestedVersion to whatever they asked for.
if (cfgVersion) {
args.version = requestedVersion = cfgVersion;
// and not use the local version
localVersion = undefined;
}
}
// if this is still valid, then we're not overriding it from configuration.
if (localVersion) {
process.chdir(cwd);
if (await launchCore(localVersion, "app.js")) {
return;
}
}
// if the resolved local version is actually a file, we'll try that as a package when we get there.
if (await isFile(localVersion)) {
// this should try to install the file.
if (args.debug) {
console.log(`Found local core package file: '${localVersion}'`);
}
requestedVersion = localVersion;
}
// failing that, we'll continue on and see if NPM can do something with the version.
if (args.debug) {
console.log(`Network Enabled: ${await networkEnabled}`);
}
// wait for the bootstrapper check to finish.
await checkForAutoRestUpdate(args);
// logic to resolve and optionally install a autorest core package.
// will throw if it's not doable.
let selectedVersion = await selectVersion(requestedVersion, force);
// let's strip the extra stuff from the command line before we require the core module.
const oldArgs = process.argv;
const newArgs = new Array<string>();
for (const each of process.argv) {
let keep = true;
for (const discard of [
"--version",
"--list-installed",
"--list-available",
"--reset",
"--latest",
"--latest-release",
"--runtime-id",
]) {
if (each === discard || each.startsWith(`${discard}=`) || each.startsWith(`${discard}:`)) {
keep = false;
}
}
if (keep) {
newArgs.push(each);
}
}
process.argv = newArgs;
// use this to make the core aware that this run may be legal even without any inputs
// this is a valid scenario for "preparation calls" to autorest like `autorest --reset` or `autorest --latest`
if (args.reset || args.latest || args.version == "latest") {
// if there is *any* other argument left, that's an indicator that the core is supposed to do something
process.argv.push("--allow-no-input");
}
// if they never said the version on the command line, we should make a check for the config version.
if (!args.version) {
selectedVersion = (await configurationSpecifiedVersion(args, selectedVersion)) || selectedVersion;
}
if (args.debug) {
console.log(`Starting ${newCorePackage} from ${await selectedVersion.location}`);
}
// reset the working folder to the correct place.
process.chdir(cwd);
const result = await launchCore(await selectedVersion.modulePath, "app.js");
if (!result) {
throw new Error(`Unable to start AutoRest Core from ${await selectedVersion.modulePath}`);
}
} catch (exception) {
console.log(chalk.redBright("Failure:"));
console.error(chalk.bold(exception));
console.error(chalk.bold((<Error>exception).stack));
process.exit(1);
}
}
main();

Просмотреть файл

@ -0,0 +1,83 @@
import { homedir } from "os";
import { join } from "path";
export interface AutorestArgs {
// Versioning
"v3"?: boolean;
"preview"?: boolean;
"prerelease"?: boolean;
"version"?: string;
"latest"?: boolean;
"reset"?: boolean;
"debug"?: boolean;
"info"?: boolean;
"json"?: boolean;
"configFileOrFolder"?: string;
"force"?: boolean;
"message-format"?: "regular" | "json" | "yaml";
"list-available"?: boolean;
"clear-temp"?: boolean;
"list-installed"?: boolean;
"skip-upgrade-check"?: boolean;
}
/**
* Regex to parse flags in format:
* --foo:bar
* --foo=bar
* --foo
*/
const FLAG_REGEX = /^--([^=:]+)([=:](.+))?$/;
/**
* Parse a list of command line arguments.
* @param argv List of cli args(process.argv)
*/
export const parseArgs = (argv: string[]): AutorestArgs => {
const result: any = {};
for (const arg of argv) {
const match = FLAG_REGEX.exec(arg);
if (match) {
const key = match[1];
const rawValue = resolvePathArg(match[3] || "true");
result[key] = parseValue(rawValue);
}
}
return result;
};
const cwd = process.cwd();
/**
* Check if the argument raw value is a relative path or using ~ for user home dir
* and then convert it to an aboluste one.
* @param rawValue Raw argument value.
* @returns string value
*/
const resolvePathArg = (rawValue: string): string => {
if (rawValue.startsWith(".")) {
// starts with a . or .. -> this is a relative path to current directory
rawValue = join(cwd, rawValue);
}
// untildify!
if (/^~[/|\\]/g.exec(rawValue)) {
rawValue = join(homedir(), rawValue.substring(2));
}
return rawValue;
};
const parseValue = (rawValue: string) => {
try {
const value = JSON.parse(rawValue);
// restrict allowed types (because with great type selection comes great responsibility)
if (typeof value !== "string" && typeof value !== "boolean") {
return rawValue;
}
return value;
} catch (e) {
return rawValue;
}
};

Просмотреть файл

@ -11,8 +11,9 @@ import { When } from "@azure-tools/tasks";
import { mkdtempSync, rmdirSync } from "fs";
import { tmpdir } from "os";
import { spawn } from "child_process";
import { AutorestArgs } from "./args";
export const pkgVersion: string = require(`${__dirname}/../package.json`).version;
export const pkgVersion: string = require(`${__dirname}/../../package.json`).version;
process.env["autorest.home"] = process.env["autorest.home"] || homedir();
try {
@ -23,7 +24,7 @@ try {
}
export const rootFolder = join(process.env["autorest.home"], ".autorest");
const args = (<any>global).__args || {};
const args: AutorestArgs = (<any>global).__args || {};
export const extensionManager: Promise<ExtensionManager> = ExtensionManager.Create(rootFolder);
export const oldCorePackage = "@microsoft.azure/autorest-core";
@ -167,7 +168,7 @@ export async function runCoreOutOfProc(localPath: string | null, entrypoint: str
// - loads the actual entrypoint that we expect is there.
const cmd = `
process.argv = ${JSON.stringify(process.argv)};
if (require('fs').existsSync('${__dirname}/static-loader.js')) { require('${__dirname}/static-loader.js').load('${__dirname}/static_modules.fs'); }
if (require('fs').existsSync('${__dirname}/../static-loader.js')) { require('${__dirname}/../static-loader.js').load('${__dirname}/../static_modules.fs'); }
const { color } = require('${__dirname}/coloring');
require('${ep}')
`
@ -204,7 +205,11 @@ export async function ensureAutorestHome() {
await mkdir(rootFolder);
}
export async function selectVersion(requestedVersion: string, force: boolean, minimumVersion?: string) {
export async function selectVersion(
requestedVersion: string,
force: boolean,
minimumVersion?: string,
): Promise<Extension> {
const installedVersions = await installedCores();
let currentVersion = installedVersions[0] || null;
@ -230,7 +235,7 @@ export async function selectVersion(requestedVersion: string, force: boolean, mi
}
}
let selectedVersion: any = null;
let selectedVersion: Extension = null;
// take the highest version that satisfies the version range.
for (const each of installedVersions.sort((a, b) => semver.compare(a?.version, b?.version))) {
if (semver.satisfies(each.version, requestedVersion)) {

Просмотреть файл

@ -0,0 +1,3 @@
export * from "./reset";
export * from "./show-available-core-versions";
export * from "./show-installed-extensions";

Просмотреть файл

@ -0,0 +1,33 @@
import { AutorestArgs } from "../args";
import { extensionManager, rootFolder } from "../autorest-as-a-service";
import { color } from "../coloring";
/**
* Reset autorest, this will:
* - Clear all installed extensions
* - Cleared all installed core
* @param args CLI args
* @returns Exit code.
*/
export const resetAutorest = async (args: AutorestArgs): Promise<number> => {
if (args.debug) {
console.log(`Resetting autorest extension folder '${rootFolder}'`);
}
try {
await (await extensionManager).reset();
console.log(
color(
"\n\n## Cleared the AutoRest extension folder.\nOn the next run, extensions will be reacquired from the repository.",
),
);
return 0;
} catch (e) {
console.log(
color(
"\n\n## The AutoRest extension folder appears to be locked.\nDo you have a process that is currently using AutoRest (perhaps the vscode extension?).\n\nUnable to reset the extension folder, exiting.",
),
);
return 10;
}
};

Просмотреть файл

@ -0,0 +1,33 @@
import chalk from "chalk";
import { AutorestArgs } from "../args";
import { availableVersions, newCorePackage } from "../autorest-as-a-service";
/**
* Shows the valid available autorest core packages.
* @param args CLI args
* @returns Exit code.
*/
export const showAvailableCoreVersions = async (args: AutorestArgs): Promise<number> => {
let table = "";
let max = 10;
const cores = await availableVersions();
for (const v of cores) {
max--;
table += `\n ${chalk.cyan.bold(newCorePackage.padEnd(30, " "))} ${chalk.grey.bold(v.padEnd(14, " "))} `;
if (!max) {
break;
}
}
if (args.json) {
console.log(JSON.stringify(cores, null, " "));
} else {
if (table) {
console.log(
`${chalk.green.bold.underline(" Extension Name".padEnd(30, " "))} ${chalk.green.bold.underline(
"Version".padEnd(14, " "),
)}\n${table}`,
);
}
}
return 0;
};

Просмотреть файл

@ -0,0 +1,39 @@
import chalk from "chalk";
import { AutorestArgs } from "../args";
import { extensionManager, newCorePackage, oldCorePackage } from "../autorest-as-a-service";
import { color } from "../coloring";
/**
* Shows all the autorest extensions that are installed.
* @param args CLI args
* @returns Exit code.
*/
export const showInstalledExtensions = async (args: AutorestArgs): Promise<number> => {
const extensions = await (await extensionManager).getInstalledExtensions();
let table = "";
if (extensions.length > 0) {
for (const extension of extensions) {
table += `\n ${chalk.cyan(
(extension.name === newCorePackage || extension.name === oldCorePackage ? "core" : "extension").padEnd(10),
)} ${chalk.cyan.bold(extension.name.padEnd(40))} ${chalk.cyan(extension.version.padEnd(12))} ${chalk.cyan(
extension.location,
)}`;
}
}
if (args.json) {
console.log(JSON.stringify(extensions, null, " "));
} else {
if (table) {
console.log(
color(
`\n\n# Showing All Installed Extensions\n\n ${chalk.underline("Type".padEnd(10))} ${chalk.underline(
"Extension Name".padEnd(40),
)} ${chalk.underline("Version".padEnd(12))} ${chalk.underline("Location")} ${table}\n\n`,
),
);
} else {
console.log(color("\n\n# Showing All Installed Extensions\n\n > No Extensions are currently installed.\n\n"));
}
}
return 0;
};

Просмотреть файл

@ -0,0 +1,82 @@
import { EnumerateFiles, ReadUri, ResolveUri } from "@azure-tools/uri";
import chalk from "chalk";
import { dirname } from "path";
import { AutorestArgs } from "./args";
import { resolveEntrypoint, selectVersion } from "./autorest-as-a-service";
import * as vm from "vm";
import { Extension } from "@azure-tools/extension";
/**
* Return the version requested of the core extension.
* @param args ClI args.
* @returns npm version/tag.
*/
export const getRequestedCoreVersion = (args: AutorestArgs): string => {
return args.version || (args.latest && "latest") || (args.preview && "preview") || "latest-installed";
};
const cwd = process.cwd();
/**
* Tries to load the version of autorest core from a config file.
* @param args CLI Version.
* @param selectedVersion Path to or loaded version of @autorest/core.
*/
export const configurationSpecifiedVersion = async (args: AutorestArgs, selectedVersion: string | Extension) => {
try {
// we can either have a selectedVerison object or a path. See if we can find the AutoRest API
const autorestApi = await resolveEntrypoint(
typeof selectedVersion === "string" ? selectedVersion : await selectedVersion.modulePath,
"main",
);
// things we need in the sandbox.
const sandbox = {
require,
console,
rfs: {
EnumerateFileUris: async (folderUri: string): Promise<Array<string>> => {
return EnumerateFiles(folderUri, ["readme.md"]);
},
ReadFile: (uri: string): Promise<string> => ReadUri(uri),
},
cfgfile: ResolveUri(`file://${cwd}`, args.configFileOrFolder || ""),
switches: args,
};
// *sigh* ... there's a bug in most versions of autorest-core that to use the API you have to
// have the current directory set to the package location. We'll fix this in the future versions.
process.chdir(dirname(autorestApi));
const configSpecifiedVersion = await vm.runInNewContext(
`
async function go() {
// load the autorest api library
const r = require('${autorestApi}');
const api = new r.AutoRest(rfs,cfgfile);
// don't let the version from the cmdline affect this!
delete switches.version;
api.AddConfiguration(switches);
// resolve the configuration and return the version if there is one.
return (await api.view).rawConfig.version;
}
go();
`,
sandbox,
);
// if we got back a result, lets return that.
if (configSpecifiedVersion) {
const newVersion = await selectVersion(configSpecifiedVersion, false);
console.log(
chalk.yellow(
`NOTE: AutoRest core version selected from configuration: ${chalk.yellow.bold(configSpecifiedVersion)}.`,
),
);
return newVersion;
}
return undefined;
} catch (e) {
return undefined;
}
};

Просмотреть файл

@ -5,16 +5,16 @@
// if this is being run directly, call the app entrypoint (we're probably running the local folder)
if (require.main === module) {
require("../entrypoints/app");
require("../../entrypoints/app");
}
// load modules from static linker filesystem.
if (
process.argv.indexOf("--no-static-loader") === -1 &&
process.env["no-static-loader"] === undefined &&
require("fs").existsSync("./static-loader.js")
require("fs").existsSync("../static-loader.js")
) {
require("./static-loader.js").load(`${__dirname}/static_modules.fs`);
require("../static-loader.js").load(`${__dirname}/../static_modules.fs`);
}
// everything else.
import { tryRequire, resolveEntrypoint, ensureAutorestHome, selectVersion } from "./autorest-as-a-service";

Просмотреть файл

@ -0,0 +1,63 @@
import { homedir } from "os";
import { join } from "path";
import { parseArgs } from "../src/args";
const cwd = process.cwd();
describe("Args", () => {
it("defaults to undefined", () => {
expect(parseArgs([""]).debug).toBe(undefined);
});
it("parse flags", () => {
expect(parseArgs(["--debug"]).debug).toBe(true);
});
it("parse flags with explicity value=true", () => {
expect(parseArgs(["--debug=true"]).debug).toBe(true);
});
it("parse flags with explicity value:true", () => {
expect(parseArgs(["--debug:true"]).debug).toBe(true);
});
it("parse flags with explicity value=false", () => {
expect(parseArgs(["--debug=false"]).debug).toBe(false);
});
it("parse flags with explicity value:false", () => {
expect(parseArgs(["--debug:false"]).debug).toBe(false);
});
it("parse args with absolute path using : seperator", () => {
expect(parseArgs(["--configFileOrFolder:/path/to/folder"]).configFileOrFolder).toEqual("/path/to/folder");
});
it("parse args with absolute path using = seperator", () => {
expect(parseArgs(["--configFileOrFolder=/path/to/folder"]).configFileOrFolder).toEqual("/path/to/folder");
});
it("parse args with relative path", () => {
expect(parseArgs(["--configFileOrFolder:./path/to/folder"]).configFileOrFolder).toEqual(
join(cwd, "path/to/folder"),
);
});
it("parse args with ~ path", () => {
expect(parseArgs(["--configFileOrFolder:~/path/to/folder"]).configFileOrFolder).toEqual(
join(homedir(), "path/to/folder"),
);
});
it("parse args with quotes", () => {
expect(parseArgs([`--configFileOrFolder:"/path/to/folder"`]).configFileOrFolder).toEqual("/path/to/folder");
});
it("parse multipe args", () => {
expect(parseArgs(["--debug", "--configFileOrFolder=/path/to/folder", "--preview:false"])).toEqual({
debug: true,
configFileOrFolder: "/path/to/folder",
preview: false,
});
});
});

Просмотреть файл

@ -2,7 +2,7 @@
"compilerOptions": {
"outDir": "dist",
"noErrorTruncation": true,
"noImplicitAny": false,
"noImplicitAny": true,
"module": "commonjs",
"sourceMap": true,
"newLine": "LF",
@ -15,5 +15,6 @@
"typeRoots": ["./node_modules/@types"],
"lib": ["es2018"]
},
"include": ["src/**/*.ts", "definitions/**/*.d.ts", "test/**/*.ts"],
"exclude": ["dist", "node_modules", "_lib", "lib/core/language-service"]
}