Télécharger des artefacts sur Nexus, sans Maven

102

J'ai un projet non-Java qui produit un artefact de construction versionné et je souhaite le télécharger dans un référentiel Nexus. Parce que le projet n'est pas Java, il n'utilise pas Maven pour les builds. Et je préfère ne pas introduire de fichiers Maven / POM juste pour obtenir des fichiers dans Nexus.

Les liens sur les blogs vers l'API Nexus REST aboutissent tous sur un mur de connexion, sans lien "Créer un utilisateur" que je puisse voir.

Alors, quel est le meilleur moyen (ou tout moyen raisonnable) de télécharger des artefacts de construction dans un référentiel Nexus sans Maven? "bash + curl" serait génial, ou même un script Python.

Adam Vandenberg
la source
Remarque, assurez-vous d'avoir un settings.xml dans ~ / .m2 avec les serveurs et l'authentification appropriés définis.
Adam Vandenberg

Réponses:

98

Avez-vous envisagé d'utiliser la ligne de commande Maven pour télécharger des fichiers?

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

Cela générera automatiquement le Maven POM pour l'artefact.

Mettre à jour

L'article suivant de Sonatype indique que le plugin maven "deploy-file" est la solution la plus simple, mais il fournit également quelques exemples utilisant curl:

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

Mark O'Connor
la source
Si seulement cela nous permettait de télécharger directement des fichiers à partir de ce zip, mais cela ne semble pas possible si vous le téléchargez comme ça.
sorin le
@sorin Il n'est pas possible de télécharger des fichiers depuis un zip en utilisant Maven. C'est une exigence inhabituelle et le seul gestionnaire de dépendances que je connaisse qui puisse le faire est lierre (et ce n'est pas simple) voir l'exemple suivant: stackoverflow.com/questions/3445696/…
Mark O'Connor
J'ai installé Nexus pour simplifier les choses, mais qu'est-ce que c'est que ça? .. Et si j'ai un JAR fait maison sans connaître ses dépendances? Mon IDE ne cesse de se plaindre de l'absence de * .pom. J'espérais que Nexus avait déjà géré cela pour moi, mais NOOOOO sh ...
vintproykt
66

Utilisation de 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

Vous pouvez voir ce que signifient les paramètres ici: https://support.sonatype.com/entries/22189106-How-can-I-programately-upload-an-artifact-into-Nexus-

Pour que les autorisations pour ce travail fonctionnent, j'ai créé un nouveau rôle dans l'interface graphique d'administration et j'ai ajouté deux privilèges à ce rôle: Artifact Download et Artifact Upload. Le rôle standard «Repo: All Maven Repositories (Full Control)» ne suffit pas. Vous ne trouverez pas cela dans la documentation de l'API REST fournie avec le serveur Nexus, donc ces paramètres peuvent changer à l'avenir.

Sur un problème de Sonatype JIRA , il a été mentionné qu'ils "vont réviser l'API REST (et la façon dont sa documentation est générée) dans une prochaine version, probablement plus tard cette année".

Ed I
la source
disons que nous publions à partir de Jenkins et autorisons uniquement les utilisateurs de build à publier sur Nexus, comment gérez-vous le problème du mot de passe simple? Jenkins a-t-il un plugin pour le téléchargement afin que nous puissions utiliser les informations d'identification Jenkins?
Jirong Hu
8

Pas besoin d'utiliser ces commandes .. vous pouvez utiliser directement l'interface web nexus afin de télécharger votre JAR en utilisant les paramètres GAV.

entrez la description de l'image ici

C'est donc très simple.

Praneel PIDIKITI
la source
24
Une interface graphique n'aide pas; Je dois pouvoir télécharger via un script de ligne de commande utilisé dans le cadre d'un processus de construction.
Adam Vandenberg
Eh bien, cela se traduit par une requête HTTP POST, vous ne pensez pas?
Yngve Sneen Lindal
5
@YngveSneenLindal Bien sûr, mais cela ne signifie pas que ces arguments POST sont une API bien définie à utiliser publiquement.
Ken Williams
@KenWilliams Bien sûr, je ne l'ai pas non plus affirmé. Mais ils fonctionneront et représenteront une solution, c'est mon point.
Yngve Sneen Lindal
Au moins, pour notre Sonatype Nexus ™ 2.11.1-01, j'ai dû accorder le privilège à l'utilisateur Artifact Upload. Malheureusement, je n'ai rien trouvé dans la documentation mentionnant cela ... (Edit: Je vois, Ed j'ai déjà souligné cela )
Alberto
8

Vous pouvez ABSOLUMENT le faire sans utiliser quoi que ce soit de MAVEN. J'utilise personnellement NING HttpClient (v1.8.16, pour prendre en charge java6).

