Unggah artefak ke Nexus, tanpa Maven

102

Saya memiliki proyek non-Java yang menghasilkan artefak versi berversi, dan saya ingin mengunggah ini ke repositori Nexus. Karena proyek tersebut bukan Java, ia tidak menggunakan Maven untuk build. Dan saya lebih suka tidak memperkenalkan file Maven / POM hanya untuk memasukkan file ke Nexus.

Tautan di blog ke Nexus REST API semuanya berakhir di dinding masuk, tanpa tautan "buat pengguna" yang dapat saya lihat.

Jadi, apa cara terbaik (atau yang masuk akal) untuk mengunggah artefak bangunan ke repositori Nexus tanpa Maven? "bash + curl" akan bagus, atau bahkan skrip Python.

Adam Vandenberg
sumber
Catatan, pastikan Anda memiliki settings.xml di ~ / .m2 dengan server & autentikasi yang ditentukan.
Adam Vandenberg

Jawaban:

98

Sudahkah Anda mempertimbangkan untuk menggunakan baris perintah Maven untuk mengunggah 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

Ini secara otomatis akan menghasilkan POM Maven untuk artefak tersebut.

Memperbarui

Artikel Sonatype berikut menyatakan bahwa plugin maven "deploy-file" adalah solusi termudah, tetapi juga menyediakan beberapa contoh menggunakan curl:

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

Mark O'Connor
sumber
Andai saja ini memungkinkan kami untuk mengunduh file dari dalam zip ini secara langsung tetapi tampaknya tidak mungkin jika Anda mengunggahnya seperti ini.
sorin
@sorin Tidak mungkin mengunduh file dari dalam dalam zip menggunakan Maven. Ini adalah persyaratan yang tidak biasa dan satu-satunya manajer ketergantungan yang saya tahu yang dapat melakukannya adalah ivy (dan ini tidak sederhana) lihat contoh berikut: stackoverflow.com/questions/3445696/…
Mark O'Connor
Saya telah menginstal Nexus untuk membuat semuanya lebih sederhana, tetapi apa sebenarnya ini? .. Bagaimana jika saya memiliki beberapa JAR buatan sendiri tanpa mengetahui ketergantungannya? IDE saya terus mengeluh tentang * .pom yang hilang. Saya berharap Nexus sudah menanganinya untuk saya, tapi TAK TERBATAS ...
vintproykt
66

Menggunakan 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

Anda dapat melihat arti parameter di sini: https://support.sonatype.com/entries/22189106-How-can-I-programatically-upload-an-artifact-into-Nexus-

Agar izin untuk ini berfungsi, saya membuat peran baru di admin GUI dan saya menambahkan dua hak istimewa ke peran itu: Download Artefak dan Upload Artefak. Peran standar "Repo: All Maven Repositories (Kendali Penuh)" - tidak cukup. Anda tidak akan menemukannya di dokumentasi REST API yang disertakan dengan server Nexus, jadi parameter ini mungkin berubah di masa mendatang.

Pada masalah Sonatype JIRA , disebutkan bahwa mereka "akan merombak REST API (dan cara dokumentasinya dibuat) dalam rilis mendatang, kemungkinan besar akhir tahun ini".

Ed I
sumber
katakanlah kami menerbitkan dari Jenkins, dan hanya mengizinkan pengguna build untuk menerbitkan ke Nexus, bagaimana Anda mengelola masalah sandi biasa? Apakah Jenkins memiliki plugin untuk mengupload sehingga kita dapat menggunakan kredensial Jenkins?
Jirong Hu
8

Tidak perlu menggunakan perintah ini .. Anda dapat langsung menggunakan Antarmuka web nexus untuk mengunggah JAR Anda menggunakan parameter GAV.

masukkan deskripsi gambar di sini

Jadi sangat sederhana.

Praneel PIDIKITI
sumber
24
GUI tidak membantu; Saya harus dapat mengunggah melalui skrip baris perintah yang digunakan sebagai bagian dari proses pembuatan.
Adam Vandenberg
Nah, itu diterjemahkan menjadi permintaan HTTP POST, bukan begitu?
Yngve Sneen Lindal
5
@YngveSneenLindal Tentu, tapi itu tidak berarti argumen POST tersebut adalah API yang didefinisikan dengan baik untuk digunakan secara publik.
Ken Williams
@KenWiams Tentu, saya juga tidak mengklaim itu. Tapi mereka akan bekerja dan mewakili solusi, itulah maksud saya.
Yngve Sneen Lindal
Setidaknya, untuk Sonatype Nexus ™ 2.11.1-01 kami, saya harus memberikan hak istimewa kepada pengguna Artifact Upload. Sayangnya, saya tidak dapat menemukan apa pun di dokumen yang menyebutkan ini ... (Sunting: Begitu , Ed , saya sudah menunjukkan ini )
Alberto
8

Anda BENAR - BENAR dapat melakukan ini tanpa menggunakan apapun yang berhubungan dengan MAVEN. Saya pribadi menggunakan NING HttpClient (v1.8.16, untuk mendukung java6).

