Przesyłaj artefakty do Nexusa bez Mavena

102

Mam projekt inny niż Java, który tworzy wersjonowany artefakt kompilacji i chcę przesłać go do repozytorium Nexusa. Ponieważ projekt nie jest Javą, nie używa Mavena do kompilacji. I wolałbym nie wprowadzać plików Maven / POM tylko po to, aby przenieść je do Nexusa.

Wszystkie linki na blogach do Nexus REST API kończą się na tablicy logowania, bez linku „utwórz użytkownika”, który widzę.

Jaki jest więc najlepszy (lub rozsądny) sposób przesyłania artefaktów kompilacji do repozytorium Nexusa bez Mavena? „bash + curl” byłby świetny, a nawet skrypt w Pythonie.

Adam Vandenberg
źródło
Uwaga, upewnij się, że masz plik settings.xml w ~ / .m2 z odpowiednimi serwerami i zdefiniowaną autoryzacją.
Adam Vandenberg

Odpowiedzi:

98

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 POM Mavena dla artefaktu.

Aktualizacja

Poniższy artykuł w Sonatype stwierdza, że ​​wtyczka maven "deploy-file" jest najłatwiejszym rozwiązaniem, ale zawiera również kilka przykładów użycia curl:

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

Mark O'Connor
źródło
Gdyby tylko to pozwoliło nam pobrać pliki bezpośrednio z tego pliku zip, ale wydaje się, że nie jest to możliwe, jeśli prześlesz go w ten sposób.
sorin
@sorin Nie można pobierać plików z poziomu pliku zip za pomocą Maven. To niezwykłe wymaganie, a jedynym menedżerem zależności, którego znam, który może to zrobić, jest bluszcz (i to nie jest proste). Zobacz następujący przykład: stackoverflow.com/questions/3445696/…
Mark O'Connor
Zainstalowałem Nexusa, żeby wszystko było prostsze, ale co to u licha jest? .. Co jeśli mam JARa domowej roboty bez wiedzy o jego zależnościach? Moje IDE ciągle narzeka na brak * .pom. Miałem nadzieję, że Nexus już to załatwił, ale NOOOOO sh ...
vintproykt
66

Korzystanie z 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 tutaj: https://support.sonatype.com/entries/22189106-How-can-I-programatically-upload-an-artifact-into-Nexus-

Aby nadać uprawnienia do tej pracy, utworzyłem nową rolę w GUI administratora i dodałem do tej roli dwa uprawnienia: Pobieranie artefaktów i Przesyłanie artefaktów. Standardowa rola „Repo: All Maven Repositories (Full Control)” nie wystarczy. Nie znajdziesz tego w dokumentacji interfejsu API REST, która jest dostarczana z serwerem Nexus, więc te parametry mogą ulec zmianie w przyszłości.

W kwestii Sonatype JIRA wspomniano, że w nadchodzącej wersji, najprawdopodobniej jeszcze w tym roku, „zamierzają odnowić REST API (i sposób generowania jego dokumentacji)”.

Ed I
źródło
powiedzmy, że publikujemy z Jenkinsa i zezwalamy tylko użytkownikom kompilacji na publikowanie w Nexusie, jak radzisz sobie z problemem zwykłego hasła? Czy Jenkins ma wtyczkę do przesyłania, abyśmy mogli użyć poświadczeń Jenkins?
Jirong Hu
8

Nie ma potrzeby używania tych poleceń ... możesz bezpośrednio użyć interfejsu sieciowego Nexus, aby przesłać swój plik JAR przy użyciu parametrów GAV.

wprowadź opis obrazu tutaj

Więc to jest bardzo proste.

Praneel PIDIKITI
źródło
24
GUI nie pomaga; Muszę mieć możliwość przesyłania za pomocą skryptu wiersza polecenia używanego jako część procesu kompilacji.
Adam Vandenberg
Cóż, tłumaczy się to na żądanie HTTP POST, nie sądzisz?
Yngve Sneen Lindal
5
@YngveSneenLindal Oczywiście, ale to nie znaczy, że te argumenty POST są dobrze zdefiniowanym interfejsem API do użytku publicznego.
Ken Williams
@KenWilliams Jasne, też tego nie twierdziłem. Ale będą działać i stanowić rozwiązanie, o to mi chodzi.
Yngve Sneen Lindal
Przynajmniej w przypadku naszego Sonatype Nexus ™ 2.11.1-01 musiałem przyznać użytkownikowi ten przywilej Artifact Upload. Niestety, nie mogłem znaleźć niczego w dokumentach wspominających o tym ... (Edycja: widzę, Ed już to wskazałem )
Alberto
8

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

