Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: don't trigger poll unless target ref matches #270

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions src/main/java/com/cloudbees/jenkins/GitHubBranch.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.cloudbees.jenkins;

import hudson.plugins.git.BranchSpec;

import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Identifies a GitHub branch and hides the SCM branch from its clients.
*/
public class GitHubBranch {

private static final String GITHUB_BRANCH_PREFIX = "refs/heads";
private static final int GITHUB_BRANCH_PREFIX_LENGTH = 10;

private final BranchSpec branch;

private GitHubBranch(final BranchSpec branch) {
this.branch = branch;
}

public boolean matches(GitHubRepositoryName repository, String ref) {
// ref (github) -> refs/heads/master
// branch (git SCM) -> REPO/remote/branch
// matching the meaningful part of the github branch name with
// the configured SCM branch expression
if (ref != null && !ref.equals("")) {

Check warning on line 27 in src/main/java/com/cloudbees/jenkins/GitHubBranch.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 27 is only partially covered, 2 branches are missing
String branchExpression = "*";
if (repository != null && repository.repositoryName != null) {

Check warning on line 29 in src/main/java/com/cloudbees/jenkins/GitHubBranch.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 29 is only partially covered, one branch is missing
branchExpression = repository.repositoryName;
}
if (ref.startsWith(GITHUB_BRANCH_PREFIX)) {
branchExpression += ref.substring(GITHUB_BRANCH_PREFIX_LENGTH);
} else {
branchExpression += "/" + ref;
}
LOGGER.log(Level.FINE, "Does SCM branch " + branch.getName()
+ " match GitHub branch " + branchExpression + "?");
return branch.matches(branchExpression);
}
return false;

Check warning on line 41 in src/main/java/com/cloudbees/jenkins/GitHubBranch.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 41 is not covered by tests
}

public static GitHubBranch create(BranchSpec branch) {
if (isValidGitHubBranch(branch)) {

Check warning on line 45 in src/main/java/com/cloudbees/jenkins/GitHubBranch.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 45 is only partially covered, one branch is missing
return new GitHubBranch(branch);
} else {
return null;

Check warning on line 48 in src/main/java/com/cloudbees/jenkins/GitHubBranch.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 48 is not covered by tests
}
}

// TODO: implement logic to validate git SCM branches.

Check warning on line 52 in src/main/java/com/cloudbees/jenkins/GitHubBranch.java

View check run for this annotation

ci.jenkins.io / Open Tasks Scanner

TODO

NORMAL: implement logic to validate git SCM branches.
private static boolean isValidGitHubBranch(BranchSpec branch) {
return true;
}

private static final Logger LOGGER = Logger.getLogger(GitHubBranch.class
.getName());
}
23 changes: 15 additions & 8 deletions src/main/java/com/cloudbees/jenkins/GitHubPushCause.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,38 +19,45 @@
* The name of the user who pushed to GitHub.
*/
private String pushedBy;
/**
* The target ref of the triggering GitHub push.
*/
private String ref;

