Загружать артефакты в Nexus без Maven

102

У меня есть проект, отличный от Java, который создает артефакт сборки с поддержкой версий, и я хочу загрузить его в репозиторий Nexus. Поскольку проект не является Java, он не использует Maven для сборок. И я бы предпочел не вводить файлы Maven / POM только для того, чтобы загрузить файлы в Nexus.

Ссылки в блогах на Nexus REST API все заканчиваются на стене входа, без ссылки «создать пользователя», которую я вижу.

Итак, каков наилучший (или любой разумный) способ загрузки артефактов сборки в репозиторий Nexus без Maven? "bash + curl" было бы здорово, или даже скрипт Python.

Адам Ванденберг
источник
Обратите внимание: убедитесь, что у вас есть файл settings.xml в ~ / .m2 с определенными соответствующими серверами и авторизацией.
Адам Ванденберг,

Ответы:

98

Вы рассматривали возможность использования командной строки Maven для загрузки файлов?

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

Это автоматически создаст Maven POM для артефакта.

Обновить

В следующей статье Sonatype говорится, что плагин maven "deploy-file" - самое простое решение, но он также предоставляет несколько примеров с использованием curl:

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

Марк О'Коннор
источник
Если бы только это позволило нам загружать файлы напрямую из этого zip-архива, но, похоже, это невозможно, если вы загрузите его таким образом.
sorin
@sorin Невозможно загружать файлы из ZIP-архива с помощью Maven. Это необычное требование, и единственный менеджер зависимостей, который, как я знаю, может это сделать, - это ivy (и это непросто). См. Следующий пример: stackoverflow.com/questions/3445696/…
Марк О'Коннор
Я установил Nexus, чтобы все было проще, но что это за чертовщина? .. Что делать, если у меня есть самодельный JAR, не зная о его зависимостях? Моя IDE продолжает жаловаться на отсутствие * .pom. Я надеялся, что Nexus уже справился с этим за меня, но НЕЕЕЕЕТ ш ...
винтпройт
66

Использование 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

Вы можете увидеть, что означают параметры здесь: https://support.sonatype.com/entries/22189106-How-can-I-programatic-upload-an-artifact-into-Nexus-

Чтобы разрешить эту работу, я создал новую роль в графическом интерфейсе администратора и добавил к ней две привилегии: загрузка артефактов и загрузка артефактов. Стандартного «Repo: All Maven Repositories (Full Control)» - роли недостаточно. Вы не найдете этого в документации по REST API, поставляемой вместе с сервером Nexus, поэтому эти параметры могут измениться в будущем.

В отношении проблемы Sonatype JIRA было упомянуто, что они «собираются пересмотреть REST API (и способ создания документации) в следующем выпуске, скорее всего, в конце этого года».

Эд я
источник
скажем, мы публикуем из Jenkins и разрешаем только пользователям сборки публиковать в Nexus, как вы справляетесь с проблемой простого пароля? Есть ли у Jenkins плагин для загрузки, чтобы мы могли использовать учетные данные Jenkins?
Цзиронг Ху
8

Нет необходимости использовать эти команды .. вы можете напрямую использовать веб-интерфейс nexus, чтобы загрузить свой JAR с помощью параметров GAV.

введите описание изображения здесь

Все очень просто.

Пранел ПИДИКИТИ
источник
24
Графический интерфейс не помогает; Мне нужно иметь возможность загружать файлы с помощью сценария командной строки, используемого как часть процесса сборки.
Адам Ванденберг
Ну, это переводится в HTTP-запрос POST, вам не кажется?
Yngve Sneen Lindal
5
@YngveSneenLindal Конечно, но это не значит, что эти аргументы POST представляют собой четко определенный API для публичного использования.
Кен Уильямс
@KenWilliams Конечно, я и этого не утверждал. Но они будут работать и представлять решение, в этом моя точка зрения.
Yngve Sneen Lindal
По крайней мере, для нашего Sonatype Nexus ™ 2.11.1-01 мне пришлось предоставить пользователю привилегию Artifact Upload. К сожалению, я не смог найти ничего в документах, в которых упоминалось бы об этом ... (Edit: я вижу, Эд Я уже указал на это )
Альберто
8

Вы можете АБСОЛЮТНО сделать это, не используя ничего, связанного с MAVEN. Я лично использую NING HttpClient (v1.8.16, для поддержки java6).

