Faça upload de artefatos para o Nexus, sem Maven

102

Eu tenho um projeto não Java que produz um artefato de compilação com versão e quero carregá-lo em um repositório Nexus. Como o projeto não é Java, ele não usa Maven para compilações. E prefiro não introduzir arquivos Maven / POM apenas para colocá-los no Nexus.

Os links em blogs para a API REST do Nexus terminam em uma parede de login, sem nenhum link "criar usuário" que eu possa ver.

Então, qual é a melhor (ou qualquer maneira razoável) de fazer upload de artefatos de construção para um repositório Nexus sem Maven? "bash + curl" seria ótimo, ou até mesmo um script Python.

Adam Vandenberg
fonte
Observe, certifique-se de ter um settings.xml em ~ / .m2 com os servidores e autenticação apropriados definidos.
Adam Vandenberg,

Respostas:

98

Você está pensando em usar a linha de comando Maven para fazer upload de arquivos?

mvn deploy:deploy-file \
    -Durl=$REPO_URL \
    -DrepositoryId=$REPO_ID \
    -DgroupId=org.myorg \
    -DartifactId=myproj \
    -Dversion=1.2.3  \
    -Dpackaging=zip \
    -Dfile=myproj.zip

Isso irá gerar automaticamente o Maven POM para o artefato.

Atualizar

O artigo Sonatype a seguir afirma que o plug-in maven "deploy-file" é a solução mais fácil, mas também fornece alguns exemplos usando curl:

https://support.sonatype.com/entries/22189106-How-can-I-programatically-upload-an-artifact-into-Nexus-

Mark O'Connor
fonte
Se ao menos isso nos permitisse baixar arquivos diretamente deste zip, mas parece que não é possível se você carregá-lo desta forma.
sorin
@sorin Não é possível baixar arquivos de dentro de um zip usando o Maven. É um requisito incomum e o único gerenciador de dependência que conheço que pode fazer isso é ivy (e não é simples) veja o seguinte exemplo: stackoverflow.com/questions/3445696/…
Mark O'Connor
Instalei o Nexus para tornar tudo mais simples, mas o que diabos é isso? .. E se eu tiver algum JAR feito em casa sem conhecimento de suas dependências? Meu IDE continua reclamando da falta de * .pom. Eu esperava que o Nexus já
tivesse
66

Usando curl:

curl -v \
    -F "r=releases" \
    -F "g=com.acme.widgets" \
    -F "a=widget" \
    -F "v=0.1-1" \
    -F "p=tar.gz" \
    -F "file=@./widget-0.1-1.tar.gz" \
    -u myuser:mypassword \
    http://localhost:8081/nexus/service/local/artifact/maven/content

Você pode ver o que os parâmetros significam aqui: https://support.sonatype.com/entries/22189106-How-can-I-programatically-upload-an-artifact-into-Nexus-

Para fazer as permissões para este trabalho, eu criei uma nova função na GUI do administrador e adicionei dois privilégios a essa função: Download de artefato e Upload de artefato. A função padrão "Repo: Todos os Repositórios Maven (Controle Total)" não é suficiente. Você não encontrará isso na documentação da API REST que vem com o servidor Nexus, portanto, esses parâmetros podem mudar no futuro.

Em um problema do Sonatype JIRA , foi mencionado que eles "irão revisar a API REST (e a forma como sua documentação é gerada) em um próximo lançamento, provavelmente ainda este ano".

Ed I
fonte
Digamos que publicemos do Jenkins e permitamos que apenas os usuários do build publiquem no Nexus, como você gerencia o problema da senha simples? O Jenkins tem um plugin para o upload para que possamos usar as credenciais do Jenkins?
Jirong Hu de
8

Não há necessidade de usar esses comandos .. você pode usar diretamente a interface web do nexus para fazer o upload do seu JAR usando os parâmetros GAV.

insira a descrição da imagem aqui

