Maven없이 Nexus에 아티팩트 업로드


102

버전이 지정된 빌드 아티팩트를 생성하는 비 Java 프로젝트가 있으며이를 Nexus 저장소에 업로드하고 싶습니다. 프로젝트가 Java가 아니기 때문에 빌드에 Maven을 사용하지 않습니다. 넥서스에 파일을 가져 오기 위해 Maven / POM 파일을 소개하는 것이 아닙니다.

블로그의 Nexus REST API에 대한 링크는 모두 로그인 벽에 있으며 내가 볼 수있는 "사용자 만들기"링크가 없습니다.

그렇다면 Maven없이 Nexus 저장소에 빌드 아티팩트를 업로드하는 가장 좋은 (또는 합리적인) 방법은 무엇입니까? "bash + curl"은 훌륭하거나 Python 스크립트 일 수도 있습니다.


적절한 서버 및 인증이 정의 된 ~ / .m2에 settings.xml이 있는지 확인하십시오.
Adam Vandenberg

답변:


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 기사에서는 "deploy-file"maven 플러그인이 가장 쉬운 솔루션이라고 설명하지만 curl을 사용하는 몇 가지 예제도 제공합니다.

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


이렇게하면이 zip 내에서 직접 파일을 다운로드 할 수 있지만 이렇게 업로드하면 불가능한 것 같습니다.
sorin

@sorin Maven을 사용하여 zip으로 파일을 다운로드 할 수 없습니다. 이것은 비정상적인 요구 사항이며 내가 아는 유일한 종속성 관리자는 ivy입니다 (간단하지 않습니다). 다음 예제를 참조하십시오. stackoverflow.com/questions/3445696/…
Mark O'Connor

모든 것을 더 간단하게 만들기 위해 Nexus를 설치했지만,이게 뭐죠? .. 의존성에 대한 지식없이 집에서 만든 JAR이 있다면 어떨까요? 내 IDE는 * .pom 누락에 대해 계속 불평합니다. Nexus가 이미 저를 위해 처리해줬으면했지만 NOOOOO sh ...
vintproykt

66

컬 사용 :

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-programatically-upload-an-artifact-into-Nexus-

이 작업에 대한 권한을 만들기 위해 관리자 GUI에서 새 역할을 만들고 해당 역할에 아티팩트 다운로드 및 아티팩트 업로드라는 두 가지 권한을 추가했습니다. 표준 "Repo : All Maven Repositories (모든 권한)"역할로는 충분하지 않습니다. Nexus 서버와 함께 번들로 제공되는 REST API 문서에서는이를 찾을 수 없으므로 이러한 매개 변수는 향후 변경 될 수 있습니다.

Sonatype JIRA 문제 , 그것은 그들이 "대부분 올해, 향후 릴리스에서 REST API를 (그리고 그것의 문서가 생성되는 방식을) 정밀 검사 예정"고 언급했다.


Jenkins에서 게시하고 빌드 사용자 만 Nexus에 게시하도록 허용한다고 가정 해 보겠습니다. 일반 비밀번호 문제를 어떻게 관리합니까? Jenkins에는 Jenkins 자격 증명을 사용할 수 있도록 업로드 용 플러그인이 있습니까?
Jirong Hu

8

이 명령을 사용할 필요가 없습니다. .. GAV 매개 변수를 사용하여 JAR을 업로드하기 위해 nexus 웹 인터페이스를 직접 사용할 수 있습니다.

여기에 이미지 설명 입력

그래서 매우 간단합니다.


24
GUI는 도움이되지 않습니다. 빌드 프로세스의 일부로 사용되는 명령 줄 스크립트를 통해 업로드 할 수 있어야합니다.
Adam Vandenberg

글쎄, 그것은 HTTP POST 요청으로 번역됩니다. 그렇지 않습니까?
Yngve Sneen Lindal

5
@YngveSneenLindal 물론입니다. 그렇다고 POST 인수가 공개적으로 사용하기 위해 잘 정의 된 API라는 의미는 아닙니다.
Ken Williams

