package org.jenkinsci.plugins.p4.scm; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.Util; import hudson.model.Action; import hudson.model.TaskListener; import jenkins.scm.api.SCMHead; import jenkins.scm.api.SCMHeadCategory; import jenkins.scm.api.SCMHeadEvent; import jenkins.scm.api.SCMHeadObserver; import jenkins.scm.api.SCMProbe; import jenkins.scm.api.SCMRevision; import jenkins.scm.api.SCMSource; import jenkins.scm.api.SCMSourceCriteria; import jenkins.scm.api.SCMSourceEvent; import jenkins.scm.api.SCMSourceOwner; import jenkins.scm.api.mixin.ChangeRequestSCMHead; import jenkins.scm.api.trait.SCMSourceTrait; import jenkins.scm.impl.ChangeRequestSCMHeadCategory; import jenkins.scm.impl.TagSCMHeadCategory; import net.sf.json.JSONObject; import org.jenkinsci.plugins.p4.PerforceScm; import org.jenkinsci.plugins.p4.browsers.P4Browser; import org.jenkinsci.plugins.p4.changes.P4ChangeRef; import org.jenkinsci.plugins.p4.changes.P4Ref; import org.jenkinsci.plugins.p4.changes.P4RefBuilder; import org.jenkinsci.plugins.p4.client.ConnectionHelper; import org.jenkinsci.plugins.p4.client.ViewMapHelper; import org.jenkinsci.plugins.p4.credentials.P4BaseCredentials; import org.jenkinsci.plugins.p4.populate.Populate; import org.jenkinsci.plugins.p4.review.ReviewProp; import org.jenkinsci.plugins.p4.scm.events.P4BranchScanner; import org.jenkinsci.plugins.p4.workspace.ManualWorkspaceImpl; import org.jenkinsci.plugins.p4.workspace.Workspace; import org.jenkinsci.plugins.p4.workspace.WorkspaceSpec; import org.jenkinsci.plugins.workflow.multibranch.WorkflowBranchProjectFactory; import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject; import org.kohsuke.stapler.DataBoundSetter; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.logging.Logger; import static org.jenkinsci.plugins.p4.review.ReviewProp.P4_CHANGE; public abstract class AbstractP4SCMSource extends SCMSource { private static Logger logger = Logger.getLogger(AbstractP4SCMSource.class.getName()); protected final String credential; private List<SCMSourceTrait> traits = new ArrayList<>(); private String includes; private String charset; private String format; private Populate populate; public AbstractP4SCMSource(String credential) { this.credential = credential; } @DataBoundSetter public void setFormat(String format) { this.format = format; } @DataBoundSetter public void setPopulate(Populate populate) { this.populate = populate; } @DataBoundSetter public void setIncludes(String includes) { this.includes = includes; } @DataBoundSetter public void setCharset(String charset) { this.charset = charset; } @DataBoundSetter public void setTraits(@CheckForNull List<SCMSourceTrait> traits) { this.traits = new ArrayList<>(Util.fixNull(traits)); } public String getCredential() { return credential; } public List<SCMSourceTrait> getTraits() { if (traits == null) { traits = new ArrayList<>(); } return Collections.unmodifiableList(traits); } public String getIncludes() { return includes; } public String getCharset() { return charset; } public String getFormat() { return format; } public Populate getPopulate() { return populate; } public abstract P4Browser getBrowser(); public abstract List<P4SCMHead> getHeads(@NonNull TaskListener listener) throws Exception; public abstract List<P4SCMHead> getTags(@NonNull TaskListener listener) throws Exception; public Workspace getWorkspace(P4Path path) { if (path == null) { throw new IllegalArgumentException("missing path"); } StringBuffer depotView = new StringBuffer(); depotView.append(path.getPath()); depotView.append("/..."); String client = getFormat(); String view = ViewMapHelper.getClientView(depotView.toString(), client); WorkspaceSpec spec = new WorkspaceSpec(view, null); return new ManualWorkspaceImpl(getCharset(), false, client, spec); } public String getScriptPathOrDefault() { SCMSourceOwner owner = getOwner(); if (owner instanceof WorkflowMultiBranchProject) { WorkflowMultiBranchProject branchProject = (WorkflowMultiBranchProject) owner; WorkflowBranchProjectFactory branchProjectFactory = (WorkflowBranchProjectFactory) branchProject.getProjectFactory(); return branchProjectFactory.getScriptPath(); } return "Jenkinsfile"; } @Override protected void retrieve(@CheckForNull SCMSourceCriteria criteria, @NonNull SCMHeadObserver observer, @CheckForNull SCMHeadEvent<?> event, @NonNull TaskListener listener) throws IOException, InterruptedException { try { List<P4SCMHead> heads = getHeads(listener); List<P4SCMHead> tags = getTags(listener); heads.addAll(tags); for (P4SCMHead head : heads) { logger.fine("SCM: retrieve Head: " + head); // get SCMRevision from payload if trigger event, else build from head (latest) SCMRevision revision = getRevision(head, listener); if (event != null) { JSONObject payload = (JSONObject) event.getPayload(); P4SCMRevision rev = getRevision(payload); if (rev.getHead().equals(head)) { revision = rev; logger.fine("SCM: retrieve (trigger) Revision: " + revision); } else { if (rev.getHead() instanceof ChangeRequestSCMHead) { if (((P4ChangeRequestSCMHead) rev.getHead()).getPath().getPath().equals(head.getPath().getPath())) { revision = rev; logger.fine("SCM: retrieve (trigger) Swarm Review: " + revision); } } } } // null criteria means that all branches match. if (criteria == null) { // get revision and add observe observer.observe(head, revision); } else { try (ConnectionHelper p4 = new ConnectionHelper(getOwner(), credential, listener)) { SCMSourceCriteria.Probe probe = new P4SCMProbe(p4, head); if (criteria.isHead(probe, listener)) { logger.fine("SCM: observer head: " + head + " revision: " + revision); if (revision != null) { observer.observe(revision.getHead(), revision); } } } } // check for user abort checkInterrupt(); } } catch (Exception e) { throw new IOException(e.getMessage()); } } @Override protected SCMProbe createProbe(@NonNull SCMHead head, @CheckForNull SCMRevision revision) throws IOException { return newProbe(head, revision); } @Override public PerforceScm build(@NonNull SCMHead head, @CheckForNull SCMRevision revision) { return new P4SCMBuilder(this, head, revision).withTraits(getTraits()).build(); } /** * SCMSource level action. `jenkins.branch.MetadataAction` * * @param event Optional event (might be null) use payload to help filter calls. * @param listener the listener to report progress on. * @return the list of {@link Action} instances to persist. * @throws IOException if an error occurs while performing the operation. * @throws InterruptedException if any thread has interrupted the current thread. */ @Override protected List<Action> retrieveActions(@CheckForNull SCMSourceEvent event, @NonNull TaskListener listener) throws IOException, InterruptedException { List<Action> result = new ArrayList<>(); return result; } /** * SCMHead level action. * * @param head Changes on a branch * @param event Optional event (might be null) use payload to help filter calls. * @param listener the listener to report progress on. * @return the list of {@link Action} instances to persist. * @throws IOException if an error occurs while performing the operation. * @throws InterruptedException if any thread has interrupted the current thread. */ @Override protected List<Action> retrieveActions(@NonNull SCMHead head, @CheckForNull SCMHeadEvent event, @NonNull TaskListener listener) throws IOException, InterruptedException { List<Action> result = new ArrayList<>(); return result; } /** * SCMRevision level action. * * @param revision the {@link SCMRevision} * @param event Optional event (might be null) use payload to help filter calls. * @param listener the listener to report progress on. * @return the list of {@link Action} instances to persist. * @throws IOException if an error occurs while performing the operation. * @throws InterruptedException if any thread has interrupted the current thread. */ @Override protected List<Action> retrieveActions(@NonNull SCMRevision revision, @CheckForNull SCMHeadEvent event, @NonNull TaskListener listener) throws IOException, InterruptedException { List<Action> result = new ArrayList<>(); return result; } /** * Enable specific SCMHeadCategory categories. * <p> * TagSCMHeadCategory: Branches, Streams, Swarm and Graph * ChangeRequestSCMHeadCategory: Swarm and Graph * * @param category the Category * @return {@code true} if the supplied category is enabled for this {@link SCMSource} instance. */ @Override protected boolean isCategoryEnabled(@NonNull SCMHeadCategory category) { if (category instanceof ChangeRequestSCMHeadCategory) { return true; } if (category instanceof TagSCMHeadCategory) { return true; } return true; } public List<String> getIncludePaths() { return toLines(includes); } protected List<String> toLines(String value) { if (value == null) { return new ArrayList<>(); } String[] array = value.split("[\\r\\n]+"); return Arrays.asList(array); } /** * Get the Latest change for the path specified in P4SCMHead. * * @param head SCMHead * @param listener for logging * @return The latest change as a P4SCMRevision object * @throws Exception pushed up stack */ public P4SCMRevision getRevision(P4SCMHead head, TaskListener listener) throws Exception { try (ConnectionHelper p4 = new ConnectionHelper(getOwner(), credential, listener)) { // TODO look for graph revisions too long change = -1; P4Path path = head.getPath(); String rev = path.getRevision(); rev = (rev != null && !rev.isEmpty()) ? "/...@" + rev : "/..."; long c = p4.getHead(path.getPath() + rev); change = (c > change) ? c : change; P4SCMRevision revision = new P4SCMRevision(head, new P4ChangeRef(change)); return revision; } } /** * A specific revision based on the Event Payload. * * @param payload JSON payload from an external Event * @return the change as a P4SCMRevision object or null if no match. */ public P4SCMRevision getRevision(JSONObject payload) { // Verify Change is set in JSON String change = getProperty(payload, P4_CHANGE); if (change == null) { return null; } P4Ref ref = P4RefBuilder.get(change); P4BranchScanner scanner = getScanner(ref); if (scanner == null) { return null; } String path = scanner.getProjectRoot(); String branch = scanner.getBranch(); return P4SCMRevision.builder(path, branch, ref); } /** * Scans for a Jenkinsfile given a submitted change. * <p> * Looks a the first submitted file and walks up the path looking for a Jenkinsfile. * * @param ref A Perforce Change or Label * @return Scanning results. */ protected P4BranchScanner getScanner(P4Ref ref) { P4BaseCredentials baseCredentials = ConnectionHelper.findCredential(credential, getOwner()); P4BranchScanner scanner = new P4BranchScanner(baseCredentials, ref, getScriptPathOrDefault()); // Check matching Project path included in Source if (scanner.getProjectRoot() == null || !findInclude(scanner.getProjectRoot())) { return null; } return scanner; } protected boolean findInclude(String path) { path = (path.endsWith("/*")) ? path.substring(0, path.lastIndexOf("/*")) : path; path = (path.endsWith("/...")) ? path.substring(0, path.lastIndexOf("/...")) : path; List<String> includes = getIncludePaths(); for (String i : includes) { if (i.startsWith(path)) { return true; } } return false; } protected String getProperty(JSONObject payload, ReviewProp property) { if (!payload.has(property.getProp())) { return null; } String value = payload.getString(property.getProp()); if (value == null || value.isEmpty()) { return null; } return value; } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#5 | 24662 | Paul Allen |
Undo *SCMSource rename to original *ScmSource. Reslove configuration loading issue due to 1.9.0 bad release. |
||
#4 | 24597 | Paul Allen | Fix P4Path bug on MultiBranch to include branch. | ||
#3 | 24571 | Paul Allen |
Support for Swarm pre-commit review events. Swarm pre-commit review event creates a new 'Reviews' Tag in the MultiBranch and builds with specified change and shelf. |
||
#2 | 24529 | Paul Allen |
Swarm Commit Event support, refactoring and test. JENKINS-52605 (Fix doc in 'Includes' help bubble for MultiBranch) JENKINS-52066 (Improve Swarm Commit and Branch Event support) |
||
#1 | 24504 | Paul Allen |
Tree walter for BranchSCMSource Events. On a change-submit event use the change number and grab a submitted file. Walk up the path looking for a Jenkinsfile, then derive 'Project' path, 'Branch' name and 'Path' for the SCMHead/Revision. This assumes that the Jenkinsfile is in the route of your projects. Alternatively use the Swarm Event and pass your own 'Project', 'Branch' and 'Path'. |
||
//guest/perforce_software/p4jenkins/main/src/main/java/org/jenkinsci/plugins/p4/scm/AbstractP4ScmSource.java | |||||
#19 | 24501 | Paul Allen | Refactor to use P4SCMXxx naming convention. | ||
#18 | 24497 | Paul Allen |
Force use of revision for Head. Update P4Head->P4Path revision with P4Revision to avoid builds on unbounded 'latest'. |
||
#17 | 24492 | Paul Allen |
Initial work for MultiBranch Event trigger. JENKINS-52066 (Triggered Events and not Polling per change) |
||
#16 | 24454 | Paul Allen |
Exclude and Include mapping support. Refactored P4Path usage and added P4SwarmPath. JENKINS-49804 |
||
#15 | 23373 | Paul Allen |
Lightweight checkout support. Implementation for scm-api classes SCMFile and SCMFileSystem, allowing Jenkins to navigate Perforce within the scope of a workspace view. Lightweight checkout uses a tempoary Perforce workspace to naviagete and fetch the files. The client name and client view mapping will be modified from a template name e.g. jenkins-${NODE_NAME}-${JOB_NAME} to the tempoary name jenkinsTemp-UUID. Alternativly if a user as used ${P4_CLIENT} in the client mapping this will remain unchanged and will be get expanded during the job run. JENKINS-45999 JENKINS-46269 |
||
#14 | 23327 | Paul Allen | Use client less connection when scanning MultiBranch. | ||
#13 | 23324 | Paul Allen | Use short lived temporary client when scanning MultiBranch. | ||
#12 | 23320 | Paul Allen | Remove deprecated ID for SCMSource constructor. | ||
#11 | 23261 | Paul Allen | Backwards compatibility for new Mappings field in MultiBranch. | ||
#10 | 23217 | Paul Allen |
Add the ability to provide a view spec to Helix Branches SCM Pr/61. Review Pr/64 |
||
#9 | 23028 | Paul Allen |
MultiBranch docs and tidy up. Move getScriptPathOrDefault and getWorkspace from Helix Branches to AbstractP4ScmSource. |
||
#8 | 22875 | Paul Allen | Refactor P4Path and Swarm API | ||
#7 | 22866 | Paul Allen |
Merge pull request #55 from jenkinsci/hth Global Pipeline Library support. JENKINS-46121 JENKINS-46550 |
||
#6 | 22863 | Paul Allen | Store change in P4Revision as P4Ref. | ||
#5 | 22678 | Paul Allen |
MultiBranch support for Helix4Git (GitHub PR model). Update MultiBranch product names to Helix. |
||
#4 | 22396 | Paul Allen |
MultiBranch support for Perforce Swarm Reviews. A work in progress - adds Swarm API support to find branches and reviews from a Swarm project. #review-22354 |
||
#3 | 22128 | Paul Allen |
Helix Graph support for Jenkins. JENKINS-40354 |
||
#2 | 21794 | Paul Allen |
Merge pull request #39 from Dohbedoh/JENKINS-34825 Jenkins 34825 |
||
#1 | 21758 | Paul Allen |
Merge pull request #38 from jenkinsci/dev scm-api 2.0.2 updates |