Portanto, é muito simples.

Praneel PIDIKITI
fonte
24
Uma GUI não ajuda; Preciso ser capaz de fazer upload por meio de um script de linha de comando usado como parte de um processo de construção.
Adam Vandenberg
Bem, isso se traduz em uma solicitação HTTP POST, você não acha?
Yngve Sneen Lindal,
5
@YngveSneenLindal Claro, mas isso não significa que esses argumentos POST sejam uma API bem definida para uso público.
Ken Williams,
@KenWilliams Claro, eu também não disse isso. Mas eles vão funcionar e representar uma solução, é isso que quero dizer.
Yngve Sneen Lindal
Pelo menos, para nosso Sonatype Nexus ™ 2.11.1-01 eu tive que conceder o privilégio ao usuário Artifact Upload. Infelizmente, não consegui encontrar nada nos documentos que mencionasse isso ... (Edit: Entendo, Ed já indiquei isso )
Alberto
8

Você pode fazer isso ABSOLUTAMENTE sem usar nada relacionado ao MAVEN. Eu pessoalmente uso o NING HttpClient (v1.8.16, para suportar java6).

Por alguma razão, Sonatype torna incrivelmente difícil descobrir quais URLs, cabeçalhos e cargas úteis devem ser corretos; e eu tive que farejar o tráfego e adivinhar ... Existem alguns blogs / documentação pouco úteis lá, no entanto, ou é irrelevante oss.sonatype.org, ou é baseado em XML (e descobri que nem mesmo funciona). Documentação de merda da parte deles, IMHO, e esperançosamente futuros pesquisadores podem achar esta resposta útil. Muito obrigado a https://stackoverflow.com/a/33414423/2101812 por sua postagem, pois ajudou muito.

Se você liberar em algum lugar diferente oss.sonatype.org, apenas substitua por qualquer que seja o host correto.

Aqui está o código (licença CC0) que escrevi para fazer isso. Onde profileestá seu sonatype / nexus profileID (como 4364f3bbaf163) e repo(como comdorkbox-1003) são analisados ​​a partir da resposta quando você carrega seu POM / Jar inicial.

Fechar repo:

/**
 * Closes the repo and (the server) will verify everything is correct.
 * @throws IOException
 */
private static
String closeRepo(final String authInfo, final String profile, final String repo, final String nameAndVersion) throws IOException {

    String repoInfo = "{'data':{'stagedRepositoryId':'" + repo + "','description':'Closing " + nameAndVersion + "'}}";
    RequestBuilder builder = new RequestBuilder("POST");
    Request request = builder.setUrl("https://oss.sonatype.org/service/local/staging/profiles/" + profile + "/finish")
                             .addHeader("Content-Type", "application/json")
                             .addHeader("Authorization", "Basic " + authInfo)

                             .setBody(repoInfo.getBytes(OS.UTF_8))

                             .build();

    return sendHttpRequest(request);
}

Promova repo:

/**
 * Promotes (ie: release) the repo. Make sure to drop when done
 * @throws IOException
 */
private static
String promoteRepo(final String authInfo, final String profile, final String repo, final String nameAndVersion) throws IOException {

    String repoInfo = "{'data':{'stagedRepositoryId':'" + repo + "','description':'Promoting " + nameAndVersion + "'}}";
    RequestBuilder builder = new RequestBuilder("POST");
    Request request = builder.setUrl("https://oss.sonatype.org/service/local/staging/profiles/" + profile + "/promote")
                     .addHeader("Content-Type", "application/json")
                     .addHeader("Authorization", "Basic " + authInfo)

                     .setBody(repoInfo.getBytes(OS.UTF_8))

                     .build();
    return sendHttpRequest(request);
}

Abandone o repo:

/**
 * Drops the repo
 * @throws IOException
 */