@KenWilliams 물론, 나도 그것을 주장하지 않았습니다. 그러나 그들은 작동하고 해결책을 나타낼 것입니다. 그것이 제 요점입니다.
Yngve Sneen Lindal 2014 년

최소한 Sonatype Nexus ™ 2.11.1-01의 경우 사용자에게 권한을 부여해야했습니다 Artifact Upload. 불행히도 문서 에서 이것을 언급 하는 것을 찾을 수 없었습니다 ... (편집 : 알겠습니다 , Ed는 이미 이것을 지적했습니다 )
Alberto

8

MAVEN과 관련된 것을 사용하지 않고도 절대적으로 이것을 할 수 있습니다 . 개인적으로 NING HttpClient (v1.8.16, java6 지원)를 사용합니다.

어떤 이유로 든 Sonatype은 올바른 URL, 헤더 및 페이로드가 무엇인지 파악하기가 매우 어렵습니다. 트래픽을 스니핑하고 추측해야했습니다 ... 거의 유용한 블로그 / 문서가 있지만.와 관련이 없거나 oss.sonatype.orgXML 기반입니다 (작동하지 않는 것으로 나타났습니다). 그들의 부분, IMHO 및 희망적으로 미래의 구직자에 대한 쓰레기 문서는이 답변을 유용하게 찾을 수 있습니다. 게시물에 대한 https://stackoverflow.com/a/33414423/2101812 에게 많은 감사를드립니다 .

이외의 다른 위치에서 릴리스 oss.sonatype.org하는 경우 올바른 호스트로 바꾸십시오.

이 작업을 수행하기 위해 작성한 (CC0 라이선스) 코드는 다음과 같습니다. 어디 profile당신의 sonatype / 넥서스 profileID가 (예입니다 4364f3bbaf163)와 repo(예 :가 comdorkbox-1003) 당신이 당신의 초기 POM / 항아리를 업로드 응답에서 구문 분석됩니다.

저장소 닫기 :

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

EDIT1 :

저장소의 활동 / 상태를 얻는 방법

/**
 * 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 사용자 및 암호를 설정하여 sonatype-nexus-staging이라는 서버를 정의했습니다. 스냅 샷을 배포하는 경우 이미이 작업을 수행했을 것입니다. 그러나 여기에서 더 많은 정보를 찾을 수 있습니다 .
  2. 로컬 settings.xml에는 여기에 지정된 nexus 플러그인이 포함되어 있습니다 .
  3. 현재 디렉토리에있는 pom.xml의 정의에 올바른 Maven 좌표가 있습니다. 그렇지 않은 경우 명령 줄에서 groupId, artifactId 및 버전을 지정할 수 있습니다.
  4. -Dauto = true는 대화 형 프롬프트를 해제하므로 스크립트를 작성할 수 있습니다.

궁극적으로이 모든 작업은 Nexus에 REST 호출을 생성하는 것입니다. 완전한 Nexus REST API가 있지만 페이 월 뒤에 있지 않은 문서를 찾는 데는 거의 운이 없었습니다. 위의 플러그인에 대한 디버그 모드를 켜고을 사용하여 알아낼 수 -Dnexus.verboseDebug=true -X있습니다.

이론적으로 UI로 이동하여 Firebug Net 패널을 켜고 / service POST를 관찰하고 경로를 추론 할 수도 있습니다.


3

Apache httpcomponents 4.0을 사용하여 Java에서 필요로하는 사람들을 위해 :

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

첫 번째 게시물. 나는 자바에 higlighting을 추가하려고 시도했지만 얻을 수 없었다.
McMosfet 2015 년

3

루비 https://github.com/RiotGames/nexus_cli 에서 Sonatype Nexus REST 호출을 둘러싼 CLI 래퍼.

사용 예 :

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

"유물"하지 가공품

1

편리한 명령 줄 인터페이스 또는 Python API가 필요한 경우 repositorytools를 참조 하세요.

그것을 사용하면 명령으로 nexus에 아티팩트를 업로드 할 수 있습니다.

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 속성을 제공 할 수 있습니다 (일반적으로 아티팩트를 저장하기위한 파일 구조).



-1

@Adam Vandenberg Nexus에 POST 할 Java 코드 용. 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.