Plugins multiplataforma do NuGet

No NuGet 4.8+ foi adicionado suporte para plugins multiplataforma. Isso foi conseguido com a construção de um novo modelo de extensibilidade de plugins, que tem que estar em conformidade com um conjunto rigoroso de regras de operação. Os plugins são executáveis independentes (executáveis no universo .NET Core), que os Clientes NuGet iniciam em um processo separado. Esse é um plugin verdadeiro de gravação única, executado em todos os lugares. Ele funcionará com todas as ferramentas de cliente NuGet. Os plugins podem ser .NET Framework (NuGet.exe, MSBuild.exe e Visual Studio) ou .NET Core (dotnet.exe). Um protocolo de comunicação versionado entre o cliente NuGet e o plugin é definido. Durante o handshake de inicialização, os 2 processos negociam a versão do protocolo.

Para cobrir todos os cenários de ferramentas de cliente NuGet, seria necessário um .NET Framework e um plugin do .NET Core. Segue abaixo a descrição das combinações cliente/estrutura dos plugins.

Ferramenta de cliente Estrutura
Visual Studio .NET Framework
dotnet.exe .NET Core
NuGet.exe .NET Framework
MSBuild.exe .NET Framework
NuGet.exe no Mono .NET Framework

Como funciona

O fluxo de trabalho de alto nível pode ser descrito da seguinte maneira:

  1. O NuGet descobre plugins disponíveis.
  2. Quando aplicável, o NuGet iterará sobre os plugins em ordem de prioridade e os iniciará um a um.
  3. O NuGet usará o primeiro plugin que pode atender a solicitação.
  4. Os plugins serão encerrados quando não forem mais necessários.

Requisitos gerais do plugin

A versão de protocolo atual é 2.0.0. Nesta versão, os requisitos são os seguintes:

  • Tenha um assembly de assinatura Authenticode válido e confiável que será executado no Windows e no Mono. Ainda não há nenhum requisito de confiança especial para assemblies executados em Linux e Mac. Problema relevante
  • Suporte a inicialização sem monitoração de estado no contexto de segurança atual das ferramentas de cliente NuGet. Por exemplo, as ferramentas de cliente NuGet não executarão elevação ou inicialização adicional fora do protocolo de plugin descrito posteriormente.
  • Não ser interativo, a menos que explicitamente especificado.
  • Aderir à versão negociada do protocolo de plugin.
  • Responde a todas as solicitações dentro de um prazo razoável.
  • Honrar os pedidos de cancelamento para qualquer operação em andamento.

A especificação técnica é descrita em mais detalhes nas seguintes especificações:

Cliente - Interação de Plugin

As ferramentas de cliente NuGet e os plugins se comunicam com JSON por meio de fluxos padrão (stdin, stdout, stderr). Todos os dados devem ser codificados em UTF-8. Os plugins são iniciados com o argumento "-Plugin". No caso de um usuário iniciar diretamente um executável de plugin sem esse argumento, o plugin pode dar uma mensagem informativa em vez de esperar por um handshake de protocolo. O tempo limite de handshake do protocolo é de 5 segundos. O plugin deve concluir a configuração no menor número possível. As ferramentas de cliente NuGet consultarão as operações compatíveis com um plugin passando o índice de serviço para uma origem NuGet. Um plugin pode usar o índice de serviço para verificar a presença de tipos de serviço compatíveis.

A comunicação entre as ferramentas do cliente NuGet e o plugin é bidirecional. Cada solicitação tem um tempo limite de 5 segundos. Se as operações devem levar mais tempo, o respectivo processo deve enviar uma mensagem de progresso para evitar que a solicitação atinja o tempo limite. Após 1 minuto de inatividade, um plugin é considerado ocioso e é desligado.

Instalação e descoberta de plugins