private static
String dropRepo(final String authInfo, final String profile, final String repo, final String nameAndVersion) throws IOException {

    String repoInfo = "{'data':{'stagedRepositoryId':'" + repo + "','description':'Dropping " + nameAndVersion + "'}}";
    RequestBuilder builder = new RequestBuilder("POST");
    Request request = builder.setUrl("https://oss.sonatype.org/service/local/staging/profiles/" + profile + "/drop")
                     .addHeader("Content-Type", "application/json")
                     .addHeader("Authorization", "Basic " + authInfo)

                     .setBody(repoInfo.getBytes(OS.UTF_8))

                     .build();

    return sendHttpRequest(request);
}

Excluir turds de assinatura:

/**
 * Deletes the extra .asc.md5 and .asc.sh1 'turds' that show-up when you upload the signature file. And yes, 'turds' is from sonatype
 * themselves. See: https://issues.sonatype.org/browse/NEXUS-4906
 * @throws IOException
 */
private static
void deleteSignatureTurds(final String authInfo, final String repo, final String groupId_asPath, final String name,
                          final String version, final File signatureFile)
                throws IOException {

    String delURL = "https://oss.sonatype.org/service/local/repositories/" + repo + "/content/" +
                    groupId_asPath + "/" + name + "/" + version + "/" + signatureFile.getName();

    RequestBuilder builder;
    Request request;

    builder = new RequestBuilder("DELETE");
    request = builder.setUrl(delURL + ".sha1")
                     .addHeader("Authorization", "Basic " + authInfo)
                     .build();
    sendHttpRequest(request);

    builder = new RequestBuilder("DELETE");
    request = builder.setUrl(delURL + ".md5")
                     .addHeader("Authorization", "Basic " + authInfo)
                     .build();
    sendHttpRequest(request);
}

Uploads de arquivos:

    public
    String upload(final File file, final String extension, String classification) throws IOException {

        final RequestBuilder builder = new RequestBuilder("POST");
        final RequestBuilder requestBuilder = builder.setUrl(uploadURL);
        requestBuilder.addHeader("Authorization", "Basic " + authInfo)

                      .addBodyPart(new StringPart("r", repo))
                      .addBodyPart(new StringPart("g", groupId))
                      .addBodyPart(new StringPart("a", name))
                      .addBodyPart(new StringPart("v", version))
                      .addBodyPart(new StringPart("p", "jar"))
                      .addBodyPart(new StringPart("e", extension))
                      .addBodyPart(new StringPart("desc", description));


        if (classification != null) {
            requestBuilder.addBodyPart(new StringPart("c", classification));
        }

        requestBuilder.addBodyPart(new FilePart("file", file));
        final Request request = requestBuilder.build();

        return sendHttpRequest(request);
    }

EDIT1:

Como obter a atividade / status de um repo

/**
 * Gets the activity information for a repo. If there is a failure during verification/finish -- this will provide what it was.
 * @throws IOException
 */
private static
String activityForRepo(final String authInfo, final String repo) throws IOException {

    RequestBuilder builder = new RequestBuilder("GET");
    Request request = builder.setUrl("https://oss.sonatype.org/service/local/staging/repository/" + repo + "/activity")
                             .addHeader("Content-Type", "application/json")
                             .addHeader("Authorization", "Basic " + authInfo)

                             .build();

    return sendHttpRequest(request);
}
Nathan
fonte
6

As chamadas que você precisa fazer para o Nexus são chamadas de API REST.

O maven-nexus-plugin é um plugin Maven que você pode usar para fazer essas chamadas. Você pode criar um pom fictício com as propriedades necessárias e fazer essas chamadas por meio do plug-in Maven.

Algo como:

mvn -DserverAuthId=sonatype-nexus-staging -Dauto=true nexus:staging-close