По какой - то причине, Sonatype делает его невероятно трудности , чтобы выяснить , что правильные URL - адреса, заголовки и полезные нагрузки должны быть; и мне пришлось понюхать трафик и угадать ... Там есть несколько мало полезных блогов / документации, однако это либо не имеет отношения к делу oss.sonatype.org, либо основано на XML (и я обнаружил, что это даже не работает). Дерьмовая документация с их стороны, ИМХО, и, надеюсь, будущие соискатели найдут этот ответ полезным. Большое спасибо https://stackoverflow.com/a/33414423/2101812 за их сообщение, так как это очень помогло.

Если вы выпускаете где-то еще oss.sonatype.org, просто замените его на правильный хост.

Вот код (с лицензией CC0), который я написал для этого. Где profileваш sonatype / nexus profileID (например, 4364f3bbaf163) и repo(например, comdorkbox-1003) анализируются из ответа, когда вы загружаете свой исходный POM / Jar.

Закрытие репо:

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

Продвигать репо:

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

Отбросить репо:

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

Удалить палочки с подписью:

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

Загрузка файлов:

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

РЕДАКТИРОВАТЬ1:

Как получить активность / статус репо

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

Вызовы, которые вам нужно сделать против Nexus, - это вызовы REST api.

Maven-nexus-plugin - это плагин Maven, который вы можете использовать для этих вызовов. Вы можете создать фиктивный pom с необходимыми свойствами и выполнять эти вызовы через плагин Maven.

Что-то вроде:

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

Предполагаемые вещи:

  1. Вы определили сервер в вашем ~ / .m2 / settings.xml с именем sonatype-nexus-staging с вашим пользователем sonatype и настроенными паролем - вы, вероятно, уже сделали это, если развертываете снимки. Но вы можете найти больше информации здесь .
  2. Ваш локальный файл settings.xml включает плагины nexus, указанные здесь .
  3. Файл pom.xml, расположенный в вашем текущем каталоге, имеет правильные координаты Maven в своем определении. Если нет, вы можете указать groupId, artifactId и версию в командной строке.
  4. Параметр -Dauto = true отключит интерактивные подсказки, чтобы вы могли создать сценарий.

В конечном итоге все, что это делает, - это создание вызовов REST в Nexus. Существует полный API-интерфейс Nexus REST, но мне не удалось найти для него документацию, которая не находится за платным доступом. Вы можете включить режим отладки для указанного выше плагина и выяснить это, используя-Dnexus.verboseDebug=true -X .

Теоретически вы также можете войти в пользовательский интерфейс, включить панель Firebug Net и посмотреть на / service POST и вывести путь туда.

Алекс Миллер
источник
3

для тех, кому это нужно на Java, используя 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
источник
первый пост. Я пытался добавить выделение для java, но не смог.
McMosfet
3

В ruby https://github.com/RiotGames/nexus_cli CLI-оболочка для вызовов REST Sonatype Nexus.

Пример использования:

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

Конфигурация выполняется через .nexus_cliфайл.

url:            "http://my-nexus-server/nexus/"
repository:     "my-repository-id"
username:       "username"
password:       "password"
Франсуа
источник
2

Вы также можете использовать метод прямого развертывания с помощью curl. Вам не нужен pom для вашего файла, но он также не будет сгенерирован, поэтому, если он вам нужен, вам придется загрузить его отдельно.

Вот команда:

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
Джидиуф
источник
«артефакт», а не артефакт
Ram
1

Если вам нужен удобный интерфейс командной строки или API Python, посмотрите репозиторий.

Используя его, вы можете загрузить артефакт в нексус командой

artifact upload foo-1.2.3.ext releases com.fooware

Чтобы он работал, вам также нужно будет установить некоторые переменные среды

export REPOSITORY_URL=https://repo.example.com
export REPOSITORY_USER=admin
export REPOSITORY_PASSWORD=mysecretpassword
Мишель Самиа
источник
0

Вы можете вручную загрузить артефакты, нажав кнопку загрузки артефактов на сервере Nexus и указав необходимые свойства GAV для загрузки (обычно это файловая структура для хранения артефакта).

Джиджендиран
источник
0

Для последних версий Nexus OSS (> = 3.9.0)

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

Пример для версий 3.9.0 - 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"
Adrianlzt
источник
-1

@Adam Vandenberg Для кода Java для POST в 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

Вместо этого вы можете использовать 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
Скотт Джонс
источник
этот ответ неверен. В случае curl groupId должен быть представлен как org / myorg (замените точку "." На косую черту "/")
madduci 01