Laden Sie Artefakte ohne Maven auf Nexus hoch

102

Ich habe ein Nicht-Java-Projekt, das ein versioniertes Build-Artefakt erzeugt, und ich möchte dieses in ein Nexus-Repository hochladen. Da das Projekt kein Java ist, wird Maven nicht für Builds verwendet. Und ich möchte lieber keine Maven / POM-Dateien einführen, nur um Dateien in Nexus zu bekommen.

Die Links in Blogs zur Nexus REST-API enden alle an einer Anmeldewand, ohne dass ich einen Link zum Erstellen eines Benutzers sehen kann.

Was ist der beste (oder vernünftigste) Weg, um Build-Artefakte ohne Maven in ein Nexus-Repository hochzuladen? "bash + curl" wäre toll oder sogar ein Python-Skript.

Adam Vandenberg
quelle
Beachten Sie, dass Sie in ~ / .m2 eine settings.xml mit den entsprechenden Servern und der definierten Authentifizierung haben.
Adam Vandenberg

Antworten:

98

Haben Sie darüber nachgedacht, die Maven-Befehlszeile zum Hochladen von Dateien zu verwenden?

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

Dadurch wird automatisch der Maven POM für das Artefakt generiert.

Aktualisieren

Der folgende Sonatype-Artikel besagt, dass das Maven-Plugin "Deploy-File" die einfachste Lösung ist, bietet aber auch einige Beispiele für die Verwendung von Curl:

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

Mark O'Connor
quelle
Wenn dies nur erlauben würde, Dateien direkt aus dieser Zip-Datei herunterzuladen, aber es scheint nicht möglich zu sein, wenn Sie es so hochladen.
Sorin
@sorin Mit Maven ist es nicht möglich, Dateien von innen in einer Zip-Datei herunterzuladen. Es ist eine ungewöhnliche Anforderung und der einzige Abhängigkeitsmanager, den ich kenne, der dies kann, ist Efeu (und es ist nicht einfach). Siehe folgendes Beispiel: stackoverflow.com/questions/3445696/…
Mark O'Connor
Ich habe Nexus installiert, um alles einfacher zu machen, aber was ist das? .. Was ist, wenn ich eine hausgemachte JAR habe, ohne die Abhängigkeiten zu kennen? Meine IDE beschwert sich immer wieder über fehlende * .pom. Ich hoffte, dass Nexus das schon für mich erledigt hat, aber NOOOOO sh ...
vintproykt
66

Curl verwenden:

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

Sie können hier sehen, was die Parameter bedeuten: https://support.sonatype.com/entries/22189106-How-can-I-programatisch-upload-an-artifact-into-Nexus-

Damit die Berechtigungen für diese Funktion funktionieren, habe ich eine neue Rolle in der Administrator-GUI erstellt und dieser Rolle zwei Berechtigungen hinzugefügt: Artefakt-Download und Artefakt-Upload. Die Standardrolle "Repo: Alle Maven-Repositorys (Vollzugriff)" - reicht nicht aus. Sie finden dies nicht in der REST-API-Dokumentation, die im Lieferumfang des Nexus-Servers enthalten ist, sodass sich diese Parameter möglicherweise in Zukunft ändern.

In einem Sonatype JIRA-Problem wurde erwähnt, dass sie "die REST-API (und die Art und Weise, wie ihre Dokumentation generiert wird) in einer kommenden Version, höchstwahrscheinlich später in diesem Jahr, überarbeiten werden".

Ed I.
quelle
Nehmen wir an, wir veröffentlichen von Jenkins aus und erlauben Build-Benutzern nur, in Nexus zu veröffentlichen. Wie verwalten Sie das Problem mit einfachen Kennwörtern? Hat Jenkins ein Plugin für den Upload, damit wir die Jenkins-Anmeldeinformationen verwenden können?
Jirong Hu
8

Sie müssen diese Befehle nicht verwenden. Sie können das Nexus-Webinterface direkt verwenden, um Ihre JAR mithilfe von GAV-Parametern hochzuladen.

Geben Sie hier die Bildbeschreibung ein

Es ist also sehr einfach.

Praneel PIDIKITI
quelle
24
Eine GUI hilft nicht; Ich muss in der Lage sein, über ein Befehlszeilenskript hochzuladen, das als Teil eines Erstellungsprozesses verwendet wird.
Adam Vandenberg
Nun, es wird zu einer HTTP-POST-Anfrage, nicht wahr?
Yngve Sneen Lindal
5
@YngveSneenLindal Sicher, aber das bedeutet nicht, dass diese POST-Argumente eine gut definierte API sind, die öffentlich verwendet werden kann.
Ken Williams
@ KenWilliams Klar, das habe ich auch nicht behauptet. Aber sie werden funktionieren und eine Lösung darstellen, das ist mein Punkt.
Yngve Sneen Lindal
Zumindest für unseren Sonatype Nexus ™ 2.11.1-01 musste ich dem Benutzer das Privileg gewähren Artifact Upload. Leider konnte ich in den Dokumenten, in denen dies erwähnt wurde , nichts finden ... (Bearbeiten: Ich verstehe, Ed, ich habe bereits darauf hingewiesen )
Alberto
8

Sie können dies ABSOLUT tun, ohne MAVEN zu verwenden. Ich persönlich benutze den NING HttpClient (v1.8.16, um Java6 zu unterstützen).

