<commit_before>/*
 * Copyright (C) 2011 JFrog Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jfrog.hudson.release;

import com.google.common.collect.Lists;
import hudson.model.AbstractBuild;
import hudson.model.BuildBadgeAction;
import hudson.model.Cause;
import hudson.model.TaskAction;
import hudson.model.TaskListener;
import hudson.model.TaskThread;
import hudson.security.ACL;
import hudson.security.Permission;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.jfrog.build.api.builder.PromotionBuilder;
import org.jfrog.build.client.ArtifactoryBuildInfoClient;
import org.jfrog.hudson.ArtifactoryPlugin;
import org.jfrog.hudson.ArtifactoryRedeployPublisher;
import org.jfrog.hudson.ArtifactoryServer;
import org.jfrog.hudson.action.ActionableHelper;
import org.jfrog.hudson.util.CredentialResolver;
import org.jfrog.hudson.util.Credentials;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

import javax.servlet.ServletException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;

/**
 * This badge action marks the build as a release build. The release wrapper takes the release and next version string
 * from this badge.
 *
 * @author <NAME>
 */
public class StageBuildAction extends TaskAction implements BuildBadgeAction {
    private final AbstractBuild build;

    private String targetStatus;
    private String repositoryKey;
    private String comment;
    private boolean useCopy;
    private boolean includeDependencies;

    public StageBuildAction(AbstractBuild build) {
        this.build = build;
    }

    public String getIconFileName() {
        return "/plugin/artifactory/images/artifactory-release.png";
    }

    public String getDisplayName() {
        return "Artifactory Release Promotion";
    }

    public String getUrlName() {
        if (hasStagingPermission()) {
            return "stage";
        }
        // return null to hide this action
        return null;
    }

    public boolean hasStagingPermission() {
        return getACL().hasPermission(getPermission());
    }

    @Override
    protected Permission getPermission() {
        return ArtifactoryPlugin.STAGE;
    }

    @Override
    protected ACL getACL() {
        return build.getACL();
    }

    public AbstractBuild getBuild() {
        return build;
    }

    public void setTargetStatus(String targetStatus) {
        this.targetStatus = targetStatus;
    }

