public override async Task ExecuteAsync(IOperationExecutionContext context)
        {
            await this.LoginAsync(context, this.ContainerSource);

            try
            {
                var procExec = await context.Agent.GetServiceAsync <IRemoteProcessExecuter>();

                var fileOps = await context.Agent.GetServiceAsync <IFileOperationsExecuter>();

                await fileOps.CreateDirectoryAsync(context.WorkingDirectory);

                var sourcePath = context.ResolvePath(this.SourceDirectory);
                await fileOps.CreateDirectoryAsync(sourcePath);

                var dockerfilePath = fileOps.CombinePath(sourcePath, "Dockerfile");

                if (!string.IsNullOrWhiteSpace(this.DockerfileTemplate))
                {
                    var item = SDK.GetRaftItem(RaftItemType.TextTemplate, this.DockerfileTemplate, context);
                    if (item == null)
                    {
                        this.LogError($"Text template \"{this.DockerfileTemplate}\" not found.");
                        return;
                    }

                    var dockerfileText = await context.ApplyTextTemplateAsync(item.Content, this.TemplateArguments != null?new Dictionary <string, RuntimeValue>(this.TemplateArguments) : null);

                    await fileOps.WriteAllTextAsync(dockerfilePath, dockerfileText, InedoLib.UTF8Encoding);
                }

                var containerSource = (ContainerSource)SecureResource.Create(this.ContainerSource, (IResourceResolutionContext)context);
                var containerId     = new ContainerId(this.ContainerSource, containerSource?.RegistryPrefix, this.RepositoryName, this.Tag);

                var escapeArg = GetEscapeArg(context);
                var args      = $"build --force-rm --progress=plain --tag={escapeArg(containerId.FullName)} {this.AdditionalArguments} {escapeArg(sourcePath)}";
                this.LogDebug("Executing docker " + args);

                var startInfo = new RemoteProcessStartInfo
                {
                    FileName         = this.DockerExePath,
                    Arguments        = args,
                    WorkingDirectory = sourcePath
                };

                using (var process = procExec.CreateProcess(startInfo))
                {
                    process.OutputDataReceived += (s, e) => this.LogInformation(e.Data);
                    process.ErrorDataReceived  += (s, e) => this.LogBuildError(context, e.Data);

                    process.Start();
                    await process.WaitAsync(context.CancellationToken);

                    if (process.ExitCode != 0)
                    {
                        this.LogError($"exit code: {process.ExitCode ?? -1}");
                        return;
                    }
                }

                var digest = await this.ExecuteGetDigest(context, containerId.FullName);

                containerId = containerId.WithDigest(digest);

                if (!string.IsNullOrEmpty(this.ContainerSource))
                {
                    await this.PushAsync(context, containerId);

                    if (this.RemoveAfterPush)
                    {
                        this.LogDebug("Removing local image after successful push...");
                        await this.RemoveAsync(context, containerId);
                    }
                }

                if (this.AttachToBuild)
                {
                    await this.AttachToBuildAsync(context, containerId);
                }
            }
            finally
            {
                await this.LogoutAsync(context, this.ContainerSource);
            }
        }
        public override async Task ExecuteAsync(IOperationExecutionContext context)
        {
            var template = await getTemplateAsync().ConfigureAwait(false);

            this.LogDebug("Detecting newlines in source template...");
            var sourceNewLines = template.Contains("\r\n") ? TemplateNewLineMode.Windows
                : template.Contains("\n") ? TemplateNewLineMode.Linux
                : TemplateNewLineMode.Auto;

            this.LogDebug("Applying template...");
            var result = await context.ApplyTextTemplateAsync(template, this.AdditionalVariables).ConfigureAwait(false);

            this.LogInformation("Template applied.");

            if (this.NewLineMode == TemplateNewLineMode.Windows)
            {
                result = result.Replace("\n", "\r\n");
            }

            if (!string.IsNullOrWhiteSpace(this.OutputFile))
            {
                var path = context.ResolvePath(this.OutputFile);
                this.LogDebug($"Writing output to {path}...");
                var fileOps = await context.Agent.GetServiceAsync <IFileOperationsExecuter>().ConfigureAwait(false);

                if (this.NewLineMode == TemplateNewLineMode.Auto)
                {
                    result = result.Replace("\n", fileOps.NewLine);
                }

                await fileOps.CreateDirectoryAsync(PathEx.GetDirectoryName(path));

                await fileOps.WriteAllTextAsync(path, result).ConfigureAwait(false);
            }

            this.LogDebug("Setting output variable...");
            this.OutputVariable = result;

            async Task <string> getTemplateAsync()
            {
                if (!string.IsNullOrEmpty(this.Literal))
                {
                    this.LogDebug("Applying literal template: " + this.Literal);
                    return(this.Literal);
                }

                if (!string.IsNullOrEmpty(this.InputFile))
                {
                    var path = context.ResolvePath(this.InputFile);
                    this.LogDebug($"Using file {path} as template...");
                    var fileOps = await context.Agent.GetServiceAsync <IFileOperationsExecuter>().ConfigureAwait(false);

                    if (!await fileOps.FileExistsAsync(path).ConfigureAwait(false))
                    {
                        throw new ExecutionFailureException("Template file not found.");
                    }

                    return(await fileOps.ReadAllTextAsync(path).ConfigureAwait(false));
                }

                if (!string.IsNullOrEmpty(this.Asset))
                {
                    string templateName;
                    string raftName;

                    var templateNameParts = this.Asset.Split(new[] { "::" }, 2, StringSplitOptions.None);
                    if (templateNameParts.Length == 2)
                    {
                        raftName     = templateNameParts[0];
                        templateName = templateNameParts[1];
                    }
                    else
                    {
                        if (SDK.ProductName == "BuildMaster")
                        {
                            raftName = context.TryGetFunctionValue("$ApplicationName")?.AsString() ?? "";
                        }
                        else
                        {
                            raftName = RaftRepository.DefaultName;
                        }

                        templateName = templateNameParts[0];
                    }

                    using (var raft = RaftRepository.OpenRaft(raftName))
                    {
                        if (raft == null)
                        {
                            throw new ExecutionFailureException("Raft not found.");
                        }

                        using (var stream = await raft.OpenRaftItemAsync(RaftItemType.TextTemplate, templateName, FileMode.Open, FileAccess.Read))
                        {
                            if (stream == null)
                            {
                                throw new ExecutionFailureException($"Template \"{templateName}\" not found in raft.");
                            }

                            this.LogDebug("Loading template from raft...");
                            using (var reader = new StreamReader(stream, InedoLib.UTF8Encoding))
                            {
                                return(await reader.ReadToEndAsync().ConfigureAwait(false));
                            }
                        }
                    }
                }

                this.LogWarning("No template specified. Setting output to empty string.");
                return(string.Empty);
            }
        }