Subir artefactos a Nexus, sin Maven


Tengo un proyecto no Java que produce un artefacto de compilación versionado, y quiero subir esto a un repositorio Nexus. Debido a que el proyecto no es Java, no utiliza Maven para las compilaciones. Y prefiero no introducir archivos Maven / POM solo para obtener archivos en Nexus.

Los enlaces en blogs a la API Nexus REST terminan en un muro de inicio de sesión, sin ningún enlace de "crear usuario" que pueda ver.

Entonces, ¿cuál es la mejor manera (o cualquier otra razonable) de cargar artefactos de compilación en un repositorio Nexus sin Maven? "bash + curl" sería genial, o incluso un script Python.

 88
Author: Adam Vandenberg, 2010-10-27

11 answers

¿Ha considerado usar la línea de comandos Maven para cargar archivos?

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

Esto generará automáticamente el POM Maven para el artefacto.

Actualización

El siguiente artículo de Sonatype indica que el complemento maven "deploy-file"es la solución más fácil, pero también proporciona algunos ejemplos usando curl:

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

 93
Author: Mark O'Connor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-07 09:08:17

Usando 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

Puedes ver lo que significan los parámetros aquí: https://support.sonatype.com/entries/22189106-How-can-I-programatically-upload-an-artifact-into-Nexus-

Para hacer que los permisos para este trabajo, he creado un nuevo rol en la interfaz gráfica de administración y he añadido dos privilegios a ese rol: Artifact Download y Artifact Upload. El rol estándar "Repo: All Maven Repositories (Full Control)"no es suficiente. No encontrará esto en la documentación de la API REST que viene incluido con el servidor Nexus, por lo que estos parámetros podrían cambiar en el futuro.

En un problema de Sonatype JIRA, se mencionó que "van a revisar la API REST (y la forma en que se genera la documentación) en una próxima versión, muy probablemente a finales de este año".

 59
Author: Ed I,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-25 22:35:35

Puedes ABSOLUTAMENTE hacer esto sin usar nada relacionado con MAVEN. Yo personalmente uso el NING HttpClient (v1.8.16, para soportar java6).

Por cualquier razón, Sonatype hace que sea increíblemente difícil averiguar cuáles son las URL, encabezados y cargas útiles correctos; y tuve que oler el tráfico y adivinar... Hay algunos apenas blogs/documentación útiles allí, sin embargo, es irrelevante para oss.sonatype.org, o está basado en XML (y yo descubrí que ni siquiera funciona). Documentación de mierda por su parte, en mi humilde opinión, y con suerte los futuros buscadores pueden encontrar esta respuesta útil. Muchas gracias a https://stackoverflow.com/a/33414423/2101812 para su puesto, ya que ayudó mucho.

Si sueltas en otro lugar que no sea oss.sonatype.org, simplemente reemplázalo con el host correcto.

Aquí está el código (con licencia CC0) que escribí para lograr esto. Donde profile es su sonatype / nexus profileID (tal como 4364f3bbaf163) y repo (tal as comdorkbox-1003) se analizan a partir de la respuesta cuando subes tu POM/Jar inicial.

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

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

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

Suprímase la mierda de firma:

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

Cargas de archivos:

    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:

Cómo obtener la actividad / estado de un repositorio

/**
 * 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);
}
 8
Author: Nathan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:54:59

No es necesario usar estos comandos .. usted puede utilizar directamente la interfaz web nexus con el fin de cargar su JAR utilizando parámetros GAV.

introduzca la descripción de la imagen aquí

Así que es muy simple.

 7
Author: Praneel PIDIKITI,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-22 17:36:17

Las llamadas que necesita hacer contra Nexus son llamadas a la api REST.

El maven-nexus-plugin es un plugin Maven que puedes usar para hacer estas llamadas. Puede crear un pom ficticio con las propiedades necesarias y realizar esas llamadas a través del complemento Maven.

Algo como:

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

Cosas asumidas:

  1. Ha definido un servidor en su~/.m2 / ajustes.xml named sonatype-nexus-staging con su usuario y contraseña de sonatype configurados-probablemente ya lo ha hecho si está implementando instantáneas. Pero puedes encontrar más información aquí.
  2. Su configuración local.xml incluye los plugins nexus como se especifica aquí.
  3. El pom.xml sentado en su directorio actual tiene las coordenadas Maven correctas en su definición. Si no, puede especificar el groupId, artifactId y la versión en la línea de comandos.
  4. El-Dauto = true desactivará las indicaciones interactivas para que pueda escribir este.

En última instancia, todo lo que esto está haciendo es crear llamadas REST en Nexus. Hay una api REST Nexus completa, pero he tenido poca suerte encontrando documentación para ello que no esté detrás de un muro de pago. Puede activar el modo de depuración para el plugin anterior y resolverlo sin embargo mediante -Dnexus.verboseDebug=true -X.

También podría teóricamente ir a la interfaz de usuario, activar el panel de red Firebug, y ver las publicaciones /service y deducir una ruta allí también.

 6
Author: Alex Miller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-01 22:05:49

Para aquellos que lo necesitan en Java, usando 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;
    }
}
 3
Author: McMosfet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-29 12:51:25

Curl es el camino difícil a seguir. Si no desea utilizar maven (por ejemplo : no permitido)

Echa un vistazo a ivy :

Otra opción gradle : http://www.gradle.org/docs/current/userguide/artifact_management.html#N14566

 2
Author: mestachs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-07 11:34:26

En ruby https://github.com/RiotGames/nexus_cli Un contenedor CLI alrededor de las llamadas REST de Sonatype Nexus.

 1
Author: Francois,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-31 21:26:45

También puede usar el método direct deploy usando curl. No necesita un pom para su archivo para ello, pero no se generará tan bien, por lo que si desea uno, tendrá que cargarlo por separado.

Aquí está el comando:

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
Author: Djidiouf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-29 01:45:02

Si necesita una interfaz de línea de comandos conveniente o una API de python, consulte repositorytools

 0
Author: Michel Samia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-11 15:12:31

Puedes usar curl en su lugar.

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
 -2
Author: Scott Jones,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-15 15:42:52