Z jakiegoś powodu Sonatype sprawia, że niezwykle trudno jest ustalić, jakie powinny być prawidłowe adresy URL, nagłówki i ładunki; i musiałem wąchać ruch i zgadywać ... Jest tam kilka mało przydatnych blogów / dokumentacji, jednak albo są one nieistotne oss.sonatype.org, albo są oparte na XML (i okazało się, że nawet nie działają). Bzdura z ich strony, IMHO i miejmy nadzieję, że przyszli poszukiwacze mogą uznać tę odpowiedź za przydatną. Wielkie dzięki dla https://stackoverflow.com/a/33414423/2101812 za ich post, ponieważ bardzo pomógł.

Jeśli wydasz gdzieś indziej niż oss.sonatype.org, po prostu zastąp go jakimkolwiek właściwym hostem.

Oto kod (na licencji CC0), który napisałem, aby to osiągnąć. Gdzie profileznajduje się identyfikator profilu sonatype / nexusa (taki jak 4364f3bbaf163) i repo(taki jak comdorkbox-1003) są analizowane z odpowiedzi, gdy przesyłasz początkowy POM / słoik.

Zamknij repozytorium:

/**
 * 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 repozytorium:

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

Upuść repozytorium:

/**
 * 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ń sygnaturki:

/**
 * 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łane pliki:

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

EDYCJA1:

Jak uzyskać aktywność / status repozytorium

/**
 * 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
źródło
6

Wywołania, które musisz wykonać na Nexusie, to wywołania REST API.

Wtyczka maven-nexus-plugin to wtyczka Maven, której możesz użyć do wykonywania tych połączeń. Możesz stworzyć fałszywy pom z niezbędnymi właściwościami i wykonywać te wywołania za pomocą wtyczki Maven.

Coś jak:

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

Zakładane rzeczy:

  1. Zdefiniowałeś serwer w swoim ~ / .m2 / settings.xml o nazwie sonatype-nexus-staging z ustawionym użytkownikiem sonatype i hasłem - prawdopodobnie już to zrobiłeś, jeśli wdrażasz migawki. Ale możesz znaleźć więcej informacji tutaj .
  2. Twój lokalny plik settings.xml zawiera wtyczki Nexusa określone tutaj .
  3. Plik pom.xml znajdujący się w bieżącym katalogu ma w definicji poprawne współrzędne Mavena. Jeśli nie, możesz określić groupId, artifactId i wersję w wierszu poleceń.
  4. -Dauto = true wyłączy interaktywne monity, abyś mógł to skryptować.

Ostatecznie wszystko to robi, to tworzenie wywołań REST w Nexusie. Istnieje pełny interfejs API Nexus REST, ale nie miałem szczęścia w znalezieniu dokumentacji, która nie jest za zaporą. Możesz włączyć tryb debugowania dla powyższej wtyczki i dowiedzieć się, jak to zrobić, używając -Dnexus.verboseDebug=true -X.

Możesz również teoretycznie przejść do interfejsu użytkownika, włączyć panel Firebug Net i obserwować / obsługiwać posty POST i również tam wydedukować ścieżkę.

Alex Miller
źródło
3

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;
    }
}
McMosfet
źródło
pierwszy post. Próbowałem dodać wyróżnienie dla java, ale nie udało mi się.
McMosfet
3

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

Przykład użycia:

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

Konfiguracja odbywa się za pośrednictwem .nexus_clipliku.

url:            "http://my-nexus-server/nexus/"
repository:     "my-repository-id"
username:       "username"
password:       "password"
Francois
źródło
2

Możesz również użyć metody bezpośredniego wdrażania przy użyciu narzędzia curl. Nie potrzebujesz pom do swojego pliku, ale nie zostanie on również wygenerowany, więc jeśli chcesz, musisz przesłać go 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
Djidiouf
źródło
„artefakt”, a nie artefakt
Ram,
1

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

Używając go, możesz przesłać artefakt do nexusa za pomocą polecenia

artifact upload foo-1.2.3.ext releases com.fooware

Aby to działało, będziesz musiał również ustawić kilka zmiennych środowiskowych

export REPOSITORY_URL=https://repo.example.com
export REPOSITORY_USER=admin
export REPOSITORY_PASSWORD=mysecretpassword
Michel Samia
źródło
0

Możesz ręcznie przesłać artefakt, klikając przycisk przesyłania artefaktów na serwerze Nexus i podać niezbędne właściwości GAV do przesłania (zazwyczaj jest to struktura pliku do przechowywania artefaktu)

jijendiran
źródło
0

Najnowsze wersje Nexus OSS (> = 3.9.0)

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

Przykład dla wersji od 3.9.0 do 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
źródło
-1

@Adam Vandenberg Dla kodu Java do POST na Nexusie. 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
źródło
-2

Zamiast tego możesz użyć 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
źródło
ta odpowiedź jest nieprawidłowa. W przypadku curl identyfikator groupId powinien być reprezentowany jako org / myorg (zastąp kropkę „.”
Ukośnikiem