com.github.rwhogg.git_vcr.App.java Source code

Java tutorial

Introduction

Here is the source code for com.github.rwhogg.git_vcr.App.java

Source

/*
App.java: Main file for Git-VCR
       
Copyright  2015 Bob W. Hogg. All Rights Reserved.
       
This file is part of Git-VCR.
       
Git-VCR is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
       
Git-VCR is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
       
You should have received a copy of the GNU General Public License
along with Git-VCR.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.github.rwhogg.git_vcr;

import com.github.rwhogg.git_vcr.review.*;
import com.squareup.okhttp.*;
import gnu.getopt.*;
import java.awt.*;
import java.io.*;
import java.net.*;
import java.nio.charset.*;
import org.apache.commons.configuration.*;
import org.eclipse.jgit.api.*;
import org.eclipse.jgit.api.ResetCommand.*;
import org.eclipse.jgit.api.errors.*;
import org.eclipse.jgit.errors.*;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.patch.*;
import org.eclipse.jgit.storage.file.*;
import org.eclipse.jgit.submodule.*;

/**
 * App is the main class for Git-VCR
 */
public class App {
    /**
     * Usage displays a usage message and then terminates.
     */
    private static void usage() {
        System.out.printf("Usage: git vcr [-h | -v] <patch file> [branch]%n");
        System.out.printf(
                "Perform an automatic code review on the given patch file, optionally starting from the given branch.%n");
        System.out.println();
        System.out.printf("Options:%n");
        System.out.printf(" -h\tShow this help message%n");
        System.out.printf(" -v\tDisplay version and license information%n");
        System.out.println();
        System.out.printf("Exit Status:%n");
        System.out.printf(" 0 if ok%n");
        System.out.printf(" 1 if there was a problem%n");
        System.out.println();
        System.out.printf("Report bugs to: https://github.com/rwhogg/git-vcr/issues%n");
        System.out.printf("Git-VCR Home Page: <https://github.com/rwhogg/git-vcr>%n");
        System.exit(1);
    }

    /**
     * version displays version information and then terminates.
     */
    private static void version() {
        System.out.printf("%s %s%n", Constants.PROGRAM_NAME, Constants.VERSION);
        System.out.printf("Copyright  2015 - 2016, Bob W. Hogg. All Rights Reserved.%n");
        System.out.printf(
                "%s is free software; see the source for copying conditions. There is NO%nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.%n",
                Constants.PROGRAM_NAME);
        System.exit(1);
    }

    /**
     * parseCommandLine returns the options associated with the given arguments
     * @param args Command-line arguments
    */
    private static Options parseCommandLine(String[] args) {
        if (args.length == 0) {
            usage();
        }

        Getopt getopt = new Getopt("git-vcr", args, "hv");
        int c;
        while ((c = getopt.getopt()) != -1) {
            switch (c) {
            case 'h':
                usage();
                break;
            case 'v':
                version();
                break;
            case '?':
                System.exit(1);
            default:
                System.err.println("Error: getopt failed with status " + c);
            }
        }

        // get the patch
        int nonOptionIndex = getopt.getOptind();
        if (nonOptionIndex == args.length) {
            usage();
        }
        Options options = new Options();
        String patchString = args[nonOptionIndex];
        try {
            options.guessPatchUrl(patchString);
        } catch (MalformedURLException e) {
            Util.error("given URL " + patchString + " is incorrectly formatted!");
        } catch (FileNotFoundException e) {
            Util.error("given file " + patchString + " could not be read!");
        } catch (IOException e) {
            Util.error("could not determine MIME type of patch file " + patchString + "!");
        }

        // get the branch
        if (nonOptionIndex < args.length - 1) {
            options.setBranch(args[nonOptionIndex + 1]);
        }

        return options;
    }

