Wgraj artefakty do Nexusa bez Mavena

Mam projekt non-Java, który produkuje wersjonowany artefakt budowania, i chcę przesłać to do repozytorium Nexus. Ponieważ projekt nie jest Javą, nie używa Mavena do kompilacji. I wolałbym nie wprowadzać plików Maven/POM tylko po to, aby dostać pliki do Nexusa.

Linki na blogach do Nexus REST API wszystkie kończą się na ścianie logowania, bez" Utwórz użytkownika " link, który widzę.

Jaki jest najlepszy (lub jakikolwiek rozsądny) sposób przesłania artefaktów do repozytorium Nexusa bez Mavena? "bash + curl" byłby świetny, a nawet skrypt Pythona.

 88
Author: Adam Vandenberg, 2010-10-27

11 answers

Czy rozważasz użycie wiersza poleceń Maven do przesyłania plików?

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

To automatycznie wygeneruje Maven POM dla artefaktu.

Update

Poniższy artykuł Sonatype stwierdza, że wtyczka Mavena "deploy-file" jest najprostszym rozwiązaniem, ale zawiera również kilka przykładów za pomocą curl:

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

 93
Author: Mark O'Connor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-07 09:08:17

Używanie 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

Możesz zobaczyć, co oznaczają parametry: https://support.sonatype.com/entries/22189106-How-can-I-programatically-upload-an-artifact-into-Nexus-

Aby uzyskać uprawnienia do tej pracy, utworzyłem nową rolę w GUI administratora i dodałem dwa uprawnienia do tej roli: Artifact Download I Artifact Upload. Standard "Repo: wszystkie repozytoria Mavena (Pełna kontrola)" - rola nie wystarczy. Nie znajdziesz tego w dokumentacji REST API, która jest dostarczany z serwerem Nexus, więc parametry te mogą się zmienić w przyszłości.

W wydaniu Sonatype JIRA wspomniano, że " w nadchodzącym wydaniu, najprawdopodobniej jeszcze w tym roku, przeprowadzą przegląd REST API (i sposobu generowania dokumentacji)".

 59
Author: Ed I,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-25 22:35:35

Możesz absolutnie zrobić to bez używania czegokolwiek związanego z MAVENEM. Osobiście używam ning HttpClient (v1.8.16, do obsługi java6).

Z jakiegokolwiek powodu Sonatype sprawia, że trudno jest ustalić, jakie mają być poprawne adresy URL, nagłówki i ładunki; musiałem powąchać ruch i zgadywać... Są tam jakieśLedwo użyteczne blogi/dokumentacja, jednak albo jest nieistotne dla oss.sonatype.org, albo jest oparte na XML (I okazało się, że nawet nie działa). Gówniana dokumentacja z ich strony, IMHO, i mam nadzieję, że przyszli poszukiwacze znajdą tę odpowiedź przydatną. Wielkie podziękowania dla https://stackoverflow.com/a/33414423/2101812 za ich post, ponieważ to bardzo pomogło.

Jeśli wypuścisz coś innego niż oss.sonatype.org, po prostu zastąp go tym, czym jest właściwy host.

Oto kod (na licencji CC0), który napisałem, aby to osiągnąć. Gdzie profile jest Twoim sonatype / Nexus profileID (takim jak 4364f3bbaf163) i repo (takim jako comdorkbox-1003) są przetwarzane z odpowiedzi podczas przesyłania początkowego POM / Jar.

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

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

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

Usuń podpis:

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

Przesyłanie plików:

    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:

Jak uzyskać aktywność / status 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);
}
 8
Author: Nathan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:59

Nie trzeba używać tych poleceń .. możesz bezpośrednio użyć interfejsu internetowego nexus, aby przesłać swój JAR za pomocą parametrów GAV.

Tutaj wpisz opis obrazka

Więc jest to bardzo proste.

 7
Author: Praneel PIDIKITI,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-22 17:36:17

Wywołania, które musisz wykonać przeciwko Nexus są wywołaniami REST api.

Maven-nexus-plugin jest wtyczką Maven, której możesz użyć do wykonywania tych połączeń. Możesz utworzyć atrapę pom z niezbędnymi właściwościami i wykonywać te połączenia za pomocą wtyczki Maven.

Coś w stylu:

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

Zakładane rzeczy:

  1. zdefiniowałeś serwer w swoim~/.m2 / ustawienia.XML o nazwie sonatype-nexus-staging z ustawionym użytkownikiem sonatype i hasłem-prawdopodobnie będziesz już to zrobiłeś, jeśli wdrażasz migawki. Ale więcej informacji znajdziesz tutaj .
  2. Twoje ustawienia lokalne.xml zawiera wtyczki nexus jak określono tutaj .
  3. Pom.XML znajdujący się w bieżącym katalogu ma w swojej definicji poprawne współrzędne Mavena. Jeśli nie, możesz określić groupId, artifactId i version w wierszu poleceń.
  4. - Dauto = true wyłączy interaktywne monity, abyś mógł skryptować to.

Ostatecznie, wszystko co robi, to tworzenie wywołań REST do Nexusa. Istnieje pełne Nexus REST api, ale nie miałem szczęścia znaleźć dla niego dokumentacji, która nie jest za paywall. Możesz włączyć tryb debugowania dla powyższej wtyczki i dowiedzieć się o tym za pomocą -Dnexus.verboseDebug=true -X.

Możesz też teoretycznie wejść do interfejsu użytkownika, włączyć panel Firebug Net i obserwować posty / service i wydedukować tam ścieżkę.

 6
Author: Alex Miller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-01 22:05:49

Dla tych, którzy potrzebują go w Javie, używając 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;
    }
}
 3
Author: McMosfet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-29 12:51:25
 2
Author: mestachs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-07 11:34:26

W ruby https://github.com/RiotGames/nexus_cli owijarka CLI wokół wywołań REST Sonatype Nexus.

 1
Author: Francois,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-31 21:26:45

Można również użyć metody direct deploy przy użyciu curl. Nie potrzebujesz do tego pliku pom, ale nie zostanie on również wygenerowany, więc jeśli chcesz, musisz go przesłać osobno.

Oto polecenie:

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
 1
Author: Djidiouf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-29 01:45:02

Jeśli potrzebujesz wygodnego interfejsu wiersza poleceń lub API Pythona, spójrz na repositorytools

 0
Author: Michel Samia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-11 15:12:31

Możesz użyć curl zamiast.

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
 -2
Author: Scott Jones,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-15 15:42:52