package org.jenkinsci.plugins.p4_client; import hudson.Extension; import hudson.FilePath; import hudson.Launcher; import hudson.model.BuildListener; import hudson.model.TaskListener; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.Node; import hudson.scm.ChangeLogParser; import hudson.scm.PollingResult; import hudson.scm.SCMDescriptor; import hudson.scm.SCMRevisionState; import hudson.scm.SCM; import hudson.security.ACL; import hudson.util.FormValidation; import hudson.util.ListBoxModel; import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.logging.Logger; import jenkins.model.Jenkins; import net.sf.json.JSONObject; import org.acegisecurity.Authentication; import org.jenkinsci.plugins.p4_client.browsers.P4Browser; import org.jenkinsci.plugins.p4_client.changes.P4ChangeParser; import org.jenkinsci.plugins.p4_client.changes.P4ChangeSet; import org.jenkinsci.plugins.p4_client.client.ClientHelper; import org.jenkinsci.plugins.p4_client.client.ConnectionHelper; import org.jenkinsci.plugins.p4_client.credentials.P4StandardCredentials; import org.jenkinsci.plugins.p4_client.workspace.ClientTags; import org.jenkinsci.plugins.p4_client.workspace.Workspace; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import com.cloudbees.plugins.credentials.CredentialsProvider; import com.cloudbees.plugins.credentials.domains.DomainRequirement; public class PerforceScm extends SCM { private static Logger logger = Logger .getLogger(PerforceScm.class.getName()); private final String credential; private final Workspace workspace; private final P4Browser browser; public String getCredential() { return credential; } public Workspace getWorkspace() { return workspace; } /** * Create a constructor that takes non-transient fields, and add the * annotation @DataBoundConstructor to it. Using the annotation helps the * Stapler class to find which constructor that should be used when * automatically copying values from a web form to a class. */ @DataBoundConstructor public PerforceScm(String credential, Workspace workspace, P4Browser browser) { this.credential = credential; this.workspace = workspace; this.browser = browser; } public PerforceScm(String credential, Workspace workspace) { this.credential = credential; this.workspace = workspace; this.browser = null; } /** * Calculate the state of the workspace of the given build. The returned * object is then fed into compareRemoteRevisionWith as the baseline * SCMRevisionState to determine if the build is necessary, and is added to * the build as an Action for later retrieval. */ @Override public SCMRevisionState calcRevisionsFromBuild(AbstractBuild<?, ?> build, Launcher launcher, TaskListener listener) throws IOException, InterruptedException { // Method not required by Perforce return null; } /** * This method does the actual polling and returns a PollingResult. The * change attribute of the PollingResult the significance of the changes * detected by this poll. */ @Override protected PollingResult compareRemoteRevisionWith( AbstractProject<?, ?> project, Launcher launcher, FilePath buildWorkspace, TaskListener listener, SCMRevisionState baseline) throws IOException, InterruptedException { try { String client = workspace.getName(); ClientHelper p4 = new ClientHelper(credential, listener, client); if (p4.updateFiles()) { return PollingResult.BUILD_NOW; } p4.disconnect(); } catch (Exception e) { listener.fatalError("Perforce Error: " + e); return null; } return PollingResult.NO_CHANGES; } /** * The checkout method is expected to check out modified files into the * project workspace. In Perforce terms a 'p4 sync' on the project's * workspace. Authorisation */ @Override public boolean checkout(AbstractBuild<?, ?> build, Launcher launcher, FilePath buildWorkspace, BuildListener listener, File changelogFile) throws IOException, InterruptedException { // IMPORTANT: Set workspace format map to build workspace name. String node = build.getBuiltOn().getNodeName(); ClientTags.set("node", (node != "") ? node : "master"); ClientTags.set("project", build.getProject().getName()); String hostname = build.getBuiltOn().toComputer().getHostName(); ClientTags.set("hostname", hostname); ClientTags.set("hash", String.valueOf(node.hashCode())); // IMPORTANT: Set workspace root and hostname workspace.setHostName(null); // TODO get real hostname! workspace.setRootPath(build.getModuleRoot().getRemote()); // Create task Map<String, String> map = build.getBuildVariables(); CheckoutTask task = new CheckoutTask(credential, workspace, listener, map); // Calculate changes in build List<Object> changes = task.getChanges(); // Invoke build boolean success = buildWorkspace.act(task); // Only write changelog if build succeed if (success) { P4ChangeSet.store(changelogFile, changes); } return success; } @Override public P4Browser getBrowser() { return browser; } /** * The checkout method should, besides checking out the modified files, * write a changelog.xml file that contains the changes for a certain build. * The changelog.xml file is specific for each SCM implementation, and the * createChangeLogParser returns a parser that can parse the file and return * a ChangeLogSet. */ @Override public ChangeLogParser createChangeLogParser() { return new P4ChangeParser(); } /** * Called before a workspace is deleted on the given node, to provide SCM an * opportunity to perform clean up. */ @Override public boolean processWorkspaceBeforeDeletion( AbstractProject<?, ?> project, FilePath buildWorkspace, Node node) { try { ConnectionHelper p4 = new ConnectionHelper(credential, null); p4.login(); p4.deleteClient(workspace); p4.disconnect(); } catch (Exception e) { logger.severe("Connection issue!"); e.printStackTrace(); return false; } return true; } /** * Returns the ScmDescriptor<?> for the SCM object. The ScmDescriptor is * used to create new instances of the SCM. */ @Override public SCMDescriptor<PerforceScm> getDescriptor() { return (DescriptorImpl) super.getDescriptor(); } public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); /** * The relationship of Descriptor and SCM (the describable) is akin to class * and object. What this means is that the descriptor is used to create * instances of the describable. Usually the Descriptor is an internal class * in the SCM class named DescriptorImpl. The Descriptor should also contain * the global configuration options as fields, just like the SCM class * contains the configurations options for a job. * * @author pallen * */ @Extension public static class DescriptorImpl extends SCMDescriptor<PerforceScm> { /** * public no-argument constructor */ public DescriptorImpl() { super(PerforceScm.class, P4Browser.class); load(); } @Override public SCM newInstance(StaplerRequest req, JSONObject formData) throws FormException { PerforceScm scm = (PerforceScm) super.newInstance(req, formData); return scm; } /** * Returns the name of the SCM, this is the name that will show up next * to CVS and Subversion when configuring a job. */ @Override public String getDisplayName() { return "Perforce Software"; } /** * The configure method is invoked when the global configuration page is * submitted. In the method the data in the web form should be copied to * the Descriptor's fields. To persist the fields to the global * configuration XML file, the save() method must be called. Data is * defined in the global.jelly page. * */ @Override public boolean configure(StaplerRequest req, JSONObject json) throws FormException { save(); return true; } /** * Credentials list, a Jelly config method for a build job. * * @return A list of Perforce credential items to populate the jelly * Select list. */ public ListBoxModel doFillCredentialItems() { ListBoxModel list = new ListBoxModel(); Class<P4StandardCredentials> type = P4StandardCredentials.class; Jenkins scope = Jenkins.getInstance(); Authentication acl = ACL.SYSTEM; DomainRequirement domain = new DomainRequirement(); List<P4StandardCredentials> credentials; credentials = CredentialsProvider.lookupCredentials(type, scope, acl, domain); if (credentials.isEmpty()) { list.add("Select credential...", null); } for (P4StandardCredentials c : credentials) { StringBuffer sb = new StringBuffer(); sb.append(c.getDescription()); sb.append(" ("); sb.append(c.getUsername()); sb.append(":"); sb.append(c.getP4port()); sb.append(")"); list.add(sb.toString(), c.getId()); } return list; } public FormValidation doCheckCredential(@QueryParameter String value) { if (value == null) { return FormValidation.ok(); } try { ConnectionHelper p4 = new ConnectionHelper(value, null); if (!p4.login()) { return FormValidation .error("Authentication Error: Unable to login."); } if (!p4.checkVersion(20121)) { return FormValidation .error("Server version is too old (min 2012.1)"); } return FormValidation.ok(); } catch (Exception e) { return FormValidation.error(e.getMessage()); } } } /** * This methods determines if the SCM plugin can be used for polling */ @Override public boolean supportsPolling() { return true; } /** * This method should return true if the SCM requires a workspace for * polling. Perforce however can report submitted, pending and shelved * changes without needing a workspace */ @Override public boolean requiresWorkspaceForPolling() { return true; } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#33 | 9672 | Paul Allen | Refactor name from 'p4_client' to 'p4'. | ||
#32 | 9495 | Paul Allen |
Fix Polling issue; using the wrong client. Removed static classes and internal referenced to PerforceScm attributes, to avoid threading issues. |
||
#31 | 9472 | Paul Allen |
Added support to pin build at a label in the populate configuration. - includes help and updates to tests. |
||
#30 | 9430 | Paul Allen |
Polling SCM: skip filter check if no filters - added more logging for Polling errors. |
||
#29 | 9429 | Paul Allen |
Fix SCM Polling bug. Client workspace was not set (unless by an earlier build). - Move listChanges method to ClientHelper. |
||
#28 | 9115 | Paul Allen |
Initial implementation of workspace Cleanup and Sync options. - Includes 3 modes: Automatic Clean/Sync, Force Clean/Sync, Sync Only Automatic Clean/Sync Uses reconcile to clean up workspace and sync changes. Force Clean/Sync Force sync of all files (does not remove files yet...) Sync Only Normal sync with no cleanup TODO: - remove of files in Force Clean/Sync mode - Inline help - Update docs - Add unit/functional tests |
||
#27 | 9091 | Paul Allen |
Added Changelist build filtering for SCM polling: - Configuration uses 'repeatableHeteroProperty' - Filter on Perforce username - Filter on Perforce Depot path (no wildcard support) |
||
#26 | 9069 | Paul Allen |
Adding initial support for tagging Jenkins builds as Perforce Automatic Labels. Only implements TagAction (manual labels); TagNotifier and test cases TODO. |
||
#25 | 9055 | Paul Allen |
Label support. Build at a label using the pram 'label'. This includes adding the label to the ChangeEntry, building the change reports and Browser links to Swarm. (TPI-102) |
||
#24 | 9038 | Paul Allen |
Improved error messages for Form Validate field checks. Uses Perforce Server messages (TPI-80) |
||
#23 | 8969 | Paul Allen |
Adds all contributing change-lists for the build to the change log (using p4 cstat). - Includes exception logging for server connection to the Jenkins console. |
||
#22 | 8940 | Paul Allen |
Major refactor for the ConnectionHelper class to simplify serialisation. Fixed remote Jenkins JNLP slave connection issue. ClientHelper now extends ConnectionHelper and takes on all methods that require a client workspace. P4StandardCredentials is sent to the remote node instead of Credentials ID due to an issue accessing the Credentials store over a remote connection. For simplicity Client ID (workspace name) is serialised instead of the Workspace object. |
||
#21 | 8929 | Paul Allen |
Clean up doCheck Warnings on configuration page (TPI-80). - Added check exceptions to log as warnings. |
||
#20 | 8915 | Paul Allen |
Support for ChangeLog and RepoBrowser. - Added RepoBrowser for Swarm (porting the others should be easy) - ChangeLog XML file now only stores the changelist number all other information is fetched from Perforce |
||
#19 | 8771 | Paul Allen |
Perforce Server 12.1 min check for: Build configuration and password/ticket credentials. Includes: - Added logging for Perforce connections (fine) and set connection pool to 2. - Add 'none' to empty charset list (connection bug?) - Supress P4Java errors for syncing ubinary, xtext, unicode |
||
#18 | 8770 | Paul Allen |
Implemented SCM hook for when Jenkins deletes a workspace. The PerforceScm plugin deletes the workspace, but leaves Jenkins to clean up the local files and directories. (includes dummy 'p4 cstat' code for change reporting) |
||
#17 | 8762 | Paul Allen |
Console Ouptut logging for SCM build steps. - Removed SLF4J and used old style logger (matching Jenkins) - Set Client Host filed to null, Jenkins sometimes gives an IP address. - Test p4java setps in SCM tasks for success(true/false) |
||
#16 | 8744 | Paul Allen |
Support basic SCM Polling. When Jenkins has "Poll SCM" checked the Perforce SCM provider just runs a 'p4 sync -n' on the Workspace. Simple and Fast. |
||
#15 | 8738 | Paul Allen |
Workspace Name Formatter. For Template and Stream workspaces it allows the substitution of the following tags: ${node} The name given to the slave Jenkins node. ${hostname} The hostname for the slave Jenkins node. ${project} The name of the Jenkins build Job. ${hash} Unique hash code of the Jenkins node. |
||
#14 | 8737 | Paul Allen |
Added basic Help for SCM Configuration page. Tidy up descriptions and fix (null:null) in Credential summary. |
||
#13 | 8694 | Paul Allen | Added support for unshelve and revert -w behaviour for builds. | ||
#12 | 8688 | Paul Allen | (Work in progress) Able to call build from review Action and pass URL params. | ||
#11 | 8664 | Paul Allen | Simplified connection to Perforce to get around the SCM initilisation (or lack of). | ||
#10 | 8663 | Paul Allen | Rollout of charset for all Workspace modes. | ||
#9 | 8661 | Paul Allen | Workspace auto fill | ||
#8 | 8641 | Paul Allen | Added workspace helper (setClient) and template/stream types. | ||
#7 | 8640 | Paul Allen |
Added Workspace and Singleton descriptor. Removed old connection code. |
||
#6 | 8639 | Paul Allen |
Added hint URL to Credentials page when no Credentials are defined. Minor refactor and UX changes. |
||
#5 | 8629 | Paul Allen |
Added p4java with connection/authorisation helper classes. Included SSL support and detection of Unicode servers. |
||
#4 | 8613 | Paul Allen | Tidy up unused Credentials data and for debug added getId() to SelectList title. | ||
#3 | 8612 | Paul Allen | Job can now select and save/load Credential choice (some cleanup TODO) | ||
#2 | 8611 | Paul Allen | Basic implementation of Credentials Store | ||
#1 | 8598 | Paul Allen | Experimentation with data binding for Jelly files and ExtensionPoint/Descriptor |