diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 000000000..fa4f7b499 --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,110 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you 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. +*/ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = + "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: : " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100755 index 000000000..01e679973 Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100755 index 000000000..00d32aab1 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip \ No newline at end of file diff --git a/mvnw b/mvnw new file mode 100755 index 000000000..5551fde8e --- /dev/null +++ b/mvnw @@ -0,0 +1,286 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + wget "$jarUrl" -O "$wrapperJarPath" + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + curl -o "$wrapperJarPath" "$jarUrl" + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubIncludeRegionsTrait.java b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubIncludeRegionsTrait.java new file mode 100644 index 000000000..aa5884b15 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubIncludeRegionsTrait.java @@ -0,0 +1,382 @@ +/* + * The MIT License + * + * Copyright (c) 2017, CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jenkinsci.plugins.github_branch_source; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import hudson.Extension; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import jenkins.scm.api.SCMHeadCategory; +import jenkins.scm.api.SCMHeadEvent; +import jenkins.scm.api.SCMSource; +import jenkins.scm.api.SCMSourceOwner; +import jenkins.scm.api.trait.SCMSourceContext; +import jenkins.scm.api.trait.SCMSourceTrait; +import jenkins.scm.api.trait.SCMSourceTraitDescriptor; +import jenkins.scm.impl.trait.Discovery; +import org.antlr.v4.runtime.misc.NotNull; +import org.apache.tools.ant.types.selectors.SelectorUtils; +import org.jenkinsci.Symbol; +import org.kohsuke.github.GHBranch; +import org.kohsuke.github.GHEventPayload; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A {@link Discovery} trait for GitHub that will discover branches on the repository. + * + * @since 2.2.0 + */ + +/* + TODO: Update pull request logic to not create a PR job if PR event has no relevant changes to job pattern +*/ + +public class GitHubIncludeRegionsTrait extends SCMSourceTrait { + private static final Logger LOGGER = LoggerFactory.getLogger(GitHubIncludeRegionsTrait.class); + + /** The regions to include for this multi branch project */ + private final String includeRegions; + + private Map lastMatchedShas; + private String matchedShas; + + /** + * Constructor for stapler. + * + * @param includeRegions the strategy id. + */ + @DataBoundConstructor + public GitHubIncludeRegionsTrait(String includeRegions) { + this.includeRegions = includeRegions; + this.matchedShas = ""; + this.lastMatchedShas = new HashMap<>(); + } + + /** + * Returns the included regions + * + * @return the included regions string. + */ + public String getIncludeRegions() { + return this.includeRegions; + } + + public synchronized String getMatchedShas() { + return this.matchedShas; + } + + public Map getLastMatchedShas() { + this.lastMatchedShas = getMatchedShaMap(); + return this.lastMatchedShas; + } + + public List getIncludeRegionsList() { + return Arrays.stream(this.includeRegions.split("\n")) + .map(e -> e.trim()) + .collect(Collectors.toList()); + } + + @CheckForNull + public String getLastMatchedShaForBranch(String branch) { + return this.getLastMatchedShas().get(branch); + } + + public void putLastMatchedShaForBranch(String branch, String lastMatchedSHA) { + this.getLastMatchedShas().put(branch, lastMatchedSHA); + this.setMatchedShaString(); + } + + @DataBoundSetter + public synchronized void setMatchedShas(String shas) { + this.matchedShas = shas; + } + + public synchronized Map getMatchedShaMap() { + LOGGER.info("building sha map from string"); + long start_time = System.nanoTime(); + + HashMap lastMatchedShas = new HashMap<>(); + if (this.getMatchedShas() == null) { + this.setMatchedShas(""); + } + + Arrays.stream(this.matchedShas.split("\n")) + .forEach( + e -> { + String[] parts = e.split(":"); + if (parts.length == 2) { + lastMatchedShas.put(parts[0], parts[1]); + } + }); + + long end_time = System.nanoTime(); + LOGGER.info("built map in {} ms", (end_time - start_time) / 1e6); + return lastMatchedShas; + } + + public synchronized void setMatchedShaString() { + LOGGER.info("building sha string from map"); + long start_time = System.nanoTime(); + + StringBuilder collectedMatches = new StringBuilder(); + for (Map.Entry match : this.lastMatchedShas.entrySet()) { + collectedMatches.append(match.getKey()); + collectedMatches.append(":"); + collectedMatches.append(match.getValue()); + collectedMatches.append("\n"); + } + + this.setMatchedShas(collectedMatches.toString()); + long end_time = System.nanoTime(); + LOGGER.info("built string in {} ms", (end_time - start_time) / 1e6); + } + + /** {@inheritDoc} */ + @Override + protected void decorateContext(SCMSourceContext context) { + GitHubSCMSourceContext ctx = (GitHubSCMSourceContext) context; + ctx.wantBranches(true); + ctx.withAuthority(new BranchDiscoveryTrait.BranchSCMHeadAuthority()); + } + + /** {@inheritDoc} */ + @Override + public boolean includeCategory(@NonNull SCMHeadCategory category) { + return category.isUncategorized(); + } + + @NotNull + public boolean matchFilesToIncludedRegions(HashMap> changedFiles) { + List includedRegions = this.getIncludeRegionsList(); + for (Map.Entry> entry : changedFiles.entrySet()) { + for (String includedRegionPattern : includedRegions) { + for (String filePath : entry.getValue()) { + if (SelectorUtils.matchPath(includedRegionPattern, filePath)) { + LOGGER.info( + "Found commit {} with changed file {} matching pattern {}", + entry.getKey(), + filePath, + includedRegionPattern); + return true; + } + } + } + } + + LOGGER.info("No commits had matching files changed"); + return false; + } + + public static String isBuildableSCMEvent( + @NotNull SCMSourceOwner owner, @NotNull SCMHeadEvent event) { + String logPrefix = "[" + owner.getFullName() + "]:"; + + GHEventPayload.Push payload = getGHEventPayload(event); + String branch = getBranchFromEvent(event); + if (payload == null || branch == null) { + LOGGER.info("{} Could not parse payload: {} or branch: {}", logPrefix, payload, branch); + return ""; + } + + HashMap> commits = collectCommits(payload); + List traits = collectTraitsFromOwner(owner); + + // No GithubIncludedRegionTraits were defined for this job owner + if (traits.isEmpty()) { + LOGGER.info( + "{} No GithubIncludedRegionTrait was defined. Proceeding with usual behavior", logPrefix); + return payload.getHead(); + } + + GitHubIncludeRegionsTrait ghTrait = traits.get(0); + boolean matched = ghTrait.matchFilesToIncludedRegions(commits); + + // We found a match against included regions + if (matched) { + LOGGER.info( + "{} Found a commit which matched our included regions -- setting last matched sha for branch {} to current head of {}", + logPrefix, + branch, + payload.getHead()); + ghTrait.putLastMatchedShaForBranch(branch, payload.getHead()); + return payload.getHead(); + } + + LOGGER.info( + "{} No files in commits: {} matched any included regions: {}", + logPrefix, + commits.keySet(), + ghTrait.getIncludeRegionsList()); + LOGGER.info("{} Attempting to get previously built commit for branch {}", logPrefix, branch); + String lastMatchedSha = ghTrait.getLastMatchedShaForBranch(branch); + + // Found previously set match + if (lastMatchedSha != null) { + LOGGER.info( + "{} Found previously set match ({}) for branch ({})", logPrefix, lastMatchedSha, branch); + return lastMatchedSha; + } + + LOGGER.info( + "{} Had no previously built commit for branch {} - using payload head {}", + logPrefix, + branch, + payload.getHead()); + ghTrait.putLastMatchedShaForBranch(branch, payload.getHead()); + return payload.getHead(); + } + + @NonNull + private static List collectTraitsFromOwner( + @NotNull SCMSourceOwner owner) { + ArrayList filtered = new ArrayList<>(); + for (SCMSource src : owner.getSCMSources()) { + List traits = src.getTraits() != null ? src.getTraits() : new ArrayList<>(); + + for (SCMSourceTrait trait : traits) { + if (trait instanceof GitHubIncludeRegionsTrait) { + filtered.add((GitHubIncludeRegionsTrait) trait); + } + } + } + + return filtered; + } + + static String getOrSetLastBuiltCommit(SCMSourceOwner owner, @NotNull GHBranch branch) { + if (owner == null) { + LOGGER.info("null owner - cant get or set last built commit for branch {}", branch.getName()); + return branch.getSHA1(); + } + + String logPrefix = "[" + owner.getFullName() + "]:"; + List traits = collectTraitsFromOwner(owner); + if (traits.isEmpty()) { + LOGGER.info("{} No GithubIncludedRegionTrait for owner", logPrefix); + return branch.getSHA1(); + } + + GitHubIncludeRegionsTrait ghTrait = traits.get(0); + String sha = ghTrait.getLastMatchedShaForBranch(branch.getName()); + if (sha != null) { + return sha; + } + + LOGGER.info( + "{} No sha was set for branch {} - setting value to {}", + logPrefix, + branch.getName(), + branch.getSHA1()); + ghTrait.putLastMatchedShaForBranch(branch.getName(), branch.getSHA1()); + return branch.getSHA1(); + } + + private static GHEventPayload.Push getGHEventPayload(@Nullable SCMHeadEvent event) { + if (event == null) { + return null; + } + + GHEventPayload.Push payload; + + try { + payload = (GHEventPayload.Push) event.getPayload(); + } catch (Exception e) { + LOGGER.error("Unable to cash event to GHEventPayload: " + e); + return null; + } + + return payload; + } + + @CheckForNull + static String getBranchFromEvent(@Nullable SCMHeadEvent event) { + GHEventPayload.Push payload = getGHEventPayload(event); + return getBranchFromPayload(payload); + } + + @CheckForNull + private static String getBranchFromPayload(@Nullable GHEventPayload.Push payload) { + if (payload == null) { + return null; + } + + String[] parts = payload.getRef().split("/"); + if (parts.length == 0) { + LOGGER.info("Could not parse branch from parts {}", parts.toString()); + return null; + } + + String branch = parts[parts.length - 1]; + LOGGER.info("Got branch {}", branch); + return branch; + } + + @NotNull + private static HashMap> collectCommits(GHEventPayload.Push p) { + HashMap> changesBySha = new HashMap<>(); + for (GHEventPayload.Push.PushCommit commit : p.getCommits()) { + List changes = new ArrayList<>(); + changes.addAll(commit.getAdded()); + changes.addAll(commit.getModified()); + changes.addAll(commit.getRemoved()); + changesBySha.put(commit.getSha(), changes); + } + return changesBySha; + } + + /** Our descriptor. */ + @Symbol("gitHubIncludeRegionsDiscovery") + @Extension + @Discovery + public static class DescriptorImpl extends SCMSourceTraitDescriptor { + + /** {@inheritDoc} */ + @Nonnull + public String getDisplayName() { + return "GitHub Include Regions"; + } + + /** {@inheritDoc} */ + @Override + public Class getContextClass() { + return GitHubSCMSourceContext.class; + } + + /** {@inheritDoc} */ + @Override + public Class getSourceClass() { + return GitHubSCMSource.class; + } + } +} diff --git a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubSCMSource.java b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubSCMSource.java index 7cf00e4b3..96d12552a 100644 --- a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubSCMSource.java +++ b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubSCMSource.java @@ -125,21 +125,7 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.accmod.restrictions.NoExternalUse; -import org.kohsuke.github.GHBranch; -import org.kohsuke.github.GHCommit; -import org.kohsuke.github.GHException; -import org.kohsuke.github.GHFileNotFoundException; -import org.kohsuke.github.GHIssueState; -import org.kohsuke.github.GHMyself; -import org.kohsuke.github.GHOrganization; -import org.kohsuke.github.GHPermissionType; -import org.kohsuke.github.GHPullRequest; -import org.kohsuke.github.GHRef; -import org.kohsuke.github.GHRepository; -import org.kohsuke.github.GHTagObject; -import org.kohsuke.github.GHUser; -import org.kohsuke.github.GitHub; -import org.kohsuke.github.HttpException; +import org.kohsuke.github.*; import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; @@ -927,8 +913,14 @@ protected final void retrieve( @CheckForNull SCMHeadEvent event, @NonNull final TaskListener listener) throws IOException, InterruptedException { - StandardCredentials credentials = - Connector.lookupScanCredentials((Item) getOwner(), apiUri, credentialsId); + SCMSourceOwner owner = getOwner(); + StandardCredentials credentials = Connector.lookupScanCredentials(owner, apiUri, credentialsId); + + String shaToProcess = ""; + if (owner != null && event != null) { + shaToProcess = GitHubIncludeRegionsTrait.isBuildableSCMEvent(owner, event); + } + // Github client and validation final GitHub github = Connector.connect(apiUri, credentials); try { @@ -984,6 +976,7 @@ public GHPermissionType fetch(String username) int count = 0; for (final GHBranch branch : request.getBranches()) { count++; + String branchName = branch.getName(); listener .getLogger() @@ -991,10 +984,34 @@ public GHPermissionType fetch(String username) "%n Checking branch %s%n", HyperlinkNote.encodeTo( resolvedRepositoryUrl + "/tree/" + branchName, branchName)); + + SCMRevisionImpl revision; BranchSCMHead head = new BranchSCMHead(branchName); + String eventBranchName = GitHubIncludeRegionsTrait.getBranchFromEvent(event); + + // We're processing an event for a gh webhook, rely on sha to process from above + if (branchName.equals(eventBranchName)) { + listener + .getLogger() + .format("%n Processing webhook event for branch %s %n", eventBranchName); + listener + .getLogger() + .format("%n Got commit SHA to process.. using %s %n", shaToProcess); + revision = new SCMRevisionImpl(head, shaToProcess); + } else { // we're not processing a webhook event, but rather a scan repo event + listener + .getLogger() + .format( + "%n Processing repo scan...getting last built commit for branch %s %n", + branch.getName()); + String sha = GitHubIncludeRegionsTrait.getOrSetLastBuiltCommit(owner, branch); + revision = new SCMRevisionImpl(head, sha); + } + + // TODO: fake processing this event if necessary / possible if (request.process( head, - new SCMRevisionImpl(head, branch.getSHA1()), + revision, new SCMSourceRequest.ProbeLambda() { @NonNull @Override diff --git a/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubIncludeRegionsTrait/config.jelly b/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubIncludeRegionsTrait/config.jelly new file mode 100644 index 000000000..c21f7d04b --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubIncludeRegionsTrait/config.jelly @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubIncludeRegionsTrait/help-includeRegions.html b/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubIncludeRegionsTrait/help-includeRegions.html new file mode 100644 index 000000000..cec43df57 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubIncludeRegionsTrait/help-includeRegions.html @@ -0,0 +1,11 @@ +
+ Each inclusion uses ant pattern matching, + and must be separated by a new line. + An empty list implies that nothing is included. +

+

+    src/main/java/**/*.java
+  
+ The example above illustrates that a build will only occur, if at least one java file + have been committed to the SCM in the src/main/java/ region. +
\ No newline at end of file