    /**
     * main is the entry point for Git-VCR
     * @param args Command-line arguments
     */
    public static void main(String[] args) {
        Options options = parseCommandLine(args);

        HierarchicalINIConfiguration configuration = null;
        try {
            configuration = getConfiguration();
        } catch (ConfigurationException e) {
            Util.error("could not parse configuration file!");
        }

        // verify we are in a git folder and then construct the repo
        final File currentFolder = new File(".");
        FileRepositoryBuilder builder = new FileRepositoryBuilder();
        Repository localRepo = null;
        try {
            localRepo = builder.findGitDir().build();
        } catch (IOException e) {
            Util.error("not in a Git folder!");
        }

        // deal with submodules
        assert localRepo != null;
        if (localRepo.isBare()) {
            FileRepositoryBuilder parentBuilder = new FileRepositoryBuilder();
            Repository parentRepo;
            try {
                parentRepo = parentBuilder.setGitDir(new File("..")).findGitDir().build();
                localRepo = SubmoduleWalk.getSubmoduleRepository(parentRepo, currentFolder.getName());
            } catch (IOException e) {
                Util.error("could not find parent of submodule!");
            }
        }

        // if we need to retrieve the patch file, get it now
        URL patchUrl = options.getPatchUrl();
        String patchPath = patchUrl.getFile();
        File patchFile = null;
        HttpUrl httpUrl = HttpUrl.get(patchUrl);
        if (httpUrl != null) {
            try {
                patchFile = com.twitter.common.io.FileUtils.SYSTEM_TMP.createFile(".diff");
                Request request = new Request.Builder().url(httpUrl).build();
                OkHttpClient client = new OkHttpClient();
                Call call = client.newCall(request);
                Response response = call.execute();
                ResponseBody body = response.body();
                if (!response.isSuccessful()) {
                    Util.error("could not retrieve diff file from URL " + patchUrl);
                }
                String content = body.string();
                org.apache.commons.io.FileUtils.write(patchFile, content, (Charset) null);
            } catch (IOException ie) {
                Util.error("could not retrieve diff file from URL " + patchUrl);
            }
        } else {
            patchFile = new File(patchPath);
        }

        // find the patch
        //noinspection ConstantConditions
        if (!patchFile.canRead()) {
            Util.error("patch file " + patchFile.getAbsolutePath() + " is not readable!");
        }

        final Git git = new Git(localRepo);

        // handle the branch
        String branchName = options.getBranchName();
        String theOldCommit = null;
        try {
            theOldCommit = localRepo.getBranch();
        } catch (IOException e2) {
            Util.error("could not get reference to current branch!");
        }
        final String oldCommit = theOldCommit; // needed to reference from shutdown hook

        if (branchName != null) {
            // switch to the branch
            try {
                git.checkout().setName(branchName).call();
            } catch (RefAlreadyExistsException e) {
                // FIXME Auto-generated catch block
                e.printStackTrace();
            } catch (RefNotFoundException e) {
                Util.error("the branch " + branchName + " was not found!");
            } catch (InvalidRefNameException e) {
                Util.error("the branch name " + branchName + " is invalid!");
            } catch (org.eclipse.jgit.api.errors.CheckoutConflictException e) {
                Util.error("there was a checkout conflict!");
            } catch (GitAPIException e) {
                Util.error("there was an unspecified Git API failure!");
            }
        }

        // ensure there are no changes before we apply the patch
        try {
            if (!git.status().call().isClean()) {
                Util.error("cannot run git-vcr while there are uncommitted changes!");
            }
        } catch (NoWorkTreeException e1) {
            // won't happen
            assert false;
        } catch (GitAPIException e1) {
            Util.error("call to git status failed!");
        }

        // list all the files changed
        String patchName = patchFile.getName();
        Patch patch = new Patch();
        try {
            patch.parse(new FileInputStream(patchFile));
        } catch (FileNotFoundException e) {
            assert false;
        } catch (IOException e) {
            Util.error("could not parse the patch file!");
        }

        ReviewResults oldResults = new ReviewResults(patchName, patch, configuration, false);
        try {
            oldResults.review();
        } catch (InstantiationException e1) {
            Util.error("could not instantiate a review tool class!");
        } catch (IllegalAccessException e1) {
            Util.error("illegal access to a class");
        } catch (ClassNotFoundException e1) {
            Util.error("could not find a review tool class");
        } catch (ReviewFailedException e1) {
            e1.printStackTrace();
            Util.error("Review failed!");
        }

        // we're about to change the repo, so register a shutdown hook to clean it up
        Runtime.getRuntime().addShutdownHook(new Thread() {
            public void run() {
                cleanupGit(git, oldCommit);
            }
        });

        // apply the patch
        try {
            git.apply().setPatch(new FileInputStream(patchFile)).call();
        } catch (PatchFormatException e) {
            Util.error("patch file " + patchFile.getAbsolutePath() + " is malformatted!");
        } catch (PatchApplyException e) {
            Util.error("patch file " + patchFile.getAbsolutePath() + " did not apply correctly!");
        } catch (FileNotFoundException e) {
            assert false;
        } catch (GitAPIException e) {
            Util.error(e.getLocalizedMessage());
        }

        ReviewResults newResults = new ReviewResults(patchName, patch, configuration, true);
        try {
            newResults.review();
        } catch (InstantiationException e1) {
            Util.error("could not instantiate a review tool class!");
        } catch (IllegalAccessException e1) {
            Util.error("illegal access to a class");
        } catch (ClassNotFoundException e1) {
            Util.error("could not find a review tool class");
        } catch (ReviewFailedException e1) {
            e1.printStackTrace();
            Util.error("Review failed!");
        }

        // generate and show the report
        VelocityReport report = new VelocityReport(patch, oldResults, newResults);
        File reportFile = null;
        try {
            reportFile = com.twitter.common.io.FileUtils.SYSTEM_TMP.createFile(".html");
            org.apache.commons.io.FileUtils.write(reportFile, report.toString(), (String) null);
        } catch (IOException e) {
            Util.error("could not generate the results page!");
        }

        try {
            assert reportFile != null;
            Desktop.getDesktop().open(reportFile);
        } catch (IOException e) {
            Util.error("could not open the results page!");
        }
    }

    /**
     * Clean up the Git repo
     * @param git Git to use
     * @param oldCommit Hash of the commit to return to
     */
    private static void cleanupGit(Git git, String oldCommit) {
        try {
            git.reset().setMode(ResetType.HARD).call();
            git.checkout().setName(oldCommit).call();
        } catch (org.eclipse.jgit.api.errors.CheckoutConflictException e) {
            Util.error("there was an checkout conflict!");
        } catch (GitAPIException e) {
            Util.error("there was an unspecified Git API error!");
        }
        git.close();
    }

    /**
     * Returns configuration details from the default configuration file
     * @return the configuration details
     * @throws ConfigurationException if the configuration file is malformed or inadequate
     */
    public static HierarchicalINIConfiguration getConfiguration() throws ConfigurationException {
        return new HierarchicalINIConfiguration(Constants.CONFIG_FILENAME);
    }
}