Coisas presumidas:

  1. Você definiu um servidor em seu ~ / .m2 / settings.xml chamado sonatype-nexus-staging com seu usuário sonatype e senha configurados - você provavelmente já terá feito isso se estiver implantando instantâneos. Mas você pode encontrar mais informações aqui .
  2. Seu settings.xml local inclui os plug-ins nexus conforme especificado aqui .
  3. O pom.xml localizado em seu diretório atual possui as coordenadas Maven corretas em sua definição. Caso contrário, você pode especificar o groupId, o artifactId e a versão na linha de comando.
  4. O -Dauto = true desligará os prompts interativos para que você possa fazer o script.

Em última análise, tudo o que isso está fazendo é criar chamadas REST no Nexus. Há uma API Nexus REST completa, mas não tive sorte em encontrar documentação para ela que não estivesse atrás de um acesso pago. Você pode ativar o modo de depuração para o plug-in acima e descobri-lo usando -Dnexus.verboseDebug=true -X.

Você também poderia, teoricamente, ir para a IU, ativar o painel Firebug Net e observar os POSTs de / serviço e deduzir um caminho lá também.

Alex Miller
fonte
3

para quem precisa em Java, usando apache httpcomponents 4.0:

public class PostFile {
    protected HttpPost httppost ;
    protected MultipartEntity mpEntity; 
    protected File filePath;

    public PostFile(final String fullUrl, final String filePath){
        this.httppost = new HttpPost(fullUrl);
        this.filePath = new File(filePath);        
        this.mpEntity = new MultipartEntity();
    }

    public void authenticate(String user, String password){
        String encoding = new String(Base64.encodeBase64((user+":"+password).getBytes()));
        httppost.setHeader("Authorization", "Basic " + encoding);
    }
    private void addParts() throws UnsupportedEncodingException{
        mpEntity.addPart("r", new StringBody("repository id"));
        mpEntity.addPart("g", new StringBody("group id"));
        mpEntity.addPart("a", new StringBody("artifact id"));
        mpEntity.addPart("v", new StringBody("version"));
        mpEntity.addPart("p", new StringBody("packaging"));
        mpEntity.addPart("e", new StringBody("extension"));

        mpEntity.addPart("file", new FileBody(this.filePath));

    }

    public String post() throws ClientProtocolException, IOException {
        HttpClient httpclient = new DefaultHttpClient();
        httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
        addParts();
        httppost.setEntity(mpEntity);
        HttpResponse response = httpclient.execute(httppost);

        System.out.println("executing request " + httppost.getRequestLine());
        System.out.println(httppost.getEntity().getContentLength());

        HttpEntity resEntity = response.getEntity();

        String statusLine = response.getStatusLine().toString();
        System.out.println(statusLine);
        if (resEntity != null) {
            System.out.println(EntityUtils.toString(resEntity));
        }
        if (resEntity != null) {
            resEntity.consumeContent();
        }
        return statusLine;
    }
}
McMosfet
fonte
primeiro post. Eu tentei adicionar higlighting para java, mas não conseguia.
McMosfet
3

Em ruby https://github.com/RiotGames/nexus_cli Um wrapper CLI em torno de chamadas REST Sonatype Nexus.

Exemplo de uso:

nexus-cli push_artifact com.mycompany.artifacts:myartifact:tgz:1.0.0 ~/path/to/file/to/push/myartifact.tgz

A configuração é feita por meio do .nexus_cliarquivo.

url:            "http://my-nexus-server/nexus/"
repository:     "my-repository-id"
username:       "username"
password:       "password"
François
fonte
2

Você também pode usar o método de implantação direta usando curl. Você não precisa de um pom para o seu arquivo, mas ele não será gerado também, então se você quiser um, terá que carregá-lo separadamente.

Aqui está o comando:

version=1.2.3
artefact="myartefact"
repoId=yourrepository
groupId=org.myorg
REPO_URL=http://localhost:8081/nexus

curl -u nexususername:nexuspassword --upload-file filename.tgz $REPO_URL/content/repositories/$repoId/$groupId/$artefact/$version/$artefact-$version.tgz
Djidiouf
fonte
"artefato" não artefato
Ram
1

