Skip to content

Commit e2f8a0e

Browse files
authored
fix: API returns error when job template has cache setting (#174)
* fix: merge template before cache setting parsing * feat: add tests
1 parent ff9c194 commit e2f8a0e

5 files changed

Lines changed: 149 additions & 18 deletions

File tree

lib/phase/flatten.js

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ function flattenStageSetupAndTeardownJobs(doc) {
730730
* @param {TemplateFactory} templateFactory Template Factory to get templates
731731
* @returns {Promise}
732732
*/
733-
function flattenPhase(parsedDoc, templateFactory) {
733+
async function flattenPhase(parsedDoc, templateFactory) {
734734
const doc = parsedDoc;
735735

736736
// Flatten stage setup and teardown into jobs
@@ -745,36 +745,39 @@ function flattenPhase(parsedDoc, templateFactory) {
745745
// Flatten shared into jobs
746746
doc.jobs = flattenSharedIntoJobs(parsedDoc.shared, parsedDoc.jobs);
747747

748+
// Flatten templates
749+
const { warnings, newJobs } = await flattenTemplates(doc, templateFactory);
750+
751+
// Clean through the job values
752+
const cleanedJobs = cleanComplexEnvironment(newJobs);
753+
754+
// Convert steps into proper expanded output
755+
const convertedJobs = convertSteps(cleanedJobs);
756+
757+
// Append flattened jobs and return flattened doc
758+
doc.jobs = convertedJobs;
759+
delete doc.shared;
760+
761+
// Flatten cache settings if the pipeline has cache settings
748762
if (parsedDoc.cache) {
749763
doc.jobs = flattenCacheSettings(parsedDoc.cache, doc.jobs);
750764
delete doc.cache;
751765
} else {
766+
// Some job has cache config, but there is no cache settings in the pipeline
752767
Object.keys(doc.jobs).forEach(jobName => {
753768
if (doc.jobs[jobName].cache !== undefined) {
754769
throw new Error('Cache disable/enable is set without the cache setting itself.');
755770
}
756771
});
757772
}
758773

759-
// Flatten templates
760-
return flattenTemplates(doc, templateFactory).then(({ warnings, newJobs }) => {
761-
// Clean through the job values
762-
const cleanedJobs = cleanComplexEnvironment(newJobs);
763-
// Convert steps into proper expanded output
764-
const convertedJobs = convertSteps(cleanedJobs);
774+
const errors = checkAdditionalRules(doc);
765775

766-
// Append flattened jobs and return flattened doc
767-
doc.jobs = convertedJobs;
768-
delete doc.shared;
769-
770-
const errors = checkAdditionalRules(doc);
771-
772-
if (errors) {
773-
throw new Error(errors.toString());
774-
}
776+
if (errors) {
777+
throw new Error(errors.toString());
778+
}
775779

776-
return { warnings, flattenedDoc: doc };
777-
});
780+
return { warnings, flattenedDoc: doc };
778781
}
779782

780783
module.exports = {
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{
2+
"annotations": {},
3+
"parameters": {},
4+
"jobs": {
5+
"enabled": [{
6+
"annotations": {},
7+
"image": "node:4",
8+
"commands": [
9+
{
10+
"name": "install",
11+
"command": "npm install"
12+
},
13+
{
14+
"name": "test",
15+
"command": "npm test"
16+
}
17+
],
18+
"environment": {
19+
"SD_TEMPLATE_FULLNAME": "mytemplate",
20+
"SD_TEMPLATE_NAME": "mytemplate",
21+
"SD_TEMPLATE_NAMESPACE": "",
22+
"SD_TEMPLATE_VERSION": "1.2.3"
23+
},
24+
"requires": [
25+
"~pr",
26+
"~commit"
27+
],
28+
"cache": {
29+
"pipeline": [ "pipeline-cache" ],
30+
"event": [],
31+
"job": []
32+
},
33+
"secrets": [],
34+
"settings": {},
35+
"templateId": 7754
36+
}],
37+
"disabled": [{
38+
"annotations": {},
39+
"image": "node:4",
40+
"commands": [
41+
{
42+
"name": "install",
43+
"command": "npm install"
44+
},
45+
{
46+
"name": "test",
47+
"command": "npm test"
48+
}
49+
],
50+
"environment": {
51+
"SD_TEMPLATE_FULLNAME": "mytemplate",
52+
"SD_TEMPLATE_NAME": "mytemplate",
53+
"SD_TEMPLATE_NAMESPACE": "",
54+
"SD_TEMPLATE_VERSION": "1.2.3"
55+
},
56+
"requires": [
57+
"~pr",
58+
"~commit"
59+
],
60+
"secrets": [],
61+
"settings": {},
62+
"templateId": 7754
63+
}]
64+
},
65+
"workflowGraph": {
66+
"nodes": [
67+
{ "name": "~pr" },
68+
{ "name": "~commit" },
69+
{ "name": "enabled" },
70+
{ "name": "disabled" }
71+
],
72+
"edges": [
73+
{ "src": "~pr", "dest": "enabled" },
74+
{ "src": "~commit", "dest": "enabled" },
75+
{ "src": "~pr", "dest": "disabled" },
76+
{ "src": "~commit", "dest": "disabled" }
77+
]
78+
},
79+
"subscribe": {}
80+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
cache:
2+
pipeline: [ pipeline-cache ]
3+
4+
jobs:
5+
enabled:
6+
requires: [ ~pr, ~commit ]
7+
template: TemplateCacheTestNamespace/cachetemplate@1
8+
disabled:
9+
requires: [ ~pr, ~commit ]
10+
template: TemplateCacheTestNamespace/cachetemplate@2
11+
cache: false

test/data/templateWithCache.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"id": 7754,
3+
"name": "mytemplate",
4+
"version": "1.2.3",
5+
"description": "test template",
6+
"maintainer": "bar@foo.com",
7+
"labels": [],
8+
"config": {
9+
"image": "node:4",
10+
"cache": true,
11+
"steps": [
12+
{ "install": "npm install" },
13+
{ "test": "npm test" }
14+
]
15+
}
16+
}

test/index.test.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,8 @@ describe('config parser', () => {
355355
let jobParametersTemplate;
356356
let providerTemplate;
357357
let jobWithRequiresTemplate;
358+
let jobWithCacheTemplate1;
359+
let jobWithCacheTemplate2;
358360

359361
beforeEach(() => {
360362
firstTemplate = JSON.parse(loadData('template.json'));
@@ -367,6 +369,8 @@ describe('config parser', () => {
367369
jobParametersTemplate = JSON.parse(loadData('templateWithParameters.json'));
368370
providerTemplate = JSON.parse(loadData('templateWithProvider.json'));
369371
jobWithRequiresTemplate = JSON.parse(loadData('templateWithRequires.json'));
372+
jobWithCacheTemplate1 = JSON.parse(loadData('templateWithCache.json'));
373+
jobWithCacheTemplate2 = JSON.parse(loadData('templateWithCache.json'));
370374
templateFactoryMock.getFullNameAndVersion.returns({ isVersion: true });
371375
templateFactoryMock.getTemplate.withArgs('mytemplate@1.2.3').resolves(firstTemplate);
372376
templateFactoryMock.getTemplate.withArgs('yourtemplate@2').resolves(secondTemplate);
@@ -387,6 +391,14 @@ describe('config parser', () => {
387391
templateFactoryMock.getTemplate
388392
.withArgs('JobTestNamespace/jobrequirestemplate@2')
389393
.resolves(jobWithRequiresTemplate);
394+
// Since the resolved values are the same object, their properties can be overwritten during other parsing.
395+
// We need to separate that resolved values in tests.
396+
templateFactoryMock.getTemplate
397+
.withArgs('TemplateCacheTestNamespace/cachetemplate@1')
398+
.resolves(jobWithCacheTemplate1);
399+
templateFactoryMock.getTemplate
400+
.withArgs('TemplateCacheTestNamespace/cachetemplate@2')
401+
.resolves(jobWithCacheTemplate2);
390402
});
391403

392404
it('flattens templates successfully', () =>
@@ -597,6 +609,15 @@ describe('config parser', () => {
597609
);
598610
}));
599611

612+
it('flattens cache config with templates containing parsed cache setting', () =>
613+
parser({
614+
yaml: loadData('basic-job-and-template-with-cache.yaml'),
615+
templateFactory: templateFactoryMock,
616+
triggerFactory
617+
}).then(data => {
618+
assert.deepEqual(data, JSON.parse(loadData('basic-job-and-template-with-cache.json')));
619+
}));
620+
600621
it('flattens templates with provider', () =>
601622
parser({
602623
yaml: loadData('basic-job-with-provider.yaml'),

0 commit comments

Comments
 (0)