split the common.ts into several files

This commit is contained in:
Artur Stolear
2020-02-07 22:18:42 +02:00
parent 237764149f
commit 7dee6ba225
20 changed files with 2354 additions and 1468 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,23 +1,21 @@
export const TYPES = {
IBuildAgent: Symbol.for("BuildAgent"),
IDotnetTool: Symbol.for("DotnetTool"),
IGitVersionTool: Symbol.for("GitVersionTool"),
IGitReleaseManagerTool: Symbol.for("GitReleaseManagerTool"),
IVersionManager: Symbol.for("VersionManager"),
};
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import * as semver from "semver";
import * as http from "typed-rest-client/HttpClient";
export const SetupOptions = {
includePrerelease: "includePrerelease",
versionSpec: "versionSpec",
};
import cmp from "semver-compare";
import { inject, injectable } from "inversify";
import { BuildAgent } from "../agent/mock/build-agent";
import { Container } from "inversify";
const container = new Container();
export interface IVersionManager {
isExplicitVersion(versionSpec: string): boolean;
evaluateVersions(versions: string[], versionSpec: string): string;
cleanVersion(version: string): string;
export interface IExecResult {
stdout: string;
stderr: string;
code: number;
error: Error;
}
export interface IBuildAgent {
@@ -42,216 +40,4 @@ export interface IBuildAgent {
setOutput(name: string, value: string): void;
getInput(input: string, required?: boolean): string;
getBooleanInput(input: string, required?: boolean): boolean;
}
export interface IDotnetTool {
run(args: string[]): Promise<IExecResult>;
toolInstall(toolName: string, versionSpec: string, checkLatest: boolean, includePre: boolean): Promise<string>;
}
export interface IExecResult {
stdout: string;
stderr: string;
code: number;
error: Error;
}
export const TYPES = {
IBuildAgent: Symbol.for("BuildAgent"),
IDotnetTool: Symbol.for("DotnetTool"),
IGitVersionTool: Symbol.for("GitVersionTool"),
IGitReleaseManagerTool: Symbol.for("GitReleaseManagerTool"),
IVersionManager: Symbol.for("VersionManager"),
};
export const SetupOptions = {
includePrerelease: "includePrerelease",
versionSpec: "versionSpec",
};
@injectable()
class DotnetTool implements IDotnetTool {
private buildAgent: IBuildAgent;
private versionManager: IVersionManager;
private httpClient: http.HttpClient;
constructor(
@inject(TYPES.IBuildAgent) buildAgent: IBuildAgent,
@inject(TYPES.IVersionManager) versionManager: IVersionManager,
) {
this.buildAgent = buildAgent;
this.versionManager = versionManager;
this.httpClient = new http.HttpClient("dotnet");
}
public run(args: string[]): Promise<IExecResult> {
return this.buildAgent.exec("dotnet", args);
}
public async toolInstall(toolName: string, versionSpec: string, checkLatest: boolean, includePre: boolean)
: Promise<string> {
console.log("");
console.log("--------------------------");
console.log(`Installing ${toolName} version ` + versionSpec);
console.log("--------------------------");
if (this.versionManager.isExplicitVersion(versionSpec)) {
checkLatest = false; // check latest doesn't make sense when explicit version
}
let toolPath: string;
if (!checkLatest) {
//
// Let's try and resolve the version spec locally first
//
toolPath = this.buildAgent.find(toolName, versionSpec);
}
if (!toolPath) {
let version: string;
if (this.versionManager.isExplicitVersion(versionSpec)) {
//
// Explicit version was specified. No need to query for list of versions.
//
version = versionSpec;
} else {
//
// Let's query and resolve the latest version for the versionSpec.
// If the version is an explicit version (1.1.1 or v1.1.1) then no need to query.
// If your tool doesn't offer a mechanism to query,
// then it can only support exact version inputs.
//
version = await this.queryLatestMatch(toolName, versionSpec, includePre);
if (!version) {
throw new Error(`Unable to find ${toolName} version '${versionSpec}'.`);
}
//
// Check the cache for the resolved version.
//
toolPath = this.buildAgent.find(toolName, version);
}
if (!toolPath) {
//
// Download, extract, cache
//
toolPath = await this.acquireTool(toolName, version);
}
}
//
// Prepend the tools path. This prepends the PATH for the current process and
// instructs the agent to prepend for each task that follows.
//
this.buildAgent.debug(`toolPath: ${toolPath}`);
if (os.platform() !== "win32") {
const dotnetRoot = path.dirname(fs.readlinkSync(await this.buildAgent.which("dotnet")));
this.buildAgent.exportVariable("DOTNET_ROOT", dotnetRoot);
}
this.buildAgent.addPath(toolPath);
return toolPath;
}
private async queryLatestMatch(toolName: string, versionSpec: string, includePrerelease: boolean): Promise<string> {
this.buildAgent.debug(`querying tool versions for ${toolName}${versionSpec ? `@${versionSpec}` : ""} ${includePrerelease ? "including pre-releases" : ""}`);
const downloadPath = `https://api-v2v3search-0.nuget.org/query?q=${encodeURIComponent(toolName.toLowerCase())}&prerelease=${includePrerelease ? "true" : "false"}&semVerLevel=2.0.0`;
const res = await this.httpClient.get(downloadPath);
if (!res || res.message.statusCode !== 200) {
return null;
}
const body: string = await res.readBody();
const data = JSON.parse(body).data;
const versions = (data[0].versions as { version: string }[]).map((x) => x.version);
if (!versions || !versions.length) {
return null;
}
this.buildAgent.debug(`got versions: ${versions.join(", ")}`);
return this.versionManager.evaluateVersions(versions, versionSpec);
}
private async acquireTool(toolName: string, version: string): Promise<string> {
const tempDirectory = await this.buildAgent.createTempDir();
let args = ["tool", "install", toolName, "--tool-path", tempDirectory];
if (version) {
version = this.versionManager.cleanVersion(version);
args = args.concat(["--version", version]);
}
const result = await this.run(args);
const status = result.code === 0 ? "success" : "failure";
const message = result.code === 0 ? result.stdout : result.stderr;
this.buildAgent.debug(`tool install result: ${status} ${message}`);
if (result.code) {
throw new Error("Error installing tool");
}
return await this.buildAgent.cacheDir(tempDirectory, toolName, version);
}
}
@injectable()
class VersionManager implements IVersionManager {
private buildAgent: IBuildAgent;
constructor(
@inject(TYPES.IBuildAgent) buildAgent: IBuildAgent,
) {
this.buildAgent = buildAgent;
}
public isExplicitVersion(versionSpec: string): boolean {
const c = semver.clean(versionSpec);
this.buildAgent.debug("isExplicit: " + c);
const valid = semver.valid(c) != null;
this.buildAgent.debug("explicit? " + valid);
return valid;
}
public evaluateVersions(versions: string[], versionSpec: string): string {
let version: string;
this.buildAgent.debug("evaluating " + versions.length + " versions");
versions = versions.sort(cmp);
for (let i = versions.length - 1; i >= 0; i--) {
const potential: string = versions[i];
const satisfied: boolean = semver.satisfies(potential, versionSpec);
if (satisfied) {
version = potential;
break;
}
}
if (version) {
this.buildAgent.debug("matched: " + version);
} else {
this.buildAgent.debug("match not found");
}
return version;
}
public cleanVersion(version: string): string {
this.buildAgent.debug("cleaning: " + version);
return semver.clean(version);
}
}
container.bind<IVersionManager>(TYPES.IVersionManager).to(VersionManager);
container.bind<IBuildAgent>(TYPES.IBuildAgent).to(BuildAgent);
container.bind<IDotnetTool>(TYPES.IDotnetTool).to(DotnetTool);
export { container };
}

146
src/core/dotnet-tool.ts Normal file
View File

@@ -0,0 +1,146 @@
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import * as http from "typed-rest-client/HttpClient";
import { inject, injectable } from "inversify";
import { TYPES, IExecResult, IBuildAgent } from "./common";
import { IVersionManager } from "./versionManager";
export interface IDotnetTool {
run(args: string[]): Promise<IExecResult>;
toolInstall(toolName: string, versionSpec: string, checkLatest: boolean, includePre: boolean): Promise<string>;
}
@injectable()
export class DotnetTool implements IDotnetTool {
private buildAgent: IBuildAgent;
private versionManager: IVersionManager;
private httpClient: http.HttpClient;
constructor(
@inject(TYPES.IBuildAgent) buildAgent: IBuildAgent,
@inject(TYPES.IVersionManager) versionManager: IVersionManager,
) {
this.buildAgent = buildAgent;
this.versionManager = versionManager;
this.httpClient = new http.HttpClient("dotnet");
}
public run(args: string[]): Promise<IExecResult> {
return this.buildAgent.exec("dotnet", args);
}
public async toolInstall(toolName: string, versionSpec: string, checkLatest: boolean, includePre: boolean)
: Promise<string> {
console.log("");
console.log("--------------------------");
console.log(`Installing ${toolName} version ` + versionSpec);
console.log("--------------------------");
if (this.versionManager.isExplicitVersion(versionSpec)) {
checkLatest = false; // check latest doesn't make sense when explicit version
}
let toolPath: string;
if (!checkLatest) {
//
// Let's try and resolve the version spec locally first
//
toolPath = this.buildAgent.find(toolName, versionSpec);
}
if (!toolPath) {
let version: string;
if (this.versionManager.isExplicitVersion(versionSpec)) {
//
// Explicit version was specified. No need to query for list of versions.
//
version = versionSpec;
} else {
//
// Let's query and resolve the latest version for the versionSpec.
// If the version is an explicit version (1.1.1 or v1.1.1) then no need to query.
// If your tool doesn't offer a mechanism to query,
// then it can only support exact version inputs.
//
version = await this.queryLatestMatch(toolName, versionSpec, includePre);
if (!version) {
throw new Error(`Unable to find ${toolName} version '${versionSpec}'.`);
}
//
// Check the cache for the resolved version.
//
toolPath = this.buildAgent.find(toolName, version);
}
if (!toolPath) {
//
// Download, extract, cache
//
toolPath = await this.acquireTool(toolName, version);
}
}
//
// Prepend the tools path. This prepends the PATH for the current process and
// instructs the agent to prepend for each task that follows.
//
this.buildAgent.debug(`toolPath: ${toolPath}`);
if (os.platform() !== "win32") {
const dotnetRoot = path.dirname(fs.readlinkSync(await this.buildAgent.which("dotnet")));
this.buildAgent.exportVariable("DOTNET_ROOT", dotnetRoot);
}
this.buildAgent.addPath(toolPath);
return toolPath;
}
private async queryLatestMatch(toolName: string, versionSpec: string, includePrerelease: boolean): Promise<string> {
this.buildAgent.debug(`querying tool versions for ${toolName}${versionSpec ? `@${versionSpec}` : ""} ${includePrerelease ? "including pre-releases" : ""}`);
const downloadPath = `https://api-v2v3search-0.nuget.org/query?q=${encodeURIComponent(toolName.toLowerCase())}&prerelease=${includePrerelease ? "true" : "false"}&semVerLevel=2.0.0`;
const res = await this.httpClient.get(downloadPath);
if (!res || res.message.statusCode !== 200) {
return null;
}
const body: string = await res.readBody();
const data = JSON.parse(body).data;
const versions = (data[0].versions as { version: string }[]).map((x) => x.version);
if (!versions || !versions.length) {
return null;
}
this.buildAgent.debug(`got versions: ${versions.join(", ")}`);
return this.versionManager.evaluateVersions(versions, versionSpec);
}
private async acquireTool(toolName: string, version: string): Promise<string> {
const tempDirectory = await this.buildAgent.createTempDir();
let args = ["tool", "install", toolName, "--tool-path", tempDirectory];
if (version) {
version = this.versionManager.cleanVersion(version);
args = args.concat(["--version", version]);
}
const result = await this.run(args);
const status = result.code === 0 ? "success" : "failure";
const message = result.code === 0 ? result.stdout : result.stderr;
this.buildAgent.debug(`tool install result: ${status} ${message}`);
if (result.code) {
throw new Error("Error installing tool");
}
return await this.buildAgent.cacheDir(tempDirectory, toolName, version);
}
}

13
src/core/ioc.ts Normal file
View File

@@ -0,0 +1,13 @@
import { Container } from "inversify";
import { IVersionManager, VersionManager } from "./versionManager";
import { TYPES, IBuildAgent } from "./common";
import { BuildAgent } from "../agent/mock/build-agent";
import { IDotnetTool, DotnetTool } from "./dotnet-tool";
const container = new Container();
container.bind<IVersionManager>(TYPES.IVersionManager).to(VersionManager);
container.bind<IBuildAgent>(TYPES.IBuildAgent).to(BuildAgent);
container.bind<IDotnetTool>(TYPES.IDotnetTool).to(DotnetTool);
export default container;

View File

@@ -0,0 +1,59 @@
import cmp from "semver-compare";
import * as semver from "semver";
import { injectable, inject } from "inversify";
import { IBuildAgent, TYPES } from "./common";
export interface IVersionManager {
isExplicitVersion(versionSpec: string): boolean;
evaluateVersions(versions: string[], versionSpec: string): string;
cleanVersion(version: string): string;
}
@injectable()
export class VersionManager implements IVersionManager {
private buildAgent: IBuildAgent;
constructor(
@inject(TYPES.IBuildAgent) buildAgent: IBuildAgent,
) {
this.buildAgent = buildAgent;
}
public isExplicitVersion(versionSpec: string): boolean {
const c = semver.clean(versionSpec);
this.buildAgent.debug("isExplicit: " + c);
const valid = semver.valid(c) != null;
this.buildAgent.debug("explicit? " + valid);
return valid;
}
public evaluateVersions(versions: string[], versionSpec: string): string {
let version: string;
this.buildAgent.debug("evaluating " + versions.length + " versions");
versions = versions.sort(cmp);
for (let i = versions.length - 1; i >= 0; i--) {
const potential: string = versions[i];
const satisfied: boolean = semver.satisfies(potential, versionSpec);
if (satisfied) {
version = potential;
break;
}
}
if (version) {
this.buildAgent.debug("matched: " + version);
} else {
this.buildAgent.debug("match not found");
}
return version;
}
public cleanVersion(version: string): string {
this.buildAgent.debug("cleaning: " + version);
return semver.clean(version);
}
}

View File

@@ -1,5 +1,7 @@
import { container, IBuildAgent, TYPES, SetupOptions } from "../../core/common";
import { IGitReleaseManagerTool, GitReleaseManagerTool } from "../../core/gitreleasemanager";
import { IBuildAgent, TYPES, SetupOptions } from "../../core/common";
import { IGitReleaseManagerTool, GitReleaseManagerTool } from "../../tools/gitreleasemanager";
import container from "../../core/ioc";
container.bind<IGitReleaseManagerTool>(TYPES.IGitReleaseManagerTool).to(GitReleaseManagerTool);

View File

@@ -1,5 +1,7 @@
import { container, IBuildAgent, TYPES, SetupOptions } from "../../core/common";
import { IGitVersionTool, IGitVersionArguments, IGitVersion, GitVersionTool } from "../../core/gitversion";
import { IBuildAgent, TYPES, SetupOptions } from "../../core/common";
import { IGitVersionTool, IGitVersionArguments, IGitVersion, GitVersionTool } from "../../tools/gitversion";
import container from "../../core/ioc";
container.bind<IGitVersionTool>(TYPES.IGitVersionTool).to(GitVersionTool);

View File

@@ -1,5 +1,6 @@
import { IDotnetTool, IBuildAgent, TYPES } from "./common";
import { TYPES, IBuildAgent } from "../core/common";
import { injectable, inject } from "inversify";
import { IDotnetTool } from "../core/dotnet-tool";
export interface IGitReleaseManagerTool {
install(versionSpec: string, includePrerelease: boolean): Promise<void>;

View File

@@ -1,6 +1,7 @@
import { IBuildAgent, IDotnetTool, IExecResult, TYPES } from "./common";
import { injectable, inject } from "inversify";
import path = require("path");
import { injectable, inject } from "inversify";
import { IExecResult, IBuildAgent, TYPES } from "../core/common";
import { IDotnetTool } from "../core/dotnet-tool";
export interface IGitVersionArguments {
targetPath: string;