public GitHubPushCause(String pusher) {
this("", pusher);
public GitHubPushCause(String pusher, String ref) {
this("", pusher, ref);
}

public GitHubPushCause(String pollingLog, String pusher) {
public GitHubPushCause(String pollingLog, String pusher, String ref) {
super(pollingLog);
pushedBy = pusher;
this.pushedBy = pusher;
this.ref = ref;

Check warning on line 34 in src/main/java/com/cloudbees/jenkins/GitHubPushCause.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 28-34 are not covered by tests
}

public GitHubPushCause(File pollingLog, String pusher) throws IOException {
public GitHubPushCause(File pollingLog, String pusher, String ref) throws IOException {
super(pollingLog);
pushedBy = pusher;
this.pushedBy = pusher;
this.ref = ref;
}

@Override
public String getShortDescription() {
return format("Started by GitHub push by %s", trimToEmpty(pushedBy));
return format("Started by GitHub push to %s by %s", trimToEmpty(ref), trimToEmpty(pushedBy));
}

@Override
public boolean equals(Object o) {
return o instanceof GitHubPushCause
&& Objects.equals(this.pushedBy, ((GitHubPushCause) o).pushedBy)
&& Objects.equals(this.ref, ((GitHubPushCause) o).ref)

Check warning on line 52 in src/main/java/com/cloudbees/jenkins/GitHubPushCause.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 52 is not covered by tests
&& super.equals(o);
}

@Override
public int hashCode() {
int hash = super.hashCode();
hash = 89 * hash + Objects.hash(this.pushedBy);
hash = 89 * hash + Objects.hash(this.ref);
return hash;
}
}

16 changes: 13 additions & 3 deletions src/main/java/com/cloudbees/jenkins/GitHubPushTrigger.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,11 @@
/**
* Called when a POST is made.
*/
public void onPost(String triggeredByUser) {
public void onPost(String triggeredByUser, String triggeredByRef) {
onPost(GitHubTriggerEvent.create()
.withOrigin(SCMEvent.originOf(Stapler.getCurrentRequest2()))
.withTriggeredByUser(triggeredByUser)
.withTriggeredByRef(triggeredByRef)
.build()
);
}
Expand All @@ -103,6 +104,7 @@
Job<?, ?> currentJob = notNull(job, "Job can't be null");

final String pushBy = event.getTriggeredByUser();
final String ref = event.getTriggeredByRef();
DescriptorImpl d = getDescriptor();
d.checkThreadPoolSizeAndUpdateIfNecessary();
d.queue.execute(new Runnable() {
Expand Down Expand Up @@ -151,50 +153,58 @@
if (runPolling()) {
GitHubPushCause cause;
try {
cause = new GitHubPushCause(getLogFileForJob(currentJob), pushBy);
cause = new GitHubPushCause(getLogFileForJob(currentJob), pushBy, ref);
} catch (IOException e) {
LOGGER.warn("Failed to parse the polling log", e);
cause = new GitHubPushCause(pushBy);
cause = new GitHubPushCause(pushBy, ref);
}

if (asParameterizedJobMixIn(currentJob).scheduleBuild(cause)) {
LOGGER.info("SCM changes detected in " + currentJob.getFullName()
+ ". Triggering #" + currentJob.getNextBuildNumber());
} else {
LOGGER.info("SCM changes detected in " + currentJob.getFullName()
+ ". Job is already in the queue");
}
}
}
});
}

/**
* Returns the file that records the last/current polling activity.
*/
public File getLogFile() {
try {
return getLogFileForJob(notNull(job, "Job can't be null!"));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}

/**
* Returns the file that records the last/current polling activity.
*/
private File getLogFileForJob(@NonNull Job job) throws IOException {
return new File(job.getRootDir(), "github-polling.log");
}

/**
* @deprecated Use {@link GitHubRepositoryNameContributor#parseAssociatedNames(AbstractProject)}
*/
@Deprecated
public Set<GitHubRepositoryName> getGitHubRepositories() {
return Collections.emptySet();
}

/**
* @deprecated
* Use {@link GitHubRepositoryNameContributor#parseAssociatedBranches(AbstractProject)}
*/
public Set<GitHubBranch> getGitHubBranches() {

Check warning on line 204 in src/main/java/com/cloudbees/jenkins/GitHubPushTrigger.java

View check run for this annotation

ci.jenkins.io / Java Compiler

compiler:compile

NORMAL: deprecated item is not annotated with @deprecated
return Collections.emptySet();

Check warning on line 205 in src/main/java/com/cloudbees/jenkins/GitHubPushTrigger.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 159-205 are not covered by tests
}

@Override
public void start(Job<?, ?> project, boolean newInstance) {
super.start(project, newInstance);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import hudson.model.Item;
import hudson.model.Job;
import hudson.model.TaskListener;
import hudson.plugins.git.BranchSpec;
import hudson.plugins.git.GitSCM;
import hudson.scm.SCM;
import jenkins.model.Jenkins;
Expand Down Expand Up @@ -90,34 +91,90 @@
}
}

/**
* Looks at the definition of {@link Item} and list up its branches, then puts them into
* the collection.
*/
public /*abstract*/ void parseAssociatedBranches(Item item, Collection<GitHubBranch> result) {
if (Util.isOverridden(
GitHubRepositoryNameContributor.class,
getClass(),
"parseAssociatedBranches",
Job.class,
Collection.class
)) {
// if this impl is legacy, it cannot contribute to non-jobs, so not an error
if (item instanceof Job) {
parseAssociatedBranches((Job<?, ?>) item, result);
}
} else if (Util.isOverridden(
GitHubRepositoryNameContributor.class,
getClass(),
"parseAssociatedBranches",
AbstractProject.class,
Collection.class
)) {
// if this impl is legacy, it cannot contribute to non-projects, so not an error
if (item instanceof AbstractProject) {
parseAssociatedBranches((AbstractProject<?, ?>) item, result);
}
} else {
throw new AbstractMethodError("you must override the new overload of parseAssociatedBranches");
}
};

public static ExtensionList<GitHubRepositoryNameContributor> all() {
return Jenkins.getInstance().getExtensionList(GitHubRepositoryNameContributor.class);
}

/**
* @deprecated Use {@link #parseAssociatedNames(Job)}
*/
@Deprecated
public static Collection<GitHubRepositoryName> parseAssociatedNames(AbstractProject<?, ?> job) {
return parseAssociatedNames((Item) job);
}

/**
* @deprecated Use {@link #parseAssociatedNames(Item)}
*/
@Deprecated
public static Collection<GitHubRepositoryName> parseAssociatedNames(Job<?, ?> job) {
return parseAssociatedNames((Item) job);
}

public static Collection<GitHubRepositoryName> parseAssociatedNames(Item item) {
Set<GitHubRepositoryName> names = new HashSet<GitHubRepositoryName>();
for (GitHubRepositoryNameContributor c : all()) {
c.parseAssociatedNames(item, names);
}
return names;
}

/**
* @deprecated Use {@link #parseAssociatedBranches(Job)}
*/
@Deprecated
public static Collection<GitHubBranch> parseAssociatedBranches(AbstractProject<?, ?> job) {
return parseAssociatedBranches((Item) job);
}

/**
* @deprecated Use {@link #parseAssociatedBranches(Item)}
*/
@Deprecated
public static Collection<GitHubBranch> parseAssociatedBranches(Job<?, ?> job) {
return parseAssociatedBranches((Item) job);

Check warning on line 167 in src/main/java/com/cloudbees/jenkins/GitHubRepositoryNameContributor.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 99-167 are not covered by tests
}

public static Collection<GitHubBranch> parseAssociatedBranches(Item item) {
Set<GitHubBranch> names = new HashSet<GitHubBranch>();
for (GitHubRepositoryNameContributor c : all()) {
c.parseAssociatedBranches(item, names);
}
return names;
}

/**
* Default implementation that looks at SCMs
*/
Expand All @@ -134,6 +191,16 @@
}
}