Os plugins serão descobertos por meio de uma estrutura de diretórios baseada em convenções. Cenários de CI/CD e usuários avançados podem usar variáveis de ambiente para substituir o comportamento. Ao usar variáveis de ambiente, somente caminhos absolutos são permitidos. Observe que NUGET_NETFX_PLUGIN_PATHS e NUGET_NETCORE_PLUGIN_PATHS só estão disponíveis com a versão 5.3+ das ferramentas NuGet e posterior.

  • NUGET_NETFX_PLUGIN_PATHS - define os plug-ins que serão usados pelas ferramentas baseadas no .NET Framework (NuGet.exe/MSBuild.exe/Visual Studio). Tem precedência sobre NUGET_PLUGIN_PATHS. (Somente NuGet versão 5.3+)
  • NUGET_NETCORE_PLUGIN_PATHS - define os plugins que serão usados pelas ferramentas baseadas no .NET Core (dotnet.exe). Tem precedência sobre NUGET_PLUGIN_PATHS. (Somente NuGet versão 5.3+)
  • NUGET_PLUGIN_PATHS - define os plugins que serão utilizados para aquele processo NuGet, prioridade preservada. Se essa variável de ambiente estiver definida, ela substituirá a descoberta baseada em convenção. Ignorado se uma das variáveis específicas da estrutura for especificada.
  • Local do usuário, o local de Início do NuGet no %UserProfile%/.nuget/plugins. Este local não pode ser substituído. Um diretório raiz diferente será usado para plug-ins do .NET Core e do .NET Framework.
Estrutura Local de descoberta raiz
.NET Core %UserProfile%/.nuget/plugins/netcore
.NET Framework %UserProfile%/.nuget/plugins/netfx

Cada plugin deve ser instalado em sua própria pasta. O ponto de entrada do plugin será o nome da pasta instalada, com as extensões .dll para .NET Core e .exe extensão para .NET Framework.

.nuget
    plugins
        netfx
            myPlugin
                myPlugin.exe
                nuget.protocol.dll
                ...
        netcore
            myPlugin
                myPlugin.dll
                nuget.protocol.dll
                ...

Observação

Atualmente não há nenhuma história de usuário para a instalação dos plugins. É tão simples quanto mover os arquivos necessários para o local predeterminado.

Operações com suporte

Duas operações são compatíveis com o novo protocolo de plug-in.

Nome da operação Versão mínima do protocolo Versão mínima do cliente NuGet
Baixar Pacote 1.0.0 4.3.0
Autenticação 2.0.0 4.8.0

Executar plug-ins no runtime correto

Para o NuGet em cenários dotnet.exe, os plug-ins precisam ser capazes de executar sob esse runtime específico do dotnet.exe. Cabe ao provedor de plugins e ao consumidor garantir que uma combinação dotnet.exe/plugin compatível seja usada. Um problema potencial pode surgir com os plug-ins de localização do usuário quando, por exemplo, um dotnet.exe sob o runtime 2.0 tenta usar um plug-in escrito para o runtime 2.1.

Recursos em cache

A verificação de segurança e instanciação dos plugins é dispendiosa. A operação de download acontece com muito mais frequência do que a operação de autenticação, no entanto, o usuário médio do NuGet provavelmente só terá um plugin de autenticação. Para melhorar a experiência, o NuGet armazenará em cache as declarações de operação para a solicitação fornecida. Esse cache é por plugin com a chave do plugin sendo o caminho do plugin, e a expiração para esse cache de recursos é de 30 dias.

O cache está localizado em %LocalAppData%/NuGet/plugins-cache e ser substituído pela variável de ambiente NUGET_PLUGINS_CACHE_PATH. Para limpar esse cache, é possível executar o comando locals com a opção plugins-cache. A all opção locais agora também excluirá o cache de plugins.

Índice de mensagens de protocolo

Mensagens da versão 1.0.0 do protocolo:

  1. Fechar

    • Direção da solicitação: NuGet -> plugin
    • A solicitação não conterá carga útil
    • Nenhuma resposta é esperada. A resposta adequada é que o processo de plugin saia imediatamente.
  2. Copiar arquivos no pacote

    • Direção da solicitação: NuGet -> plugin
    • A solicitação conterá:
      • a ID e a versão do pacote
      • o local do repositório de origem do pacote
      • caminho do diretório de destino
      • um enumerável de arquivos no pacote a ser copiado para o caminho do diretório de destino
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação
      • Um enumerável de caminhos completos para arquivos copiados no diretório de destino se a operação foi bem-sucedida
  3. Copiar arquivo de pacote (.nupkg)

    • Direção da solicitação: NuGet -> plugin
    • A solicitação conterá:
      • a ID e a versão do pacote
      • o local do repositório de origem do pacote
      • o caminho para o arquivo de destino
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação
  4. Obter credenciais

    • Direção da solicitação: plugin -> NuGet
    • A solicitação conterá:
      • o local do repositório de origem do pacote
      • o código de status HTTP obtido do repositório de origem do pacote usando credenciais atuais
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação
      • um nome de usuário, se disponível
      • uma senha, se disponível
  5. Obter Arquivos no pacote

    • Direção da solicitação: NuGet -> plugin
    • A solicitação conterá:
      • a ID e a versão do pacote
      • o local do repositório de origem do pacote
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação
      • um enumerável de caminhos de arquivo no pacote se a operação foi bem-sucedida
  6. Obter reclamações de operação

    • Direção da solicitação: NuGet -> plugin
    • A solicitação conterá:
      • o serviço index.json para uma origem de pacote
      • o local do repositório de origem do pacote
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação
      • Um enumerável de operações compatíveis (por exemplo: download de pacotes) se a operação foi bem-sucedida. Se um plugin não for compatível com o código-fonte do pacote, o plug-in deverá retornar um conjunto vazio de operações compatíveis.