Pour une raison quelconque, Sonatype rend incroyablement difficile de déterminer ce que les URL, en-têtes et charges utiles corrects sont censés être; et j'ai dû renifler le trafic et deviner ... Il y a des blogs / documentation à peine utiles là-bas, mais soit ils ne sont pas pertinents oss.sonatype.org, soit ils sont basés sur XML (et j'ai découvert que cela ne fonctionne même pas). La documentation de merde de leur part, à mon humble avis, et j'espère que les futurs chercheurs pourront trouver cette réponse utile. Un grand merci à https://stackoverflow.com/a/33414423/2101812 pour leur message, car cela a beaucoup aidé.

Si vous sortez ailleurs que oss.sonatype.org, remplacez-le simplement par le bon hôte.

Voici le code (sous licence CC0) que j'ai écrit pour accomplir cela. Où profileest votre identifiant de profil sonatype / nexus (tel que 4364f3bbaf163) et repo(tel que comdorkbox-1003) sont analysés à partir de la réponse lorsque vous téléchargez votre POM / Jar initial.

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

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

Déposer le dépôt:

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

Supprimer les problèmes de signature:

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

Téléchargements de fichiers:

    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:

Comment obtenir l'activité / le statut d'un repo

/**
 * 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);
}
Nathan
la source
6

Les appels que vous devez effectuer contre Nexus sont des appels API REST.

Le plugin maven-nexus est un plugin Maven que vous pouvez utiliser pour effectuer ces appels. Vous pouvez créer un pom factice avec les propriétés nécessaires et effectuer ces appels via le plugin Maven.

Quelque chose comme:

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

Choses supposées:

  1. Vous avez défini un serveur dans votre ~ / .m2 / settings.xml nommé sonatype-nexus-staging avec votre utilisateur et mot de passe sonatype - vous l'aurez probablement déjà fait si vous déployez des instantanés. Mais vous pouvez trouver plus d'informations ici .
  2. Votre fichier settings.xml local inclut les plugins nexus comme spécifié ici .
  3. Le pom.xml situé dans votre répertoire actuel a les coordonnées Maven correctes dans sa définition. Sinon, vous pouvez spécifier le groupId, artifactId et la version sur la ligne de commande.
  4. -Dauto = true désactivera les invites interactives afin que vous puissiez créer un script.

En fin de compte, tout ce que cela fait, c'est créer des appels REST dans Nexus. Il existe une API Nexus REST complète, mais j'ai eu peu de chance de trouver de la documentation qui n'est pas derrière un paywall. Vous pouvez activer le mode de débogage pour le plugin ci-dessus et le comprendre cependant en utilisant -Dnexus.verboseDebug=true -X.

Vous pouvez également théoriquement accéder à l'interface utilisateur, activer le panneau Firebug Net et surveiller les POST / service et en déduire également un chemin.

Alex Miller
la source
3

pour ceux qui en ont besoin en Java, en utilisant 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
la source
premier poste. J'ai essayé d'ajouter du surlignage pour java mais je ne l'ai pas compris.
McMosfet
3

In ruby https://github.com/RiotGames/nexus_cli Un wrapper CLI autour des appels REST Sonatype Nexus.

Exemple d'utilisation:

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

La configuration se fait via le .nexus_clifichier.

url:            "http://my-nexus-server/nexus/"
repository:     "my-repository-id"
username:       "username"
password:       "password"
François
la source
2

Vous pouvez également utiliser la méthode de déploiement direct à l'aide de curl. Vous n'avez pas besoin d'un pom pour votre fichier, mais il ne sera pas généré également, donc si vous en voulez un, vous devrez le télécharger séparément.

Voici la commande:

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
Djidiouf
la source
"artefact" pas un artefact
Bélier
1

Si vous avez besoin d'une interface de ligne de commande pratique ou d'une API python, consultez repositorytools

En l'utilisant, vous pouvez télécharger un artefact vers nexus avec la commande

artifact upload foo-1.2.3.ext releases com.fooware

Pour que cela fonctionne, vous devrez également définir des variables d'environnement

export REPOSITORY_URL=https://repo.example.com
export REPOSITORY_USER=admin
export REPOSITORY_PASSWORD=mysecretpassword
Michel Samia
la source
0

Vous pouvez télécharger manuellement les artefacts en cliquant sur le bouton de téléchargement des artefacts sur le serveur Nexus et fournir les propriétés GAV nécessaires pour le téléchargement (il s'agit généralement de la structure de fichier pour stocker l'artefact)

jijendiran
la source
0

Pour les versions récentes de Nexus OSS (> = 3.9.0)

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

Exemple pour les versions 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 "[email protected];type=application/json" -F "raw.asset1.filename=test.txt"
adrianlzt
la source
-1

@Adam Vandenberg Pour le code Java pour POST sur 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;
    }

}
Mano Anbalagan
la source
-2

Vous pouvez utiliser curl à la place.

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
Scott Jones
la source
cette réponse n'est pas correcte. Avec curl, le groupId doit être représenté par org / myorg (remplacez le point "." Par une barre oblique "/")
madduci