// Copyright Keysight Technologies 2012-2019
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at http://mozilla.org/MPL/2.0/.
using OpenTap.Cli;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using OpenTap.Authentication;
namespace OpenTap.Package
{
///
/// Indicates a well defined action to be performed on a package.
/// A number of common actions are , , and .
/// Deriving from this, and annotating the class and any public properties with and attributes
/// will allow it to be called from the OpenTAP.Package CLI.
///
public abstract class PackageAction : ICliAction
{
/// Log source for PackageAction plugins.
protected static TraceSource log = OpenTap.Log.CreateSource("PackageAction");
///
/// A delegate used by
///
/// Indicates progress from 0 to 100.
///
public delegate void ProgressUpdateDelegate(int progressPercent, string message);
///
/// Called by the action to indicate how far it has gotten. Will usually be called with a progressPercent of 100 to indicate that it is done.
///
public event ProgressUpdateDelegate ProgressUpdate;
///
/// A delegate type used by the event.
///
///
public delegate void ErrorDelegate(Exception ex);
///
/// Called when a critical error happens.
///
public event ErrorDelegate Error;
///
/// Call this to raise the event.
///
///
protected void RaiseError(Exception ex)
{
if (Error != null)
Error(ex);
}
///
/// Call this to raise the event.
///
///
///
protected void RaiseProgressUpdate(int progressPercent, string message)
{
if (ProgressUpdate != null)
ProgressUpdate(progressPercent, message);
}
///
/// The code to be executed by the action.
///
/// Return 0 to indicate success. Otherwise return a custom errorcode that will be set as the exitcode from the CLI.
public abstract int Execute(CancellationToken cancellationToken);
internal string[] ExtractRepositoryTokens(string[] repositories, bool saveSettings)
{
if (repositories == null) return null;
// Repositories can have additional arguments appended as key-value-pairs.
// Currently, the only supported key is 'token='
// The goal here is the following:
// 1. Extract the tokens
// 2. Add them to AuthenticationSettings
// 3. Save the updated settings if needed and requested
// 4. Return the list of repositories without the kvps
var result = new string[repositories.Length];
bool tokenAdded = false;
for (int i = 0; i < repositories.Length; i++)
{
var argument = repositories[i];
var parts = argument.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
var repo = parts[0];
string token = null;
for (int j = 1; j < parts.Length; j++)
{
var part = parts[j];
// A user token could contain an equal sign. It should be fine if we just use the first equal
// sign as a pivot. This is also the way environment variables are handled on Unix systems.
var pivot = part.IndexOf('=');
if (pivot == -1)
{
log.Warning($"Missing '=' sign in key-value-pair '{part}'. This value will be ignored.");
continue;
}
var key = part.Substring(0, pivot);
var value = part.Substring(pivot + 1);
switch (key)
{
case "token":
token = value;
break;
default:
log.Warning(
$"Unrecognized key '{key}' specified in repository argument '{argument}'. This value will be ignored.");
break;
}
}
// Only accepts tokens for http repositories
var repoType = PackageRepositoryHelpers.DetermineRepositoryType(repo);
if (repoType is HttpPackageRepository && Uri.TryCreate(repoType.Url, UriKind.Absolute, out var uri))
{
repo = repoType.Url;
// Add the specified token to the current authentication settings
if (token != null)
{
tokenAdded = true;
// If a token is already configured for this repo, update it
if (AuthenticationSettings.Current.Tokens.FirstOrDefault(t => t.Domain.Equals(uri.Authority)) is
TokenInfo t)
{
t.AccessToken = token;
}
// Otherwise, add it
else
{
AuthenticationSettings.Current.Tokens.Add(new TokenInfo(token, null, uri.Authority));
}
}
}
result[i] = repo;
}
if (saveSettings && tokenAdded)
{
try
{
AuthenticationSettings.Current.Save();
}
catch (Exception e)
{
log.Warning($"Error saving credentials: '{e.Message}");
log.Debug(e);
}
}
return result;
}
}
internal static class PackageActionHelper
{
private readonly static TraceSource log = OpenTap.Log.CreateSource("PackageAction");
///
/// Logs the assembly name and version then executes the action with the given parameters.
///
/// The oackage action to be executed.
/// The parameters for the action.
/// Return 0 to indicate success. Otherwise return a custom errorcode that will be set as the exitcode from the CLI.
public static int Execute(this PackageAction action, string[] parameters)
{
action.LogAssemblyNameAndVersion();
ICliAction cliAction = action;
return cliAction.PerformExecute(parameters);
}
public static List FilterPreRelease(this List packages, string PreRelease)
{
if (PreRelease != null)
packages = packages.Where(p => (p.Version.PreRelease ?? "").ToLower() == (PreRelease ?? "").ToLower()).ToList();
else
{
var filteredPackages = new List();
var packageGroups = packages.GroupBy(p => p.Name);
foreach (var item in packageGroups)
{
if (item.Any(p => string.IsNullOrEmpty(p.Version.PreRelease)))
filteredPackages.AddRange(item.Where(p => string.IsNullOrEmpty(p.Version.PreRelease)));
else
filteredPackages.AddRange(item);
}
packages = filteredPackages;
}
return packages;
}
private static void LogAssemblyNameAndVersion(this PackageAction action)
{
log.Debug("{0} version {1}", typeof(Installer).Assembly.GetName().Name, typeof(Installer).Assembly.GetName().Version.ToString(3));
}
}
}