Skip to content
This repository was archived by the owner on Oct 30, 2020. It is now read-only.

Commit 083f993

Browse files
author
Paulo Baima
committed
Refactoring interfaces to external files and fixing unit tests
1 parent c8a233c commit 083f993

File tree

12 files changed

+193
-107
lines changed

12 files changed

+193
-107
lines changed

unify-release-build-task/.taskkey

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
99a4bdfa-da99-41fb-ab0b-2e3e4fa2da92
1+
8c4133fb-c26d-45d8-ad4b-64e5c107a455

unify-release-build-task/src/helpers/azureDevOpsClientWrapper.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import * as azdev from "azure-devops-node-api";
22
import { IBuildApi } from "azure-devops-node-api/BuildApi";
33
import { BuildReason, BuildStatus, BuildResult, BuildQueryOrder, QueryDeletedOption, Build } from "azure-devops-node-api/interfaces/BuildInterfaces"
4-
import { injectable, inject } from "tsyringe";
4+
import { injectable } from "tsyringe";
5+
import { IAzureDevOpsWrapper } from "../interfaces/types";
56

67
@injectable()
7-
export default class AzureDevOpsWrapper {
8+
export default class AzureDevOpsWrapper implements IAzureDevOpsWrapper {
89
constructor() { }
910

1011
async getBuildApi(organizationUrl: string, token: string): Promise<IBuildApi> {
Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
import "reflect-metadata";
2-
import { container } from "tsyringe";
3-
import * as TaskLib from "azure-pipelines-task-lib/task";
2+
import { container, Lifecycle } from "tsyringe";
3+
import * as TaskLib from "azure-pipelines-task-lib/task";
4+
import VariableManager from "./variableManager";
5+
import BuildService from "../services/buildService";
6+
import AzureDevOpsWrapper from "./azureDevOpsClientWrapper";
7+
import UnifyReleaseService from "../services/unifyReleaseService";
8+
import AzureDevOpsConfiguration from "../models/AzureDevOpsConfiguration";
49

5-
container.register("TaskLib", {useValue: TaskLib});
10+
// Services
11+
container.register("IBuildService", { useClass: BuildService })
12+
container.register("IUnifyReleaseService", { useClass: UnifyReleaseService })
13+
14+
// Models
15+
container.register("IAzureDevOpsConfiguration", { useClass: AzureDevOpsConfiguration }, { lifecycle: Lifecycle.Singleton })
16+
// Helpers
17+
container.register("IVariableManager", { useClass: VariableManager }, { lifecycle: Lifecycle.Singleton })
18+
container.register("IAzureDevOpsWrapper", { useClass: AzureDevOpsWrapper }, { lifecycle: Lifecycle.Singleton })
19+
20+
// Others
21+
container.register("TaskLib", { useValue: TaskLib });

unify-release-build-task/src/helpers/variableManager.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
import { resolve } from "path"
22
import { config } from "dotenv"
3-
import { injectable, inject } from "tsyringe";
3+
import { singleton, injectable, inject } from "tsyringe";
4+
import { IVariableManager } from "../interfaces/types";
45

56
config({ path: resolve(__dirname, "../.env") })
6-
@injectable()
7-
export default class VariableManager {
87

8+
9+
10+
@singleton()
11+
export default class VariableManager implements IVariableManager {
912
constructor(@inject("TaskLib") private taskLib: any) {
1013
}
1114

1215
getInput(key: string, required: boolean): string | undefined {
1316
if (process.env.NODE_ENV == "development") {
1417
if (required && !process.env[key]) {
1518
throw new Error(`Required Parameter ${key} not supplied.`)
16-
}
19+
}
1720
return process.env[key];
1821
} else {
1922
return this.taskLib.getInput(key, required)!;
Lines changed: 4 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,15 @@
11
import "./helpers/dependency-injection";
22
import tl = require('azure-pipelines-task-lib/task');
3-
import AzureDevOpsClient from "./services/buildService";
43
import { container } from "tsyringe";
5-
import AzureDevopsConfiguration from "./models/AzureDevOpsConfiguration"
4+
import { IUnifyReleaseService } from "./interfaces/types";
65
async function run() {
76
try {
8-
9-
var configuration = container.resolve(AzureDevopsConfiguration)
10-
var devOpsClient = container.resolve(AzureDevOpsClient);
11-
12-
var buildDetails = await devOpsClient.getBuildInfo(configuration.teamFoundationCollectionUri, configuration.accessToken, configuration.teamFoundationProject, configuration.currentBuildId);
13-
14-
15-
16-
console.log(`Task Parameters: ReleaseTag: ${configuration.releaseTag}, ReleaseOnCancel: ${configuration.releaseOnCancel}, ReleaseOnError ${configuration.releaseOnError}`)
17-
console.log(`Processing Build ${buildDetails.id} from Source Version ${buildDetails.sourceVersion}`);
18-
19-
var relatedBuilds = await devOpsClient.listRelatedBuilds(configuration.teamFoundationCollectionUri, configuration.accessToken,
20-
configuration.teamFoundationProject, buildDetails.sourceVersion,
21-
configuration.waitForAllTriggeredBuilds,
22-
[configuration.definition1, configuration.definition2, configuration.definition3, configuration.definition4, configuration.definition5]);
23-
24-
25-
/*
26-
let shouldCreateTag = true;
27-
for (let build of relatedBuilds.values()) {
28-
console.log(`Checking the last Build Run for Definition ${build.definition.name}`);
29-
console.log(`---> Build Id: ${build.id}. Build Status ${BuildStatus[build.status]}. Build Result ${BuildResult[build.result]}`)
30-
if (buildDetails.id == build.id) {
31-
console.log(`---> Build ${build.id} is the same of current build ${buildDetails.id}`)
32-
continue;
33-
}
34-
35-
if (build.status == BuildStatus.Completed && (build.result == BuildResult.Succeeded || build.result == BuildResult.PartiallySucceeded)) {
36-
console.log(`Build Succeeded, continue.`)
37-
continue;
38-
}
39-
40-
if (build.result == BuildResult.Canceled && !releaseOnCancel) {
41-
shouldCreateTag = false;
42-
break;
43-
}
44-
45-
46-
if (build.result == BuildResult.Failed && !releaseOnError) {
47-
shouldCreateTag = false;
48-
break;
49-
}
50-
}
51-
52-
if (shouldCreateTag) {
53-
await devOpsClient.addBuildTag(teamfoundationCollectionUri, accessToken, teamfoundationProject, buildDetails.id, releaseTag);
54-
console.log(`No Concurrent Builds, Creating tag ${releaseTag}. Please remember to add this tag as a condition in your release pipeline trigger.`)
55-
}
56-
57-
/* if (inputString == 'bad') {
58-
tl.setResult(tl.TaskResult.Failed, 'Bad input was given');
59-
return;
60-
}*/
7+
let unifyReleaseService = container.resolve<IUnifyReleaseService>("IUnifyReleaseService");
8+
await unifyReleaseService.unifyRelease();
9+
tl.setResult(tl.TaskResult.Succeeded, "Task Completed");
6110
}
6211
catch (err) {
6312
tl.setResult(tl.TaskResult.Failed, err.message);
6413
}
6514
}
66-
6715
run();
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { IBuildApi } from "azure-devops-node-api/BuildApi";
2+
import { BuildReason, BuildStatus, BuildResult, QueryDeletedOption, BuildQueryOrder, Build } from "azure-devops-node-api/interfaces/BuildInterfaces";
3+
4+
// Models
5+
export interface IAzureDevOpsConfiguration {
6+
releaseTag: string;
7+
waitForAllTriggeredBuilds: boolean;
8+
definition1: string;
9+
definition2: string;
10+
definition3: string;
11+
definition4: string;
12+
definition5: string;
13+
releaseOnCancel: boolean;
14+
releaseOnError: boolean;
15+
teamFoundationCollectionUri: string;
16+
teamFoundationProject: string;
17+
accessToken: string;
18+
currentBuildId: number;
19+
}
20+
21+
// Services
22+
export interface IBuildService {
23+
24+
getBuildInfo(organizationUrl: string, token: string, project: string, buildId: number): Promise<Build>;
25+
26+
listRelatedBuilds(organizationUrl: string, token: string, project: string, sourceVersion: string, waitForAllBuilds: Boolean, definitionFilters?: string[]): Promise<Map<string, Build>>;
27+
28+
addBuildTag(organizationUrl: string, project: string, token: string, buildId: number, tag: string);
29+
30+
}
31+
32+
export interface IUnifyReleaseService {
33+
unifyRelease(): void;
34+
}
35+
36+
// Helpers
37+
export interface IAzureDevOpsWrapper {
38+
getBuildApi(organizationUrl: string, token: string): Promise<IBuildApi>;
39+
40+
getBuild(organizationUrl: string, token: string, project: string, buildId: number);
41+
42+
getBuilds(organizationUrl: string, token: string, project: string, definitions?: number[],
43+
queues?: number[], buildNumber?: string, minTime?: Date, maxTime?: Date,
44+
requestedFor?: string, reasonFilter?: BuildReason, statusFilter?: BuildStatus,
45+
resultFilter?: BuildResult, tagFilters?: string[], properties?: string[],
46+
top?: number, continuationToken?: string, maxBuildsPerDefinition?: number,
47+
deletedFilter?: QueryDeletedOption, queryOrder?: BuildQueryOrder,
48+
branchName?: string, buildIds?: number[], repositoryId?: string,
49+
repositoryType?: string): Promise<Build[]>;
50+
51+
addBuildTag(organizationUrl: string, token: string, project: string, buildId: number, tag: string);
52+
}
53+
54+
export interface IVariableManager {
55+
getInput(key: string, required: boolean): string | undefined;
56+
getBooleanInput(key: string, required: boolean): boolean | undefined;
57+
getVariable(key: string): string | undefined;
58+
}

unify-release-build-task/src/models/AzureDevOpsConfiguration.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import "reflect-metadata";
22
import { injectable, inject } from "tsyringe";
3-
import VariableManager from "../helpers/variableManager";
3+
import { IAzureDevOpsConfiguration, IVariableManager } from "../interfaces/types";
44

55
@injectable()
6-
export default class AzureDevOpsConfiguration {
6+
export default class AzureDevOpsConfiguration implements IAzureDevOpsConfiguration {
77
public releaseTag: string;
88
public waitForAllTriggeredBuilds: boolean;
99
public definition1: string;
@@ -18,7 +18,7 @@ export default class AzureDevOpsConfiguration {
1818
public accessToken: string;
1919
public currentBuildId: number;
2020

21-
constructor(@inject(VariableManager) private variableManager : VariableManager){
21+
constructor(@inject("IVariableManager") private variableManager: IVariableManager) {
2222
this.releaseTag = variableManager.getInput('releaseTag', true)!;
2323
this.waitForAllTriggeredBuilds = variableManager.getBooleanInput('waitForAllBuilds', false)!;
2424
this.definition1 = variableManager.getInput('definition1', false)!;

unify-release-build-task/src/services/buildService.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import * as azdev from "azure-devops-node-api";
2-
import * as ba from "azure-devops-node-api/BuildApi";
31
import { Build, BuildStatus, BuildResult } from "azure-devops-node-api/interfaces/BuildInterfaces";
4-
import { stringify } from "querystring";
5-
import { IBuildApi } from "azure-devops-node-api/BuildApi";
6-
import AzureDevOpsClientWrapper from "../helpers/azureDevOpsClientWrapper";
2+
import IAzureDevOpsClientWrapper from "../helpers/azureDevOpsClientWrapper";
73
import { injectable, inject } from "tsyringe";
4+
import { IBuildService } from "../interfaces/types";
5+
86

97
@injectable()
10-
export default class AzureDevOpsClient {
8+
export default class BuildService implements IBuildService {
119

12-
constructor(@inject(AzureDevOpsClientWrapper) private azureDevOpsClient: AzureDevOpsClientWrapper) {
10+
constructor(@inject("IAzureDevOpsClientWrapper") private azureDevOpsClient: IAzureDevOpsClientWrapper) {
1311

1412
}
1513

@@ -51,7 +49,7 @@ export default class AzureDevOpsClient {
5149
return builds.sort((a, b) => b.queueTime.getDate() - a.queueTime.getDate());
5250
}
5351

54-
public async addBuildTag(organizationUrl: string, token: string, project: string, buildId: number, tag: string) {
52+
public async addBuildTag(organizationUrl: string, project: string, token: string, buildId: number, tag: string) {
5553
return await await this.azureDevOpsClient.addBuildTag(organizationUrl, token, project, buildId, tag);
5654
}
5755
}
Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import { inject } from "tsyringe";
2-
import BuildService from "../services/buildService";
3-
import AzureDevopsConfiguration from "../models/AzureDevOpsConfiguration";
1+
import { inject, injectable } from "tsyringe";
2+
import { IUnifyReleaseService, IBuildService, IAzureDevOpsConfiguration } from "../interfaces/types";
3+
import { BuildStatus, BuildResult } from "azure-devops-node-api/interfaces/BuildInterfaces";
44

5-
export default class UnifyReleaseService {
5+
@injectable()
6+
export default class UnifyReleaseService implements IUnifyReleaseService {
67

7-
constructor(@inject(BuildService) private buildService: BuildService, @inject(AzureDevopsConfiguration) private configuration: AzureDevopsConfiguration) {
8-
9-
}
8+
constructor(
9+
@inject("IBuildService") private buildService: IBuildService,
10+
@inject("IAzureDevOpsConfiguration") private configuration: IAzureDevOpsConfiguration) { };
1011

1112
async unifyRelease() {
1213
var buildDetails = await this.buildService.getBuildInfo(this.configuration.teamFoundationCollectionUri, this.configuration.accessToken, this.configuration.teamFoundationProject, this.configuration.currentBuildId);
@@ -18,7 +19,41 @@ export default class UnifyReleaseService {
1819
ReleaseOnError ${this.configuration.releaseOnError}
1920
WaitForAllDefinitions: ${this.configuration.waitForAllTriggeredBuilds}`);
2021

21-
22+
var relatedBuilds = await this.buildService.listRelatedBuilds(this.configuration.teamFoundationCollectionUri, this.configuration.accessToken,
23+
this.configuration.teamFoundationProject, buildDetails.sourceVersion,
24+
this.configuration.waitForAllTriggeredBuilds,
25+
[this.configuration.definition1, this.configuration.definition2, this.configuration.definition3, this.configuration.definition4, this.configuration.definition5]);
26+
27+
let shouldCreateTag = true;
28+
for (let build of relatedBuilds.values()) {
29+
console.log(`Checking the last Build Run for Definition ${build.definition.name}`);
30+
console.log(`---> Build Id: ${build.id}. Build Status ${BuildStatus[build.status]}. Build Result ${BuildResult[build.result]}`)
31+
if (buildDetails.id == build.id) {
32+
console.log(`---> Build ${build.id} is the same of current build ${buildDetails.id}`)
33+
continue;
34+
}
35+
36+
if (build.status == BuildStatus.Completed && (build.result == BuildResult.Succeeded || build.result == BuildResult.PartiallySucceeded)) {
37+
console.log(`Build Succeeded, continue.`)
38+
continue;
39+
}
40+
41+
if (build.result == BuildResult.Canceled && !this.configuration.releaseOnCancel) {
42+
shouldCreateTag = false;
43+
break;
44+
}
45+
46+
47+
if (build.result == BuildResult.Failed && !this.configuration.releaseOnError) {
48+
shouldCreateTag = false;
49+
break;
50+
}
51+
}
52+
53+
if (shouldCreateTag) {
54+
await this.buildService.addBuildTag(this.configuration.teamFoundationCollectionUri, this.configuration.teamFoundationProject, this.configuration.accessToken, this.configuration.currentBuildId, this.configuration.releaseTag);
55+
console.log(`No Concurrent Builds, Creating tag ${this.configuration.releaseTag}. Please remember to add this tag as a condition in your release pipeline trigger.`)
56+
}
2257
}
2358

2459
}

unify-release-build-task/tests/models/azureDevOpsConfigurationTests.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
import "reflect-metadata";
33
import { expect } from 'chai';
44
import 'mocha';
5-
import { stubObject as Stub, StubbedInstance, stubInterface, stubObject } from "ts-sinon";
5+
import { StubbedInstance, stubInterface as StubInterface } from "ts-sinon";
66

77
//# Imports
8-
import VariableManager from "../../src/helpers/variableManager";
98
import AzureDevOpsConfiguration from "../../src/models/AzureDevOpsConfiguration";
9+
import { IVariableManager } from "../../src/interfaces/types";
1010

1111
//# Tests
1212
describe('AzureDevOpsConfiguration', () => {
@@ -15,7 +15,7 @@ describe('AzureDevOpsConfiguration', () => {
1515

1616
describe('constructor', () => {
1717
it('Should get variables from variable manager', async () => {
18-
let variableManagerStub: StubbedInstance<VariableManager> = Stub(new VariableManager(null));
18+
let variableManagerStub: StubbedInstance<IVariableManager> = StubInterface<IVariableManager>();
1919
variableManagerStub.getInput.withArgs('releaseTag', true).returns("releaseTag");
2020
variableManagerStub.getInput.withArgs('definition1', false).returns("definition1");
2121
variableManagerStub.getInput.withArgs('definition2', false).returns("definition2");

0 commit comments

Comments
 (0)