Aus irgendeinem Grund macht es Sonatype unglaublich schwierig, die richtigen URLs, Header und Nutzdaten herauszufinden. und ich musste den Verkehr schnüffeln und raten ... Es gibt einige kaum nützliche Blogs / Dokumentationen, die jedoch entweder irrelevant oss.sonatype.orgsind oder auf XML basieren (und ich fand heraus, dass sie nicht einmal funktionieren). Mist Dokumentation von ihrer Seite, IMHO, und hoffentlich zukünftige Suchende können diese Antwort nützlich finden. Vielen Dank an https://stackoverflow.com/a/33414423/2101812 für ihren Beitrag, der sehr geholfen hat.

Wenn Sie eine andere Version als veröffentlichen oss.sonatype.org, ersetzen Sie sie einfach durch den richtigen Host.

Hier ist der (CC0-lizenzierte) Code, den ich geschrieben habe, um dies zu erreichen. Wo profileist Ihr Sonatyp / Nexus-Profil? ID (wie 4364f3bbaf163) und repo(wie comdorkbox-1003) werden aus der Antwort analysiert, wenn Sie Ihr erstes POM / Jar hochladen.

Repo schließen:

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

Repo fördern:

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

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

Signatur-Trottel löschen:

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

Datei-Uploads:

    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:

So erhalten Sie die Aktivität / den Status für ein 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
quelle
6

Die Anrufe, die Sie gegen Nexus tätigen müssen, sind REST-API-Anrufe.

Das Maven-Nexus-Plugin ist ein Maven-Plugin, mit dem Sie diese Anrufe tätigen können. Sie können einen Dummy-Pom mit den erforderlichen Eigenschaften erstellen und diese Aufrufe über das Maven-Plugin tätigen.

Etwas wie:

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

Angenommene Dinge:

  1. Sie haben in Ihrer Datei ~ / .m2 / settings.xml einen Server mit dem Namen sonatype-nexus-staging definiert, bei dem Ihr Sonatype-Benutzer und Ihr Kennwort eingerichtet sind. Dies haben Sie wahrscheinlich bereits getan, wenn Sie Snapshots bereitstellen. Weitere Informationen finden Sie hier .
  2. Ihre lokale settings.xml enthält die hier angegebenen Nexus-Plugins .
  3. Die pom.xml in Ihrem aktuellen Verzeichnis hat die richtigen Maven-Koordinaten in ihrer Definition. Wenn nicht, können Sie die Gruppen-ID, die Artefakt-ID und die Version in der Befehlszeile angeben.
  4. Mit -Dauto = true werden die interaktiven Eingabeaufforderungen deaktiviert, sodass Sie dies per Skript ausführen können.

Letztendlich werden lediglich REST-Aufrufe in Nexus erstellt. Es gibt eine vollständige Nexus REST-API, aber ich hatte wenig Glück, eine Dokumentation dafür zu finden, die sich nicht hinter einer Paywall befindet. Sie können den Debug-Modus für das obige Plugin aktivieren und es jedoch mithilfe von herausfinden -Dnexus.verboseDebug=true -X.

Sie können theoretisch auch in die Benutzeroberfläche gehen, das Firebug Net-Bedienfeld aktivieren, POSTs suchen / warten und dort auch einen Pfad ableiten.

Alex Miller
quelle
3

für diejenigen, die es in Java benötigen, mit 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
quelle
erster Beitrag. Ich habe versucht, Higlighting für Java hinzuzufügen, aber ich habe es nicht verstanden.
McMosfet
3

In Ruby https://github.com/RiotGames/nexus_cli Ein CLI-Wrapper für Sonatype Nexus-REST-Aufrufe.

Anwendungsbeispiel:

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

Die Konfiguration erfolgt über die .nexus_cliDatei.

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

Sie können die direkte Bereitstellungsmethode auch mit curl verwenden. Sie benötigen kein POM für Ihre Datei, aber es wird auch nicht generiert. Wenn Sie eines möchten, müssen Sie es separat hochladen.

Hier ist der Befehl:

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
quelle
"Artefakt" nicht Artefakt
Ram
1

Wenn Sie eine praktische Befehlszeilenschnittstelle oder Python-API benötigen, schauen Sie sich die Repository- Tools an

Mit ihm können Sie Artefakte mit Befehl auf nexus hochladen

artifact upload foo-1.2.3.ext releases com.fooware

Damit es funktioniert, müssen Sie auch einige Umgebungsvariablen festlegen

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

Sie können die Artefakte manuell hochladen, indem Sie auf dem Nexus-Server auf die Schaltfläche Artefakte hochladen klicken und die erforderlichen GAV-Eigenschaften für das Hochladen angeben (im Allgemeinen ist dies die Dateistruktur zum Speichern des Artefakts).

Jijendiran
quelle
0

Für neuere Versionen von Nexus OSS (> = 3.9.0)

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

Beispiel für die Versionen 3.9.0 bis 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
quelle
-1

@Adam Vandenberg Für Java-Code zum POST an 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
quelle
-2

Sie können stattdessen Curl verwenden.

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
quelle
Diese Antwort ist nicht korrekt. Mit curl sollte die groupId als org / myorg dargestellt werden (ersetzen Sie den Punkt "." Durch einen Schrägstrich "/")
madduci