将工件上传到Nexus,无需Maven

我有一个非Java项目,生成版本化的构build工件,我想上传到Nexus存储库。 由于该项目不是Java,因此不使用Maven构build。 我宁愿不介绍Maven / POM文件只是为了获取文件到Nexus。

博客上的链接到Nexus REST API都在login墙上,没有“创build用户”链接,我可以看到。

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

你有没有考虑使用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插件是最简单的解决scheme,但它也提供了一些使用curl的例子:

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

使用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-

为了使这项工作的权限,我在pipe理GUI中创build了一个新的angular色,并且为这个angular色添加了两个特权:工件下载和工件上传。 标准的“回购:所有的Maven仓库(完全控制)” – angular色是不够的。 在与Nexus服务器捆绑在一起的REST API文档中,您不会find它,所以这些参数将来可能会发生变化。

在Sonatype JIRA问题上 ,有人提到,他们“将在即将到来的版本中彻底检查REST API(以及它生成文档的方式),很可能在今年晚些时候”。

不需要使用这些命令..您可以直接使用nexus web Interface来使用GAV参数上传您的JAR。

在这里输入图像描述

所以这很简单。

您需要对Nexus进行的调用是REST API调用。

maven-nexus-plugin是一个Maven插件,您可以使用它来进行这些调用。 您可以创build一个具有必要属性的虚拟pom,并通过Maven插件进行这些调用。

就像是:

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

假设的事情:

  1. 您已经在您的〜/ .m2 / settings.xml中定义了一个名为sonatype-nexus-staging的服务器,并设置了您的sonatype用户名和密码 – 如果您正在部署快照,您可能已经完成了此操作。 但是你可以在这里find更多的信息。
  2. 您的本地settings.xml包含此处指定的nexus插件。
  3. 位于当前目录中的pom.xml在其定义中具有正确的Maven坐标。 如果不是,则可以在命令行上指定groupId,artifactId和版本。
  4. -Dauto = true将closures交互式提示,以便您可以编写脚本。

最终,所有这些工作都是将REST调用创build到Nexus中。 有一个完整的Nexus REST api,但我有一点运气find它不是付费墙背后的文档。 您可以打开上面的插件的debugging模式,然后通过使用-Dnexus.verboseDebug=true -X

你理论上也可以进入UI,打开Firebug Net面板,并观察/服务POST,并在那里推导出一条path。

绝对可以做到这一点,而不使用MAVEN相关的任何东西。 我个人使用NING HttpClient(v1.8.16,支持java6)。

无论出于何种原因,Sonatype使得找出正确的URL,标题和有效载荷应该是什么难以置信 ; 我不得不嗅探stream量和猜测…有一些几乎没有用的博客/文档,但它是无关oss.sonatype.org ,或者它是基于XML(我发现它甚至不工作) 。 垃圾文件,恕我直言,希望未来的求职者可以find这个答案有用。 非常感谢https://stackoverflow.com/a/33414423/2101812他们的post,因为它帮助了很多。;

如果你释放oss.sonatype.org以外的oss.sonatype.org ,只要把它replace成正确的主机。

这是我写的(CC0许可)代码来完成这个。 当你上传你最初的POM / Jar的时候,你的sonatype / nexus profileID(例如4364f3bbaf163 )和repo (比如comdorkbox-1003 )被从响应中parsing出来。

closures回购:

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

file upload:

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

对于那些需要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; } } 

curl是艰难的路要走。 如果你不想使用maven(例如:不允许)

看看常春藤

其他选项gradle : http : //www.gradle.org/docs/current/userguide/artifact_management.html#N14566

在ruby中https://github.com/RiotGames/nexus_cli一个围绕着Sonatype Nexus REST调用的CLI包装。

您也可以使用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 

如果您需要一个方便的命令行界面或python API,请查看repositorytools

你可以用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