protected override async Task <object> RemoteExecuteAsync(IRemoteOperationExecutionContext context)
        {
            var client = new ProGetClient(this.Server, this.FeedName, this.UserName, this.Password, this, context.CancellationToken);

            try
            {
                this.LogInformation($"Pushing package {this.Name} to ProGet...");

                string path = context.ResolvePath(this.FilePath);

                this.LogDebug("Using package file: " + path);

                if (!FileEx.Exists(path))
                {
                    this.LogError(this.FilePath + " does not exist.");
                    return(null);
                }

                if (string.IsNullOrWhiteSpace(this.Name) || string.IsNullOrWhiteSpace(this.Version))
                {
                    try
                    {
                        using (var package = new UniversalPackage(path))
                        {
                            if (string.IsNullOrWhiteSpace(package.Name) || package.Version == null)
                            {
                                this.LogError("Name and Version properties are required unless pushing a package that already has those properties set.");
                                return(null);
                            }
                        }
                    }
                    catch
                    {
                        this.LogError("Name and Version properties are required unless pushing a package that already has those properties set.");
                        return(null);
                    }
                }

                using (var file = FileEx.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    var data = new ProGetPackagePushData
                    {
                        Title        = this.Title,
                        Description  = this.Description,
                        Icon         = this.Icon,
                        Dependencies = this.Dependencies?.ToArray()
                    };

                    await client.PushPackageAsync(this.Group, this.Name, this.Version, data, file);
                }
            }
            catch (ProGetException ex)
            {
                this.LogError(ex.FullMessage);
                return(null);
            }

            this.LogInformation("Package pushed.");
            return(null);
        }
        protected override async Task <object> RemoteExecuteAsync(IRemoteOperationExecutionContext context)
        {
            var client = new ProGetClient(this.Server, this.FeedName, this.UserName, this.Password, this);

            try
            {
                this.LogInformation($"Pushing package {this.Name} to ProGet...");

                string path = context.ResolvePath(this.FilePath);

                this.LogDebug("Using package file: " + path);

                if (!FileEx.Exists(path))
                {
                    this.LogError(this.FilePath + " does not exist.");
                    return(null);
                }

                using (var file = FileEx.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    var data = new ProGetPackagePushData
                    {
                        Title        = this.Title,
                        Description  = this.Description,
                        Icon         = this.Icon,
                        Dependencies = this.Dependencies?.ToArray()
                    };

                    await client.PushPackageAsync(this.Group, this.Name, this.Version, data, file).ConfigureAwait(false);
                }
            }
            catch (ProGetException ex)
            {
                this.LogError(ex.FullMessage);
                return(null);
            }

            this.LogInformation("Package pushed.");
            return(null);
        }
        public static async Task DeployAsync(IOperationExecutionContext context, IProGetPackageInstallTemplate template, ILogSink log, string installationReason, bool recordDeployment, Action <OperationProgress> setProgress = null)
        {
            var fileOps = await context.Agent.GetServiceAsync <IFileOperationsExecuter>().ConfigureAwait(false);

            var client = new ProGetClient(template.FeedUrl, template.FeedName, template.UserName, template.Password, log, context.CancellationToken);

            try
            {
                var packageId = PackageName.Parse(template.PackageName);

                log.LogInformation($"Connecting to {template.FeedUrl} to get metadata for {template.PackageName}...");
                var packageInfo = await client.GetPackageInfoAsync(packageId).ConfigureAwait(false);

                string version;

                if (string.Equals(template.PackageVersion, "latest-stable", StringComparison.OrdinalIgnoreCase))
                {
                    var stableVersions = packageInfo.versions
                                         .Select(v => UniversalPackageVersion.TryParse(v))
                                         .Where(v => string.IsNullOrEmpty(v?.Prerelease));
                    if (!stableVersions.Any())
                    {
                        log.LogError($"Package {template.PackageName} does not have any stable versions.");
                        return;
                    }

                    version = stableVersions.Max().ToString();
                    log.LogInformation($"Latest stable version of {template.PackageName} is {version}.");
                }
                else if (!string.IsNullOrEmpty(template.PackageVersion) && !string.Equals(template.PackageVersion, "latest", StringComparison.OrdinalIgnoreCase))
                {
                    if (!packageInfo.versions.Contains(template.PackageVersion, StringComparer.OrdinalIgnoreCase))
                    {
                        log.LogError($"Package {template.PackageName} does not have a version {template.PackageVersion}.");
                        return;
                    }

                    version = template.PackageVersion;
                }
                else
                {
                    version = packageInfo.latestVersion;
                    log.LogInformation($"Latest version of {template.PackageName} is {version}.");
                }

                var deployInfo     = recordDeployment ? PackageDeploymentData.Create(context, log, $"Deployed by {installationReason} operation. See the URL for more info.") : null;
                var targetRootPath = context.ResolvePath(template.TargetDirectory);
                log.LogDebug("Target path: " + targetRootPath);

                log.LogInformation("Downloading package...");
                using (var content = await client.DownloadPackageContentAsync(packageId, version, deployInfo, (position, length) => setProgress?.Invoke(new OperationProgress(length == 0 ? null : (int?)(100 * position / length), "downloading package"))).ConfigureAwait(false))
                {
                    var tempDirectoryName = fileOps.CombinePath(await fileOps.GetBaseWorkingDirectoryAsync().ConfigureAwait(false), Guid.NewGuid().ToString("N"));
                    // ensure directory exists on server
                    await fileOps.CreateDirectoryAsync(tempDirectoryName);

                    var tempZipFileName = tempDirectoryName + ".zip";

                    try
                    {
                        setProgress?.Invoke(new OperationProgress(0, "copying package to agent"));
                        using (var remote = await fileOps.OpenFileAsync(tempZipFileName, FileMode.CreateNew, FileAccess.Write).ConfigureAwait(false))
                        {
                            await content.CopyToAsync(remote, 81920, context.CancellationToken, position => setProgress?.Invoke(new OperationProgress((int)(100 * position / content.Length), "copying package to agent"))).ConfigureAwait(false);
                        }
                        setProgress?.Invoke(new OperationProgress("extracting package to temporary directory"));
                        await fileOps.ExtractZipFileAsync(tempZipFileName, tempDirectoryName, IO.FileCreationOptions.Overwrite).ConfigureAwait(false);

                        var expectedFiles       = new HashSet <string>(StringComparer.OrdinalIgnoreCase);
                        var expectedDirectories = new HashSet <string>(StringComparer.OrdinalIgnoreCase);
                        content.Position = 0;
                        using (var zip = new ZipArchive(content, ZipArchiveMode.Read, true))
                        {
                            foreach (var entry in zip.Entries)
                            {
                                // TODO: use AH.ReadZip when it is available in Otter.
                                var fullName = entry.FullName.Replace('\\', '/');
                                if (!fullName.StartsWith("package/", StringComparison.OrdinalIgnoreCase) || fullName.Length <= "package/".Length)
                                {
                                    continue;
                                }

                                if (entry.IsDirectory())
                                {
                                    expectedDirectories.Add(fullName.Substring("package/".Length).Trim('/'));
                                }
                                else
                                {
                                    expectedFiles.Add(fullName.Substring("package/".Length));
                                    var parts = fullName.Substring("package/".Length).Split('/');
                                    for (int i = 1; i < parts.Length; i++)
                                    {
                                        // Add directories that are not explicitly in the zip file.
                                        expectedDirectories.Add(string.Join("/", parts.Take(i)));
                                    }
                                }
                            }
                        }

                        var jobExec = await context.Agent.TryGetServiceAsync <IRemoteJobExecuter>().ConfigureAwait(false);

                        if (jobExec != null)
                        {
                            var job = new PackageDeploymentJob
                            {
                                DeleteExtra         = template.DeleteExtra,
                                TargetRootPath      = targetRootPath,
                                TempDirectoryName   = tempDirectoryName,
                                ExpectedDirectories = expectedDirectories.ToArray(),
                                ExpectedFiles       = expectedFiles.ToArray()
                            };

                            job.MessageLogged   += (s, e) => log.Log(e.Level, e.Message);
                            job.ProgressChanged += (s, e) => setProgress?.Invoke(e);

                            setProgress?.Invoke(new OperationProgress("starting remote job on agent"));
                            await jobExec.ExecuteJobAsync(job, context.CancellationToken).ConfigureAwait(false);
                        }
                        else
                        {
                            setProgress?.Invoke(new OperationProgress("ensuring target directory exists"));
                            await fileOps.CreateDirectoryAsync(targetRootPath).ConfigureAwait(false);

                            int index = 0;
                            if (template.DeleteExtra)
                            {
                                setProgress?.Invoke(new OperationProgress("checking existing files"));
                                var remoteFileList = await fileOps.GetFileSystemInfosAsync(targetRootPath, MaskingContext.IncludeAll).ConfigureAwait(false);

                                foreach (var file in remoteFileList)
                                {
                                    index++;
                                    setProgress?.Invoke(new OperationProgress(100 * index / remoteFileList.Count, "checking existing files"));

                                    var relativeName = file.FullName.Substring(targetRootPath.Length).Replace('\\', '/').Trim('/');
                                    if (file is SlimDirectoryInfo)
                                    {
                                        if (!expectedDirectories.Contains(relativeName))
                                        {
                                            log.LogDebug("Deleting extra directory: " + relativeName);
                                            await fileOps.DeleteDirectoryAsync(file.FullName).ConfigureAwait(false);
                                        }
                                    }
                                    else
                                    {
                                        if (!expectedFiles.Contains(relativeName))
                                        {
                                            log.LogDebug($"Deleting extra file: " + relativeName);
                                            await fileOps.DeleteFileAsync(file.FullName).ConfigureAwait(false);
                                        }
                                    }
                                }
                            }

                            index = 0;
                            foreach (var relativeName in expectedDirectories)
                            {
                                index++;
                                setProgress?.Invoke(new OperationProgress(100 * index / expectedDirectories.Count, "ensuring target subdirectories exist"));

                                await fileOps.CreateDirectoryAsync(fileOps.CombinePath(targetRootPath, relativeName)).ConfigureAwait(false);
                            }

                            index = 0;
                            foreach (var relativeName in expectedFiles)
                            {
                                var sourcePath = fileOps.CombinePath(tempDirectoryName, "package", relativeName);
                                var targetPath = fileOps.CombinePath(targetRootPath, relativeName);

                                index++;
                                setProgress?.Invoke(new OperationProgress(100 * index / expectedFiles.Count, "moving files to target directory"));

                                await fileOps.MoveFileAsync(sourcePath, targetPath, true).ConfigureAwait(false);
                            }
                        }

                        setProgress?.Invoke(new OperationProgress("cleaning temporary files"));
                    }
                    finally
                    {
                        await Task.WhenAll(
                            fileOps.DeleteFileAsync(tempZipFileName),
                            fileOps.DeleteDirectoryAsync(tempDirectoryName)
                            ).ConfigureAwait(false);
                    }
                }

                setProgress?.Invoke(new OperationProgress("recording package installation in machine registry"));
                using (var registry = await PackageRegistry.GetRegistryAsync(context.Agent, false).ConfigureAwait(false))
                {
                    var package = new RegisteredPackage
                    {
                        Group              = packageId.Group,
                        Name               = packageId.Name,
                        Version            = version,
                        InstallPath        = targetRootPath,
                        FeedUrl            = template.FeedUrl,
                        InstallationDate   = DateTimeOffset.Now.ToString("o"),
                        InstallationReason = installationReason,
                        InstalledUsing     = $"{SDK.ProductName}/{SDK.ProductVersion} (InedoCore/{Extension.Version})"
                    };

                    try
                    {
                        using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
                            using (context.CancellationToken.Register(() => cancellationTokenSource.Cancel()))
                            {
                                await registry.LockAsync(cancellationTokenSource.Token).ConfigureAwait(false);

                                await registry.RegisterPackageAsync(package, context.CancellationToken).ConfigureAwait(false);

                                // doesn't need to be in a finally because dispose will unlock if necessary, but prefer doing it asynchronously
                                await registry.UnlockAsync().ConfigureAwait(false);
                            }
                    }
                    catch (TaskCanceledException)
                    {
                        log.LogWarning("Registering the package in the machine package registry timed out.");
                    }
                }
            }
            catch (ProGetException ex)
            {
                log.LogError(ex.FullMessage);
                return;
            }

            setProgress?.Invoke(null);
            log.LogInformation("Package deployed!");
        }