@Override
public void parseAssociatedBranches(Item item, Collection<GitHubBranch> result) {
SCMTriggerItem triggerItem = SCMTriggerItems.asSCMTriggerItem(item);
if (triggerItem != null) {

Check warning on line 197 in src/main/java/com/cloudbees/jenkins/GitHubRepositoryNameContributor.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 197 is only partially covered, one branch is missing
for (SCM scm : triggerItem.getSCMs()) {
addBranches(scm, result);
}
}
}

protected EnvVars buildEnv(Job<?, ?> job) {
EnvVars env = new EnvVars();
for (EnvironmentContributor contributor : EnvironmentContributor.all()) {
Expand All @@ -160,5 +227,17 @@
}
}
}

protected static void addBranches(SCM scm, Collection<GitHubBranch> r) {
if (scm instanceof GitSCM) {

Check warning on line 232 in src/main/java/com/cloudbees/jenkins/GitHubRepositoryNameContributor.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 232 is only partially covered, one branch is missing
GitSCM git = (GitSCM) scm;
for (BranchSpec branch : git.getBranches()) {
GitHubBranch gitHubBranch = GitHubBranch.create(branch);
if (gitHubBranch != null) {

Check warning on line 236 in src/main/java/com/cloudbees/jenkins/GitHubRepositoryNameContributor.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 236 is only partially covered, one branch is missing
r.add(gitHubBranch);
}
}
}
}
}
}
31 changes: 29 additions & 2 deletions src/main/java/com/cloudbees/jenkins/GitHubTrigger.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
void onPost();

// TODO: document me
void onPost(String triggeredByUser);
void onPost(String triggeredByUser, String triggeredByRef);

/**
* Obtains the list of the repositories that this trigger is looking at.
Expand All @@ -40,9 +40,26 @@
*/
Set<GitHubRepositoryName> getGitHubRepositories();

/**
* Obtains the list of the branches that this trigger is looking at.
*
* If the implementation of this class maintain its own list of GitHub branches, it should
* continue to implement this method for backward compatibility, and it gets picked up by
* {@link GitHubRepositoryNameContributor#parseAssociatedBranches(AbstractProject)}.
*
* <p>
* Alternatively, if the implementation doesn't worry about the backward compatibility, it can
* implement this method to return an empty collection, then just implement {@link GitHubRepositoryNameContributor}.
*
* @deprecated
* Call {@link GitHubRepositoryNameContributor#parseAssociatedBranches(AbstractProject)} instead.
*/
Set<GitHubBranch> getGitHubBranches();

Check warning on line 57 in src/main/java/com/cloudbees/jenkins/GitHubTrigger.java

View check run for this annotation

ci.jenkins.io / Java Compiler

compiler:compile

NORMAL: deprecated item is not annotated with @deprecated

/**
* Contributes {@link GitHubRepositoryName} from {@link GitHubTrigger#getGitHubRepositories()}
* for backward compatibility
* and {@link GitHubBranch} from {@link GitHubTrigger#getGitHubBranches()} for
* backward compatibility.
*/
@Extension
class GitHubRepositoryNameContributorImpl extends GitHubRepositoryNameContributor {
Expand All @@ -56,5 +73,15 @@
}
}
}

@Override
public void parseAssociatedBranches(Item item, Collection<GitHubBranch> result) {
if (item instanceof ParameterizedJobMixIn.ParameterizedJob) {

Check warning on line 79 in src/main/java/com/cloudbees/jenkins/GitHubTrigger.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 79 is only partially covered, one branch is missing
ParameterizedJobMixIn.ParameterizedJob p = (ParameterizedJobMixIn.ParameterizedJob) item;
for (GitHubTrigger ght : Util.filter(p.getTriggers().values(), GitHubTrigger.class)) {
result.addAll(ght.getGitHubBranches());
}
}
}
}
}
Loading
Loading