Carica artefatti su Nexus, senza Maven


102

Ho un progetto non Java che produce un artefatto di build con versione e desidero caricarlo in un repository Nexus. Poiché il progetto non è Java, non utilizza Maven per le build. E preferirei non introdurre file Maven / POM solo per caricare i file in Nexus.

I collegamenti sui blog all'API REST di Nexus finiscono tutti su un muro di accesso, senza alcun collegamento "crea utente" che posso vedere.

Quindi, qual è il modo migliore (o ragionevole) per caricare artefatti di build su un repository Nexus senza Maven? "bash + curl" sarebbe fantastico, o anche uno script Python.


Nota, assicurati di avere un settings.xml in ~ / .m2 con i server appropriati e l'autenticazione definiti.
Adam Vandenberg,

Risposte:


98

Hai pensato di utilizzare la riga di comando di Maven per caricare i file?

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

Questo genererà automaticamente il Maven POM per l'artefatto.

Aggiornare

Il seguente articolo di Sonatype afferma che il plugin maven "deploy-file" è la soluzione più semplice, ma fornisce anche alcuni esempi usando curl:

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


Se solo questo ci permettesse di scaricare i file direttamente da questo zip, ma sembra che non sia possibile se lo carichi in questo modo.
sorin

@sorin Non è possibile scaricare file dall'interno in uno zip utilizzando Maven. È un requisito insolito e l'unico gestore delle dipendenze che so che può farlo è ivy (e non è semplice) vedi il seguente esempio: stackoverflow.com/questions/3445696/…
Mark O'Connor

Ho installato Nexus per rendere tutto più semplice, ma che diavolo è questo? .. E se avessi qualche JAR fatto in casa senza conoscere le sue dipendenze? Il mio IDE continua a lamentarsi della mancanza di * .pom. Speravo che Nexus lo gestisse già per me, ma NOOOOO sh ...
vintproykt

66

Utilizzando 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

Puoi vedere cosa significano i parametri qui: https://support.sonatype.com/entries/22189106-How-can-I-programatically-upload-an-artifact-into-Nexus-

Per fare in modo che i permessi per questo lavoro, ho creato un nuovo ruolo nella GUI di amministrazione e ho aggiunto due privilegi a quel ruolo: Artifact Download e Artifact Upload. Il ruolo standard "Repo: All Maven Repositories (Full Control)" non è sufficiente. Non lo troverai nella documentazione dell'API REST fornita in bundle con il server Nexus, quindi questi parametri potrebbero cambiare in futuro.

Su un numero di Sonatype JIRA , è stato detto che "stanno per revisionare l'API REST (e il modo in cui viene generata la documentazione) in una prossima versione, molto probabilmente entro la fine dell'anno".


diciamo che pubblichiamo da Jenkins e consentiamo solo agli utenti della build di pubblicare su Nexus, come gestisci il problema della semplice password? Jenkins ha un plug-in per il caricamento in modo da poter utilizzare le credenziali di Jenkins?
Jirong Hu

8

Non c'è bisogno di usare questi comandi .. puoi usare direttamente l'interfaccia web di nexus per caricare il tuo JAR usando i parametri GAV.

inserisci qui la descrizione dell'immagine

Quindi è molto semplice.


24
Una GUI non aiuta; Devo essere in grado di caricare tramite uno script della riga di comando utilizzato come parte di un processo di compilazione.
Adam Vandenberg

Bene, si traduce in una richiesta HTTP POST, non credi?
Yngve Sneen Lindal

5
@YngveSneenLindal Certo, ma ciò non significa che gli argomenti POST siano un'API ben definita da utilizzare pubblicamente.
Ken Williams

@KenWilliams Certo, non ho affermato neanche questo. Ma funzioneranno e rappresenteranno una soluzione, questo è il mio punto.
Yngve Sneen Lindal

Almeno, per il nostro Sonatype Nexus ™ 2.11.1-01 ho dovuto concedere il privilegio all'utente Artifact Upload. Sfortunatamente, non sono riuscito a trovare nulla nei documenti che menzionano questo ... (Modifica: vedo, Ed l'ho già fatto notare )
Alberto

8

Puoi ASSOLUTAMENTE farlo senza utilizzare nulla relativo a MAVEN. Personalmente uso NING HttpClient (v1.8.16, per supportare java6).