예제 #4
0
        public override async Task <PersistedConfiguration> CollectAsync(IOperationCollectionContext context)
        {
            var fileOps = context.Agent.GetService <IFileOperationsExecuter>();

            var client = new ProGetClient(this.Template.FeedUrl, this.Template.FeedName, this.Template.UserName, this.Template.Password, this);

            try
            {
                var packageId = PackageName.Parse(this.Template.PackageName);

                var packageInfo = await client.GetPackageInfoAsync(packageId).ConfigureAwait(false);

                var version = new ProGetPackageVersionSpecifier(this.Template.PackageVersion).GetBestMatch(packageInfo.versions);
                if (version == null)
                {
                    this.LogError($"Package {this.Template.PackageName} does not have a version {this.Template.PackageVersion}.");
                    return(null);
                }

                this.LogInformation($"Resolved package version is {version}.");

                if (!await fileOps.DirectoryExistsAsync(this.Template.TargetDirectory).ConfigureAwait(false))
                {
                    this.LogInformation(this.Template.TargetDirectory + " does not exist.");
                    return(new ProGetPackageConfiguration
                    {
                        TargetDirectory = this.Template.TargetDirectory
                    });
                }

                var mask = new MaskingContext(this.Template.Includes, this.Template.Excludes);

                this.LogInformation(this.Template.TargetDirectory + " exists; getting remote file list...");

                var remoteFileList = await fileOps.GetFileSystemInfosAsync(this.Template.TargetDirectory, mask).ConfigureAwait(false);

                var remoteFiles = new Dictionary <string, SlimFileSystemInfo>(remoteFileList.Count, StringComparer.OrdinalIgnoreCase);

                foreach (var file in remoteFileList)
                {
                    var relativeName = file.FullName.Substring(this.Template.TargetDirectory.Length).Replace('\\', '/').Trim('/');
                    if (file is SlimDirectoryInfo)
                    {
                        relativeName += "/";
                    }

                    remoteFiles.Add(relativeName, file);
                }

                remoteFileList = null; // async GC optimization

                this.LogDebug($"{this.Template.TargetDirectory} contains {remoteFiles.Count} file system entries.");

                this.LogInformation($"Connecting to {this.Template.FeedUrl} to get metadata for {this.Template.PackageName}:{version}...");
                var versionInfo = await client.GetPackageVersionInfoAsync(packageId, version).ConfigureAwait(false);

                if (versionInfo.fileList == null)
                {
                    this.LogError("File list is unavailable for this package; it may be an orphaned entry.");
                    return(null);
                }

                this.LogDebug($"Package contains {versionInfo.fileList.Length} file system entries.");

                foreach (var entry in versionInfo.fileList)
                {
                    var relativeName = entry.name;
                    if (!mask.IsMatch(relativeName))
                    {
                        continue;
                    }

                    var file = remoteFiles.GetValueOrDefault(relativeName);
                    if (file == null)
                    {
                        this.LogInformation($"Entry {relativeName} is not present in {this.Template.TargetDirectory}.");
                        return(new ProGetPackageConfiguration
                        {
                            TargetDirectory = this.Template.TargetDirectory
                        });
                    }

                    if (!entry.name.EndsWith("/"))
                    {
                        var fileInfo = (SlimFileInfo)file;
                        if (entry.size != fileInfo.Size || entry.date != fileInfo.LastWriteTimeUtc)
                        {
                            this.LogInformation($"File {relativeName} in {this.Template.TargetDirectory} is different from file in package.");
                            this.LogDebug($"Source info: {entry.size} bytes, {entry.date} timestamp");
                            this.LogDebug($"Target info: {fileInfo.Size} bytes, {fileInfo.LastWriteTimeUtc} timestamp");
                            return(new ProGetPackageConfiguration
                            {
                                TargetDirectory = this.Template.TargetDirectory
                            });
                        }
                    }
                }

                if (this.Template.DeleteExtra)
                {
                    foreach (var name in remoteFiles.Keys)
                    {
                        if (!versionInfo.fileList.Any(entry => entry.name == name))
                        {
                            this.LogInformation($"File {name} in {this.Template.TargetDirectory} does not exist in package.");
                            return(new ProGetPackageConfiguration
                            {
                                TargetDirectory = this.Template.TargetDirectory
                            });
                        }
                    }
                }

                this.LogInformation($"All package files and directories are present in {this.Template.TargetDirectory}.");
                return(new ProGetPackageConfiguration
                {
                    Current = true,
                    TargetDirectory = this.Template.TargetDirectory
                });
            }
            catch (ProGetException ex)
            {
                this.LogError(ex.FullMessage);
                return(null);
            }
        }