    public void setRepositoryKey(String repositoryKey) {
        this.repositoryKey = repositoryKey;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public void setUseCopy(boolean useCopy) {
        this.useCopy = useCopy;
    }

    public void setIncludeDependencies(boolean includeDependencies) {
        this.includeDependencies = includeDependencies;
    }

    /**
     * @return List of target repositories for deployment (release repositories first). Called from the UI.
     */
    @SuppressWarnings({"UnusedDeclaration"})
    public List<String> getRepositoryKeys() {
        ArtifactoryRedeployPublisher artifactoryPublisher = ActionableHelper.getPublisher(
                build.getProject(), ArtifactoryRedeployPublisher.class);
        if (artifactoryPublisher != null) {
            List<String> repos = artifactoryPublisher.getArtifactoryServer().getReleaseRepositoryKeysFirst();
            repos.add(0, "");  // option not to move
            return repos;
        } else {
            return Collections.emptyList();
        }
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public List<String> getTargetStatuses() {
        return Lists.newArrayList(/*"Staged", */"Released", "Rolled-back");
    }

    /**
     * @return The repository selected by the latest promotion (to be selected by default).
     */
    public String lastPromotionRepository() {
        // TODO: implement
        return null;
    }

    /**
     * Select which view to display based on the state of the promotion. Will return the form if user selects to perform
     * promotion. Progress will be returned if the promotion is currently in progress.
     */
    @SuppressWarnings({"UnusedDeclaration"})
    public void doIndex(StaplerRequest req, StaplerResponse resp) throws IOException, ServletException {
        req.getView(this, chooseAction()).forward(req, resp);
    }

    private synchronized String chooseAction() {
        return workerThread == null ? "form.jelly" : "progress.jelly";
    }

    /**
     * Form submission is calling this method
     */
    @SuppressWarnings({"UnusedDeclaration"})
    public void doSubmit(StaplerRequest req, StaplerResponse resp) throws IOException, ServletException {
        getACL().checkPermission(getPermission());

        req.bindParameters(this);

        new StageWorkerThread().start();

        resp.sendRedirect(".");
    }

    /**
     * The thread that performs the promotion asynchronously.
     */
    public final class StageWorkerThread extends TaskThread {

        public StageWorkerThread() {
            super(StageBuildAction.this, ListenerAndText.forMemory(null));
        }

        @Override
        protected void perform(TaskListener listener) {
            ArtifactoryBuildInfoClient client = null;
            try {
                listener.getLogger().println("Promoting build ....");
                ArtifactoryRedeployPublisher artifactoryPublisher = ActionableHelper.getPublisher(
                        build.getProject(), ArtifactoryRedeployPublisher.class);
                ArtifactoryServer server = artifactoryPublisher.getArtifactoryServer();

                Credentials deployer = CredentialResolver.getPreferredDeployer(artifactoryPublisher, server);
                client = server.createArtifactoryClient(deployer.getUsername(), deployer.getPassword());

                Cause.UserCause userCause = ActionableHelper.getUserCause(build);
                String username = null;
                if (userCause != null) {
                    username = userCause.getUserName();
                }

                // do a dry run first
                PromotionBuilder promotionBuilder = new PromotionBuilder()
                        .status(targetStatus)
                        .comment(comment)
                        .ciUser(username)
                        .targetRepo(repositoryKey)
                        .dependencies(includeDependencies)
                        .copy(useCopy)
                        .dryRun(true);
                listener.getLogger().println("Performing dry run staging (no changes are made during dry run) ...");<commit_msg>HAP-157 - allow extending the way to get the Artifactory server and its credentials. Each plugin may get this information in different ways (publishers or wrappers)<commit_after>/*
 * Copyright (C) 2011 JFrog Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jfrog.hudson.release;

import com.google.common.collect.Lists;
import hudson.model.AbstractBuild;
import hudson.model.BuildBadgeAction;
import hudson.model.Cause;
import hudson.model.TaskAction;
import hudson.model.TaskListener;
import hudson.model.TaskThread;
import hudson.security.ACL;
import hudson.security.Permission;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.jfrog.build.api.builder.PromotionBuilder;
import org.jfrog.build.client.ArtifactoryBuildInfoClient;
import org.jfrog.hudson.ArtifactoryPlugin;
import org.jfrog.hudson.ArtifactoryRedeployPublisher;
import org.jfrog.hudson.ArtifactoryServer;
import org.jfrog.hudson.action.ActionableHelper;
import org.jfrog.hudson.util.CredentialResolver;
import org.jfrog.hudson.util.Credentials;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

import javax.servlet.ServletException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;

/**
 * This badge action marks the build as a release build. The release wrapper takes the release and next version string
 * from this badge.
 *
 * @author <NAME>
 */
public class StageBuildAction extends TaskAction implements BuildBadgeAction {
    private final AbstractBuild build;

    private String targetStatus;
    private String repositoryKey;
    private String comment;
    private boolean useCopy;
    private boolean includeDependencies;

    public StageBuildAction(AbstractBuild build) {
        this.build = build;
    }

    public String getIconFileName() {
        return "/plugin/artifactory/images/artifactory-release.png";
    }

    public String getDisplayName() {
        return "Artifactory Release Promotion";
    }

    public String getUrlName() {
        if (hasStagingPermission()) {
            return "stage";
        }
        // return null to hide this action
        return null;
    }

    public boolean hasStagingPermission() {
        return getACL().hasPermission(getPermission());
    }

    @Override
    protected Permission getPermission() {
        return ArtifactoryPlugin.STAGE;
    }

    @Override
    protected ACL getACL() {
        return build.getACL();
    }

    public AbstractBuild getBuild() {
        return build;
    }

    public void setTargetStatus(String targetStatus) {
        this.targetStatus = targetStatus;
    }

    public void setRepositoryKey(String repositoryKey) {
        this.repositoryKey = repositoryKey;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public void setUseCopy(boolean useCopy) {
        this.useCopy = useCopy;
    }

    public void setIncludeDependencies(boolean includeDependencies) {
        this.includeDependencies = includeDependencies;
    }

    /**
     * @return List of target repositories for deployment (release repositories first). Called from the UI.
     */
    @SuppressWarnings({"UnusedDeclaration"})
    public List<String> getRepositoryKeys() {
        ArtifactoryRedeployPublisher artifactoryPublisher = ActionableHelper.getPublisher(
                build.getProject(), ArtifactoryRedeployPublisher.class);
        if (artifactoryPublisher != null) {
            List<String> repos = artifactoryPublisher.getArtifactoryServer().getReleaseRepositoryKeysFirst();
            repos.add(0, "");  // option not to move
            return repos;
        } else {
            return Collections.emptyList();
        }
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public List<String> getTargetStatuses() {
        return Lists.newArrayList(/*"Staged", */"Released", "Rolled-back");
    }

    /**
     * @return The repository selected by the latest promotion (to be selected by default).
     */
    public String lastPromotionRepository() {
        // TODO: implement
        return null;
    }

    /**
     * Select which view to display based on the state of the promotion. Will return the form if user selects to perform
     * promotion. Progress will be returned if the promotion is currently in progress.
     */
    @SuppressWarnings({"UnusedDeclaration"})
    public void doIndex(StaplerRequest req, StaplerResponse resp) throws IOException, ServletException {
        req.getView(this, chooseAction()).forward(req, resp);
    }

    private synchronized String chooseAction() {
        return workerThread == null ? "form.jelly" : "progress.jelly";
    }

    /**
     * Form submission is calling this method
     */
    @SuppressWarnings({"UnusedDeclaration"})
    public void doSubmit(StaplerRequest req, StaplerResponse resp) throws IOException, ServletException {
        getACL().checkPermission(getPermission());

        req.bindParameters(this);
        ArtifactoryRedeployPublisher artifactoryPublisher = ActionableHelper.getPublisher(
                build.getProject(), ArtifactoryRedeployPublisher.class);
        ArtifactoryServer server = getArtifactoryServer();

        new StageWorkerThread(server, getCredentials(server)).start();

        resp.sendRedirect(".");
    }

    /**
     * @return The Artifactory server that is used for the build.
     */
    protected ArtifactoryServer getArtifactoryServer() {
        ArtifactoryRedeployPublisher artifactoryPublisher = ActionableHelper.getPublisher(
                build.getProject(), ArtifactoryRedeployPublisher.class);
        return artifactoryPublisher.getArtifactoryServer();
    }

    /**
     * @param server The Artifactory server that is used for the build.
     * @return The credentials that were used for this server.
     */
    protected Credentials getCredentials(ArtifactoryServer server) {
        ArtifactoryRedeployPublisher artifactoryPublisher = ActionableHelper.getPublisher(
                build.getProject(), ArtifactoryRedeployPublisher.class);
        Credentials deployer = CredentialResolver.getPreferredDeployer(artifactoryPublisher, server);
        return deployer;
    }

    /**
     * The thread that performs the promotion asynchronously.
     */
    public final class StageWorkerThread extends TaskThread {

        private final ArtifactoryServer artifactoryServer;
        private final Credentials deployer;

        public StageWorkerThread(ArtifactoryServer artifactoryServer, Credentials deployer) {
            super(StageBuildAction.this, ListenerAndText.forMemory(null));
            this.artifactoryServer = artifactoryServer;
            this.deployer = deployer;
        }

        @Override
        protected void perform(TaskListener listener) {
            ArtifactoryBuildInfoClient client = null;
            try {
                listener.getLogger().println("Promoting build ....");

                client = artifactoryServer.createArtifactoryClient(deployer.getUsername(), deployer.getPassword());

                Cause.UserCause userCause = ActionableHelper.getUserCause(build);
                String username = null;
                if (userCause != null) {
                    username = userCause.getUserName();
                }

                // do a dry run first
                PromotionBuilder promotionBuilder = new PromotionBuilder()
                        .status(targetStatus)
                        .comment(comment)
                        .ciUser(username)
                        .targetRepo(repositoryKey)
                        .dependencies(includeDependencies)
                        .copy(useCopy)
                        .dryRun(true);
                listener.getLogger().println("Performing dry run staging (no changes are made during dry run) ...");
{'fraction_non_alphanumeric': 0.08447517284726587, 'fraction_numerical': 0.0013199245757385292, 'mean_word_length': 2.8675255226057366, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3434146', 'n_tokens_mistral': 4287, 'n_tokens_neox': 3999, 'n_words': 1101}