Untuk alasan apa pun, Sonatype membuatnya sangat sulit untuk mengetahui URL, header, dan payload yang benar; dan saya harus mengendus lalu lintas dan menebak ... Ada beberapa blog / dokumentasi yang hampir tidak berguna di sana, namun tidak relevan dengan oss.sonatype.org, atau berbasis XML (dan saya menemukan itu bahkan tidak berfungsi). Dokumentasi omong kosong dari pihak mereka, IMHO, dan semoga para pencari masa depan dapat menemukan jawaban ini bermanfaat. Terima kasih banyak kepada https://stackoverflow.com/a/33414423/2101812 untuk kiriman mereka, karena itu sangat membantu.

Jika Anda merilis di tempat lain selain oss.sonatype.org, ganti saja dengan host apa pun yang benar.

Berikut adalah kode (berlisensi CC0) yang saya tulis untuk melakukannya. Di mana profilesonatype / nexus profileID Anda (seperti 4364f3bbaf163) dan repo(seperti comdorkbox-1003) diurai dari respons ketika Anda mengunggah POM / Jar awal Anda.

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

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

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

Hapus kotoran tanda tangan:

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

Unggahan 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:

Cara mendapatkan aktivitas / status untuk 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
sumber
6

Panggilan yang perlu Anda lakukan terhadap Nexus adalah panggilan REST api.

Maven-nexus-plugin adalah plugin Maven yang dapat Anda gunakan untuk melakukan panggilan ini. Anda dapat membuat pom tiruan dengan properti yang diperlukan dan melakukan panggilan tersebut melalui plugin Maven.

Sesuatu seperti:

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

Hal-hal yang diasumsikan:

  1. Anda telah menentukan server di ~ / .m2 / settings.xml Anda bernama sonatype-nexus-staging dengan pengguna sonatype Anda dan penyetelan sandi - Anda mungkin sudah melakukan ini jika menerapkan snapshot. Tetapi Anda dapat menemukan info lebih lanjut di sini .
  2. Setting.xml lokal Anda menyertakan plugin nexus seperti yang ditentukan di sini .
  3. Pom.xml yang ada di direktori Anda saat ini memiliki koordinat Maven yang benar dalam definisinya. Jika tidak, Anda dapat menentukan groupId, artifactId, dan versinya pada baris perintah.
  4. The -Dauto = true akan mematikan prompt interaktif sehingga Anda dapat membuat skrip ini.

Pada akhirnya, semua yang dilakukan ini adalah membuat panggilan REST ke Nexus. Ada api Nexus REST lengkap tetapi saya kurang beruntung menemukan dokumentasi untuk itu yang tidak berada di balik paywall. Anda dapat mengaktifkan mode debug untuk plugin di atas dan mengetahuinya dengan menggunakan-Dnexus.verboseDebug=true -X .

Anda juga bisa secara teoritis masuk ke UI, menyalakan panel Firebug Net, dan melihat / service POST dan menyimpulkan jalur di sana juga.

Alex Miller
sumber
3

bagi mereka yang membutuhkannya di Java, menggunakan 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
sumber
Pos pertama. Saya sudah mencoba menambahkan higlighting untuk java tetapi tidak bisa mendapatkannya.
McMosfet
3

Di ruby https://github.com/RiotGames/nexus_cli Pembungkus CLI seputar panggilan Sonatype Nexus REST.

Contoh Penggunaan:

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

Konfigurasi dilakukan melalui .nexus_clifile.

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

Anda juga dapat menggunakan metode penerapan langsung menggunakan curl. Anda tidak memerlukan pom untuk file Anda untuk itu tetapi itu tidak akan dibuat juga jadi jika Anda menginginkannya, Anda harus mengunggahnya secara terpisah.

Ini perintahnya:

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
sumber
"artefak" bukan artefak
Ram
1

Jika Anda memerlukan antarmuka baris perintah atau API python yang nyaman, lihat alat repositori

Dengan menggunakannya, Anda dapat mengunggah artefak ke nexus dengan perintah

artifact upload foo-1.2.3.ext releases com.fooware

Untuk membuatnya bekerja, Anda juga perlu mengatur beberapa variabel lingkungan

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

Anda dapat mengunggah artefak secara manual dengan mengeklik tombol unggah artefak di server Nexus dan memberikan properti GAV yang diperlukan untuk mengunggah (biasanya ini adalah struktur file untuk menyimpan artefak)

jijendiran
sumber
0

Untuk versi terbaru dari Nexus OSS (> = 3.9.0)

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

Contoh untuk versi 3.9.0 hingga 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.dll
sumber
-1

@Adam Vandenberg Untuk kode Java untuk POST ke 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
sumber
-2

Anda bisa menggunakan curl sebagai gantinya.

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
sumber
jawaban ini tidak benar. Dengan curl, groupId harus direpresentasikan sebagai org / myorg (gantikan titik "." Dengan garis miring "/")
madduci