Se você precisar de uma interface de linha de comando conveniente ou API python, consulte as ferramentas de repositório

Usando-o, você pode fazer o upload do artefato para o Nexus com o comando

artifact upload foo-1.2.3.ext releases com.fooware

Para fazer funcionar, você também precisará definir algumas variáveis ​​de ambiente

export REPOSITORY_URL=https://repo.example.com
export REPOSITORY_USER=admin
export REPOSITORY_PASSWORD=mysecretpassword
Michel Samia
fonte
0

Você pode carregar manualmente os artefatos clicando no botão de upload de artefatos no servidor Nexus e fornecer as propriedades GAV necessárias para o upload (geralmente é a estrutura de arquivo para armazenar o artefato)

Jijendiran
fonte
0

Para versões recentes do Nexus OSS (> = 3.9.0)

https://support.sonatype.com/hc/en-us/articles/115006744008-How-can-I-programmatically-upload-files-into-Nexus-3-

Exemplo para as versões 3.9.0 a 3.13.0:

curl -D - -u user:pass -X POST "https://nexus.domain/nexus/service/rest/beta/components?repository=somerepo" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "raw.directory=/test/" -F "[email protected];type=application/json" -F "raw.asset1.filename=test.txt"
adrianlzt
fonte
-1

@Adam Vandenberg Para código Java para POST no Nexus. https://github.com/manbalagan/nexusuploader

public class NexusRepository implements RepoTargetFactory {

    String DIRECTORY_KEY= "raw.directory";
    String ASSET_KEY= "raw.asset1";
    String FILENAME_KEY= "raw.asset1.filename";

    String repoUrl;
    String userName;
    String password;

    @Override
    public void setRepoConfigurations(String repoUrl, String userName, String password) {
        this.repoUrl = repoUrl;
        this.userName = userName;
        this.password = password;
    }

    public String pushToRepository() {
        HttpClient httpclient = HttpClientBuilder.create().build();
        HttpPost postRequest = new HttpPost(repoUrl) ;
        String auth = userName + ":" + password;
        byte[] encodedAuth = Base64.encodeBase64(
                auth.getBytes(StandardCharsets.ISO_8859_1));
        String authHeader = "Basic " + new String(encodedAuth);
        postRequest.setHeader(HttpHeaders.AUTHORIZATION, authHeader);
        try
        {
            byte[] packageBytes = "Hello. This is my file content".getBytes();
            MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
            InputStream packageStream = new ByteArrayInputStream(packageBytes);
            InputStreamBody inputStreamBody = new InputStreamBody(packageStream, ContentType.APPLICATION_OCTET_STREAM);
            multipartEntityBuilder.addPart(DIRECTORY_KEY, new StringBody("DIRECTORY"));
            multipartEntityBuilder.addPart(FILENAME_KEY, new StringBody("MyFile.txt"));
            multipartEntityBuilder.addPart(ASSET_KEY, inputStreamBody);
            HttpEntity entity = multipartEntityBuilder.build();
            postRequest.setEntity(entity); ;

            HttpResponse response = httpclient.execute(postRequest) ;
            if (response != null)
            {
                System.out.println(response.getStatusLine().getStatusCode());
            }
        }
        catch (Exception ex)
        {
            ex.printStackTrace() ;
        }
        return null;
    }

}
Mano Anbalagan
fonte
-2

Você pode usar o curl.

version=1.2.3
artifact="artifact"
repoId=repositoryId
groupId=org/myorg
REPO_URL=http://localhost:8081/nexus

curl -u username:password --upload-file filename.tgz $REPO_URL/content/repositories/$repoId/$groupId/$artefact/$version/$artifact-$version.tgz
Scott Jones
fonte
esta resposta não está correta. Com curl, o groupId deve ser representado como org / myorg (substitua o ponto "." Por barra "/")
madduci 01 de