在没有Maven的情况下将工件上传到Nexus


102

我有一个非Java项目,该项目会生成版本化的构建工件,因此我想将其上传到Nexus存储库。因为该项目不是Java,所以它不使用Maven进行构建。而且我不想引入Maven / POM文件只是为了使文件进入Nexus。

博客上指向Nexus REST API的链接都以登录墙结尾,没有我看到的“创建用户”链接。

那么,在没有Maven的情况下将构建工件上传到Nexus存储库的最佳(或任何合理的)方法是什么?“ bash + curl”会很棒,甚至是Python脚本。


注意,请确保在〜/ .m2中有一个settings.xml,其中定义了适当的服务器和身份验证。
亚当·范登堡

Answers:


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无法使用Maven从zip内下载文件。这是一个不寻常的要求,唯一的依赖经理,我知道,能做到这一点是常春藤(这不是简单),请看下面的例子:stackoverflow.com/questions/3445696/...
马克·奥康纳

我安装Nexus使其更简单,但这引人注目?..如果我有一些不知道其依赖项的自制JAR怎么办?我的IDE一直抱怨缺少* .pom。我希望Nexus已经为我解决了这一问题,但请不要……
vintproykt

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

为了获得这项工作的权限,我在管理GUI中创建了一个新角色,并为该角色添加了两个特权:Artifact Download和Artifact Upload。标准的“回购:所有Maven存储库(完全控制)”角色是不够的。在Nexus服务器随附的REST API文档中找不到此功能,因此这些参数将来可能会更改。

Sonatype JIRA问题上,提到他们“将在即将发布的版本中对REST API(及其文档的生成方式)进行全面检查,很可能在今年晚些时候”。


假设我们是从Jenkins发布的,并且只允许构建用户发布到Nexus,那么您如何管理普通密码问题?Jenkins是否具有用于上传的插件,以便我们可以使用Jenkins凭据?
Jirong Hu

8

无需使用这些命令..您可以直接使用nexus Web界面,以便使用GAV参数上传JAR。

在此处输入图片说明

因此非常简单。


24
GUI没有帮助;我需要能够通过在构建过程中使用的命令行脚本进行上传。
亚当·范登堡

好吧,它转换为HTTP POST请求,您不觉得吗?
Yngve Sneen Lindal

5
@YngveSneenLindal当然可以,但这并不意味着这些POST参数是定义良好的API,可以公开使用。
肯·威廉姆斯

@KenWilliams当然,我也没有要求。但是,它们将起作用并代表解决方案,这就是我的观点。
Yngve Sneen Lindal 2014年

至少,对于我们的Sonatype Nexus™2.11.1-01,我必须向用户授予特权Artifact Upload。不幸的是,我在文档中找不到任何提及此的信息…(编辑:我明白了,Ed我已经指出了这一点
Alberto 2015年

8

您可以ABSOLUTELY做到这一点,而无需使用任何相关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的/承上启下配置文件ID(如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);
    }

编辑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将关闭交互式提示,因此您可以编写脚本。

最终,所有要做的就是在Nexus中创建REST调用。有一个完整的Nexus REST API,但是我找它的文档很少,而且没有付费用。您可以使用来打开上述插件的调试模式,然后找出来-Dnexus.verboseDebug=true -X

从理论上讲,您也可以进入UI,打开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;
    }
}

第一篇文章。我尝试为Java添加高亮显示,但无法理解。
McMosfet 2015年

3

在ruby中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,但也不会生成该文件,因此,如果要使用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

如果您需要方便的命令行界面或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属性(通常是用于存储工件的文件结构)


0

对于最新版本的Nexus OSS(> = 3.9.0)

https://support.sonatype.com/hc/zh-CN/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"

-1

@Adam Vandenberg用于将Java代码发布到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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.