예제 #5
0
        public override async Task ConfigureAsync(IOperationExecutionContext context)
        {
            var fileOps = context.Agent.GetService <IFileOperationsExecuter>();

            var client = new ProGetClient(this.Template.FeedUrl, this.Template.FeedName, this.Template.UserName, this.Template.Password, this);

            try
            {
                var packageId = PackageName.Parse(this.Template.PackageName);

                this.LogInformation($"Connecting to {this.Template.FeedUrl} to get metadata for {this.Template.PackageName}...");
                var packageInfo = await client.GetPackageInfoAsync(packageId).ConfigureAwait(false);

                string version;

                if (!string.IsNullOrEmpty(this.Template.PackageVersion) && !string.Equals(this.Template.PackageVersion, "latest", StringComparison.OrdinalIgnoreCase))
                {
                    if (!packageInfo.versions.Contains(this.Template.PackageVersion, StringComparer.OrdinalIgnoreCase))
                    {
                        this.LogError($"Package {this.Template.PackageName} does not have a version {this.Template.PackageVersion}.");
                        return;
                    }

                    version = this.Template.PackageVersion;
                }
                else
                {
                    version = packageInfo.latestVersion;
                    this.LogInformation($"Latest version of {this.Template.PackageName} is {version}.");
                }

                var deployInfo = PackageDeploymentData.Create(context, this, "Deployed by Ensure-Package operation, see URL for more info.");

                this.LogInformation("Downloading package...");
                using (var zip = await client.DownloadPackageAsync(packageId, version, deployInfo).ConfigureAwait(false))
                {
                    var dirsCreated = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

                    await fileOps.CreateDirectoryAsync(this.Template.TargetDirectory).ConfigureAwait(false);

                    dirsCreated.Add(this.Template.TargetDirectory);

                    foreach (var entry in zip.Entries)
                    {
                        if (!entry.FullName.StartsWith("package/", StringComparison.OrdinalIgnoreCase) || entry.Length <= "package/".Length)
                        {
                            continue;
                        }

                        var relativeName = entry.FullName.Substring("package/".Length);

                        var targetPath = fileOps.CombinePath(this.Template.TargetDirectory, relativeName);
                        if (relativeName.EndsWith("/"))
                        {
                            if (dirsCreated.Add(targetPath))
                            {
                                await fileOps.CreateDirectoryAsync(targetPath).ConfigureAwait(false);
                            }
                        }
                        else
                        {
                            var dir = PathEx.GetDirectoryName(targetPath);
                            if (dirsCreated.Add(dir))
                            {
                                await fileOps.CreateDirectoryAsync(dir);
                            }

                            using (var targetStream = await fileOps.OpenFileAsync(targetPath, FileMode.Create, FileAccess.Write).ConfigureAwait(false))
                                using (var sourceStream = entry.Open())
                                {
                                    await sourceStream.CopyToAsync(targetStream).ConfigureAwait(false);
                                }

                            await fileOps.SetLastWriteTimeAsync(targetPath, entry.LastWriteTime.DateTime).ConfigureAwait(false);
                        }
                    }
                }
            }
            catch (ProGetException ex)
            {
                this.LogError(ex.FullMessage);
                return;
            }

            this.LogInformation("Package deployed!");
        }
        public override async Task ExecuteAsync(IOperationExecutionContext context)
        {
            var fileOps = context.Agent.GetService <IFileOperationsExecuter>();

            var client = new ProGetClient(this.Server, this.FeedName, this.UserName, this.Password, this);

            try
            {
                var packageId = ProGet.PackageName.Parse(this.PackageName);

                this.LogInformation($"Connecting to {this.Server} to get metadata for {this.PackageName}...");
                var packageInfo = await client.GetPackageInfoAsync(packageId).ConfigureAwait(false);

                var version = new ProGetPackageVersionSpecifier(this.PackageVersion).GetBestMatch(packageInfo.versions);
                if (version == null)
                {
                    this.LogError($"Package {this.PackageName} does not have a version {this.PackageVersion}.");
                    return;
                }

                this.LogInformation($"Resolved package version is {version}.");

                var deployInfo = PackageDeploymentData.Create(context, this, "Deployed by Get-Package operation, see URL for more info.");

                this.LogInformation("Downloading package...");
                using (var zip = await client.DownloadPackageAsync(packageId, version, deployInfo).ConfigureAwait(false))
                {
                    var dirsCreated = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

                    this.LogDebug("Creating directory: " + this.TargetDirectory);
                    await fileOps.CreateDirectoryAsync(this.TargetDirectory).ConfigureAwait(false);

                    dirsCreated.Add(this.TargetDirectory);

                    foreach (var entry in zip.Entries)
                    {
                        if (!entry.FullName.StartsWith("package/", StringComparison.OrdinalIgnoreCase) || entry.Length <= "package/".Length)
                        {
                            continue;
                        }

                        var relativeName = entry.FullName.Substring("package/".Length);

                        var targetPath = fileOps.CombinePath(this.TargetDirectory, relativeName);
                        if (relativeName.EndsWith("/"))
                        {
                            if (dirsCreated.Add(targetPath))
                            {
                                await fileOps.CreateDirectoryAsync(targetPath).ConfigureAwait(false);
                            }
                        }
                        else
                        {
                            var dir = PathEx.GetDirectoryName(targetPath);
                            if (dirsCreated.Add(dir))
                            {
                                await fileOps.CreateDirectoryAsync(dir).ConfigureAwait(false);
                            }

                            using (var targetStream = await fileOps.OpenFileAsync(targetPath, FileMode.Create, FileAccess.Write).ConfigureAwait(false))
                                using (var sourceStream = entry.Open())
                                {
                                    await sourceStream.CopyToAsync(targetStream).ConfigureAwait(false);
                                }

                            // timestamp in zip file is stored as UTC by convention
                            await fileOps.SetLastWriteTimeAsync(targetPath, entry.LastWriteTime.DateTime).ConfigureAwait(false);
                        }
                    }
                }
            }
            catch (ProGetException ex)
            {
                this.LogError(ex.FullMessage);
                return;
            }

            this.LogInformation("Package deployed!");
        }