refactore, using inversify for dependency injection

This commit is contained in:
Artur Stolear
2019-11-22 13:31:52 +02:00
parent 4704c3ce45
commit 01a1780abe
46 changed files with 24858 additions and 5975 deletions

9
.gitignore vendored
View File

@@ -1,10 +1,5 @@
node_modules/
*.js
.taskkey
dist/azure/task/
!dist/azure/task/icon.png
!dist/azure/task/task.json
*.vsix
*.js
!dist/**/*.js

26
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,26 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"preLaunchTask": "tsc: build - src/azure/tsconfig.json",
"name": "Run Azure Pipeline Task",
"program": "${workspaceFolder}/src/azure/main.ts",
"outFiles": ["${workspaceFolder}/**/*.js"]
},
{
"type": "node",
"request": "launch",
"preLaunchTask": "tsc: build - src/github/tsconfig.json",
"name": "Run GitHub Actions",
"program": "${workspaceFolder}/src/github/main.ts",
"outFiles": ["${workspaceFolder}/**/*.js"]
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

16558
dist/azure/index.js vendored Normal file

File diff suppressed because one or more lines are too long

14
dist/azure/lib.json vendored Normal file
View File

@@ -0,0 +1,14 @@
{
"messages": {
"TOOL_LIB_CachingTool": "Caching tool: %s %s %s",
"_TOOL_LIB_CachingTool.comment": "This informational log message indicates that a tool, that was just downloaded, is being copied into the cache directory. %s %s %s represent the tool name, version information, and processor architecture.",
"TOOL_LIB_Downloading": "Downloading: %s",
"_TOOL_LIB_Downloading.comment": "This information log message indicates that a file is being downloaded. %s represents the URL being downloaded.",
"TOOL_LIB_ExtractingArchive": "Extracting archive",
"_TOOL_LIB_ExtractingArchive.comment": "This informational log message indicates that an archive file is being extracted. For example, a .zip file, .7z file, or .tar.gz file.",
"TOOL_LIB_FoundInCache": "Found tool in cache: %s %s %s",
"_TOOL_LIB_FoundInCache.comment": "This informational log message indicates that the request tool is already cached, and does not need to be downloaded again. %s %s %s represent the tool name, version information, and processor architecture.",
"TOOL_LIB_PrependPath": "Prepending PATH environment variable with directory: %s",
"_TOOL_LIB_PrependPath.comment": "This informational log message indicates that a directory is being prepended to the front of the PATH environment variable. The directories specified by the PATH environment variable are used to resolve the location of command line tools."
}
}

36
dist/azure/lib1.json vendored Normal file
View File

@@ -0,0 +1,36 @@
{
"messages": {
"LIB_UnhandledEx": "Unhandled: %s",
"LIB_FailOnCode": "Failure return code: %d",
"LIB_MkdirFailed": "Unable to create directory '%s'. %s",
"LIB_MkdirFailedFileExists": "Unable to create directory '%s'. Conflicting file exists: '%s'",
"LIB_MkdirFailedInvalidDriveRoot": "Unable to create directory '%s'. Root directory does not exist: '%s'",
"LIB_MkdirFailedInvalidShare": "Unable to create directory '%s'. Unable to verify the directory exists: '%s'. If directory is a file share, please verify the share name is correct, the share is online, and the current process has permission to access the share.",
"LIB_MultilineSecret": "Secrets cannot contain multiple lines",
"LIB_ProcessError": "There was an error when attempting to execute the process '%s'. This may indicate the process failed to start. Error: %s",
"LIB_ProcessExitCode": "The process '%s' failed with exit code %s",
"LIB_ProcessStderr": "The process '%s' failed because one or more lines were written to the STDERR stream",
"LIB_ReturnCode": "Return code: %d",
"LIB_ResourceFileNotExist": "Resource file doesn\\'t exist: %s",
"LIB_ResourceFileAlreadySet": "Resource file has already set to: %s",
"LIB_ResourceFileNotSet": "Resource file haven\\'t set, can\\'t find loc string for key: %s",
"LIB_StdioNotClosed": "The STDIO streams did not close within %s seconds of the exit event from process '%s'. This may indicate a child process inherited the STDIO streams and has not yet exited.",
"LIB_WhichNotFound_Linux": "Unable to locate executable file: '%s'. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable.",
"LIB_WhichNotFound_Win": "Unable to locate executable file: '%s'. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also verify the file has a valid extension for an executable file.",
"LIB_LocStringNotFound": "Can\\'t find loc string for key: %s",
"LIB_ParameterIsRequired": "%s not supplied",
"LIB_InputRequired": "Input required: %s",
"LIB_InvalidPattern": "Invalid pattern: '%s'",
"LIB_EndpointNotExist": "Endpoint not present: %s",
"LIB_EndpointDataNotExist": "Endpoint data parameter %s not present: %s",
"LIB_EndpointAuthNotExist": "Endpoint auth data not present: %s",
"LIB_InvalidEndpointAuth": "Invalid endpoint auth: %s",
"LIB_InvalidSecureFilesInput": "Invalid secure file input: %s",
"LIB_PathNotFound": "Not found %s: %s",
"LIB_PathHasNullByte": "Path cannot contain null bytes",
"LIB_OperationFailed": "Failed %s: %s",
"LIB_UseFirstGlobMatch": "Multiple workspace matches. using first.",
"LIB_MergeTestResultNotSupported": "Merging test results from multiple files to one test run is not supported on this version of build agent for OSX/Linux, each test result file will be published as a separate test run in VSO/TFS.",
"LIB_PlatformNotSupported": "Platform not supported: %s"
}
}

View File

@@ -1,6 +0,0 @@
Versioning when using Git, solved. GitVersion looks at your Git history and works out the [semantic version](http://semver.org) of the commit being built.
It works with most branching strategies but has been designed mainly around GitFlow and GitHubFlow (pull request workflow). The calculated version numbers can then be accessed through variables such as `$(GitVersion.NuGetVersion)` and `$(GitVersion.SemVer)`. It is also very configurable to allow it to work with most release workflows!
![Build Task](https://raw.githubusercontent.com/GitTools/GitVersion/master/src/GitVersionVsixTask/images/build-task.png)
![Builds](https://raw.githubusercontent.com/GitTools/GitVersion/master/src/GitVersionVsixTask/images/builds.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -1,106 +0,0 @@
{
"id": "dd065e3b-6aef-46af-845c-520195836b35",
"name": "UseGitVersion",
"friendlyName": "GitVersion Task$titleSuffix$",
"description": "Easy Semantic Versioning (http://semver.org) for projects using Git",
"author": "GitVersion Contributors",
"helpMarkDown": "See the [documentation](http://gitversion.readthedocs.org/en/latest/) for help",
"category": "Build",
"demands": [],
"version": {
"Major": "5",
"Minor": "1",
"Patch": "2"
},
"minimumAgentVersion": "2.115.0",
"groups": [
{
"name": "gitversionDetails",
"displayName": "GitVersion details",
"isExpanded": true
},
{
"name": "additional",
"displayName": "Additional Options",
"isExpanded": false
}
],
"instanceNameFormat": "GitVersion",
"inputs": [
{
"name": "versionSpec",
"type": "string",
"label": "The version spec",
"defaultValue": "",
"required": true,
"helpMarkDown": "Required version in the form of 5.x or exact version like 5.0.0"
},
{
"name": "includePrerelease",
"type": "boolean",
"label": "Include pre-release versions",
"defaultValue": "false",
"required": false,
"helpMarkDown": "Include pre-release versions when matching a version"
},
{
"name": "targetPath",
"type": "string",
"label": "Working directory path",
"defaultValue": "",
"required": false,
"helpMarkDown": "Optionally supply the path to the working directory",
"groupName": "gitversionDetails"
},
{
"name": "useConfigFile",
"type": "boolean",
"label": "Specify Configuration file",
"defaultValue": "false",
"required": false,
"helpMarkDown": "Whether to use a custom configuration file"
},
{
"name": "configFilePath",
"type": "filePath",
"label": "Configuration file",
"defaultValue": "",
"required": false,
"helpMarkDown": "Optional path to config file (defaults to GitVersion.yml)",
"visibleRule": "useConfigFile = true"
},
{
"name": "updateAssemblyInfo",
"type": "boolean",
"label": "Update AssemblyInfo files",
"defaultValue": "false",
"required": false,
"helpMarkDown": "Whether to update versions in the AssemblyInfo files"
},
{
"name": "updateAssemblyInfoFilename",
"type": "string",
"label": "Update Assembly File",
"defaultValue": "",
"required": false,
"helpMarkDown": "Update versions in specified file",
"visibleRule": "updateAssemblyInfo = true"
},
{
"name": "additionalArguments",
"type": "string",
"label": "Additional GitVersion arguments",
"defaultValue": "",
"required": false,
"helpMarkDown": "Additional arguments to send to GitVersion",
"groupName": "additional"
}
],
"execution": {
"Node": {
"target": "GitVersion.js",
"argumentFormat": "",
"workingDirectory": "."
}
}
}

View File

@@ -1,71 +0,0 @@
{
"manifestVersion": 1,
"id": "usegitversion$idSuffix$",
"name": "UseGitVersion$titleSuffix$",
"publisher": "gittools",
"public": true,
"author": "GitVersion Contributors",
"version": "$version$",
"description": "Build task for easy semantic versioning for projects using Git.",
"targets": [{
"id": "Microsoft.VisualStudio.Services"
}],
"files": [
{ "path": "task" }
],
"categories": [
"Azure Pipelines"
],
"galleryFlags": [
"$visibility$"
],
"icons": {
"default": "images/extension-icon.png"
},
"tags": [
"semver",
"git",
"versioning",
"gitflow",
"githubflow"
],
"links": {
"learn": {
"uri": "http://gitversion.readthedocs.org/en/stable"
},
"getstarted": {
"uri": "http://gitversion.readthedocs.org/en/stable/build-server-support/build-server/tfs-build-vnext/#running-inside-tfs"
},
"license": {
"uri": "https://github.com/GitTools/GitVersion/blob/master/LICENSE"
},
"repository": {
"uri": "https://github.com/GitTools/GitVersion"
},
"support": {
"uri": "https://github.com/GitTools/GitVersion/issues"
}
},
"screenshots": [{
"path": "images/builds.png"
},
{
"path": "images/build-task.png"
}
],
"content": {
"details": {
"path": "overview.md"
}
},
"contributions": [{
"id": "gitversion-task",
"type": "ms.vss-distributed-task.task",
"targets": [
"ms.vss-distributed-task.tasks"
],
"properties": {
"name": "GitVersionTask"
}
}]
}

9390
dist/github/index.js vendored

File diff suppressed because one or more lines are too long

3005
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,49 +1,37 @@
{
"name": "use-gitversion",
"version": "1.0.0",
"description": "Github Action that installs and uses the GitVersion tool",
"main": "index.js",
"scripts": {
"build-github": "tsc -p src/github",
"build-azure": "tsc -p src/azure && tsc -p src/azure/tests",
"test-azure": "mocha src/azure/tests/test-suite.js",
"package-github": "ncc build src/github/GitVersion.js -o dist/github",
"package-azure": "ncc build src/github/GitVersion.js -o dist/azure/task && tfx extension create --root dist/azure --manifest-globs vss-extension.json --output-path dist/azure"
},
"description": "Easy Semantic Versioning (http://semver.org) for projects using Git",
"repository": {
"type": "git",
"url": "git+https://github.com/GitTools/use-gitversion.git"
"url": "https://github.com/GitTools/use-gitversion.git"
},
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "GitTools",
"contributors": [
"GitTools contributors"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/GitTools/use-gitversion/issues"
},
"homepage": "https://github.com/GitTools/use-gitversion#readme",
"dependencies": {
"@actions/core": "^1.2.0",
"@actions/exec": "^1.0.1",
"@actions/io": "^1.0.1",
"@actions/tool-cache": "^1.1.2",
"azure-pipelines-task-lib": "2.8.0",
"azure-pipelines-task-lib": "2.9.3",
"azure-pipelines-tool-lib": "0.12.0",
"semver": "^6.3.0",
"semver-compare": "^1.0.0",
"inversify": "^5.0.1",
"typed-rest-client": "1.5.0",
"uuid": "^3.3.3"
},
"devDependencies": {
"@types/mocha": "^5.2.7",
"@types/node": "^12.12.7",
"@types/node": "12.12.11",
"@types/q": "1.5.2",
"@types/semver": "^6.2.0",
"@types/uuid": "^3.4.6",
"@zeit/ncc": "^0.20.5",
"copy-node-modules": "^1.1.1",
"mocha": "^6.2.1",
"mocha-xunit-reporter": "^1.1.0",
"tfx-cli": "0.6.3",
"typescript": "^3.7.2"
"reflect-metadata": "^0.1.13",
"tslint": "^5.20.1",
"typescript": "^3.7.2",
"@zeit/ncc": "^0.20.5"
}
}

View File

@@ -1,24 +1,38 @@
export interface IProxy {
find: (toolName: string, versionSpec: string, arch?: string) => string;
debug: (message: string) => void;
exportVariable: (name: string, val: string) => void;
addPath: (inputPath: string) => void;
which: (tool: string, check?: boolean) => Promise<string>;
cacheDir: (sourceDir: string, tool: string, version: string, arch?: string) => Promise<string>;
export interface IVersionManager {
isExplicitVersion: (versionSpec: string) => boolean;
evaluateVersions: (versions: string[], versionSpec: string) => string;
cleanVersion: (version: string) => string;
createTempDir: () => Promise<string>;
executeDotNet: (args: string[]) => Promise<IExecSyncResult>;
};
}
export interface IExecSyncResult {
/** standard output */
export interface IBuildAgent {
find: (toolName: string, versionSpec: string, arch?: string) => string;
cacheDir: (sourceDir: string, tool: string, version: string, arch?: string) => Promise<string>;
createTempDir: () => Promise<string>;
debug: (message: string) => void;
setFailed: (message: string, done?: boolean) => void;
setSucceeded: (message: string, done?: boolean) => void;
exportVariable: (name: string, val: string) => void;
addPath: (inputPath: string) => void;
which: (tool: string, check?: boolean) => Promise<string>;
exec: (exec: string, args: string[]) => Promise<IExecResult>;
}
export interface IDotnetTool {
run: (args: string[]) => Promise<IExecResult>;
toolInstall: (toolName: string, versionSpec: string, checkLatest: boolean, includePre: boolean) => Promise<string>;
}
export class Argument {
constructor(public readonly name: string, public readonly value: string) {
}
}
export interface IGitVersionTool {
install: (versionSpec: string, includePrerelease: boolean) => Promise<void>;
run: (...params: Argument[]) => Promise<IExecResult>;
}
export interface IExecResult {
stdout: string;
/** error output */
stderr: string;
/** return code */
code: number;
/** Error on failure */
error: Error;
}
}

View File

@@ -1,129 +0,0 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as httpm from 'typed-rest-client/HttpClient';
import { IProxy } from './Interfaces';
export class ToolInstaller {
constructor(public utils: IProxy) {
this.httpClient = new httpm.HttpClient("ToolInstaller");
}
public async downloadAndInstall(toolName: string, versionSpec: string, checkLatest: boolean, includePrerelease: boolean): Promise<string> {
console.log('');
console.log('--------------------------');
console.log(`Installing ${toolName} version ` + versionSpec);
console.log('--------------------------');
if (this.utils.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.utils.find(toolName, versionSpec);
}
if (!toolPath) {
let version: string;
if (this.utils.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, includePrerelease);
if (!version) {
throw new Error(`Unable to find ${toolName} version '${versionSpec}'.`);
}
//
// Check the cache for the resolved version.
//
toolPath = this.utils.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.utils.debug(`toolPath: ${toolPath}`);
if (os.platform() != 'win32') {
let dotnetRoot = path.dirname(fs.readlinkSync(await this.utils.which("dotnet")));
this.utils.exportVariable('DOTNET_ROOT', dotnetRoot);
}
this.utils.addPath(toolPath);
return toolPath;
}
private async queryLatestMatch(toolName: string, versionSpec: string, includePrerelease: boolean): Promise<string> {
this.utils.debug(`querying tool versions for ${toolName}${versionSpec ? `@${versionSpec}` : ""} ${includePrerelease ? "including pre-releases" : ""}`);
var downloadPath = `https://api-v2v3search-0.nuget.org/query?q=${encodeURIComponent(toolName.toLowerCase())}&prerelease=${includePrerelease ? "true" : "false"}&semVerLevel=2.0.0`;
var res = await this.httpClient.get(downloadPath);
if (!res || res.message.statusCode != 200)
{
return null;
}
let body: string = await res.readBody();
var data = JSON.parse(body).data;
const versions = (<Array<{ version: string }>>data[0].versions).map(x => x.version);
if (!versions || !versions.length)
{
return null;
}
this.utils.debug(`got versions: ${versions.join(", ")}`);
return this.utils.evaluateVersions(versions, versionSpec);
}
private async acquireTool(toolName: string, version: string): Promise<string> {
let tempDirectory = await this.utils.createTempDir();
let args = ["tool", "install", toolName, "--tool-path", tempDirectory];
if (version) {
version = this.utils.cleanVersion(version);
args = args.concat(["--version", version]);
}
let result = await this.utils.executeDotNet(args);
let status = result.code === 0 ? "success" : "failure";
let message = result.code === 0 ? result.stdout : result.stderr;
this.utils.debug(`tool install result: ${status} ${message}`);
if (result.code) {
throw new Error("Error installing tool");
}
return await this.utils.cacheDir(tempDirectory, toolName, version);
}
private httpClient: httpm.HttpClient;
}

View File

@@ -1,131 +0,0 @@
import * as path from 'path';
import * as tl from 'azure-pipelines-task-lib/task';
import { ToolInstaller } from '../ToolInstaller';
import { Proxy as utils } from './Proxy';
function isValidInputFile(input: string, file: string) {
return tl.filePathSupplied(input) && fileExists(file);
}
function fileExists(file: string) {
return tl.exist(file) && tl.stats(file).isFile();
}
function directoryExists(file: string) {
return tl.exist(file) && tl.stats(file).isDirectory();
}
export class GitVersionTask {
versionSpec: string;
includePrerelease: boolean;
targetPath: string;
useConfigFile: boolean;
configFilePath: string;
updateAssemblyInfo: boolean;
updateAssemblyInfoFilename: string;
additionalArguments: string;
sourcesDirectory: string;
constructor() {
this.versionSpec = tl.getInput('versionSpec', true);
this.includePrerelease = tl.getBoolInput('includePrerelease');
this.targetPath = tl.getInput('targetPath');
this.useConfigFile = tl.getBoolInput('useConfigFile');
this.configFilePath = tl.getInput('configFilePath');
this.updateAssemblyInfo = tl.getBoolInput('updateAssemblyInfo');
this.updateAssemblyInfoFilename = tl.getInput('updateAssemblyInfoFilename');
this.additionalArguments = tl.getInput('additionalArguments');
this.sourcesDirectory = tl.getVariable('Build.SourcesDirectory').replace(/\\/g, '/');
}
public async execute() {
try {
let toolPath = await this.installTool(this.versionSpec, this.includePrerelease);
let workingDirectory = this.getWorkingDirectory(this.targetPath);
let args = this.getArguments(workingDirectory);
let exe = tl.tool('dotnet-gitversion');
exe.arg(args);
if (this.additionalArguments) {
exe.line(this.additionalArguments);
}
const result = await exe.exec();
if (result) {
tl.setResult(tl.TaskResult.Failed, "An error occured during GitVersion execution");
} else {
tl.setResult(tl.TaskResult.Succeeded, "GitVersion executed successfully");
}
}
catch (err) {
tl.debug(err.stack);
tl.setResult(tl.TaskResult.Failed, err, true);
}
}
private getArguments(workingDirectory: string) {
let args = [
workingDirectory,
"/output",
"buildserver",
"/nofetch"
];
if (this.useConfigFile) {
if (isValidInputFile('configFilePath', this.configFilePath)) {
args.push("/config", this.configFilePath);
}
else {
throw new Error('GitVersion configuration file not found at ' + this.configFilePath);
}
}
if (this.updateAssemblyInfo) {
args.push("/updateassemblyinfo");
if (isValidInputFile('updateAssemblyInfoFilename', this.updateAssemblyInfoFilename)) {
args.push(this.updateAssemblyInfoFilename);
}
else {
throw new Error('AssemblyInfoFilename file not found at ' + this.updateAssemblyInfoFilename);
}
}
return args;
}
private async installTool(version: string, includePrerelease: boolean): Promise<string> {
let installTool = process.env["INSTALL_TOOL"];
if (installTool === null || installTool === undefined || installTool.toUpperCase() == "TRUE") {
return await new ToolInstaller(utils).downloadAndInstall("GitVersion.Tool", version, false, includePrerelease);
}
}
private getWorkingDirectory(targetPath: string) {
let workDir;
if (!targetPath) {
workDir = this.sourcesDirectory;
} else {
if (directoryExists(targetPath)) {
workDir = path.join(this.sourcesDirectory, targetPath);
}
else {
throw new Error('Directory not found at ' + targetPath);
}
}
return workDir.replace(/\\/g, '/');
}
}
var task = new GitVersionTask();
task.execute().catch((reason) => tl.setResult(tl.TaskResult.Failed, reason));

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -1,106 +0,0 @@
{
"id": "dd065e3b-6aef-46af-845c-520195836b35",
"name": "UseGitVersion",
"friendlyName": "GitVersion Task (Preview)",
"description": "Easy Semantic Versioning (http://semver.org) for projects using Git",
"author": "GitVersion Contributors",
"helpMarkDown": "See the [documentation](http://gitversion.readthedocs.org/en/latest/) for help",
"category": "Build",
"demands": [],
"version": {
"Major": "5",
"Minor": "1",
"Patch": "3"
},
"minimumAgentVersion": "2.115.0",
"groups": [
{
"name": "gitversionDetails",
"displayName": "GitVersion details",
"isExpanded": true
},
{
"name": "additional",
"displayName": "Additional Options",
"isExpanded": false
}
],
"instanceNameFormat": "GitVersion",
"inputs": [
{
"name": "versionSpec",
"type": "string",
"label": "The version spec",
"defaultValue": "",
"required": true,
"helpMarkDown": "Required version in the form of 5.x or exact version like 5.0.0"
},
{
"name": "includePrerelease",
"type": "boolean",
"label": "Include pre-release versions",
"defaultValue": "false",
"required": false,
"helpMarkDown": "Include pre-release versions when matching a version"
},
{
"name": "targetPath",
"type": "string",
"label": "Working directory path",
"defaultValue": "",
"required": false,
"helpMarkDown": "Optionally supply the path to the working directory",
"groupName": "gitversionDetails"
},
{
"name": "useConfigFile",
"type": "boolean",
"label": "Specify Configuration file",
"defaultValue": "false",
"required": false,
"helpMarkDown": "Whether to use a custom configuration file"
},
{
"name": "configFilePath",
"type": "filePath",
"label": "Configuration file",
"defaultValue": "",
"required": false,
"helpMarkDown": "Optional path to config file (defaults to GitVersion.yml)",
"visibleRule": "useConfigFile = true"
},
{
"name": "updateAssemblyInfo",
"type": "boolean",
"label": "Update AssemblyInfo files",
"defaultValue": "false",
"required": false,
"helpMarkDown": "Whether to update versions in the AssemblyInfo files"
},
{
"name": "updateAssemblyInfoFilename",
"type": "string",
"label": "Update Assembly File",
"defaultValue": "",
"required": false,
"helpMarkDown": "Update versions in specified file",
"visibleRule": "updateAssemblyInfo = true"
},
{
"name": "additionalArguments",
"type": "string",
"label": "Additional GitVersion arguments",
"defaultValue": "",
"required": false,
"helpMarkDown": "Additional arguments to send to GitVersion",
"groupName": "additional"
}
],
"execution": {
"Node": {
"target": "GitVersion.js",
"argumentFormat": "",
"workingDirectory": "."
}
}
}

View File

@@ -1,23 +0,0 @@
import { createTempDir, executeDotNet } from './ToolLib';
import { findLocalTool, prependPath, cacheDir, isExplicitVersion, evaluateVersions, cleanVersion } from 'azure-pipelines-tool-lib/tool';
import { debug, setVariable, which } from 'azure-pipelines-task-lib/task';
function _which(tool: string, check?: boolean) : Promise<string> {
return Promise.resolve(which(tool, check));
}
import { IProxy } from '../Interfaces';
export const Proxy: IProxy = {
find: findLocalTool,
debug: debug,
exportVariable: setVariable,
addPath: prependPath,
which: _which,
cacheDir: cacheDir,
isExplicitVersion: isExplicitVersion,
evaluateVersions: evaluateVersions,
cleanVersion: cleanVersion,
createTempDir: createTempDir,
executeDotNet: executeDotNet
}

View File

@@ -1,30 +0,0 @@
import * as taskLib from 'azure-pipelines-task-lib/task';
export interface IExecSyncResult {
/** standard output */
stdout: string;
/** error output */
stderr: string;
/** return code */
code: number;
/** Error on failure */
error: Error;
}
export async function createTempDir(): Promise<string> {
let tempDir = taskLib.getVariable('Agent.TempDirectory');
return Promise.resolve(tempDir);
}
export async function executeDotNet(args: string[]): Promise<IExecSyncResult> {
let tr = taskLib.tool("dotnet");
tr.arg(args);
var result = tr.execSync();
return {
code: result.code,
stdout: result.stdout,
stderr: result.stderr,
error: result.error
}
}

62
src/azure/build-agent.ts Normal file
View File

@@ -0,0 +1,62 @@
import { injectable } from "inversify";
import * as taskLib from "azure-pipelines-task-lib/task";
import * as toolLib from "azure-pipelines-tool-lib/tool";
import { IBuildAgent, IExecResult } from "../interfaces";
@injectable()
class BuildAgent implements IBuildAgent {
public find(toolName: string, versionSpec: string, arch?: string): string {
return toolLib.findLocalTool(toolName, versionSpec, arch);
}
public cacheDir(sourceDir: string, tool: string, version: string, arch?: string): Promise<string> {
return toolLib.cacheDir(sourceDir, tool, version, arch);
}
public createTempDir(): Promise<string> {
return Promise.resolve(taskLib.getVariable("Agent.TempDirectory"));
}
public debug(message: string): void {
taskLib.debug(message);
}
public setFailed(message: string, done?: boolean): void {
taskLib.setResult(taskLib.TaskResult.Failed, message, done);
}
public setSucceeded(message: string, done?: boolean): void {
taskLib.setResult(taskLib.TaskResult.Succeeded, message, done);
}
public exportVariable(name: string, val: string): void {
taskLib.setVariable(name, val);
}
public addPath(inputPath: string): void {
toolLib.prependPath(inputPath);
}
public which(tool: string, check?: boolean): Promise<string> {
return Promise.resolve(taskLib.which(tool, check));
}
public exec(exec: string, args: string[]): Promise<IExecResult> {
const tr = taskLib.tool(exec);
tr.arg(args);
const result = tr.execSync();
return Promise.resolve({
code: result.code,
error: result.error,
stderr: result.stderr,
stdout: result.stdout,
});
}
}
export {
BuildAgent,
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

15
src/azure/ioc.ts Normal file
View File

@@ -0,0 +1,15 @@
import { Container } from "inversify";
import { DotnetTool, GitVersionTool, VersionManager } from "../common";
import { IBuildAgent, IDotnetTool, IGitVersionTool, IVersionManager } from "../interfaces";
import { TYPES } from "../types";
import { BuildAgent } from "./build-agent";
const ioc = new Container();
ioc.bind<IVersionManager>(TYPES.IVersionManager).to(VersionManager);
ioc.bind<IBuildAgent>(TYPES.IBuildAgent).to(BuildAgent);
ioc.bind<IDotnetTool>(TYPES.IDotnetTool).to(DotnetTool);
ioc.bind<IGitVersionTool>(TYPES.IGitVersionTool).to(GitVersionTool);
export { ioc };

24
src/azure/main.ts Normal file
View File

@@ -0,0 +1,24 @@
import "reflect-metadata";
import { IBuildAgent, IGitVersionTool } from "../interfaces";
import { TYPES } from "../types";
import { ioc } from "./ioc";
const gitVersionTool = ioc.get<IGitVersionTool>(TYPES.IGitVersionTool);
const buildAgent = ioc.get<IBuildAgent>(TYPES.IBuildAgent);
export async function run() {
try {
const version = "5.1.2";
const includePrerelease = false;
await gitVersionTool.install(version, includePrerelease);
buildAgent.setSucceeded("GitVersion executed successfully", true);
} catch (error) {
buildAgent.setFailed(error.message, true);
}
}
run();

View File

@@ -1,6 +0,0 @@
Versioning when using Git, solved. GitVersion looks at your Git history and works out the [semantic version](http://semver.org) of the commit being built.
It works with most branching strategies but has been designed mainly around GitFlow and GitHubFlow (pull request workflow). The calculated version numbers can then be accessed through variables such as `$(GitVersion.NuGetVersion)` and `$(GitVersion.SemVer)`. It is also very configurable to allow it to work with most release workflows!
![Build Task](https://raw.githubusercontent.com/GitTools/GitVersion/master/src/GitVersionVsixTask/images/build-task.png)
![Builds](https://raw.githubusercontent.com/GitTools/GitVersion/master/src/GitVersionVsixTask/images/builds.png)

View File

@@ -1,82 +0,0 @@
import * as path from 'path';
import * as ma from 'azure-pipelines-task-lib/mock-answer';
import * as tmrm from 'azure-pipelines-task-lib/mock-run';
import * as shared from './test-shared';
let taskDir = path.join(__dirname, '..');
let taskPath = path.join(taskDir, 'GitVersion.js');
let tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);
process.env['BUILD_SOURCESDIRECTORY'] = shared.SharedValues.BUILD_SOURCESDIRECTORY;
process.env["INSTALL_TOOL"] = 'false';
tmr.setInput('versionSpec', process.env[shared.TestEnvVars.versionSpec] || '5.x');
tmr.setInput('includePrerelease', process.env[shared.TestEnvVars.includePrerelease] || 'false');
tmr.setInput('targetPath', process.env[shared.TestEnvVars.targetPath] || '');
tmr.setInput('useConfigFile', process.env[shared.TestEnvVars.useConfigFile] || 'false');
tmr.setInput('configFilePath', process.env[shared.TestEnvVars.configFilePath] || '');
tmr.setInput('updateAssemblyInfo', process.env[shared.TestEnvVars.updateAssemblyInfo] || 'false');
tmr.setInput('updateAssemblyInfoFilename', process.env[shared.TestEnvVars.updateAssemblyInfoFilename] || '');
tmr.setInput('additionalArguments', process.env[shared.TestEnvVars.additionalArguments] || '');
console.log("Inputs have been set");
let a: ma.TaskLibAnswers = <ma.TaskLibAnswers>{
"which": { "dotnet-gitversion": "dotnet-gitversion" },
"checkPath": { "dotnet-gitversion": true },
"exec": {},
"exist": {
"src": true,
"customConfig.yml" : true,
"GlobalAssemblyInfo.cs" : true
},
"stats": {
"src" : {
"isDirectory" : true
},
"customConfig.yml" : {
"isFile": true
},
"GlobalAssemblyInfo.cs" : {
"isFile": true
}
}
};
a.exec["dotnet-gitversion " + shared.SharedValues.BUILD_SOURCESDIRECTORY + " /output buildserver /nofetch"] = {
"code": 0,
"stdout": "GitVersion run successfully with defaults",
"stderr": ""
};
a.exec["dotnet-gitversion " + shared.SharedValues.BUILD_SOURCESDIRECTORY + "/src /output buildserver /nofetch"] = {
"code": 0,
"stdout": "GitVersion run successfully with custom targetPath",
"stderr": ""
};
a.exec["dotnet-gitversion " + shared.SharedValues.BUILD_SOURCESDIRECTORY + " /output buildserver /nofetch /config customConfig.yml"] = {
"code": 0,
"stdout": "GitVersion run successfully with custom config",
"stderr": ""
};
a.exec["dotnet-gitversion " + shared.SharedValues.BUILD_SOURCESDIRECTORY + " /output buildserver /nofetch /updateassemblyinfo GlobalAssemblyInfo.cs"] = {
"code": 0,
"stdout": "GitVersion run successfully with custom assembly info",
"stderr": ""
};
var mt = require('azure-pipelines-task-lib/mock-task');
mt.assertAgent = (minimum: string) =>
{
};
tmr.registerMockExport('mt', mt);
tmr.setAnswers(a);
tmr.run();

View File

@@ -1,14 +0,0 @@
export let TestEnvVars = {
versionSpec: "__versionSpec__",
includePrerelease: "__includePrerelease__",
targetPath: "__targetPath__",
useConfigFile: "__useConfigFile__",
configFilePath: "__configFilePath__",
updateAssemblyInfo: "__updateAssemblyInfo__",
updateAssemblyInfoFilename: "__updateAssemblyInfoFilename__",
additionalArguments: "__additionalArguments__"
}
export let SharedValues = {
BUILD_SOURCESDIRECTORY: "/user/build"
}

View File

@@ -1,246 +0,0 @@
import * as path from 'path';
import * as assert from 'assert';
import * as ttm from 'azure-pipelines-task-lib/mock-test';
import * as shared from './test-shared';
function runValidations(validator: () => void, tr: ttm.MockTestRunner, done: MochaDone) {
try {
validator();
console.log(tr.succeeded);
done();
}
catch (error) {
console.log("STDERR", tr.stderr);
console.log("STDOUT", tr.stdout);
done(error);
}
}
describe('GitVersion Vsix Task tests', function () {
beforeEach(() => {
delete process.env[shared.TestEnvVars.useConfigFile];
delete process.env[shared.TestEnvVars.configFilePath];
delete process.env[shared.TestEnvVars.updateAssemblyInfo];
delete process.env[shared.TestEnvVars.updateAssemblyInfoFilename];
delete process.env[shared.TestEnvVars.additionalArguments];
delete process.env[shared.TestEnvVars.targetPath];
});
after(() => {
});
it('Succeeds: Runs GitVersion default configurations', function (done: MochaDone) {
this.timeout(1000);
let tp = path.join(__dirname, 'test-setup.js');
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();
runValidations(() => {
assert(tr.succeeded, 'should have succeeded');
assert(tr.warningIssues.length == 0, "should have no warnings");
assert(tr.errorIssues.length == 0, "should have no errors");
assert(tr.stderr.length == 0, 'should not have written to stderr');
assert(tr.invokedToolCount == 1, 'should have invoked tool one time. actual: ' + tr.invokedToolCount);
assert(tr.ran("dotnet-gitversion /user/build /output buildserver /nofetch"));
assert(tr.stdOutContained('GitVersion run successfully with defaults'), "should display 'GitVersion run successfully with defaults'");
}, tr, done);
});
it('Fails: Runs GitVersion with wrong targetPath', function (done: MochaDone) {
this.timeout(1000);
process.env[shared.TestEnvVars.targetPath] = 'wrongPath';
let tp = path.join(__dirname, 'test-setup.js');
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();
runValidations(() => {
assert(tr.failed, 'should have failed');
assert(tr.warningIssues.length == 0, "should have no warnings");
assert(tr.errorIssues.length == 1, "should have thrown an error");
assert(tr.stderr.length == 0, 'should not have written to stderr');
assert(tr.invokedToolCount == 0, 'should not have invoked tool. actual: ' + tr.invokedToolCount);
assert(tr.stdOutContained('Directory not found'), "should display 'Directory not found'");
}, tr, done);
});
it('Succeeds: Runs GitVersion with correct target', function (done: MochaDone) {
this.timeout(1000);
process.env[shared.TestEnvVars.targetPath] = 'src';
let tp = path.join(__dirname, 'test-setup.js');
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();
runValidations(() => {
assert(tr.succeeded, 'should have succeeded');
assert(tr.warningIssues.length == 0, "should have no warnings");
assert(tr.errorIssues.length == 0, "should have no errors");
assert(tr.stderr.length == 0, 'should not have written to stderr');
assert(tr.invokedToolCount == 1, 'should have invoked tool one time. actual: ' + tr.invokedToolCount);
assert(tr.ran("dotnet-gitversion /user/build/src /output buildserver /nofetch"));
assert(tr.stdOutContained('GitVersion run successfully with custom targetPath'), "should display 'GitVersion run successfully with custom targetPath'");
}, tr, done);
});
it('Fails: Runs GitVersion with no configuration file specified', function (done: MochaDone) {
this.timeout(1000);
process.env[shared.TestEnvVars.useConfigFile] = 'true';
let tp = path.join(__dirname, 'test-setup.js');
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();
runValidations(() => {
assert.equal(tr.succeeded, false, 'should have failed');
assert(tr.warningIssues.length == 0, "should have no warnings");
assert(tr.errorIssues.length == 1, "should have thrown an error");
assert(tr.stderr.length == 0, 'should not have written to stderr');
assert(tr.invokedToolCount == 0, 'should not have invoked tool. actual: ' + tr.invokedToolCount);
assert(tr.stdOutContained('GitVersion configuration file not found'), "should display 'GitVersion configuration file not found'");
}, tr, done);
});
it('Fails: Runs GitVersion with wrong configuration file specified', function (done: MochaDone) {
this.timeout(1000);
process.env[shared.TestEnvVars.useConfigFile] = 'true';
process.env[shared.TestEnvVars.configFilePath] = 'wronfConfig.yml';
let tp = path.join(__dirname, 'test-setup.js');
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();
runValidations(() => {
assert(tr.failed, 'should have failed');
assert(tr.warningIssues.length == 0, "should have no warnings");
assert(tr.errorIssues.length == 1, "should have thrown an error");
assert(tr.stderr.length == 0, 'should not have written to stderr');
assert(tr.invokedToolCount == 0, 'should not have invoked tool. actual: ' + tr.invokedToolCount);
assert(tr.stdOutContained('GitVersion configuration file not found'), "should display 'GitVersion configuration file not found'");
}, tr, done);
});
it('Succeeds: Runs GitVersion with correct custom configuration file specified', function (done: MochaDone) {
this.timeout(1000);
process.env[shared.TestEnvVars.useConfigFile] = 'true';
process.env[shared.TestEnvVars.configFilePath] = 'customConfig.yml';
let tp = path.join(__dirname, 'test-setup.js');
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();
runValidations(() => {
assert(tr.succeeded, 'should have succeeded');
assert(tr.warningIssues.length == 0, "should have no warnings");
assert(tr.errorIssues.length == 0, "should have no error");
assert(tr.stderr.length == 0, 'should not have written to stderr');
assert(tr.invokedToolCount == 1, 'should have invoked tool one time. actual: ' + tr.invokedToolCount);
assert(tr.ran("dotnet-gitversion /user/build /output buildserver /nofetch /config customConfig.yml"));
assert(tr.stdOutContained('GitVersion run successfully with custom config'), "should display 'GitVersion run successfully with custom config'");
}, tr, done);
});
it('Fails: Runs GitVersion with no assembly info file specified', function (done: MochaDone) {
this.timeout(1000);
process.env[shared.TestEnvVars.updateAssemblyInfo] = 'true';
let tp = path.join(__dirname, 'test-setup.js');
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();
runValidations(() => {
assert(tr.failed, 'should have failed');
assert(tr.warningIssues.length == 0, "should have no warnings");
assert(tr.errorIssues.length == 1, "should have thrown an error");
assert(tr.stderr.length == 0, 'should not have written to stderr');
assert(tr.invokedToolCount == 0, 'should not have invoked tool. actual: ' + tr.invokedToolCount);
assert(tr.stdOutContained('AssemblyInfoFilename file not found'), "should display 'AssemblyInfoFilename file not found'");
}, tr, done);
});
it('Fails: Runs GitVersion with wrong assembly info file specified', function (done: MochaDone) {
this.timeout(1000);
process.env[shared.TestEnvVars.updateAssemblyInfo] = 'true';
process.env[shared.TestEnvVars.updateAssemblyInfoFilename] = 'wrongAssemblyInfo.cs';
let tp = path.join(__dirname, 'test-setup.js');
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();
runValidations(() => {
assert(tr.failed, 'should have failed');
assert(tr.warningIssues.length == 0, "should have no warnings");
assert(tr.errorIssues.length == 1, "should have thrown an error");
assert(tr.stderr.length == 0, 'should not have written to stderr');
assert(tr.invokedToolCount == 0, 'should not have invoked tool. actual: ' + tr.invokedToolCount);
assert(tr.stdOutContained('AssemblyInfoFilename file not found'), "should display 'AssemblyInfoFilename file not found'");
}, tr, done);
});
it('Succeeds: Runs GitVersion with correct custom assembly info file specified', function (done: MochaDone) {
this.timeout(1000);
process.env[shared.TestEnvVars.updateAssemblyInfo] = 'true';
process.env[shared.TestEnvVars.updateAssemblyInfoFilename] = 'GlobalAssemblyInfo.cs';
let tp = path.join(__dirname, 'test-setup.js');
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();
runValidations(() => {
assert(tr.succeeded, 'should have succeeded');
assert(tr.warningIssues.length == 0, "should have no warnings");
assert(tr.errorIssues.length == 0, "should have no errors");
assert(tr.stderr.length == 0, 'should not have written to stderr');
assert(tr.invokedToolCount == 1, 'should have invoked tool one time. actual: ' + tr.invokedToolCount);
assert(tr.ran("dotnet-gitversion /user/build /output buildserver /nofetch /updateassemblyinfo GlobalAssemblyInfo.cs"));
assert(tr.stdOutContained('GitVersion run successfully with custom assembly info'), "should display 'GitVersion run successfully with custom assembly info'");
}, tr, done);
});
});

View File

@@ -1,13 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"target": "es6",
"sourceMap": false,
"inlineSourceMap": true,
"declaration": false
}
}

View File

@@ -1,13 +1,3 @@
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"target": "es6",
"sourceMap": false,
"inlineSourceMap": true
},
"exclude": ["tests"]
}
"extends": "../tsconfig.json"
}

View File

@@ -1,71 +0,0 @@
{
"manifestVersion": 1,
"id": "usegitversion-preview",
"name": "UseGitVersion (Preview)",
"publisher": "gittools",
"public": true,
"author": "GitVersion Contributors",
"version": "5.1.3.1911100642",
"description": "Build task for easy semantic versioning for projects using Git.",
"targets": [{
"id": "Microsoft.VisualStudio.Services"
}],
"files": [
{ "path": "GitVersionTask" }
],
"categories": [
"Azure Pipelines"
],
"galleryFlags": [
"Preview"
],
"icons": {
"default": "images/extension-icon.png"
},
"tags": [
"semver",
"git",
"versioning",
"gitflow",
"githubflow"
],
"links": {
"learn": {
"uri": "http://gitversion.readthedocs.org/en/stable"
},
"getstarted": {
"uri": "http://gitversion.readthedocs.org/en/stable/build-server-support/build-server/tfs-build-vnext/#running-inside-tfs"
},
"license": {
"uri": "https://github.com/GitTools/GitVersion/blob/master/LICENSE"
},
"repository": {
"uri": "https://github.com/GitTools/GitVersion"
},
"support": {
"uri": "https://github.com/GitTools/GitVersion/issues"
}
},
"screenshots": [{
"path": "images/builds.png"
},
{
"path": "images/build-task.png"
}
],
"content": {
"details": {
"path": "overview.md"
}
},
"contributions": [{
"id": "gitversion-task",
"type": "ms.vss-distributed-task.task",
"targets": [
"ms.vss-distributed-task.tasks"
],
"properties": {
"name": "GitVersionTask"
}
}]
}

219
src/common.ts Normal file
View File

@@ -0,0 +1,219 @@
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";
import { inject, injectable } from "inversify";
import { Argument, IBuildAgent, IDotnetTool, IExecResult, IGitVersionTool, IVersionManager } from "./interfaces";
import { TYPES } from "./types";
@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 Array<{ 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 GitVersionTool implements IGitVersionTool {
private dotnetTool: IDotnetTool;
constructor(
@inject(TYPES.IDotnetTool) dotnetTool: IDotnetTool,
) {
this.dotnetTool = dotnetTool;
}
public async install(versionSpec: string, includePrerelease: boolean): Promise<void> {
const toolPath = await this.dotnetTool.toolInstall("GitVersion.Tool", versionSpec, false, includePrerelease);
}
public run(...params: Argument[]): Promise<IExecResult> {
return;
}
}
const cmp = require("semver-compare");
@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);
}
}
export {
DotnetTool,
GitVersionTool,
VersionManager,
};

View File

@@ -1,15 +0,0 @@
import * as core from '@actions/core';
import { ToolInstaller } from '../ToolInstaller';
import { Proxy as utils } from './Proxy';
try {
let toolInstaller = new ToolInstaller(utils);
toolInstaller.downloadAndInstall('GitVersion.Tool', '5.1.2', false, false);
// `who-to-greet` input defined in action metadata file
const nameToGreet = core.getInput('who-to-greet');
console.log(`Hello ${nameToGreet}!`);
const time = (new Date()).toTimeString();
core.setOutput("time", time);
// Get the JSON webhook payload for the event that triggered the workflow
} catch (error) {
core.setFailed(error.message);
}

View File

@@ -1,20 +0,0 @@
import { find, cacheDir } from '@actions/tool-cache';
import { debug, exportVariable, addPath } from '@actions/core';
import { which } from '@actions/io';
import { isExplicitVersion, evaluateVersions, createTempDir, cleanVersion, executeDotNet } from './ToolLib';
import { IProxy } from '../Interfaces';
export const Proxy: IProxy = {
find : find,
debug: debug,
exportVariable: exportVariable,
addPath: addPath,
which: which,
cacheDir: cacheDir,
isExplicitVersion: isExplicitVersion,
evaluateVersions: evaluateVersions,
cleanVersion: cleanVersion,
createTempDir: createTempDir,
executeDotNet: executeDotNet
}

View File

@@ -1,114 +0,0 @@
import * as path from 'path';
import * as semver from 'semver';
import * as uuidV4 from "uuid/v4";
import * as io from '@actions/io';
import * as core from '@actions/core';
import * as exec from '@actions/exec';
import { IExecSyncResult } from '../Interfaces';
const cmp = require('semver-compare');
export async function createTempDir(): Promise<string> {
const IS_WINDOWS = process.platform === "win32";
let tempDirectory: string = process.env["RUNNER_TEMP"] || "";
if (!tempDirectory) {
let baseLocation: string;
if (IS_WINDOWS) {
// On Windows use the USERPROFILE env variable
baseLocation = process.env["USERPROFILE"] || "C:\\";
} else {
if (process.platform === "darwin") {
baseLocation = "/Users";
} else {
baseLocation = "/home";
}
}
tempDirectory = path.join(baseLocation, "actions", "temp");
}
const dest = path.join(tempDirectory, uuidV4());
await io.mkdirP(dest);
return dest;
}
/**
* Checks if a version spec is an explicit version (e.g. 1.0.1 or v1.0.1)
* As opposed to a version spec like 1.x
*
* @param versionSpec
*/
export function isExplicitVersion(versionSpec: string): boolean {
let c = semver.clean(versionSpec);
core.debug('isExplicit: ' + c);
let valid = semver.valid(c) != null;
core.debug('explicit? ' + valid);
return valid;
}
/**
* Returns cleaned (removed leading/trailing whitespace, remove '=v' prefix)
* and parsed version, or null if version is invalid.
*/
export function cleanVersion(version: string): string {
core.debug('cleaning: ' + version);
return semver.clean(version);
}
/**
* evaluates a list of versions and returns the latest version matching the version spec
*
* @param versions an array of versions to evaluate
* @param versionSpec a version spec (e.g. 1.x)
*/
export function evaluateVersions(versions: string[], versionSpec: string): string {
let version: string;
core.debug('evaluating ' + versions.length + ' versions');
versions = versions.sort(cmp);
for (let i = versions.length - 1; i >= 0; i--) {
let potential: string = versions[i];
let satisfied: boolean = semver.satisfies(potential, versionSpec);
if (satisfied) {
version = potential;
break;
}
}
if (version) {
core.debug('matched: ' + version);
}
else {
core.debug('match not found');
}
return version;
}
export async function executeDotNet(args: string[]) : Promise<IExecSyncResult> {
let dotnetPath = await io.which("dotnet", true);
let resultCode = 0;
let stdout = '';
let stderr = '';
resultCode = await exec.exec(
`"${dotnetPath}"`,
args,
{
listeners: {
stdout: (data: Buffer) => {
stdout += data.toString();
},
stderr: (data: Buffer) => {
stderr += data.toString();
}
}
});
return {
code: resultCode,
stdout: stdout,
stderr: stderr,
error: null
}
}

101
src/github/build-agent.ts Normal file
View File

@@ -0,0 +1,101 @@
import * as path from "path";
import * as uuidV4 from "uuid/v4";
import * as core from "@actions/core";
import * as exe from "@actions/exec";
import * as io from "@actions/io";
import * as toolCache from "@actions/tool-cache";
import { injectable } from "inversify";
import { IBuildAgent, IExecResult } from "../interfaces";
@injectable()
class BuildAgent implements IBuildAgent {
public find(toolName: string, versionSpec: string, arch?: string): string {
return toolCache.find(toolName, versionSpec, arch);
}
public cacheDir(sourceDir: string, tool: string, version: string, arch?: string): Promise<string> {
return toolCache.cacheDir(sourceDir, tool, version, arch);
}
public async createTempDir(): Promise<string> {
const IS_WINDOWS = process.platform === "win32";
let tempDirectory: string = process.env.RUNNER_TEMP || "";
if (!tempDirectory) {
let baseLocation: string;
if (IS_WINDOWS) {
// On Windows use the USERPROFILE env variable
baseLocation = process.env.USERPROFILE || "C:\\";
} else {
if (process.platform === "darwin") {
baseLocation = "/Users";
} else {
baseLocation = "/home";
}
}
tempDirectory = path.join(baseLocation, "actions", "temp");
}
const dest = path.join(tempDirectory, uuidV4());
await io.mkdirP(dest);
return dest;
}
public debug(message: string): void {
core.debug(message);
}
public setFailed(message: string, done?: boolean): void {
core.setFailed(message);
}
public setSucceeded(message: string, done?: boolean): void {
//
}
public exportVariable(name: string, val: string): void {
core.exportVariable(name, val);
}
public addPath(inputPath: string): void {
core.addPath(inputPath);
}
public which(tool: string, check?: boolean): Promise<string> {
return io.which(tool, check);
}
public async exec(exec: string, args: string[]): Promise<IExecResult> {
const dotnetPath = await io.which(exec, true);
let resultCode = 0;
let stdout = "";
let stderr = "";
resultCode = await exe.exec(
`"${dotnetPath}"`,
args,
{
listeners: {
stderr: (data: Buffer) => {
stderr += data.toString();
},
stdout: (data: Buffer) => {
stdout += data.toString();
},
},
});
return {
code: resultCode,
error: null,
stderr,
stdout,
};
}
}
export {
BuildAgent,
};

15
src/github/ioc.ts Normal file
View File

@@ -0,0 +1,15 @@
import { Container } from "inversify";
import { DotnetTool, GitVersionTool, VersionManager } from "../common";
import { IBuildAgent, IDotnetTool, IGitVersionTool, IVersionManager } from "../interfaces";
import { TYPES } from "../types";
import { BuildAgent } from "./build-agent";
const ioc = new Container();
ioc.bind<IVersionManager>(TYPES.IVersionManager).to(VersionManager);
ioc.bind<IBuildAgent>(TYPES.IBuildAgent).to(BuildAgent);
ioc.bind<IDotnetTool>(TYPES.IDotnetTool).to(DotnetTool);
ioc.bind<IGitVersionTool>(TYPES.IGitVersionTool).to(GitVersionTool);
export { ioc };

24
src/github/main.ts Normal file
View File

@@ -0,0 +1,24 @@
import "reflect-metadata";
import { IBuildAgent, IGitVersionTool } from "../interfaces";
import { TYPES } from "../types";
import { ioc } from "./ioc";
const gitVersionTool = ioc.get<IGitVersionTool>(TYPES.IGitVersionTool);
const buildAgent = ioc.get<IBuildAgent>(TYPES.IBuildAgent);
export async function run() {
try {
const version = "5.1.2";
const includePrerelease = false;
await gitVersionTool.install(version, includePrerelease);
buildAgent.setSucceeded("GitVersion executed successfully", true);
} catch (error) {
buildAgent.setFailed(error.message, true);
}
}
run();

View File

@@ -1,13 +1,3 @@
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"target": "es6",
"sourceMap": false,
"inlineSourceMap": true,
"declaration": false
}
}
"extends": "../tsconfig.json"
}

13
src/tsconfig.json Normal file
View File

@@ -0,0 +1,13 @@
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"target": "es2015",
"sourceMap": false,
"lib": ["es2015", "dom"],
"types": ["reflect-metadata"],
"inlineSourceMap": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}

8
src/types.ts Normal file
View File

@@ -0,0 +1,8 @@
const TYPES = {
IBuildAgent: Symbol.for("BuildAgent"),
IDotnetTool: Symbol.for("DotnetTool"),
IGitVersionTool: Symbol.for("GitVersionTool"),
IVersionManager: Symbol.for("VersionManager"),
};
export { TYPES };

13
tslint.json Normal file
View File

@@ -0,0 +1,13 @@
{
"defaultSeverity": "error",
"extends": [
"tslint:recommended"
],
"jsRules": {},
"rules": {
"max-classes-per-file":false,
"no-console":false,
"no-var-requires":false
},
"rulesDirectory": []
}