Observação

Esta mensagem foi atualizada na versão 2.0.0. Cabe ao cliente preservar a compatibilidade com versões anteriores.

  1. Obter hash de pacote

    • Direção da solicitação: NuGet -> plugin
    • A solicitação conterá:
      • a ID e a versão do pacote
      • o local do repositório de origem do pacote
      • o algoritmo de hash
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação
      • um hash de arquivo de pacote usando o algoritmo de hash solicitado se a operação foi bem-sucedida
  2. Obter versões de pacote

    • Direção da solicitação: NuGet -> plugin
    • A solicitação conterá:
      • a ID do pacote
      • o local do repositório de origem do pacote
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação
      • um enumerável de versões de pacote se a operação foi bem-sucedida
  3. Obter índice de serviço

    • Direção da solicitação: plugin -> NuGet
    • A solicitação conterá:
      • o local do repositório de origem do pacote
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação
      • o índice de serviço se a operação tiver sido bem-sucedida
  4. Aperto de mãos

    • Direção da solicitação: NuGet <-> plugin
    • A solicitação conterá:
      • A versão atual do protocolo de plugin
      • A versão mínima compatível do protocolo de plug-in
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação
      • a versão do protocolo negociado se a operação foi bem-sucedida. Uma falha resultará no encerramento do plugin.
  5. Inicializar

    • Direção da solicitação: NuGet -> plugin
    • A solicitação conterá:
      • a versão da ferramenta de cliente NuGet
      • a linguagem eficaz da ferramenta de cliente NuGet. Isso leva em consideração a configuração ForceEnglishOutput, se usada.
      • o tempo limite de solicitação padrão, que substitui o padrão do protocolo.
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação. Uma falha resultará no encerramento do plugin.
  6. Log

    • Direção da solicitação: plugin -> NuGet
    • A solicitação conterá:
      • o nível de log da solicitação
      • uma mensagem para o log
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação.
  7. Monitorar a saída do processo NuGet

    • Direção da solicitação: NuGet -> plugin
    • A solicitação conterá:
      • a ID do processo NuGet
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação.
  8. Pacote de pré-busca

    • Direção da solicitação: NuGet -> plugin
    • A solicitação conterá:
      • a ID e a versão do pacote
      • o local do repositório de origem do pacote
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação
  9. Definir as credenciais

    • Direção da solicitação: NuGet -> plugin
    • A solicitação conterá:
      • o local do repositório de origem do pacote
      • o último nome de usuário de origem do pacote conhecido, se disponível
      • a última senha de origem do pacote conhecida, se disponível
      • o último nome de usuário de proxy conhecido, se disponível
      • a última senha de proxy conhecida, se disponível
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação
  10. Definir Nível do log

    • Direção da solicitação: NuGet -> plugin
    • A solicitação conterá:
      • o nível de log padrão
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação

Mensagens da versão 2.0.0 do protocolo

  1. Obter Reclamações de Operação
  • Direção da solicitação: NuGet -> plugin

    • A solicitação conterá:
      • o serviço index.json para uma origem de pacote
      • o local do repositório de origem do pacote
    • Uma resposta conterá:
      • um código de resposta que indique o resultado da operação
      • Um enumerável de operações compatíveis se a operação foi bem-sucedida. Se um plugin não for compatível com o código-fonte do pacote, o plug-in deverá retornar um conjunto vazio de operações compatíveis.

    Se o índice de serviço e a origem do pacote forem nulos, o plugin poderá responder com autenticação.

  1. Obter Credenciais de autenticação
  • Direção da solicitação: NuGet -> plugin
  • A solicitação conterá:
    • Uri
    • IsRetry
    • NonInteractive
    • CanShowDialog
  • Uma resposta conterá
    • Nome de usuário
    • Senha
    • Mensagem
    • Lista de tipos de autenticação
    • MessageResponseCode