Per qualsiasi motivo, Sonatype rende incredibilmente difficile capire quali dovrebbero essere gli URL, le intestazioni e i payload corretti; e ho dovuto annusare il traffico e indovinare ... Ci sono alcuni blog / documentazione a malapena utili lì, tuttavia è irrilevante oss.sonatype.orgo è basato su XML (e ho scoperto che non funziona nemmeno). Documentazione di merda da parte loro, IMHO, e si spera che i futuri ricercatori possano trovare utile questa risposta. Mille grazie a https://stackoverflow.com/a/33414423/2101812 per il loro post, in quanto ha aiutato molto.

Se rilasci qualcosa di diverso da oss.sonatype.org, sostituiscilo con qualunque sia l'host corretto.

Ecco il codice (con licenza CC0) che ho scritto per farlo. Dov'è il profiletuo sonatype / nexus profileID (come 4364f3bbaf163) e repo(come comdorkbox-1003) vengono analizzati dalla risposta quando carichi il tuo POM / Jar iniziale.

Chiudi 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);
}

Promuovi 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);
}

Elimina 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);
}

Elimina gli stronzi delle firme:

/**
 * 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);
}

Caricamenti di file:

    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:

Come ottenere l'attività / lo stato di un 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);
}

6

Le chiamate che devi effettuare contro Nexus sono chiamate API REST.

Il plug-in maven-nexus è un plug-in Maven che puoi utilizzare per effettuare queste chiamate. Puoi creare un finto pom con le proprietà necessarie ed effettuare quelle chiamate tramite il plugin Maven.

Qualcosa di simile a:

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

Cose presunte:

  1. Hai definito un server nel tuo ~ / .m2 / settings.xml chiamato sonatype-nexus-staging con il tuo utente sonatype e la password impostati - probabilmente lo avrai già fatto se stai distribuendo snapshot. Ma puoi trovare maggiori informazioni qui .
  2. Il file settings.xml locale include i plug-in nexus come specificato qui .
  3. Il pom.xml che si trova nella directory corrente ha le coordinate Maven corrette nella sua definizione. In caso contrario, è possibile specificare groupId, artifactId e la versione sulla riga di comando.
  4. -Dauto = true disattiverà i prompt interattivi in ​​modo da poterlo scrivere in uno script.

In definitiva, tutto ciò che sta facendo è creare chiamate REST in Nexus. Esiste un'API Nexus REST completa, ma ho avuto poca fortuna nel trovare la documentazione che non sia dietro un paywall. Puoi attivare la modalità di debug per il plug-in sopra e capirlo comunque utilizzando -Dnexus.verboseDebug=true -X.

Potresti anche teoricamente entrare nell'interfaccia utente, attivare il pannello Firebug Net e guardare POST / service e dedurre un percorso anche lì.


3

per chi ne ha bisogno in Java, utilizzando 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;
    }
}

primo post. Ho provato ad aggiungere higlighting per Java ma non sono riuscito a capirlo.
McMosfet

3

In ruby https://github.com/RiotGames/nexus_cli Un wrapper CLI per le chiamate REST di Sonatype Nexus.

Esempio di utilizzo:

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

La configurazione viene eseguita tramite il .nexus_clifile.

url:            "http://my-nexus-server/nexus/"
repository:     "my-repository-id"
username:       "username"
password:       "password"

2

È inoltre possibile utilizzare il metodo di distribuzione diretta utilizzando curl. Non hai bisogno di un pom per il tuo file ma non verrà generato così se ne vuoi uno, dovrai caricarlo separatamente.

Ecco il 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

"artefatto" non artefatto
Ram

1

Se hai bisogno di una comoda interfaccia a riga di comando o API Python, guarda repositorytools

Usandolo, puoi caricare artefatto su nexus con il comando

artifact upload foo-1.2.3.ext releases com.fooware

Per farlo funzionare, dovrai anche impostare alcune variabili d'ambiente

export REPOSITORY_URL=https://repo.example.com
export REPOSITORY_USER=admin
export REPOSITORY_PASSWORD=mysecretpassword

0

Puoi caricare manualmente gli artefatti facendo clic sul pulsante di caricamento degli artefatti nel server Nexus e fornire le proprietà GAV necessarie per il caricamento (è generalmente la struttura del file per memorizzare l'artefatto)


0

Per le versioni recenti di Nexus OSS (> = 3.9.0)

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

Esempio per le versioni da 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 "raw.asset1=@test.txt;type=application/json" -F "raw.asset1.filename=test.txt"

-1

@Adam Vandenberg Per il codice Java da POST a 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;
    }

}

-2

Puoi invece usare 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

questa risposta non è corretta. Con curl, il groupId dovrebbe essere rappresentato come org / myorg (sostituisci il punto "." Con una barra "/")
madduci
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.