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

Commit 5bb50fa

Browse files
committed
Fix broken variables type for azure-pipelines.yaml
Fixed an issue with the starter azure-pipelines.yaml an top level type definition for it. `variables` expects a sequence of maps but a top level map was being generated instead.
1 parent 480d5ae commit 5bb50fa

File tree

7 files changed

+429
-297
lines changed

7 files changed

+429
-297
lines changed

package.json

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"@types/jest": "^24.0.18",
2222
"@types/jest-when": "^2.7.0",
2323
"@types/js-yaml": "^3.12.1",
24-
"@types/mock-fs": "^3.6.30",
24+
"@types/mock-fs": "^4.10.0",
2525
"@types/node": "^12.7.8",
2626
"@types/node-emoji": "^1.8.1",
2727
"@types/shelljs": "^0.8.5",
@@ -30,19 +30,19 @@
3030
"jest": "^24.9.0",
3131
"jest-when": "^2.7.0",
3232
"lint-staged": ">=8",
33-
"mock-fs": "^4.10.1",
33+
"mock-fs": "^4.10.2",
3434
"nyc": "^14.1.1",
3535
"pkg": "^4.4.0",
3636
"prettier": "^1.18.2",
3737
"shx": "^0.3.2",
3838
"ts-jest": "^24.0.2",
3939
"ts-loader": "^6.0.4",
4040
"ts-node": "^8.4.1",
41-
"tslint": "^5.19.0",
41+
"tslint": "^5.20.0",
4242
"tslint-config-prettier": "^1.18.0",
43-
"typescript": "^3.6.2",
44-
"webpack": "^4.39.3",
45-
"webpack-cli": "^3.3.8"
43+
"typescript": "^3.6.4",
44+
"webpack": "^4.41.2",
45+
"webpack-cli": "^3.3.9"
4646
},
4747
"prettier": {
4848
"proseWrap": "always"
@@ -60,7 +60,7 @@
6060
},
6161
"dependencies": {
6262
"@azure/arm-storage": "^10.1.0",
63-
"@azure/keyvault-secrets": "^4.0.0-preview.5",
63+
"@azure/keyvault-secrets": "4.0.0-preview.5",
6464
"@azure/ms-rest-nodeauth": "^3.0.0",
6565
"@types/fs-extra": "^8.0.0",
6666
"@types/git-url-parse": "^9.0.0",
@@ -72,7 +72,6 @@
7272
"git-url-parse": "^11.1.2",
7373
"isomorphic-fetch": "^2.2.1",
7474
"js-yaml": "^3.13.1",
75-
"mock-fs": "^4.10.1",
7675
"node-emoji": "^1.10.0",
7776
"open": "^6.4.0",
7877
"shelljs": "^0.8.3",

src/commands/project/init.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@ export const initCommandDecorator = (command: commander.Command): void => {
3636
)
3737
.option(
3838
"-r, --default-ring <branch-name>",
39-
"Specify a default ring; this corresponds to a default branch which you wish to push initial revisions to"
39+
"Specify a default ring; this corresponds to a default branch which you wish to push initial revisions to",
40+
"master"
4041
)
4142
.action(async opts => {
42-
const { monoRepo = false, packagesDir = "packages", defaultRing } = opts;
43+
const { monoRepo, packagesDir, defaultRing } = opts;
4344
const projectPath = process.cwd();
4445
try {
4546
// Type check all parsed command line args here.
@@ -53,10 +54,7 @@ export const initCommandDecorator = (command: commander.Command): void => {
5354
`--packages-dir must be of type 'string', ${typeof packagesDir} given`
5455
);
5556
}
56-
if (
57-
typeof defaultRing !== "string" &&
58-
typeof defaultRing !== "undefined"
59-
) {
57+
if (typeof defaultRing !== "string") {
6058
throw new Error(
6159
`--default-ring must be of type 'string', '${defaultRing}' of type '${typeof defaultRing}' given`
6260
);

src/lib/fileutils.test.ts

Lines changed: 96 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,40 @@
1-
import fs, { write } from "fs";
2-
import mockFs from "mock-fs";
3-
1+
////////////////////////////////////////////////////////////////////////////////
2+
// !!NOTE!!
3+
// This test suite uses mock-fs to mock out the the file system
4+
// console.log CANNOT be reliably called in this suite
5+
// - https://github.com/facebook/jest/issues/5792
6+
// - https://github.com/tschaub/mock-fs/issues/234
7+
//
8+
// Workaround: The global logger object in `src/logger` does work, so use that
9+
// to debug
10+
////////////////////////////////////////////////////////////////////////////////
11+
import fs from "fs";
412
import yaml from "js-yaml";
13+
import mockFs from "mock-fs";
14+
import os from "os";
15+
import path from "path";
16+
import shelljs from "shelljs";
17+
import uuid from "uuid/v4";
18+
import { disableVerboseLogging, enableVerboseLogging, logger } from "../logger";
519
import {
620
createTestBedrockYaml,
721
createTestHldAzurePipelinesYaml,
822
createTestMaintainersYaml
923
} from "../test/mockFactory";
10-
11-
import path from "path";
12-
13-
import { disableVerboseLogging, enableVerboseLogging } from "../logger";
14-
import { IBedrockFile, IHelmConfig, IMaintainersFile } from "../types";
24+
import {
25+
IAzurePipelinesYaml,
26+
IBedrockFile,
27+
IHelmConfig,
28+
IMaintainersFile
29+
} from "../types";
1530
import {
1631
addNewServiceToBedrockFile,
1732
addNewServiceToMaintainersFile,
1833
generateDockerfile,
1934
generateGitIgnoreFile,
20-
generateHldAzurePipelinesYaml
35+
generateHldAzurePipelinesYaml,
36+
generateStarterAzurePipelinesYaml,
37+
starterAzurePipelines
2138
} from "./fileutils";
2239

2340
beforeAll(() => {
@@ -236,3 +253,73 @@ describe("generating service Dockerfile", () => {
236253
);
237254
});
238255
});
256+
257+
describe("starterAzurePipelines", () => {
258+
// Create a random workspace dir before every test
259+
let randomDirPath = "";
260+
beforeEach(() => {
261+
randomDirPath = path.join(os.tmpdir(), uuid());
262+
shelljs.mkdir("-p", randomDirPath);
263+
});
264+
265+
test("that the value of the file is the same after (de)serialization", async () => {
266+
const branches = ["qa", "prod"];
267+
const variableGroups = ["foo", "bar"];
268+
const vmImage = "gentoo";
269+
const starter = await starterAzurePipelines({
270+
branches,
271+
relProjectPaths: [path.join("packages", "a"), path.join("packages", "b")],
272+
variableGroups,
273+
vmImage
274+
});
275+
const serializedYaml = yaml.safeDump(starter, {
276+
lineWidth: Number.MAX_SAFE_INTEGER
277+
});
278+
const pipelinesPath = path.join(randomDirPath, "azure-pipelines.yaml");
279+
fs.writeFileSync(pipelinesPath, serializedYaml);
280+
const deserializedYaml = yaml.safeLoad(
281+
fs.readFileSync(pipelinesPath, "utf8")
282+
);
283+
284+
// should be equal to the initial value
285+
expect(deserializedYaml).toStrictEqual(starter);
286+
287+
// trigger.branches.include should include 'qa' and 'prod'
288+
for (const branch of branches) {
289+
expect(starter.trigger!.branches!.include!.includes(branch));
290+
}
291+
292+
// variables should include all groups
293+
for (const group of variableGroups) {
294+
expect(starter.variables!.includes({ group }));
295+
}
296+
297+
// pool.vmImage should be 'gentoo'
298+
expect(starter.pool!.vmImage).toBe(vmImage);
299+
});
300+
301+
test("that all services receive an azure-pipelines.yaml and the correct paths have been inserted", async () => {
302+
// Create service directories
303+
const servicePaths = ["a", "b", "c"].map(serviceDir => {
304+
const servicePath = path.join(randomDirPath, "packages", serviceDir);
305+
shelljs.mkdir("-p", servicePath);
306+
return servicePath;
307+
});
308+
309+
for (const servicePath of servicePaths) {
310+
await generateStarterAzurePipelinesYaml(randomDirPath, servicePath);
311+
312+
// file should exist
313+
expect(fs.existsSync(servicePath)).toBe(true);
314+
315+
// pipeline triggers should include the relative path to the service
316+
const azureYaml: IAzurePipelinesYaml = yaml.safeLoad(
317+
fs.readFileSync(path.join(servicePath, "azure-pipelines.yaml"), "utf8")
318+
);
319+
const hasCorrectIncludes = azureYaml.trigger!.paths!.include!.includes(
320+
"./" + path.relative(randomDirPath, servicePath)
321+
);
322+
expect(hasCorrectIncludes).toBe(true);
323+
}
324+
});
325+
});

src/lib/fileutils.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import fs from "fs";
22
import yaml from "js-yaml";
3-
43
import path from "path";
54
import { promisify } from "util";
65
import { logger } from "../logger";
@@ -43,7 +42,11 @@ export const generateStarterAzurePipelinesYaml = async (
4342
relProjectPaths: [path.relative(absProjectRoot, absPackagePath)]
4443
});
4544
// Write
46-
await promisify(fs.writeFile)(azurePipelinesYamlPath, starterYaml, "utf8");
45+
await promisify(fs.writeFile)(
46+
azurePipelinesYamlPath,
47+
yaml.safeDump(starterYaml, { lineWidth: Number.MAX_SAFE_INTEGER }),
48+
"utf8"
49+
);
4750
}
4851
};
4952

@@ -56,17 +59,17 @@ const generateYamlScript = (lines: string[]): string => lines.join("\n");
5659
*
5760
* @param opts Template options to pass to the the starter yaml
5861
*/
59-
const starterAzurePipelines = async (opts: {
62+
export const starterAzurePipelines = async (opts: {
6063
relProjectPaths?: string[];
6164
vmImage?: string;
6265
branches?: string[];
63-
varGroups?: string[];
64-
}) => {
66+
variableGroups?: string[];
67+
}): Promise<IAzurePipelinesYaml> => {
6568
const {
6669
relProjectPaths = ["."],
6770
vmImage = "ubuntu-latest",
6871
branches = ["master"],
69-
varGroups = []
72+
variableGroups = []
7073
} = opts;
7174

7275
// Ensure any blank paths are turned into "./"
@@ -81,9 +84,7 @@ const starterAzurePipelines = async (opts: {
8184
branches: { include: branches },
8285
paths: { include: cleanedPaths }
8386
},
84-
variables: {
85-
group: varGroups
86-
},
87+
variables: [...variableGroups.map(group => ({ group }))],
8788
pool: {
8889
vmImage
8990
},
@@ -122,7 +123,7 @@ const starterAzurePipelines = async (opts: {
122123
};
123124
// tslint:enable: object-literal-sort-keys
124125

125-
return yaml.safeDump(starter, { lineWidth: Number.MAX_SAFE_INTEGER });
126+
return starter;
126127
};
127128

128129
/**
@@ -158,7 +159,7 @@ const manifestGenerationPipelineYaml = () => {
158159
// based on https://github.com/microsoft/bedrock/blob/master/gitops/azure-devops/ManifestGeneration.md#add-azure-pipelines-build-yaml
159160
// tslint:disable: object-literal-sort-keys
160161
// tslint:disable: no-empty
161-
const pipelineyaml: IAzurePipelinesYaml = {
162+
const pipelineYaml: IAzurePipelinesYaml = {
162163
trigger: {
163164
branches: {
164165
include: ["master"]
@@ -216,7 +217,7 @@ const manifestGenerationPipelineYaml = () => {
216217
// tslint:enable: object-literal-sort-keys
217218
// tslint:enable: no-empty
218219

219-
return yaml.safeDump(pipelineyaml, { lineWidth: Number.MAX_SAFE_INTEGER });
220+
return yaml.safeDump(pipelineYaml, { lineWidth: Number.MAX_SAFE_INTEGER });
220221
};
221222

222223
/**

src/lib/pipelines/pipelines.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
DefinitionType,
1414
YamlProcess
1515
} from "azure-devops-node-api/interfaces/BuildInterfaces";
16-
1716
import { logger } from "../../logger";
1817
import { azdoUrl } from "../azdoutil";
1918

src/types.d.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,7 @@ export interface IAzurePipelinesYaml {
7070
exclude?: string[];
7171
};
7272
};
73-
variables?: {
74-
group?: string[];
75-
};
73+
variables?: Array<{ group: string } | { name: string; value: string }>;
7674
pool?: {
7775
vmImage?: string;
7876
};

0 commit comments

Comments
 (0)