-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- It depends on [email protected] as oppsed to [email protected] used in docker-common
- Loading branch information
Showing
25 changed files
with
5,437 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
"use strict"; | ||
import * as tl from "azure-pipelines-task-lib/task"; | ||
import * as fs from 'fs'; | ||
|
||
export function hasRegistryComponent(imageName: string): boolean { | ||
var periodIndex = imageName.indexOf("."), | ||
colonIndex = imageName.indexOf(":"), | ||
slashIndex = imageName.indexOf("/"); | ||
return ((periodIndex > 0 && periodIndex < slashIndex) || | ||
(colonIndex > 0 && colonIndex < slashIndex)); | ||
} | ||
|
||
export function imageNameWithoutTag(imageName: string): string { | ||
var endIndex = 0; | ||
if (hasRegistryComponent(imageName)) { | ||
// Contains a registry component that may include ":", so omit | ||
// this part of the name from the main delimiter determination | ||
endIndex = imageName.indexOf("/"); | ||
} | ||
endIndex = imageName.indexOf(":", endIndex); | ||
return generateValidImageName(endIndex < 0 ? imageName : imageName.substr(0, endIndex)); | ||
} | ||
|
||
export function generateValidImageName(imageName: string): string { | ||
imageName = imageName.toLowerCase(); | ||
imageName = imageName.replace(/ /g,""); | ||
return imageName; | ||
} | ||
|
||
export function getBaseImageNameFromDockerFile(dockerFilePath: string): string { | ||
const dockerFileContent = fs.readFileSync(dockerFilePath, 'utf-8').toString(); | ||
return getBaseImageName(dockerFileContent); | ||
} | ||
|
||
export function getBaseImageName(contents: string): string { | ||
var lines = contents.split(/[\r?\n]/); | ||
var i; | ||
for (i = 0; i < lines.length; i++) { | ||
var index = lines[i].toUpperCase().indexOf("FROM"); | ||
if (index != -1) { | ||
var rest = lines[i].substring(index + 4); | ||
var imageName = rest.trim(); | ||
return imageName; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
export function getResourceName(image: string, digest: string) { | ||
var match = image.match(/^(?:([^\/]+)\/)?(?:([^\/]+)\/)?([^@:\/]+)(?:[@:](.+))?$/); | ||
if (!match) { | ||
return null; | ||
} | ||
|
||
var registry = match[1]; | ||
var namespace = match[2]; | ||
var repository = match[3]; | ||
var tag = match[4]; | ||
|
||
if (!namespace && registry && !/[:.]/.test(registry)) { | ||
namespace = registry | ||
registry = 'docker.io' | ||
} | ||
|
||
if (!namespace && !registry) { | ||
registry = 'docker.io' | ||
namespace = 'library' | ||
} | ||
|
||
registry = registry ? registry + '/' : ''; | ||
namespace = namespace ? namespace + '/' : ''; | ||
|
||
return "https://" + registry + namespace + repository + "@sha256:" + digest; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
"use strict"; | ||
|
||
import * as tl from "azure-pipelines-task-lib/task"; | ||
import * as Q from "q"; | ||
import ContainerConnection from "./containerconnection"; | ||
import * as pipelineUtils from "./pipelineutils"; | ||
|
||
const matchPatternForSize = new RegExp(/[\d\.]+/); | ||
|
||
export function build(connection: ContainerConnection, dockerFile: string, context: string, commandArguments: string, labelArguments: string[], tagArguments: string[], onCommandOut: (output) => any): any { | ||
var command = connection.createCommand(); | ||
|
||
command.arg("build"); | ||
command.arg(["-f", dockerFile]); | ||
|
||
if (labelArguments) { | ||
labelArguments.forEach(label => { | ||
command.arg(["--label", label]); | ||
}); | ||
} | ||
|
||
command.line(commandArguments); | ||
|
||
if (tagArguments) { | ||
tagArguments.forEach(tagArgument => { | ||
command.arg(["-t", tagArgument]); | ||
}); | ||
} | ||
|
||
command.arg(context); | ||
|
||
// setup variable to store the command output | ||
let output = ""; | ||
command.on("stdout", data => { | ||
output += data; | ||
}); | ||
|
||
return connection.execCommand(command).then(() => { | ||
// Return the std output of the command by calling the delegate | ||
onCommandOut(output); | ||
}); | ||
} | ||
|
||
export function command(connection: ContainerConnection, dockerCommand: string, commandArguments: string, onCommandOut: (output) => any): any { | ||
let command = connection.createCommand(); | ||
command.arg(dockerCommand); | ||
command.line(commandArguments); | ||
|
||
// setup variable to store the command output | ||
let output = ""; | ||
command.on("stdout", data => { | ||
output += data; | ||
}); | ||
|
||
return connection.execCommand(command).then(() => { | ||
// Return the std output of the command by calling the delegate | ||
onCommandOut(output); | ||
}); | ||
} | ||
|
||
export function push(connection: ContainerConnection, image: string, commandArguments: string, onCommandOut: (image, output) => any): any { | ||
var command = connection.createCommand(); | ||
command.arg("push"); | ||
command.arg(image); | ||
command.line(commandArguments); | ||
|
||
// setup variable to store the command output | ||
let output = ""; | ||
command.on("stdout", data => { | ||
output += data; | ||
}); | ||
|
||
return connection.execCommand(command).then(() => { | ||
// Return the std output of the command by calling the delegate | ||
onCommandOut(image, output + "\n"); | ||
}); | ||
} | ||
|
||
export function getCommandArguments(args: string): string { | ||
return args ? args.replace(/\n/g, " ") : ""; | ||
} | ||
|
||
export async function getLayers(connection: ContainerConnection, imageId: string): Promise<any> { | ||
var layers = []; | ||
var history = await getHistory(connection, imageId); | ||
if (!history) { | ||
return null; | ||
} | ||
|
||
var lines = history.split(/[\r?\n]/); | ||
|
||
lines.forEach(line => { | ||
line = line.trim(); | ||
if (line.length != 0) { | ||
layers.push(parseHistory(line)); | ||
} | ||
}); | ||
|
||
return layers.reverse(); | ||
} | ||
|
||
export function getImageSize(layers: { [key: string]: string }[]): string { | ||
let imageSize = 0; | ||
for (const layer of layers) { | ||
for (let key in layer) { | ||
if (key.toLowerCase() === "size") { | ||
const layerSize = extractSizeInBytes(layer[key]); | ||
imageSize += layerSize; | ||
} | ||
} | ||
} | ||
|
||
return imageSize.toString() + "B"; | ||
} | ||
|
||
export function extractSizeInBytes(size: string): number { | ||
const sizeStringValue = size.match(matchPatternForSize); | ||
if (sizeStringValue && sizeStringValue.length > 0) { | ||
const sizeIntValue = parseFloat(sizeStringValue[0]); | ||
const sizeUnit = size.substring(sizeIntValue.toString().length); | ||
switch (sizeUnit.toLowerCase()) { | ||
case "b": return sizeIntValue; | ||
case "kb": return sizeIntValue * 1024; | ||
case "mb": return sizeIntValue * 1024 * 1024; | ||
case "gb": return sizeIntValue * 1024 * 1024 * 1024; | ||
case "tb": return sizeIntValue * 1024 * 1024 * 1024 * 1024; | ||
case "pb": return sizeIntValue * 1024 * 1024 * 1024 * 1024 * 1024; | ||
} | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
function parseHistory(input: string) { | ||
const NOP = '#(nop)'; | ||
let directive = "UNSPECIFIED"; | ||
let argument = ""; | ||
let index: number = input.indexOf(NOP); | ||
const createdByMatch = "; createdBy:"; | ||
const indexCreatedBy = input.indexOf(createdByMatch); | ||
if (index != -1) { | ||
argument = input.substr(index + 6).trim(); | ||
directive = argument.substr(0, argument.indexOf(' ')); | ||
argument = argument.substr(argument.indexOf(' ') + 1).trim(); | ||
} | ||
else { | ||
directive = 'RUN'; | ||
argument = input.substring(indexCreatedBy + createdByMatch.length, input.length - 1); | ||
} | ||
|
||
let createdAt: string = ""; | ||
let layerSize: string = ""; | ||
const createdAtMatch = "createdAt:"; | ||
const layerSizeMatch = "; layerSize:"; | ||
const indexCreatedAt = input.indexOf(createdAtMatch); | ||
const indexLayerSize = input.indexOf(layerSizeMatch); | ||
if (indexCreatedAt >= 0 && indexLayerSize >= 0) { | ||
createdAt = input.substring(indexCreatedAt + createdAtMatch.length, indexLayerSize); | ||
layerSize = input.substring(indexLayerSize + layerSizeMatch.length, indexCreatedBy); | ||
} | ||
|
||
return { "directive": directive, "arguments": argument, "createdOn": createdAt, "size": layerSize }; | ||
} | ||
|
||
async function getHistory(connection: ContainerConnection, image: string): Promise<string> { | ||
var command = connection.createCommand(); | ||
command.arg("history"); | ||
command.arg(["--format", "createdAt:{{.CreatedAt}}; layerSize:{{.Size}}; createdBy:{{.CreatedBy}}"]); | ||
command.arg("--no-trunc"); | ||
command.arg(image); | ||
|
||
const defer = Q.defer(); | ||
// setup variable to store the command output | ||
let output = ""; | ||
command.on("stdout", data => { | ||
output += data; | ||
}); | ||
|
||
try { | ||
connection.execCommand(command).then(() => { | ||
defer.resolve(); | ||
}); | ||
} | ||
catch (e) { | ||
// Swallow any exceptions encountered in executing command | ||
// such as --format flag not supported in old docker cli versions | ||
output = null; | ||
defer.resolve(); | ||
tl.warning("Not publishing to image meta data store as get history failed with error " + e); | ||
} | ||
|
||
await defer.promise; | ||
return output; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
"use strict"; | ||
|
||
import * as del from "del"; | ||
import * as fs from "fs"; | ||
import * as path from "path"; | ||
import * as url from "url"; | ||
import * as tl from "azure-pipelines-task-lib/task"; | ||
|
||
// http://www.daveeddy.com/2013/03/26/synchronous-file-io-in-nodejs/ | ||
// We needed a true Sync file write for config file | ||
export function writeFileSync(filePath: string, data: string): number { | ||
try | ||
{ | ||
const fd = fs.openSync(filePath, 'w'); | ||
var bitesWritten = fs.writeSync(fd, data); | ||
fs.fsyncSync(fd); | ||
tl.debug(tl.loc("FileContentSynced", data)); | ||
fs.closeSync(fd); | ||
return bitesWritten; | ||
} catch(e) | ||
{ | ||
tl.error(tl.loc('CantWriteDataToFile', filePath, e)); | ||
throw e; | ||
} | ||
} | ||
|
||
export function findDockerFile(dockerfilepath: string) : string { | ||
if (dockerfilepath.indexOf('*') >= 0 || dockerfilepath.indexOf('?') >= 0) { | ||
tl.debug(tl.loc('ContainerPatternFound')); | ||
let workingDirectory = tl.getVariable('System.DefaultWorkingDirectory'); | ||
let allFiles = tl.find(workingDirectory); | ||
let matchingResultsFiles = tl.match(allFiles, dockerfilepath, workingDirectory, { matchBase: true }); | ||
|
||
if (!matchingResultsFiles || matchingResultsFiles.length == 0) { | ||
throw new Error(tl.loc('ContainerDockerFileNotFound', dockerfilepath)); | ||
} | ||
|
||
return matchingResultsFiles[0]; | ||
} | ||
else | ||
{ | ||
tl.debug(tl.loc('ContainerPatternNotFound')); | ||
return dockerfilepath; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
"use strict"; | ||
|
||
import * as cp from "child_process"; | ||
import * as tl from "azure-pipelines-task-lib/task"; | ||
|
||
export function tagsAt(commit: string): string[] { | ||
var git = tl.which("git", true); | ||
var args = ["tag", "--points-at", commit]; | ||
var gitDir = tl.getVariable("Build.Repository.LocalPath"); | ||
console.log("[command]" + git + " " + args.join(" ")); | ||
var result = (cp.execFileSync(git, args, { | ||
encoding: "utf8", | ||
cwd: gitDir | ||
}) as string).trim(); | ||
console.log(result); | ||
return result.length ? result.split("\n") : []; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Type definitions for del v2.2.0 | ||
// Project: https://github.com/sindresorhus/del | ||
// Definitions by: Asana <https://asana.com>, Aya Morisawa <https://github.com/AyaMorisawa> | ||
// Definitions: https://github.com/borisyankov/DefinitelyTyped | ||
|
||
/// <reference path="../glob/glob.d.ts"/> | ||
|
||
declare module "del" { | ||
import glob = require("glob"); | ||
|
||
function Del(pattern: string): Promise<string[]>; | ||
function Del(pattern: string, options: Del.Options): Promise<string[]>; | ||
|
||
function Del(patterns: string[]): Promise<string[]>; | ||
function Del(patterns: string[], options: Del.Options): Promise<string[]>; | ||
|
||
module Del { | ||
function sync(pattern: string, options?: Options): string[]; | ||
function sync(patterns: string[], options?: Options): string[]; | ||
|
||
interface Options extends glob.IOptions { | ||
force?: boolean; | ||
dryRun?: boolean; | ||
} | ||
} | ||
|
||
export = Del; | ||
} |
Oops, something went wrong.