Interpolate generator with plugin name when using default license header (#4073)

This commit is contained in:
Timothee Guerin 2021-04-20 10:10:58 -07:00 коммит произвёл GitHub
Родитель 801c0576f0
Коммит 01653d7c03
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 161 добавлений и 51 удалений

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

@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@autorest/configuration",
"comment": "",
"type": "none"
}
],
"packageName": "@autorest/configuration",
"email": "tiguerin@microsoft.com"
}

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

@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@autorest/core",
"comment": "**Fix** Default license header containing uninterpolated {generator}",
"type": "patch"
}
],
"packageName": "@autorest/core",
"email": "tiguerin@microsoft.com"
}

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

@ -80,7 +80,7 @@ export class AutoRest extends EventEmitter {
messageEmitter.Message.Subscribe((cfg, message) => this.Message.Dispatch(message)); messageEmitter.Message.Subscribe((cfg, message) => this.Message.Dispatch(message));
const stats = new StatsCollector(); const stats = new StatsCollector();
return (this._view = await new AutorestContextLoader(this.fileSystem, stats, this.configFileOrFolderUri).CreateView( return (this._view = await new AutorestContextLoader(this.fileSystem, stats, this.configFileOrFolderUri).createView(
messageEmitter, messageEmitter,
includeDefault, includeDefault,
...this._configurations, ...this._configurations,

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

@ -95,7 +95,7 @@ export class AutorestContextLoader {
} }
} }
public async CreateView( public async createView(
messageEmitter: MessageEmitter, messageEmitter: MessageEmitter,
includeDefault: boolean, includeDefault: boolean,
...configs: AutorestRawConfiguration[] ...configs: AutorestRawConfiguration[]
@ -127,7 +127,11 @@ export class AutorestContextLoader {
loadedExtensions[definition.fullyQualified] = { loadedExtensions[definition.fullyQualified] = {
extension, extension,
autorestExtension: new LazyPromise(async () => autorestExtension: new LazyPromise(async () =>
AutoRestExtension.FromChildProcess(definition.name, await extension.start(enableDebugger)), AutoRestExtension.fromChildProcess(
definition.name,
extension.version,
await extension.start(enableDebugger),
),
), ),
}; };
} }

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

@ -19,6 +19,7 @@ import { AutorestCoreLogger } from "./logger";
import { VERSION } from "../constants"; import { VERSION } from "../constants";
import { StatsCollector } from "../stats"; import { StatsCollector } from "../stats";
import { LoggingSession } from "./logging-session"; import { LoggingSession } from "./logging-session";
import { PipelinePluginDefinition } from "../pipeline/plugin-loader";
export class AutorestContext implements AutorestLogger { export class AutorestContext implements AutorestLogger {
public config: AutorestConfiguration; public config: AutorestConfiguration;
@ -31,6 +32,7 @@ export class AutorestContext implements AutorestLogger {
public messageEmitter: MessageEmitter, public messageEmitter: MessageEmitter,
public stats: StatsCollector, public stats: StatsCollector,
public asyncLogManager: LoggingSession, public asyncLogManager: LoggingSession,
private plugin?: PipelinePluginDefinition,
) { ) {
this.config = config; this.config = config;
this.logger = new AutorestCoreLogger(config, messageEmitter, asyncLogManager); this.logger = new AutorestCoreLogger(config, messageEmitter, asyncLogManager);
@ -115,12 +117,14 @@ export class AutorestContext implements AutorestLogger {
public get HeaderText(): string { public get HeaderText(): string {
const h = this.config["header-definitions"]; const h = this.config["header-definitions"];
const defaultText = this.getDefaultHeaderText();
switch (this.config["license-header"]?.toLowerCase()) { switch (this.config["license-header"]?.toLowerCase()) {
case "microsoft_mit": case "microsoft_mit":
return `${h.microsoft}\n${h.mit}\n${h.default.replace("{core}", VERSION)}\n${h.warning}`; return `${h.microsoft}\n${h.mit}\n${defaultText}\n${h.warning}`;
case "microsoft_apache": case "microsoft_apache":
return `${h.microsoft}\n${h.apache}\n${h.default.replace("{core}", VERSION)}\n${h.warning}`; return `${h.microsoft}\n${h.apache}\n${defaultText}\n${h.warning}`;
case "microsoft_mit_no_version": case "microsoft_mit_no_version":
return `${h.microsoft}\n${h.mit}\n${h["no-version"]}\n${h.warning}`; return `${h.microsoft}\n${h.mit}\n${h["no-version"]}\n${h.warning}`;
@ -135,20 +139,26 @@ export class AutorestContext implements AutorestLogger {
return ""; return "";
case "microsoft_mit_small": case "microsoft_mit_small":
return `${h.microsoft}\n${h["mit-small"]}\n${h.default.replace("{core}", VERSION)}\n${h.warning}`; return `${h.microsoft}\n${h["mit-small"]}\n${defaultText}\n${h.warning}`;
case "microsoft_mit_small_no_codegen": case "microsoft_mit_small_no_codegen":
return `${h.microsoft}\n${h["mit-small"]}\n${h["no-version"]}`; return `${h.microsoft}\n${h["mit-small"]}\n${h["no-version"]}`;
case null: case null:
case undefined: case undefined:
return `${h.default.replace("{core}", VERSION)}\n${h.warning}`; return `${defaultText}\n${h.warning}`;
default: default:
return `${this.config["license-header"]}`; return `${this.config["license-header"]}`;
} }
} }
private getDefaultHeaderText() {
const extension = this.plugin?.extension;
const generator = extension ? `${extension?.extensionName}@${extension?.extensionVersion}` : "";
return this.config["header-definitions"].default.replace("{core}", VERSION).replace("{generator}", generator);
}
public IsOutputArtifactRequested(artifact: string): boolean { public IsOutputArtifactRequested(artifact: string): boolean {
return From(arrayOf<string>(this.config["output-artifact"])).Contains(artifact); return From(arrayOf<string>(this.config["output-artifact"])).Contains(artifact);
} }
@ -178,9 +188,21 @@ export class AutorestContext implements AutorestLogger {
return result; return result;
} }
public *getNestedConfiguration(pluginName: string): Iterable<AutorestContext> { /**
for (const nestedConfig of getNestedConfiguration(this.config, pluginName)) { * Get a new configuration that is extended with the properties under the given scope.
yield new AutorestContext(nestedConfig, this.fileSystem, this.messageEmitter, this.stats, this.asyncLogManager); * @param scope Name of the nested property to flatten.
* @param plugin Optional plugin requesting this configuration.
*/
public *getNestedConfiguration(scope: string, plugin?: PipelinePluginDefinition): Iterable<AutorestContext> {
for (const nestedConfig of getNestedConfiguration(this.config, scope)) {
yield new AutorestContext(
nestedConfig,
this.fileSystem,
this.messageEmitter,
this.stats,
this.asyncLogManager,
plugin,
);
} }
} }
@ -190,6 +212,13 @@ export class AutorestContext implements AutorestLogger {
*/ */
public extendWith(...overrides: AutorestNormalizedConfiguration[]): AutorestContext { public extendWith(...overrides: AutorestNormalizedConfiguration[]): AutorestContext {
const nestedConfig = extendAutorestConfiguration(this.config, overrides); const nestedConfig = extendAutorestConfiguration(this.config, overrides);
return new AutorestContext(nestedConfig, this.fileSystem, this.messageEmitter, this.stats, this.asyncLogManager); return new AutorestContext(
nestedConfig,
this.fileSystem,
this.messageEmitter,
this.stats,
this.asyncLogManager,
this.plugin,
);
} }
} }

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

@ -16,17 +16,17 @@ import {
PipeState, PipeState,
mergePipeStates, mergePipeStates,
} from "@azure-tools/datastore"; } from "@azure-tools/datastore";
import { AutorestContext, getExtension } from "../context"; import { AutorestContext } from "../context";
import { Channel } from "../message"; import { Channel } from "../message";
import { OutstandingTaskAwaiter } from "../outstanding-task-awaiter"; import { OutstandingTaskAwaiter } from "../outstanding-task-awaiter";
import { AutoRestExtension } from "./plugin-endpoint";
import { createArtifactEmitterPlugin } from "../plugins/emitter"; import { createArtifactEmitterPlugin } from "../plugins/emitter";
import { createExternalPlugin } from "../plugins/external";
import { createHash } from "crypto"; import { createHash } from "crypto";
import { isCached, readCache, writeCache } from "./pipeline-cache"; import { isCached, readCache, writeCache } from "./pipeline-cache";
import { values } from "@azure-tools/linq"; import { values } from "@azure-tools/linq";
import { PLUGIN_MAP } from "../plugins"; import { CORE_PLUGIN_MAP } from "../plugins";
import { loadPlugins, PipelinePluginDefinition } from "./plugin-loader";
import { mapValues, omitBy } from "lodash";
const safeEval = createSandbox(); const safeEval = createSandbox();
@ -44,6 +44,7 @@ interface PipelineNode {
function buildPipeline( function buildPipeline(
context: AutorestContext, context: AutorestContext,
plugins: { [key: string]: PipelinePluginDefinition },
): { pipeline: { [name: string]: PipelineNode }; configs: { [jsonPath: string]: AutorestContext } } { ): { pipeline: { [name: string]: PipelineNode }; configs: { [jsonPath: string]: AutorestContext } } {
const cfgPipeline = context.GetEntry("pipeline"); const cfgPipeline = context.GetEntry("pipeline");
const pipeline: { [name: string]: PipelineNode } = {}; const pipeline: { [name: string]: PipelineNode } = {};
@ -89,7 +90,8 @@ function buildPipeline(
} }
// derive information about given pipeline stage // derive information about given pipeline stage
const plugin = cfg.plugin || stageName.split("/").reverse()[0]; const pluginName = cfg.plugin || stageName.split("/").reverse()[0];
const plugin = plugins[pluginName];
const outputArtifact = cfg["output-artifact"]; const outputArtifact = cfg["output-artifact"];
let scope = cfg.scope; let scope = cfg.scope;
if (!cfg.scope) { if (!cfg.scope) {
@ -120,7 +122,7 @@ function buildPipeline(
) => { ) => {
if (inputNodes.length === 0) { if (inputNodes.length === 0) {
const config = configCache[stringify(configScope)]; const config = configCache[stringify(configScope)];
const configs = scope ? [...config.getNestedConfiguration(scope)] : [config]; const configs = scope ? [...config.getNestedConfiguration(scope, plugin)] : [config];
for (let i = 0; i < configs.length; ++i) { for (let i = 0; i < configs.length; ++i) {
const newSuffix = configs.length === 1 ? "" : "/" + i; const newSuffix = configs.length === 1 ? "" : "/" + i;
suffixes.push(suffix + newSuffix); suffixes.push(suffix + newSuffix);
@ -133,7 +135,7 @@ function buildPipeline(
} }
configCache[stringify(path)] = configs[i]; configCache[stringify(path)] = configs[i];
pipeline[stageName + suffix + newSuffix] = { pipeline[stageName + suffix + newSuffix] = {
pluginName: plugin, pluginName: pluginName,
outputArtifact, outputArtifact,
configScope: path, configScope: path,
inputs, inputs,
@ -185,19 +187,11 @@ function isDrainRequired(p: PipelineNode) {
} }
export async function runPipeline(configView: AutorestContext, fileSystem: IFileSystem): Promise<void> { export async function runPipeline(configView: AutorestContext, fileSystem: IFileSystem): Promise<void> {
// built-in plugins const plugins = await loadPlugins(configView);
const __extensionExtension = mapValues(
// dynamically loaded, auto-discovered plugins omitBy(plugins, (x) => x.builtIn),
const __extensionExtension: { [pluginName: string]: AutoRestExtension } = {}; (x) => x.extension,
for (const useExtensionQualifiedName of configView.GetEntry("used-extension") || []) { );
const extension = await getExtension(useExtensionQualifiedName);
for (const plugin of await extension.GetPluginNames(configView.CancellationToken)) {
if (!PLUGIN_MAP[plugin]) {
PLUGIN_MAP[plugin] = createExternalPlugin(extension, plugin);
__extensionExtension[plugin] = extension;
}
}
}
// __status scope // __status scope
const startTime = Date.now(); const startTime = Date.now();
@ -232,8 +226,8 @@ export async function runPipeline(configView: AutorestContext, fileSystem: IFile
// TODO: think about adding "number of files in scope" kind of validation in between pipeline steps // TODO: think about adding "number of files in scope" kind of validation in between pipeline steps
const fsInput = configView.DataStore.GetReadThroughScope(fileSystem); const fsInput = configView.DataStore.getReadThroughScope(fileSystem);
const pipeline = buildPipeline(configView); const pipeline = buildPipeline(configView, plugins);
const times = !!configView.config["timestamp"]; const times = !!configView.config["timestamp"];
const tasks: { [name: string]: Promise<DataSource> } = {}; const tasks: { [name: string]: Promise<DataSource> } = {};
@ -292,12 +286,12 @@ export async function runPipeline(configView: AutorestContext, fileSystem: IFile
configEntry?.["null"] === true || values(configView.GetEntry("null")).any((each) => each === pluginName); configEntry?.["null"] === true || values(configView.GetEntry("null")).any((each) => each === pluginName);
const plugin = usenull const plugin = usenull
? PLUGIN_MAP.null ? CORE_PLUGIN_MAP.null
: passthru : passthru
? PLUGIN_MAP.identity ? CORE_PLUGIN_MAP.identity
: pluginName === "pipeline-emitter" : pluginName === "pipeline-emitter"
? pipelineEmitterPlugin ? pipelineEmitterPlugin
: PLUGIN_MAP[pluginName]; : plugins[pluginName]?.plugin;
if (!plugin) { if (!plugin) {
throw new Error(`Plugin '${pluginName}' not found.`); throw new Error(`Plugin '${pluginName}' not found.`);

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

@ -62,17 +62,21 @@ export class AutoRestExtension extends EventEmitter {
public static async FromModule(modulePath: string): Promise<AutoRestExtension> { public static async FromModule(modulePath: string): Promise<AutoRestExtension> {
const childProc = fork(modulePath, [], <any>{ silent: true }); const childProc = fork(modulePath, [], <any>{ silent: true });
return AutoRestExtension.FromChildProcess(modulePath, childProc); return AutoRestExtension.fromChildProcess(modulePath, "", childProc);
} }
public static async FromChildProcess(extensionName: string, childProc: ChildProcess): Promise<AutoRestExtension> { public static async fromChildProcess(
extensionName: string,
version: string,
childProc: ChildProcess,
): Promise<AutoRestExtension> {
if (childProc.stdout === null) { if (childProc.stdout === null) {
throw new Error("Child Process has no stdout pipe."); throw new Error("Child Process has no stdout pipe.");
} }
if (childProc.stdin === null) { if (childProc.stdin === null) {
throw new Error("Child Process has no stdin pipe."); throw new Error("Child Process has no stdin pipe.");
} }
const plugin = new AutoRestExtension(extensionName, childProc.stdout, childProc.stdin, childProc); const plugin = new AutoRestExtension(extensionName, version, childProc.stdout, childProc.stdin, childProc);
if (childProc.stderr !== null) { if (childProc.stderr !== null) {
childProc.stderr.pipe(process.stderr); childProc.stderr.pipe(process.stderr);
} }
@ -91,7 +95,8 @@ export class AutoRestExtension extends EventEmitter {
private __inspectTraffic: Array<[number, boolean /*outgoing (core => ext)*/, string]> = []; private __inspectTraffic: Array<[number, boolean /*outgoing (core => ext)*/, string]> = [];
public constructor( public constructor(
private extensionName: string, public extensionName: string,
public extensionVersion: string,
reader: Readable, reader: Readable,
writer: Writable, writer: Writable,
private childProcess: ChildProcess, private childProcess: ChildProcess,

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

@ -0,0 +1,58 @@
import { AutorestContext, getExtension } from "../context";
import { CORE_PLUGIN_MAP } from "../plugins";
import { createExternalPlugin } from "../plugins/external";
import { PipelinePlugin } from "./common";
import { AutoRestExtension } from "./plugin-endpoint";
export interface PipelinePluginDefinition {
/**
* Name of the plugin.
*/
readonly name: string;
/**
* Plugin function.
*/
readonly plugin: PipelinePlugin;
/**
* Extension defining plugin or undefined if built-in.
*/
readonly extension: AutoRestExtension | undefined;
/**
* If this plugin is built-in inside of @autorest/core.
*/
readonly builtIn: boolean;
}
/**
* Resolve all the plugins defined in core and loaded extensions.
* @param context AutorestContext
* @returns Map of plugin name to plugin definition.
*/
export async function loadPlugins(
context: AutorestContext,
): Promise<{ [pluginName: string]: PipelinePluginDefinition }> {
const plugins: { [pluginName: string]: PipelinePluginDefinition } = {};
for (const [name, plugin] of Object.entries(CORE_PLUGIN_MAP)) {
plugins[name] = { name, plugin, builtIn: true, extension: undefined };
}
for (const useExtensionQualifiedName of context.config["used-extension"] || []) {
const extension = await getExtension(useExtensionQualifiedName);
for (const name of await extension.GetPluginNames(context.CancellationToken)) {
if (!plugins[name]) {
plugins[name] = {
name,
plugin: createExternalPlugin(extension, name),
builtIn: false,
extension: extension,
};
}
}
}
return plugins;
}

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

@ -32,13 +32,12 @@ import { createApiVersionParameterHandlerPlugin } from "./version-param-handler"
import { createJsonToYamlPlugin, createYamlToJsonPlugin } from "./yaml-and-json"; import { createJsonToYamlPlugin, createYamlToJsonPlugin } from "./yaml-and-json";
import { createOpenApiSchemaValidatorPlugin, createSwaggerSchemaValidatorPlugin } from "./schema-validation"; import { createOpenApiSchemaValidatorPlugin, createSwaggerSchemaValidatorPlugin } from "./schema-validation";
import { createOpenAPIStatsCollectorPlugin } from "./openapi-stats-collector"; import { createOpenAPIStatsCollectorPlugin } from "./openapi-stats-collector";
import { PipelinePlugin } from "../pipeline/common";
import { QuickDataSource } from "@azure-tools/datastore"; import { QuickDataSource } from "@azure-tools/datastore";
import { createCSharpReflectApiVersionPlugin } from "./metadata-generation"; import { createCSharpReflectApiVersionPlugin } from "./metadata-generation";
import { createComponentModifierPlugin } from "./component-modifier"; import { createComponentModifierPlugin } from "./component-modifier";
import { createSemanticValidationPlugin } from "./semantics-validation"; import { createSemanticValidationPlugin } from "./semantics-validation";
export const PLUGIN_MAP: { [name: string]: PipelinePlugin } = { export const CORE_PLUGIN_MAP = {
"help": createHelpPlugin(), "help": createHelpPlugin(),
"identity": createIdentityPlugin(), "identity": createIdentityPlugin(),
"null": createNullPlugin(), "null": createNullPlugin(),

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

@ -4,7 +4,7 @@ This configuration applies to every run of AutoRest, but with less priority than
## Basic Settings ## Basic Settings
``` yaml ```yaml
azure-arm: false azure-arm: false
output-folder: generated output-folder: generated
openapi-type: arm openapi-type: arm
@ -40,7 +40,7 @@ require:
These extra settings are applied when `azure-arm: true`: These extra settings are applied when `azure-arm: true`:
``` yaml $(azure-arm) ```yaml $(azure-arm)
head-as-boolean: true head-as-boolean: true
``` ```
@ -49,7 +49,7 @@ head-as-boolean: true
If `--legacy` isn't provided, we set the `track2` option to `true` to indicate If `--legacy` isn't provided, we set the `track2` option to `true` to indicate
to V3 generators that the user wants to generate a Track 2 library. to V3 generators that the user wants to generate a Track 2 library.
``` yaml !$(legacy) ```yaml !$(legacy)
track2: true track2: true
``` ```
@ -57,11 +57,11 @@ track2: true
If we don't specify `--help`, we will trigger the setting to load files If we don't specify `--help`, we will trigger the setting to load files
``` yaml !$(help) ```yaml !$(help)
perform-load: true # kick off loading perform-load: true # kick off loading
``` ```
``` yaml enableAllVersionsMode() ```yaml enableAllVersionsMode()
# when an autorest-v3 generator is loading, and a profile or api-verison is specified, # when an autorest-v3 generator is loading, and a profile or api-verison is specified,
# we need to force the tag: all-api-versions so that it loads the whole api set. # we need to force the tag: all-api-versions so that it loads the whole api set.
# but not TOO high, as then it'll be evaluated before $(pipeline-model) # but not TOO high, as then it'll be evaluated before $(pipeline-model)
@ -69,11 +69,11 @@ tag: all-api-versions
load-priority: 500 load-priority: 500
``` ```
``` yaml ```yaml
header-definitions: header-definitions:
warning: Changes may cause incorrect behavior and will be lost if the code is regenerated. warning: Changes may cause incorrect behavior and will be lost if the code is regenerated.
default: 'Code generated by Microsoft (R) AutoRest Code Generator (autorest: {core}, generator: {generator})' default: "Code generated by Microsoft (R) AutoRest Code Generator (autorest: {core}, generator: {generator})"
no-version: Code generated by Microsoft (R) AutoRest Code Generator. no-version: Code generated by Microsoft (R) AutoRest Code Generator.
@ -93,5 +93,4 @@ header-definitions:
mit-small: Licensed under the MIT License. mit-small: Licensed under the MIT License.
microsoft: Copyright (c) Microsoft Corporation. All rights reserved. microsoft: Copyright (c) Microsoft Corporation. All rights reserved.
```