UNPKG

generator-tfs

Version:

Generates an app with CI/CD in Team Foundation Server

203 lines (183 loc) 6.88 kB
// This is the code that deals with TFS const fs = require('fs'); const async = require('async'); const request = require('request'); const util = require('../app/utility'); const RELEASE_API_VERSION = '3.0-preview.3'; function run(args, gen, done) { 'use strict'; var build = {}; var approverId; var queueId = 0; var azureSub = { name: args.azureSub }; var teamProject = {}; var azureEndpoint = {}; var approverUniqueName; var approverDisplayName; var dockerEndpoint = {}; var dockerRegistryEndpoint = {}; var token = util.encodePat(args.pat); async.series([ function (mainSeries) { util.findProject(args.tfs, args.project, token, gen, function (err, tp) { teamProject = tp; mainSeries(err, tp); }); }, function (mainSeries) { async.parallel([ function (inParallel) { util.findQueue(args.queue, args.tfs, teamProject, token, function (err, id) { queueId = id; inParallel(err, id); }); }, function (inParallel) { util.findDockerServiceEndpoint(args.tfs, teamProject.id, args.dockerHost, token, gen, function (err, ep) { dockerEndpoint = ep; inParallel(err, dockerEndpoint); }); }, function (inParallel) { util.findDockerRegistryServiceEndpoint(args.tfs, teamProject.id, args.dockerRegistry, token, function (err, ep) { dockerRegistryEndpoint = ep; inParallel(err, dockerRegistryEndpoint); }); }, function (inParallel) { util.findBuild(args.tfs, teamProject, token, args.target, function (err, bld) { build = bld; approverId = bld.authoredBy.id; approverUniqueName = bld.authoredBy.uniqueName; approverDisplayName = bld.authoredBy.displayName; inParallel(err, bld); }); }, function (inParallel) { if (args.target === `paas`) { util.findAzureServiceEndpoint(args.tfs, teamProject.id, azureSub, token, gen, function (err, ep) { azureEndpoint = ep; inParallel(err, azureEndpoint); }); } else { azureEndpoint = undefined; inParallel(null, undefined); } } ], mainSeries); }, function (mainSeries) { var relArgs = { token: token, build: build, queueId: queueId, account: args.tfs, target: args.target, appName: args.appName, approverId: approverId, teamProject: teamProject, template: args.releaseJson, endpoint: azureEndpoint ? azureEndpoint.id : null, dockerHostEndpoint: dockerEndpoint, approverUniqueName: approverUniqueName, dockerRegistryId: args.dockerRegistryId, approverDisplayName: approverDisplayName, dockerRegistryEndpoint: dockerRegistryEndpoint, dockerPorts: args.dockerPorts }; findOrCreateRelease(relArgs, gen, function (err, rel) { mainSeries(err, rel); }); } ], function (err, results) { // This is just for test and will be undefined during normal use if (done) { done(err); } if (err) { // To get the stacktrace run with the --debug built-in option when // running the generator. gen.env.error(err.message); } }); } function findOrCreateRelease(args, gen, callback) { 'use strict'; util.tryFindRelease(args, function (e, rel) { if (e) { callback(e); } if (!rel) { createRelease(args, gen, callback); } else { gen.log(`+ Found release definition`); callback(e, rel); } }); } function createRelease(args, gen, callback) { 'use strict'; gen.log(`+ Creating CD release definition`); // Load the template and replace values. var tokens = { '{{BuildId}}': args.build.id, '"{{QueueId}}"': args.queueId, '{{WebAppName}}': args.appName, '{{BuildName}}': args.build.name, '{{ApproverId}}': args.approverId, '{{ProjectId}}': args.teamProject.id, '{{ConnectedServiceID}}': args.endpoint, '{{ProjectName}}': args.teamProject.name, '{{ApproverDisplayName}}': args.approverDisplayName, '{{ProjectLowerCase}}': args.teamProject.name.toLowerCase(), '{{dockerPorts}}': args.dockerPorts ? args.dockerPorts : null, '{{ApproverUniqueName}}': args.approverUniqueName.replace("\\", "\\\\"), '{{dockerHostEndpoint}}': args.dockerHostEndpoint ? args.dockerHostEndpoint.id : null, '{{dockerRegistryId}}': args.dockerRegistryId ? args.dockerRegistryId.toLowerCase() : null, '{{dockerRegistryEndpoint}}': args.dockerRegistryEndpoint ? args.dockerRegistryEndpoint.id : null, '{{ReleaseDefName}}': args.target === 'docker' ? `${args.teamProject.name}-Docker-CD` : `${args.teamProject.name}-CD` }; var contents = fs.readFileSync(args.template, 'utf8'); var options = { method: 'POST', headers: { 'cache-control': 'no-cache', 'content-type': 'application/json', 'authorization': `Basic ${args.token}` }, json: true, url: `${args.account}/${args.teamProject.name}/_apis/release/definitions`, qs: { 'api-version': RELEASE_API_VERSION }, body: JSON.parse(util.tokenize(contents, tokens)) }; // I have witnessed the release returning a 403 if you try // to create it too quickly. The release REST API appears // to return 403 for several reasons and could cause an // infinite loop on this code waiting on RM to become ready. var status = ''; async.whilst( function () { return status !== 'failed' && status !== 'succeeded'; }, function (finished) { request(options, function (err, resp, body) { if (resp.statusCode == 400) { status = "failed"; finished(new Error("x " + resp.body.message), null); } else if (resp.statusCode >= 300) { status = "in progress"; finished(err, null); } else { status = "succeeded"; finished(err, body); } }); }, function (err, results) { callback(err, results); } ); } module.exports = { // Exports the portions of the file we want to share with files that require // it. run: run, findOrCreateRelease: findOrCreateRelease };