Repository: jenkinsci/tfs-plugin Branch: master Commit: aea4ce0f1fd8 Files: 435 Total size: 19.2 MB Directory structure: gitextract_x4w9l1z2/ ├── .gitignore ├── Jenkinsfile ├── README.md ├── ReleaseNotes.md ├── Releasing.md ├── Testing.md ├── contributing.md ├── pom.xml ├── tfs/ │ ├── checkstyle.xml │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── hudson/ │ │ │ └── plugins/ │ │ │ └── tfs/ │ │ │ ├── ChangeSetReader.java │ │ │ ├── ChangeSetWriter.java │ │ │ ├── CommitParameterAction.java │ │ │ ├── JenkinsEventNotifier.java │ │ │ ├── PullRequestParameterAction.java │ │ │ ├── SafeParametersAction.java │ │ │ ├── TFSLabeler.java │ │ │ ├── TFSRevisionState.java │ │ │ ├── TeamBuildDetailsAction.java │ │ │ ├── TeamBuildEndpoint.java │ │ │ ├── TeamCollectResultsPostBuildAction.java │ │ │ ├── TeamCollectionConfiguration.java │ │ │ ├── TeamCompletedStatusPostBuildAction.java │ │ │ ├── TeamEventsEndpoint.java │ │ │ ├── TeamFoundationServerScm.java │ │ │ ├── TeamGlobalStatusAction.java │ │ │ ├── TeamGlobalStatusPoster.java │ │ │ ├── TeamHookCause.java │ │ │ ├── TeamPRPushTrigger.java │ │ │ ├── TeamPendingStatusBuildStep.java │ │ │ ├── TeamPluginGlobalConfig.java │ │ │ ├── TeamPullRequestMergedDetailsAction.java │ │ │ ├── TeamPushCause.java │ │ │ ├── TeamPushTrigger.java │ │ │ ├── TeamResultsAction.java │ │ │ ├── TeamUpdateWorkItemPostBuildAction.java │ │ │ ├── UnsupportedIntegrationAction.java │ │ │ ├── actions/ │ │ │ │ ├── CheckoutAction.java │ │ │ │ └── RemoveWorkspaceAction.java │ │ │ ├── browsers/ │ │ │ │ ├── TeamFoundationServerRepositoryBrowser.java │ │ │ │ └── TeamSystemWebAccessBrowser.java │ │ │ ├── commands/ │ │ │ │ ├── AbstractCallableCommand.java │ │ │ │ ├── AbstractCommand.java │ │ │ │ ├── Command.java │ │ │ │ ├── DeleteWorkspaceCommand.java │ │ │ │ ├── GetFilesToWorkFolderCommand.java │ │ │ │ ├── GetWorkspaceMappingCommand.java │ │ │ │ ├── LabelCommand.java │ │ │ │ ├── ListWorkspacesCommand.java │ │ │ │ ├── NewWorkspaceCommand.java │ │ │ │ ├── RemoteChangesetVersionCommand.java │ │ │ │ └── ServerConfigurationProvider.java │ │ │ ├── listeners/ │ │ │ │ └── JenkinsRunListener.java │ │ │ ├── model/ │ │ │ │ ├── AbstractCommand.java │ │ │ │ ├── AbstractHookEvent.java │ │ │ │ ├── AliasOnlyUserAccountMapper.java │ │ │ │ ├── AutomaticCredentialsConfigurer.java │ │ │ │ ├── BuildCommand.java │ │ │ │ ├── BuildParameter.java │ │ │ │ ├── BuildWithParametersCommand.java │ │ │ │ ├── ChangeLogSet.java │ │ │ │ ├── ChangeSet.java │ │ │ │ ├── ClonePersistenceStoreProvider.java │ │ │ │ ├── ConnectHookEvent.java │ │ │ │ ├── ConnectionParameters.java │ │ │ │ ├── CredentialsConfigurer.java │ │ │ │ ├── CredentialsConfigurerDescriptor.java │ │ │ │ ├── DomainUserAccountMapper.java │ │ │ │ ├── ExtraSettings.java │ │ │ │ ├── GitCodePushedEventArgs.java │ │ │ │ ├── GitPullRequestEx.java │ │ │ │ ├── GitPullRequestMergedEvent.java │ │ │ │ ├── GitPushEvent.java │ │ │ │ ├── GitStatusContext.java │ │ │ │ ├── GitStatusState.java │ │ │ │ ├── HttpMethod.java │ │ │ │ ├── JobCompletionEventArgs.java │ │ │ │ ├── JsonPatchOperation.java │ │ │ │ ├── LegacyIdentityManagementService.java │ │ │ │ ├── Link.java │ │ │ │ ├── ListOfGitRepositories.java │ │ │ │ ├── ManualCredentialsConfigurer.java │ │ │ │ ├── MockableVersionControlClient.java │ │ │ │ ├── ModernConnectionAdvisor.java │ │ │ │ ├── ModernHTTPClientFactory.java │ │ │ │ ├── NativeLibraryExtractor.java │ │ │ │ ├── NativeLibraryManager.java │ │ │ │ ├── PingCommand.java │ │ │ │ ├── PingHookEvent.java │ │ │ │ ├── Project.java │ │ │ │ ├── ProxyHostEx.java │ │ │ │ ├── PullRequestMergeCommitCreatedEventArgs.java │ │ │ │ ├── Server.java │ │ │ │ ├── TeamBuildPayload.java │ │ │ │ ├── TeamGitStatus.java │ │ │ │ ├── TeamRequestedResult.java │ │ │ │ ├── TeamResultType.java │ │ │ │ ├── TfsUserLookup.java │ │ │ │ ├── UserAccountMapper.java │ │ │ │ ├── UserAccountMapperDescriptor.java │ │ │ │ ├── UserHomePersistenceStore.java │ │ │ │ ├── UserLookup.java │ │ │ │ ├── WebProxySettings.java │ │ │ │ ├── WorkItem.java │ │ │ │ ├── WorkItemTrackingResource.java │ │ │ │ ├── WorkItemTrackingResourceReference.java │ │ │ │ ├── Workspace.java │ │ │ │ ├── WorkspaceConfiguration.java │ │ │ │ ├── WorkspaceMapping.java │ │ │ │ ├── Workspaces.java │ │ │ │ └── servicehooks/ │ │ │ │ ├── Event.java │ │ │ │ ├── EventScope.java │ │ │ │ ├── FormattedEventMessage.java │ │ │ │ └── ResourceContainer.java │ │ │ ├── rm/ │ │ │ │ ├── Artifact.java │ │ │ │ ├── ArtifactVersion.java │ │ │ │ ├── ConnectReleaseWebHookEvent.java │ │ │ │ ├── CreatedBy.java │ │ │ │ ├── Definition.java │ │ │ │ ├── DefinitionReference.java │ │ │ │ ├── InstanceReference.java │ │ │ │ ├── ModifiedBy.java │ │ │ │ ├── Project.java │ │ │ │ ├── ReleaseArtifact.java │ │ │ │ ├── ReleaseArtifactVersionsResponse.java │ │ │ │ ├── ReleaseBody.java │ │ │ │ ├── ReleaseDefinition.java │ │ │ │ ├── ReleaseManagementCI.java │ │ │ │ ├── ReleaseManagementException.java │ │ │ │ ├── ReleaseManagementHttpClient.java │ │ │ │ ├── ReleaseSummaryAction.java │ │ │ │ ├── ReleaseWebHook.java │ │ │ │ ├── ReleaseWebHookAction.java │ │ │ │ ├── ReleaseWebHookHelper.java │ │ │ │ ├── ReleaseWebHookReference.java │ │ │ │ ├── ReleaseWebHookResource.java │ │ │ │ ├── ReleaseWebHookStatus.java │ │ │ │ ├── ReleaseWebHookSummaryAction.java │ │ │ │ ├── RetentionPolicy.java │ │ │ │ └── Version.java │ │ │ ├── telemetry/ │ │ │ │ ├── TelemetryContextInitializer.java │ │ │ │ └── TelemetryHelper.java │ │ │ └── util/ │ │ │ ├── ActionHelper.java │ │ │ ├── BuildVariableResolver.java │ │ │ ├── BuildWorkspaceConfigurationRetriever.java │ │ │ ├── DateUtil.java │ │ │ ├── EndpointHelper.java │ │ │ ├── KeyValueTextReader.java │ │ │ ├── License.txt │ │ │ ├── MaskedArgumentListBuilder.java │ │ │ ├── MediaType.java │ │ │ ├── QueryString.java │ │ │ ├── ResourceHelper.java │ │ │ ├── StringBodyParameter.java │ │ │ ├── StringHelper.java │ │ │ ├── TeamRestClient.java │ │ │ ├── TeamStatus.java │ │ │ ├── TextTableParser.java │ │ │ ├── UriHelper.java │ │ │ └── XmlHelper.java │ │ ├── resources/ │ │ │ └── hudson/ │ │ │ └── plugins/ │ │ │ └── tfs/ │ │ │ ├── TFSLabeler/ │ │ │ │ ├── config.jelly │ │ │ │ └── global.jelly │ │ │ ├── TeamBuildDetailsAction/ │ │ │ │ ├── index.jelly │ │ │ │ └── summary.jelly │ │ │ ├── TeamBuildEndpoint.html │ │ │ ├── TeamCollectResultsPostBuildAction/ │ │ │ │ ├── config.groovy │ │ │ │ └── help.html │ │ │ ├── TeamCollectionConfiguration/ │ │ │ │ ├── config.groovy │ │ │ │ └── help-collectionUrl.html │ │ │ ├── TeamCompletedStatusPostBuildAction/ │ │ │ │ └── help.html │ │ │ ├── TeamEventsEndpoint.html │ │ │ ├── TeamFoundationServerScm/ │ │ │ │ ├── config.jelly │ │ │ │ ├── config_fr.properties │ │ │ │ ├── help-cloakedPaths.html │ │ │ │ ├── help-credentialsConfigurer.html │ │ │ │ ├── help-localPath.html │ │ │ │ ├── help-projectPath.html │ │ │ │ ├── help-serverUrl.html │ │ │ │ └── help-workspaceName.html │ │ │ ├── TeamPRPushTrigger/ │ │ │ │ ├── config.jelly │ │ │ │ └── help.html │ │ │ ├── TeamPendingStatusBuildStep/ │ │ │ │ └── help.html │ │ │ ├── TeamPluginGlobalConfig/ │ │ │ │ ├── config.groovy │ │ │ │ ├── help-configFolderPerNode.html │ │ │ │ ├── help-enableTeamPushTriggerForAllJobs.html │ │ │ │ ├── help-enableTeamStatusForAllJobs.html │ │ │ │ ├── help-releaseWebhookConfigurations.html │ │ │ │ ├── help-userAccountMapper.html │ │ │ │ └── help.jelly │ │ │ ├── TeamPullRequestMergedDetailsAction/ │ │ │ │ ├── index.jelly │ │ │ │ └── summary.jelly │ │ │ ├── TeamPushTrigger/ │ │ │ │ ├── TeamPollingAction/ │ │ │ │ │ └── index.jelly │ │ │ │ ├── config.jelly │ │ │ │ └── help.html │ │ │ ├── TeamUpdateWorkItemPostBuildAction/ │ │ │ │ └── help.html │ │ │ ├── browsers/ │ │ │ │ └── TeamSystemWebAccessBrowser/ │ │ │ │ └── config.jelly │ │ │ ├── model/ │ │ │ │ ├── AliasOnlyUserAccountMapper/ │ │ │ │ │ └── config.groovy │ │ │ │ ├── BuildCommand.json │ │ │ │ ├── BuildWithParametersCommand.json │ │ │ │ ├── ChangeLogSet/ │ │ │ │ │ ├── digest.jelly │ │ │ │ │ ├── digest_fr.properties │ │ │ │ │ ├── index.jelly │ │ │ │ │ └── index_fr.properties │ │ │ │ ├── DomainUserAccountMapper/ │ │ │ │ │ └── config.groovy │ │ │ │ ├── GitPullRequestMergedEvent.json │ │ │ │ ├── GitPushEvent.json │ │ │ │ ├── ManualCredentialsConfigurer/ │ │ │ │ │ ├── config.groovy │ │ │ │ │ └── help-userName.html │ │ │ │ ├── TeamRequestedResult/ │ │ │ │ │ ├── config.groovy │ │ │ │ │ └── help-includes.html │ │ │ │ └── native/ │ │ │ │ ├── aix/ │ │ │ │ │ └── ppc/ │ │ │ │ │ ├── libnative_auth.a │ │ │ │ │ ├── libnative_console.a │ │ │ │ │ ├── libnative_filesystem.a │ │ │ │ │ ├── libnative_misc.a │ │ │ │ │ └── libnative_synchronization.a │ │ │ │ ├── hpux/ │ │ │ │ │ └── PA_RISC/ │ │ │ │ │ ├── libnative_auth.sl │ │ │ │ │ ├── libnative_console.sl │ │ │ │ │ ├── libnative_filesystem.sl │ │ │ │ │ ├── libnative_misc.sl │ │ │ │ │ └── libnative_synchronization.sl │ │ │ │ └── macosx/ │ │ │ │ ├── libnative_auth.jnilib │ │ │ │ ├── libnative_console.jnilib │ │ │ │ ├── libnative_filesystem.jnilib │ │ │ │ ├── libnative_keychain.jnilib │ │ │ │ ├── libnative_misc.jnilib │ │ │ │ └── libnative_synchronization.jnilib │ │ │ └── rm/ │ │ │ ├── ReleaseManagementCI/ │ │ │ │ ├── config.jelly │ │ │ │ ├── help-collectionUrl.html │ │ │ │ ├── help-credentialsId.html │ │ │ │ ├── help-destinationPath.html │ │ │ │ ├── help-isArchiveLog.html │ │ │ │ ├── help-projectName.html │ │ │ │ ├── help-releaseDefinitionName.html │ │ │ │ └── help.html │ │ │ ├── ReleaseSummaryAction/ │ │ │ │ └── summary.jelly │ │ │ ├── ReleaseWebHook/ │ │ │ │ ├── config.groovy │ │ │ │ ├── help-payloadUrl.html │ │ │ │ ├── help-secret.html │ │ │ │ └── help-webhookName.html │ │ │ ├── ReleaseWebHookAction/ │ │ │ │ └── config.groovy │ │ │ ├── ReleaseWebHookReference/ │ │ │ │ ├── config.groovy │ │ │ │ └── help-webHookName.html │ │ │ └── ReleaseWebHookSummaryAction/ │ │ │ └── summary.jelly │ │ └── webapp/ │ │ ├── WEB-INF/ │ │ │ └── lib/ │ │ │ ├── ThirdPartyNotices.html │ │ │ ├── license.html │ │ │ └── redist.txt │ │ ├── browsers/ │ │ │ └── tswa.html │ │ └── labelname.html │ └── test/ │ ├── java/ │ │ ├── hudson/ │ │ │ ├── plugins/ │ │ │ │ └── tfs/ │ │ │ │ ├── ChangeSetReaderTest.java │ │ │ │ ├── ChangeSetWriterTest.java │ │ │ │ ├── CommitParameterActionTest.java │ │ │ │ ├── EndToEndTfs.java │ │ │ │ ├── FunctionalTest.java │ │ │ │ ├── IntegrationTestHelper.java │ │ │ │ ├── IntegrationTestHelperTest.java │ │ │ │ ├── IntegrationTests.java │ │ │ │ ├── InterceptingTaskListener.java │ │ │ │ ├── LoggingFiltersSourceAdapter.java │ │ │ │ ├── SwedishLocaleTestCase.java │ │ │ │ ├── TFSRevisionStateTest.java │ │ │ │ ├── TeamBuildEndpointTest.java │ │ │ │ ├── TeamCollectionConfigurationTest.java │ │ │ │ ├── TeamEventsEndpointTest.java │ │ │ │ ├── TeamFoundationServerScmIntegrationTest.java │ │ │ │ ├── TeamFoundationServerScmTest.java │ │ │ │ ├── Util.java │ │ │ │ ├── actions/ │ │ │ │ │ ├── CheckoutActionTest.java │ │ │ │ │ └── RemoveWorkspaceActionTest.java │ │ │ │ ├── browsers/ │ │ │ │ │ └── TeamSystemWebAccessBrowserTest.java │ │ │ │ ├── commands/ │ │ │ │ │ ├── AbstractCallableCommandTest.java │ │ │ │ │ ├── AbstractCommandTest.java │ │ │ │ │ ├── DeleteWorkspaceCommandTest.java │ │ │ │ │ ├── GetFilesToWorkFolderCommandTest.java │ │ │ │ │ ├── LabelCommandTest.java │ │ │ │ │ ├── ListWorkspacesCommandTest.java │ │ │ │ │ ├── NewWorkspaceCommandTest.java │ │ │ │ │ └── RemoteChangesetVersionCommandTest.java │ │ │ │ ├── model/ │ │ │ │ │ ├── AbstractHookEventTest.java │ │ │ │ │ ├── ChangeLogSetIntegrationTest.java │ │ │ │ │ ├── ChangeLogSetTest.java │ │ │ │ │ ├── ChangeSetTest.java │ │ │ │ │ ├── GitCodePushedEventArgsTest.java │ │ │ │ │ ├── GitPullRequestMergedEventTest.java │ │ │ │ │ ├── GitPushEventTest.java │ │ │ │ │ ├── GitStatusContextTest.java │ │ │ │ │ ├── NativeLibraryManagerTest.java │ │ │ │ │ ├── ProjectTest.java │ │ │ │ │ ├── PullRequestMergeCommitCreatedEventArgsTest.java │ │ │ │ │ ├── ServerIntegrationTest.java │ │ │ │ │ ├── ServerTest.java │ │ │ │ │ ├── TeamGitStatusTest.java │ │ │ │ │ ├── WorkspaceConfigurationTest.java │ │ │ │ │ ├── WorkspacesTest.java │ │ │ │ │ └── servicehooks/ │ │ │ │ │ └── EventScopeTest.java │ │ │ │ └── util/ │ │ │ │ ├── BuildVariableResolverTest.java │ │ │ │ ├── BuildWorkspaceConfigurationRetrieverTest.java │ │ │ │ ├── DateUtilTest.java │ │ │ │ ├── KeyValueTextReaderTest.java │ │ │ │ ├── MaskedArgumentListBuilderTest.java │ │ │ │ ├── QueryStringTest.java │ │ │ │ ├── StringHelperTest.java │ │ │ │ ├── TeamRestClientTest.java │ │ │ │ ├── TextTableParserTest.java │ │ │ │ ├── UriHelperTest.java │ │ │ │ └── XmlHelperTest.java │ │ │ └── util/ │ │ │ └── SecretOverride.java │ │ └── jenkins/ │ │ └── security/ │ │ └── ConfidentialStoreOverride.java │ └── resources/ │ ├── hudson/ │ │ └── plugins/ │ │ └── tfs/ │ │ ├── FunctionalTest/ │ │ │ ├── agent/ │ │ │ │ ├── config.xml │ │ │ │ ├── hudson.plugins.tfs.TeamFoundationServerScm.xml │ │ │ │ ├── identity.key.enc │ │ │ │ ├── jobs/ │ │ │ │ │ └── agent/ │ │ │ │ │ ├── builds/ │ │ │ │ │ │ ├── 2015-07-15_20-37-42/ │ │ │ │ │ │ │ ├── build.xml │ │ │ │ │ │ │ ├── changelog.xml │ │ │ │ │ │ │ └── log │ │ │ │ │ │ ├── lastFailedBuild │ │ │ │ │ │ ├── lastStableBuild │ │ │ │ │ │ ├── lastSuccessfulBuild │ │ │ │ │ │ ├── lastUnstableBuild │ │ │ │ │ │ ├── lastUnsuccessfulBuild │ │ │ │ │ │ └── legacyIds │ │ │ │ │ ├── config.xml │ │ │ │ │ └── nextBuildNumber │ │ │ │ └── secret.key │ │ │ ├── cloakedPaths/ │ │ │ │ ├── config.xml │ │ │ │ ├── hudson.plugins.tfs.TeamFoundationServerScm.xml │ │ │ │ ├── identity.key.enc │ │ │ │ ├── jobs/ │ │ │ │ │ └── cloakedPaths/ │ │ │ │ │ ├── builds/ │ │ │ │ │ │ ├── 2015-07-15_20-37-42/ │ │ │ │ │ │ │ ├── build.xml │ │ │ │ │ │ │ ├── changelog.xml │ │ │ │ │ │ │ └── log │ │ │ │ │ │ ├── lastFailedBuild │ │ │ │ │ │ ├── lastStableBuild │ │ │ │ │ │ ├── lastSuccessfulBuild │ │ │ │ │ │ ├── lastUnstableBuild │ │ │ │ │ │ ├── lastUnsuccessfulBuild │ │ │ │ │ │ └── legacyIds │ │ │ │ │ ├── config.xml │ │ │ │ │ └── nextBuildNumber │ │ │ │ └── secret.key │ │ │ ├── createLabel/ │ │ │ │ ├── config.xml │ │ │ │ ├── hudson.plugins.tfs.TeamFoundationServerScm.xml │ │ │ │ ├── identity.key.enc │ │ │ │ ├── jobs/ │ │ │ │ │ └── createLabel/ │ │ │ │ │ ├── builds/ │ │ │ │ │ │ ├── 2015-07-15_20-37-42/ │ │ │ │ │ │ │ ├── build.xml │ │ │ │ │ │ │ ├── changelog.xml │ │ │ │ │ │ │ └── log │ │ │ │ │ │ ├── lastFailedBuild │ │ │ │ │ │ ├── lastStableBuild │ │ │ │ │ │ ├── lastSuccessfulBuild │ │ │ │ │ │ ├── lastUnstableBuild │ │ │ │ │ │ ├── lastUnsuccessfulBuild │ │ │ │ │ │ └── legacyIds │ │ │ │ │ ├── config.xml │ │ │ │ │ └── nextBuildNumber │ │ │ │ └── secret.key │ │ │ ├── newJob/ │ │ │ │ ├── config.xml │ │ │ │ ├── hudson.plugins.tfs.TeamFoundationServerScm.xml │ │ │ │ ├── identity.key.enc │ │ │ │ ├── jobs/ │ │ │ │ │ └── newJob/ │ │ │ │ │ ├── builds/ │ │ │ │ │ │ ├── lastFailedBuild │ │ │ │ │ │ ├── lastStableBuild │ │ │ │ │ │ ├── lastSuccessfulBuild │ │ │ │ │ │ ├── lastUnstableBuild │ │ │ │ │ │ ├── lastUnsuccessfulBuild │ │ │ │ │ │ └── legacyIds │ │ │ │ │ ├── config.xml │ │ │ │ │ └── nextBuildNumber │ │ │ │ └── secret.key │ │ │ ├── oldPollingFallback/ │ │ │ │ ├── config.xml │ │ │ │ ├── identity.key.enc │ │ │ │ ├── jobs/ │ │ │ │ │ └── oldPollingFallback/ │ │ │ │ │ ├── builds/ │ │ │ │ │ │ ├── 2015-07-10_12-11-34/ │ │ │ │ │ │ │ ├── build.xml │ │ │ │ │ │ │ ├── log │ │ │ │ │ │ │ └── polling.log │ │ │ │ │ │ ├── lastFailedBuild │ │ │ │ │ │ ├── lastStableBuild │ │ │ │ │ │ ├── lastSuccessfulBuild │ │ │ │ │ │ ├── lastUnstableBuild │ │ │ │ │ │ ├── lastUnsuccessfulBuild │ │ │ │ │ │ └── legacyIds │ │ │ │ │ ├── config.xml │ │ │ │ │ └── nextBuildNumber │ │ │ │ └── secret.key │ │ │ ├── upgradeEncodedPassword/ │ │ │ │ ├── config.xml │ │ │ │ ├── identity.key.enc │ │ │ │ ├── jobs/ │ │ │ │ │ └── upgradeEncodedPassword/ │ │ │ │ │ ├── builds/ │ │ │ │ │ │ ├── 2015-07-15_20-37-42/ │ │ │ │ │ │ │ ├── build.xml │ │ │ │ │ │ │ ├── changelog.xml │ │ │ │ │ │ │ └── log │ │ │ │ │ │ ├── lastFailedBuild │ │ │ │ │ │ ├── lastStableBuild │ │ │ │ │ │ ├── lastSuccessfulBuild │ │ │ │ │ │ ├── lastUnstableBuild │ │ │ │ │ │ ├── lastUnsuccessfulBuild │ │ │ │ │ │ └── legacyIds │ │ │ │ │ ├── config.xml │ │ │ │ │ └── nextBuildNumber │ │ │ │ └── secret.key │ │ │ └── useWebProxyServer/ │ │ │ ├── config.xml │ │ │ ├── hudson.plugins.tfs.TeamFoundationServerScm.xml │ │ │ ├── identity.key.enc │ │ │ ├── jobs/ │ │ │ │ └── useWebProxyServer/ │ │ │ │ ├── builds/ │ │ │ │ │ ├── 2015-07-15_20-37-42/ │ │ │ │ │ │ ├── build.xml │ │ │ │ │ │ ├── changelog.xml │ │ │ │ │ │ └── log │ │ │ │ │ ├── lastFailedBuild │ │ │ │ │ ├── lastStableBuild │ │ │ │ │ ├── lastSuccessfulBuild │ │ │ │ │ ├── lastUnstableBuild │ │ │ │ │ ├── lastUnsuccessfulBuild │ │ │ │ │ └── legacyIds │ │ │ │ ├── config.xml │ │ │ │ └── nextBuildNumber │ │ │ ├── proxy.xml │ │ │ └── secret.key │ │ ├── action/ │ │ │ ├── tf-changeset-1.log │ │ │ ├── tf-changeset-2.log │ │ │ └── tf-changeset-3.log │ │ ├── commands/ │ │ │ ├── issue-3683.txt │ │ │ ├── issue-4184.txt │ │ │ ├── issue-4943.txt │ │ │ ├── issue-6454.txt │ │ │ ├── issue-6870-2.txt │ │ │ ├── issue-6870.txt │ │ │ ├── tf-changeset-1.txt │ │ │ ├── tf-changeset-2.txt │ │ │ ├── tf-changeset-3.txt │ │ │ ├── tf-changeset-german-1.txt │ │ │ └── tf-workfold-list.txt │ │ ├── git.push-sample.json │ │ └── util/ │ │ └── XmlHelperTest/ │ │ ├── peekValue_File/ │ │ │ └── input.xml │ │ └── pokeValue_File/ │ │ ├── expected.xml │ │ └── input.xml │ ├── tf-get.log │ ├── tf-history.log │ ├── tf-workfold.log │ ├── tf-workspace.log │ ├── tf-workspaces.log │ ├── tf.bat │ └── tf.sh └── tfs-sdk/ ├── pom.xml └── src/ └── com.microsoft.tfs.sdk-14.0.3.jar ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .settings **/target **/workspaces **/work .project .classpath /.idea **/*.iml ================================================ FILE: Jenkinsfile ================================================ pipeline { agent any stages { stage('Build') { steps { buildPlugin() } } } } ================================================ FILE: README.md ================================================ Azure DevOps and Team Foundation Server plugin for Jenkins ============================ Copyright © Erik Ramfelt, Olivier Dagenais, CloudBees, Inc. and others. Licensed under [MIT Licence]. ## Summary This plugin triggers a release in Azure DevOps, through a post-build step in Jenkins. It also integrates [Team Foundation Version Control] (also known as TFVC) and Git to Jenkins by connecting to Team Foundation Server (TFS). ## Quick links * The legacy [wiki] page on the Jenkins Confluence instance * CI build status - [![Build Status](https://ci.jenkins.io/buildStatus/icon?job=Plugins/tfs-plugin/master)](https://ci.jenkins.io/job/Plugins/job/tfs-plugin/job/master/) * Issues are tracked by the [Jenkins JIRA] * Download the latest release [from the Jenkins CDN](http://updates.jenkins-ci.org/latest/tfs.hpi) or [from the GitHub Releases page](https://github.com/jenkinsci/tfs-plugin/releases) ## What can you do with it? That depends on which version control system you use in Azure DevOps/TFS: ### Team Foundation Version Control Allows you to use a TFVC repository as an SCM in a Jenkins jobs. At the moment, this plugin supports: * Retrieving read-only copies of files and folders from a TFVC repository. * Polling a TFVC repository to automatically start builds when there are changes. * Links from the Jenkins change sets to the TFVC repository web interface. _(Also known as a repository browser)_ * Creating a label in the TFVC repository The plugin will automatically create a workspace in TFS/Azure DevOps and map a work folder (in the Jenkins workspace) to it. ### Git The TFS plug-in for Jenkins adds some features to better integrate with Git repositories hosted in TFS/Azure DevOps: * A push trigger, to request builds of specific commits in Git repositories without needing to schedule SCM polling * Instead of adding the **Build when a change is pushed to TFS/Azure DevOps** trigger to every job, you can also check the checkbox next to **Enable Push Trigger for all jobs** in the Jenkins global configuration. Individual jobs can still opt-out of this global opt-in by enabling the **Poll SCM** trigger and checking its **Ignore post-commit hooks** checkbox. * A build step that adds a "build pending" status to the associated pull request and/or commit in TFS/Azure DevOps * Instead of adding the **Set build pending status in TFS/Azure DevOps** build step to every job, you can also check the checkbox next to **Enable Team Status for all jobs** in the Jenkins global configuration. * A post-build action that add a "build completed" status to the associated pull request and/or commit in TFS/Azure DevOps * Instead of adding the **Set build completion status in TFS/Azure DevOps** post-build action to every job, you can also check the checkboxbox next to **Enable Team Status for all jobs** in the Jenkins global configuration. * A link to (and summary information about) the associated TFS/Azure DevOps build that triggered the Jenkins build. * A link to (and summary information about) the associated TFS/Azure DevOps pull request that triggered the Jenkins build. * Links to associated TFS/Azure DevOps work items. * Associated TFS/Azure DevOps work items link back to the Jenkins build. There are two possibilities to trigger a build with SCM Changeset: * TFS plugin Please refer to the [Webhooks with Azure DevOps](https://www.visualstudio.com/en-us/docs/service-hooks/services/webhooks) page for instructions on configuring the integration in TFS. Available endpoints can be found under [http://yourJenkins/team-events](http://yourJenkins/team-events) * Git plugin You have to enable Git SCM polling to receive commit notifications Please refer to the [Jenkins with Azure DevOps](https://www.visualstudio.com/en-us/docs/service-hooks/services/jenkins) page for instructions on configuring the integration in TFS. # Supported versions The following sub-sections list the various versions of software that were tested and are thus supported. The plugin might work with other versions, they just haven't been tested. ## Team Foundation Server (TFS) / Azure DevOps The following table indicates compatibility and support for versions of TFS and Azure DevOps. > Version | Supported by the TFS plugin? | Mainstream Support End Date > ------- | ------ | --------------------------- > [Azure DevOps] | :white_check_mark: | n/a > Visual Studio Team Foundation Server 2017 | :white_check_mark: | [2022/01/11](https://support.microsoft.com/en-us/lifecycle/search?alpha=Visual%20Studio%20Team%20Foundation%20Server%202017) > Visual Studio Team Foundation Server 2015 | :white_check_mark: | [2020/10/13](https://support.microsoft.com/en-us/lifecycle?p1=18576) > Microsoft Visual Studio Team Foundation Server 2013 | :white_check_mark: | [2019/04/09](https://support.microsoft.com/en-us/lifecycle?p1=17358) > Microsoft Visual Studio Team Foundation Server 2012 | :white_check_mark: | [2018/01/09](https://support.microsoft.com/en-us/lifecycle?p1=16804) > Microsoft Visual Studio Team Foundation Server 2010 | :x: | :warning: [2015/07/14](https://support.microsoft.com/en-us/lifecycle?p1=15011) > Microsoft Visual Studio Team System 2008 Team Foundation Server | :x: | :warning: [2013/04/09](https://support.microsoft.com/en-us/lifecycle?p1=13083) > Microsoft Visual Studio 2005 Team Foundation Server | :x: | :warning: [2011/07/12](https://support.microsoft.com/en-us/lifecycle?p1=10449) Whereas for **Trigger release in TFS/Azure DevOps** post build action, only following table is supported: > Version | Supported by the TFS plugin? | Mainstream Support End Date > ------- | ------ | --------------------------- > [Azure DevOps] | :white_check_mark: | n/a > [Team Foundation Server "15" RC1] | :white_check_mark: | n/a ## Operating Systems The plugin has been tested against the following operating systems and versions, with the latest updates as of 2015/08/27. Name | Version ---- | ------- Windows Server | 2012 R2 Mac OS X | Yosemite 10.10.5 Ubuntu Linux | Server 14.04 LTS ## Jenkins The plugin is built against Jenkins version **1.580** and that's the version integration tests are run against. # Configuration ## Requirements ### 4.0.0 and later (New!) Ever since release 4.0.0, a command-line client or tool is no longer necessary as all the interaction with TFS or Azure DevOps is done using the [TFS SDK for Java]. The native libraries needed by the SDK are automatically copied to a sub-directory under the agent user's home folder. ### 3.2.0 and earlier Versions 3.2.0 and earlier of the plugin required a command line tool to be installed on the build agents to retrieve source code from the TFVC repository. 1. Install either Microsoft Visual Studio or [Microsoft Team Explorer Everywhere] Command-Line Client (CLC) on the build agents 2. Add `tf.exe` (Visual Studio) OR one of `tf.cmd` or `tf` (TEE CLC) to the `PATH` of the build agents' user(s). ## Global configuration To make use of the Git integration with TFS/Azure DevOps and/or to use automatic credentials configuration with the TFVC SCM, it is necessary to first configure your team project collection(s). Follow these instructions for each team project collection (most organizations will only have one). 1. Add credentials: 1. Select **Jenkins** > **Credentials** 2. Select **Add domain** 1. In the _Domain Name_ field, enter the host's friendly name, such as `fabrikam-fiber-inc` 2. In the _Description_ field, you can enter some notes, such as who maintains the server, etc. 3. Next to _Specification_, select **Add** > **Hostname** 1. In the _Include_ field, enter the Fully-Qualified Domain Name (FQDN), such as `fabrikam-fiber-inc.visualstudio.com` 4. Click **OK** 3. Select **Add Credentials** 1. For the _Kind_ field, select **Username with password** 2. For the _Scope_ field, select **Global (Jenkins, nodes, items, all child items, etc)** 3. See the _User name and password_ section below for the values of the _Username_ and _Password_; a Personal Access Token (PAT) is strongly recommended. If the credentials will be used for TFVC, select **All scopes**, otherwise select the following _Authorized Scopes_: 1. `Code (read)` 2. `Code (status)` 3. `Work items (read and write)` 4. You can use the _Description_ field to record details about the PAT, such as its intended collection, the selected authorization scopes and expiration date. For example: `fabrikam-fiber-inc, code read+status, wit read+write, expires 2017-08-05` 5. Click **OK** 2. Add the collection URL and associate it with the right credentials: 1. Select **Jenkins** > **Manage Jenkins** > **Configure System** 2. Scroll to **TFS/Azure DevOps** and click **Add** 1. If using Azure DevOps, the value of the _Collection URL_ field should omit `/DefaultCollection`. 2. Select the associated `Credentials` value created earlier. 3. Click **Test Connection**. 3. Click **Save** ### Automatic integration To avoid having to configure every job to enable integration features, you can check the checkbox next to either or both of: * Enable Push Trigger for all jobs * Enable Team Status for all jobs ![Automatic Integration](images/AutomaticIntegration.png) ### Advanced In some environments, the "home" directory is mounted over a network and shared between many computers, including Jenkins servers and their associated build nodes, which eventually leads to corruption of the configuration directory used for TFVC workspaces. If you have such an environment, check the box next to **Store TFVC configuration in computer-specific folders** to use a sub-directory for each computer. :warning: WARNING :warning: Turning this on is equivalent to setting the `TEE_PROFILE_DIRECTORY` environment variable and thus any manual operations performed using the Command-Line Client (CLC) will need to be performed with the `TEE_PROFILE_DIRECTORY` environment variable set accordingly. ## Job configuration ### Team Foundation Version Control If your source code is in a TFVC repository, this section is for you. ![SCM configuration](images/tfs-job-config4.png) Field | Description ----- | ----------- `Collection URL` | The URL to the [Team Project Collection](https://msdn.microsoft.com/en-us/library/dd236915(v=vs.120).aspx). If you added your team project collection(s) in the global configuration, the field will show you a list to pick from. Examples: `https://tfs02.codeplex.com`, `https://fabrikam-fiber-inc.visualstudio.com`, `http://tfs:8080/tfs/DefaultCollection` `Project path` | The Team Project and path to retrieve from the server. The project path must start with `$/`, and contain any sub path that exists in the project repository. Example: `$/Fabrikam-Fiber-TFVC/AuthSample-dev` `Credentials` | If you added your team project collection(s) in the global configuration, select **Automatic** and the credentials will be looked up automatically, otherwise you can select **Manual** and configure the `User name` and `User password` fields. `Manual` > `User name` | The name of the user that will be connecting to TFS/Azure DevOps to query history, checkout files, etc. See _User name and password_ below for a full description. `Manual` > `User password` | The password, alternate password or personal access token associated with the user. See _User name and password_ below for more details. `Use update` | If this option is checked, then the workspace will not be deleted and re-created at the start of each build, making the build faster, but this causes the artifacts from the previous build to remain when a new build starts. `Local workfolder` | The name of the local work folder. The specified folder will contain the files retrieved from the repository. Default is `.`, ie the files will be downloaded into the Hudson workspace folder. `Workspace name` | The name of the workspace that Jenkins should use when creating and deleting workspaces on the server. The workspace name supports three macros; `${JOB_NAME}` is replaced by the job name, `${USER_NAME}` is replaced by the user name Jenkins is running as and `${NODE_NAME}` is replaced by the name of the node. Default workspace name is `Hudson-${JOB_NAME}-${NODE_NAME}`. `Cloaked paths` | A collection of server paths to cloak to exclude from the workspace and from the build trigger. Multiple entries must be placed onto separate lines. `Repository browser` | Select `Microsoft Team Foundation Server/Azure DevOps` to turn on links inside Jenkins jobs (in the **Changes** page) back to TFS/Azure DevOps, for easier traceability. If the TFS server is reached by users through a different URL than that provided in `Collection URL`, such as the Fully-Qualified Domain Name (FQDN), provide a value for the `URL` sub-field. ### Git If your source code is in a Git repository located on a TFS/Azure DevOps server, this section is for you. > :warning: Make sure you first followed the instructions in the **Global configuration** section and added your team project collection(s), associated with credentials. :warning: ![Git configuration](images/git-job-config.png) If you didn't have the Git plug-in for Jenkins already, installing the TFS plug-in for Jenkins should have brought it on as a dependency. 1. Use the **Git** _Source Code Management_ and add the URL to your Git repository in TFS/Azure DevOps, omitting the `/DefaultCollection` if you are using Azure DevOps. 2. If you haven't done so already, follow the instructions in the "User name and password" section to generate a Personal Access Token, and then add a "Credentials" entry as specified in the "Global configuration" section. You should then be able to select it in the _Credentials_ field. 3. To be able to build the merge commits created for pull requests in TFS/Azure DevOps, click the **Advanced...** button 1. In the _Name_ field, enter **origin** (or some unique name if you already have other repositories) 2. In the _Refspec_ field, enter `+refs/heads/*:refs/remotes/origin/* +refs/pull/*:refs/remotes/origin-pull/*` (replacing "origin" as necessary) 4. If you haven't already enabled the Push Trigger for all jobs, scroll down to _Build Triggers_ and you can check the **Build when a change is pushed to TFS/Azure DevOps** checkbox. 5. If you haven't already enabled Team Status for all jobs, scroll down to _Build_, select **Add build step** > **Set build pending status in TFS/Azure DevOps**, moving it _first_ in the list of steps, to notify TFS/Azure DevOps as early as possible that a Jenkins build has been started. 6. Add other build steps, as necessary. 7. If you haven't already enabled Team Status for all jobs, scroll down to _Post-build Actions_, select **Add post-build action** > **Set build completion status in TFS/Azure DevOps**. 8. If you would like to collect results for publication in TFS/Azure DevOps, scroll down to _Post-build Actions_, select **Add post-build action** > **Collect results for TFS/Azure DevOps** and then add one or more results to collect. 9. If the Jenkins job will be used to validate pull requests in TFS/Azure DevOps and you would like to add links from the associated work items back to the Jenkins build, select **Add post-build action** > **Add link to associated work items in TFS/Azure DevOps**. ### User name and password #### Team Foundation Server (on-premises) For \[on-premises\] Team Foundation Server, the _User name_ can be specified in two ways: 1. `EXAMPLE-DOMAIN\user` 2. `user@domain.example.com` #### Azure DevOps (previously known as Visual Studio Team Services or VSTS) For Azure DevOps, there are also two options: 1. Personal access tokens (recommended) 1. In Azure DevOps, click your name in the top right corner and select **Security**. 2. In the _Personal access tokens_ area, select **Add**. 3. Describe the token (use something like "Jenkins server at jenkins.example.com"), select an expiry timeframe, double-check the Azure DevOps account the token will be valid for and, if the user account will be used for TFVC, select **All scopes** otherwise you can select smaller scopes based on what features you will need. 4. Click **\[Create Token\]** and copy the generated personal access token to the clipboard. 5. Back to Jenkins, enter the e-mail address associated with your Azure DevOps account as the _User name_ and the generated personal access token as the _User password_. 2. Alternate credentials 1. In Azure DevOps, click your name in the top right corner and select **Security**. 2. In the _Alternate credentials_ area, select **Enable alternate authentication credentials**. 3. Enter a secondary user name and password, then click **\[Save\]**. 4. Back to Jenkins, re-enter those credentials in the _User name_ and _User password_ fields. ## Checkout by label (New since version 3.2.0) The plugin now supports checking out from a specific label or any valid [versionspec](https://www.visualstudio.com/docs/tfvc/use-team-foundation-version-control-commands#use-a-versionspec-argument-to-specify-affected-versions-of-items). Here's how to configure a job to do that: > :information_source: Polling the server doesn't make sense when you want to build for a specific label because polling is not \[currently\] label-aware and could queue a build **every polling interval**. :information_source: 1. Turn **off** SCM polling by making sure the **Poll SCM** checkbox is _cleared_ (unchecked). 2. Tick the **This build is parameterised** checkbox 1. Add a **String Parameter** 2. Set its _Name_ to **VERSION_SPEC** 3. Set its _Description_ to the following: ``` Enter a valid version spec to use when checking out. Labels are prefixed with "L" and changesets are prefixed with "C". See the following for a versionspec reference: https://www.visualstudio.com/docs/tfvc/use-team-foundation-version-control-commands#use-a-versionspec-argument-to-specify-affected-versions-of-items Examples: "LFoo", "C42" ``` 3. **Save** the job. Now, the next time you want to queue a build, you will need to provide a value for the **VERSION_SPEC** parameter. The build will then perform a checkout of the source as of the specified **VERSION_SPEC**. ## Proxy server support (New since version 4.1.0) In the event Jenkins is deployed on a network with no direct access to other networks (such as the internet), the TFS plugin now supports connecting through proxy servers. > :information_source: Support for proxy servers requiring authentication was added in version 5.1.0. :information_source: Follow the instructions at [JenkinsBehindProxy](https://wiki.jenkins-ci.org/display/JENKINS/JenkinsBehindProxy) to configure Jenkins' use of a proxy server, which the TFS plugin also uses. ## Integration with TFS/Azure DevOps (New since version 5.0.0) There are some steps to perform in both Jenkins and in TFS/Azure DevOps to activate the integration. This section assumes you have already configured one or more team project collections, as described in the **Global configuration** section above and then configured the Jenkins jobs as described in the **Git** section above. ### Trigger builds when code is pushed to a Git repository in TFS/Azure DevOps 1. Go to the team project's _Administration_ and then select **Service Hooks** 2. Select the `+` button 3. Select **Jenkins** and click **Next** 4. Select **Code pushed**, configure the **Filters** and click **Next** 5. You can now configure which action will be performed. The choices are **Trigger generic build** and **Trigger Git build**. Once you select the action, its _Settings_ must be configured 1. Enter the URL to your Jenkins server. (hint: it's the destination when you click the Jenkins logo in the top left) 2. Enter your _User name_ 3. For the _User API token_, click on your user name in Jenkins (in the top-right corner), then **Configure** and finally click the **Show API Token...*** button. Copy & paste the token back in TFS/Azure DevOps 4. Once valid credentials have been provided, more fields will become available. If you selected **Trigger generic build**, keep going with these fields 1. The _Build_ field should now be configurable as a drop-down list 2. Add any additional parameters, if any 5. Click **Test**, inspect the results of the test and click **Close** 6. If the test was successful, click **Finish** ### Trigger builds when a pull request is created or updated in TFS/Azure DevOps 1. Go to the team project's _Administration_ and then select **Service Hooks** 2. Select the `+` button 3. Select **Jenkins** and click **Next** 4. Select one of **Code pushed** or **Pull request merge commit created**, configure the **Filters** and click **Next** 5. The only action available is **Trigger generic build**, its _Settings_ must be configured 1. Enter the URL to your Jenkins server. (hint: it's the destination when you click the Jenkins logo in the top left) 2. Enter your _User name_ 3. For the _User API token_, click on your user name in Jenkins (in the top-right corner), then **Configure** and finally click the **Show API Token...*** button. Copy & paste the token back in TFS/Azure DevOps 4. Once valid credentials have been provided, more fields will become available 5. The _Build_ field should now be configurable as a drop-down list 6. Add any additional parameters, if any 7. Click **Test**, inspect the results of the test and click **Close** 8. If the test was successful, click **Finish** ## Build environment variables The plugin will set the following environment variables for the build, after a checkout: * **TFS_WORKSPACE** \- The name of the workspace. * **TFS_WORKFOLDER** \- The full path to the working folder. * **TFS_PROJECTPATH** \- The TFVC project path that is mapped to the workspace. * **TFS_SERVERURL** \- The URL to the Team Project Collection. * **TFS_USERNAME** \- The user name that is used to connect to TFS/Azure DevOps. * **TFS_CHANGESET** \- The change set number that is checked out in the workspace # Trigger release in TFS/Azure DevOps [MSDN documentation](https://blogs.msdn.microsoft.com/visualstudioalm/2016/05/27/continuous-deploymentdelivery-with-jenkins-and-vs-team-services/) ### Overview Once you have configured Continuous Integration (CI) with Jenkins to be able to build with every code checkin/commit, the next step toward automating your DevOps pipeline is to be able to deploy automatically by setting up the Continuous Deployment (CD) pipeline. [VS Team Service Release Management](https://www.visualstudio.com/features/release-management-vs) service lets you automate your deployments so that you could deliver your apps/services easily and deliver them often. You can setup the CI and CD process all on VS Azure DevOps. However, if you have the CI pipleine already set with Jenkins, VS Team Service has good integration points through its [APIs](https://www.visualstudio.com/integrate/api/overview#Releasepreview) that can let you interact with its release service from any other third-party - Jenkins in this case. This plugin makes use these APIs that lets you trigger a release in VS Azure DevOps or TFS, upon completion of a build in Jenkins. The plugin has a post build step - "VS Azure DevOps Continuous Deployment". ### Using the plugin Assuming that you have already [created the Release Definition](https://www.visualstudio.com/en-us/docs/release/author-release-definition/more-release-definition) and [linked the Jenkins as artifact source](https://www.visualstudio.com/en-us/docs/release/author-release-definition/understanding-artifacts#jenkins) in TFS/Azure DevOps - Release Management, you need to follow the following steps at the Jenkins side to trigger releases automatically, upon build creation. **0. Setup Release Definition with Jenkins as artifact source** This document assumes that you have already set up the RM definition that uses Jenkins artifact to deploy. This means your build/job is configured properly and archives artifacts. If not, see the following video to set up Release Definition with Jenkins build [![Release Jenkins artifact](images/rmWithJenkins-YT.png)](https://www.youtube.com/watch?v=ZC4hWYqdP_o&index=5&list=PLP3SfFPBD6cTJ2Jp5cHvjQ3flrbwQu-nN) **1. Add the post build action** Go to the Job configuration and add the post build action - **Trigger release in TFS/Azure DevOps**. ![Add post build action](images/addPostBuildAction.png) **2. Fill in the required fields** Fill in the details required for this post build action. You need the following details: * **Collection URL:** e.g. https://fabfiber.visualstudio.com/**DefaultCollection** <- Note that you need the url till the collection. * **Team project:** The VS Azure DevOps Project in which you have defined the release definition. * **Release definition:** The Release definition **name** that links this Jenkins job as an artifact source. You need to now enter the credentials that lets Jenkins trigger a release with the latest completed build, on your behalf. If you are using VS Azure DevOps, you just need to enter **PAT** with atleast "Release (read, write and execute)" scope. (Refer to this [link](https://www.visualstudio.com/en-us/get-started/setup/use-personal-access-tokens-to-authenticate) to understand how to create PAT). In case you are using TFS, you need to enter the **username** and **password**. ![Add post build action](images/fillFieldsForPostBuildAction.JPG) **3. All set. See CD in action** You have now automated your deployment trigger thereby enabling continuous deployment i.e. a checkin/commit would trigger a build and that will trigger a release. Go ahead and test your setup by manually triggering a build in Jenkins or by a code checkin/commit that kicks off Jenkins build which in turn will trigger the release in VS Azure DevOps. # FAQ ### How should I set up the plugin for my CodePlex project? * Find out the server for your project, which is displayed in the source code page for your project at codeplex.com. * The user name must be suffixed with `_cp` and the domain is `snd`. If your user name is redsolo, then enter "`snd\redsolo_cp`" as the user name in the plugin configuration. * Note that the user must be a member of the project to be able to create a workspace on the CodePlex server. That's all you need to do to start retrieving files from your project at codeplex.com. ### The plugin is having problems parsing the dates that TF outputs, what can I do? > :information_source: If you can upgrade to version 4 and up, then you can avoid a whole class of TF output parsing difficulties, otherwise, read on. :information_source: The TF command line outputs date according to the locale and Microsofts own specification. Sometimes the outputed date can not be parsed by any of the default locale dependent parsers that the JDK includes (_for some more details, see_ _[JENKINS-4184]_ _and_ _[JENKINS-4021]_). This will throw an exception in the change set parsing and fail the build. To fix this, do the following: * Change the locale by Windows Regional Settings to United States and English on the server and all hudson nodes. After that tf.exe should output dates in english, which can be parsed properly. * Start Hudson using the UnitedStates, English locale. Either set it using `-Duser.language=en -Duser.country=US` on the command line or check the documentation for the container that Hudson is running within. ### If I have multiple artifacts linked in my Release Definition, will this plugin trigger a release? Yes, it is supported from 1.3 version onwards. # Timeline ## Future The best way to get an idea of what will be coming in future releases is to look at the [list of open pull requests](https://github.com/jenkinsci/tfs-plugin/pulls). ## Present The next release will be 5.3.0. See what's been committed [since 5.2.1](https://github.com/jenkinsci/tfs-plugin/compare/tfs-5.2.1...master) and the upcoming [ReleaseNotes.md](ReleaseNotes.md). ## Past Details about previous releases can be found on the [Releases page](https://github.com/jenkinsci/tfs-plugin/releases). [wiki]: http://wiki.jenkins-ci.org/display/JENKINS/Team+Foundation+Server+Plugin [MIT Licence]: http://opensource.org/licenses/MIT [CloudBees]: https://www.cloudbees.com/ [Jenkins JIRA]: http://issues.jenkins-ci.org/secure/IssueNavigator.jspa?mode=hide&reset=true&jqlQuery=project+%3D+JENKINS+AND+status+in+%28Open%2C+%22In+Progress%22%2C+Reopened%29+AND+component+%3D+%27tfs-plugin%27 [Team Foundation Version Control]: https://msdn.microsoft.com/en-us/library/ms181237.aspx [Azure DevOps]: https://www.visualstudio.com/products/visual-studio-team-services-vs [TFS SDK for Java]: http://blogs.msdn.com/b/bharry/archive/2011/05/16/announcing-a-java-sdk-for-tfs.aspx [Microsoft Team Explorer Everywhere]: http://www.microsoft.com/en-us/download/details.aspx?id=40785 [JENKINS-4021]: https://issues.jenkins-ci.org/browse/JENKINS-4021 [JENKINS-4184]: https://issues.jenkins-ci.org/browse/JENKINS-4184 ================================================ FILE: ReleaseNotes.md ================================================ These notes are for release **(to be determined)**. Other releases and their notes can be found at the [tfs-plugin GitHub Releases](https://github.com/jenkinsci/tfs-plugin/releases) page. * Major: * TODO * Minor: * TODO ================================================ FILE: Releasing.md ================================================ Pre-requisites ============== 1. Machine: preferably Linux over Windows, to avoid any weirdness between Cygwin and Git for Windows 1. Oracle JDK 8 (see note in [Installing Jenkins on Red Hat distributions](https://wiki.jenkins-ci.org/display/JENKINS/Installing+Jenkins+on+Red+Hat+distributions) about CentOS's default Java) 1. Maven 3.2 or better 1. A recent enough Git 1. Make sure the `COMPUTERNAME` environment variable is defined, as the end-to-end tests rely on its presence. One can use the [EnvInject plugin](https://wiki.jenkins-ci.org/display/JENKINS/EnvInject+Plugin) to set it during the execution of the release job. 1. A GitHub clone you can pull from and push to non-interactively. (Consider configuring GitHub with a public key and use the SSH protocol for everything) 1. A "Jenkins infrastructure" account. They have some sort of LDAP server that provides SSO for JIRA, Confluence and Artifactory. 1. If you can log in to https://repo.jenkins-ci.org with your account, you're set to punch in those credentials in your `~/.m2/settings.xml` file: ```xml repo.jenkins-ci.org TODO TODO ``` 2. A great test is to try to perform a `mvn deploy`, which should attempt to upload some files to the snapshot repo and will need those credentials. 3. If you're worried about putting your "Jenkins infrastructure" password in plain-text in that file, Maven has a password encryption facility that relies on a master password in another file. Presumably, you secure access to the 2nd file by placing it on a thumbdrive that you carry with you when you're not at your computer, etc. 1. A TFS server or a Azure DevOps account, configured as per `Testing.md` Release ======= 1. Pre-release. Perform these manual steps on your workstation: 1. Run a full build, with all its end-to-end tests; it takes about 5 minutes: ``` mvn clean verify --batch-mode -Dtfs_server_name=<TFS host name or Azure DevOps account host name> -Dtfs_user_name=<user> -Dtfs_user_password=<password> ``` 2. Look at the commits since the last release by going to https://github.com/jenkinsci/tfs-plugin/releases and clicking the "XX commits to master since this release" link. It will be easiest to surf the associated pull requests, so hit Ctrl+F, search for "Merge pull request" and Ctrl+click every #XXX link to the right of the highlights. 3. Fill in the categories of the `ReleaseNotes.md` template, usually in one of the following formats: 1. <Summary>. Thanks to @<GitHub user name> for pull request #<pull request number>. 2. <Summary>, via pull request #<pull request number>. 4. Decide on the release version and on the next development version, based on the rules of [Semantic Versioning](http://semver.org/). 5. Update `ReleaseNotes.md` with the release version and merge/push to `master`. 6. Merge any "wiki" changes to `master`. 2. Automated release. Create a Jenkins job as follows: 1. General 1. Check "This project is parameterised" 1. String parameter **releaseVersion** 2. String parameter **developmentVersion** 2. SCM 1. Git 1. Repository Url: **git@github.com:jenkinsci/tfs-plugin.git** 2. Credentials: (select your previously-entered private key file) 3. Name: **origin** 4. Refspec: **+refs/heads/master:refs/remotes/origin/master** 5. Branch Specifier: **refs/heads/master** 6. Repository browser: **githubweb** 7. Additional Behaviours: 1. Clean before checkout 2. Check out to specific local branch (to avoid ["Git fatal: ref HEAD is not a symbolic ref" while using maven release plugin](https://stackoverflow.com/a/21184154/)) 1. Branch name: **master** 3. Build Environment 1. Add timestamps to the Console Output 2. Inject environment variables to the build process 1. **COMPUTERNAME** (the host name of the Jenkins node that will run the job) 2. **TFS_SERVER_NAME** (the TFS host name or Azure DevOps account host name) 3. Use secret text(s) or file(s) 1. **TFS_USER_NAME** and **TFS_USER_PASSWORD** are initialized from a credential 4. Build. Add the following steps: 1. "Shell script" step to check and prepare (filling in the blanks at the `git config` lines near the end) ```bash set +e # verify releaseVersion and developmentVersion if [[ "$releaseVersion" != +([0-9])\.+([0-9])\.+([0-9]) ]] then echo "ERROR: '$releaseVersion' is not a valid releaseVersion" exit 1 fi if [[ "$developmentVersion" != +([0-9])\.+([0-9])\.+([0-9])-SNAPSHOT ]] then echo "ERROR: '$developmentVersion' is not a valid developmentVersion" exit 1 fi # test SSH connection to Git ssh -Tv git@github.com if [[ $? != "1" ]] then echo "ERROR: Unable to connect to GitHub via SSH" exit 1 fi git config --local user.name '' git config --local user.email '' exit 0 ``` 2. "Maven" step as a dry-run, running all tests and performing a SNAPSHOT deploy ``` deploy dependency:go-offline --batch-mode -Dtfs_server_name=${TFS_SERVER_NAME} -Dtfs_user_name=${TFS_USER_NAME} -Dtfs_user_password=${TFS_USER_PASSWORD} ``` 3. "Maven" step to actually release ``` clean release:prepare release:perform --batch-mode -Dtag=tfs-${releaseVersion} -DreleaseVersion=${releaseVersion} -DdevelopmentVersion=${developmentVersion} ``` 4. "Shell script" step for post-release actions (filling in the blanks at the `git config` lines near the beginning) ```bash cd target/checkout git config --local user.name '' git config --local user.email ''   git checkout -b update_documentation_for_$releaseVersion origin/master   cat > ReleaseNotes.md < Application Tier > Team Project Collections 2. Click **Create Collection** 3. Set the *Name* to **jenkins-tfs-plugin** 4. Indicate in the *Description* field that this team project collection was created to isolate the automated tests from normal usage of the server. A hyperlink to this page would probably be a good idea. 5. Click **Verify** 6. Assuming everything was fine, click **Create** 7. Wait about 1 minute for the process to complete. 8. Assuming the team project collection was created OK, click **Close** 2. Follow the instructions at [Create a team project](https://msdn.microsoft.com/library/ms181477.aspx) to create a *Team Project*, as specified below: 1. When prompted for the *Name*, enter **FunctionalTests** 2. Indicate in the *Description* field that this team project was created for the automated functional tests. A hyperlink to this page would probably be a good idea. 3. The choice of process template is not important. 4. Select **Team Foundation Version Control** as the *version control system*. 4. Follow the instructions at [Add team members](https://msdn.microsoft.com/en-us/library/jj920206.aspx) to add the test user as a member of the **FunctionalTests** team project 5. Ensure the user has access to the team project 1. Open a web browser in InPrivate/incognito/private mode. This will make sure you aren't re-using authentication cookies or saved credentials. 2. Navigate to the **jenkins-tfs-plugin** team project collection on the server. Example: `http://tfs.corp.example.com:8080/tfs/jenkins-tfs-plugin` 3. Log in as the test user. 4. Confirm you can browse to the **FunctionalTests** team project. How to configure your Azure DevOps account ------------------------------------------- TODO: write this How to configure your development environment --------------------------------------------- Tests that need to connect to a server will only run during the `verify` phase *if* the `tfs_server_name` property was provided to Maven. The value of this property is the *fully-qualified DNS name (FQDN)* of the server, because a non-qualified host name appears to trigger NTLM authentication attempts. You should also provide the following properties: 1. tfs_server_name - set this to the host name of the server (ex. tfs.corp.example.com) 2. tfs_collection_url - set this to the full url of the collection (ex. http://tfs.corp.example.com:8081/tfs/jenkins-tfs-plugin) 3. tfs_user_name - set this to the test user you gave permissions to above (the default value if not provided is **jenkins-tfs-plugin**) 4. tfs_user_password - set this to the password of the test user (the default value if not provided is **for-test-only**) 5. Set the COMPUTERNAME environment variable as the end-to-end tests rely on its presence Example: mvn clean verify -Dtfs_collection_url="http://tfs.corp.example.com:8081/tfs/jenkins-tfs-plugin" -Dtfs_server_name=tfs.corp.example.com -Dtfs_user_name=jenkins-tfs-plugin -Dtfs_user_password=for-test-only ================================================ FILE: contributing.md ================================================ Contributing to the Team Foundation Server plugin for Jenkins ============================================================= ## Building Sources This is a Maven project with Java sources. The mvn package command will produce an HPI file that is the Jenkins Plugin file that will need to be installed on your Jenkins Server. - SET JAVA_HOME to the location of the JRE to build with (JRE 1.8 works) - Clone the repo from https://github.com/jenkinsci/tfs-plugin.git - change dir to the tfs-plugin folder - run "mvn package" - If you do not have Maven installed yet, here are [instructions](http://www.mkyong.com/maven/how-to-install-maven-in-windows/) on how to install it on Windows. - Initial build will have to download lots of libraries. This could take a few minutes. - This produces tfs-plugin\tfs\target\tfs.hpi ## Using IntelliJ IDEA To use Intellij IDEA as the editor for this project simply do the following after getting sources: 1) Open the tfs-plugin folder (root folder) in IntelliJ (I installed IntelliJ 17 community edition from https://www.jetbrains.com/idea/) 2) Go to File->Project Structure and click on Project 3) Specify the Project SDK (Java 1.8 works) You should now be able to build from within IntelliJ - NOTE to build the hpi file you will have to - bring up the Maven Projects tool window (View->Tool Windows->Maven Projects) and click the "execute maven goal" button - Then type "package" in the "Command Line" text box (of the Execute Maven Goal dialog) ## Debugging the Plugin See https://wiki.jenkins-ci.org/display/JENKINS/Plugin+tutorial for information on how to debug the plugin. From within IntelliJ: 1) Create a new Run configuration (Run | Edit Configurations... | + ) 1) Type = Maven 1) Name = run hpi 1) Working directory should be the full path to "../tfs-plugin/tfs" (NOTE this is NOT the root folder) 1) Command Line ` hpi:run -Djetty.port=8090 -Dhudson.plugins.tfs.telemetry.isDeveloperMode=true ` 1) or use whatever port you want 1) On the Runner tab 1) Environment Variables == MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n 1) To set the environment variables, make sure you uncheck 'Use project settings' first. Then use the '...' to set the variable. 1) Run or Debug this configuration using the play and debug icons (top right) 1) Set any breakpoints you want in IntelliJ 1) Navigate to http://localhost:8090/jenkins Note: this runs Jenkins on your local OS not in a Docker image. As such, any configurations you make are preserved between runs. You do not need to have Jenkins previously installed locally (IntelliJ will run Jenkins from a local JAR file). ## Installing Jenkins Locally for Manual Testing The easiest method is to run Jenkins in a Docker image. 1) Install Docker for your OS (https://www.docker.com/community-edition) 1) Install and run the Jenkins image ` docker run --name localJenkins -p 9191:9191 -p 50001:50001 --env "JENKINS_OPTS=--httpPort=9191" --env JENKINS_SLAVE_AGENT_PORT=50001 --env hudson.plugins.tfs.telemetry.isDeveloperMode=true jenkins ` - NOTES: - Note that this command line avoids port 8080 (the default) in case you have VSO deployed as well - Look in the output for the admin password - The output sent to the console is also where you will see any logger output - Note the environment variable "hudson.plugins.tfs.telemetry.isDeveloperMode". It is important to set this variable so that AppInsights data is sent to right key - This installs a Linux Jenkins server on Docker (NOT one based on Windows or the host OS) 1) Setup Jenkins 1) Go to http://localhost:9191 1) Enter the admin password 1) Install default plugins 1) Run Jenkins 1) Install Plugin Manually 1) Go to http://localhost:9191/pluginManager/advanced 1) Browse to the tfs.hpi file and Upload it 1) To update the plugin, repeat steps 1 and 2 and then restart Jenkins by going to http://localhost:9191/restart 1) To stop Jenkins and start from scratch ` docker stop localJenkins docker container prune ` Then repeat step 2 and 3 above ================================================ FILE: pom.xml ================================================ 4.0.0 org.jenkins-ci.plugins plugin 3.2 tfs-parent pom Team Foundation Server Plug-in parent 5.157.1-SNAPSHOT 1.642.3 7 2.19.1 ${maven-surefire-plugin.version} UTF-8 UTF-8 2.8 dastahel David Staheli dastahel@microsoft.com -5 jpricket Jason Prickett jpricket@microsoft.com -5 jeffyoung Jeff Young jeyou@microsoft.com -5 jenkinssriramb Sriram Balasubramaniyan sriramb@microsoft.com +5 kalyan Kalyan kasubram@microsoft.com +5 keljos Kellie Jos kej@microsoft.com -5 mosabua Manfred Moser manfred@simpligility.com -8 leantk Leah Antkiewicz leantk@microsoft.com -5 scm:git:https://github.com/jenkinsci/tfs-plugin.git scm:git:https://github.com/jenkinsci/tfs-plugin.git HEAD repo.jenkins-ci.org https://repo.jenkins-ci.org/snapshots/ repo.jenkins-ci.org https://repo.jenkins-ci.org/releases/ tfs-sdk tfs repo.jenkins-ci.org https://repo.jenkins-ci.org/public/ repo.jenkins-ci.org https://repo.jenkins-ci.org/public/ ================================================ FILE: tfs/checkstyle.xml ================================================ ================================================ FILE: tfs/pom.xml ================================================ 4.0.0 org.jenkins-ci.plugins tfs-parent 5.157.1-SNAPSHOT tfs hpi Team Foundation Server Plug-in 5.157.1-SNAPSHOT http://wiki.jenkins-ci.org/display/JENKINS/Team+Foundation+Server+Plugin false MIT licenseAll source code is under the MIT license. Microsoft Visual Studio Team Foundation Server 2012 Software Development Kit for Java license terms http://download.microsoft.com/download/5/9/9/5993F89B-AEF0-4381-9CEE-D3D7BA9EA33B/license.html Apache Commons Codec, Apache Commons Logging, Apache HttpClient, Apache Commons Lang, Apache log4j, and Apache ServiceMix are licensed by the Apache Foundation under the Apache License, Version 2.0. http://www.apache.org/licenses/LICENSE-2.0 The Apache Foundation's Xerces Java Parser project is licensed by the Apache Foundation under the Apache License, Version 1.1. http://www.apache.org/licenses/LICENSE-1.1 The Woodstox XML Processor and the StAX API are licensed by their authors under the Apache License, Version 2.0. http://www.apache.org/licenses/LICENSE-2.0 This product includes software from the Hypersonic SQL DB http://hsqldb.org/. http://www.hsqldb.org/web/hsqlLicense.html This product includes software from the Cryptix project http://www.cryptix.org/. http://cryptix.org/LICENSE.TXT org.apache.maven.plugins maven-jar-plugin 2.4 true maven-release-plugin 2.5.1 deploy org.jenkins-ci.tools maven-hpi-plugin true 3.2.0 maven-javadoc-plugin ${maven-javadoc-plugin.version} verify-javadoc verify javadoc org.apache.maven.plugins maven-surefire-plugin ${maven-surefire-plugin.version} org.apache.maven.surefire surefire-junit47 ${maven-surefire-plugin.version} hudson.plugins.tfs.IntegrationTests org.apache.maven.plugins maven-checkstyle-plugin 2.17 validate validate checkstyle.xml UTF-8 true true check integration_test_with_TFS_server tfs_server_name org.apache.maven.plugins maven-failsafe-plugin ${maven-surefire-plugin.version} org.apache.maven.surefire surefire-junit47 ${maven-surefire-plugin.version} integration-test verify **/Test*.java **/*Test.java **/*TestCase.java hudson.plugins.tfs.IntegrationTests org.jenkins-ci.plugins mailer 1.16 org.jenkins-ci.plugins credentials 1.25 org.jenkins-ci.plugins.workflow workflow-job 2.10 org.jenkins-ci.plugins.workflow workflow-cps 2.30 org.jenkins-ci annotation-indexer org.jenkins-ci.plugins.workflow workflow-support 2.14 org.jenkins-ci.plugins git 2.5.2 com.microsoft.tfs.sdk com.microsoft.tfs.sdk 14.0.3 junit junit 4.12 test xmlunit xmlunit 1.6 test org.mockito mockito-core 1.10.19 test org.littleshoot littleproxy 1.1.0 test org.jenkins-ci.plugins junit 1.6 test org.apache.httpcomponents httpclient 4.5 jar com.google.code.gson gson 2.3.1 org.json json 20090211 ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/ChangeSetReader.java ================================================ package hudson.plugins.tfs; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import hudson.model.Run; import hudson.scm.RepositoryBrowser; import org.apache.commons.digester.Digester; import org.xml.sax.SAXException; import hudson.plugins.tfs.model.ChangeLogSet; import hudson.plugins.tfs.model.ChangeSet; import hudson.scm.ChangeLogParser; import hudson.util.Digester2; /** * TeamFoundation change log reader. * * @author Erik Ramfelt */ public class ChangeSetReader extends ChangeLogParser { @Override public ChangeLogSet parse(final Run build, final RepositoryBrowser browser, final File changelogFile) throws IOException, SAXException { try (FileInputStream stream = new FileInputStream(changelogFile); Reader reader = new InputStreamReader(stream, Charset.defaultCharset())) { return parse(build, browser, reader); } } /** Performs the actual parsing. */ public ChangeLogSet parse(final Run build, final RepositoryBrowser browser, final Reader reader) throws IOException, SAXException { List changesetList = new ArrayList(); Digester digester = new Digester2(); digester.push(changesetList); digester.addObjectCreate("*/changeset", ChangeSet.class); digester.addSetProperties("*/changeset"); digester.addBeanPropertySetter("*/changeset/date", "dateStr"); digester.addBeanPropertySetter("*/changeset/user"); digester.addBeanPropertySetter("*/changeset/checked_in_by_user", "checkedInBy"); digester.addBeanPropertySetter("*/changeset/comment"); digester.addSetNext("*/changeset", "add"); digester.addObjectCreate("*/changeset/items/item", ChangeSet.Item.class); digester.addSetProperties("*/changeset/items/item"); digester.addBeanPropertySetter("*/changeset/items/item", "path"); digester.addSetNext("*/changeset/items/item", "add"); digester.parse(reader); return new ChangeLogSet(build, browser, changesetList); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/ChangeSetWriter.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Util; import hudson.plugins.tfs.model.ChangeSet; import hudson.plugins.tfs.util.DateUtil; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; import java.util.List; import org.apache.commons.io.IOUtils; /** * Team Foundation change log writer. * * @author Erik Ramfelt */ public class ChangeSetWriter { /** * Writes the list of change sets to the file * @param changeSets list of change sets * @param changelogFile file to write change sets to * * @throws IOException If an I/O error occurs */ @SuppressFBWarnings(value = "DM_DEFAULT_ENCODING", justification = "Better mot modify charset in case it might raise errors") public void write(List changeSets, File changelogFile) throws IOException { FileWriter writer = new FileWriter(changelogFile); try { write(changeSets, writer); } finally { IOUtils.closeQuietly(writer); } } /** * Writes the list of change sets to the writer * @param changeSets list of change sets * @param output output writer */ public void write(List changeSets, Writer output) { PrintWriter writer = new PrintWriter(output); writer.println(""); writer.println(""); for (ChangeSet changeSet : changeSets) { writer.println(String.format("\t", changeSet.getVersion())); write(changeSet, writer); writer.println("\t"); } writer.println(""); writer.flush(); } private void write(ChangeSet changeSet, PrintWriter writer) { writer.println(String.format("\t\t%s", DateUtil.TFS_DATETIME_FORMATTER.get().format(changeSet.getDate()))); if (Util.fixEmpty(changeSet.getDomain()) == null) { writer.println(String.format("\t\t%s", escapeForXml(changeSet.getUser()))); } else { writer.println(String.format("\t\t%s\\%s", escapeForXml(changeSet.getDomain()), escapeForXml(changeSet.getUser()))); } if (Util.fixEmpty(changeSet.getCheckedInBy()) != null) { writer.println(String.format("\t\t%s", escapeForXml(changeSet.getCheckedInBy()))); } writer.println(String.format("\t\t%s", escapeForXml(changeSet.getComment()))); if (changeSet.getItems().size() > 0) { writer.println("\t\t"); for (ChangeSet.Item item : changeSet.getItems()) { writer.println(String.format("\t\t\t%s", escapeForXml(item.getAction()), escapeForXml(item.getPath()))); } writer.println("\t\t"); } } /** * * Converts the input in the way that it can be written to the XML. * Special characters are converted to XML understandable way. * * @param object The object to be escaped. * @return Escaped string that can be written to XML. */ private String escapeForXml(Object object) { if(object == null) { return null; } //Loop through and replace the special chars. String string = object.toString(); int size = string.length(); char ch; StringBuilder escapedString = new StringBuilder(size); for(int index = 0;index < size;index ++) { //Convert special chars. ch = string.charAt(index); switch(ch) { case '&' : escapedString.append("&"); break; case '<' : escapedString.append("<"); break; case '>' : escapedString.append(">"); break; case '\'' : escapedString.append("'"); break; case '\"' : escapedString.append(""");break; default: escapedString.append(ch); } } return escapedString.toString(); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/CommitParameterAction.java ================================================ package hudson.plugins.tfs; import hudson.plugins.git.RevisionParameterAction; import hudson.plugins.tfs.model.GitCodePushedEventArgs; import hudson.plugins.tfs.util.UriHelper; import org.eclipse.jgit.transport.RemoteConfig; import org.eclipse.jgit.transport.URIish; import java.net.URI; /** * Used as a build parameter to record information about the associated project and * Visual Studio Team Services account or TFS server to facilitate integration. */ public class CommitParameterAction extends RevisionParameterAction { private final GitCodePushedEventArgs gitCodePushedEventArgs; /** * Saves the repo uri and GitCodePushedEventArgs. */ public CommitParameterAction(final GitCodePushedEventArgs e) { super(e.commit, e.getRepoURIish()); this.gitCodePushedEventArgs = e; } /** * Returns the GitCodePushedEventArgs. */ public GitCodePushedEventArgs getGitCodePushedEventArgs() { return gitCodePushedEventArgs; } /** * Returns true if the git repo uri can originate from any of the remotes passed in. */ @Override public boolean canOriginateFrom(final Iterable remotes) { final URI repoUri = gitCodePushedEventArgs.repoUri; for (final RemoteConfig remote : remotes) { for (final URIish remoteURL : remote.getURIs()) { final URI remoteUri = URI.create(remoteURL.toString()); if (UriHelper.areSameGitRepo(remoteUri, repoUri)) { return true; } } } return false; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/JenkinsEventNotifier.java ================================================ package hudson.plugins.tfs; import hudson.plugins.tfs.model.ConnectionParameters; import hudson.plugins.tfs.model.JobCompletionEventArgs; import hudson.plugins.tfs.util.TeamRestClient; import jenkins.model.Jenkins; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.HttpHostConnectException; import org.apache.http.impl.client.HttpClientBuilder; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URI; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Formatter; import java.util.List; import java.util.logging.Logger; /** * This class sends Jenkins events to all "connected" team collections. * Current list of events: * - Job Completion event */ public final class JenkinsEventNotifier { private static final Logger log = Logger.getLogger(JenkinsEventNotifier.class.getName()); private static final String ENCODING = "UTF-8"; /** * Hiding the constructor for this Utility class. */ private JenkinsEventNotifier() { } /** * Send the Job Completion event to connected TFS/VSTS servers. */ public static void sendJobCompletionEvent(final JSONObject payload) { final List connectedCollections = TeamCollectionConfiguration.getConnectedCollections(); for (final TeamCollectionConfiguration c : connectedCollections) { try { // Check to see if there are any collections "connected" to this Jenkins server final ConnectionParameters connectionParameters = c.getConnectionParameters(); final TeamRestClient client = new TeamRestClient(URI.create(c.getCollectionUrl())); payload.put("server", connectionParameters.getConnectionKey()); final String jsonPayload = payload.toString(); final JobCompletionEventArgs args = new JobCompletionEventArgs( connectionParameters.getConnectionKey(), jsonPayload, getPayloadSignature(connectionParameters.getConnectionSignature(), jsonPayload)); client.sendJobCompletionEvent(args); } catch (final Exception e) { log.warning("ERROR: sendJobCompletionEvent: (collection=" + c.getCollectionUrl() + ") " + e.getMessage()); } } } /** * This is a helper method to get the JSON for a Jenkins object. * @param url * @return */ public static String getApiJson(final String url) { try { Jenkins jenkins = Jenkins.getInstance(); if (jenkins == null) { return ""; } final String rootUrl = jenkins.getRootUrl(); final String fullUrl = urlCombine(rootUrl, url, "api", "json"); final HttpClient client = HttpClientBuilder.create().build(); final HttpGet request = new HttpGet(fullUrl); // add request header request.addHeader("User-Agent", "Jenkins-Self"); final HttpResponse response = client.execute(request); final int statusCode = response.getStatusLine().getStatusCode(); if (statusCode <= HttpURLConnection.HTTP_ACCEPTED) { try (BufferedReader reader = new BufferedReader( new InputStreamReader(response.getEntity().getContent(), ENCODING))) { final StringBuilder result = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { result.append(line); result.append("\n"); } return result.toString(); } } else { log.warning("ERROR: getApiJson: (url=" + url + ") failed due to Http error #" + statusCode); return null; } } catch (final HttpHostConnectException e) { log.warning("ERROR: getApiJson: (url=" + url + ") " + e.getMessage()); return null; } catch (final Exception e) { log.warning("ERROR: getApiJson: (url=" + url + ") " + e.getMessage()); throw new RuntimeException(e); } } /** * Calculates the payload hash. * @param secret * @param payload * @return * @throws NoSuchAlgorithmException * @throws InvalidKeyException * @throws UnsupportedEncodingException */ public static String getPayloadSignature(final String secret, final String payload) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException { final SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(ENCODING), "HmacSHA1"); final Mac mac = Mac.getInstance("HmacSHA1"); mac.init(signingKey); return toHexString(mac.doFinal(payload.getBytes(ENCODING))); } private static String urlCombine(final String url, final String... parts) { final StringBuilder sb = new StringBuilder(); if (url != null) { sb.append(url); for (final String s : parts) { if (StringUtils.isNotBlank(s)) { if (sb.length() > 0 && sb.charAt(sb.length() - 1) != '/') { sb.append("/"); } sb.append(s); } } } return sb.toString(); } private static String toHexString(final byte[] bytes) { final Formatter formatter = new Formatter(); for (final byte b : bytes) { formatter.format("%02x", b); } return formatter.toString(); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/PullRequestParameterAction.java ================================================ package hudson.plugins.tfs; import hudson.plugins.tfs.model.PullRequestMergeCommitCreatedEventArgs; /** * Action that adds the pull request merge event args to the build information. */ public class PullRequestParameterAction extends CommitParameterAction { private final PullRequestMergeCommitCreatedEventArgs args; public PullRequestParameterAction(final PullRequestMergeCommitCreatedEventArgs args) { super(args); this.args = args; } public PullRequestMergeCommitCreatedEventArgs getPullRequestMergeCommitCreatedEventArgs() { return args; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/SafeParametersAction.java ================================================ package hudson.plugins.tfs; import hudson.EnvVars; import hudson.Extension; import hudson.model.EnvironmentContributor; import hudson.model.ParameterValue; import hudson.model.ParametersAction; import hudson.model.Run; import hudson.model.TaskListener; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Extension class of ParametersAction to pass in parameters as safe parameters.. */ @Restricted(NoExternalUse.class) public class SafeParametersAction extends ParametersAction { private List parameters; public SafeParametersAction(final List parameters) { this.parameters = parameters; } public SafeParametersAction(final ParameterValue... parameters) { this(Arrays.asList(parameters)); } @Override public List getParameters() { return Collections.unmodifiableList(parameters); } @Override public ParameterValue getParameter(final String name) { for (ParameterValue parameter : parameters) { if (parameter != null && parameter.getName().equals(name)) { return parameter; } } return null; } /** * Environment contributor for SafeParametersAction. */ @Extension public static final class SafeParametersActionEnvironmentContributor extends EnvironmentContributor { @Override public void buildEnvironmentFor(final Run r, final EnvVars envs, final TaskListener listener) throws IOException, InterruptedException { SafeParametersAction action = r.getAction(SafeParametersAction.class); if (action != null) { for (ParameterValue p : action.getParameters()) { envs.putIfNotNull(p.getName(), String.valueOf(p.getValue())); } } } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TFSLabeler.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs; import hudson.EnvVars; import hudson.Extension; import hudson.FilePath; import hudson.Launcher; import hudson.Util; import hudson.model.*; import hudson.plugins.tfs.commands.LabelCommand; import hudson.plugins.tfs.model.Server; import hudson.plugins.tfs.telemetry.TelemetryHelper; import hudson.plugins.tfs.util.BuildVariableResolver; import hudson.scm.SCM; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.BuildStepMonitor; import hudson.tasks.Notifier; import hudson.tasks.Publisher; import hudson.util.VariableResolver; import hudson.util.VariableResolver.Union; import org.kohsuke.stapler.DataBoundConstructor; import java.io.IOException; import java.util.logging.Logger; /** * Used to create a label in TFS after a build is completed. * @author Rodrigo Lopes (rodrigolopes) */ public class TFSLabeler extends Notifier { private String whenToLabel; private String labelName; private static final Logger logger = Logger.getLogger(TFSLabeler.class.getName()); @Extension public static class DescriptorImpl extends BuildStepDescriptor { public DescriptorImpl() { super(TFSLabeler.class); } @Override public String getDisplayName() { return "Create a label in TFVC"; } @Override public boolean isApplicable(Class jobType) { return true; } } @DataBoundConstructor public TFSLabeler(String whenToLabel, String labelName) { this.whenToLabel = whenToLabel; this.labelName = labelName; } @Override public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { SCM scm = build.getRootBuild().getProject().getScm(); if (!(scm instanceof TeamFoundationServerScm)) { listener.getLogger().println("Labels are only supported for projects using the 'Team Foundation Server' SCM"); return false; } TeamFoundationServerScm tfsScm = (TeamFoundationServerScm) scm; boolean buildSuccess = Result.SUCCESS.equals(build.getResult()); String whenCreateLabel = getWhenToLabel(); if ("always".equals(whenCreateLabel) || ("success".equals(whenCreateLabel) && buildSuccess)) { final Launcher localLauncher = launcher != null ? launcher : new Launcher.LocalLauncher(listener); Server server = tfsScm.createServer(localLauncher, listener, build.getRootBuild()); Computer computer = Computer.currentComputer(); String normalizedLabelName = computeDynamicValue(build, getLabelName()); String tfsWorkspace = tfsScm.getWorkspaceName(build.getRootBuild(), computer); String tfsProjectPath = computeDynamicValue(build, tfsScm.getProjectPath()); try { logger.info(String.format("Create label '%s' on workspace '%s' with project path '%s' ", normalizedLabelName, tfsWorkspace, tfsProjectPath)); LabelCommand labelCommand = new LabelCommand(server, normalizedLabelName, tfsWorkspace, tfsProjectPath); server.execute(labelCommand.getCallable()); // Send telemetry TelemetryHelper.sendEvent("team-label", new TelemetryHelper.PropertyMapBuilder() .serverContext(server.getUrl(), server.getUrl()) .build()); } finally { server.close(); } } return true; } /** * Replace an expression in the form ${name} in the given String * by the value of the matching environment variable or build parameter.
*/ private String computeDynamicValue(AbstractBuild build, String parameterizedValue) throws IllegalStateException, InterruptedException, IOException { final EnvVars envVars = build.getEnvironment(TaskListener.NULL); final VariableResolver environmentVariables = new VariableResolver() { public String resolve(String name) { return envVars.get(name); } }; final BuildVariableResolver buildVariables = new BuildVariableResolver(build.getProject()); @SuppressWarnings("unchecked") final Union bothVariables = new VariableResolver.Union(buildVariables, environmentVariables); String value = Util.replaceMacro(parameterizedValue, bothVariables); logger.fine("oldValue = " + parameterizedValue + "; newValue = " + value); return value; } public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.STEP; } public String getWhenToLabel() { return whenToLabel; } public String getLabelName() { return labelName; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TFSRevisionState.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; import hudson.scm.SCMRevisionState; @ExportedBean public class TFSRevisionState extends SCMRevisionState { @Exported(visibility=2) public final int changesetVersion; @Exported(visibility=1) public final String projectPath; public TFSRevisionState(String changesetVersion, String projectPath) { this.changesetVersion = Integer.parseInt(changesetVersion, 10); this.projectPath = projectPath; } public TFSRevisionState(int changesetVersion, String projectPath) { this.changesetVersion = changesetVersion; this.projectPath = projectPath; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamBuildDetailsAction.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs; import hudson.model.Action; import hudson.plugins.tfs.util.QueryString; import hudson.plugins.tfs.util.UriHelper; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; import java.io.Serializable; import java.net.URI; import java.util.HashMap; import java.util.Map; /** * Captures the details of the TFS/Team Services build which triggered us. */ @ExportedBean(defaultVisibility = 999) public class TeamBuildDetailsAction implements Action, Serializable { private static final long serialVersionUID = 1L; public Map buildVariables = new HashMap(); public String buildUrl; public TeamBuildDetailsAction() { } public TeamBuildDetailsAction(final Map buildVariables) { this.buildVariables = new HashMap(buildVariables); this.buildUrl = determineBuildUrl(buildVariables).toString(); } static URI determineBuildUrl(final Map buildVariables) { // TODO: eventually call the build REST API to obtain the proper web URL final String collectionUri = buildVariables.get("System.TeamFoundationCollectionUri"); final String projectName = buildVariables.get("System.TeamProject"); final String buildId = buildVariables.get("Build.BuildId"); final QueryString query = new QueryString("buildId", buildId); final URI result = UriHelper.join( collectionUri, projectName, "_build", "index", query); return result; } @Override public String getIconFileName() { return "/plugin/tfs/48x48/logo.png"; } @Override public String getDisplayName() { return "TFS/Team Services build"; } @Override public String getUrlName() { return "team-build"; } // the following methods are called from this/summary.jelly and this/index.jelly @Exported public String getBuildNumber() { return buildVariables.get("Build.BuildNumber"); } @Exported public String getBuildDefinitionName() { return buildVariables.get("Build.DefinitionName"); } @Exported public String getBuildUrl() { return buildUrl; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamBuildEndpoint.java ================================================ package hudson.plugins.tfs; import com.fasterxml.jackson.databind.ObjectMapper; import hudson.Extension; import hudson.model.BuildAuthorizationToken; import hudson.model.BuildableItem; import hudson.model.Item; import hudson.model.Job; import hudson.model.UnprotectedRootAction; import hudson.plugins.tfs.model.AbstractCommand; import hudson.plugins.tfs.model.BuildCommand; import hudson.plugins.tfs.model.BuildWithParametersCommand; import hudson.plugins.tfs.model.PingCommand; import hudson.plugins.tfs.model.TeamBuildPayload; import hudson.plugins.tfs.telemetry.TelemetryHelper; import hudson.plugins.tfs.util.EndpointHelper; import hudson.plugins.tfs.util.MediaType; import jenkins.model.Jenkins; import jenkins.model.ParameterizedJobMixIn; import jenkins.util.TimeDuration; import net.sf.json.JSONObject; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.kohsuke.stapler.ForwardToView; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.HttpResponses; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_CREATED; import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import static javax.servlet.http.HttpServletResponse.SC_OK; /** * The endpoint that TFS/Team Services will PUT to when it wants to schedule a build in Jenkins. */ @Extension public class TeamBuildEndpoint implements UnprotectedRootAction { private static final Logger LOGGER = Logger.getLogger(TeamBuildEndpoint.class.getName()); private static final Map COMMAND_FACTORIES_BY_NAME; public static final String URL_NAME = "team-build"; public static final String PARAMETER = "parameter"; public static final String BUILD_SOURCE_BRANCH = "Build.SourceBranch"; public static final String QUEUEJOBTASK_MULTIBRANCH_JOB_BRANCH = "QueueJobTask.MultibranchPipelineBranch"; static final String URL_PREFIX = "/" + URL_NAME + "/"; static { final Map map = new TreeMap(String.CASE_INSENSITIVE_ORDER); map.put("ping", new PingCommand.Factory()); map.put("build", new BuildCommand.Factory()); map.put("buildWithParameters", new BuildWithParametersCommand.Factory()); COMMAND_FACTORIES_BY_NAME = Collections.unmodifiableMap(map); } private String commandName; private String jobName; @Override public String getIconFileName() { return null; } @Override public String getDisplayName() { return null; } @Override public String getUrlName() { return URL_NAME; } public String getCommandName() { return commandName; } public String getJobName() { return jobName; } boolean decodeCommandAndJobNames(final String pathInfo) { if (pathInfo.startsWith(URL_PREFIX)) { final String restOfPath = pathInfo.substring(URL_PREFIX.length()); final int firstSlash = restOfPath.indexOf('/'); if (firstSlash != -1) { commandName = restOfPath.substring(0, firstSlash); if (firstSlash < restOfPath.length() - 1) { final String encodedJobName = restOfPath.substring(firstSlash + 1); try { jobName = URLDecoder.decode(encodedJobName, MediaType.UTF_8.name()); } catch (final UnsupportedEncodingException e) { throw new Error(e); } return true; } } else { commandName = restOfPath; } } return false; } /** * External endpoint for getting a description of the endpoints in this class. */ public HttpResponse doIndex(final HttpServletRequest request) throws IOException { final Class me = this.getClass(); final InputStream stream = me.getResourceAsStream("TeamBuildEndpoint.html"); final Jenkins instance = Jenkins.getActiveInstance(); final String rootUrl = instance.getRootUrl(); final String commandRows = describeCommands(COMMAND_FACTORIES_BY_NAME, URL_NAME); try { final String template = IOUtils.toString(stream, MediaType.UTF_8); final String content = String.format(template, URL_NAME, commandRows, rootUrl); return HttpResponses.html(content); } finally { IOUtils.closeQuietly(stream); } } static String describeCommands(final Map commandMap, final String urlName) { final String newLine = System.getProperty("line.separator"); final StringBuilder sb = new StringBuilder(); for (final Map.Entry commandPair : commandMap.entrySet()) { final String commandName = commandPair.getKey(); final AbstractCommand.Factory factory = commandPair.getValue(); sb.append("").append(newLine); sb.append("").append(commandName).append("").append(newLine); sb.append("").append('/').append(urlName).append('/').append(commandName).append('/').append("JOB_NAME").append("").append(newLine); final String rawSample = factory.getSampleRequestPayload(); final String escapedSample = StringEscapeUtils.escapeHtml4(rawSample); sb.append("
").append(escapedSample).append("
").append(newLine); sb.append("").append(newLine); } return sb.toString(); } @SuppressWarnings("deprecation" /* We want to do exactly what Jenkins does */) void checkPermission(final Job job, final ParameterizedJobMixIn.ParameterizedJob jobMixin, final StaplerRequest req, final StaplerResponse rsp) throws IOException { final BuildAuthorizationToken authToken = jobMixin.getAuthToken(); hudson.model.BuildAuthorizationToken.checkPermission(job, authToken, req, rsp); } void dispatch(final StaplerRequest req, final StaplerResponse rsp, final TimeDuration delay) throws IOException { try { final JSONObject response = innerDispatch(req, rsp, delay); if (response.containsKey("created")) { rsp.setStatus(SC_CREATED); } else { rsp.setStatus(SC_OK); } rsp.setContentType(MediaType.APPLICATION_JSON_UTF_8); final PrintWriter w = rsp.getWriter(); final String responseJsonString = response.toString(); w.print(responseJsonString); w.println(); } catch (final IllegalArgumentException e) { LOGGER.log(Level.WARNING, "IllegalArgumentException", e); EndpointHelper.error(SC_BAD_REQUEST, e); } catch (final ForwardToView e) { throw e; } catch (final Exception e) { final String template = "Error while performing reaction to '%s' command."; final String message = String.format(template, commandName); LOGGER.log(Level.SEVERE, message, e); EndpointHelper.error(SC_INTERNAL_SERVER_ERROR, e); } } /** * If we are calling this method, it means we didn't find any job or project with jobName. Assuming we are building * multibranch pipeline projects in this case. * * We will try to determine the branch name in the following sequence: * 1. For JenkinsQueueJob task 1.115.0+, we send the branch in "QueueJobTask.MultibranchPipelineBranch". * 2. Check if the jobName is composed from ${multibranch_pipeline}/${branch_name} * 3. Check if the payload has BuildSource variable defined (for PR builds) * * If we can't determine the branch name, throw. */ private String getBranch(final String jobName, final StaplerRequest req) { final String json = req.getParameter("json"); final JSONObject formData = JSONObject.fromObject(json); final TeamBuildPayload payload = EndpointHelper.MAPPER.convertValue(formData, TeamBuildPayload.class); String sourceBranch = payload.BuildVariables.get(QUEUEJOBTASK_MULTIBRANCH_JOB_BRANCH); if (sourceBranch == null || sourceBranch.trim().isEmpty()) { final int idx = jobName.indexOf('/'); if (idx > 0) { sourceBranch = jobName.substring(idx + 1); } else { sourceBranch = payload.BuildVariables.get(BUILD_SOURCE_BRANCH); } } if (sourceBranch == null || sourceBranch.trim().isEmpty()) { throw new IllegalArgumentException("Could not find branch from job name. If building a multibranch" + "pipeline job, the job name should be in the format of '${multibranch pipeline name}/${branch}.'"); } try { return URLEncoder.encode(sourceBranch.replace("refs/heads/", ""), "UTF-8"); } catch (final UnsupportedEncodingException e) { throw new RuntimeException("Failed to encode branch: " + sourceBranch, e); } } private String getJobNameFromNestedFolder(final String jobName) { final int idx = jobName.indexOf('/'); if (idx > 0) { return jobName.substring(0, idx); } return jobName; } private Job getJob(final String jobName, final StaplerRequest req) { final Jenkins jenkins = Jenkins.getActiveInstance(); Job job = jenkins.getItemByFullName(jobName, Job.class); if (job == null) { /* For jobs queued by JenkinsQueueJob task 1.115.0+, the jobname sent over the wire is the real job name * without branch name tagged at the end. * Try get the job as it was specified first, if there is no such job, fall back to existing logic and * assume the jobname is in the format of ${multibranchPipelineJobname}/${branchName]. */ final Item mbPipelineJobItem = jenkins.getItemByFullName(jobName); final Item item = (mbPipelineJobItem != null) ? mbPipelineJobItem : jenkins.getItemByFullName(getJobNameFromNestedFolder(jobName)); if (item != null) { final Collection allJobs = item.getAllJobs(); final String sourceBranch = getBranch(jobName, req); for (final Job j : allJobs) { if (j.getName().equals(sourceBranch)) { job = j; break; } } } } if (job == null) { throw new IllegalArgumentException("Job: " + jobName + " not found"); } return job; } private JSONObject innerDispatch(final StaplerRequest req, final StaplerResponse rsp, final TimeDuration delay) throws IOException, ServletException { commandName = null; jobName = null; final String pathInfo = req.getPathInfo(); if (!decodeCommandAndJobNames(pathInfo)) { if (commandName == null) { throw new IllegalArgumentException("Command not provided"); } if (jobName == null) { throw new IllegalArgumentException("Job name not provided after command"); } } if (!COMMAND_FACTORIES_BY_NAME.containsKey(commandName)) { throw new IllegalArgumentException("Command not implemented"); } final Job job = getJob(jobName, req); final ParameterizedJobMixIn.ParameterizedJob jobMixin = (ParameterizedJobMixIn.ParameterizedJob) job; checkPermission(job, jobMixin, req, rsp); final TimeDuration actualDelay = delay == null ? new TimeDuration(jobMixin.getQuietPeriod()) : delay; final AbstractCommand.Factory factory = COMMAND_FACTORIES_BY_NAME.get(commandName); final AbstractCommand command = factory.create(); final JSONObject response; final JSONObject formData = req.getSubmittedForm(); final ObjectMapper mapper = EndpointHelper.MAPPER; final TeamBuildPayload teamBuildPayload = mapper.convertValue(formData, TeamBuildPayload.class); final BuildableItem buildable = (BuildableItem) job; response = command.perform(job, buildable, req, formData, mapper, teamBuildPayload, actualDelay); return response; } /** * External endpoint for testing the connection to Jenkins. */ public void doPing( final StaplerRequest request, final StaplerResponse response, @QueryParameter final TimeDuration delay ) throws IOException { dispatch(request, response, delay); } /** * External endpoint for triggering a build. */ public void doBuild( final StaplerRequest request, final StaplerResponse response, @QueryParameter final TimeDuration delay ) throws IOException { // Send telemetry TelemetryHelper.sendEvent("team-build", new TelemetryHelper.PropertyMapBuilder() .build()); dispatch(request, response, delay); } /** * External endpoint for triggering a build with paramters. */ public void doBuildWithParameters( final StaplerRequest request, final StaplerResponse response, @QueryParameter final TimeDuration delay ) throws IOException { // Send telemetry TelemetryHelper.sendEvent("team-build-parameters", new TelemetryHelper.PropertyMapBuilder() .build()); dispatch(request, response, delay); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamCollectResultsPostBuildAction.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs; import hudson.Extension; import hudson.FilePath; import hudson.Launcher; import hudson.model.AbstractProject; import hudson.model.Run; import hudson.model.TaskListener; import hudson.plugins.tfs.model.TeamRequestedResult; import hudson.plugins.tfs.model.TeamResultType; import hudson.plugins.tfs.telemetry.TelemetryHelper; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.BuildStepMonitor; import hudson.tasks.Publisher; import hudson.tasks.Recorder; import hudson.util.DirScanner; import hudson.util.io.Archiver; import hudson.util.io.ArchiverFactory; import jenkins.tasks.SimpleBuildStep; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; import javax.annotation.Nonnull; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.nio.file.Files; import java.util.ArrayList; import java.util.List; public class TeamCollectResultsPostBuildAction extends Recorder implements SimpleBuildStep { private static final String TEAM_RESULTS = "team-results"; static final String TEAM_RESULTS_ZIP = "team-results.zip"; private List requestedResults = new ArrayList(); @DataBoundConstructor public TeamCollectResultsPostBuildAction() { } public List getRequestedResults() { return requestedResults; } @DataBoundSetter public void setRequestedResults(final List requestedResults) { this.requestedResults = requestedResults; } @Override public void perform( @Nonnull final Run run, @Nonnull final FilePath workspace, @Nonnull final Launcher launcher, @Nonnull final TaskListener listener) throws InterruptedException, IOException { TelemetryHelper.sendEvent("team-collect-results", new TelemetryHelper.PropertyMapBuilder() .build()); // TODO: do we want to emit an error or warning like the following? /* if (requestedResults == null || requestedResults.size() == 0) { final String template = "No results were requested. Aborting the '%s' post-build action."; final String message = String.format(template, displayName); listener.error(message); return; } */ final PrintStream logger = listener.getLogger(); logger.print("Recording results..."); final File rootDir = run.getRootDir(); final File resultsRoot = new File(rootDir, TEAM_RESULTS); for (final TeamRequestedResult requestedResult : requestedResults) { final TeamResultType teamResultType = requestedResult.getTeamResultType(); final String folderName = teamResultType.getFolderName(); logger.print(" " + teamResultType.getDisplayName()); final File resultFolder = new File(resultsRoot, folderName); Files.createDirectories(resultFolder.toPath()); final String includes = requestedResult.getIncludes(); final FilePath resultPath = new FilePath(resultFolder); final int numCopied = workspace.copyRecursiveTo(includes, resultPath); logger.print(" (" + numCopied + " file" + ((numCopied == 1) ? "" : "s") + ")"); } logger.print(". Compressing..."); final ArchiverFactory zip = ArchiverFactory.ZIP; final File resultsZipFile = new File(rootDir, TEAM_RESULTS_ZIP); final FileOutputStream outputStream = new FileOutputStream(resultsZipFile); try { final Archiver archiver = zip.create(outputStream); try { final DirScanner scanner = new DirScanner.Glob(TEAM_RESULTS + "/**", null, false); scanner.scan(rootDir, archiver); } finally { IOUtils.closeQuietly(archiver); } } finally { IOUtils.closeQuietly(outputStream); } FileUtils.deleteDirectory(resultsRoot); TeamResultsAction.addToRun(run); logger.println(" Done."); } @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) super.getDescriptor(); } @Override public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.NONE; } @Extension public static class DescriptorImpl extends BuildStepDescriptor { @Override public boolean isApplicable(final Class jobType) { return true; } @Override public String getDisplayName() { return "Collect results for TFS/Team Services"; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamCollectionConfiguration.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs; import com.cloudbees.plugins.credentials.Credentials; import com.cloudbees.plugins.credentials.CredentialsMatcher; import com.cloudbees.plugins.credentials.CredentialsMatchers; import com.cloudbees.plugins.credentials.CredentialsProvider; import com.cloudbees.plugins.credentials.CredentialsScope; import com.cloudbees.plugins.credentials.SystemCredentialsProvider; import com.cloudbees.plugins.credentials.common.StandardListBoxModel; import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; import com.cloudbees.plugins.credentials.domains.Domain; import com.cloudbees.plugins.credentials.domains.DomainRequirement; import com.cloudbees.plugins.credentials.domains.DomainSpecification; import com.cloudbees.plugins.credentials.domains.HostnameRequirement; import com.cloudbees.plugins.credentials.domains.HostnameSpecification; import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl; import com.microsoft.tfs.core.exceptions.TFSUnauthorizedException; import hudson.Extension; import hudson.model.AbstractDescribableImpl; import hudson.model.Descriptor; import hudson.model.Item; import hudson.model.ItemGroup; import hudson.plugins.tfs.model.ConnectionParameters; import hudson.plugins.tfs.model.ListOfGitRepositories; import hudson.plugins.tfs.model.MockableVersionControlClient; import hudson.plugins.tfs.model.Server; import hudson.plugins.tfs.util.StringHelper; import hudson.plugins.tfs.util.TeamRestClient; import hudson.plugins.tfs.util.UriHelper; import hudson.security.ACL; import hudson.util.FormValidation; import hudson.util.ListBoxModel; import jenkins.model.Jenkins; import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; public class TeamCollectionConfiguration extends AbstractDescribableImpl { private static final Logger LOGGER = Logger.getLogger(TeamCollectionConfiguration.class.getName()); private final String collectionUrl; private final String credentialsId; private ConnectionParameters connectionParameters; @DataBoundConstructor public TeamCollectionConfiguration(final String collectionUrl, final String credentialsId) { this.collectionUrl = collectionUrl; this.credentialsId = credentialsId; } public String getCollectionUrl() { return collectionUrl; } public String getCredentialsId() { return credentialsId; } public ConnectionParameters getConnectionParameters() { if (connectionParameters == null) { connectionParameters = new ConnectionParameters(); } return connectionParameters; } @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) super.getDescriptor(); } @Extension public static class DescriptorImpl extends Descriptor { @Override public String getDisplayName() { return "Team Project Collection"; } @SuppressWarnings("unused") public FormValidation doCheckCollectionUrl( @QueryParameter final String value) { if (StringUtils.isBlank(value)) { return FormValidation.warning("Please provide a value"); } final URI uri; try { uri = new URI(value); } catch (final URISyntaxException e) { return FormValidation.error("Malformed TFS/Team Services collection URL (%s)", e.getMessage()); } final String hostName = uri.getHost(); if (StringUtils.isBlank(hostName)) { return FormValidation.error("Please provide a host name"); } if (isTeamServices(hostName)) { return checkTeamServices(uri); } // TODO: check that it's not a deep URL to a repository, work item, API endpoint, etc. return FormValidation.ok(); } @SuppressWarnings("unused") public FormValidation doTestCredentials( @QueryParameter final String collectionUrl, @QueryParameter final String credentialsId) { final String errorTemplate = "Error: %s"; String hostName = null; try { final URI uri = new URI(collectionUrl); hostName = uri.getHost(); } catch (final URISyntaxException e) { return FormValidation.error(errorTemplate, e.getMessage()); } try { final StandardUsernamePasswordCredentials credential = findCredentialsById(credentialsId); if (isTeamServices(hostName)) { if (credential == null) { return FormValidation.error(errorTemplate, "Team Services accounts need credentials, preferably a Personal Access Token"); } } return testConnection(collectionUrl, credential); } catch (final IOException e) { return FormValidation.error(e, errorTemplate, e.getMessage()); } } @SuppressWarnings("unused") public ListBoxModel doFillCredentialsIdItems( @QueryParameter final String collectionUrl) { final Jenkins jenkins = Jenkins.getInstance(); String hostName = null; try { final URI uri = new URI(collectionUrl); hostName = uri.getHost(); } catch (final URISyntaxException ignored) { } if (hostName == null || !jenkins.hasPermission(Jenkins.ADMINISTER)) { return new ListBoxModel(); } final List matches = findCredentials(hostName); return new StandardListBoxModel() .withEmptySelection() .withAll(matches); } } static FormValidation checkTeamServices(final URI uri) { if (UriHelper.hasPath(uri)) { return FormValidation.error("A Team Services collection URL must have an empty path."); } return FormValidation.ok(); } static boolean areSameCollectionUri(final URI a, final URI b) { if (a == null) { throw new IllegalArgumentException("Parameter 'a' is null"); } if (b == null) { throw new IllegalArgumentException("Parameter 'b' is null"); } final String aHost = a.getHost(); final String bHost = b.getHost(); if (isTeamServices(aHost) && isTeamServices(bHost)) { return StringHelper.equalIgnoringCase(aHost, bHost); } return UriHelper.areSame(a, b); } public static boolean isTeamServices(final String hostName) { return StringHelper.endsWithIgnoreCase(hostName, ".visualstudio.com"); } static FormValidation testConnection(final String collectionUri, final StandardUsernamePasswordCredentials credentials) throws IOException { final Server server = Server.create(null, null, collectionUri, credentials, null, null); try { final MockableVersionControlClient vcc = server.getVersionControlClient(); return FormValidation.ok("Success via SOAP API."); } catch (final TFSUnauthorizedException e) { // performing TFVC requires All Scopes and someone might be setting up for Git only; ignore } final TeamRestClient client = new TeamRestClient(collectionUri, credentials); try { final ListOfGitRepositories repositories = client.getRepositories(); if (repositories.count < 1) { return FormValidation.warning("There does not seem to be any Git repositories"); } return FormValidation.ok("Success via REST API."); } catch (final IOException e) { return FormValidation.error("Error: " + e.getMessage()); } } static StandardUsernamePasswordCredentials findCredential(final String hostName, final String credentialsId) { final List matches = findCredentials(hostName); final CredentialsMatcher matcher = CredentialsMatchers.withId(credentialsId); final StandardUsernamePasswordCredentials result = CredentialsMatchers.firstOrNull(matches, matcher); return result; } public static List findCredentials(final String hostName) { final Jenkins jenkins = Jenkins.getInstance(); return findCredentials(hostName, jenkins); } public static List findCredentials(final String hostName, ItemGroup own) { final HostnameRequirement requirement = new HostnameRequirement(hostName); final List matches = CredentialsProvider.lookupCredentials( StandardUsernamePasswordCredentials.class, own, ACL.SYSTEM, requirement ); return matches; } public static List findCredentials(final String hostName, Item own) { final HostnameRequirement requirement = new HostnameRequirement(hostName); final List matches = CredentialsProvider.lookupCredentials( StandardUsernamePasswordCredentials.class, own, ACL.SYSTEM, requirement ); return matches; } public static StandardUsernamePasswordCredentials findCredentialsById(final String credentialsId) { final Jenkins jenkins = Jenkins.getInstance(); final List matches = CredentialsProvider.lookupCredentials( StandardUsernamePasswordCredentials.class, jenkins, ACL.SYSTEM, Collections.emptyList() ); final CredentialsMatcher matcher = CredentialsMatchers.withId(credentialsId); final StandardUsernamePasswordCredentials result = CredentialsMatchers.firstOrNull(matches, matcher); return result; } public static String setCredentials(final String hostName, String username, String password) { List domainSpecifications = new ArrayList<>(); domainSpecifications.add(new HostnameSpecification(hostName, null)); Domain domain = new Domain("Generated for " + hostName, "", domainSpecifications); SystemCredentialsProvider.getInstance().getDomainCredentialsMap().put(domain, new ArrayList()); String credentialsId; StandardUsernamePasswordCredentials newCredential = new UsernamePasswordCredentialsImpl( CredentialsScope.GLOBAL, credentialsId = UUID.randomUUID().toString(), "Generated for " + username, username, password ); SystemCredentialsProvider.getInstance().getDomainCredentialsMap().get(domain).add(newCredential); try { SystemCredentialsProvider.getInstance().save(); } catch (IOException ex) { LOGGER.log(Level.WARNING, "SystemCredentialsProvider instance save failed: ", ex); } return credentialsId; } // TODO: we'll probably also want findCredentialsForGitRepo, where we match part of the URL path public static StandardUsernamePasswordCredentials findCredentialsForCollection(final URI collectionUri) { final TeamPluginGlobalConfig config = TeamPluginGlobalConfig.get(); // TODO: consider using a different data structure to speed up this look-up final List pairs = config.getCollectionConfigurations(); for (final TeamCollectionConfiguration pair : pairs) { final String candidateCollectionUrlString = pair.getCollectionUrl(); final URI candidateCollectionUri = URI.create(candidateCollectionUrlString); if (areSameCollectionUri(candidateCollectionUri, collectionUri)) { final String credentialsId = pair.credentialsId; if (credentialsId != null) { return findCredentialsById(credentialsId); } return null; } } final String template = "There is no team project collection configured for the URL '%1$s'.%n" + "Please go to Jenkins > Manage Jenkins > Configure System and then " + "add a Team Project Collection with a Collection URL of '%1$s'."; final String message = String.format(template, collectionUri); throw new IllegalArgumentException(message); } public static TeamCollectionConfiguration findCollection(final URI collectionUri) { final TeamPluginGlobalConfig config = TeamPluginGlobalConfig.get(); // TODO: consider using a different data structure to speed up this look-up final List pairs = config.getCollectionConfigurations(); for (final TeamCollectionConfiguration pair : pairs) { final String candidateCollectionUrlString = pair.getCollectionUrl(); final URI candidateCollectionUri = URI.create(candidateCollectionUrlString); if (areSameCollectionUri(candidateCollectionUri, collectionUri)) { return pair; } } return null; } public static List getConnectedCollections() { final List connectedCollections = new ArrayList<>(); final TeamPluginGlobalConfig config = TeamPluginGlobalConfig.get(); final List collections = config.getCollectionConfigurations(); for (final TeamCollectionConfiguration c : collections) { if (c.getConnectionParameters().isSendJobCompletionEvents() && StringUtils.isNotEmpty(c.getConnectionParameters().getTeamCollectionUrl())) { connectedCollections.add(c); } } return connectedCollections; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamCompletedStatusPostBuildAction.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs; import hudson.Extension; import hudson.FilePath; import hudson.Launcher; import hudson.model.AbstractProject; import hudson.model.Descriptor; import hudson.model.Run; import hudson.model.TaskListener; import hudson.plugins.tfs.util.TeamStatus; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.BuildStepMonitor; import hudson.tasks.Builder; import hudson.tasks.Notifier; import hudson.tasks.Publisher; import jenkins.tasks.SimpleBuildStep; import org.kohsuke.stapler.DataBoundConstructor; import javax.annotation.Nonnull; import java.io.IOException; /** * A _Post-Build Action_ that reports the completion status of an associated build to TFS/Team Services. */ public class TeamCompletedStatusPostBuildAction extends Notifier implements SimpleBuildStep { @DataBoundConstructor public TeamCompletedStatusPostBuildAction() { } @Override public void perform( @Nonnull final Run run, @Nonnull final FilePath workspace, @Nonnull final Launcher launcher, @Nonnull final TaskListener listener ) throws InterruptedException, IOException { if (!TeamGlobalStatusAction.isApplicable(run)){ perform(run, listener); } } public void perform(final @Nonnull Run run, final @Nonnull TaskListener listener) { try { TeamStatus.createFromRun(run, listener, getDisplayName()); } catch (final IllegalArgumentException e) { listener.error(e.getMessage()); } catch (final Exception e) { e.printStackTrace(listener.error("Error while trying to update completion status in TFS/Team Services")); } } String getDisplayName() { final Descriptor descriptor = getDescriptor(); return descriptor.getDisplayName(); } @Override public BuildStepMonitor getRequiredMonitorService() { // we don't need the outcome of any previous builds for this step return BuildStepMonitor.NONE; } @Extension public static class DescriptorImpl extends BuildStepDescriptor { @Override public boolean isApplicable(final Class jobType) { return true; } @Override public String getDisplayName() { return "Set build completion status in TFS/Team Services"; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamEventsEndpoint.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs; import hudson.Extension; import hudson.model.Item; import hudson.model.Job; import hudson.model.UnprotectedRootAction; import hudson.plugins.git.GitStatus; import hudson.plugins.tfs.model.AbstractHookEvent; import hudson.plugins.tfs.model.ConnectHookEvent; import hudson.plugins.tfs.model.GitPullRequestMergedEvent; import hudson.plugins.tfs.model.GitPushEvent; import hudson.plugins.tfs.model.PingHookEvent; import hudson.plugins.tfs.model.servicehooks.Event; import hudson.plugins.tfs.rm.ConnectReleaseWebHookEvent; import hudson.plugins.tfs.telemetry.TelemetryHelper; import hudson.plugins.tfs.util.EndpointHelper; import hudson.plugins.tfs.util.MediaType; import hudson.plugins.tfs.util.StringBodyParameter; import hudson.triggers.Trigger; import jenkins.model.Jenkins; import jenkins.model.ParameterizedJobMixIn; import net.sf.json.JSONObject; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.HttpResponses; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.interceptor.RequirePOST; import javax.annotation.Nonnull; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import static javax.servlet.http.HttpServletResponse.SC_OK; /** * The endpoint that TFS/Team Services will POST to on Git code push, pull request merge commit creation, etc. */ @Extension public class TeamEventsEndpoint implements UnprotectedRootAction { private static final Logger LOGGER = Logger.getLogger(TeamEventsEndpoint.class.getName()); private static final Map HOOK_EVENT_FACTORIES_BY_NAME; static { final Map eventMap = new TreeMap(String.CASE_INSENSITIVE_ORDER); eventMap.put("ping", new PingHookEvent.Factory()); eventMap.put("gitPullRequestMerged", new GitPullRequestMergedEvent.Factory()); eventMap.put("gitPush", new GitPushEvent.Factory()); eventMap.put("connect", new ConnectHookEvent.Factory()); eventMap.put("rmWebhook", new ConnectReleaseWebHookEvent.Factory()); HOOK_EVENT_FACTORIES_BY_NAME = Collections.unmodifiableMap(eventMap); } public static final String URL_NAME = "team-events"; static final String URL_PREFIX = "/" + URL_NAME + "/"; @Override public String getIconFileName() { return null; } @Override public String getDisplayName() { return null; } @Override public String getUrlName() { return URL_NAME; } public HttpResponse doIndex(final HttpServletRequest request) throws IOException { final Class me = this.getClass(); final InputStream stream = me.getResourceAsStream("TeamEventsEndpoint.html"); final Jenkins instance = Jenkins.getActiveInstance(); final String rootUrl = instance.getRootUrl(); final String eventRows = describeEvents(HOOK_EVENT_FACTORIES_BY_NAME, URL_NAME); try { final String template = IOUtils.toString(stream, MediaType.UTF_8); final String content = String.format(template, URL_NAME, eventRows, rootUrl); return HttpResponses.html(content); } finally { IOUtils.closeQuietly(stream); } } static String describeEvents(final Map eventMap, final String urlName) { final String newLine = System.getProperty("line.separator"); final StringBuilder sb = new StringBuilder(); for (final Map.Entry eventPair : eventMap.entrySet()) { final String eventName = eventPair.getKey(); final AbstractHookEvent.Factory factory = eventPair.getValue(); sb.append("").append(newLine); sb.append("").append(eventName).append("").append(newLine); sb.append("").append('/').append(urlName).append('/').append(eventName).append("").append(newLine); final String rawSample = factory.getSampleRequestPayload(); final String escapedSample = StringEscapeUtils.escapeHtml4(rawSample); sb.append("
").append(escapedSample).append("
").append(newLine); sb.append("").append(newLine); } return sb.toString(); } static String pathInfoToEventName(final String pathInfo) { if (pathInfo.startsWith(URL_PREFIX)) { final String restOfPath = pathInfo.substring(URL_PREFIX.length()); final int firstSlash = restOfPath.indexOf('/'); final String eventName; if (firstSlash != -1) { eventName = restOfPath.substring(0, firstSlash); } else { eventName = restOfPath; } return eventName; } return null; } void dispatch(final StaplerRequest request, final StaplerResponse rsp, final String body) { final String pathInfo = request.getPathInfo(); final String eventName = pathInfoToEventName(pathInfo); try { final JSONObject response = innerDispatch(body, eventName, HOOK_EVENT_FACTORIES_BY_NAME); rsp.setStatus(SC_OK); rsp.setContentType(MediaType.APPLICATION_JSON_UTF_8); final PrintWriter w = rsp.getWriter(); final String responseJsonString = response.toString(); w.print(responseJsonString); w.println(); } catch (final IllegalArgumentException e) { LOGGER.log(Level.WARNING, "IllegalArgumentException", e); EndpointHelper.error(SC_BAD_REQUEST, e); } catch (final Exception e) { final String template = "Error while performing reaction to '%s' event."; final String message = String.format(template, eventName); LOGGER.log(Level.SEVERE, message, e); EndpointHelper.error(SC_INTERNAL_SERVER_ERROR, e); } } static JSONObject innerDispatch(final String body, final String eventName, final Map factoriesByName) throws IOException { if (StringUtils.isBlank(eventName) || !factoriesByName.containsKey(eventName)) { throw new IllegalArgumentException("Invalid event"); } final AbstractHookEvent.Factory factory = factoriesByName.get(eventName); final Event serviceHookEvent = deserializeEvent(body); final String message = serviceHookEvent.getMessage() != null ? serviceHookEvent.getMessage().getText() : ""; final String detailedMessage = serviceHookEvent.getDetailedMessage() != null ? serviceHookEvent.getDetailedMessage().getText() : ""; final AbstractHookEvent hookEvent = factory.create(); return hookEvent.perform(EndpointHelper.MAPPER, serviceHookEvent, message, detailedMessage); } public static Event deserializeEvent(final String input) throws IOException { final Event serviceHookEvent = EndpointHelper.MAPPER.readValue(input, Event.class); final String eventType = serviceHookEvent.getEventType(); if (StringUtils.isEmpty(eventType)) { throw new IllegalArgumentException("Payload did not contain 'eventType'."); } // TODO: assert eventType with what Factory claims to support final Object resource = serviceHookEvent.getResource(); if (resource == null) { throw new IllegalArgumentException("Payload did not contain 'resource'."); } return serviceHookEvent; } @RequirePOST public void doPing( final StaplerRequest request, final StaplerResponse response, @StringBodyParameter @Nonnull final String body) { dispatch(request, response, body); } @RequirePOST public void doGitPullRequestMerged( final StaplerRequest request, final StaplerResponse response, @StringBodyParameter @Nonnull final String body) { // Send telemetry TelemetryHelper.sendEvent("team-events-git-pr-merged", new TelemetryHelper.PropertyMapBuilder() .build()); dispatch(request, response, body); } @RequirePOST public void doGitPush( final StaplerRequest request, final StaplerResponse response, @StringBodyParameter @Nonnull final String body) { // Send telemetry TelemetryHelper.sendEvent("team-events-git-push", new TelemetryHelper.PropertyMapBuilder() .build()); dispatch(request, response, body); } @RequirePOST public void doConnect( final StaplerRequest request, final StaplerResponse response, @StringBodyParameter @Nonnull final String body) { // Send telemetry TelemetryHelper.sendEvent("team-events-connect", new TelemetryHelper.PropertyMapBuilder() .build()); dispatch(request, response, body); } @RequirePOST public void doRmwebhook( final StaplerRequest request, final StaplerResponse response, @StringBodyParameter @Nonnull final String body) { // Send telemetry TelemetryHelper.sendEvent("team-events-rmwebhook", new TelemetryHelper.PropertyMapBuilder().build()); dispatch(request, response, body); } public static T findTrigger(final Job job, final Class tClass) { if (job instanceof ParameterizedJobMixIn.ParameterizedJob) { final ParameterizedJobMixIn.ParameterizedJob pJob = (ParameterizedJobMixIn.ParameterizedJob) job; for (final Object trigger : pJob.getTriggers().values()) { if (tClass.isInstance(trigger)) { return tClass.cast(trigger); } } } return null; } // A job may have multiple triggers of the same type. For example, both TeamPRPushTrigger and TeamPushTrigger are TeamPushTrigger type. public static List findTriggers(final Job job, final Class tClass) { List triggerList = new ArrayList<>(); if (job instanceof ParameterizedJobMixIn.ParameterizedJob) { final ParameterizedJobMixIn.ParameterizedJob pJob = (ParameterizedJobMixIn.ParameterizedJob) job; for (final Object trigger : pJob.getTriggers().values()) { if (tClass.isInstance(trigger)) { triggerList.add(tClass.cast(trigger)); } } } return triggerList; } /** * A response contributor for triggering polling of a project. * * @since 1.4.1 */ public static class PollingScheduledResponseContributor extends GitStatus.ResponseContributor { /** * The project */ private final Item project; /** * Constructor. * * @param project the project. */ public PollingScheduledResponseContributor(Item project) { this.project = project; } /** * {@inheritDoc} */ @Override public void addHeaders(StaplerRequest req, StaplerResponse rsp) { rsp.addHeader("Triggered", project.getAbsoluteUrl()); } /** * {@inheritDoc} */ @Override public void writeBody(PrintWriter w) { w.println("Scheduled polling of " + project.getFullDisplayName()); } } public static class ScheduledResponseContributor extends GitStatus.ResponseContributor { /** * The project */ private final Item project; /** * Constructor. * * @param project the project. */ public ScheduledResponseContributor(Item project) { this.project = project; } /** * {@inheritDoc} */ @Override public void addHeaders(StaplerRequest req, StaplerResponse rsp) { rsp.addHeader("Triggered", project.getAbsoluteUrl()); } /** * {@inheritDoc} */ @Override public void writeBody(PrintWriter w) { w.println("Scheduled " + project.getFullDisplayName()); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamFoundationServerScm.java ================================================ package hudson.plugins.tfs; import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.ChangesetVersionSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.DateVersionSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.VersionSpec; import hudson.Extension; import hudson.FilePath; import hudson.Launcher; import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.Job; import hudson.model.Computer; import hudson.model.Node; import hudson.model.ParametersAction; import hudson.model.Run; import hudson.model.TaskListener; import hudson.plugins.tfs.actions.CheckoutAction; import hudson.plugins.tfs.actions.RemoveWorkspaceAction; import hudson.plugins.tfs.browsers.TeamFoundationServerRepositoryBrowser; import hudson.plugins.tfs.browsers.TeamSystemWebAccessBrowser; import hudson.plugins.tfs.model.ChangeSet; import hudson.plugins.tfs.model.CredentialsConfigurer; import hudson.plugins.tfs.model.CredentialsConfigurerDescriptor; import hudson.plugins.tfs.model.ManualCredentialsConfigurer; import hudson.plugins.tfs.model.Project; import hudson.plugins.tfs.model.Server; import hudson.plugins.tfs.model.WorkspaceConfiguration; import hudson.plugins.tfs.util.BuildVariableResolver; import hudson.plugins.tfs.util.BuildWorkspaceConfigurationRetriever; import hudson.plugins.tfs.util.BuildWorkspaceConfigurationRetriever.BuildWorkspaceConfiguration; import hudson.scm.ChangeLogParser; import hudson.scm.PollingResult; import hudson.scm.PollingResult.Change; import hudson.scm.RepositoryBrowser; import hudson.scm.RepositoryBrowsers; import hudson.scm.SCM; import hudson.scm.SCMDescriptor; import hudson.scm.SCMRevisionState; import hudson.util.ComboBoxModel; import hudson.util.FormValidation; import hudson.util.LogTaskListener; import hudson.util.Scrambler; import hudson.util.Secret; import hudson.util.VariableResolver; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import javax.annotation.CheckForNull; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; import static hudson.Util.fixEmpty; /** * SCM for Microsoft Team Foundation Server. * * @author Erik Ramfelt */ public class TeamFoundationServerScm extends SCM { public static final String WORKSPACE_ENV_STR = "TFS_WORKSPACE"; public static final String WORKFOLDER_ENV_STR = "TFS_WORKFOLDER"; public static final String PROJECTPATH_ENV_STR = "TFS_PROJECTPATH"; public static final String SERVERURL_ENV_STR = "TFS_SERVERURL"; public static final String USERNAME_ENV_STR = "TFS_USERNAME"; public static final String WORKSPACE_CHANGESET_ENV_STR = "TFS_CHANGESET"; private static final String VERSION_SPEC = "VERSION_SPEC"; private static final int RADIX_10 = 10; private final String serverUrl; private final String projectPath; private Collection cloakedPaths; private String localPath; private final String workspaceName; @Deprecated private String userPassword; private Secret password; private String userName; private CredentialsConfigurer credentialsConfigurer; private boolean useUpdate; private boolean useOverwrite; private String versionSpec; private TeamFoundationServerRepositoryBrowser repositoryBrowser; private transient String normalizedWorkspaceName; private transient String workspaceChangesetVersion; private static final Logger logger = Logger.getLogger(TeamFoundationServerScm.class.getName()); /** * Constructor used for unit tests. * * @param serverUrl the URL to the team project collection * @param projectPath the path in TFVC to download from * @param workspaceName the name (or expression) to use when mapping the workspace */ TeamFoundationServerScm(final String serverUrl, final String projectPath, final String workspaceName) { this(serverUrl, projectPath, workspaceName, null, null); } /** * Constructor used during serialization (and a few tests). * * WARNING: do NOT add parameters to this constructor when adding fields for new settings. * Instead, add a setter annotated with {@link DataBoundSetter} in the "Bean properties" section. * See {@link #setLocalPath(String)} for an example. * * @param serverUrl the URL to the team project collection * @param projectPath the path in TFVC to download from * @param workspaceName the name (or expression) to use when mapping the workspace * @param userName the name of the user account to use to talk to TFS/Team Services * @param password the password or personal access token to use to talk to TFS/Team Services */ @DataBoundConstructor public TeamFoundationServerScm(final String serverUrl, final String projectPath, final String workspaceName, final String userName, final Secret password) { this.serverUrl = serverUrl; this.projectPath = projectPath; this.workspaceName = (Util.fixEmptyAndTrim(workspaceName) == null ? "Hudson-${JOB_NAME}-${NODE_NAME}" : workspaceName); this.userName = userName; this.password = password; } @SuppressWarnings("unused" /* Migrate legacy data */) private Object readResolve() { if (password == null && userPassword != null) { password = Secret.fromString(Scrambler.descramble(userPassword)); userPassword = null; } if (userName != null && password != null) { credentialsConfigurer = new ManualCredentialsConfigurer(userName, password); } return this; } // Bean properties needed for job configuration public String getServerUrl() { return serverUrl; } public String getWorkspaceName() { return workspaceName; } public String getProjectPath() { return projectPath; } public String getLocalPath() { return (Util.fixEmptyAndTrim(localPath) == null ? "." : localPath); } @DataBoundSetter public void setLocalPath(final String localPath) { this.localPath = localPath; } public String getVersionSpec() { return versionSpec; } @DataBoundSetter public void setVersionSpec(final String versionSpec) { this.versionSpec = versionSpec; } public boolean isUseUpdate() { return useUpdate; } @DataBoundSetter public void setUseUpdate(final boolean useUpdate) { this.useUpdate = useUpdate; } public boolean isUseOverwrite() { return useOverwrite; } @DataBoundSetter public void setUseOverwrite(final boolean useOverwrite) { this.useOverwrite = useOverwrite; } public String getUserPassword() { return Secret.toString(password); } public Secret getPassword() { return password; } public String getUserName() { return userName; } /** * * @return a ManualCredentialsConfigurer object with the supplied userName/password */ public CredentialsConfigurer getCredentialsConfigurer() { if (credentialsConfigurer == null) { credentialsConfigurer = new ManualCredentialsConfigurer(userName, password); } return credentialsConfigurer; } @DataBoundSetter public void setCredentialsConfigurer(final CredentialsConfigurer credentialsConfigurer) { this.credentialsConfigurer = credentialsConfigurer; } public String getCloakedPaths() { return serializeCloakedPathCollectionToString(this.cloakedPaths); } @DataBoundSetter public void setCloakedPaths(final String cloakedPaths) { this.cloakedPaths = splitCloakedPaths(cloakedPaths); } // Bean properties END static String serializeCloakedPathCollectionToString(final Collection cloakedPaths) { return cloakedPaths == null ? StringUtils.EMPTY : StringUtils.join(cloakedPaths, "\n"); } String getWorkspaceName(final Run build, final Computer computer) { normalizedWorkspaceName = workspaceName; if (build != null) { normalizedWorkspaceName = substituteBuildParameter(build, normalizedWorkspaceName); normalizedWorkspaceName = Util.replaceMacro(normalizedWorkspaceName, new BuildVariableResolver(build.getParent(), computer)); } normalizedWorkspaceName = normalizedWorkspaceName.replaceAll("[\"/:<>\\|\\*\\?]+", "_"); normalizedWorkspaceName = normalizedWorkspaceName.replaceAll("[\\.\\s]+$", "_"); return normalizedWorkspaceName; } /** * Returns the URL to the team project collection. */ public String getServerUrl(final Run run) { return substituteBuildParameter(run, serverUrl); } String getProjectPath(final Run run) { return Util.replaceMacro(substituteBuildParameter(run, projectPath), new BuildVariableResolver(run.getParent())); } Collection getCloakedPaths(final Run run) { final List paths = new ArrayList(); if (cloakedPaths != null) { final BuildVariableResolver resolver = new BuildVariableResolver(run.getParent()); for (final String cloakedPath : cloakedPaths) { final String path = substituteBuildParameter(run, cloakedPath); final String enhancedPath = Util.replaceMacro(path, resolver); paths.add(enhancedPath); } } return paths; } private String substituteBuildParameter(final Run run, final String text) { if (run instanceof AbstractBuild) { AbstractBuild build = (AbstractBuild) run; if (build.getAction(ParametersAction.class) != null) { return build.getAction(ParametersAction.class).substitute(build, text); } } return text; } static Collection splitCloakedPaths(final String cloakedPaths) { final List cloakedPathsList = new ArrayList(); if (cloakedPaths != null && cloakedPaths.length() > 0) { final StringBuilder cloakedPath = new StringBuilder(cloakedPaths.length()); for (final char character : cloakedPaths.toCharArray()) { switch (character) { case '\n': if (cloakedPath.length() > 0) { cloakedPathsList.add(cloakedPath.toString().trim()); cloakedPath.setLength(0); } break; default: cloakedPath.append(character); break; } } if (cloakedPath.length() > 0) { cloakedPathsList.add(cloakedPath.toString().trim()); } } return cloakedPathsList; } @Override public void checkout(final Run build, final Launcher launcher, final FilePath workspaceFilePath, final TaskListener listener, final File changelogFile, final SCMRevisionState baseline) throws IOException, InterruptedException { Server server = createServer(launcher, listener, build); try { WorkspaceConfiguration workspaceConfiguration = new WorkspaceConfiguration(server.getUrl(), getWorkspaceName(build, workspaceFilePath.toComputer()), getProjectPath(build), getCloakedPaths(build), getLocalPath()); final Run previousBuild = build.getPreviousBuild(); // Check if the configuration has changed if (previousBuild != null) { Computer computer = workspaceFilePath.toComputer(); if (computer != null) { BuildWorkspaceConfiguration nodeConfiguration = new BuildWorkspaceConfigurationRetriever().getLatestForNode(computer.getNode(), previousBuild); if ((nodeConfiguration != null) && nodeConfiguration.workspaceExists() && (!workspaceConfiguration.equals(nodeConfiguration))) { listener.getLogger().println("Deleting workspace as the configuration has changed since a build was performed on this computer."); new RemoveWorkspaceAction(workspaceConfiguration.getWorkspaceName()).remove(server); nodeConfiguration.setWorkspaceWasRemoved(); nodeConfiguration.save(); } } } build.addAction(workspaceConfiguration); String singleVersionSpec = versionSpec; if (build instanceof AbstractBuild) { VariableResolver buildVariableResolver = ((AbstractBuild) build).getBuildVariableResolver(); singleVersionSpec = buildVariableResolver.resolve(VERSION_SPEC); } final String projPath = workspaceConfiguration.getProjectPath(); final Project project = server.getProject(projPath); final int changeSet = recordWorkspaceChangesetVersion(build, listener, project, projPath, singleVersionSpec); CheckoutAction action = new CheckoutAction(workspaceConfiguration.getWorkspaceName(), workspaceConfiguration.getProjectPath(), workspaceConfiguration.getCloakedPaths(), workspaceConfiguration.getWorkfolder(), isUseUpdate(), isUseOverwrite()); List list; if (StringUtils.isNotEmpty(singleVersionSpec)) { list = action.checkoutBySingleVersionSpec(server, workspaceFilePath, singleVersionSpec); } else { final VersionSpec previousBuildVersionSpec = determineVersionSpecFromBuild(previousBuild, 1, changeSet); final ChangesetVersionSpec currentBuildVersionSpec = new ChangesetVersionSpec(changeSet); list = action.checkout(server, workspaceFilePath, previousBuildVersionSpec, currentBuildVersionSpec); } if (changelogFile != null) { ChangeSetWriter writer = new ChangeSetWriter(); writer.write(list, changelogFile); } } finally { server.close(); } } static VersionSpec determineVersionSpecFromBuild(final Run build, final int offset, final int maximumChangeSetNumber) { final VersionSpec result; if (build != null) { final TFSRevisionState revisionState = build.getAction(TFSRevisionState.class); if (revisionState != null) { final int changeSetNumber = revisionState.changesetVersion + offset; if (changeSetNumber <= maximumChangeSetNumber) { result = new ChangesetVersionSpec(changeSetNumber); } else { result = null; } } else { result = null; } } else { result = null; } return result; } int recordWorkspaceChangesetVersion(final Run build, final TaskListener listener, final Project project, final String projectPath, final String singleVersionSpec) throws IOException, InterruptedException { final VersionSpec workspaceVersion; if (!StringUtils.isEmpty(singleVersionSpec)) { workspaceVersion = VersionSpec.parseSingleVersionFromSpec(singleVersionSpec, null); } else { workspaceVersion = new DateVersionSpec(build.getTimestamp()); } int buildChangeset; setWorkspaceChangesetVersion(null); buildChangeset = project.getRemoteChangesetVersion(workspaceVersion); setWorkspaceChangesetVersion(Integer.toString(buildChangeset, RADIX_10)); // by adding this action, we prevent calcRevisionsFromBuild() from being called build.addAction(new TFSRevisionState(buildChangeset, projectPath)); return buildChangeset; } void setWorkspaceChangesetVersion(final String workspaceChangesetVersion) { this.workspaceChangesetVersion = workspaceChangesetVersion; } @Override public boolean pollChanges(final AbstractProject hudsonProject, final Launcher launcher, final FilePath workspace, final TaskListener listener) throws IOException, InterruptedException { Run lastRun = hudsonProject.getLastBuild(); if (lastRun == null) { return true; } else { Server server = createServer(launcher, listener, lastRun); try { return (server.getProject(getProjectPath(lastRun)).getDetailedHistoryWithoutCloakedPaths( lastRun.getTimestamp(), Calendar.getInstance(), getCloakedPaths(lastRun) ).size() > 0); } finally { server.close(); } } } @Override public boolean processWorkspaceBeforeDeletion(final Job project, final FilePath workspace, final Node node) throws IOException, InterruptedException { Node nodeIter = node; Run lastRun = project.getLastBuild(); if (lastRun == null) { return true; } // Due to an error in Hudson core (pre 1.321), null was sent in for all invocations of this method // Therefore we try to work around the problem, and see if its only built on one node or not. if (nodeIter == null) { while (lastRun != null) { if (!(lastRun instanceof AbstractBuild)) { return false; } AbstractBuild build = (AbstractBuild) lastRun; Node buildNode = build.getBuiltOn(); if (nodeIter == null) { nodeIter = buildNode; } else { if (!buildNode.getNodeName().equals(nodeIter.getNodeName())) { logger.warning("Could not wipe out workspace as there is no way of telling what Node the request is for. Please upgrade Hudson to a newer version."); return false; } } lastRun = lastRun.getPreviousBuild(); } if (nodeIter == null) { return true; } lastRun = project.getLastBuild(); } BuildWorkspaceConfiguration configuration = new BuildWorkspaceConfigurationRetriever().getLatestForNode(nodeIter, lastRun); if ((configuration != null) && configuration.workspaceExists()) { LogTaskListener listener = new LogTaskListener(logger, Level.INFO); Launcher launcher = nodeIter.createLauncher(listener); Server server = createServer(launcher, listener, lastRun); try { if (new RemoveWorkspaceAction(configuration.getWorkspaceName()).remove(server)) { configuration.setWorkspaceWasRemoved(); configuration.save(); } } finally { server.close(); } } return true; } protected Server createServer(final Launcher launcher, final TaskListener taskListener, final Run run) throws IOException { final CredentialsConfigurer credentialsConfig = getCredentialsConfigurer(); final String collectionUri = getServerUrl(run); final StandardUsernamePasswordCredentials credentials = credentialsConfig.getCredentials(collectionUri); return Server.create(launcher, taskListener, collectionUri, credentials, null, null); } @Override public boolean requiresWorkspaceForPolling() { return false; } @Override public boolean supportsPolling() { return true; } @Override public ChangeLogParser createChangeLogParser() { return new ChangeSetReader(); } @Override public FilePath getModuleRoot(final FilePath workspace) { return workspace.child(getLocalPath()); } @Override public TeamFoundationServerRepositoryBrowser getBrowser() { return repositoryBrowser; } /** * * @return a new TeamSystemWebAccessBrowser even if no repository browser (value in UI is Auto) is * configured since its the only implementation that exists anyway */ @Override @CheckForNull public RepositoryBrowser guessBrowser() { return new TeamSystemWebAccessBrowser(serverUrl); } // Convenience method for tests. public void setRepositoryBrowser(final TeamFoundationServerRepositoryBrowser repositoryBrowser) { this.repositoryBrowser = repositoryBrowser; } // TODO: 2.60+ Delete this override. @Override public void buildEnvVars(final AbstractBuild build, final Map env) { buildEnvironment(build, env); } /** * TODO: 2.60+ - add @Override. * Sets the environment variables TFS_WORKSPACE, TFS_WORKFOLDER, TFS_PROJECTPATH, TFS_SERVERURL, TFS_USERNAME, TFS_CHANGESET and * VERSION_SPEC during the build */ public void buildEnvironment(final Run build, final Map env) { final TeamBuildDetailsAction buildDetailsAction = build.getAction(TeamBuildDetailsAction.class); if (buildDetailsAction != null) { //Add the TFS build variables as environment variables in the Jenkins environment //https://www.visualstudio.com/en-us/docs/build/define/variables for (Map.Entry entry : buildDetailsAction.buildVariables.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); if (value != null) { //Replace . with _ and ensure they're UPPERCASE to match Team Services pattern env.put(key.replace('.', '_').toUpperCase(), value); } } } if (normalizedWorkspaceName != null) { env.put(WORKSPACE_ENV_STR, normalizedWorkspaceName); } if (env.containsKey("WORKSPACE")) { env.put(WORKFOLDER_ENV_STR, env.get("WORKSPACE") + File.separator + getLocalPath()); } if (projectPath != null) { env.put(PROJECTPATH_ENV_STR, projectPath); } if (serverUrl != null) { env.put(SERVERURL_ENV_STR, serverUrl); } if (userName != null) { env.put(USERNAME_ENV_STR, userName); } if (workspaceChangesetVersion != null && workspaceChangesetVersion.length() > 0) { env.put(WORKSPACE_CHANGESET_ENV_STR, workspaceChangesetVersion); } } @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) super.getDescriptor(); } /** Descriptor implementation class. Used to create instances of TeamFoundationServerScm. */ @Extension public static class DescriptorImpl extends SCMDescriptor { public static final Pattern WORKSPACE_NAME_REGEX = Pattern.compile("[^\"/:<>\\|\\*\\?]+[^\\s\\.\"/:<>\\|\\*\\?]$", Pattern.CASE_INSENSITIVE); public static final Pattern PROJECT_PATH_REGEX = Pattern.compile("^\\$\\/.*", Pattern.CASE_INSENSITIVE); public static final Pattern CLOAKED_PATHS_REGEX = Pattern.compile("\\s*\\$[^\\n;]+(\\s*[\\n]\\s*\\$[^\\n;]+){0,}\\s*", Pattern.CASE_INSENSITIVE); @Override public boolean isApplicable(final Job project) { return true; } public DescriptorImpl() { super(TeamFoundationServerScm.class, TeamFoundationServerRepositoryBrowser.class); load(); } @Override public SCM newInstance(final StaplerRequest req, final JSONObject formData) throws FormException { TeamFoundationServerScm scm = (TeamFoundationServerScm) super.newInstance(req, formData); scm.repositoryBrowser = RepositoryBrowsers.createInstance(TeamFoundationServerRepositoryBrowser.class, req, formData, "browser"); // TODO: is there a more polymorphic way of doing this? if (scm.credentialsConfigurer instanceof ManualCredentialsConfigurer) { // ManualCredentialsConfigurer has its fields "transient"; transfer the values here // for backward-compatibility final ManualCredentialsConfigurer manualCredentialsConfigurer = (ManualCredentialsConfigurer) scm.credentialsConfigurer; scm.userName = manualCredentialsConfigurer.getUserName(); scm.password = manualCredentialsConfigurer.getPassword(); } return scm; } private FormValidation doRegexCheck(final Pattern[] regexArray, final String noMatchText, final String nullText, final String value) { String val = value; val = fixEmpty(val); if (val == null) { if (nullText == null) { return FormValidation.ok(); } else { return FormValidation.error(nullText); } } for (Pattern regex : regexArray) { if (regex.matcher(val).matches()) { return FormValidation.ok(); } } return FormValidation.error(noMatchText); } /** Populates comboBox with detected collections URLs. */ public ComboBoxModel doFillServerUrlItems() { final TeamPluginGlobalConfig pluginGlobalConfig = TeamPluginGlobalConfig.get(); final List collectionConfigurations = pluginGlobalConfig.getCollectionConfigurations(); final ComboBoxModel result = new ComboBoxModel(collectionConfigurations.size()); for (final TeamCollectionConfiguration collectionConfiguration : collectionConfigurations) { result.add(collectionConfiguration.getCollectionUrl()); } return result; } /** Validates Project path form field. */ public FormValidation doProjectPathCheck(@QueryParameter final String value) { return doRegexCheck(new Pattern[]{PROJECT_PATH_REGEX}, "Project path must begin with '$/'.", "Project path is mandatory.", value); } /** Validates Workspace form field. */ public FormValidation doWorkspaceNameCheck(@QueryParameter final String value) { return doRegexCheck(new Pattern[]{WORKSPACE_NAME_REGEX}, "Workspace name cannot end with a space or period, and cannot contain any of the following characters: \"/:<>|*?", "Workspace name is mandatory", value); } /** Validates Cloaked Paths form field. */ public FormValidation doCloakedPathsCheck(@QueryParameter final String value) { return doRegexCheck(new Pattern[]{CLOAKED_PATHS_REGEX}, "Each cloaked path must begin with '$/'. Multiple paths must be separated by blank lines.", null, value); } public List getCredentialsConfigurerDescriptors() { return CredentialsConfigurer.all(); } @Override public boolean configure(final StaplerRequest req, final JSONObject formData) throws FormException { save(); return true; } @Override public String getDisplayName() { return "Team Foundation Version Control (TFVC)"; } } @Override public SCMRevisionState calcRevisionsFromBuild(final Run build, final FilePath workspace, final Launcher launcher, final TaskListener listener) throws IOException, InterruptedException { /* * This method does nothing, since the work has already been done in * the checkout() method, as per the documentation: * """ * As an optimization, SCM implementation can choose to compute SCMRevisionState * and add it as an action during check out, in which case this method will not called. * """ */ return SCMRevisionState.NONE; } @Override public PollingResult compareRemoteRevisionWith( final Job project, final Launcher launcher, final FilePath workspace, final TaskListener listener, final SCMRevisionState baseline) throws IOException, InterruptedException { final Launcher localLauncher = launcher != null ? launcher : new Launcher.LocalLauncher(listener); Run build = project.getLastBuild(); final Server server = createServer(localLauncher, listener, build); if (!(baseline instanceof TFSRevisionState)) { // This plugin was just upgraded, we don't yet have a new-style baseline, // so we perform an old-school poll if (project instanceof AbstractProject) { boolean shouldBuild = pollChanges((AbstractProject) project, localLauncher, workspace, listener); return shouldBuild ? PollingResult.BUILD_NOW : PollingResult.NO_CHANGES; } else { // On the pipeline case, we can't call pollChanges. Query the TFS Server directly instead if (build == null) { return PollingResult.BUILD_NOW; } else { try { return (server.getProject(getProjectPath(build)).getDetailedHistoryWithoutCloakedPaths( build.getTimestamp(), Calendar.getInstance(), getCloakedPaths(build) ).size() > 0) ? PollingResult.BUILD_NOW : PollingResult.NO_CHANGES; } finally { server.close(); } } } } final TFSRevisionState tfsBaseline = (TFSRevisionState) baseline; if (!projectPath.equalsIgnoreCase(tfsBaseline.projectPath)) { // There's no PollingResult.INCOMPARABLE, so we use the next closest thing return PollingResult.BUILD_NOW; } final Project tfsProject = server.getProject(projectPath); try { final ChangeSet latest = tfsProject.getLatestUncloakedChangeset(tfsBaseline.changesetVersion, cloakedPaths); final TFSRevisionState tfsRemote = (latest != null) ? new TFSRevisionState(latest.getVersion(), projectPath) : tfsBaseline; // TODO: we could return INSIGNIFICANT if all the changesets // contain the string "***NO_CI***" at the end of their comment final Change change = tfsBaseline.changesetVersion == tfsRemote.changesetVersion ? Change.NONE : Change.SIGNIFICANT; return new PollingResult(tfsBaseline, tfsRemote, change); } catch (final Exception e) { e.printStackTrace(listener.fatalError(e.getMessage())); return PollingResult.NO_CHANGES; } finally { server.close(); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamGlobalStatusAction.java ================================================ package hudson.plugins.tfs; import hudson.model.Action; import hudson.model.InvisibleAction; import hudson.model.Run; import java.io.Serializable; import java.util.List; /** * Added to the build when triggered by TFS/Team Services AND the "Enable Team Status for all jobs" * option was enabled. */ public class TeamGlobalStatusAction extends InvisibleAction implements Serializable { /** * Add the passed in actions if EnableTeamStatusForAllJobs is configured. */ public static void addIfApplicable(final List actions) { final TeamPluginGlobalConfig config = TeamPluginGlobalConfig.get(); if (config.isEnableTeamStatusForAllJobs()) { actions.add(new TeamGlobalStatusAction()); } } /** * Returns true if the run already contains a TeamGlobalStatusAction. */ public static boolean isApplicable(final Run run) { final TeamGlobalStatusAction action = run.getAction(TeamGlobalStatusAction.class); return action != null; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamGlobalStatusPoster.java ================================================ package hudson.plugins.tfs; import hudson.Extension; import hudson.model.AbstractBuild; import hudson.model.TaskListener; import hudson.model.listeners.RunListener; import javax.annotation.Nonnull; /** * Posts the status to TFS/Team Services if the {@link TeamGlobalStatusAction} was contributed. */ @Extension public class TeamGlobalStatusPoster extends RunListener { @Override public void onStarted(final AbstractBuild build, final TaskListener listener) { if (TeamGlobalStatusAction.isApplicable(build)) { final TeamPendingStatusBuildStep step = new TeamPendingStatusBuildStep(); step.perform(build, listener); } } @Override public void onCompleted(final AbstractBuild build, @Nonnull final TaskListener listener) { if (TeamGlobalStatusAction.isApplicable(build)) { final TeamCompletedStatusPostBuildAction step = new TeamCompletedStatusPostBuildAction(); step.perform(build, listener); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamHookCause.java ================================================ package hudson.plugins.tfs; import hudson.plugins.git.GitStatus; /** * Attached to the build if it was started by a TFS/Team Services commit. */ public class TeamHookCause extends GitStatus.CommitHookCause { public TeamHookCause(final String sha1) { super(sha1); } @Override public String getShortDescription() { return "Started by TFS/Team Services web hook for commit " + sha1; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamPRPushTrigger.java ================================================ package hudson.plugins.tfs; import hudson.Extension; import hudson.model.Job; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; /** * Triggers a build when we receive a TFS/Team Services Git code push event in a TFS pull request. */ public class TeamPRPushTrigger extends TeamPushTrigger { private String targetBranches; @DataBoundConstructor public TeamPRPushTrigger() { } public TeamPRPushTrigger(final Job job, final String targetBranches, final String jobContext) { this.job = job; this.targetBranches = targetBranches; setJobContext(jobContext); } public String getTargetBranches() { return targetBranches; } @DataBoundSetter public void setTargetBranches(final String targetBranches) { this.targetBranches = targetBranches; } /** * This class extends trigger descriptor class from TeamPushTrigger, creating a separate check box for TeamPRPushTrigger. */ @Extension public static class DescriptorImpl extends TeamPushTrigger.DescriptorImpl { @Override public String getDisplayName() { return "Build when a change is pushed to a TFS pull request"; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamPendingStatusBuildStep.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs; import hudson.Extension; import hudson.FilePath; import hudson.Launcher; import hudson.model.AbstractProject; import hudson.model.Descriptor; import hudson.model.Run; import hudson.model.TaskListener; import hudson.plugins.tfs.util.TeamStatus; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.Builder; import jenkins.tasks.SimpleBuildStep; import org.kohsuke.stapler.DataBoundConstructor; import javax.annotation.Nonnull; import java.io.IOException; /** * A _Build Step_ that reports the status of an associated build as "Pending" to TFS/Team Services. */ public class TeamPendingStatusBuildStep extends Builder implements SimpleBuildStep { @DataBoundConstructor public TeamPendingStatusBuildStep() { } @Override public void perform( @Nonnull final Run run, @Nonnull final FilePath workspace, @Nonnull final Launcher launcher, @Nonnull final TaskListener listener ) throws InterruptedException, IOException { if (!TeamGlobalStatusAction.isApplicable(run)){ perform(run, listener); } } public void perform(final @Nonnull Run run, final @Nonnull TaskListener listener) { try { TeamStatus.createFromRun(run, listener, getDisplayName()); } catch (final IllegalArgumentException e) { listener.error(e.getMessage()); } catch (final Exception e) { e.printStackTrace(listener.error("Error while trying to update pending status in TFS/Team Services")); } } String getDisplayName() { final Descriptor descriptor = getDescriptor(); return descriptor.getDisplayName(); } @Extension public static class DescriptorImpl extends BuildStepDescriptor { @Override public boolean isApplicable(Class jobType) { return true; } @Override public String getDisplayName() { return "Set build pending status in TFS/Team Services"; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamPluginGlobalConfig.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs; import hudson.Extension; import hudson.ExtensionList; import hudson.plugins.tfs.model.DomainUserAccountMapper; import hudson.plugins.tfs.model.UserAccountMapper; import hudson.plugins.tfs.model.UserAccountMapperDescriptor; import hudson.plugins.tfs.rm.ReleaseWebHook; import jenkins.model.GlobalConfiguration; import jenkins.model.Jenkins; import net.sf.json.JSONObject; import org.apache.commons.lang3.ObjectUtils; import org.kohsuke.stapler.StaplerRequest; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; /** * All the settings that apply globally. */ @Extension public class TeamPluginGlobalConfig extends GlobalConfiguration { private static final Logger LOGGER = Logger.getLogger(TeamPluginGlobalConfig.class.getName()); public static final TeamPluginGlobalConfig DEFAULT_CONFIG = new TeamPluginGlobalConfig(false); private List collectionConfigurations = new ArrayList(); private List releaseWebHookConfigurations = new ArrayList(); private boolean configFolderPerNode; private boolean enableTeamPushTriggerForAllJobs; private boolean enableTeamStatusForAllJobs; private UserAccountMapper userAccountMapper; public TeamPluginGlobalConfig() { this(true); } TeamPluginGlobalConfig(final boolean shouldLoadConfig) { if (shouldLoadConfig) { load(); } } public TeamPluginGlobalConfig(final List collectionConfigurations) { this.collectionConfigurations = collectionConfigurations; } public static TeamPluginGlobalConfig get() { TeamPluginGlobalConfig result = DEFAULT_CONFIG; if (Jenkins.getInstance() != null) { final ExtensionList configurationExtensions = all(); final TeamPluginGlobalConfig config = configurationExtensions.get(TeamPluginGlobalConfig.class); result = ObjectUtils.defaultIfNull(config, DEFAULT_CONFIG); } return result; } public List getCollectionConfigurations() { return collectionConfigurations; } public void setCollectionConfigurations(final List collectionConfigurations) { this.collectionConfigurations = collectionConfigurations; } public List getReleaseWebHookConfigurations() { return this.releaseWebHookConfigurations; } public void setReleaseWebHookConfigurations(final List releaseWebHookConfigurations) { this.releaseWebHookConfigurations = releaseWebHookConfigurations; } public boolean isConfigFolderPerNode() { return configFolderPerNode; } public void setConfigFolderPerNode(final boolean configFolderPerNode) { this.configFolderPerNode = configFolderPerNode; } public boolean isEnableTeamPushTriggerForAllJobs() { return enableTeamPushTriggerForAllJobs; } public void setEnableTeamPushTriggerForAllJobs(final boolean enableTeamPushTriggerForAllJobs) { this.enableTeamPushTriggerForAllJobs = enableTeamPushTriggerForAllJobs; } public boolean isEnableTeamStatusForAllJobs() { return enableTeamStatusForAllJobs; } public void setEnableTeamStatusForAllJobs(final boolean enableTeamStatusForAllJobs) { this.enableTeamStatusForAllJobs = enableTeamStatusForAllJobs; } public UserAccountMapper getUserAccountMapper() { if (userAccountMapper == null) { userAccountMapper = new DomainUserAccountMapper(); } return userAccountMapper; } public void setUserAccountMapper(UserAccountMapper userAccountMapper) { this.userAccountMapper = userAccountMapper; } public List getUserAccountMapperDescriptors() { return UserAccountMapper.all(); } @Override public boolean configure(StaplerRequest req, JSONObject json) throws FormException { try { req.bindJSON(this, json); // stapler oddity, empty lists are not set on bean by "req.bindJSON(this, json)" this.releaseWebHookConfigurations = req.bindJSONToList(ReleaseWebHook.class, json.get("releaseWebHookConfigurations")); } catch (final Exception e) { final String message = "Configuration error: " + e.getMessage(); LOGGER.log(Level.WARNING, message, e); LOGGER.log(Level.FINE, "Form data: {}", json.toString()); throw new FormException(message, e, "team-configuration"); } save(); return true; } @Override public String getDisplayName() { return "TFS/Team Services"; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamPullRequestMergedDetailsAction.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs; import com.microsoft.teamfoundation.core.webapi.model.TeamProjectReference; import com.microsoft.teamfoundation.sourcecontrol.webapi.model.GitRepository; import com.microsoft.visualstudio.services.webapi.model.ResourceRef; import hudson.model.Action; import hudson.model.Run; import hudson.plugins.tfs.model.GitPullRequestEx; import hudson.plugins.tfs.model.GitPushEvent; import hudson.plugins.tfs.util.UriHelper; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; import java.io.Serializable; import java.net.URI; import java.util.Collections; import java.util.List; /** * Captures details of the TFS/Team Services pull request event which triggered us. */ @ExportedBean(defaultVisibility = 999) public class TeamPullRequestMergedDetailsAction implements Action, Serializable { private static final long serialVersionUID = 1L; private static final String URL_NAME = "team-pullRequestMergedDetails"; public transient GitPullRequestEx gitPullRequest; public String message; public String detailedMessage; public String collectionUri; public TeamPullRequestMergedDetailsAction() { } public TeamPullRequestMergedDetailsAction(final GitPullRequestEx gitPullRequest, final String message, final String detailedMessage, final String collectionUri) { this.gitPullRequest = gitPullRequest; this.message = message; this.detailedMessage = detailedMessage; this.collectionUri = collectionUri; } public static URI addWorkItemsForRun(final Run run, final List destination) { final TeamPullRequestMergedDetailsAction action = run.getAction(TeamPullRequestMergedDetailsAction.class); if (action != null && action.hasWorkItems()) { Collections.addAll(destination, action.getWorkItems()); final GitPullRequestEx gitPullRequest = action.gitPullRequest; final URI collectionUri = URI.create(action.collectionUri); return collectionUri; } return null; } @Override public String getIconFileName() { return "/plugin/tfs/48x48/logo.png"; } @Override public String getDisplayName() { return "TFS/Team Services pull request"; } @Override public String getUrlName() { return URL_NAME; } // the following methods are called from this/summary.jelly and/or this/index.jelly @Exported public String getMessage() { return message; } @Exported public String getDetailedMessage() { return detailedMessage; } @Exported public ResourceRef[] getWorkItems() { return gitPullRequest.getWorkItemRefs(); } @Exported public boolean hasWorkItems() { final ResourceRef[] workItemRefs = gitPullRequest.getWorkItemRefs(); return workItemRefs != null && workItemRefs.length > 0; } @Exported public String getPullRequestUrl() { final GitRepository repository = gitPullRequest.getRepository(); final URI collectionUri = URI.create(this.collectionUri); final TeamProjectReference project = repository.getProject(); final URI pullRequestUrl = UriHelper.join(collectionUri, project.getName(), "_git", repository.getName(), "pullrequest", gitPullRequest.getPullRequestId() ); final String result = pullRequestUrl.toString(); return result; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamPushCause.java ================================================ package hudson.plugins.tfs; import hudson.triggers.SCMTrigger.SCMTriggerCause; import org.apache.commons.lang.StringUtils; import java.io.File; import java.io.IOException; /** * Indicates that a build was queued because of a TFS/Team Services Git code push event. */ public class TeamPushCause extends SCMTriggerCause { private final String pushedBy; private final String context; public TeamPushCause(final String pushedBy, final String context) { this("", pushedBy, context); } public TeamPushCause(final File logFile, final String pushedBy, final String context) throws IOException { super(logFile); this.pushedBy = pushedBy; this.context = context; } public TeamPushCause(final String pollingLog, final String pushedBy, final String context) { super(pollingLog); this.pushedBy = pushedBy; this.context = context; } @Override public String getShortDescription() { final String template = "Started by TFS/Team Services push by %s"; final String message = String.format(template, StringUtils.trimToEmpty(pushedBy)); return message; } @Override public boolean equals(final Object o) { return super.equals(o); } @Override public int hashCode() { return super.hashCode(); } public String getRunContext() { return this.context; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamPushTrigger.java ================================================ package hudson.plugins.tfs; import hudson.Extension; import hudson.Util; import hudson.console.AnnotatedLargeText; import hudson.model.Action; import hudson.model.CauseAction; import hudson.model.Item; import hudson.model.Job; import hudson.model.queue.QueueTaskFuture; import hudson.plugins.tfs.model.GitCodePushedEventArgs; import hudson.plugins.tfs.util.ActionHelper; import hudson.plugins.tfs.util.MediaType; import hudson.triggers.Trigger; import hudson.triggers.TriggerDescriptor; import hudson.util.StreamTaskListener; import jenkins.model.ParameterizedJobMixIn; import jenkins.triggers.SCMTriggerItem; import org.apache.commons.jelly.XMLOutput; import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.DataBoundConstructor; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.io.Writer; import java.text.DateFormat; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.kohsuke.stapler.DataBoundSetter; /** * Triggers a build when we receive a TFS/Team Services Git code push event. */ public class TeamPushTrigger extends Trigger> { private static final Logger LOGGER = Logger.getLogger(TeamPushTrigger.class.getName()); private String jobContext; @DataBoundConstructor public TeamPushTrigger() { } public TeamPushTrigger(final Job job) { this.job = job; } public TeamPushTrigger(final Job job, final String jobContext) { this.job = job; this.jobContext = jobContext; } /** * Execute function. */ public void execute(final GitCodePushedEventArgs gitCodePushedEventArgs, final List actions, final boolean bypassPolling) { // TODO: Consider executing the poll + queue asynchronously final Runner runner = new Runner(gitCodePushedEventArgs, actions, bypassPolling); runner.run(); } public File getLogFile() { return new File(job.getRootDir(), "team-polling.log"); } public String getJobContext() { return jobContext; } @DataBoundSetter public void setJobContext(final String jobContext) { this.jobContext = jobContext; } /** * Runner class for TeamPushTrigger. */ // TODO: This was inspired by SCMTrigger.Runner; it would be worth extracting something for re-use public class Runner implements Runnable { private final GitCodePushedEventArgs gitCodePushedEventArgs; private final List actions; private final boolean bypassPolling; public Runner(final GitCodePushedEventArgs gitCodePushedEventArgs, final List actions, final boolean bypassPolling) { this.gitCodePushedEventArgs = gitCodePushedEventArgs; this.actions = actions; this.bypassPolling = bypassPolling; } private SCMTriggerItem job() { return SCMTriggerItem.SCMTriggerItems.asSCMTriggerItem(job); } private boolean runPolling() { final String failedToRecord = "Failed to record SCM polling for " + job; try { final StreamTaskListener listener = new StreamTaskListener(getLogFile(), MediaType.UTF_8); try { final PrintStream logger = listener.getLogger(); final long startTimeMillis = System.currentTimeMillis(); final Date date = new Date(startTimeMillis); logger.println("Started on " + DateFormat.getDateTimeInstance().format(date)); final boolean result = job().poll(listener).hasChanges(); final long endTimeMillis = System.currentTimeMillis(); logger.println("Done. Took " + Util.getTimeSpanString(endTimeMillis - startTimeMillis)); if (result) { logger.println("Changes found"); } else { logger.println("No changes"); } return result; } catch (final Error e) { e.printStackTrace(listener.error(failedToRecord)); LOGGER.log(Level.SEVERE, failedToRecord, e); throw e; } catch (final RuntimeException e) { e.printStackTrace(listener.error(failedToRecord)); LOGGER.log(Level.SEVERE, failedToRecord, e); throw e; } finally { listener.close(); } } catch (final IOException e) { LOGGER.log(Level.SEVERE, failedToRecord, e); return false; } } @Override public void run() { boolean shouldSchedule = bypassPolling; String changesDetected = ""; if (!bypassPolling) { // pipeline jobs might have runPolling() returned as false, while still should be scheduled. // we should schedule them as long as they are associated with a valid commit. if (runPolling() || StringUtils.isNotBlank(gitCodePushedEventArgs.commit)) { changesDetected = "SCM changes detected in " + job.getFullDisplayName() + ". "; shouldSchedule = true; } } else { changesDetected = "Polling bypassed for " + job.getFullDisplayName() + ". "; } if (shouldSchedule) { final SCMTriggerItem p = job(); final String name = "#" + p.getNextBuildNumber(); final String pushedBy = gitCodePushedEventArgs.pushedBy; TeamPushCause cause; final File logFile = getLogFile(); if (logFile.isFile()) { try { cause = new TeamPushCause(logFile, pushedBy, getJobContext()); } catch (IOException e) { LOGGER.log(Level.WARNING, "Failed to parse the polling log", e); cause = new TeamPushCause(pushedBy, getJobContext()); } } else { cause = new TeamPushCause(pushedBy, getJobContext()); } final int quietPeriod = p.getQuietPeriod(); final CauseAction causeAction = new CauseAction(cause); final Action[] actionArray = ActionHelper.create(actions, causeAction); final QueueTaskFuture queueTaskFuture = p.scheduleBuild2(quietPeriod, actionArray); if (queueTaskFuture != null) { LOGGER.info(changesDetected + "Triggering " + name); } else { LOGGER.info(changesDetected + "Job is already in the queue"); } } } } @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) super.getDescriptor(); } /** * This class extends trigger descriptor class. */ @Extension public static class DescriptorImpl extends TriggerDescriptor { @Override public boolean isApplicable(final Item item) { return item instanceof Job && SCMTriggerItem.SCMTriggerItems.asSCMTriggerItem(item) != null && item instanceof ParameterizedJobMixIn.ParameterizedJob; } @Override public String getDisplayName() { return "Build when a change is pushed to TFS/Team Services"; } } @Override public Collection getProjectActions() { if (job == null) { return Collections.emptyList(); } return Collections.singleton(new TeamPollingAction()); } /** * This class defines team polling action. */ public final class TeamPollingAction implements Action { @Override public String getIconFileName() { return "/plugin/tfs/48x48/logo.png"; } @Override public String getDisplayName() { return "TFS/Team Services hook log"; } @Override public String getUrlName() { return "TeamPollLog"; } // the following methods are called from TeamPushTrigger/TeamPollingAction/index.jelly @SuppressWarnings("unused") public Job getOwner() { return job; } /** * Get log. */ @SuppressWarnings("unused") public String getLog() throws IOException { return Util.loadFile(getLogFile()); } /** * Write log. */ @SuppressWarnings("unused") public void writeLogTo(final XMLOutput out) throws IOException { final File logFile = getLogFile(); final AnnotatedLargeText text = new AnnotatedLargeText(logFile, MediaType.UTF_8, true, this); final Writer writer = out.asWriter(); text.writeHtmlTo(0, writer); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamResultsAction.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs; import hudson.model.Run; import hudson.plugins.tfs.util.EndpointHelper; import hudson.plugins.tfs.util.MediaType; import jenkins.model.RunAction2; import org.apache.commons.io.IOUtils; import org.kohsuke.stapler.ForwardToView; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.export.ExportedBean; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.Serializable; import java.util.logging.Level; import java.util.logging.Logger; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; /** * Added by {@link TeamCollectResultsPostBuildAction} to enable the download of the ZIP * file containing the collected results from the build. */ @ExportedBean(defaultVisibility = 999) public class TeamResultsAction implements RunAction2, Serializable { private static final long serialVersionUID = 1L; private static final Logger LOGGER = Logger.getLogger(TeamResultsAction.class.getName()); public transient Run run; @Override public void onAttached(final Run r) { this.run = r; } @Override public void onLoad(final Run r) { this.run = r; } @Override public String getIconFileName() { return null; } @Override public String getDisplayName() { return null; } @Override public String getUrlName() { return "team-results"; } public static void addToRun(final Run run) { final TeamResultsAction action = new TeamResultsAction(); run.addAction(action); } @SuppressWarnings("unused" /* API method */) public void doZip(final StaplerRequest req, final StaplerResponse rsp) throws IOException { try { if (run == null) { throw new IllegalArgumentException("There is no associated Run"); } final File rootDir = run.getRootDir(); final File resultsZipFile = new File(rootDir, TeamCollectResultsPostBuildAction.TEAM_RESULTS_ZIP); if (!resultsZipFile.isFile()) { throw new IllegalArgumentException("There is no results file in this build"); } final FileInputStream resultsZipStream = new FileInputStream(resultsZipFile); rsp.setContentType(MediaType.APPLICATION_ZIP); final long lastModified = resultsZipFile.lastModified(); final long contentLength = resultsZipFile.length(); final String fileName = resultsZipFile.getName(); try { rsp.serveFile(req, resultsZipStream, lastModified, contentLength, fileName); } finally { IOUtils.closeQuietly(resultsZipStream); } } catch (final IllegalArgumentException e) { LOGGER.log(Level.WARNING, "IllegalArgumentException", e); EndpointHelper.error(SC_BAD_REQUEST, e); } catch (final ForwardToView e) { throw e; } catch (final Exception e) { LOGGER.log(Level.SEVERE, "Unknown error", e); EndpointHelper.error(SC_INTERNAL_SERVER_ERROR, e); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/TeamUpdateWorkItemPostBuildAction.java ================================================ package hudson.plugins.tfs; import com.microsoft.visualstudio.services.webapi.model.ResourceRef; import hudson.Extension; import hudson.FilePath; import hudson.Launcher; import hudson.model.AbstractProject; import hudson.model.Run; import hudson.model.TaskListener; import hudson.plugins.tfs.telemetry.TelemetryHelper; import hudson.plugins.tfs.util.TeamRestClient; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.BuildStepMonitor; import hudson.tasks.Notifier; import hudson.tasks.Publisher; import jenkins.tasks.SimpleBuildStep; import org.kohsuke.stapler.DataBoundConstructor; import javax.annotation.Nonnull; import java.io.IOException; import java.net.URI; import java.util.ArrayList; /** * A _Post-Build Action_ that updates associated work items with a link back * to the Jenkins build. */ @SuppressWarnings("unused" /* Jenkins extension */) public class TeamUpdateWorkItemPostBuildAction extends Notifier implements SimpleBuildStep { @DataBoundConstructor public TeamUpdateWorkItemPostBuildAction() { } @Override public void perform( @Nonnull final Run run, @Nonnull final FilePath workspace, @Nonnull final Launcher launcher, @Nonnull final TaskListener listener ) throws InterruptedException, IOException { try { final String absoluteUrl = run.getAbsoluteUrl(); final ArrayList workItems = new ArrayList(); final URI collectionUri = TeamPullRequestMergedDetailsAction.addWorkItemsForRun(run, workItems); if (collectionUri != null) { final TeamRestClient client = new TeamRestClient(collectionUri); for (final ResourceRef workItem : workItems) { final String workItemIdString = workItem.getId(); final Integer workItemId = Integer.valueOf(workItemIdString, 10); client.addHyperlinkToWorkItem(workItemId, absoluteUrl); } // Send telemetry TelemetryHelper.sendEvent("team-workitem-update", new TelemetryHelper.PropertyMapBuilder() .serverContext(collectionUri.toString(), collectionUri.toString()) .build()); } } catch (final IllegalArgumentException e) { listener.error(e.getMessage()); } catch (final Exception e) { e.printStackTrace(listener.error("Error while trying to update associated work items in TFS/Team Services")); } } @Override public BuildStepMonitor getRequiredMonitorService() { // we don't need the outcome of any previous builds for this step return BuildStepMonitor.NONE; } /** * Class descriptor. */ @Extension public static class DescriptorImpl extends BuildStepDescriptor { @Override public boolean isApplicable(final Class jobType) { return true; } @Override public String getDisplayName() { return "Add link to associated work items in TFS/Team Services"; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/UnsupportedIntegrationAction.java ================================================ package hudson.plugins.tfs; import hudson.model.Action; import hudson.model.InvisibleAction; import hudson.model.Run; import hudson.model.TaskListener; import javax.annotation.Nonnull; import java.io.Serializable; import java.util.List; /** * Contributed to a build whenever the rich TFS/Team Services integration endpoints * are used with unsupported parameters. */ public class UnsupportedIntegrationAction extends InvisibleAction implements Serializable { private static final long serialVersionUID = 1L; private final String reason; public UnsupportedIntegrationAction(final String reason) { this.reason = reason; } /** * Returns true if the run does not contain an UnsupportedIntegrationAction. */ public static boolean isSupported(@Nonnull final Run run, @Nonnull final TaskListener listener) { final UnsupportedIntegrationAction action = run.getAction(UnsupportedIntegrationAction.class); return action == null; } /** * Adds this action to the build with the given reason. */ public static void addToBuild(final List actions, final String reason) { final UnsupportedIntegrationAction action = new UnsupportedIntegrationAction(reason); actions.add(action); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/actions/CheckoutAction.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.actions; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.DateVersionSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.VersionSpec; import hudson.FilePath; import hudson.model.TaskListener; import hudson.plugins.tfs.commands.RemoteChangesetVersionCommand; import hudson.plugins.tfs.model.ChangeSet; import hudson.plugins.tfs.model.Project; import hudson.plugins.tfs.model.Server; import hudson.plugins.tfs.model.Workspace; import hudson.plugins.tfs.model.Workspaces; import java.io.IOException; import java.io.PrintStream; import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.HashSet; import java.util.List; public class CheckoutAction { private final String workspaceName; private final String projectPath; private final Collection cloakedPaths; private final String localFolder; private final boolean useUpdate; private final boolean useOverwrite; public CheckoutAction(String workspaceName, String projectPath, Collection cloakedPaths, String localFolder, boolean useUpdate, boolean useOverwrite) { this.workspaceName = workspaceName; this.projectPath = projectPath; this.cloakedPaths = cloakedPaths; this.localFolder = localFolder; this.useUpdate = useUpdate; this.useOverwrite = useOverwrite; } public List checkout(Server server, FilePath workspacePath, Calendar lastBuildTimestamp, Calendar currentBuildTimestamp) throws IOException, InterruptedException, ParseException { final VersionSpec lastBuildVersionSpec; if (lastBuildTimestamp != null) { lastBuildVersionSpec = new DateVersionSpec(lastBuildTimestamp); } else{ lastBuildVersionSpec = null; } final VersionSpec currentBuildVersionSpec = new DateVersionSpec(currentBuildTimestamp); return checkout(server, workspacePath, lastBuildVersionSpec, currentBuildVersionSpec); } public List checkout(final Server server, final FilePath workspacePath, final VersionSpec lastBuildVersionSpec, final VersionSpec currentBuildVersionSpec) throws IOException, InterruptedException { Project project = getProject(server, workspacePath); final String versionSpecString = RemoteChangesetVersionCommand.toString(currentBuildVersionSpec); final String normalizedFolder = determineCheckoutPath(workspacePath, localFolder); project.getFiles(normalizedFolder, versionSpecString, useOverwrite); if (lastBuildVersionSpec != null) { return project.getDetailedHistoryWithoutCloakedPaths(lastBuildVersionSpec, currentBuildVersionSpec, cloakedPaths); } return new ArrayList(); } public List checkoutBySingleVersionSpec(Server server, FilePath workspacePath, String singleVersionSpec) throws IOException, InterruptedException { Project project = getProject(server, workspacePath); final String normalizedFolder = determineCheckoutPath(workspacePath, localFolder); project.getFiles(normalizedFolder, singleVersionSpec, useOverwrite); return project.getDetailedHistory(singleVersionSpec); } static String determineCheckoutPath(final FilePath workspacePath, final String localFolder) { final FilePath combinedPath = new FilePath(workspacePath, localFolder); final String result = combinedPath.getRemote(); return result; } private Project getProject(final Server server, final FilePath workspacePath) throws IOException, InterruptedException { final Workspaces workspaces = server.getWorkspaces(); final Project project = server.getProject(projectPath); final FilePath localFolderPath = workspacePath.child(localFolder); final String localPath = localFolderPath.getRemote(); final TaskListener listener = server.getListener(); final PrintStream logger = listener.getLogger(); final HashSet workspaceNamesToDelete = new HashSet(); final String existingWorkspaceName = workspaces.getWorkspaceMapping(localPath); if (workspaces.exists(workspaceName)) { if (!useUpdate) { workspaceNamesToDelete.add(workspaceName); } if (existingWorkspaceName == null) { logger.println("Warning: Although the server thinks the workspace exists, no mapping was found."); workspaceNamesToDelete.add(workspaceName); } else if (existingWorkspaceName.equalsIgnoreCase(workspaceName)) { // workspace exists and "localPath" is mapped there: everything is fine. } else { // workspace exists AND "localPath" is mapped in another workspace??? final String template = "WARNING: Workspace '%s' already exists AND '%s' is also mapped in workspace '%s'. Is there a configuration error?"; final String message = String.format(template, workspaceName, localPath, existingWorkspaceName); logger.println(message); workspaceNamesToDelete.add(workspaceName); workspaceNamesToDelete.add(existingWorkspaceName); } final boolean localFolderExists = localFolderPath.exists(); if (!localFolderExists) { logger.println("Warning: The local folder is missing."); workspaceNamesToDelete.add(workspaceName); } } else { // there is (apparently) no workspace called "workspaceName"... if (existingWorkspaceName != null && !existingWorkspaceName.equalsIgnoreCase(workspaceName)) { // "localPath" is mapped under another workspace name: delete the old one final String template = "Workspace was apparently renamed, will delete the old one: '%s'."; final String message = String.format(template, existingWorkspaceName); logger.println(message); workspaceNamesToDelete.add(existingWorkspaceName); } } for (final String workspaceNameToDelete : workspaceNamesToDelete) { final Workspace workspace = workspaces.getWorkspace(workspaceNameToDelete); workspaces.deleteWorkspace(workspace); } Workspace workspace; if (! workspaces.exists(workspaceName)) { if ((!useUpdate || workspaceNamesToDelete.size() > 0) && localFolderPath.exists()) { localFolderPath.deleteContents(); } final String serverPath = project.getProjectPath(); workspace = workspaces.newWorkspace(workspaceName, serverPath, cloakedPaths, localPath); } else { workspace = workspaces.getWorkspace(workspaceName); } return project; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/actions/RemoveWorkspaceAction.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.actions; import java.io.IOException; import hudson.plugins.tfs.model.Server; import hudson.plugins.tfs.model.Workspace; import hudson.plugins.tfs.model.Workspaces; /** * Removes a workspace from a TFS server. * The tf command "workspace /delete" removes any mappings also. * @author Erik Ramfelt */ public class RemoveWorkspaceAction { private final String workspaceName; public RemoveWorkspaceAction(String workspaceName) { this.workspaceName = workspaceName; } public boolean remove(Server server) throws IOException, InterruptedException { Workspaces workspaces = server.getWorkspaces(); if (workspaces.exists(workspaceName)) { Workspace workspace = workspaces.getWorkspace(workspaceName); workspaces.deleteWorkspace(workspace); return true; } return false; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/browsers/TeamFoundationServerRepositoryBrowser.java ================================================ package hudson.plugins.tfs.browsers; import java.io.IOException; import java.net.URL; import hudson.plugins.tfs.model.ChangeSet; import hudson.scm.RepositoryBrowser; /** * Repository browser for the Team Foundation Server SCM. * * @author Erik Ramfelt */ public abstract class TeamFoundationServerRepositoryBrowser extends RepositoryBrowser { /** * Determines the link to the diff between the version in * the specified revision of {@link ChangeSet.Item} to its previous version. * * @param item the item for which a link to differences will be generated. * * @return null if the browser doesn't have any URL for diff. * * @throws IOException If an I/O error occurs */ public abstract URL getDiffLink(ChangeSet.Item item) throws IOException; /** * Determines the link to a single file under TFS. * * @param item the item for which a link to differences will be generated. * * @return null if the browser doesn't have any suitable URL. * * @throws IOException If an I/O error occurs */ public abstract URL getFileLink(ChangeSet.Item item) throws IOException; private static final long serialVersionUID = 1L; } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/browsers/TeamSystemWebAccessBrowser.java ================================================ package hudson.plugins.tfs.browsers; import hudson.Extension; import hudson.Util; import hudson.model.AbstractProject; import hudson.model.Descriptor; import hudson.plugins.tfs.TeamFoundationServerScm; import hudson.plugins.tfs.model.ChangeSet; import hudson.plugins.tfs.util.QueryString; import hudson.scm.EditType; import hudson.scm.RepositoryBrowser; import hudson.scm.SCM; import org.kohsuke.stapler.DataBoundConstructor; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; /** * Provides information and links back to the TFS/VSTS repository for the current change set. */ public class TeamSystemWebAccessBrowser extends TeamFoundationServerRepositoryBrowser { private static final long serialVersionUID = 1L; private final String url; @DataBoundConstructor public TeamSystemWebAccessBrowser(final String urlExample) { this.url = Util.fixEmpty(urlExample); } public String getUrl() { return url; } private String getServerConfiguration(final ChangeSet changeset) { AbstractProject project = changeset.getParent().build.getProject(); SCM scm = project.getScm(); if (scm instanceof TeamFoundationServerScm) { return ((TeamFoundationServerScm) scm).getServerUrl(changeset.getParent().build); } else { final DescriptorImpl descriptor = (DescriptorImpl) getDescriptor(); final String displayName = descriptor.getDisplayName(); throw new IllegalStateException("'" + displayName + "' repository browser can only be used with the 'Team Foundation Server' SCM"); } } private String getBaseUrlString(final ChangeSet changeSet) throws MalformedURLException { String baseUrl = url; if (baseUrl == null) { baseUrl = getServerConfiguration(changeSet); } baseUrl = normalizeToEndWithSlash(new URL(baseUrl)).toString(); return baseUrl; } /** * Gets the link to a specific change set. */ @Override public URL getChangeSetLink(final ChangeSet changeSet) throws IOException { final String baseUrlString = getBaseUrlString(changeSet); final URL baseUrl = new URL(baseUrlString); return new URL(baseUrl, "_versionControl/changeset/" + changeSet.getVersion()); } private URL createChangeSetItemLink(final ChangeSet.Item item, final String action) throws IOException { final ChangeSet changeSet = item.getParent(); final URL changeSetUrl = getChangeSetLink(changeSet); final QueryString qs = new QueryString(); qs.put("path", item.getPath()); qs.put("version", changeSet.getVersion()); qs.put("_a", action); return new URL(changeSetUrl, "#" + qs.toString()); } /** * Gets the link for a specific file in a change set. */ public URL getFileLink(final ChangeSet.Item item) throws IOException { return createChangeSetItemLink(item, "contents"); } /** * Gets the link to compare a specific file in a change set. */ public URL getDiffLink(final ChangeSet.Item item) throws IOException { if (item.getEditType() != EditType.EDIT) { return null; } return createChangeSetItemLink(item, "compare"); } /** * Gets the descriptor for the repository. */ @Extension public static final class DescriptorImpl extends Descriptor> { public DescriptorImpl() { super(TeamSystemWebAccessBrowser.class); } @Override public String getDisplayName() { return "Microsoft Team Foundation Server/Visual Studio Team Services"; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/commands/AbstractCallableCommand.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.commands; import com.microsoft.tfs.core.TFSTeamProjectCollection; import com.microsoft.tfs.core.clients.versioncontrol.VersionControlClient; import com.microsoft.tfs.core.clients.versioncontrol.VersionControlConstants; import com.microsoft.tfs.core.clients.versioncontrol.Workstation; import com.microsoft.tfs.core.config.persistence.PersistenceStoreProvider; import hudson.model.TaskListener; import hudson.plugins.tfs.model.ExtraSettings; import hudson.plugins.tfs.model.Server; import hudson.plugins.tfs.model.WebProxySettings; import hudson.remoting.Callable; import jenkins.security.MasterToSlaveCallable; import java.io.IOException; import java.io.Serializable; public abstract class AbstractCallableCommand extends MasterToSlaveCallable implements Serializable { private final String url; private final String userName; private final String userPassword; private final TaskListener listener; private final WebProxySettings webProxySettings; private final ExtraSettings extraSettings; protected AbstractCallableCommand(final ServerConfigurationProvider serverConfig) { url = serverConfig.getUrl(); userName = serverConfig.getUserName(); userPassword = serverConfig.getUserPassword(); listener = serverConfig.getListener(); webProxySettings = serverConfig.getWebProxySettings(); extraSettings = serverConfig.getExtraSettings(); } protected void updateCache(final TFSTeamProjectCollection connection) { final PersistenceStoreProvider persistenceStoreProvider = connection.getPersistenceStoreProvider(); final Workstation workstation = Workstation.getCurrent(persistenceStoreProvider); final VersionControlClient vcc = connection.getVersionControlClient(); workstation.updateWorkspaceInfoCache(vcc, VersionControlConstants.AUTHENTICATED_USER); } public Server createServer() throws IOException { final Server server = new Server(null, listener, url, userName, userPassword, webProxySettings, extraSettings); return server; } public abstract Callable getCallable(); } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/commands/AbstractCommand.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.commands; import hudson.Util; import hudson.plugins.tfs.util.MaskedArgumentListBuilder; import hudson.util.ArgumentListBuilder; public abstract class AbstractCommand implements Command { private final ServerConfigurationProvider config; public AbstractCommand(ServerConfigurationProvider configurationProvider) { this.config = configurationProvider; } protected void addServerArgument(ArgumentListBuilder arguments) { arguments.add(String.format("-server:%s", config.getUrl())); } protected void addLoginArgument(MaskedArgumentListBuilder arguments) { if ((Util.fixEmpty(config.getUserName()) != null) && (config.getUserPassword()!= null)) { arguments.addMasked(String.format("-login:%s,%s", config.getUserName(), config.getUserPassword())); } } public ServerConfigurationProvider getConfig() { return config; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/commands/Command.java ================================================ package hudson.plugins.tfs.commands; import hudson.plugins.tfs.util.MaskedArgumentListBuilder; /** * Command that issues a tf command line client command. * * @author Erik Ramfelt */ public interface Command { /** * Returns the arguments to be sent to the TF command line client. * @return arguments for the TF tool */ MaskedArgumentListBuilder getArguments(); } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/commands/DeleteWorkspaceCommand.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.commands; import com.microsoft.tfs.core.TFSTeamProjectCollection; import com.microsoft.tfs.core.clients.versioncontrol.VersionControlConstants; import com.microsoft.tfs.core.clients.versioncontrol.WorkspacePermissions; import com.microsoft.tfs.core.clients.versioncontrol.Workstation; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace; import com.microsoft.tfs.core.clients.versioncontrol.workspacecache.WorkspaceInfo; import com.microsoft.tfs.core.config.persistence.PersistenceStoreProvider; import com.microsoft.tfs.jni.helpers.LocalHost; import hudson.model.TaskListener; import hudson.plugins.tfs.model.MockableVersionControlClient; import hudson.plugins.tfs.model.Server; import hudson.remoting.Callable; import java.io.IOException; import java.io.PrintStream; public class DeleteWorkspaceCommand extends AbstractCallableCommand { private static final String DeletingTemplate = "Deleting workspaces named '%s' from computer '%s'..."; private static final String DeletedTemplate = "Deleted %d workspace(s) named '%s'."; private final String workspaceName; private final String computerName; public DeleteWorkspaceCommand(final ServerConfigurationProvider server, final String workspaceName) { this(server, workspaceName, null); } public DeleteWorkspaceCommand(final ServerConfigurationProvider server, final String workspaceName, final String computerName) { super(server); this.workspaceName = workspaceName; this.computerName = computerName; } public Callable getCallable() { return this; } public Void call() throws IOException { final Server server = createServer(); final MockableVersionControlClient vcc = server.getVersionControlClient(); final TaskListener listener = server.getListener(); final PrintStream logger = listener.getLogger(); final String computerName = (DeleteWorkspaceCommand.this.computerName == null) ? LocalHost.getShortName() : DeleteWorkspaceCommand.this.computerName; final String deletingMessage = String.format(DeletingTemplate, workspaceName, computerName); logger.println(deletingMessage); final WorkspacePermissions filter = WorkspacePermissions.NONE_OR_NOT_SUPPORTED; final Workspace[] workspaces = vcc.queryWorkspaces(workspaceName, null, computerName, filter); int numDeletions = 0; for (final Workspace innerWorkspace : workspaces) { vcc.deleteWorkspace(innerWorkspace); // work around a defect in the TFS SDK for Java // TODO: check if this workaround is still necessary after upgrading final WorkspaceInfo workspaceInfo = vcc.removeCachedWorkspace(workspaceName, VersionControlConstants.AUTHENTICATED_USER); if (workspaceInfo != null) { final TFSTeamProjectCollection tpc = vcc.getConnection(); final PersistenceStoreProvider provider = tpc.getPersistenceStoreProvider(); final Workstation currentWorkstation = Workstation.getCurrent(provider); currentWorkstation.saveConfigIfDirty(); } numDeletions++; } final String deletedMessage = String.format(DeletedTemplate, numDeletions, workspaceName); logger.println(deletedMessage); return null; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/commands/GetFilesToWorkFolderCommand.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.commands; import java.io.PrintStream; import com.microsoft.tfs.core.clients.versioncontrol.GetOptions; import com.microsoft.tfs.core.clients.versioncontrol.events.GetEvent; import com.microsoft.tfs.core.clients.versioncontrol.events.GetListener; import com.microsoft.tfs.core.clients.versioncontrol.events.VersionControlEventEngine; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.LatestVersionSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.VersionSpec; import hudson.model.TaskListener; import hudson.plugins.tfs.model.MockableVersionControlClient; import hudson.plugins.tfs.model.Server; import hudson.remoting.Callable; public class GetFilesToWorkFolderCommand extends AbstractCallableCommand implements GetListener { private static final String GettingTemplate = "Getting version '%s' to '%s'..."; private static final String GotTemplate = "Finished getting version '%s'. Retrieved %d resources."; private final String workFolder; private final String versionSpec; private final boolean useOverwrite; private final boolean shouldLogEachGet; private PrintStream logger; private int getCount = 0; public GetFilesToWorkFolderCommand(final ServerConfigurationProvider server, final String workFolder, final String versionSpec, boolean useOverwrite) { this(server, workFolder, versionSpec, useOverwrite, false); // using shouldLogEachGet false as default, could be controlled by a config option at a later stage if desired, just adds noise to log though } public GetFilesToWorkFolderCommand(final ServerConfigurationProvider server, final String workFolder, final String versionSpec, boolean useOverwrite, final boolean shouldLogEachGet) { super(server); this.workFolder = workFolder; this.versionSpec = versionSpec; this.useOverwrite = useOverwrite; this.shouldLogEachGet = shouldLogEachGet; } @Override public Callable getCallable() { return this; } void setLogger(final PrintStream logger) { this.logger = logger; } public Void call() throws Exception { final Server server = createServer(); final MockableVersionControlClient vcc = server.getVersionControlClient(); final TaskListener listener = server.getListener(); logger = listener.getLogger(); final VersionSpec getVersionSpec; if (versionSpec != null) { getVersionSpec = VersionSpec.parseSingleVersionFromSpec(versionSpec, null); } else { getVersionSpec = LatestVersionSpec.INSTANCE; } final String versionSpecString = RemoteChangesetVersionCommand.toString(getVersionSpec); final String gettingMessage = String.format(GettingTemplate, versionSpecString, workFolder); logger.println(gettingMessage); final Workspace workspace = vcc.getWorkspace(workFolder); final VersionControlEventEngine eventEngine = vcc.getEventEngine(); eventEngine.addGetListener(this); workspace.get(getVersionSpec, useOverwrite ? GetOptions.OVERWRITE : GetOptions.NONE); eventEngine.removeGetListener(this); final String gotMessage = String.format(GotTemplate, versionSpecString, getCount); logger.println(gotMessage); return null; } public void onGet(final GetEvent getEvent) { getCount++; if (shouldLogEachGet) { logger.println(getEvent.getTargetLocalItem()); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/commands/GetWorkspaceMappingCommand.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.commands; import com.microsoft.tfs.core.TFSTeamProjectCollection; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace; import hudson.model.TaskListener; import hudson.plugins.tfs.model.MockableVersionControlClient; import hudson.plugins.tfs.model.Server; import hudson.remoting.Callable; import java.io.PrintStream; public class GetWorkspaceMappingCommand extends AbstractCallableCommand { private static final String CheckingMappingTemplate = "Checking if there exists a mapping for %s..."; private static final String FoundResultTemplate = "yes, in workspace '%s'."; private final String localPath; public GetWorkspaceMappingCommand(final ServerConfigurationProvider serverConfig, final String localPath) { super(serverConfig); this.localPath = localPath; } @Override public Callable getCallable() { return this; } @Override public String call() throws Exception { final Server server = createServer(); final MockableVersionControlClient vcc = server.getVersionControlClient(); final TFSTeamProjectCollection connection = vcc.getConnection(); updateCache(connection); final TaskListener listener = server.getListener(); final PrintStream logger = listener.getLogger(); final String checkingMessage = String.format(CheckingMappingTemplate, localPath); logger.print(checkingMessage); final Workspace workspace = vcc.tryGetWorkspace(localPath); final boolean existsMapping = workspace != null; final String result = existsMapping ? workspace.getName() : null; final String resultMessage = existsMapping ? String.format(FoundResultTemplate, result) : "no."; logger.println(resultMessage); return result; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/commands/LabelCommand.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.commands; import com.microsoft.tfs.core.clients.versioncontrol.VersionControlConstants; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.LabelChildOption; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.LabelResult; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.RecursionType; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.VersionControlLabel; import com.microsoft.tfs.core.clients.versioncontrol.specs.ItemSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.LabelItemSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.WorkspaceVersionSpec; import hudson.model.TaskListener; import hudson.plugins.tfs.model.MockableVersionControlClient; import hudson.plugins.tfs.model.Server; import hudson.remoting.Callable; import java.io.PrintStream; /** * Command to create a label on TFS. * @author Rodrigo Lopes (rodrigolopes) */ public class LabelCommand extends AbstractCallableCommand { private static final String CreatingTemplate = "Creating label '%s' on '%s' as of the current version in workspace '%s'..."; private static final String CreatedTemplate = "Created label '%s'."; private final String labelName; private final String workspaceName; private final String projectPath; public LabelCommand(final ServerConfigurationProvider configurationProvider, final String labelName, final String workspaceName, final String projectPath) { super(configurationProvider); this.labelName = labelName; this.workspaceName = workspaceName; this.projectPath = projectPath; } private String getLabelComment() { // TODO 1. Solve issue with quotes and spaces // TODO 2. Include build information in the comment. return "Automatically_applied_by_Jenkins_TFS_plugin"; } @Override public Callable getCallable() { return this; } public Void call() throws Exception { final Server server = createServer(); final MockableVersionControlClient vcc = server.getVersionControlClient(); final TaskListener listener = server.getListener(); final PrintStream logger = listener.getLogger(); final String userName = VersionControlConstants.AUTHENTICATED_USER; final String creatingMessage = String.format(CreatingTemplate, labelName, projectPath, workspaceName); logger.println(creatingMessage); final VersionControlLabel versionControlLabel = new VersionControlLabel(labelName, userName, userName, null, getLabelComment()); final ItemSpec itemSpec = new ItemSpec(projectPath, RecursionType.FULL); final WorkspaceVersionSpec workspaceVersionSpec = new WorkspaceVersionSpec(workspaceName, userName, userName); final LabelItemSpec labelItemSpec = new LabelItemSpec(itemSpec, workspaceVersionSpec, false); final LabelItemSpec[] items = {labelItemSpec}; final LabelResult[] labelResults = vcc.createLabel(versionControlLabel, items, LabelChildOption.FAIL); if (labelResults == null || labelResults.length == 0) { throw new RuntimeException("Label creation failed."); } else { final String createdMessage = String.format(CreatedTemplate, labelName); logger.println(createdMessage); } return null; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/commands/ListWorkspacesCommand.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.commands; import com.microsoft.tfs.core.clients.versioncontrol.WorkspacePermissions; import com.microsoft.tfs.jni.helpers.LocalHost; import hudson.Util; import hudson.model.TaskListener; import hudson.plugins.tfs.model.MockableVersionControlClient; import hudson.plugins.tfs.model.Server; import hudson.plugins.tfs.model.Workspace; import hudson.plugins.tfs.util.TextTableParser; import hudson.remoting.Callable; import org.apache.commons.lang.StringUtils; import java.io.IOException; import java.io.PrintStream; import java.io.Reader; import java.util.ArrayList; import java.util.List; public class ListWorkspacesCommand extends AbstractCallableCommand, Exception> { private static final String ListingWorkspacesTemplate = "Downloading list of workspaces from %s..."; private final String computer; private final boolean shouldLogWorkspaces; public interface WorkspaceFactory { Workspace createWorkspace(String name, String computer, String owner, String comment); } public ListWorkspacesCommand(final ServerConfigurationProvider server) { // TODO: shouldLogWorkspaces could be controlled by a property this(server, null, false); } ListWorkspacesCommand(final ServerConfigurationProvider server, final String computer, final boolean shouldLogWorkspaces) { super(server); this.computer = computer; this.shouldLogWorkspaces = shouldLogWorkspaces; } @Override public Callable, Exception> getCallable() { return this; } public List call() throws Exception { final Server server = createServer(); final MockableVersionControlClient vcc = server.getVersionControlClient(); final TaskListener listener = server.getListener(); final PrintStream logger = listener.getLogger(); final String computerName = (computer != null) ? computer : LocalHost.getShortName(); final String listWorkspacesMessage = String.format(ListingWorkspacesTemplate, server.getUrl()); logger.println(listWorkspacesMessage); final com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace[] sdkWorkspaces = vcc.queryWorkspaces( null, null, computerName, WorkspacePermissions.NONE_OR_NOT_SUPPORTED ); final List result = new ArrayList(sdkWorkspaces.length); for (final com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace sdkWorkspace : sdkWorkspaces) { final String name = sdkWorkspace.getName(); final String computer = sdkWorkspace.getComputer(); final String ownerName = sdkWorkspace.getOwnerName(); final String comment = Util.fixNull(sdkWorkspace.getComment()); final Workspace workspace = new Workspace( name, computer, ownerName, comment); result.add(workspace); } if (shouldLogWorkspaces) { log(result, logger); } return result; } public List parse(Reader consoleReader) throws IOException { List list = new ArrayList(); TextTableParser parser = new TextTableParser(consoleReader, 1); while (parser.nextRow()) { Workspace workspace = new Workspace( parser.getColumn(0), parser.getColumn(2), parser.getColumn(1), Util.fixNull(parser.getColumn(3))); list.add(workspace); } return list; } static void log(final List workspaces, final PrintStream logger) { int maxName = "Workspace".length(); int maxOwner = "Owner".length(); int maxComputer = "Computer".length(); int maxComment = "Comment".length(); for (final Workspace workspace : workspaces) { final String name = workspace.getName(); maxName = Math.max(maxName, name.length()); final String ownerName = workspace.getOwner(); maxOwner = Math.max(maxOwner, ownerName.length()); final String computer = workspace.getComputer(); maxComputer = Math.max(maxComputer, computer.length()); final String comment = workspace.getComment(); maxComment = Math.max(maxComment, comment.length()); } final String template = "%1$-" + maxName + "s %2$-" + maxOwner + "s %3$-" + maxComputer + "s %4$-" + maxComment + "s"; final String header = String.format(template, "Workspace", "Owner", "Computer", "Comment"); logger.println(header); final String divider = String.format(template, StringUtils.repeat("-", maxName), StringUtils.repeat("-", maxOwner), StringUtils.repeat("-", maxComputer), StringUtils.repeat("-", maxComment)); logger.println(divider); for (final Workspace workspace : workspaces) { final String name = workspace.getName(); final String ownerName = workspace.getOwner(); final String computer = workspace.getComputer(); final String comment = workspace.getComment(); final String line = String.format(template, name, ownerName, computer, comment); logger.println(line); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/commands/NewWorkspaceCommand.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.commands; import com.microsoft.tfs.core.TFSTeamProjectCollection; import com.microsoft.tfs.core.clients.versioncontrol.VersionControlConstants; import com.microsoft.tfs.core.clients.versioncontrol.WorkspaceLocation; import com.microsoft.tfs.core.clients.versioncontrol.WorkspaceOptions; import com.microsoft.tfs.core.clients.versioncontrol.path.LocalPath; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.RecursionType; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.WorkingFolder; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.WorkingFolderType; import hudson.model.TaskListener; import hudson.plugins.tfs.model.MockableVersionControlClient; import hudson.plugins.tfs.model.Server; import hudson.remoting.Callable; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class NewWorkspaceCommand extends AbstractCallableCommand { private static final WorkingFolder[] EMPTY_WORKING_FOLDER_ARRAY = new WorkingFolder[0]; private static final String CloakingTemplate = "Cloaking '%s' in workspace '%s'..."; private static final String CreatingTemplate = "Creating workspace '%s' owned by '%s'..."; private static final String CreatedTemplate = "Created workspace '%s'."; private static final String MappingTemplate = "Mapping '%s' to local folder '%s' in workspace '%s'..."; private final String workspaceName; private final String serverPath; private final Collection cloakedPaths; private final String localPath; public NewWorkspaceCommand(final ServerConfigurationProvider server, final String workspaceName, final String serverPath, Collection cloakedPaths, final String localPath) { super(server); this.workspaceName = workspaceName; this.serverPath = serverPath; this.cloakedPaths = cloakedPaths; this.localPath = localPath; } public Callable getCallable() { return this; } public Void call() throws IOException { final Server server = createServer(); final MockableVersionControlClient vcc = server.getVersionControlClient(); final TFSTeamProjectCollection connection = vcc.getConnection(); final TaskListener listener = server.getListener(); final PrintStream logger = listener.getLogger(); final String userName = server.getUserName(); final String creatingMessage = String.format(CreatingTemplate, workspaceName, userName); logger.println(creatingMessage); WorkingFolder[] foldersToMap = null; if (serverPath != null && localPath != null) { final String mappingMessage = String.format(MappingTemplate, serverPath, localPath, workspaceName); logger.println(mappingMessage); final List folderList = new ArrayList(); folderList.add(new WorkingFolder(serverPath, LocalPath.canonicalize(localPath), WorkingFolderType.MAP, RecursionType.FULL)); for (final String cloakedPath : cloakedPaths) { final String cloakingMessage = String.format(CloakingTemplate, cloakedPath, workspaceName); logger.println(cloakingMessage); folderList.add(new WorkingFolder(cloakedPath, null, WorkingFolderType.CLOAK)); } foldersToMap = folderList.toArray(EMPTY_WORKING_FOLDER_ARRAY); } updateCache(connection); // TODO: we might need to delete a previous workspace that had another name vcc.createWorkspace( foldersToMap, workspaceName, VersionControlConstants.AUTHENTICATED_USER, VersionControlConstants.AUTHENTICATED_USER, null /* TODO: set comment to something nice/useful */, WorkspaceLocation.SERVER /* TODO: pull request #33 adds LOCAL support */, WorkspaceOptions.NONE ); final String createdMessage = String.format(CreatedTemplate, workspaceName); logger.println(createdMessage); return null; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/commands/RemoteChangesetVersionCommand.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.commands; import com.google.common.base.Strings; import com.microsoft.tfs.core.clients.versioncontrol.VersionControlConstants; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Changeset; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.RecursionType; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.DateVersionSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.LabelVersionSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.VersionSpec; import hudson.model.TaskListener; import hudson.plugins.tfs.model.MockableVersionControlClient; import hudson.plugins.tfs.model.Server; import hudson.plugins.tfs.util.DateUtil; import hudson.plugins.tfs.util.TextTableParser; import hudson.remoting.Callable; import org.apache.commons.lang.StringUtils; import java.io.BufferedReader; import java.io.IOException; import java.io.PrintStream; import java.io.Reader; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; /** * TF command for retrieving the latest remote change set version in effect at the specified time. * * @author Olivier Dagenais * */ public class RemoteChangesetVersionCommand extends AbstractCallableCommand { private static final String QueryingTemplate = "Querying for remote changeset at '%s' as of '%s'..."; private static final String ResultTemplate = "Query result is: Changeset #%d by '%s' on '%s'."; private static final String FailedTemplate = "Query returned no result!"; private final String versionSpecString; private final String path; public RemoteChangesetVersionCommand( final ServerConfigurationProvider server, final String remotePath, final VersionSpec versionSpec) { super(server); this.path = remotePath; this.versionSpecString = toString(versionSpec); } public Callable getCallable() { return this; } public Integer call() throws Exception { final Server server = createServer(); final MockableVersionControlClient vcc = server.getVersionControlClient(); final TaskListener listener = server.getListener(); final PrintStream logger = listener.getLogger(); final VersionSpec versionSpec = VersionSpec.parseSingleVersionFromSpec(versionSpecString, VersionControlConstants.AUTHENTICATED_USER); final String specString = RemoteChangesetVersionCommand.toString(versionSpec); final String queryingMessage = String.format(QueryingTemplate, path, specString); logger.println(queryingMessage); final Changeset[] serverChangeSets = vcc.queryHistory( path, versionSpec, 0 /* deletionId */, RecursionType.FULL, null /* user */, null, null, 1 /* maxCount */, false /* includeFileDetails */, true /* slotMode */, false /* includeDownloadInfo */, false /* sortAscending */ ); Integer changeSetNumber = null; final String resultMessage; if (serverChangeSets != null && serverChangeSets.length >= 1) { final Changeset serverChangeset = serverChangeSets[0]; changeSetNumber = serverChangeset.getChangesetID(); final Date changeSetDate = serverChangeset.getDate().getTime(); final String author = serverChangeset.getOwner(); final SimpleDateFormat simpleDateFormat = DateUtil.TFS_DATETIME_FORMATTER.get(); simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); final String changeSetDateIso8601 = simpleDateFormat.format(changeSetDate); resultMessage = String.format(ResultTemplate, changeSetNumber, author, changeSetDateIso8601); } else { resultMessage = FailedTemplate; } logger.println(resultMessage); return changeSetNumber; } public static String toString(final VersionSpec versionSpec) { // TODO: just call versionSpec.toString() once DateVersionSpec.toString() uses ISO 8601 format if (versionSpec instanceof DateVersionSpec){ final DateVersionSpec dateVersionSpec = (DateVersionSpec) versionSpec; return DateUtil.toString(dateVersionSpec); } else if (versionSpec instanceof LabelVersionSpec) { final LabelVersionSpec labelVersionSpec = (LabelVersionSpec) versionSpec; // TODO: It seems to me LabelVersionSpec.toString() should emit "Lfoo" when its scope is null final String label = labelVersionSpec.getLabel(); final String scope = labelVersionSpec.getScope(); final StringBuilder sb = new StringBuilder(1 + label.length() + Strings.nullToEmpty(scope).length()); sb.append('L'); sb.append(label); if (!Strings.isNullOrEmpty(scope)) { sb.append('@'); sb.append(scope); } return sb.toString(); } return versionSpec.toString(); } public String parse(Reader consoleReader) throws ParseException, IOException { TextTableParser parser = new TextTableParser(new BufferedReader(consoleReader), 1); while (parser.nextRow()) { return parser.getColumn(0); } return StringUtils.EMPTY; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/commands/ServerConfigurationProvider.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.commands; import hudson.model.TaskListener; import hudson.plugins.tfs.model.ExtraSettings; import hudson.plugins.tfs.model.WebProxySettings; public interface ServerConfigurationProvider { public String getUrl(); public String getUserName(); public String getUserPassword(); public TaskListener getListener(); public WebProxySettings getWebProxySettings(); public ExtraSettings getExtraSettings(); } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/listeners/JenkinsRunListener.java ================================================ package hudson.plugins.tfs.listeners; import hudson.Extension; import hudson.model.Run; import hudson.model.TaskListener; import hudson.model.listeners.RunListener; import hudson.plugins.tfs.TeamCompletedStatusPostBuildAction; import hudson.plugins.tfs.TeamPendingStatusBuildStep; import hudson.plugins.tfs.UnsupportedIntegrationAction; import javax.annotation.Nonnull; import java.util.logging.Logger; /** * This class listens to the events of every Jenkins run instance. */ @Extension public class JenkinsRunListener extends RunListener { protected static final Logger log = Logger.getLogger(JenkinsRunListener.class.getName()); public JenkinsRunListener() { log.fine("JenkinsRunListener: constructor"); } @Override public void onDeleted(final Run run) { } @Override public void onStarted(final Run run, final TaskListener listener) { if (UnsupportedIntegrationAction.isSupported(run, listener)) { final TeamPendingStatusBuildStep step = new TeamPendingStatusBuildStep(); step.perform(run, listener); } } @Override public void onFinalized(final Run run) { } @Override public void onCompleted(final Run run, @Nonnull final TaskListener listener) { log.info("onCompleted: " + run.toString()); if (UnsupportedIntegrationAction.isSupported(run, listener)) { final TeamCompletedStatusPostBuildAction step = new TeamCompletedStatusPostBuildAction(); step.perform(run, listener); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/AbstractCommand.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.fasterxml.jackson.databind.ObjectMapper; import hudson.model.BuildableItem; import hudson.model.Job; import hudson.plugins.tfs.model.servicehooks.Event; import jenkins.util.TimeDuration; import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; public abstract class AbstractCommand { public AbstractCommand() { } public interface Factory { AbstractCommand create(); String getSampleRequestPayload(); } /** * Actually do the work of the command, using the supplied {@code requestPayload} and * {@code teamBuildPayload}, then returning the output as a {@link JSONObject}. * * @param job an {@link Job to operate on} * @param buildableItem an {@link BuildableItem to operate on} * @param request a {@link StaplerRequest} to help build parameter values * @param requestPayload a {@link JSONObject} representing the command's input * @param mapper an {@link ObjectMapper} instance to use to convert the {@link Event#resource} * @param teamBuildPayload a {@link TeamBuildPayload} representing the command's input * @param delay how long to wait before the project starts executing * * @return a {@link JSONObject} representing the hook event's output */ public abstract JSONObject perform(final Job job, final BuildableItem buildableItem, final StaplerRequest request, final JSONObject requestPayload, final ObjectMapper mapper, final TeamBuildPayload teamBuildPayload, final TimeDuration delay); } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/AbstractHookEvent.java ================================================ package hudson.plugins.tfs.model; import com.fasterxml.jackson.databind.ObjectMapper; import hudson.model.AbstractProject; import hudson.model.Action; import hudson.model.Cause; import hudson.model.CauseAction; import hudson.model.Item; import hudson.model.Job; import hudson.model.ParameterDefinition; import hudson.model.ParameterValue; import hudson.model.ParametersDefinitionProperty; import hudson.model.StringParameterValue; import hudson.plugins.git.BranchSpec; import hudson.plugins.git.GitSCM; import hudson.plugins.git.GitStatus; import hudson.plugins.git.UserRemoteConfig; import hudson.plugins.git.extensions.impl.IgnoreNotifyCommit; import hudson.plugins.tfs.SafeParametersAction; import hudson.plugins.tfs.TeamEventsEndpoint; import hudson.plugins.tfs.TeamGlobalStatusAction; import hudson.plugins.tfs.TeamHookCause; import hudson.plugins.tfs.TeamPRPushTrigger; import hudson.plugins.tfs.TeamPluginGlobalConfig; import hudson.plugins.tfs.TeamPushTrigger; import hudson.plugins.tfs.model.servicehooks.Event; import hudson.plugins.tfs.util.ActionHelper; import hudson.plugins.tfs.util.TeamStatus; import hudson.plugins.tfs.util.UriHelper; import hudson.scm.SCM; import hudson.security.ACL; import hudson.triggers.SCMTrigger; import jenkins.model.Jenkins; import jenkins.triggers.SCMTriggerItem; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.acegisecurity.context.SecurityContext; import org.acegisecurity.context.SecurityContextHolder; import org.apache.commons.io.IOUtils; import org.eclipse.jgit.transport.RemoteConfig; import org.eclipse.jgit.transport.URIish; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition; import org.jenkinsci.plugins.workflow.flow.FlowDefinition; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Logger; import org.apache.commons.lang.StringUtils; /** * This class abstracts the hook event. */ public abstract class AbstractHookEvent { private static final Logger LOGGER = Logger.getLogger(AbstractHookEvent.class.getName()); private static final String TRIGGER_ANY_BRANCH = "**"; /** * Interface of hook event factory. */ public interface Factory { /** * Create the factory. */ AbstractHookEvent create(); /** * Get sample request payload. */ String getSampleRequestPayload(); } /** * Actually do the work of the hook event, using the supplied * {@code mapper} to convert the event's data from the supplied {@code serviceHookEvent} * and returning the output as a {@link JSONObject}. * * @param mapper an {@link ObjectMapper} instance to use to convert the {@link Event#resource} * @param serviceHookEvent an {@link Event} that represents the request payload * and from which the {@link Event#resource} can be obtained * @param message a simple description of the event * @param detailedMessage a longer description of the event, with some details * * @return a {@link JSONObject} representing the hook event's output */ public abstract JSONObject perform(final ObjectMapper mapper, final Event serviceHookEvent, final String message, final String detailedMessage); static JSONObject fromResponseContributors(final List contributors) { final JSONObject result = new JSONObject(); final JSONArray messages = new JSONArray(); for (final GitStatus.ResponseContributor contributor : contributors) { final StringWriter stringWriter = new StringWriter(); final PrintWriter printWriter = new PrintWriter(stringWriter); try { contributor.writeBody(printWriter); printWriter.flush(); } finally { IOUtils.closeQuietly(printWriter); } final String contributorMessage = stringWriter.toString(); messages.add(contributorMessage); } result.put("messages", messages); return result; } GitStatus.ResponseContributor triggerJob(final GitCodePushedEventArgs gitCodePushedEventArgs, final List actions, final boolean bypassPolling, final Item project, final SCMTriggerItem scmTriggerItem, final Boolean repoMatches, final Boolean branchMatches) { if (!(project instanceof AbstractProject && ((AbstractProject) project).isDisabled())) { if (project instanceof Job) { final Job job = (Job) project; final int quietPeriod = scmTriggerItem.getQuietPeriod(); final String targetUrl = job.getAbsoluteUrl() + job.getNextBuildNumber(); final ArrayList values = getDefaultParameters(job); final String vstsRefspec = getVstsRefspec(gitCodePushedEventArgs); values.add(new StringParameterValue("vstsRefspec", vstsRefspec)); values.add(new StringParameterValue("vstsBranchOrCommit", gitCodePushedEventArgs.commit)); SafeParametersAction paraAction = new SafeParametersAction(values); final Action[] actionsNew = ActionHelper.create(actions, paraAction); final List actionsWithSafeParams = new ArrayList(Arrays.asList(actionsNew)); final TeamPluginGlobalConfig config = TeamPluginGlobalConfig.get(); final SCMTrigger scmTrigger = TeamEventsEndpoint.findTrigger(job, SCMTrigger.class); if (config.isEnableTeamPushTriggerForAllJobs()) { if (scmTrigger == null || !scmTrigger.isIgnorePostCommitHooks()) { // trigger is null OR job does NOT have explicitly opted out of hooks final TeamPushTrigger trigger = new TeamPushTrigger(job); trigger.execute(gitCodePushedEventArgs, actionsWithSafeParams, bypassPolling); if (bypassPolling) { return new TeamEventsEndpoint.ScheduledResponseContributor(project); } else { return new TeamEventsEndpoint.PollingScheduledResponseContributor(project); } } } if (scmTrigger != null && !scmTrigger.isIgnorePostCommitHooks()) { // queue build without first polling final Cause cause = new TeamHookCause(gitCodePushedEventArgs.commit); final CauseAction causeAction = new CauseAction(cause); final Action[] actionArray = ActionHelper.create(actionsWithSafeParams, causeAction); scmTriggerItem.scheduleBuild2(quietPeriod, actionArray); if (gitCodePushedEventArgs instanceof PullRequestMergeCommitCreatedEventArgs) { try { TeamStatus.createFromJob((PullRequestMergeCommitCreatedEventArgs) gitCodePushedEventArgs, job); } catch (IOException ex) { LOGGER.warning("Could not create TeamStatus: " + ex.toString()); } } return new TeamEventsEndpoint.ScheduledResponseContributor(project); } if ((repoMatches || repoMatches(gitCodePushedEventArgs, job)) && (branchMatches || branchMatches(gitCodePushedEventArgs, job))) { TeamPushTrigger pushTrigger = null; if (gitCodePushedEventArgs instanceof PullRequestMergeCommitCreatedEventArgs) { pushTrigger = TeamEventsEndpoint.findTrigger(job, TeamPRPushTrigger.class); } else { // Check whether current job has an EXACT TeamPushTrigger instead of a TeamPRPushTrigger whose type is also TeamPushTrigger. final List listTriggers = TeamEventsEndpoint.findTriggers(job, TeamPushTrigger.class); if (!listTriggers.isEmpty()) { for (TeamPushTrigger trigger : listTriggers) { if (!(trigger instanceof TeamPRPushTrigger)) { pushTrigger = trigger; break; } } } } if (pushTrigger != null) { pushTrigger.execute(gitCodePushedEventArgs, actionsWithSafeParams, bypassPolling); if (bypassPolling) { return new TeamEventsEndpoint.ScheduledResponseContributor(project); } else { return new TeamEventsEndpoint.PollingScheduledResponseContributor(project); } } } } } return null; } private Boolean repoMatches(final GitCodePushedEventArgs gitCodePushedEventArgs, final Job job) { if (job instanceof WorkflowJob) { final FlowDefinition jobDef = ((WorkflowJob) job).getDefinition(); if (jobDef instanceof CpsScmFlowDefinition) { final SCM jobSCM = ((CpsScmFlowDefinition) jobDef).getScm(); if (jobSCM instanceof GitSCM) { final GitSCM gitJobSCM = (GitSCM) jobSCM; final URIish uri = gitCodePushedEventArgs.getRepoURIish(); for (final UserRemoteConfig remoteConfig : gitJobSCM.getUserRemoteConfigs()) { final String jobRepoUrl = remoteConfig.getUrl(); if (StringUtils.equalsIgnoreCase(jobRepoUrl, uri.toString())) { return true; } } } } } return false; } private Boolean branchMatches(final GitCodePushedEventArgs gitCodePushedEventArgs, final Job job) { // Jobs triggered by PR merge need to check whether its target branch matches the one specified in the parameter of PR trigger UI if (gitCodePushedEventArgs instanceof PullRequestMergeCommitCreatedEventArgs) { TeamPRPushTrigger pushTrigger = TeamEventsEndpoint.findTrigger(job, TeamPRPushTrigger.class); if (pushTrigger != null) { final String targetBranches = pushTrigger.getTargetBranches(); if (targetBranches != null) { String[] branches = targetBranches.split(" "); if (branches != null) { for (String branchFullName : branches) { // branchFullName could be in the format of */pr_status String[] items = branchFullName.split("/"); if (StringUtils.equalsIgnoreCase(items[items.length - 1], gitCodePushedEventArgs.targetBranch)) { return true; } } } } } } else { // Pipeline jobs triggered by code push need to check whether its target branch matches the one in its Git parameter if (job instanceof WorkflowJob) { final FlowDefinition jobDef = ((WorkflowJob) job).getDefinition(); if (jobDef instanceof CpsScmFlowDefinition) { final SCM jobSCM = ((CpsScmFlowDefinition) jobDef).getScm(); if (jobSCM instanceof GitSCM) { final GitSCM gitJobSCM = (GitSCM) jobSCM; for (final BranchSpec branchFullName : gitJobSCM.getBranches()) { // branchFullName could be in the format of */pr_status String[] items = branchFullName.getName().split("/"); if (StringUtils.equalsIgnoreCase(items[items.length - 1], gitCodePushedEventArgs.targetBranch)) { return true; } } } } } } return false; } // TODO: it would be easiest if pollOrQueueFromEvent built a JSONObject directly List pollOrQueueFromEvent(final GitCodePushedEventArgs gitCodePushedEventArgs, final List actions, final boolean bypassPolling) { List result = new ArrayList(); final String commit = gitCodePushedEventArgs.commit; if (commit == null) { result.add(new GitStatus.MessageResponseContributor("No commits were pushed, skipping further event processing.")); return result; } final URIish uri = gitCodePushedEventArgs.getRepoURIish(); TeamGlobalStatusAction.addIfApplicable(actions); // run in high privilege to see all the projects anonymous users don't see. // this is safe because when we actually schedule a build, it's a build that can // happen at some random time anyway. SecurityContext old = ACL.impersonate(ACL.SYSTEM); try { boolean scmFound = false; final Jenkins jenkins = Jenkins.getInstance(); if (jenkins == null) { LOGGER.severe("Jenkins.getInstance() is null"); return result; } int totalBranchMatches = 0; for (final Item project : Jenkins.getActiveInstance().getAllItems()) { final SCMTriggerItem scmTriggerItem = SCMTriggerItem.SCMTriggerItems.asSCMTriggerItem(project); if (scmTriggerItem == null || scmTriggerItem.getSCMs() == null) { continue; } // Pipeline job if (scmTriggerItem.getSCMs().isEmpty()) { GitStatus.ResponseContributor triggerResult = triggerJob(gitCodePushedEventArgs, actions, bypassPolling, project, scmTriggerItem, false, false); if (triggerResult != null) { result.add(triggerResult); } continue; } for (final SCM scm : scmTriggerItem.getSCMs()) { if (!(scm instanceof GitSCM)) { continue; } final GitSCM git = (GitSCM) scm; scmFound = true; for (final RemoteConfig repository : git.getRepositories()) { boolean repositoryMatches = false; for (final URIish remoteURL : repository.getURIs()) { if (UriHelper.areSameGitRepo(uri, remoteURL)) { repositoryMatches = true; break; } } // Jobs triggered by PR merge need to check whether its target branch matches the one specified in the parameter of PR trigger UI if (repositoryMatches && gitCodePushedEventArgs instanceof PullRequestMergeCommitCreatedEventArgs) { GitStatus.ResponseContributor triggerResult = triggerJob(gitCodePushedEventArgs, actions, bypassPolling, project, scmTriggerItem, true, false); if (triggerResult != null) { result.add(triggerResult); } break; } boolean branchMatches = false; // Jobs triggered by code push need to check whether its target branch matches the one in its Git parameter for (final BranchSpec branch : git.getBranches()) { final String branchString = branch.toString(); // Might be in the form of */master final String[] items = branchString.split("/"); final String branchName = items[items.length - 1]; if (branchName.equalsIgnoreCase(TRIGGER_ANY_BRANCH) || branchName.equalsIgnoreCase(gitCodePushedEventArgs.targetBranch)) { branchMatches = true; totalBranchMatches++; break; } } if (!repositoryMatches || !branchMatches || git.getExtensions().get(IgnoreNotifyCommit.class) != null) { continue; } GitStatus.ResponseContributor triggerResult = triggerJob(gitCodePushedEventArgs, actions, bypassPolling, project, scmTriggerItem, true, true); if (triggerResult != null) { result.add(triggerResult); break; } } } } if (!scmFound) { result.add(new GitStatus.MessageResponseContributor("No Git jobs found")); } else if (totalBranchMatches == 0) { final String template = "No Git jobs matched the remote URL '%s' requested by an event."; final String message = String.format(template, uri); LOGGER.warning(message); } return result; } finally { SecurityContextHolder.setContext(old); } } private ArrayList getDefaultParameters(final Job job) { ArrayList values = new ArrayList(); ParametersDefinitionProperty pdp = job.getProperty(ParametersDefinitionProperty.class); if (pdp != null) { for (ParameterDefinition pd : pdp.getParameterDefinitions()) { if (pd.getName().equals("sha1")) { continue; } values.add(pd.getDefaultParameterValue()); } } return values; } private String getVstsRefspec(final GitCodePushedEventArgs gitCodePushedEventArgs) { if (gitCodePushedEventArgs instanceof PullRequestMergeCommitCreatedEventArgs) { int prId = ((PullRequestMergeCommitCreatedEventArgs) gitCodePushedEventArgs).pullRequestId; return String.format("+refs/pull/%d/merge:refs/remotes/pull/%d/merge", prId, prId); } else { return "+refs/heads/*:refs/remotes/origin/*"; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/AliasOnlyUserAccountMapper.java ================================================ package hudson.plugins.tfs.model; import hudson.Extension; import org.kohsuke.stapler.DataBoundConstructor; /** * Maps user accounts without using the DOMAIN. */ public class AliasOnlyUserAccountMapper extends UserAccountMapper { private static final long serialVersionUID = 1L; /** * Constructor. */ @DataBoundConstructor public AliasOnlyUserAccountMapper() { } @Override public String mapUserAccount(final String input) { final String[] split = input.split("\\\\"); final String result; if (split.length == 2) { result = split[1]; } else { result = input; } return result; } /** * A descriptor for the class. */ @Extension public static final class DescriptorImpl extends UserAccountMapperDescriptor { @Override public String getDisplayName() { return "Resolve user using 'alias' only, removing 'DOMAIN\\'"; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/AutomaticCredentialsConfigurer.java ================================================ package hudson.plugins.tfs.model; import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; import hudson.Extension; import hudson.plugins.tfs.TeamCollectionConfiguration; import org.kohsuke.stapler.DataBoundConstructor; import java.net.URI; /** * Finds credentials in the TeamCollectionConfiguration. */ public class AutomaticCredentialsConfigurer extends CredentialsConfigurer { private static final long serialVersionUID = 1L; /** * Constructor for use with data binding. */ @DataBoundConstructor public AutomaticCredentialsConfigurer() { } @Override public StandardUsernamePasswordCredentials getCredentials(final String collectionUriString) { final URI collectionUri = URI.create(collectionUriString); return TeamCollectionConfiguration.findCredentialsForCollection(collectionUri); } /** * Class descriptor. */ @Extension public static final class DescriptorImpl extends CredentialsConfigurerDescriptor { @Override public String getDisplayName() { return "Automatic"; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/BuildCommand.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.fasterxml.jackson.databind.ObjectMapper; import com.microsoft.teamfoundation.sourcecontrol.webapi.model.GitPush; import hudson.model.Action; import hudson.model.BuildableItem; import hudson.model.Cause; import hudson.model.CauseAction; import hudson.model.Executor; import hudson.model.Job; import hudson.model.ParameterDefinition; import hudson.model.ParameterValue; import hudson.model.ParametersAction; import hudson.model.ParametersDefinitionProperty; import hudson.model.Queue; import hudson.model.Run; import hudson.model.SimpleParameterDefinition; import hudson.model.queue.ScheduleResult; import hudson.plugins.tfs.CommitParameterAction; import hudson.plugins.tfs.PullRequestParameterAction; import hudson.plugins.tfs.TeamBuildDetailsAction; import hudson.plugins.tfs.TeamBuildEndpoint; import hudson.plugins.tfs.TeamGlobalStatusAction; import hudson.plugins.tfs.TeamPullRequestMergedDetailsAction; import hudson.plugins.tfs.UnsupportedIntegrationAction; import hudson.plugins.tfs.model.servicehooks.Event; import hudson.plugins.tfs.util.ActionHelper; import hudson.plugins.tfs.util.MediaType; import hudson.util.RunList; import jenkins.model.Jenkins; import jenkins.util.TimeDuration; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.StaplerRequest; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.logging.Logger; public class BuildCommand extends AbstractCommand { private static final Logger LOGGER = Logger.getLogger(BuildCommand.class.getName()); private static final String BUILD_REPOSITORY_PROVIDER = "Build.Repository.Provider"; private static final String BUILD_REPOSITORY_URI = "Build.Repository.Uri"; private static final String BUILD_REPOSITORY_NAME = "Build.Repository.Name"; private static final String SYSTEM_TEAM_PROJECT = "System.TeamProject"; private static final String BUILD_SOURCE_VERSION = "Build.SourceVersion"; private static final String BUILD_REQUESTED_FOR = "Build.RequestedFor"; private static final String SYSTEM_TEAM_FOUNDATION_COLLECTION_URI = "System.TeamFoundationCollectionUri"; private static final String COMMIT_ID = "commitId"; private static final String PULL_REQUEST_ID = "pullRequestId"; private static final String UNSUPPORTED_TEMPLATE = "The rich integration with TFS/Team Services is not supported. Reason: %s"; public static String formatUnsupportedReason(final String reason) { return String.format(UNSUPPORTED_TEMPLATE, reason); } public static class Factory implements AbstractCommand.Factory { @Override public AbstractCommand create() { return new BuildCommand(); } @Override public String getSampleRequestPayload() { final Class me = this.getClass(); final InputStream stream = me.getResourceAsStream("BuildCommand.json"); try { return IOUtils.toString(stream, MediaType.UTF_8); } catch (final IOException e) { throw new Error(e); } finally { IOUtils.closeQuietly(stream); } } } protected JSONObject innerPerform(final Job job, final BuildableItem buildableItem, final TimeDuration delay, final List extraActions) { final JSONObject result = new JSONObject(); final Jenkins jenkins = Jenkins.getActiveInstance(); final Queue queue = jenkins.getQueue(); final Cause cause = new Cause.UserIdCause(); final CauseAction causeAction = new CauseAction(cause); final Action[] actionArray = ActionHelper.create(extraActions, causeAction); for (Action a : extraActions) { if (a instanceof TeamPullRequestMergedDetailsAction) { cancelPreviousPullRequestBuilds(job, (TeamPullRequestMergedDetailsAction) a, queue); } } final ScheduleResult scheduleResult = queue.schedule2(buildableItem, delay.getTime(), actionArray); final Queue.Item item = scheduleResult.getItem(); if (item != null) { result.put("created", jenkins.getRootUrl() + item.getUrl()); } return result; } @Override public JSONObject perform(final Job job, final BuildableItem buildableItem, final StaplerRequest req, final JSONObject requestPayload, final ObjectMapper mapper, final TeamBuildPayload teamBuildPayload, final TimeDuration delay) { // These values are for optional parameters of the same name, for the git.pullrequest.merged event String commitId = null; String pullRequestId = null; final List actions = new ArrayList(); if (teamBuildPayload.BuildVariables != null) { contributeTeamBuildParameterActions(teamBuildPayload.BuildVariables, actions); } else if (teamBuildPayload.ServiceHookEvent != null) { final Event event = teamBuildPayload.ServiceHookEvent; final String eventType = event.getEventType(); final Object resource = event.getResource(); if ("git.push".equals(eventType)) { final GitPush gitPush = mapper.convertValue(resource, GitPush.class); final GitCodePushedEventArgs args = GitPushEvent.decodeGitPush(gitPush, event); final Action action = new CommitParameterAction(args); actions.add(action); TeamGlobalStatusAction.addIfApplicable(actions); } else if ("git.pullrequest.merged".equals(eventType)) { final GitPullRequestEx gitPullRequest = mapper.convertValue(resource, GitPullRequestEx.class); final PullRequestMergeCommitCreatedEventArgs args = GitPullRequestMergedEvent.decodeGitPullRequest(gitPullRequest, event); // record the values for the special optional parameters commitId = args.commit; pullRequestId = Integer.toString(args.pullRequestId, 10); final Action action = new PullRequestParameterAction(args); actions.add(action); final String message = event.getMessage().getText(); final String detailedMessage = event.getDetailedMessage().getText(); final Action teamPullRequestMergedDetailsAction = new TeamPullRequestMergedDetailsAction(gitPullRequest, message, detailedMessage, args.collectionUri.toString()); actions.add(teamPullRequestMergedDetailsAction); TeamGlobalStatusAction.addIfApplicable(actions); } } final ParametersDefinitionProperty pp = job.getProperty(ParametersDefinitionProperty.class); if (pp != null && requestPayload.containsKey(TeamBuildEndpoint.PARAMETER)) { final List values = new ArrayList(); final JSONArray a = requestPayload.getJSONArray(TeamBuildEndpoint.PARAMETER); final List parameterNames = new ArrayList<>(); for (final Object o : a) { final JSONObject jo = (JSONObject) o; final String name = jo.getString("name"); parameterNames.add(name); final ParameterDefinition d = pp.getParameterDefinition(name); if (d == null) throw new IllegalArgumentException("No such parameter definition: " + name); final ParameterValue parameterValue; // commitId and pullRequestId are special and override any user-provided value // when the team-event's eventType was "git.pullrequest.merged" if (name.equals(COMMIT_ID) && commitId != null && d instanceof SimpleParameterDefinition) { final SimpleParameterDefinition spd = (SimpleParameterDefinition) d; parameterValue = spd.createValue(commitId); // erase value to avoid adding it a second time commitId = null; } else if (name.equals(PULL_REQUEST_ID) && pullRequestId != null && d instanceof SimpleParameterDefinition) { final SimpleParameterDefinition spd = (SimpleParameterDefinition) d; parameterValue = spd.createValue(pullRequestId); // erase value to avoid adding it a second time pullRequestId = null; } else { parameterValue = d.createValue(req, jo); } if (parameterValue != null) { values.add(parameterValue); } else { throw new IllegalArgumentException("Cannot retrieve the parameter value: " + name); } } //Pick up default build parameters that have not been overridden for(final ParameterDefinition paramDef : pp.getParameterDefinitions()) { if(!parameterNames.contains(paramDef.getName()) && paramDef instanceof SimpleParameterDefinition){ values.add(paramDef.getDefaultParameterValue()); } } // typical case: set optional "git.pullrequest.merged" parameters if (commitId != null) { final ParameterDefinition d = pp.getParameterDefinition(COMMIT_ID); if (d != null && d instanceof SimpleParameterDefinition) { final SimpleParameterDefinition spd = (SimpleParameterDefinition) d; final ParameterValue parameterValue = spd.createValue(commitId); values.add(parameterValue); } } if (pullRequestId != null) { final ParameterDefinition d = pp.getParameterDefinition(PULL_REQUEST_ID); if (d != null && d instanceof SimpleParameterDefinition) { final SimpleParameterDefinition spd = (SimpleParameterDefinition) d; final ParameterValue parameterValue = spd.createValue(pullRequestId); values.add(parameterValue); } } final ParametersAction action = new ParametersAction(values); actions.add(action); } return innerPerform(job, buildableItem, delay, actions); } private void cancelPreviousPullRequestBuilds(Job job, TeamPullRequestMergedDetailsAction pullReqeuestMergedDetails, Queue queue) { RunList allBuilds = job.getBuilds(); for (Run run : allBuilds) { TeamPullRequestMergedDetailsAction cause = run.getAction(TeamPullRequestMergedDetailsAction.class); if (cause != null && run.isBuilding()) { if (cause instanceof TeamPullRequestMergedDetailsAction && cause.gitPullRequest.getPullRequestId() == pullReqeuestMergedDetails.gitPullRequest.getPullRequestId()) { LOGGER.info("Canceling previously triggered Job: " + run.getFullDisplayName()); Executor executor = run.getExecutor(); if (executor != null) executor.doStop(); Queue.Item item = queue.getItem(run.getQueueId()); if (item != null) queue.cancel(item); } } } } static void contributeTeamBuildParameterActions(final Map teamBuildParameters, final List actions) { final Action teamBuildDetails = new TeamBuildDetailsAction(teamBuildParameters); actions.add(teamBuildDetails); if (teamBuildParameters.containsKey(BUILD_REPOSITORY_PROVIDER)) { final String provider = teamBuildParameters.get(BUILD_REPOSITORY_PROVIDER); final boolean isTeamGit = "TfGit".equalsIgnoreCase(provider) || "TfsGit".equalsIgnoreCase(provider); if (isTeamGit) { // "Build.Repository.Uri" is null/whitespace/empty when the 'Jenkins Queue Job' task runs in TFS Release Management. // In this case, do not reference a CommitParameterAction in the actions reported as unsupported. final String repoUriString = teamBuildParameters.get(BUILD_REPOSITORY_URI); if (StringUtils.isNotBlank(repoUriString)) { final URI repoUri = URI.create(repoUriString); final String collectionUriString = teamBuildParameters.get(SYSTEM_TEAM_FOUNDATION_COLLECTION_URI); final URI collectionUri = URI.create(collectionUriString); final String projectId = teamBuildParameters.get(SYSTEM_TEAM_PROJECT); final String repoId = teamBuildParameters.get(BUILD_REPOSITORY_NAME); final String commit = teamBuildParameters.get(BUILD_SOURCE_VERSION); final String pushedBy = teamBuildParameters.get(BUILD_REQUESTED_FOR); final GitCodePushedEventArgs args = new GitCodePushedEventArgs(); args.collectionUri = collectionUri; args.repoUri = repoUri; args.projectId = projectId; args.repoId = repoId; args.commit = commit; args.pushedBy = pushedBy; final CommitParameterAction action = new CommitParameterAction(args); actions.add(action); } UnsupportedIntegrationAction.addToBuild(actions, "Posting build status is not supported for builds triggered by the 'Jenkins Queue Job' task."); } else { final String reason = String.format( "The '%s' build variable has a value of '%s', which is not supported.", BUILD_REPOSITORY_PROVIDER, provider); UnsupportedIntegrationAction.addToBuild(actions, reason); LOGGER.warning(formatUnsupportedReason(reason)); } } else { final String reason = String.format( "There was no value provided for the '%s' build variable.", BUILD_REPOSITORY_PROVIDER); UnsupportedIntegrationAction.addToBuild(actions, reason); LOGGER.warning(formatUnsupportedReason(reason)); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/BuildParameter.java ================================================ package hudson.plugins.tfs.model; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** * Used by TFS Build payload. */ @SuppressFBWarnings(value = "UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD", justification = "Used by TeamBuildPayload") public class BuildParameter { // Turn off checkstyle for the public fields in this class. //CHECKSTYLE:OFF public String name; public String value; //CHECKSTYLE:ON } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/BuildWithParametersCommand.java ================================================ package hudson.plugins.tfs.model; import hudson.plugins.tfs.util.MediaType; import org.apache.commons.io.IOUtils; import java.io.IOException; import java.io.InputStream; /** * Used for builds with parameters. */ public class BuildWithParametersCommand extends BuildCommand { /** * Factory for creating BuildWithParametersCommand instances. */ public static class Factory implements AbstractCommand.Factory { @Override public AbstractCommand create() { return new BuildWithParametersCommand(); } @Override public String getSampleRequestPayload() { final Class me = this.getClass(); final InputStream stream = me.getResourceAsStream("BuildWithParametersCommand.json"); try { return IOUtils.toString(stream, MediaType.UTF_8); } catch (final IOException e) { throw new Error(e); } finally { IOUtils.closeQuietly(stream); } } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/ChangeLogSet.java ================================================ package hudson.plugins.tfs.model; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.model.AbstractBuild; import hudson.model.Run; import hudson.scm.RepositoryBrowser; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * ChangeLogSet for the Team Foundation Server SCM * The log set will set the parent of the log entries in the constructor. * * @author Erik Ramfelt */ @SuppressFBWarnings(value = "NM_SAME_SIMPLE_NAME_AS_SUPERCLASS", justification = "Public so shouldn't be changed") public class ChangeLogSet extends hudson.scm.ChangeLogSet { private final List changesets; public ChangeLogSet(final Run build, final RepositoryBrowser browser, final List changesets) { super(build, browser); this.changesets = changesets; for (ChangeSet changeset : changesets) { changeset.setParent(this); } } @Deprecated /* TODO: Used by TeamSystemWebAccessBrowserTest, should update to use non-deprecated method instead */ public ChangeLogSet(final AbstractBuild build, final ChangeSet[] changesetArray) { this(build, build.getProject().getScm().getEffectiveBrowser(), changesetArray); } public ChangeLogSet(final Run build, final RepositoryBrowser browser, final ChangeSet[] changesetArray) { super(build, browser); changesets = new ArrayList(); for (ChangeSet changeset : changesetArray) { changeset.setParent(this); changesets.add(changeset); } } @Override public boolean isEmptySet() { return changesets.isEmpty(); } /** Returns a ChangeSet iterator. */ public Iterator iterator() { return changesets.iterator(); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/ChangeSet.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; import hudson.model.User; import hudson.plugins.tfs.util.DateUtil; import hudson.scm.EditType; @ExportedBean(defaultVisibility=999) public class ChangeSet extends hudson.scm.ChangeLogSet.Entry { private User authorUser; private User checkedInByUser; private String version; private String userString; private String domain; private Date date; private String comment; private List items; private String checkedInByUserString; public ChangeSet() { this("", null, "", ""); } public ChangeSet(String version, Date date, String userString, String comment) { this.version = version; this.date = (date != null) ? new Date(date.getTime()) : null; this.comment = comment; items = new ArrayList<>(); setUser(userString); } public ChangeSet(String version, Date date, User author, String comment) { this.version = version; this.date = (date != null) ? new Date(date.getTime()) : null; this.authorUser = author; this.userString = author.getId(); this.comment = comment; items = new ArrayList<>(); } @Override public Collection getAffectedPaths() { Collection paths = new ArrayList(items.size()); for (Item item : items) { paths.add(item.getPath()); } return paths; } @Override public Collection getAffectedFiles() { return items; } @Override public ChangeLogSet getParent() { return (ChangeLogSet)super.getParent(); } @Override public User getAuthor() { if(authorUser == null) { authorUser = User.get(userString); } return authorUser; } @Override public String getMsg() { return comment; } @Exported public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } /** * Returns a human readable display name of the changeset number. * *

* This method is primarily intended for visualization of the data. */ @Exported public String getCommitId() { return version; } @Exported public String getDomain() { return domain; } @Exported public String getUser() { return userString; } public void setUser(String user) { String[] split = user.split("\\\\"); if (split.length == 2) { this.domain = split[0]; this.userString = split[1]; } else { this.userString = user; this.domain = null; } } @Exported public Date getDate() { if (date != null) { return new Date(date.getTime()); } return null; } public void setDateStr(String dateStr) throws ParseException { date = DateUtil.TFS_DATETIME_FORMATTER.get().parse(dateStr); } @Exported public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public void setCheckedInBy(String checkedInByUserString) { if (checkedInByUserString != null) { String[] split = checkedInByUserString.split("\\\\"); if (split.length == 2) { this.checkedInByUserString = split[1]; } else { this.checkedInByUserString = checkedInByUserString; } } } public String getCheckedInBy() { return checkedInByUserString; } public User getCheckedInByUser() { if (checkedInByUser == null) { checkedInByUser = User.get(checkedInByUserString); } return checkedInByUser; } public void setCheckedInByUser(User checkedInBy) { this.checkedInByUser = checkedInBy; } @Exported public List getItems() { return items; } public void add(ChangeSet.Item item) { items.add(item); item.setParent(this); } @Override protected void setParent(hudson.scm.ChangeLogSet parent) { super.setParent(parent); } @ExportedBean(defaultVisibility=999) public static class Item implements hudson.scm.ChangeLogSet.AffectedFile { private String path; private String action; private ChangeSet parent; public Item() { this("",""); } public Item(String path, String action) { this.path = path; this.action = action; } public ChangeSet getParent() { return parent; } void setParent(ChangeSet parent) { this.parent = parent; } @Exported public String getPath() { return path; } public void setPath(String path) { this.path = path; } @Exported public String getAction() { return action; } public void setAction(String action) { this.action = action; } @Exported public EditType getEditType() { if (action.equalsIgnoreCase("delete")) { return EditType.DELETE; } if (action.equalsIgnoreCase("add")) { return EditType.ADD; } return EditType.EDIT; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/ClonePersistenceStoreProvider.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.microsoft.tfs.core.config.persistence.PersistenceStoreProvider; import com.microsoft.tfs.core.persistence.FilesystemPersistenceStore; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; public class ClonePersistenceStoreProvider implements PersistenceStoreProvider { private final FilesystemPersistenceStore cacheStore; private final FilesystemPersistenceStore configurationStore; private final FilesystemPersistenceStore logStore; private final String hostName; public ClonePersistenceStoreProvider(final PersistenceStoreProvider sourcePersistenceStoreProvider, final String hostName) { this.hostName = hostName; final FilesystemPersistenceStore sourceCache = sourcePersistenceStoreProvider.getCachePersistenceStore(); final File cacheFolder = createAndCopy(sourceCache, hostName); this.cacheStore = new FilesystemPersistenceStore(cacheFolder); final FilesystemPersistenceStore sourceConfiguration = sourcePersistenceStoreProvider.getConfigurationPersistenceStore(); final File configurationFolder = createAndCopy(sourceConfiguration, hostName); this.configurationStore = new FilesystemPersistenceStore(configurationFolder); final FilesystemPersistenceStore sourceLog = sourcePersistenceStoreProvider.getLogPersistenceStore(); final File logFolder = createAndCopy(sourceLog, hostName); this.logStore = new FilesystemPersistenceStore(logFolder); } static File createAndCopy(final FilesystemPersistenceStore sourceStore, final String nodeName) { final File sourceBase = sourceStore.getStoreFile(); final String childName = sourceBase.getName(); final File sourceParent = sourceBase.getParentFile(); final File destinationBase = new File(sourceParent, nodeName); final File destination = new File(destinationBase, childName); if (!destination.isDirectory() && sourceBase.isDirectory()) { try { FileUtils.copyDirectory(sourceBase, destination); } catch (final IOException e) { throw new Error(e); } } return destination; } public FilesystemPersistenceStore getCachePersistenceStore() { return cacheStore; } public FilesystemPersistenceStore getConfigurationPersistenceStore() { return configurationStore; } public FilesystemPersistenceStore getLogPersistenceStore() { return logStore; } public String getHostName() { return hostName; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/ConnectHookEvent.java ================================================ package hudson.plugins.tfs.model; import com.fasterxml.jackson.databind.ObjectMapper; import hudson.plugins.tfs.TeamCollectionConfiguration; import hudson.plugins.tfs.TeamPluginGlobalConfig; import hudson.plugins.tfs.model.servicehooks.Event; import net.sf.json.JSONObject; import java.net.URI; /** * This HookEvent is for the TeamEventsEndpoint "Connect" event. */ public class ConnectHookEvent extends AbstractHookEvent { /** * Factory to create ConnectHookEvent. */ public static class Factory implements AbstractHookEvent.Factory { @Override public AbstractHookEvent create() { return new ConnectHookEvent(); } @Override public String getSampleRequestPayload() { return "{\n" + " \"eventType\": \"connect\",\n" + " \"resource\":\n" + " {\n" + " \"teamCollectionUrl\": \"https://xplatalm.visualstudio.com\"\n" + " \"connectionKey\": \"MyJenkinsServer\"\n" + " \"connectionSignature\": \"ABC13ABC123ABC13ABC123ABC13ABC123\"\n" + " \"sendJobCompletionEvents\": true\n" + " }\n" + "}"; } } @Override public JSONObject perform(final ObjectMapper mapper, final Event serviceHookEvent, final String message, final String detailedMessage) { final Object resource = serviceHookEvent.getResource(); final ConnectionParameters parameters = mapper.convertValue(resource, ConnectionParameters.class); //TODO permissions? // Store the key and the information for the correct collection final TeamCollectionConfiguration collection = TeamCollectionConfiguration.findCollection(URI.create(parameters.getTeamCollectionUrl())); if (collection != null) { // Store key and signature with collection collection.getConnectionParameters().setConnectionKey(parameters.getConnectionKey()); collection.getConnectionParameters().setConnectionSignature(parameters.getConnectionSignature()); collection.getConnectionParameters().setSendJobCompletionEvents(parameters.isSendJobCompletionEvents()); collection.getConnectionParameters().setTeamCollectionUrl(parameters.getTeamCollectionUrl()); // Save collection info final TeamPluginGlobalConfig config = TeamPluginGlobalConfig.get(); config.save(); } else { throw new IllegalArgumentException("Unable to connect to unknown server: " + parameters.getTeamCollectionUrl()); } return JSONObject.fromObject(serviceHookEvent); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/ConnectionParameters.java ================================================ package hudson.plugins.tfs.model; /** * These parameters are stored and modified from outside Jenkins through the TeamEventsEndpoint "connect" event. * An instance of this class is owned by every TeamCollectionConfiguration instance. */ public class ConnectionParameters { private String teamCollectionUrl; private String connectionKey; private String connectionSignature; private boolean sendJobCompletionEvents; /** * Constructor. */ public ConnectionParameters() { } public String getConnectionKey() { return connectionKey; } public void setConnectionKey(final String connectionKey) { this.connectionKey = connectionKey; } public String getConnectionSignature() { return connectionSignature; } public void setConnectionSignature(final String connectionSignature) { this.connectionSignature = connectionSignature; } public String getTeamCollectionUrl() { return teamCollectionUrl; } public void setTeamCollectionUrl(final String teamCollectionUrl) { this.teamCollectionUrl = teamCollectionUrl; } public boolean isSendJobCompletionEvents() { return sendJobCompletionEvents; } public void setSendJobCompletionEvents(final boolean sendJobCompletionEvents) { this.sendJobCompletionEvents = sendJobCompletionEvents; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/CredentialsConfigurer.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; import hudson.DescriptorExtensionList; import hudson.ExtensionPoint; import hudson.model.Describable; import jenkins.model.Jenkins; import java.io.Serializable; public abstract class CredentialsConfigurer implements ExtensionPoint, Describable, Serializable { private static final long serialVersionUID = 1L; public final String getDisplayName() { return getDescriptor().getDisplayName(); } public CredentialsConfigurerDescriptor getDescriptor() { final Jenkins jenkins = Jenkins.getActiveInstance(); return (CredentialsConfigurerDescriptor) jenkins.getDescriptorOrDie(getClass()); } public abstract StandardUsernamePasswordCredentials getCredentials(final String collectionUri); public static DescriptorExtensionList all() { final Jenkins jenkins = Jenkins.getActiveInstance(); return jenkins.getDescriptorList(CredentialsConfigurer.class); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/CredentialsConfigurerDescriptor.java ================================================ package hudson.plugins.tfs.model; import hudson.model.Descriptor; /** * Descriptor for CredentialsConfigurer. */ public abstract class CredentialsConfigurerDescriptor extends Descriptor { } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/DomainUserAccountMapper.java ================================================ package hudson.plugins.tfs.model; import hudson.Extension; import org.kohsuke.stapler.DataBoundConstructor; /** * Mapper for Domain User accounts. */ public class DomainUserAccountMapper extends UserAccountMapper { private static final long serialVersionUID = 1L; /** * Constructor for data binding. */ @DataBoundConstructor public DomainUserAccountMapper() { } @Override public String mapUserAccount(final String input) { return input; } /** * Class descriptor. */ @Extension public static final class DescriptorImpl extends UserAccountMapperDescriptor { @Override public String getDisplayName() { return "Resolve user using 'DOMAIN\\alias'"; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/ExtraSettings.java ================================================ package hudson.plugins.tfs.model; import hudson.model.Computer; import hudson.plugins.tfs.TeamPluginGlobalConfig; import jenkins.model.Jenkins; import java.io.Serializable; /** * This class exists to shuttle settings between MASTER to remote nodes, * who would otherwise be unable to determine said settings because they * don't have access to the {@link jenkins.model.Jenkins} instance or * anything that could be obtained from it. */ public class ExtraSettings implements Serializable { private static final long serialVersionUID = 1L; private boolean configFolderPerNode; private String nodeComputerName; public static final ExtraSettings DEFAULT = new ExtraSettings(); @SuppressWarnings("unused" /* Needed by Serializable interface */) private ExtraSettings() { } /** * Construtor. * @param teamPluginGlobalConfig */ public ExtraSettings(final TeamPluginGlobalConfig teamPluginGlobalConfig) { if (teamPluginGlobalConfig != null) { this.configFolderPerNode = teamPluginGlobalConfig.isConfigFolderPerNode(); final Jenkins instance = Jenkins.getInstance(); this.nodeComputerName = ""; if (instance != null) { final Computer currentComputer = Computer.currentComputer(); if (currentComputer != null) { this.nodeComputerName = currentComputer.getName(); } } } else { this.configFolderPerNode = false; this.nodeComputerName = null; } } public boolean isConfigFolderPerNode() { return configFolderPerNode; } public void setConfigFolderPerNode(final boolean configFolderPerNode) { this.configFolderPerNode = configFolderPerNode; } public String getNodeComputerName() { return nodeComputerName; } public void setNodeComputerName(final String nodeComputerName) { this.nodeComputerName = nodeComputerName; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/GitCodePushedEventArgs.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import org.eclipse.jgit.transport.URIish; import java.io.Serializable; import java.net.URI; import java.net.URISyntaxException; public class GitCodePushedEventArgs implements Serializable { private static final long serialVersionUID = 1L; public URI collectionUri; public URI repoUri; public String projectId; public String repoId; public String commit; public String pushedBy; public String targetBranch; public URIish getRepoURIish() { final String repoUriString = repoUri.toString(); try { return new URIish(repoUriString); } catch (final URISyntaxException e) { // shouldn't happen throw new Error(e); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/GitPullRequestEx.java ================================================ package hudson.plugins.tfs.model; import com.microsoft.teamfoundation.sourcecontrol.webapi.model.GitPullRequest; import com.microsoft.visualstudio.services.webapi.model.ResourceRef; import java.util.Arrays; /** * Work around some missing fields in the version of vso-httpclient-java * by extending {@link GitPullRequest}. */ public class GitPullRequestEx extends GitPullRequest { private ResourceRef[] workItemRefs; /** * Returns the work item references. */ public ResourceRef[] getWorkItemRefs() { if (workItemRefs != null) { return Arrays.copyOf(workItemRefs, workItemRefs.length); } return null; } /** * Sets the work item references. */ public void setWorkItemRefs(final ResourceRef[] workItemRefs) { this.workItemRefs = (workItemRefs != null) ? Arrays.copyOf(workItemRefs, workItemRefs.length) : null; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/GitPullRequestMergedEvent.java ================================================ package hudson.plugins.tfs.model; import com.fasterxml.jackson.databind.ObjectMapper; import com.microsoft.teamfoundation.sourcecontrol.webapi.model.GitCommitRef; import com.microsoft.teamfoundation.sourcecontrol.webapi.model.GitPullRequest; import com.microsoft.teamfoundation.sourcecontrol.webapi.model.GitRepository; import com.microsoft.visualstudio.services.webapi.model.IdentityRef; import hudson.model.Action; import hudson.plugins.git.GitStatus; import hudson.plugins.tfs.PullRequestParameterAction; import hudson.plugins.tfs.model.servicehooks.Event; import hudson.plugins.tfs.util.ResourceHelper; import hudson.plugins.tfs.TeamPullRequestMergedDetailsAction; import net.sf.json.JSONObject; import java.net.URI; import java.util.ArrayList; import java.util.List; /** * A Git push event corresponding to the merge of a pull request on VSTS/TFS. */ public class GitPullRequestMergedEvent extends GitPushEvent { /** * Factory for creating a GitPullRequestMergedEvent. */ public static class Factory implements AbstractHookEvent.Factory { @Override public AbstractHookEvent create() { return new GitPullRequestMergedEvent(); } @Override public String getSampleRequestPayload() { return ResourceHelper.fetchAsString(this.getClass(), "GitPullRequestMergedEvent.json"); } } static String determineCreatedBy(final GitPullRequest gitPullRequest) { final IdentityRef createdBy = gitPullRequest.getCreatedBy(); final String result = createdBy.getDisplayName(); return result; } /* Given the following sample payload fragment: "lastMergeSourceCommit": { "commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c", "url": "https://fabrikam.visualstudio.com/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c" }, "lastMergeTargetCommit": { "commitId": "a511f535b1ea495ee0c903badb68fbc83772c882", "url": "https://fabrikam.visualstudio.com/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/a511f535b1ea495ee0c903badb68fbc83772c882" }, "lastMergeCommit": { "commitId": "eef717f69257a6333f221566c1c987dc94cc0d72", "url": "https://fabrikam.visualstudio.com/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72" }, "commits": [ { "commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c", "url": "https://fabrikam.visualstudio.com/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c" } ], ...we are assuming the user pushed `53d54a` (lastMergeSourceCommit) and Team Services attempted to merge it with `a511f5` (the tip of whatever the branch the PR is targeting, lastMergeTargetCommit), yielding `eef717f`. */ static String determineMergeCommit(final GitPullRequest gitPullRequest) { final GitCommitRef lastMergeCommit = gitPullRequest.getLastMergeCommit(); final String result = lastMergeCommit.getCommitId(); return result; } static String determineTargetBranch(final GitPullRequest gitPullRequest) { // In the form of ref/heads/master final String targetRefName = gitPullRequest.getTargetRefName(); String[] items = targetRefName.split("/"); return items[items.length - 1]; } @Override public JSONObject perform(final ObjectMapper mapper, final Event serviceHookEvent, final String message, final String detailedMessage) { final Object resource = serviceHookEvent.getResource(); final GitPullRequestEx gitPullRequest = mapper.convertValue(resource, GitPullRequestEx.class); final PullRequestMergeCommitCreatedEventArgs args = decodeGitPullRequest(gitPullRequest, serviceHookEvent); final PullRequestParameterAction parameterAction = new PullRequestParameterAction(args); final Action teamPullRequestMergedDetailsAction = new TeamPullRequestMergedDetailsAction(gitPullRequest, message, detailedMessage, args.collectionUri.toString()); final ArrayList actions = new ArrayList(); actions.add(parameterAction); actions.add(teamPullRequestMergedDetailsAction); final List contributors = pollOrQueueFromEvent(args, actions, true); final JSONObject response = fromResponseContributors(contributors); return response; } static PullRequestMergeCommitCreatedEventArgs decodeGitPullRequest(final GitPullRequest gitPullRequest, final Event serviceHookEvent) { final GitRepository repository = gitPullRequest.getRepository(); final URI collectionUri = determineCollectionUri(repository, serviceHookEvent); final String repoUriString = repository.getRemoteUrl(); final URI repoUri = URI.create(repoUriString); final String projectId = determineProjectId(repository); final String repoId = repository.getName(); final String commit = determineMergeCommit(gitPullRequest); final String pushedBy = determineCreatedBy(gitPullRequest); final int pullRequestId = gitPullRequest.getPullRequestId(); final String targetBranch = determineTargetBranch(gitPullRequest); final PullRequestMergeCommitCreatedEventArgs args = new PullRequestMergeCommitCreatedEventArgs(); args.collectionUri = collectionUri; args.repoUri = repoUri; args.projectId = projectId; args.repoId = repoId; args.commit = commit; args.pushedBy = pushedBy; args.pullRequestId = pullRequestId; args.targetBranch = targetBranch; return args; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/GitPushEvent.java ================================================ package hudson.plugins.tfs.model; import com.fasterxml.jackson.databind.ObjectMapper; import com.microsoft.teamfoundation.core.webapi.model.TeamProjectReference; import com.microsoft.teamfoundation.sourcecontrol.webapi.model.GitCommitRef; import com.microsoft.teamfoundation.sourcecontrol.webapi.model.GitPush; import com.microsoft.teamfoundation.sourcecontrol.webapi.model.GitRepository; import com.microsoft.visualstudio.services.webapi.model.IdentityRef; import hudson.model.Action; import hudson.plugins.git.GitStatus; import hudson.plugins.tfs.CommitParameterAction; import hudson.plugins.tfs.model.servicehooks.Event; import hudson.plugins.tfs.model.servicehooks.ResourceContainer; import hudson.plugins.tfs.util.ResourceHelper; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * A Git push event corresponding to the push of a set of commits on VSTS/TFS. */ public class GitPushEvent extends AbstractHookEvent { /** * Factory for creating a GitPushEvent. */ public static class Factory implements AbstractHookEvent.Factory { @Override public AbstractHookEvent create() { return new GitPushEvent(); } @Override public String getSampleRequestPayload() { return ResourceHelper.fetchAsString(this.getClass(), "GitPushEvent.json"); } } @Override public JSONObject perform(final ObjectMapper mapper, final Event serviceHookEvent, final String message, final String detailedMessage) { final Object resource = serviceHookEvent.getResource(); final GitPush gitPush = mapper.convertValue(resource, GitPush.class); final GitCodePushedEventArgs args = decodeGitPush(gitPush, serviceHookEvent); final CommitParameterAction parameterAction = new CommitParameterAction(args); final ArrayList actions = new ArrayList(); actions.add(parameterAction); final List contributors = pollOrQueueFromEvent(args, actions, false); final JSONObject response = fromResponseContributors(contributors); return response; } static URI determineCollectionUri(final URI repoApiUri) { final String path = repoApiUri.getPath(); final int i = path.indexOf("_apis/"); if (i == -1) { final String template = "Repository url '%s' did not contain '_apis/'."; throw new IllegalArgumentException(String.format(template, repoApiUri)); } final String pathBeforeApis = path.substring(0, i); final URI uri; try { uri = new URI(repoApiUri.getScheme(), repoApiUri.getAuthority(), pathBeforeApis, repoApiUri.getQuery(), repoApiUri.getFragment()); } catch (final URISyntaxException e) { throw new Error(e); } return uri; } static URI determineCollectionUri(final GitRepository repository, final Event serviceHookEvent) { URI result = null; final Map containers = serviceHookEvent.getResourceContainers(); if (containers != null) { final String collection = "collection"; if (containers.containsKey(collection)) { final ResourceContainer collectionContainer = containers.get(collection); final String baseUrl = collectionContainer.getBaseUrl(); if (StringUtils.isNotEmpty(baseUrl)) { result = URI.create(baseUrl); } } } if (result == null) { final String repoApiUrlString = repository.getUrl(); final URI repoApiUri = URI.create(repoApiUrlString); result = determineCollectionUri(repoApiUri); } return result; } static String determineProjectId(final GitRepository repository) { final TeamProjectReference project = repository.getProject(); final String result = project.getName(); return result; } static String determineCommit(final GitPush gitPush) { final List commits = gitPush.getCommits(); if (commits == null || commits.size() < 1) { return null; } final GitCommitRef commit = commits.get(0); return commit.getCommitId(); } static String determinePushedBy(final GitPush gitPush) { final IdentityRef pushedBy = gitPush.getPushedBy(); final String result = pushedBy.getDisplayName(); return result; } static String determineTargetBranch(final GitPush gitPush) { // In the form of ref/heads/master final String targetBranch = gitPush.getRefUpdates().get(0).getName(); String[] items = targetBranch.split("/"); return items[items.length - 1]; } static GitCodePushedEventArgs decodeGitPush(final GitPush gitPush, final Event serviceHookEvent) { final GitRepository repository = gitPush.getRepository(); final URI collectionUri = determineCollectionUri(repository, serviceHookEvent); final String repoUriString = repository.getRemoteUrl(); final URI repoUri = URI.create(repoUriString); final String projectId = determineProjectId(repository); final String repoId = repository.getName(); final String commit = determineCommit(gitPush); final String pushedBy = determinePushedBy(gitPush); final String targetBranch = GitPushEvent.determineTargetBranch(gitPush); final GitCodePushedEventArgs args = new GitCodePushedEventArgs(); args.collectionUri = collectionUri; args.repoUri = repoUri; args.projectId = projectId; args.repoId = repoId; args.commit = commit; args.pushedBy = pushedBy; args.targetBranch = targetBranch; return args; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/GitStatusContext.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import net.sf.json.JSONObject; import net.sf.json.JsonConfig; public class GitStatusContext { public String name; public String genre; public GitStatusContext() { } public GitStatusContext(final String name, final String genre) { this.name = name; this.genre = genre; } public static GitStatusContext fromJsonString(final String jsonString) { final JSONObject jsonObject = JSONObject.fromObject(jsonString); final GitStatusContext result; final JsonConfig jsonConfig = new JsonConfig(); jsonConfig.setRootClass(GitStatusContext.class); result = (GitStatusContext) JSONObject.toBean(jsonObject, jsonConfig); return result; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/GitStatusState.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.fasterxml.jackson.annotation.JsonCreator; import java.util.Collections; import java.util.Map; import java.util.TreeMap; public enum GitStatusState { NotSet(0), Pending(1), Succeeded(2), Failed(3), Error(4), ; public static final Map CASE_INSENSITIVE_LOOKUP; static { final Map map = new TreeMap(String.CASE_INSENSITIVE_ORDER); for (final GitStatusState value : GitStatusState.values()) { map.put(value.name(), value); } CASE_INSENSITIVE_LOOKUP = Collections.unmodifiableMap(map); } @SuppressWarnings("unused" /* Invoked by Jackson via @JsonCreator */) @JsonCreator public static GitStatusState caseInsensitiveValueOf(final String name) { if (name == null) { throw new NullPointerException("Name is null"); } if (!CASE_INSENSITIVE_LOOKUP.containsKey(name)) { throw new IllegalArgumentException("No enum constant " + name); } return CASE_INSENSITIVE_LOOKUP.get(name); } private final int value; GitStatusState(final int value) { this.value = value; } public int getValue() { return value; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/HttpMethod.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.microsoft.tfs.core.httpclient.methods.GetMethod; import com.microsoft.tfs.core.httpclient.methods.HeadMethod; import com.microsoft.tfs.core.httpclient.methods.PostMethod; import com.microsoft.tfs.core.httpclient.methods.StringRequestEntity; import hudson.plugins.tfs.util.MediaType; import org.apache.commons.io.IOUtils; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.ProtocolException; public enum HttpMethod { GET { @Override public com.microsoft.tfs.core.httpclient.HttpMethod createClientMethod(final String uri, final String body) { return new GetMethod(uri); } }, POST, HEAD { @Override public com.microsoft.tfs.core.httpclient.HttpMethod createClientMethod(final String uri, final String body) { return new HeadMethod(uri); } }, PATCH { @Override public com.microsoft.tfs.core.httpclient.HttpMethod createClientMethod(final String uri, final String body) { return innerCreateClientMethod(uri, body, MediaType.APPLICATION_JSON_PATCH_JSON); } }, OPTIONS, PUT, DELETE, TRACE, ; public com.microsoft.tfs.core.httpclient.HttpMethod createClientMethod(final String uri, final String body) { return innerCreateClientMethod(uri, body, MediaType.APPLICATION_JSON); } PostMethod innerCreateClientMethod(final String uri, final String body, final String contentType) { final PostMethod method = new PostMethod(uri); // https://www.visualstudio.com/en-us/docs/integrate/get-started/rest/basics#http-method-override method.addRequestHeader("X-HTTP-Method-Override", this.name()); final String charset = MediaType.UTF_8.toString(); final StringRequestEntity requestEntity; try { requestEntity = new StringRequestEntity(body, contentType, charset); } catch (final UnsupportedEncodingException e) { // this shouldn't happen throw new Error(e); } method.setRequestEntity(requestEntity); return method; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/JobCompletionEventArgs.java ================================================ package hudson.plugins.tfs.model; /** * This class holds the information needed to send the Job Completion event. */ public class JobCompletionEventArgs { private final String serverKey; private final String payload; private final String payloadSignature; /** * Construtor. * @param serverKey * @param payload * @param payloadSignature */ public JobCompletionEventArgs(final String serverKey, final String payload, final String payloadSignature) { this.serverKey = serverKey; this.payload = payload; this.payloadSignature = payloadSignature; } public String getServerKey() { return serverKey; } public String getPayload() { return payload; } public String getPayloadSignature() { return payloadSignature; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/JsonPatchOperation.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.microsoft.visualstudio.services.webapi.patch.Operation; /** * Workaround for broken version in * com.microsoft.visualstudio.services.webapi.patch.json * where #getPath() returns this.op */ public class JsonPatchOperation { private Operation op; private String path; private Object value; public Operation getOp() { return op; } public void setOp(Operation op) { this.op = op; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/LegacyIdentityManagementService.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import org.apache.commons.lang.NotImplementedException; import com.microsoft.tfs.core.clients.webservices.GroupProperty; import com.microsoft.tfs.core.clients.webservices.IIdentityManagementService; import com.microsoft.tfs.core.clients.webservices.IdentityDescriptor; import com.microsoft.tfs.core.clients.webservices.IdentitySearchFactor; import com.microsoft.tfs.core.clients.webservices.MembershipQuery; import com.microsoft.tfs.core.clients.webservices.ReadIdentityOptions; import com.microsoft.tfs.core.clients.webservices.TeamFoundationIdentity; import com.microsoft.tfs.util.GUID; /** * An {@link IIdentityManagementService} implementation to provide similar * functionality to that provided by TFS 2010 and up on TFS 2008. * * Right now, the {@link TfsUserLookup} is the only consumer of * {@link IIdentityManagementService} implementations, for the purpose of * determining a TFS user's display name and e-mail address, given their * account name. */ public class LegacyIdentityManagementService implements IIdentityManagementService { public TeamFoundationIdentity[] readIdentities(IdentityDescriptor[] paramArrayOfIdentityDescriptor, MembershipQuery paramMembershipQuery, ReadIdentityOptions paramReadIdentityOptions) { throw new NotImplementedException(); } public TeamFoundationIdentity readIdentity(IdentityDescriptor paramIdentityDescriptor, MembershipQuery paramMembershipQuery, ReadIdentityOptions paramReadIdentityOptions) { throw new NotImplementedException(); } public TeamFoundationIdentity[] readIdentities(GUID[] paramArrayOfGUID, MembershipQuery paramMembershipQuery) { throw new NotImplementedException(); } public TeamFoundationIdentity[][] readIdentities(IdentitySearchFactor paramIdentitySearchFactor, String[] paramArrayOfString, MembershipQuery paramMembershipQuery, ReadIdentityOptions paramReadIdentityOptions) { throw new NotImplementedException(); } public TeamFoundationIdentity readIdentity(IdentitySearchFactor searchFactor, String accountName, MembershipQuery membershipQuery, ReadIdentityOptions readIdentityOptions) { return new TeamFoundationIdentity(new IdentityDescriptor("identityType", "identifier"), accountName, true, null, null); } public IdentityDescriptor createApplicationGroup(String paramString1, String paramString2, String paramString3) { throw new NotImplementedException(); } public TeamFoundationIdentity[] listApplicationGroups(String paramString, ReadIdentityOptions paramReadIdentityOptions) { throw new NotImplementedException(); } public void updateApplicationGroup(IdentityDescriptor paramIdentityDescriptor, GroupProperty paramGroupProperty, String paramString) { throw new NotImplementedException(); } public void deleteApplicationGroup(IdentityDescriptor paramIdentityDescriptor) { throw new NotImplementedException(); } public void addMemberToApplicationGroup(IdentityDescriptor paramIdentityDescriptor1, IdentityDescriptor paramIdentityDescriptor2) { throw new NotImplementedException(); } public void removeMemberFromApplicationGroup(IdentityDescriptor paramIdentityDescriptor1, IdentityDescriptor paramIdentityDescriptor2) { throw new NotImplementedException(); } public boolean isMember(IdentityDescriptor paramIdentityDescriptor1, IdentityDescriptor paramIdentityDescriptor2) { throw new NotImplementedException(); } public boolean refreshIdentity(IdentityDescriptor paramIdentityDescriptor) { throw new NotImplementedException(); } public String getScopeName(String paramString) { throw new NotImplementedException(); } public boolean isOwner(IdentityDescriptor paramIdentityDescriptor) { throw new NotImplementedException(); } public boolean isOwnedWellKnownGroup(IdentityDescriptor paramIdentityDescriptor) { throw new NotImplementedException(); } public String getIdentityDomainScope() { throw new NotImplementedException(); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/Link.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** * Workaround for missing Link model class in current version of vso-httpclient-java */ @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", justification = "Used by JsonPathOperation") public class Link { public String rel; public String url; public Link() { } public Link(final String rel, final String url) { this.rel = rel; this.url = url; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/ListOfGitRepositories.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.microsoft.teamfoundation.sourcecontrol.webapi.model.GitRepository; import java.util.List; public class ListOfGitRepositories { public int count; public List value; } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/ManualCredentialsConfigurer.java ================================================ package hudson.plugins.tfs.model; import com.cloudbees.plugins.credentials.CredentialsScope; import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.util.Secret; import org.kohsuke.stapler.DataBoundConstructor; /** * Manual credentials configurer. */ @SuppressFBWarnings(value = "SE_TRANSIENT_FIELD_NOT_RESTORED", justification = "Maintain compatibility") public class ManualCredentialsConfigurer extends CredentialsConfigurer { private static final long serialVersionUID = 1L; private final transient String userName; private final transient Secret password; /** * Constructor for data binding. */ @DataBoundConstructor public ManualCredentialsConfigurer(final String userName, final Secret password) { this.userName = userName; this.password = password; } public String getUserName() { return userName; } public Secret getPassword() { return password; } @Override public StandardUsernamePasswordCredentials getCredentials(final String collectionUri) { final StandardUsernamePasswordCredentials credentials = new UsernamePasswordCredentialsImpl( CredentialsScope.GLOBAL, null, null, this.userName, this.password.getPlainText() ); return credentials; } /** * Class descriptor. */ @Extension public static final class DescriptorImpl extends CredentialsConfigurerDescriptor { @Override public String getDisplayName() { return "Manual"; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/MockableVersionControlClient.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.microsoft.tfs.core.TFSTeamProjectCollection; import com.microsoft.tfs.core.clients.versioncontrol.VersionControlClient; import com.microsoft.tfs.core.clients.versioncontrol.VersionControlConstants; import com.microsoft.tfs.core.clients.versioncontrol.WorkspaceLocation; import com.microsoft.tfs.core.clients.versioncontrol.WorkspaceOptions; import com.microsoft.tfs.core.clients.versioncontrol.WorkspacePermissions; import com.microsoft.tfs.core.clients.versioncontrol.Workstation; import com.microsoft.tfs.core.clients.versioncontrol.events.EventSource; import com.microsoft.tfs.core.clients.versioncontrol.events.VersionControlEventEngine; import com.microsoft.tfs.core.clients.versioncontrol.exceptions.ItemNotMappedException; import com.microsoft.tfs.core.clients.versioncontrol.exceptions.ServerPathFormatException; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.*; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace; import com.microsoft.tfs.core.clients.versioncontrol.specs.LabelItemSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.VersionSpec; import com.microsoft.tfs.core.clients.versioncontrol.workspacecache.WorkspaceInfo; import com.microsoft.tfs.util.Closable; import javax.annotation.Nonnull; /** * A non-final wrapper over {@link com.microsoft.tfs.core.clients.versioncontrol.VersionControlClient} */ public class MockableVersionControlClient implements Closable { private final VersionControlClient vcc; private boolean isClosed = false; public MockableVersionControlClient(final VersionControlClient vcc) { this.vcc = vcc; } public void close() { if (!isClosed) { vcc.close(); isClosed = true; } } private void makeSureNotClosed() { if (isClosed) { throw new UnsupportedOperationException("Instance has been closed and can no longer be used."); } } /** * Create or update a label for items in this workspace. * * @param label * the label to create or update (must not be null) * @param items * the items to be included in the label creation or update (not * null). * @param options * options that affect the processing of the label creation or update * (must not be null or empty). * @return the label results, null if none were returned. May be empty but * never null. */ public LabelResult[] createLabel( final VersionControlLabel label, final LabelItemSpec[] items, final LabelChildOption options) { makeSureNotClosed(); return vcc.createLabel(label, items, options); } /** * Create a workspace on the server. *

* *

* This method is an core event origination point. The * {@link EventSource} object that accompanies each event fired by this * method describes the execution context (current thread, etc.) when and * where this method was invoked. * * @param workingFolders * the initial working folder mappings for this workspace. May be * null, which means no working folders mapped. * @param workspaceName * the name of the new workspace (must not be null) * @param owner * the name of the workspace owner (if null, empty, or * {@link VersionControlConstants#AUTHENTICATED_USER} the currently * authorized user's name is used) * @param ownerDisplayName * the display name of the workspace owner (if null, * empty, or {@link VersionControlConstants#AUTHENTICATED_USER} the * currently authorized user's display name is used) * @param comment * an optional comment to be stored with this workspace (may be * null). * @param location * where the workspace data is stored (if null, the * server's default is used) * @param options * options to use on the newly created workspace (if * null, the default options are used) * @return the workspace object created by the server. */ public Workspace createWorkspace( final WorkingFolder[] workingFolders, final String workspaceName, final String owner, final String ownerDisplayName, final String comment, final WorkspaceLocation location, final WorkspaceOptions options) { makeSureNotClosed(); return vcc.createWorkspace( workingFolders, workspaceName, owner, ownerDisplayName, comment, location, options ); } /** * Delete a workspace on the server. *

* *

* This method is an core event origination point. The * {@link EventSource} object that accompanies each event fired by this * method describes the execution context (current thread, etc.) when and * where this method was invoked. * * @param workspace * the workspace to delete. */ public void deleteWorkspace(final Workspace workspace) { makeSureNotClosed(); vcc.deleteWorkspace(workspace); } public TFSTeamProjectCollection getConnection() { makeSureNotClosed(); return vcc.getConnection(); } /** * @return a reference to the EventEngine used by this client. Add (and * remove) listeners to this event engine instance in order to be * notified of events. All client events are dispatched through this * event engine. */ public VersionControlEventEngine getEventEngine() { makeSureNotClosed(); return vcc.getEventEngine(); } /** * Look up the local workspace for the specified repository, workspaceName * and workspaceOwner combo. This will only ever return anything if the * workspaceOwner matches the current user. This returns the actual * instance, not a copy! */ public Workspace getLocalWorkspace(final String workspaceName, final String workspaceOwner) { makeSureNotClosed(); return vcc.getLocalWorkspace(workspaceName, workspaceOwner); } /** * Gets the latest changeset ID from the server. * * @return the changeset ID number of the latest changeset. */ public int getLatestChangesetID() { makeSureNotClosed(); return vcc.getLatestChangesetID(); } /** * Retrieve the workspace that is mapped to the provided local path. This * method searches all known workspaces on the current computer to identify * a workspace that has explicitly or implicitly mapped the provided local * path. If no workspace is found, this method throws a * ItemNotMappedException. * * @param localPath * A local path for which a workspace is desired (must not be * null) * @return A reference to the workspace object that has mapped the specified * local path * @throws ItemNotMappedException * if the path is not mapped to any local workspace */ public Workspace getWorkspace(final String localPath) throws ItemNotMappedException { makeSureNotClosed(); return vcc.getWorkspace(localPath); } /** * Queries the server for history about an item. History items are returned * as an array of changesets. * * @param serverOrLocalPath * the server or local path to the server item being queried for its * history (must not be null or empty). * @param version * the version of the item to query history for (history older than * this version will be returned) (must not be null) * @param deletionID * the deletion ID for the item, if it is a deleted item (pass 0 if * the item is not deleted). * @param recursion * whether to query recursively (must not be null) * @param user * only include historical changes made by this user (pass null to * retrieve changes made by all users). * @param versionFrom * the beginning version to query historical changes from (pass null * to start at the first version). * @param versionTo * the ending version to query historical changes to (pass null to * end at the most recent version). * @param maxCount * the maximum number of changes to return (pass Integer.MAX_VALUE * for all available values). Must be > 0. * @param includeFileDetails * true to include individual file change details with the returned * results, false to return only general changeset information. * @param slotMode * if true, all items that have occupied the given serverPath (during * different times) will have their changes returned. If false, only * the item that matches that path at the given version will have its * changes returned. * @param sortAscending * when true gets the top maxCount changes in ascending * order, when false gets them in descending order * @return the changesets that matched the history query, null if the server * did not return a changeset array. */ public Changeset[] queryHistory( final String serverOrLocalPath, final VersionSpec version, final int deletionID, final RecursionType recursion, final String user, final VersionSpec versionFrom, final VersionSpec versionTo, final int maxCount, final boolean includeFileDetails, final boolean slotMode, final boolean includeDownloadInfo, final boolean sortAscending) throws ServerPathFormatException { makeSureNotClosed(); return vcc.queryHistory( serverOrLocalPath, version, deletionID, recursion, user, versionFrom, versionTo, maxCount, includeFileDetails, slotMode, includeDownloadInfo, sortAscending ); } /** * Query the collection of labels that match the given specifications. * * @param label * the label name to match (may be null?). * @param scope * the scope of the label to match (may be null?). * @param owner * the owner of the label to match (may be null?). * @param includeItemDetails * if true, details about the labeled items are included in the * results, otherwise only general label information is included. * @param filterItem * if not null, only labels containing this item are * returned. * @param filterItemVersion * if filterItem was supplied, only labels that include this version * of the filterItem are returned, otherwise may be null. * @return the label items that matched the query. May be empty but never * null. */ public VersionControlLabel[] queryLabels( final String label, final String scope, final String owner, final boolean includeItemDetails, final String filterItem, final VersionSpec filterItemVersion) { makeSureNotClosed(); return vcc.queryLabels( label, scope, owner, includeItemDetails, filterItem, filterItemVersion ); } /** * Returns the workspace on the server that matches the given parameters. * Always queries the server immediately; does not check the local workspace * cache. *

* Unlike {@link VersionControlClient#queryWorkspaces(String, String, String)}, this method does * not update the local workspace cache when workspaces are queried, because * the workspace's computer is unknown (and the computer must be know to * update the cache). * * @param name * the workspace name to match, null to match all. * @param owner * the owner name to match, null to match all. Use * {@link VersionControlConstants#AUTHENTICATED_USER} to retrieve * workspaces owned by the currently logged in user. * @return the matching workspace or null if no matching workspace was * found. */ public Workspace queryWorkspace(final String name, final String owner) { makeSureNotClosed(); return vcc.queryWorkspace(name, owner); } /** * Returns all workspaces on the server that match the given parameters. * Always queries the server immediately; does not check the local workspace * cache. * * @param workspaceName * the workspace name to match, null to match all. * @param workspaceOwner * the owner name to match, null to match all. Use * {@link VersionControlConstants#AUTHENTICATED_USER} to retrieve * workspaces owned by the currently logged in user. * @param computer * the computer name to match, null to match all. Use * LocalHost.getShortName() to match workspaces for this computer. * @param permissionsFilter * find only workspaces matching the given permissions (must not be * null) Use * {@link WorkspacePermissions#NONE_OR_NOT_SUPPORTED} to find all * workspaces. * @return an array of matching workspaces. May be empty but never null. */ public Workspace[] queryWorkspaces( final String workspaceName, final String workspaceOwner, final String computer, @Nonnull final WorkspacePermissions permissionsFilter) { makeSureNotClosed(); return vcc.queryWorkspaces(workspaceName, workspaceOwner, computer, permissionsFilter); } /** * Removes a cached workspace that matches the given name and owner and this * client's server's GUID from the {@link Workstation}'s cache. The caller * is responsible for saving the {@link Workstation} cache. */ public WorkspaceInfo removeCachedWorkspace(final String workspaceName, String workspaceOwner) { makeSureNotClosed(); return vcc.removeCachedWorkspace(workspaceName, workspaceOwner); } /** * This is the same as GetWorkspace() except that it returns null rather * than throwing ItemNotMappedException if the path is not in any known * local workspace. * * @param localPath * A local path for which a workspace is desired (must not be * null) * @return A reference to the workspace object that has mapped the specified * local path or null if the local path is not in a local workspace */ public Workspace tryGetWorkspace(final String localPath) { makeSureNotClosed(); return vcc.tryGetWorkspace(localPath); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/ModernConnectionAdvisor.java ================================================ package hudson.plugins.tfs.model; import java.util.Locale; import java.util.TimeZone; import com.microsoft.tfs.core.config.ConnectionInstanceData; import com.microsoft.tfs.core.config.DefaultConnectionAdvisor; import com.microsoft.tfs.core.config.httpclient.HTTPClientFactory; import com.microsoft.tfs.core.config.persistence.DefaultPersistenceStoreProvider; import com.microsoft.tfs.core.config.persistence.PersistenceStoreProvider; /** * This connection advisor handles proxies appropriately by setting up a Modern http client factory. */ public class ModernConnectionAdvisor extends DefaultConnectionAdvisor { private final ProxyHostEx proxyHost; private final PersistenceStoreProvider persistenceStoreProvider; /** * Minimal constructor. * @param proxyHost */ public ModernConnectionAdvisor(final ProxyHostEx proxyHost) { this(proxyHost, null); } /** * Constructor. * @param proxyHost * @param persistenceStoreProvider */ public ModernConnectionAdvisor(final ProxyHostEx proxyHost, final PersistenceStoreProvider persistenceStoreProvider) { super(Locale.getDefault(), TimeZone.getDefault()); this.proxyHost = proxyHost; this.persistenceStoreProvider = persistenceStoreProvider; } @Override public PersistenceStoreProvider getPersistenceStoreProvider(final ConnectionInstanceData instanceData) { return persistenceStoreProvider != null ? persistenceStoreProvider : DefaultPersistenceStoreProvider.INSTANCE; } @Override public HTTPClientFactory getHTTPClientFactory(final ConnectionInstanceData connectionInstanceData) { return new ModernHTTPClientFactory(connectionInstanceData, proxyHost); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/ModernHTTPClientFactory.java ================================================ package hudson.plugins.tfs.model; import com.microsoft.tfs.core.config.ConnectionInstanceData; import com.microsoft.tfs.core.config.httpclient.DefaultHTTPClientFactory; import com.microsoft.tfs.core.httpclient.DefaultNTCredentials; import com.microsoft.tfs.core.httpclient.HostConfiguration; import com.microsoft.tfs.core.httpclient.HttpClient; import com.microsoft.tfs.core.httpclient.HttpState; import com.microsoft.tfs.core.httpclient.UsernamePasswordCredentials; import com.microsoft.tfs.core.httpclient.auth.AuthScope; import hudson.util.Secret; /** * Extends the default Http client factory to properly handle Proxy configurations. */ public class ModernHTTPClientFactory extends DefaultHTTPClientFactory { private final ProxyHostEx proxyHost; /** * Constructor. * @param connectionInstanceData */ public ModernHTTPClientFactory(final ConnectionInstanceData connectionInstanceData) { this(connectionInstanceData, null); } /** * Constructor. * @param connectionInstanceData * @param proxyHost */ public ModernHTTPClientFactory(final ConnectionInstanceData connectionInstanceData, final ProxyHostEx proxyHost) { super(connectionInstanceData); this.proxyHost = proxyHost; } @Override protected String getUserAgentExtraString(final HttpClient httpClient, final ConnectionInstanceData connectionInstanceData) { // https://stackoverflow.com/a/6773868/ final Class me = this.getClass(); final Package us = me.getPackage(); String version = us.getImplementationVersion(); if (version == null) { version = "devtest"; } return "TFS-Jenkins " + version; } @Override public void configureClientProxy(final HttpClient httpClient, final HostConfiguration hostConfiguration, final HttpState httpState, final ConnectionInstanceData connectionInstanceData) { hostConfiguration.setProxyHost(proxyHost); if (proxyHost != null) { final String proxyUser = proxyHost.getProxyUser(); final Secret proxySecret = proxyHost.getProxySecret(); if (proxyUser != null && proxySecret != null) { httpState.setProxyCredentials( AuthScope.ANY, new UsernamePasswordCredentials(proxyUser, proxySecret.getPlainText()) ); } else { httpState.setProxyCredentials( AuthScope.ANY, new DefaultNTCredentials() ); } } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/NativeLibraryExtractor.java ================================================ package hudson.plugins.tfs.model; import java.io.IOException; /** * An interface for native library extractors. */ public interface NativeLibraryExtractor { /** * Method to extract files. * @param operatingSystem * @param architecture * @param fileName * @throws IOException */ void extractFile(String operatingSystem, String architecture, String fileName) throws IOException; } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/NativeLibraryManager.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.microsoft.tfs.core.persistence.FilesystemPersistenceStore; import com.microsoft.tfs.core.persistence.PersistenceStore; import com.microsoft.tfs.core.persistence.VersionedVendorFilesystemPersistenceStore; import org.apache.commons.io.IOUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.TreeMap; public class NativeLibraryManager implements NativeLibraryExtractor { private static final String VENDOR_NAME = "Microsoft"; private static final String TFS_SDK = "TFS_SDK"; private static final String VERSION = "14.0.1"; private static final String nativeFolderPropertyName = "com.microsoft.tfs.jni.native.base-directory"; private static final String NATIVE = "native"; private static final Class metaClass = NativeLibraryManager.class; private static final TreeMap>> NATIVE_LIBRARIES = new TreeMap>>(); static { final TreeMap> aix = new TreeMap>(); final List aix_ppc = Arrays.asList( "libnative_auth.a", "libnative_console.a", "libnative_filesystem.a", "libnative_misc.a", "libnative_synchronization.a" ); aix.put("ppc", aix_ppc); NATIVE_LIBRARIES.put("aix", aix); final TreeMap> freebsd = new TreeMap>(); final List freebsd_x86 = Arrays.asList( "libnative_auth.so", "libnative_console.so", "libnative_filesystem.so", "libnative_misc.so", "libnative_synchronization.so" ); freebsd.put("x86", freebsd_x86); final List freebsd_x86_64 = Arrays.asList( "libnative_auth.so", "libnative_console.so", "libnative_filesystem.so", "libnative_misc.so", "libnative_synchronization.so" ); freebsd.put("x86_64", freebsd_x86_64); NATIVE_LIBRARIES.put("freebsd", freebsd); final TreeMap> hpux = new TreeMap>(); final List hpux_ia64_32 = Arrays.asList( "libnative_auth.so", "libnative_console.so", "libnative_filesystem.so", "libnative_misc.so", "libnative_synchronization.so" ); hpux.put("ia64_32", hpux_ia64_32); final List hpux_PA_RISC = Arrays.asList( "libnative_auth.sl", "libnative_console.sl", "libnative_filesystem.sl", "libnative_misc.sl", "libnative_synchronization.sl" ); hpux.put("PA_RISC", hpux_PA_RISC); NATIVE_LIBRARIES.put("hpux", hpux); final TreeMap> linux = new TreeMap>(); final List linux_arm = Arrays.asList( "libnative_auth.so", "libnative_console.so", "libnative_filesystem.so", "libnative_misc.so", "libnative_synchronization.so" ); linux.put("arm", linux_arm); final List linux_ppc = Arrays.asList( "libnative_auth.so", "libnative_console.so", "libnative_filesystem.so", "libnative_misc.so", "libnative_synchronization.so" ); linux.put("ppc", linux_ppc); final List linux_x86 = Arrays.asList( "libnative_auth.so", "libnative_console.so", "libnative_filesystem.so", "libnative_misc.so", "libnative_synchronization.so" ); linux.put("x86", linux_x86); final List linux_x86_64 = Arrays.asList( "libnative_auth.so", "libnative_console.so", "libnative_filesystem.so", "libnative_misc.so", "libnative_synchronization.so" ); linux.put("x86_64", linux_x86_64); NATIVE_LIBRARIES.put("linux", linux); final TreeMap> macosx = new TreeMap>(new Comparator() { @SuppressWarnings("ComparatorMethodParameterNotUsed" /* because of null key */) public int compare(final String o1, final String o2) { return 0; } }); final List macosx_universal = Arrays.asList( "libnative_auth.jnilib", "libnative_console.jnilib", "libnative_filesystem.jnilib", "libnative_keychain.jnilib", "libnative_misc.jnilib", "libnative_synchronization.jnilib" ); macosx.put(null, macosx_universal); NATIVE_LIBRARIES.put("macosx", macosx); final TreeMap> solaris = new TreeMap>(); final List solaris_sparc = Arrays.asList( "libnative_auth.so", "libnative_console.so", "libnative_filesystem.so", "libnative_misc.so", "libnative_synchronization.so" ); solaris.put("sparc", solaris_sparc); final List solaris_x86 = Arrays.asList( "libnative_auth.so", "libnative_console.so", "libnative_filesystem.so", "libnative_misc.so", "libnative_synchronization.so" ); solaris.put("x86", solaris_x86); final List solaris_x86_64 = Arrays.asList( "libnative_auth.so", "libnative_console.so", "libnative_filesystem.so", "libnative_misc.so", "libnative_synchronization.so" ); solaris.put("x86_64", solaris_x86_64); NATIVE_LIBRARIES.put("solaris", solaris); final TreeMap> win32 = new TreeMap>(); final List win32_x86 = Arrays.asList( "native_auth.dll", "native_console.dll", "native_credential.dll", "native_filesystem.dll", "native_messagewindow.dll", "native_misc.dll", "native_registry.dll", "native_synchronization.dll" ); win32.put("x86", win32_x86); final List win32_x86_64 = Arrays.asList( "native_auth.dll", "native_console.dll", "native_credential.dll", "native_filesystem.dll", "native_messagewindow.dll", "native_misc.dll", "native_registry.dll", "native_synchronization.dll" ); win32.put("x86_64", win32_x86_64); NATIVE_LIBRARIES.put("win32", win32); } private final PersistenceStore store; public NativeLibraryManager(final PersistenceStore store) { this.store = store; } public void extractFiles() throws IOException { // TODO: it would be great if we detected the current OS and architecture to extract only the needed files extractFiles(this); } static void extractFiles(final NativeLibraryExtractor extractor) throws IOException { for (final Map.Entry>> nativeLibrary : NATIVE_LIBRARIES.entrySet()) { final TreeMap> architecturesToFileNames = nativeLibrary.getValue(); for (final Map.Entry> architectureToFilename : architecturesToFileNames.entrySet()) { final List fileNames = architectureToFilename.getValue(); for (final String fileName : fileNames) { extractor.extractFile(nativeLibrary.getKey(), architectureToFilename.getKey(), fileName); } } } } public void extractFile(final String operatingSystem, final String architecture, final String fileName) throws IOException { final String pathToNativeFile = buildPathToNativeFile(operatingSystem, architecture, fileName); if (!store.containsItem(pathToNativeFile)) { InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = metaClass.getResourceAsStream(pathToNativeFile); outputStream = store.getItemOutputStream(pathToNativeFile); IOUtils.copy(inputStream, outputStream); } finally { IOUtils.closeQuietly(inputStream); IOUtils.closeQuietly(outputStream); } } } // it is important that this return a string containing forward slashes static String buildPathToNativeFile(final String operatingSystem, final String architecture, final String fileName) { final StringBuilder sb = new StringBuilder(NATIVE.length() + 1 + operatingSystem.length() + 1 + 7 /* max architecture length */ + 1 + fileName.length()); sb.append(NATIVE).append('/'); sb.append(operatingSystem).append('/'); if (architecture != null) { sb.append(architecture).append('/'); } sb.append(fileName); final String result = sb.toString(); return result; } public static synchronized void initialize() throws IOException { final String nativeFolder = System.getProperty(nativeFolderPropertyName); if (nativeFolder == null) { final File vendor = new File(VENDOR_NAME); final File vendor_sdk = new File(vendor, TFS_SDK); final File vendor_sdk_version = new File(vendor_sdk, VERSION); final FilesystemPersistenceStore store = new UserHomePersistenceStore(vendor_sdk_version); final NativeLibraryManager manager = new NativeLibraryManager(store); manager.extractFiles(); final File storeFile = store.getStoreFile(); final File nativeFile = new File(storeFile, NATIVE); final String absolutePath = nativeFile.getAbsolutePath(); System.setProperty(nativeFolderPropertyName, absolutePath); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/PingCommand.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.fasterxml.jackson.databind.ObjectMapper; import hudson.model.BuildableItem; import hudson.model.Job; import jenkins.util.TimeDuration; import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; public class PingCommand extends AbstractCommand { public static class Factory implements AbstractCommand.Factory { @Override public AbstractCommand create() { return new PingCommand(); } @Override public String getSampleRequestPayload() { return "{\n" + " \"parameter\":\n" + " [\n" + " {\"name\":\"id\",\"value\":\"123\"},\n" + " {\"name\":\"verbosity\",\"value\":\"high\"}\n" + " ]\n" + "}\n"; } } @Override public JSONObject perform(final Job project, final BuildableItem buildableItem, final StaplerRequest request, final JSONObject requestPayload, final ObjectMapper mapper, final TeamBuildPayload teamBuildPayload, final TimeDuration delay) { return requestPayload; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/PingHookEvent.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.fasterxml.jackson.databind.ObjectMapper; import hudson.plugins.tfs.model.servicehooks.Event; import net.sf.json.JSONObject; public class PingHookEvent extends AbstractHookEvent { public static class Factory implements AbstractHookEvent.Factory { @Override public AbstractHookEvent create() { return new PingHookEvent(); } @Override public String getSampleRequestPayload() { return "{\n" + " \"eventType\": \"ping\",\n" + " \"resource\":\n" + " {\n" + " \"message\": \"Hello, world!\"\n" + " }\n" + "}"; } } @Override public JSONObject perform(final ObjectMapper mapper, final Event serviceHookEvent, final String message, final String detailedMessage) { return JSONObject.fromObject(serviceHookEvent); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/Project.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.LatestVersionSpec; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.model.User; import hudson.plugins.tfs.TeamPluginGlobalConfig; import hudson.plugins.tfs.commands.GetFilesToWorkFolderCommand; import hudson.plugins.tfs.commands.RemoteChangesetVersionCommand; import hudson.plugins.tfs.model.ChangeSet.Item; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.List; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Change; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Changeset; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.RecursionType; import com.microsoft.tfs.core.clients.versioncontrol.specs.LabelSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.ChangesetVersionSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.DateVersionSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.LabelVersionSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.VersionSpec; import com.microsoft.tfs.core.clients.webservices.IIdentityManagementService; public class Project { private final String projectPath; private final Server server; private UserLookup userLookup; public Project(Server server, String projectPath) { this.server = server; this.projectPath = projectPath; } public String getProjectPath() { return projectPath; } static hudson.plugins.tfs.model.ChangeSet.Item convertServerChange (com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Change serverChange) { final String path = serverChange.getItem().getServerItem(); final String action = serverChange.getChangeType().toUIString(true); final Item result = new Item(path, action); return result; } public static hudson.plugins.tfs.model.ChangeSet convertServerChangeset (com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Changeset serverChangeset, UserLookup userLookup) { final String version = Integer.toString(serverChangeset.getChangesetID(), 10); final Date date = serverChangeset.getDate().getTime(); final String author = serverChangeset.getOwner(); final User authorUser = userLookup.find(author); final String comment = serverChangeset.getComment(); final ChangeSet result = new ChangeSet(version, date, authorUser, comment); final Change[] serverChanges = serverChangeset.getChanges(); for (final Change serverChange : serverChanges) { final Item item = convertServerChange(serverChange); result.add(item); } return result; } /** * Returns a list of changes using TFS Java SDK * @param fromVersion the version to get the history from * @param toVersion the version to get the history to * @param includeFileDetails whether or not to include details of modified items * @param maxCount the maximum number of changes to return (pass Integer.MAX_VALUE for all available values). * {@literal Must be > 0.} * @return a list of change sets */ public List getVCCHistory(VersionSpec fromVersion, VersionSpec toVersion, boolean includeFileDetails, int maxCount) { final UserLookup userLookup = getOrCreateUserLookup(); final MockableVersionControlClient vcc = server.getVersionControlClient(); final Changeset[] serverChangesets = vcc.queryHistory( projectPath, fromVersion != null ? fromVersion : toVersion, 0 /* deletionId */, RecursionType.FULL, null /* user */, fromVersion, toVersion, maxCount, includeFileDetails /* includeFileDetails */, true /* slotMode */, false /* includeDownloadInfo */, false /* sortAscending */ ); final List result = new ArrayList(); if (serverChangesets != null) { for (final Changeset serverChangeset : serverChangesets) { final ChangeSet changeSet = convertServerChangeset(serverChangeset, userLookup); result.add(changeSet); } } return result; } @SuppressFBWarnings(value = "DC_DOUBLECHECK", justification = "Only synchronize if not null") public UserLookup getOrCreateUserLookup() { if (userLookup == null) { synchronized (this) { if (userLookup == null) { final IIdentityManagementService ims = server.createIdentityManagementService(); final TeamPluginGlobalConfig teamPluginGlobalConfig = TeamPluginGlobalConfig.get(); final UserAccountMapper mapper = teamPluginGlobalConfig.getUserAccountMapper(); userLookup = new TfsUserLookup(ims, mapper); } } } return userLookup; } /** * Returns a list of change sets containing modified items. * @param fromTimestamp the timestamp to get history from * @param toTimestamp the timestamp to get history to * @return a list of change sets */ public List getDetailedHistory(Calendar fromTimestamp, Calendar toTimestamp) { final DateVersionSpec fromVersion = new DateVersionSpec(fromTimestamp); final DateVersionSpec toVersion = new DateVersionSpec(toTimestamp); return getVCCHistory(fromVersion, toVersion, true, Integer.MAX_VALUE); } public List getDetailedHistory(final String singleVersionSpec) { final VersionSpec toVersion = VersionSpec.parseSingleVersionFromSpec(singleVersionSpec, null); return getVCCHistory(toVersion, toVersion, true, 1); } /** * Returns a list of change sets not containing the modified items. * @param fromTimestamp the timestamp to get history from * @param toTimestamp the timestamp to get history to * @return a list of change sets */ public List getBriefHistory(Calendar fromTimestamp, Calendar toTimestamp) { final DateVersionSpec fromVersion = new DateVersionSpec(fromTimestamp); final DateVersionSpec toVersion = new DateVersionSpec(toTimestamp); return getVCCHistory(fromVersion, toVersion, false, Integer.MAX_VALUE); } /** * Returns a list of change sets not containing the modified items. * @param fromChangeset the changeset number to get history from * @param toTimestamp the timestamp to get history to * @return a list of change sets */ public List getBriefHistory(int fromChangeset, Calendar toTimestamp) { final ChangesetVersionSpec fromVersion = new ChangesetVersionSpec(fromChangeset); final VersionSpec toVersion = new DateVersionSpec(toTimestamp); return getVCCHistory(fromVersion, toVersion, false, Integer.MAX_VALUE); } /** * Returns the latest changeset at the project's path. * @return the {@link ChangeSet} instance representing the last entry in the history for the path */ public ChangeSet getLatestChangeset() { final List changeSets = getVCCHistory(LatestVersionSpec.INSTANCE, null, false, 1); final ChangeSet result = changeSets.size() > 0 ? changeSets.get(0) : null; return result; } /** * Gets the latest changeset that isn't in a cloaked path. * @param fromChangeset the changeset that was last seen, as a point of reference * @param cloakedPaths the list of cloaked paths in the project * @return the {@link ChangeSet} instance representing the last entry in the history for the path */ public ChangeSet getLatestUncloakedChangeset(final int fromChangeset, final Collection cloakedPaths) { final ChangesetVersionSpec fromVersion = new ChangesetVersionSpec(fromChangeset); final List changeSets = getVCCHistory(fromVersion, LatestVersionSpec.INSTANCE, true, Integer.MAX_VALUE); final ChangeSet result = findLatestUncloakedChangeset(cloakedPaths, changeSets); return result; } static ChangeSet findLatestUncloakedChangeset(final Collection cloakedPaths, final List changeSets) { ChangeSet result = null; // We need to search from latest to earliest, otherwise an incorrect result is produced int lastChangeSetNumber = Integer.MAX_VALUE; for (final ChangeSet s : changeSets) { final String stringVersion = s.getVersion(); final int changeSetNumber = Integer.parseInt(stringVersion, 10); if (changeSetNumber >= lastChangeSetNumber) { throw new IllegalArgumentException("The changeset numbers must be strictly decreasing."); } lastChangeSetNumber = changeSetNumber; final Collection changes = s.getAffectedPaths(); final boolean fullyCloaked = isChangesetFullyCloaked(changes, cloakedPaths); if (!fullyCloaked) { result = s; break; } } return result; } /** * Returns a list of changesets without any changesets that are in cloaked paths * @param fromTimestamp the timestamp to get history from * @param toTimestamp the timestamp to get history to * @param cloakedPaths the list of "cloaked" paths that would exclude * changesets that are fully covered by one or more of these paths * @return a list of change sets */ public List getDetailedHistoryWithoutCloakedPaths(final Calendar fromTimestamp, final Calendar toTimestamp, final Collection cloakedPaths) { final DateVersionSpec fromVersion = new DateVersionSpec(fromTimestamp); final DateVersionSpec toVersion = new DateVersionSpec(toTimestamp); return getDetailedHistoryWithoutCloakedPaths(fromVersion, toVersion, cloakedPaths); } public List getDetailedHistoryWithoutCloakedPaths(final VersionSpec fromVersion, final VersionSpec toVersion, final Collection cloakedPaths) { final List changeSets = getVCCHistory(fromVersion, toVersion, true, Integer.MAX_VALUE); final ArrayList changeSetNoCloaked = new ArrayList(); for (final ChangeSet changeset : changeSets) { final Collection affectedPaths = changeset.getAffectedPaths(); final boolean fullyCloaked = isChangesetFullyCloaked(affectedPaths, cloakedPaths); if (!fullyCloaked) { changeSetNoCloaked.add(changeset); } } return changeSetNoCloaked; } static boolean isChangesetFullyCloaked(final Collection changesetPaths, final Collection cloakedPaths) { if (cloakedPaths == null) { return false; } for (final String tfsPath : changesetPaths) { boolean isPathCloaked = false; for (final String cloakedPath : cloakedPaths) { if (tfsPath.regionMatches(true, 0, cloakedPath, 0, cloakedPath.length())) { isPathCloaked = true; break; } } if (!isPathCloaked) { return false; } } return true; } /** * Gets all files from server. * @param localPath the local path to get all files into * @param versionSpec the version spec to use when getting the files * @param useOverwrite if should overwrite changes */ public void getFiles(String localPath, String versionSpec, boolean useOverwrite) { GetFilesToWorkFolderCommand command = new GetFilesToWorkFolderCommand(server, localPath, versionSpec, useOverwrite); server.execute(command.getCallable()); } /** * Gets remote changeset version for specified remote path, as of versionSpec. * * @param remotePath for which to get latest changeset version * @param versionSpec a version specification to convert to a changeset number * @return changeset version for specified remote path */ public int getRemoteChangesetVersion(final String remotePath, final VersionSpec versionSpec) { RemoteChangesetVersionCommand command = new RemoteChangesetVersionCommand(server, remotePath, versionSpec); return extractChangesetNumber(command); } int extractChangesetNumber(final RemoteChangesetVersionCommand command) { final Integer changeSet = server.execute(command.getCallable()); final int result = changeSet; return result; } /** * Gets remote changeset version for the project's remote path, as of versionSpec. * * @param versionSpec a version specification to convert to a changeset number * @return changeset version for the project's remote path */ public int getRemoteChangesetVersion(final VersionSpec versionSpec) { return getRemoteChangesetVersion(projectPath, versionSpec); } @Override public int hashCode() { return new HashCodeBuilder(13, 27).append(projectPath).toHashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if ((obj == null) || (getClass() != obj.getClass())) return false; final Project other = (Project) obj; EqualsBuilder builder = new EqualsBuilder(); builder.append(this.projectPath, other.projectPath); return builder.isEquals(); } protected UserLookup getUserLookup() { return userLookup; } protected void setUserLookup(final UserLookup userLookup) { this.userLookup = userLookup; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/ProxyHostEx.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import hudson.util.Secret; public class ProxyHostEx extends com.microsoft.tfs.core.httpclient.ProxyHost { private final String proxyUser; private final Secret proxySecret; public ProxyHostEx(final String hostname, final int port, final String proxyUser, final Secret proxySecret) { super(hostname, port); this.proxyUser = proxyUser; this.proxySecret = proxySecret; } public String getProxyUser() { return proxyUser; } public Secret getProxySecret() { return proxySecret; } @Override public boolean equals(Object o) { return super.equals(o); } @Override public int hashCode() { return super.hashCode(); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/PullRequestMergeCommitCreatedEventArgs.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; public class PullRequestMergeCommitCreatedEventArgs extends GitCodePushedEventArgs { public int pullRequestId; public int iterationId = -1; } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/Server.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; import com.microsoft.tfs.core.TFSTeamProjectCollection; import com.microsoft.tfs.core.clients.versioncontrol.VersionControlClient; import com.microsoft.tfs.core.clients.webservices.IIdentityManagementService; import com.microsoft.tfs.core.clients.webservices.IdentityManagementException; import com.microsoft.tfs.core.clients.webservices.IdentityManagementService; import com.microsoft.tfs.core.config.persistence.DefaultPersistenceStoreProvider; import com.microsoft.tfs.core.config.persistence.PersistenceStoreProvider; import com.microsoft.tfs.core.httpclient.Credentials; import com.microsoft.tfs.core.httpclient.DefaultNTCredentials; import com.microsoft.tfs.core.httpclient.HttpClient; import com.microsoft.tfs.core.httpclient.UsernamePasswordCredentials; import com.microsoft.tfs.core.util.CredentialsUtils; import com.microsoft.tfs.core.util.URIUtils; import com.microsoft.tfs.jni.helpers.LocalHost; import com.microsoft.tfs.util.Closable; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Launcher; import hudson.ProxyConfiguration; import hudson.model.TaskListener; import hudson.plugins.tfs.TeamPluginGlobalConfig; import hudson.plugins.tfs.commands.ServerConfigurationProvider; import hudson.remoting.Callable; import hudson.remoting.VirtualChannel; import hudson.util.Secret; import jenkins.model.Jenkins; import jenkins.security.MasterToSlaveCallable; import java.io.IOException; import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; public class Server implements ServerConfigurationProvider, Closable { private static final Logger LOGGER = Logger.getLogger(Server.class.getName()); private final String url; private final String userName; private final String userPassword; private Workspaces workspaces; private Map projects = new HashMap(); private final Launcher launcher; private final TaskListener taskListener; private final TFSTeamProjectCollection tpc; private final WebProxySettings webProxySettings; private final ExtraSettings extraSettings; private MockableVersionControlClient mockableVcc; private static HashMap persistenceStoreProviderCache = new HashMap(); /** * This constructor overload assumes a Jenkins instance is present. */ public Server(final Launcher launcher, final TaskListener taskListener, final String url, final String username, final String password) throws IOException { this(launcher, taskListener, url, username, password, null, null); } public static Server create(final Launcher launcher, final TaskListener taskListener, final String url, final StandardUsernamePasswordCredentials credentials, final WebProxySettings webProxySettings, final ExtraSettings extraSettings) throws IOException { final String username; final String userPassword; if (credentials == null) { username = null; userPassword = null; } else { username = credentials.getUsername(); final Secret password = credentials.getPassword(); userPassword = password.getPlainText(); } return new Server(launcher, taskListener, url, username, userPassword, webProxySettings, extraSettings); } public Server(final Launcher launcher, final TaskListener taskListener, final String url, final String username, final String password, final WebProxySettings webProxySettings, final ExtraSettings extraSettings) throws IOException { this.launcher = launcher; this.taskListener = taskListener; this.url = url; this.userName = username; this.userPassword = password; final URI uri = URIUtils.newURI(url); NativeLibraryManager.initialize(); Credentials credentials = null; // In case no user name is provided and the current platform supports // default credentials, use default credentials if ((username == null || username.length() == 0) && CredentialsUtils.supportsDefaultCredentials()) { credentials = new DefaultNTCredentials(); } else if (username != null && password != null) { credentials = new UsernamePasswordCredentials(username, password); } if (credentials != null) { final VirtualChannel channel = launcher != null ? launcher.getChannel() : null; if (webProxySettings != null) { this.webProxySettings = webProxySettings; } else { final ProxyConfiguration proxyConfiguration = determineProxyConfiguration(channel); this.webProxySettings = new WebProxySettings(proxyConfiguration); } final String host = uri.getHost(); final ProxyHostEx proxyHost = this.webProxySettings.toProxyHost(host); if (extraSettings != null) { this.extraSettings = extraSettings; } else { final TeamPluginGlobalConfig globalConfig = determineGlobalConfig(channel); this.extraSettings = new ExtraSettings(globalConfig); } final PersistenceStoreProvider defaultProvider = DefaultPersistenceStoreProvider.INSTANCE; final PersistenceStoreProvider provider; if (this.extraSettings.isConfigFolderPerNode()) { final String hostName = LocalHost.getShortName(); if(persistenceStoreProviderCache.containsKey(hostName)) { provider = persistenceStoreProviderCache.get(hostName); } else { provider = new ClonePersistenceStoreProvider(defaultProvider, hostName); persistenceStoreProviderCache.put(hostName, provider); } } else { provider = defaultProvider; } final ModernConnectionAdvisor advisor = new ModernConnectionAdvisor(proxyHost, provider); this.tpc = new TFSTeamProjectCollection(uri, credentials, advisor); } else { this.webProxySettings = null; this.extraSettings = null; this.tpc = null; } } static TeamPluginGlobalConfig determineGlobalConfig(final VirtualChannel channel) { final Jenkins jenkins = Jenkins.getInstance(); final TeamPluginGlobalConfig result; if (jenkins == null) { if (channel != null) { try { result = channel.call(new MasterToSlaveCallable() { @Override public TeamPluginGlobalConfig call() throws Throwable { final Jenkins jenkins = Jenkins.getInstance(); final TeamPluginGlobalConfig result = jenkins != null ? TeamPluginGlobalConfig.get() : null; return result; } }); } catch (final Throwable throwable) { throw new Error(throwable); } } else { result = TeamPluginGlobalConfig.DEFAULT_CONFIG; } } else { result = TeamPluginGlobalConfig.get(); } return result; } static ProxyConfiguration determineProxyConfiguration(final VirtualChannel channel) { final Jenkins jenkins = Jenkins.getInstance(); final ProxyConfiguration proxyConfiguration; if (jenkins == null) { if (channel != null) { try { proxyConfiguration = channel.call(new MasterToSlaveCallable() { public ProxyConfiguration call() throws Throwable { final Jenkins jenkins = Jenkins.getInstance(); final ProxyConfiguration result = jenkins != null ? jenkins.proxy : null; return result; } }); } catch (final Throwable throwable) { throw new Error(throwable); } } else { proxyConfiguration = null; } } else { proxyConfiguration = jenkins.proxy; } return proxyConfiguration; } public Project getProject(String projectPath) { if (! projects.containsKey(projectPath)) { projects.put(projectPath, new Project(this, projectPath)); } return projects.get(projectPath); } public Workspaces getWorkspaces() { if (workspaces == null) { workspaces = new Workspaces(this); } return workspaces; } @SuppressFBWarnings(value = { "DC_DOUBLECHECK", "IS2_INCONSISTENT_SYNC"}, justification = "Only synchronize if not null") public MockableVersionControlClient getVersionControlClient() { if (mockableVcc == null) { synchronized (this) { if (mockableVcc == null) { final VersionControlClient vcc = tpc.getVersionControlClient(); mockableVcc = new MockableVersionControlClient(vcc); } } } return mockableVcc; } public HttpClient getHttpClient() { return tpc.getHTTPClient(); } public T execute(final Callable callable) { try { final VirtualChannel channel = launcher.getChannel(); final T result = channel.call(callable); return result; } catch (final Exception e) { // convert from checked to unchecked exception throw new RuntimeException(e); } } public String getUrl() { return url; } public String getUserName() { return userName; } public String getUserPassword() { return userPassword; } public Launcher getLauncher() { return launcher; } public WebProxySettings getWebProxySettings() { return webProxySettings; } public ExtraSettings getExtraSettings() { return extraSettings; } public TaskListener getListener() { return taskListener; } public synchronized void close() { if (this.mockableVcc != null) { this.mockableVcc.close(); } if (this.tpc != null) { if(this.tpc.getConfigurationServer() != null) { this.tpc.getConfigurationServer().close(); } this.tpc.close(); } } public IIdentityManagementService createIdentityManagementService() { IIdentityManagementService ims; try { ims = new IdentityManagementService(tpc); } catch (IdentityManagementException e) { ims = new LegacyIdentityManagementService(); } return ims; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/TeamBuildPayload.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.fasterxml.jackson.annotation.JsonProperty; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.plugins.tfs.model.servicehooks.Event; import java.util.List; import java.util.Map; @SuppressFBWarnings(value = "NM_FIELD_NAMING_CONVENTION", justification = "Public so shouldn't be changed") public class TeamBuildPayload { @JsonProperty("parameter") public List BuildParameters; @JsonProperty("team-build") public Map BuildVariables; @JsonProperty("team-event") public Event ServiceHookEvent; } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/TeamGitStatus.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import hudson.model.Job; import hudson.model.Result; import hudson.model.Run; import jenkins.model.Jenkins; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; import javax.annotation.Nonnull; import java.util.Collections; import java.util.HashMap; import java.util.Map; public class TeamGitStatus { private static final Map RESULT_TO_STATE; static { final Map resultToStatus = new HashMap(); resultToStatus.put(Result.SUCCESS, GitStatusState.Succeeded); resultToStatus.put(Result.UNSTABLE, GitStatusState.Failed); resultToStatus.put(Result.FAILURE, GitStatusState.Failed); resultToStatus.put(Result.NOT_BUILT, GitStatusState.Error); resultToStatus.put(Result.ABORTED, GitStatusState.Error); RESULT_TO_STATE = Collections.unmodifiableMap(resultToStatus); } public GitStatusState state; public String description; public String targetUrl; public GitStatusContext context; public static TeamGitStatus fromRun(@Nonnull final Run run) { final TeamGitStatus status = new TeamGitStatus(); final Result result = run.getResult(); if (result == null) { status.state = GitStatusState.Pending; status.description = status.state.toString(); } else { status.state = RESULT_TO_STATE.get(result); status.description = result.toString(); } final Job job = run.getParent(); status.description = job.getDisplayName() + run.getDisplayName() + ": " + status.description; status.targetUrl = run.getAbsoluteUrl(); status.context = getStatusContext(job); return status; } public static TeamGitStatus fromJob(@Nonnull final Job job) { final TeamGitStatus status = new TeamGitStatus(); status.state = GitStatusState.Pending; status.description = "Jenkins Job " + job.getDisplayName() + " queued"; status.targetUrl = job.getAbsoluteUrl(); status.context = getStatusContext(job); return status; } private static GitStatusContext getStatusContext(@Nonnull final Job job) { final String instanceUrl = StringUtils.stripEnd(Jenkins.getInstance().getRootUrl(), "/"); final String projectDisplayName = job.getParent().getFullName() + "/" + job.getDisplayName(); return new GitStatusContext(projectDisplayName, instanceUrl); } public String toJson() { final JSONObject jsonObject = JSONObject.fromObject(this); final String result = jsonObject.toString(); return result; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/TeamRequestedResult.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import hudson.Extension; import hudson.FilePath; import hudson.model.AbstractDescribableImpl; import hudson.model.AbstractProject; import hudson.model.Descriptor; import hudson.util.FormValidation; import hudson.util.ListBoxModel; import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; public class TeamRequestedResult extends AbstractDescribableImpl { private final TeamResultType teamResultType; private String includes; @DataBoundConstructor public TeamRequestedResult(final TeamResultType teamResultType) { this.teamResultType = teamResultType; } public TeamResultType getTeamResultType() { return teamResultType; } public String getIncludes() { return includes; } @DataBoundSetter public void setIncludes(final String includes) { this.includes = includes; } @Extension public static class DescriptorImpl extends Descriptor { @Override public String getDisplayName() { return "Requested build result"; } @SuppressWarnings("unused") public ListBoxModel doFillTeamResultTypeItems() { final TeamResultType[] values = TeamResultType.values(); final ListBoxModel result = new ListBoxModel(values.length); for (final TeamResultType value : values) { result.add(value.getDisplayName(), value.name()); } return result; } public FormValidation doCheckIncludes( @AncestorInPath final AbstractProject project, @QueryParameter final String value) throws IOException { if (project == null) { return FormValidation.ok(); } return FilePath.validateFileMask(project.getSomeWorkspace(), value); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/TeamResultType.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; /** * Enum to represent the type of build result (junit tests, cobertura report, etc.). */ public enum TeamResultType { JUNIT("junit", "JUnit"), NUNIT("nunit", "NUnit"), VS_TEST("vstest", "VSTest"), XUNIT("xunit", "XUnit"), COBERTURA("cobertura", "Cobertura"), JACOCO("jacoco", "JaCoCo"); private final String folderName; private final String displayName; TeamResultType(final String folderName, final String displayName) { this.folderName = folderName; this.displayName = displayName; } public String getFolderName() { return folderName; } public String getDisplayName() { return displayName; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/TfsUserLookup.java ================================================ package hudson.plugins.tfs.model; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import com.microsoft.tfs.core.clients.webservices.IIdentityManagementService; import com.microsoft.tfs.core.clients.webservices.IdentitySearchFactor; import com.microsoft.tfs.core.clients.webservices.MembershipQuery; import com.microsoft.tfs.core.clients.webservices.ReadIdentityOptions; import com.microsoft.tfs.core.clients.webservices.TeamFoundationIdentity; import hudson.model.User; import hudson.tasks.Mailer; /** * Finds user information via the UserAccountMappers that it knows about. */ public class TfsUserLookup implements UserLookup { private static final Logger logger = Logger.getLogger(TfsUserLookup.class.getName()); private final IIdentityManagementService ims; private final UserAccountMapper userAccountMapper; public TfsUserLookup(final IIdentityManagementService ims, final UserAccountMapper userAccountMapper) { this.ims = ims; this.userAccountMapper = userAccountMapper; } /** * Finds the User instance for the account name provided. * @param accountName Windows NT account name: domain\alias. */ public User find(final String accountName) { final String mappedAccountName = userAccountMapper.mapUserAccount(accountName); logger.log(Level.FINE, String.format("Looking up Jenkins user for account '%s'.", mappedAccountName)); final User jenkinsUser = User.get(mappedAccountName); Mailer.UserProperty mailerProperty = jenkinsUser.getProperty(Mailer.UserProperty.class); if (mailerProperty == null || mailerProperty.getAddress() == null || mailerProperty.getAddress().length() == 0) { logger.log(Level.FINE, String.format("No Mailer.UserProperty defined for '%s', looking in TFS", mappedAccountName)); final TeamFoundationIdentity tfsUser = ims.readIdentity( IdentitySearchFactor.ACCOUNT_NAME, accountName, MembershipQuery.NONE, ReadIdentityOptions.NONE ); if (tfsUser != null) { final String displayName = tfsUser.getDisplayName(); jenkinsUser.setFullName(displayName); final String emailAddress = (String) tfsUser.getProperty("Mail"); if (emailAddress != null) { mailerProperty = new Mailer.UserProperty(emailAddress); try { jenkinsUser.addProperty(mailerProperty); } catch (IOException e) { logger.warning(String.format("Unable to save Jenkins account for user '%s'.", accountName)); } } else { logger.info(String.format("User '%s' did not have an e-mail address configured.", accountName)); } } else { logger.warning(String.format("Unable to find user '%s'.", accountName)); } } return jenkinsUser; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/UserAccountMapper.java ================================================ package hudson.plugins.tfs.model; import hudson.DescriptorExtensionList; import hudson.ExtensionPoint; import hudson.model.Describable; import jenkins.model.Jenkins; import java.io.Serializable; /** * Extends Jenkins to add our own UserAccountMapper. */ public abstract class UserAccountMapper implements ExtensionPoint, Describable, Serializable { private static final long serialVersionUID = 1L; /** * Returns the display name for this class. */ public final String getDisplayName() { return getDescriptor().getDisplayName(); } /** * Returns the class descriptor. */ public UserAccountMapperDescriptor getDescriptor() { final Jenkins jenkins = Jenkins.getActiveInstance(); return (UserAccountMapperDescriptor) jenkins.getDescriptorOrDie(getClass()); } /** * Abstract method to map an account from user input. */ public abstract String mapUserAccount(final String input); /** * Gets all user account descriptors. */ public static DescriptorExtensionList all() { final Jenkins jenkins = Jenkins.getActiveInstance(); return jenkins.getDescriptorList(UserAccountMapper.class); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/UserAccountMapperDescriptor.java ================================================ package hudson.plugins.tfs.model; import hudson.model.Descriptor; /** * Class descriptor for UserAccountMapper. */ public abstract class UserAccountMapperDescriptor extends Descriptor { } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/UserHomePersistenceStore.java ================================================ package hudson.plugins.tfs.model; import com.microsoft.tfs.core.persistence.FilesystemPersistenceStore; import java.io.File; import java.io.IOException; import java.io.OutputStream; /** * Inspired by http://stackoverflow.com/a/20885974. */ public class UserHomePersistenceStore extends FilesystemPersistenceStore { public UserHomePersistenceStore(final File subDirectory) { super(new File(System.getProperty("user.home"), subDirectory.getPath())); } @Override public OutputStream getItemOutputStream(final String itemName) throws IOException { final File itemFile = this.getItemFile(itemName); final File folder = itemFile.getParentFile(); if (!folder.exists()) { final boolean fullyCreated = folder.mkdirs(); if (!fullyCreated) { throw new IOException("Unable to create folder structure for " + folder.getAbsolutePath()); } } return super.getItemOutputStream(itemName); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/UserLookup.java ================================================ package hudson.plugins.tfs.model; import hudson.model.User; /** * An interface to define the UserLookup contract. */ public interface UserLookup { /** * @param accountName Windows NT account name: domain\alias. * * @return the Jenkins {@link User} object associated with the account name */ User find(String accountName); } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/WebProxySettings.java ================================================ package hudson.plugins.tfs.model; import hudson.ProxyConfiguration; import hudson.util.Secret; import org.apache.commons.lang.StringUtils; import java.io.Serializable; import java.net.Authenticator; import java.net.InetSocketAddress; import java.net.PasswordAuthentication; import java.net.Proxy; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Pattern; /** * A {@link Serializable} adapter between {@link ProxyConfiguration} and {@link ProxyHostEx}. */ public class WebProxySettings implements Serializable { private static final long serialVersionUID = 401L; private final String hostName; private final int port; private final String proxyUser; private final Secret proxySecret; private final List noProxyHostPatterns; @SuppressWarnings("unused" /* Needed by Serializable interface */) private WebProxySettings() { this(null, -1, null, null, null); } /** * Convenience constructor, mostly for tests. * * @param hostName the name (or address) of the proxy server. * May be {@code null}, meaning there is no proxy server configured. * @param port the port that the proxy server is listening on * @param noProxyHostPatterns a list of {@link Pattern} representing hosts that should not be proxied. * May be {@code null}, meaning all hosts will be proxied. * @param proxyUser the name of the user with which to authenticate to the proxy server. * May be {@code null}, meaning the proxy server doesn't need authentication. * @param proxySecret the password of the user with which to authenticate to the proxy server. * May be {@code null}, meaning the proxy server doesn't need authentication. */ public WebProxySettings(final String hostName, final int port, final List noProxyHostPatterns, final String proxyUser, final Secret proxySecret) { this.hostName = hostName; this.port = port; this.noProxyHostPatterns = copyNoProxyHostPatterns(noProxyHostPatterns); this.proxyUser = proxyUser; this.proxySecret = proxySecret; } /** * Initialize a {@link WebProxySettings} from a Jenkins {@link ProxyConfiguration}. * * @param proxyConfiguration the proxy settings as obtained from Jenkins. * May be {@code null}, meaning there is no proxy configured. */ public WebProxySettings(final ProxyConfiguration proxyConfiguration) { if (proxyConfiguration != null) { this.hostName = proxyConfiguration.name; this.port = proxyConfiguration.port; this.proxyUser = proxyConfiguration.getUserName(); this.noProxyHostPatterns = copyNoProxyHostPatterns(proxyConfiguration.getNoProxyHostPatterns()); this.proxySecret = Secret.fromString(proxyConfiguration.getEncryptedPassword()); } else { this.hostName = null; this.port = -1; this.proxyUser = null; this.proxySecret = null; this.noProxyHostPatterns = copyNoProxyHostPatterns(null); } } private static ArrayList copyNoProxyHostPatterns(final List noProxyHostPatterns) { return new ArrayList( noProxyHostPatterns == null ? Collections.emptyList() : noProxyHostPatterns ); } /** * Initialize a {@link ProxyHostEx} from this {@link WebProxySettings} for the provided hostToProxy. * May return null, which either means there is no proxy server configured or it does not apply * to the provided hostToProxy. * * @param hostToProxy the name of the host for which proxying is considered. * @return an instance of {@link ProxyHostEx} or {@code null} if no proxy is to be used. */ public ProxyHostEx toProxyHost(final String hostToProxy) { final ProxyHostEx proxyHost; if (this.hostName != null) { final boolean shouldProxy = shouldProxy(hostToProxy, noProxyHostPatterns); if (shouldProxy) { proxyHost = new ProxyHostEx(hostName, port, proxyUser, proxySecret); } else { proxyHost = null; } } else { proxyHost = null; } return proxyHost; } /** * Convert the host to the proxy information. */ public Proxy toProxy(final String hostToProxy) { final Proxy proxy; if (this.hostName != null) { final boolean shouldProxy = shouldProxy(hostToProxy, noProxyHostPatterns); if (shouldProxy) { final InetSocketAddress proxyAddress = new InetSocketAddress(hostName, port); proxy = new Proxy(Proxy.Type.HTTP, proxyAddress); if (proxyUser != null && proxySecret != null) { final Authenticator authenticator = new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { if (StringUtils.equalsIgnoreCase(hostName, this.getRequestingHost())) { return new PasswordAuthentication(proxyUser, proxySecret.getPlainText().toCharArray()); } return null; } }; Authenticator.setDefault(authenticator); } } else { proxy = Proxy.NO_PROXY; } } else { proxy = Proxy.NO_PROXY; } return proxy; } static boolean shouldProxy(final String host, final List noProxyHostPatterns) { // inspired by https://github.com/jenkinsci/git-client-plugin/commit/2fefeae06db79d09d6604994001f8f2bd21549e1 boolean shouldProxy = true; for (final Pattern p : noProxyHostPatterns) { if (p.matcher(host).matches()) { shouldProxy = false; break; } } return shouldProxy; } public String getHostName() { return hostName; } public int getPort() { return port; } public String getProxyUser() { return proxyUser; } public Secret getProxySecret() { return proxySecret; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/WorkItem.java ================================================ package hudson.plugins.tfs.model; import com.microsoft.tfs.core.clients.workitem.internal.query.WorkItemRelation; import java.util.ArrayList; import java.util.HashMap; /** * Workaround for missing classes in vso-httpclient-java. */ public class WorkItem extends WorkItemTrackingResource { private HashMap fields; private int id; private ArrayList relations; private int rev; public HashMap getFields() { return fields; } public void setFields(final HashMap fields) { this.fields = fields; } public int getId() { return id; } public void setId(final int id) { this.id = id; } public ArrayList getRelations() { return relations; } public void setRelations(final ArrayList relations) { this.relations = relations; } public int getRev() { return rev; } public void setRev(final int rev) { this.rev = rev; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/WorkItemTrackingResource.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import com.fasterxml.jackson.annotation.JsonProperty; import com.microsoft.visualstudio.services.webapi.model.ReferenceLinks; /** * Workaround for missing classes in vso-httpclient-java. */ public class WorkItemTrackingResource extends WorkItemTrackingResourceReference { private ReferenceLinks _links; @JsonProperty("_links") public ReferenceLinks getLinks() { return _links; } @JsonProperty("_links") public void setLinks(final ReferenceLinks _links) { this._links = _links; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/WorkItemTrackingResourceReference.java ================================================ package hudson.plugins.tfs.model; /** * Workaround for missing classes in vso-httpclient-java. */ public class WorkItemTrackingResourceReference { private String url; public String getUrl() { return url; } public void setUrl(final String url) { this.url = url; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/Workspace.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import java.io.Serializable; public class Workspace implements Serializable{ private final String name; private final String computer; private final String owner; private final String comment; public Workspace(String name, String computer, String owner, String comment) { this.name = name; this.computer = computer; this.owner = owner; this.comment = comment; } public Workspace(String name) { this(name, "", "", ""); } public String getName() { return name; } public String getComputer() { return computer; } public String getOwner() { return owner; } public String getComment() { return comment; } @Override public int hashCode() { return new HashCodeBuilder(13, 27).append(name).append(owner).append(computer).toHashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if ((obj == null) || (getClass() != obj.getClass())) return false; final Workspace other = (Workspace) obj; EqualsBuilder builder = new EqualsBuilder(); builder.append(this.name, other.name); builder.append(this.owner, other.owner); builder.append(this.computer, other.computer); return builder.isEquals(); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/WorkspaceConfiguration.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import java.io.Serializable; import java.util.Collection; import hudson.model.InvisibleAction; /** * An action for storing TFS configuration data in a build * * @author Erik Ramfelt, redsolo */ public class WorkspaceConfiguration extends InvisibleAction implements Serializable { private static final long serialVersionUID = 1L; private final String workspaceName; private final String workfolder; private final String projectPath; private final String serverUrl; private boolean workspaceExists; private Collection cloakedPaths; public WorkspaceConfiguration(String serverUrl, String workspaceName, String projectPath, Collection cloakedPaths, String workfolder) { this.workspaceName = workspaceName; this.workfolder = workfolder; this.projectPath = projectPath; this.serverUrl = serverUrl; this.workspaceExists = true; this.cloakedPaths = cloakedPaths; } public WorkspaceConfiguration(WorkspaceConfiguration configuration) { this.workspaceName = configuration.workspaceName; this.workfolder = configuration.workfolder; this.projectPath = configuration.projectPath; this.serverUrl = configuration.serverUrl; this.workspaceExists = configuration.workspaceExists; this.cloakedPaths = configuration.cloakedPaths; } public String getWorkspaceName() { return workspaceName; } public String getWorkfolder() { return workfolder; } public String getProjectPath() { return projectPath; } public String getServerUrl() { return serverUrl; } public boolean workspaceExists() { return workspaceExists; } public void setWorkspaceWasRemoved() { this.workspaceExists = false; } public Collection getCloakedPaths() { return cloakedPaths; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((projectPath == null) ? 0 : projectPath.hashCode()); result = prime * result + ((serverUrl == null) ? 0 : serverUrl.hashCode()); result = prime * result + ((workfolder == null) ? 0 : workfolder.hashCode()); result = prime * result + (workspaceExists ? 1231 : 1237); result = prime * result + ((workspaceName == null) ? 0 : workspaceName.hashCode()); result = prime * result + ((cloakedPaths == null) ? 0 : cloakedPaths.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof WorkspaceConfiguration)) return false; WorkspaceConfiguration other = (WorkspaceConfiguration) obj; if (projectPath == null) { if (other.projectPath != null) return false; } else if (!projectPath.equals(other.projectPath)) return false; if (serverUrl == null) { if (other.serverUrl != null) return false; } else if (!serverUrl.equals(other.serverUrl)) return false; if (workfolder == null) { if (other.workfolder != null) return false; } else if (!workfolder.equals(other.workfolder)) return false; if (workspaceExists != other.workspaceExists) return false; if (workspaceName == null) { if (other.workspaceName != null) return false; } else if (!workspaceName.equals(other.workspaceName)) return false; if (cloakedPaths == null) { if (other.cloakedPaths != null) return false; } else if (other.cloakedPaths == null) return false; else if (cloakedPaths.size() != other.cloakedPaths.size()) return false; else if (!cloakedPaths.containsAll(other.cloakedPaths)) return false; return true; } @Override public String toString() { return String.format("WorkspaceConfiguration [projectPath=%s, serverUrl=%s, workfolder=%s, workspaceExists=%s, workspaceName=%s]", projectPath, serverUrl, workfolder, workspaceExists, workspaceName); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/WorkspaceMapping.java ================================================ package hudson.plugins.tfs.model; /** * Mapping between a working folder and a project path that exists in a workspace. */ public class WorkspaceMapping { private final String projectPath; private final String localPath; public WorkspaceMapping(final String projectPath, final String localPath) { this.projectPath = projectPath; this.localPath = localPath; } public String getProjectPath() { return projectPath; } public String getLocalPath() { return localPath; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/Workspaces.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.model; import hudson.plugins.tfs.commands.DeleteWorkspaceCommand; import hudson.plugins.tfs.commands.GetWorkspaceMappingCommand; import hudson.plugins.tfs.commands.ListWorkspacesCommand; import hudson.plugins.tfs.commands.NewWorkspaceCommand; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Class that creates, deletes and gets workspaces from a TeamFoundationServer. * * @author Erik Ramfelt */ public class Workspaces implements ListWorkspacesCommand.WorkspaceFactory { private Map workspaces = new HashMap(); private Server server; private boolean mapIsPopulatedFromServer; public Workspaces(Server server) { this.server = server; } /** * Get the list of workspaces from the server * @return the list of workspaces at the server */ private List getListFromServer() { // the 2nd arg must NOT be provided, to force computerName resolution on the agent & not the master ListWorkspacesCommand command = new ListWorkspacesCommand(server); final List result = server.execute(command.getCallable()); return result; } /** * Populate the map field with workspaces from the server once. */ private void populateMapFromServer() { if (!mapIsPopulatedFromServer) { for (Workspace workspace : getListFromServer()) { workspaces.put(workspace.getName(), workspace); } mapIsPopulatedFromServer = true; } } /** * Returns the workspace with the specified name * @param workspaceName the name of the workspace name * @return the workspace with the specified name; null if it wasnt found */ public Workspace getWorkspace(String workspaceName) { if (!workspaces.containsKey(workspaceName)) { populateMapFromServer(); } return workspaces.get(workspaceName); } /** * Returns the if the workspace with the specified name exists on the server * @param workspaceName the name of the workspace * @return true if the workspace exists on server; false otherwise */ public boolean exists(String workspaceName) { if (!workspaces.containsKey(workspaceName)) { populateMapFromServer(); } return workspaces.containsKey(workspaceName); } /** * Returns the if the workspace exists on the server * @param workspace the workspace * @return true if the workspace exists on server; false otherwise */ public boolean exists(Workspace workspace) { return exists(workspace.getName()); } /** * Returns the name of the workspace that mapped at the given localPath, * if applicable. * * @param localPath the path where TFVC files would be downloaded * @return the workspace name if there exists a mapping; * {@code null} otherwise. */ public String getWorkspaceMapping(final String localPath) { final GetWorkspaceMappingCommand command = new GetWorkspaceMappingCommand(server, localPath); final String result = server.execute(command.getCallable()); return result; } /** * Create workspace on server, map it and return a workspace object with the specified name * @param workspaceName the name of the new workspace * @param serverPath the path in TFVC to map * @param cloakedPaths the paths in TFVC to exclude from mapping * @param localPath the path in the local filesystem to map * @return a workspace */ public Workspace newWorkspace(final String workspaceName, final String serverPath, Collection cloakedPaths, final String localPath) { NewWorkspaceCommand command = new NewWorkspaceCommand(server, workspaceName, serverPath, cloakedPaths, localPath); server.execute(command.getCallable()); Workspace workspace = new Workspace(workspaceName); workspaces.put(workspaceName, workspace); return workspace; } /** * Deletes the workspace from the server * @param workspace the workspace to delete */ public void deleteWorkspace(Workspace workspace) { DeleteWorkspaceCommand command = new DeleteWorkspaceCommand(server, workspace.getName()); workspaces.remove(workspace.getName()); server.execute(command.getCallable()); } public Workspace createWorkspace(String name, String computer, String owner, String comment) { return new Workspace(name, computer, owner, comment); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/servicehooks/Event.java ================================================ package hudson.plugins.tfs.model.servicehooks; import java.util.Map; import java.util.UUID; /** * Encapsulates the properties of an event. */ public class Event { private UUID id; private String eventType; private String publisherId; private EventScope scope; private FormattedEventMessage message; private FormattedEventMessage detailedMessage; private Object resource; private String resourceVersion; private Map resourceContainers; public Event() { } public UUID getId() { return id; } public void setId(final UUID id) { this.id = id; } public String getEventType() { return eventType; } public void setEventType(final String eventType) { this.eventType = eventType; } public String getPublisherId() { return publisherId; } public void setPublisherId(final String publisherId) { this.publisherId = publisherId; } public EventScope getScope() { return scope; } public void setScope(final EventScope scope) { this.scope = scope; } public FormattedEventMessage getMessage() { return message; } public void setMessage(final FormattedEventMessage message) { this.message = message; } public FormattedEventMessage getDetailedMessage() { return detailedMessage; } public void setDetailedMessage(final FormattedEventMessage detailedMessage) { this.detailedMessage = detailedMessage; } public Object getResource() { return resource; } public void setResource(final Object resource) { this.resource = resource; } public String getResourceVersion() { return resourceVersion; } public void setResourceVersion(final String resourceVersion) { this.resourceVersion = resourceVersion; } public Map getResourceContainers() { return resourceContainers; } public void setResourceContainers(final Map resourceContainers) { this.resourceContainers = resourceContainers; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/servicehooks/EventScope.java ================================================ package hudson.plugins.tfs.model.servicehooks; import com.fasterxml.jackson.annotation.JsonCreator; import java.util.Collections; import java.util.Map; import java.util.TreeMap; /** * Enum used to specify the scope of the incoming event from TFS/VSTS. */ public enum EventScope { /** * No input scope specified. */ All, /** * Team Project scope. */ Project, /** * Team scope. */ Team, /** * Collection scope. */ Collection, /** * Account scope. */ Account, /** * Deployment scope. */ Deployment; private static final Map CASE_INSENSITIVE_LOOKUP; static { final Map map = new TreeMap(String.CASE_INSENSITIVE_ORDER); for (final EventScope value : EventScope.values()) { map.put(value.name(), value); } CASE_INSENSITIVE_LOOKUP = Collections.unmodifiableMap(map); } /** * Use to compare event scopes to a string representation of the scope. */ @SuppressWarnings("unused" /* Invoked by Jackson via @JsonCreator */) @JsonCreator public static EventScope caseInsensitiveValueOf(final String name) { if (name == null) { throw new NullPointerException("Name is null"); } if (!CASE_INSENSITIVE_LOOKUP.containsKey(name)) { throw new IllegalArgumentException("No enum constant " + name); } return CASE_INSENSITIVE_LOOKUP.get(name); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/servicehooks/FormattedEventMessage.java ================================================ package hudson.plugins.tfs.model.servicehooks; /** * Provides different formats of an event message. */ public class FormattedEventMessage { private String text; private String html; private String markdown; public String getText() { return text; } public void setText(final String text) { this.text = text; } public String getHtml() { return html; } public void setHtml(final String html) { this.html = html; } public String getMarkdown() { return markdown; } public void setMarkdown(final String markdown) { this.markdown = markdown; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/model/servicehooks/ResourceContainer.java ================================================ package hudson.plugins.tfs.model.servicehooks; import java.util.UUID; /** * The base class for all resource containers, i.e. Account, Collection, Project */ public class ResourceContainer { private UUID id; private String baseUrl; private String url; private String name; public ResourceContainer() { } public UUID getId() { return id; } public void setId(final UUID id) { this.id = id; } public String getBaseUrl() { return baseUrl; } public void setBaseUrl(final String baseUrl) { this.baseUrl = baseUrl; } public String getUrl() { return url; } public void setUrl(final String url) { this.url = url; } public String getName() { return name; } public void setName(final String name) { this.name = name; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/Artifact.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; import java.util.HashMap; import java.util.Map; /** * @author Ankit Goyal */ public class Artifact { private Integer id; private String type; private String alias; private DefinitionReference definitionReference; private final Map additionalProperties = new HashMap(); /** * * @return * The id */ public Integer getId() { return id; } /** * * @param id * The id */ public void setId(Integer id) { this.id = id; } /** * * @return * The type */ public String getType() { return type; } /** * * @param type * The type */ public void setType(String type) { this.type = type; } /** * * @return * The alias */ public String getAlias() { return alias; } /** * * @param alias * The alias */ public void setAlias(String alias) { this.alias = alias; } /** * * @return * The definitionReference */ public DefinitionReference getDefinitionReference() { return definitionReference; } /** * * @param definitionReference * The definitionReference */ public void setDefinitionReference(DefinitionReference definitionReference) { this.definitionReference = definitionReference; } public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperty(String name, Object value) { this.additionalProperties.put(name, value); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ArtifactVersion.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; import java.util.ArrayList; import java.util.List; /** * * @author angoya */ public class ArtifactVersion { private Object sourceId; private String alias; private List versions = new ArrayList(); private Object errorMessage; /** * * @return * The sourceId */ public Object getSourceId() { return sourceId; } /** * * @param sourceId * The sourceId */ public void setSourceId(Object sourceId) { this.sourceId = sourceId; } /** * * @return * The alias */ public String getAlias() { return alias; } /** * * @param alias * The alias */ public void setAlias(String alias) { this.alias = alias; } /** * * @return * The versions */ public List getVersions() { return versions; } /** * * @param versions * The versions */ public void setVersions(List versions) { this.versions = versions; } /** * * @return * The errorMessage */ public Object getErrorMessage() { return errorMessage; } /** * * @param errorMessage * The errorMessage */ public void setErrorMessage(Object errorMessage) { this.errorMessage = errorMessage; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ConnectReleaseWebHookEvent.java ================================================ package hudson.plugins.tfs.rm; import com.fasterxml.jackson.databind.ObjectMapper; import hudson.model.AbstractProject; import hudson.model.Descriptor; import hudson.model.Item; import hudson.plugins.tfs.TeamPluginGlobalConfig; import hudson.plugins.tfs.model.AbstractHookEvent; import hudson.plugins.tfs.model.servicehooks.Event; import hudson.tasks.Publisher; import hudson.util.DescribableList; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; import jenkins.model.Jenkins; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; /** * This HookEvent is for to create/delete Release Webhook. */ public class ConnectReleaseWebHookEvent extends AbstractHookEvent { private static final Logger logger = Logger.getLogger(ReleaseWebHookAction.class.getName()); private static final String CREATE_WEBHOOK_EVENTNAME = "rmwebhook-create"; private static final String REMOVE_WEBHOOK_ENENTNAME = "rmwebhook-remove"; private static final String LIST_WEBHOOK_EVENTNAME = "rmwebhook-list"; private static final String LINK_WEBHOOK_EVENTNAME = "rmwebhook-link"; private static final String UNLINK_WEBHOOK_EVENTNAME = "rmwebhook-unlink"; /** * Factory to create ConnectReleaseWebHookEvent. */ public static class Factory implements AbstractHookEvent.Factory { @Override public ConnectReleaseWebHookEvent create() { return new ConnectReleaseWebHookEvent(); } @Override public String getSampleRequestPayload() { return "{\n" + " \"eventType\": rmwebhook-create\n" + " \"resource\": {" + " \"webhookName\": \"webhook name\"\n" + " \"payloadUrl\": \"https://xplatalm.visualstudio.com/_apis/Release/receiveExternalEvent/wenhookId\"\n" + " \"secret\": \"secret\"\n" + " }" + "}"; } } @Override public JSONObject perform(final ObjectMapper mapper, final Event event, final String message, final String detailedMessage) { final Object resource = event.getResource(); final ReleaseWebHookResource parameters = mapper.convertValue(resource, ReleaseWebHookResource.class); if (event.getEventType().equalsIgnoreCase(CREATE_WEBHOOK_EVENTNAME)) { createReleaseWebHook(parameters); } else if (event.getEventType().equalsIgnoreCase(REMOVE_WEBHOOK_ENENTNAME)) { deleteReleaseWebHook(parameters); } else if (event.getEventType().equalsIgnoreCase(LIST_WEBHOOK_EVENTNAME)) { return listReleaseWebHook(); } else if (event.getEventType().equalsIgnoreCase(LINK_WEBHOOK_EVENTNAME)) { linkWebHook(parameters); } else if (event.getEventType().equalsIgnoreCase(UNLINK_WEBHOOK_EVENTNAME)) { unlinkWebHook(parameters); } else { throw new UnsupportedOperationException("Webhook operation " + parameters.getOperationType() + " is not supported"); } return JSONObject.fromObject(event); } private String validateAndGetPayloadUrl(final ReleaseWebHookResource parameters) { String payloadUrl = parameters.getPayloadUrl(); if (StringUtils.isBlank(payloadUrl)) { throw new InvalidParameterException("PayloadUrl is empty"); } final URI uri; try { uri = new URI(payloadUrl); } catch (final URISyntaxException e) { throw new InvalidParameterException("Malformed Payload URL " + e.getMessage()); } final String hostName = uri.getHost(); if (StringUtils.isBlank(hostName)) { throw new InvalidParameterException("Payload URL is empty"); } return StringUtils.stripEnd(StringUtils.trim(payloadUrl), "/"); } private AbstractProject validateAndGetJenkinsProject(final ReleaseWebHookResource resource) { String projectName = resource.getProjectName(); if (StringUtils.isBlank(projectName)) { throw new InvalidParameterException("Project name is empty"); } for (final Item project : Jenkins.getActiveInstance().getAllItems()) { if (project instanceof AbstractProject && project.getFullName().equalsIgnoreCase(projectName)) { return (AbstractProject) project; } } throw new InvalidParameterException("Cannot find Jenkins Project with the name " + resource.getProjectName()); } private ReleaseWebHook validateAndGetReleaseWebHookByName(final String webHookName) { ReleaseWebHook webHook = null; for (ReleaseWebHook webHookItem : ReleaseWebHookHelper.getReleaseWebHookConfigurations()) { if (webHookItem.getWebHookName().equalsIgnoreCase(webHookName)) { webHook = webHookItem; logger.fine(String.format("WebHook found for webhook name %s", webHookName)); break; } } if (webHook == null) { throw new InvalidParameterException(String.format("Cannot find webhook with the name %s", webHookName)); } return webHook; } private void createReleaseWebHook(final ReleaseWebHookResource resource) { if (resource == null) { throw new InvalidParameterException("resource is null"); } String payloadUrl = validateAndGetPayloadUrl(resource); String secret = resource.getSecret(); String webHookName = resource.getWebHookName(); if (StringUtils.isBlank(webHookName)) { throw new InvalidParameterException("webhook name is empty"); } List releaseWebHooks = ReleaseWebHookHelper.getReleaseWebHookConfigurations(); for (ReleaseWebHook webHook : releaseWebHooks) { if (webHook.getWebHookName().equalsIgnoreCase(webHookName)) { if (webHook.getPayloadUrl().equalsIgnoreCase(payloadUrl)) { logger.fine(String.format("WebHook with the name %s and payloadUrl %s already exists", webHookName, payloadUrl)); return; } else { throw new InvalidParameterException(String.format("WebHook with the name %s already exists. Provide a unique name for the ReleaseWebHook", webHookName)); } } } ReleaseWebHook webHook = new ReleaseWebHook(webHookName, payloadUrl, secret); releaseWebHooks.add(webHook); ReleaseWebHookHelper.saveReleaseWebHookConfigurations(releaseWebHooks); } private void deleteReleaseWebHook(final ReleaseWebHookResource resource) { if (resource == null) { throw new InvalidParameterException("resource is null"); } final TeamPluginGlobalConfig config = TeamPluginGlobalConfig.get(); if (config == null) { throw new InternalError("Cannot load TFS global configuration"); } String webHookNameToDelete = resource.getWebHookName(); ReleaseWebHook webHook = validateAndGetReleaseWebHookByName(webHookNameToDelete); for (final Item projectItem : Jenkins.getActiveInstance().getAllItems()) { if (projectItem instanceof AbstractProject) { AbstractProject project = (AbstractProject) projectItem; ReleaseWebHookAction action = getReleaseWebHookActionFromProject(project); if (action != null) { for (ReleaseWebHookReference webHookReference : action.getWebHookReferences()) { if (webHookReference.getWebHookName().equalsIgnoreCase(webHookNameToDelete)) { throw new UnsupportedOperationException(String.format("WebHook %s is referenced in project %s. Cannot delete until all the references are removed.", webHookNameToDelete, project.getName())); } } } } } List releaseWebHooks = ReleaseWebHookHelper.getReleaseWebHookConfigurations(); releaseWebHooks.remove(webHook); ReleaseWebHookHelper.saveReleaseWebHookConfigurations(releaseWebHooks); } private JSONObject listReleaseWebHook() { JSONArray webHooks = new JSONArray(); for (ReleaseWebHook webHookConfig : ReleaseWebHookHelper.getReleaseWebHookConfigurations()) { JSONObject webHook = new JSONObject(); webHook.put("WebHookName", webHookConfig.getWebHookName()); webHook.put("PayloadUrl", webHookConfig.getPayloadUrl()); webHooks.add(webHook); } JSONObject result = new JSONObject(); result.put("ReleaseWebHooks", webHooks); return result; } private void linkWebHook(final ReleaseWebHookResource resource) { if (resource == null) { throw new InvalidParameterException("resource is null"); } AbstractProject project = validateAndGetJenkinsProject(resource); String webHookNameToLink = resource.getWebHookName(); // validate the webhook exists in the server config ReleaseWebHook webHook = validateAndGetReleaseWebHookByName(webHookNameToLink); ReleaseWebHookAction action = getReleaseWebHookActionFromProject(project); if (action != null && action.getWebHookReferences() != null) { for (ReleaseWebHookReference webHookReference : action.getWebHookReferences()) { if (webHookReference.getWebHookName().equalsIgnoreCase(webHookNameToLink)) { logger.fine(String.format("WebHook %s already added in the project %s", webHookNameToLink, project.getName())); return; } } } ReleaseWebHookReference webHookReference = new ReleaseWebHookReference(webHookNameToLink); if (action == null) { List webHookReferences = new ArrayList(); webHookReferences.add(webHookReference); action = new ReleaseWebHookAction(webHookReferences); DescribableList> publishersList = project.getPublishersList(); publishersList.add(action); } else { List webHookReferences = action.getWebHookReferences(); if (webHookReferences == null) { webHookReferences = new ArrayList(); } webHookReferences.add(webHookReference); action.setWebHookReferences(webHookReferences); logger.fine(String.format("WebHook %s is linked to project %s", webHookReference, project)); } saveProject(project); } private void unlinkWebHook(final ReleaseWebHookResource resource) { if (resource == null) { throw new InvalidParameterException("resource is null"); } AbstractProject project = validateAndGetJenkinsProject(resource); String webHookNameToUnlink = resource.getWebHookName(); if (StringUtils.isBlank(webHookNameToUnlink)) { throw new InvalidParameterException("webhook name is empty"); } ReleaseWebHookAction action = getReleaseWebHookActionFromProject(project); if (action == null) { logger.fine(String.format("Cannot find ReleaseWebHook post build action")); return; } List webHookReferences = action.getWebHookReferences(); for (ReleaseWebHookReference webHookReference : webHookReferences) { if (webHookReference.getWebHookName().equalsIgnoreCase(webHookNameToUnlink)) { logger.fine(String.format("Found webhook %s in project %s. Removing it", webHookNameToUnlink, project.getName())); webHookReferences.remove(webHookReference); if (webHookReferences.isEmpty()) { // no webhook exists, removing the post build action itself logger.fine(String.format("ReleaseWebHook post build action is empty, removing the post build action from project")); DescribableList> publishersList = project.getPublishersList(); publishersList.remove(action); } saveProject(project); return; } } logger.fine(String.format("Cannnot find webhook with the name %s in project %s", webHookNameToUnlink, project.getName())); } private ReleaseWebHookAction getReleaseWebHookActionFromProject(final AbstractProject project) { if (project == null) { return null; } ReleaseWebHookAction action = null; DescribableList> publishersList = project.getPublishersList(); for (Publisher publisher : publishersList) { if (publisher instanceof ReleaseWebHookAction) { action = (ReleaseWebHookAction) publisher; } } return action; } private void saveProject(final AbstractProject project) { if (project != null) { try { project.save(); } catch (IOException ex) { throw new InternalError(String.format("Updating project %s failed with an error %s", project.getName(), ex.getMessage())); } } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/CreatedBy.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; import java.util.HashMap; import java.util.Map; /** * @author Ankit Goyal */ public class CreatedBy { private String id; private String displayName; private String uniqueName; private String url; private String imageUrl; private final Map additionalProperties = new HashMap(); /** * * @return * The id */ public String getId() { return id; } /** * * @param id * The id */ public void setId(String id) { this.id = id; } /** * * @return * The displayName */ public String getDisplayName() { return displayName; } /** * * @param displayName * The displayName */ public void setDisplayName(String displayName) { this.displayName = displayName; } /** * * @return * The uniqueName */ public String getUniqueName() { return uniqueName; } /** * * @param uniqueName * The uniqueName */ public void setUniqueName(String uniqueName) { this.uniqueName = uniqueName; } /** * * @return * The url */ public String getUrl() { return url; } /** * * @param url * The url */ public void setUrl(String url) { this.url = url; } /** * * @return * The imageUrl */ public String getImageUrl() { return imageUrl; } /** * * @param imageUrl * The imageUrl */ public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperty(String name, Object value) { this.additionalProperties.put(name, value); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/Definition.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; import java.util.HashMap; import java.util.Map; /** * @author Ankit Goyal */ public class Definition { private String id; private String name; private final Map additionalProperties = new HashMap(); /** * * @return * The id */ public String getId() { return id; } /** * * @param id * The id */ public void setId(String id) { this.id = id; } /** * * @return * The name */ public String getName() { return name; } /** * * @param name * The name */ public void setName(String name) { this.name = name; } public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperty(String name, Object value) { this.additionalProperties.put(name, value); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/DefinitionReference.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; import java.util.HashMap; import java.util.Map; /** * @author Ankit Goyal */ public class DefinitionReference { private Definition definition; private Project project; private final Map additionalProperties = new HashMap(); /** * * @return * The definition */ public Definition getDefinition() { return definition; } /** * * @param definition * The definition */ public void setDefinition(Definition definition) { this.definition = definition; } /** * * @return * The project */ public Project getProject() { return project; } /** * * @param project * The project */ public void setProject(Project project) { this.project = project; } public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperty(String name, Object value) { this.additionalProperties.put(name, value); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/InstanceReference.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; import com.google.gson.annotations.SerializedName; /** * * @author angoya */ public class InstanceReference { @SerializedName("name") private String name; @SerializedName("id") private String id; @SerializedName("sourceBranch") private String sourceBranch; /** * * @return * The name */ public String getName() { return name; } /** * * @param name * The name */ public void setName(String name) { this.name = name; } /** * * @return * The id */ public String getId() { return id; } /** * * @param id * The id */ public void setId(String id) { this.id = id; } /** * * @return * The sourceBranch */ public String getSourceBranch() { return sourceBranch; } /** * * @param sourceBranch * The sourceBranch */ public void setSourceBranch(String sourceBranch) { this.sourceBranch = sourceBranch; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ModifiedBy.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; import java.util.HashMap; import java.util.Map; /** * @author Ankit Goyal */ public class ModifiedBy { private String id; private String displayName; private String uniqueName; private String url; private String imageUrl; private final Map additionalProperties = new HashMap(); /** * * @return The id */ public String getId() { return id; } /** * * @param id The id */ public void setId(String id) { this.id = id; } /** * * @return The displayName */ public String getDisplayName() { return displayName; } /** * * @param displayName The displayName */ public void setDisplayName(String displayName) { this.displayName = displayName; } /** * * @return The uniqueName */ public String getUniqueName() { return uniqueName; } /** * * @param uniqueName The uniqueName */ public void setUniqueName(String uniqueName) { this.uniqueName = uniqueName; } /** * * @return The url */ public String getUrl() { return url; } /** * * @param url The url */ public void setUrl(String url) { this.url = url; } /** * * @return The imageUrl */ public String getImageUrl() { return imageUrl; } /** * * @param imageUrl The imageUrl */ public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperty(String name, Object value) { this.additionalProperties.put(name, value); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/Project.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; import java.util.HashMap; import java.util.Map; /** * @author Ankit Goyal */ public class Project { private String id; private String name; private final Map additionalProperties = new HashMap(); /** * * @return * The id */ public String getId() { return id; } /** * * @param id * The id */ public void setId(String id) { this.id = id; } /** * * @return * The name */ public String getName() { return name; } /** * * @param name * The name */ public void setName(String name) { this.name = name; } public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperty(String name, Object value) { this.additionalProperties.put(name, value); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ReleaseArtifact.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; import com.google.gson.annotations.SerializedName; /** * * @author angoya */ public class ReleaseArtifact { @SerializedName("alias") private String alias; @SerializedName("instanceReference") private InstanceReference instanceReference; /** * * @return * The alias */ public String getAlias() { return alias; } /** * * @param alias * The alias */ public void setAlias(String alias) { this.alias = alias; } /** * * @return * The instanceReference */ public InstanceReference getInstanceReference() { return instanceReference; } /** * * @param instanceReference * The instanceReference */ public void setInstanceReference(InstanceReference instanceReference) { this.instanceReference = instanceReference; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ReleaseArtifactVersionsResponse.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; import java.util.ArrayList; import java.util.List; /** * * @author angoya */ public class ReleaseArtifactVersionsResponse { private List artifactVersions = new ArrayList(); /** * * @return * The artifactVersions */ public List getArtifactVersions() { return artifactVersions; } /** * * @param artifactVersions * The artifactVersions */ public void setArtifactVersions(List artifactVersions) { this.artifactVersions = artifactVersions; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ReleaseBody.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; import java.util.ArrayList; import java.util.List; import com.google.gson.annotations.SerializedName; /** * * @author angoya */ public class ReleaseBody { @SerializedName("definitionId") private Integer definitionId; @SerializedName("description") private String description; @SerializedName("artifacts") private List artifacts = new ArrayList(); @SerializedName("isDraft") private Boolean isDraft; @SerializedName("manualEnvironments") private List manualEnvironments = new ArrayList(); /** * * @return * The definitionId */ public Integer getDefinitionId() { return definitionId; } /** * * @param definitionId * The definitionId */ public void setDefinitionId(Integer definitionId) { this.definitionId = definitionId; } /** * * @return * The description */ public String getDescription() { return description; } /** * * @param description * The description */ public void setDescription(String description) { this.description = description; } /** * * @return * The artifacts */ public List getArtifacts() { return artifacts; } /** * * @param artifacts * The artifacts */ public void setArtifacts(List artifacts) { this.artifacts = artifacts; } /** * * @return * The isDraft */ public Boolean getIsDraft() { return isDraft; } /** * * @param isDraft * The isDraft */ public void setIsDraft(Boolean isDraft) { this.isDraft = isDraft; } /** * * @return * The manualEnvironments */ public List getManualEnvironments() { return manualEnvironments; } /** * * @param manualEnvironments * The manualEnvironments */ public void setManualEnvironments(List manualEnvironments) { this.manualEnvironments = manualEnvironments; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ReleaseDefinition.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author Ankit Goyal */ public class ReleaseDefinition { private Integer id; private Integer revision; private String name; private CreatedBy createdBy; private String createdOn; private ModifiedBy modifiedBy; private String modifiedOn; private List artifacts = new ArrayList(); private String releaseNameFormat; private RetentionPolicy retentionPolicy; private String url; private final Map additionalProperties = new HashMap(); /** * * @return * The id */ public Integer getId() { return id; } /** * * @param id * The id */ public void setId(Integer id) { this.id = id; } /** * * @return * The revision */ public Integer getRevision() { return revision; } /** * * @param revision * The revision */ public void setRevision(Integer revision) { this.revision = revision; } /** * * @return * The name */ public String getName() { return name; } /** * * @param name * The name */ public void setName(String name) { this.name = name; } /** * * @return * The createdBy */ public CreatedBy getCreatedBy() { return createdBy; } /** * * @param createdBy * The createdBy */ public void setCreatedBy(CreatedBy createdBy) { this.createdBy = createdBy; } /** * * @return * The createdOn */ public String getCreatedOn() { return createdOn; } /** * * @param createdOn * The createdOn */ public void setCreatedOn(String createdOn) { this.createdOn = createdOn; } /** * * @return * The modifiedBy */ public ModifiedBy getModifiedBy() { return modifiedBy; } /** * * @param modifiedBy * The modifiedBy */ public void setModifiedBy(ModifiedBy modifiedBy) { this.modifiedBy = modifiedBy; } /** * * @return * The modifiedOn */ public String getModifiedOn() { return modifiedOn; } /** * * @param modifiedOn * The modifiedOn */ public void setModifiedOn(String modifiedOn) { this.modifiedOn = modifiedOn; } /** * * @return * The artifacts */ public List getArtifacts() { return artifacts; } /** * * @param artifacts * The artifacts */ public void setArtifacts(List artifacts) { this.artifacts = artifacts; } /** * * @return * The releaseNameFormat */ public String getReleaseNameFormat() { return releaseNameFormat; } /** * * @param releaseNameFormat * The releaseNameFormat */ public void setReleaseNameFormat(String releaseNameFormat) { this.releaseNameFormat = releaseNameFormat; } /** * * @return * The retentionPolicy */ public RetentionPolicy getRetentionPolicy() { return retentionPolicy; } /** * * @param retentionPolicy * The retentionPolicy */ public void setRetentionPolicy(RetentionPolicy retentionPolicy) { this.retentionPolicy = retentionPolicy; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperty(String name, Object value) { this.additionalProperties.put(name, value); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ReleaseManagementCI.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; import com.cloudbees.plugins.credentials.common.StandardListBoxModel; import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; import com.google.gson.Gson; import hudson.Launcher; import hudson.Extension; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; import hudson.model.Item; import hudson.model.Result; import hudson.plugins.tfs.TeamCollectionConfiguration; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.BuildStepMonitor; import hudson.tasks.Notifier; import hudson.tasks.Publisher; import hudson.util.ListBoxModel; import hudson.util.Secret; import org.apache.commons.lang.StringUtils; import org.json.JSONException; import org.json.JSONObject; import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import java.io.IOException; import java.io.Serializable; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.kohsuke.stapler.QueryParameter; /** * @author Ankit Goyal */ public class ReleaseManagementCI extends Notifier implements Serializable { private static final long serialVersionUID = -760016860995557L; private static final Logger logger = Logger.getLogger(ReleaseManagementCI.class.getName()); public final String collectionUrl; public final String projectName; public final String releaseDefinitionName; public transient String username; public transient Secret password; public String credentialsId; public ReleaseManagementCI(String collectionUrl, String projectName, String releaseDefinitionName, String username, Secret password) { if (collectionUrl.endsWith("/")) { this.collectionUrl = collectionUrl; } else { this.collectionUrl = collectionUrl + "/"; } //this.collectionUrl = this.collectionUrl.toLowerCase().replaceFirst(".visualstudio.com", ".vsrm.visualstudio.com"); this.projectName = projectName; this.releaseDefinitionName = releaseDefinitionName; this.username = username; this.password = password; } // Fields in config.jelly must match the parameter names in the "DataBoundConstructor" @DataBoundConstructor public ReleaseManagementCI(String collectionUrl, String projectName, String releaseDefinitionName, String credentialsId) { if (collectionUrl.endsWith("/")) { this.collectionUrl = collectionUrl; } else { this.collectionUrl = collectionUrl + "/"; } this.projectName = projectName; this.releaseDefinitionName = releaseDefinitionName; StandardUsernamePasswordCredentials credential = TeamCollectionConfiguration.findCredentialsById(credentialsId); this.username = credential == null ? "" : credential.getUsername(); this.password = credential == null ? Secret.fromString("") : credential.getPassword(); this.credentialsId = credentialsId; } protected Object readResolve() { if (StringUtils.isNotBlank(collectionUrl) && password != null && StringUtils.isNotBlank(password.getPlainText())) { try { final URI uri = new URI(collectionUrl); String hostName = uri.getHost(); List credentials = TeamCollectionConfiguration.findCredentials(hostName); for (StandardUsernamePasswordCredentials credential : credentials) { if ((StringUtils.isBlank(username) || credential.getUsername().equals(username)) && credential.getPassword().getPlainText().equals(password.getPlainText())) { this.credentialsId = credential.getId(); return this; } } this.credentialsId = TeamCollectionConfiguration.setCredentials(hostName, username, password.getPlainText()); } catch (Exception ex) { logger.log(Level.WARNING, String.format("Get or generate credentials for collection url: %s and username: %s failed.", collectionUrl, username), ex); } } return this; } /* * (non-Javadoc) * * @see hudson.tasks.BuildStep#getRequiredMonitorService() */ @Override public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.NONE; } /* * (non-Javadoc) * * @see * hudson.tasks.BuildStepCompatibilityLayer#perform(hudson.model.AbstractBuild * , hudson.Launcher, hudson.model.BuildListener) */ @Override public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { StandardUsernamePasswordCredentials credential = TeamCollectionConfiguration.findCredentialsById(credentialsId); this.username = credential == null ? "" : credential.getUsername(); this.password = credential == null ? Secret.fromString("") : credential.getPassword(); String jobName = build.getProject().getName(); int buildId = build.number; String buildNumber = build.getDisplayName(); if (build.getResult() == Result.SUCCESS) { ReleaseManagementHttpClient releaseManagementHttpClient = new ReleaseManagementHttpClient( this.collectionUrl.toLowerCase().replaceFirst(".visualstudio.com", ".vsrm.visualstudio.com"), this.username, this.password); try { ReleaseDefinition releaseDefinition = null; List releaseDefinitions = releaseManagementHttpClient.GetReleaseDefinitions(this.projectName); for(final ReleaseDefinition rd : releaseDefinitions) { if(rd.getName().equalsIgnoreCase(this.releaseDefinitionName)) { releaseDefinition = rd; break; } } if(releaseDefinition == null) { listener.getLogger().printf("No release definition found with name: %s%n", this.releaseDefinitionName); listener.getLogger().println("Release will not be triggered."); } else { CreateRelease(releaseManagementHttpClient, releaseDefinition, jobName, buildNumber, buildId, listener, build, launcher); } } catch (ReleaseManagementException ex) { ex.printStackTrace(listener.error("Failed to trigger release.%n")); } catch (JSONException ex) { ex.printStackTrace(listener.error("Failed to trigger release.%n")); } } return true; } void CreateRelease( ReleaseManagementHttpClient releaseManagementHttpClient, ReleaseDefinition releaseDefinition, String jobName, String buildNumber, int buildId, BuildListener listener, AbstractBuild build, Launcher launcher) throws ReleaseManagementException, JSONException { Artifact jenkinsArtifact = null; for(final Artifact artifact : releaseDefinition.getArtifacts()) { if(artifact.getType().equalsIgnoreCase("jenkins") && artifact.getDefinitionReference().getDefinition().getName().equalsIgnoreCase(jobName)) { jenkinsArtifact = artifact; break; } } if(jenkinsArtifact == null) { listener.getLogger().printf("No jenkins artifact found with name: %s%n", jobName); } else { List releaseArtifacts = PrepareReleaseArtifacts( releaseDefinition, jenkinsArtifact, buildNumber, buildId, listener, releaseManagementHttpClient); String description = "Triggered by " + buildNumber; ReleaseBody releaseBody = new ReleaseBody(); releaseBody.setDescription(description); releaseBody.setDefinitionId(releaseDefinition.getId()); releaseBody.setArtifacts(releaseArtifacts); releaseBody.setIsDraft(false); String body = new Gson().toJson(releaseBody); listener.getLogger().printf("Triggering release...%n"); String response = releaseManagementHttpClient.CreateRelease(this.projectName, body); listener.getLogger().printf("Successfully triggered release.%n"); JSONObject object = new JSONObject(response); listener.getLogger().printf("Release Name: %s%n", object.getString("name")); listener.getLogger().printf("Release id: %s%n", object.getString("id")); build.addAction(new ReleaseSummaryAction(jobName, buildId, object.getJSONObject("_links").getJSONObject("web").getString("href"))); } } private List PrepareReleaseArtifacts(ReleaseDefinition releaseDefinition, Artifact jenkinsArtifact, String buildNumber, int buildId, BuildListener listener, ReleaseManagementHttpClient releaseManagementHttpClient) throws ReleaseManagementException { List releaseArtifacts = new ArrayList(); for(final Artifact artifact : releaseDefinition.getArtifacts()) { ReleaseArtifact releaseArtifact = new ReleaseArtifact(); InstanceReference instanceReference = new InstanceReference(); if(artifact == jenkinsArtifact) { instanceReference.setName(buildNumber); instanceReference.setId(Integer.toString(buildId)); } else { listener.getLogger().printf("Fetching latest version for artifact: %s%n", artifact.getAlias()); ReleaseArtifactVersionsResponse response = releaseManagementHttpClient.GetVersions(this.projectName, new ArrayList(Arrays.asList(artifact))); if(response.getArtifactVersions().isEmpty()) { throw new ReleaseManagementException("Could not fetch versions for the linked artifact sources"); } if(response.getArtifactVersions().get(0).getVersions().isEmpty()) { throw new ReleaseManagementException("Could not fetch versions for the linked artifact: " + artifact.getAlias()); } instanceReference.setName(response.getArtifactVersions().get(0).getVersions().get(0).getName()); instanceReference.setId(response.getArtifactVersions().get(0).getVersions().get(0).getId()); } releaseArtifact.setAlias(artifact.getAlias()); releaseArtifact.setInstanceReference(instanceReference); releaseArtifacts.add(releaseArtifact); } return releaseArtifacts; } @Extension public static final class DescriptorImpl extends BuildStepDescriptor { /* * (non-Javadoc) * * @see hudson.tasks.BuildStepDescriptor#isApplicable(java.lang.Class) */ @Override public boolean isApplicable(Class jobType) { return true; } /* * (non-Javadoc) * * @see hudson.model.Descriptor#getDisplayName() */ @Override public String getDisplayName() { return "Trigger release in Azure Devops Server/Services"; } public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Item owner, @QueryParameter String collectionUrl) { StandardListBoxModel listBoxModel = new StandardListBoxModel(); listBoxModel.withEmptySelection(); String hostName = null; try { final URI uri = new URI(collectionUrl); hostName = uri.getHost(); } catch (final URISyntaxException ignored) { } listBoxModel.withAll(TeamCollectionConfiguration.findCredentials(hostName, owner)); return listBoxModel; } public ListBoxModel doFillProjectNameItems(@QueryParameter String collectionUrl, @QueryParameter String credentialsId) { StandardListBoxModel listBoxModel = new StandardListBoxModel(); listBoxModel.withEmptySelection(); if (StringUtils.isBlank(collectionUrl) || StringUtils.isBlank(credentialsId)) { return listBoxModel; } StandardUsernamePasswordCredentials credential = TeamCollectionConfiguration.findCredentialsById(credentialsId); if (credential == null) { return listBoxModel; } String username = credential.getUsername(); Secret password = credential.getPassword(); try { ReleaseManagementHttpClient releaseManagementHttpClient = new ReleaseManagementHttpClient( collectionUrl.toLowerCase(), username, password); List projects = releaseManagementHttpClient.GetProjectItems(); for (Project project : projects) { listBoxModel.add(project.getName()); } } catch (ReleaseManagementException ex) { logger.log(Level.WARNING, String.format("Get team project for collection url: %s failed.", collectionUrl), ex); } return listBoxModel; } public ListBoxModel doFillReleaseDefinitionNameItems(@QueryParameter String collectionUrl, @QueryParameter String credentialsId, @QueryParameter String projectName) { StandardListBoxModel listBoxModel = new StandardListBoxModel(); listBoxModel.withEmptySelection(); if (StringUtils.isBlank(collectionUrl) || StringUtils.isBlank(credentialsId) || StringUtils.isBlank(projectName)) { return listBoxModel; } StandardUsernamePasswordCredentials credential = TeamCollectionConfiguration.findCredentialsById(credentialsId); if (credential == null) { return listBoxModel; } String username = credential.getUsername(); Secret password = credential.getPassword(); try { ReleaseManagementHttpClient releaseManagementHttpClient = new ReleaseManagementHttpClient( collectionUrl.toLowerCase().replaceFirst(".visualstudio.com", ".vsrm.visualstudio.com"), username, password); List releaseDefinitions = releaseManagementHttpClient.GetReleaseDefinitions(projectName); for (ReleaseDefinition releaseDefinition : releaseDefinitions) { listBoxModel.add(releaseDefinition.getName()); } } catch (ReleaseManagementException ex) { logger.log(Level.WARNING, String.format("Get release definition for project: %s failed.", projectName), ex); } return listBoxModel; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ReleaseManagementException.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; /** * @author Ankit Goyal */ public class ReleaseManagementException extends Exception { public ReleaseManagementException() { super(); } /** * Constructs an {@code ReleaseManagementExcpetion} with the specified detail message. * * @param message * The detail message (which is saved for later retrieval * by the {@link #getMessage()} method) */ public ReleaseManagementException(String message) { super(message); } /** * Constructs an {@code ReleaseManagementExcpetion} with the specified detail message * and cause. * *

Note that the detail message associated with {@code cause} is * not automatically incorporated into this exception's detail * message. * * @param message * The detail message (which is saved for later retrieval * by the {@link #getMessage()} method) * * @param cause * The cause (which is saved for later retrieval by the * {@link #getCause()} method). (A null value is permitted, * and indicates that the cause is nonexistent or unknown.) * * @since 1.6 */ public ReleaseManagementException(String message, Throwable cause) { super(message, cause); } /** * Constructs an {@code ReleaseManagementExcpetion} with the specified cause and a * detail message of {@code (cause==null ? null : cause.toString())} * (which typically contains the class and detail message of {@code cause}). * This constructor is useful for IO exceptions that are little more * than wrappers for other throwables. * * @param cause * The cause (which is saved for later retrieval by the * {@link #getCause()} method). (A null value is permitted, * and indicates that the cause is nonexistent or unknown.) * * @since 1.6 */ public ReleaseManagementException(Throwable cause) { super(cause); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ReleaseManagementHttpClient.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; import com.google.gson.Gson; import hudson.util.Secret; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.codec.binary.Base64; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.json.JSONException; import org.json.JSONObject; /** * @author Ankit Goyal */ public class ReleaseManagementHttpClient { private final HttpClient httpClient; private final String username; private final Secret password; private final String accountUrl; private final String basicAuth; ReleaseManagementHttpClient(String accountUrl, String username, Secret password) { this.accountUrl = accountUrl; this.username = username; this.password = password; this.httpClient = new HttpClient(); this.basicAuth = "Basic " + new String(Base64.encodeBase64((this.username + ":" + Secret.toString(this.password)).getBytes(Charset.defaultCharset())), Charset.defaultCharset()); } public List GetReleaseDefinitions(String project) throws ReleaseManagementException { String url = this.accountUrl + project + "/_apis/release/definitions?$expand=artifacts"; String response = this.ExecuteGetMethod(url); DefinitionResponse definitionResponse = new Gson().fromJson(response, DefinitionResponse.class); return definitionResponse.getValue(); } public String CreateRelease(String project, String body) throws ReleaseManagementException { String url = this.accountUrl + project + "/_apis/release/releases?api-version=3.0-preview.2"; return this.ExecutePostmethod(url, body); } public ReleaseArtifactVersionsResponse GetVersions(String project, List artifacts) throws ReleaseManagementException { String url = this.accountUrl + project + "/_apis/release/artifacts/versions?api-version=3.0-preview.1"; final String body = new Gson().toJson(artifacts); String response = this.ExecutePostmethod(url, body); return new Gson().fromJson(response, ReleaseArtifactVersionsResponse.class); } public List GetProjectItems() throws ReleaseManagementException { String url = this.accountUrl + "/_apis/projects?api-version=1.0"; String response = this.ExecuteGetMethod(url); try { String values = new JSONObject(response).getString("value"); return Arrays.asList(new Gson().fromJson(values, Project[].class)); } catch (JSONException ex) { throw new ReleaseManagementException(ex); } } private String ExecutePostmethod(String url, String body) throws ReleaseManagementException { PostMethod postMethod = new PostMethod(url); postMethod.addRequestHeader("Authorization", this.basicAuth); postMethod.addRequestHeader("Content-Type", "application/json"); postMethod.setRequestBody(body); String response; try { int status = this.httpClient.executeMethod(postMethod); response = postMethod.getResponseBodyAsString(); if(status >= 300) { throw new ReleaseManagementException("Error occurred.%nStatus: " + status + "%nResponse: " + response + "%n"); } } catch(Exception ex) { throw new ReleaseManagementException(ex); } return response; } private String ExecuteGetMethod(String url) throws ReleaseManagementException { GetMethod getMethod = new GetMethod(url); getMethod.addRequestHeader("Authorization", this.basicAuth); String response; try { int status = this.httpClient.executeMethod(getMethod); response = getMethod.getResponseBodyAsString(); if(status >= 300) { throw new ReleaseManagementException("Error occurred.%nStatus: " + status + "%nResponse: " + response + "%n"); } } catch(Exception ex) { throw new ReleaseManagementException(ex); } return response; } private class DefinitionResponse { private Integer count; private List value = new ArrayList(); private final Map additionalProperties = new HashMap(); /** * * @return * The count */ public Integer getCount() { return count; } /** * * @param count * The count */ public void setCount(Integer count) { this.count = count; } /** * * @return * The value */ public List getValue() { return value; } /** * * @param value * The value */ public void setValue(List value) { this.value = value; } public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperty(String name, Object value) { this.additionalProperties.put(name, value); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ReleaseSummaryAction.java ================================================ package hudson.plugins.tfs.rm; import hudson.model.InvisibleAction; import org.kohsuke.stapler.export.ExportedBean; /** * Show release summary link in build page. */ @ExportedBean public class ReleaseSummaryAction extends InvisibleAction { private String projectName; private int buildNo; private String releaseLink; public ReleaseSummaryAction() { } public ReleaseSummaryAction(final String projectName, final int buildNo, final String releaseLink) { this.projectName = projectName; this.buildNo = buildNo; this.releaseLink = releaseLink; } public String getProjectName() { return projectName; } public int getBuildNo() { return buildNo; } public String getReleaseLink() { return releaseLink; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ReleaseWebHook.java ================================================ package hudson.plugins.tfs.rm; import hudson.Extension; import hudson.model.AbstractDescribableImpl; import hudson.model.Descriptor; import hudson.util.FormValidation; import java.net.URI; import java.net.URISyntaxException; import org.kohsuke.stapler.DataBoundConstructor; import java.util.logging.Logger; import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.QueryParameter; /** * * @author Kalyan */ public class ReleaseWebHook extends AbstractDescribableImpl { private static final Logger logger = Logger.getLogger(ReleaseWebHook.class.getName()); private final String webHookName; private final String payloadUrl; private final String secret; @DataBoundConstructor public ReleaseWebHook(final String webHookName, final String payloadUrl, final String secret) { this.webHookName = webHookName; this.payloadUrl = payloadUrl; this.secret = secret; } public ReleaseWebHook(final String webHookName, final String payloadUrl) { this.webHookName = webHookName; this.payloadUrl = payloadUrl; this.secret = StringUtils.EMPTY; } public String getWebHookName() { return this.webHookName; } public String getPayloadUrl() { return this.payloadUrl; } public String getSecret() { return this.secret; } @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) super.getDescriptor(); } /** * DescriptorImpl. */ @Extension public static class DescriptorImpl extends Descriptor { @Override public String getDisplayName() { return "Release Webhook"; } /** * Validates Payload URL. * @param value * @return */ @SuppressWarnings("unused") public FormValidation doCheckPayloadUrl(@QueryParameter final String value) { if (StringUtils.isBlank(value)) { return FormValidation.warning("Please provide a value"); } final URI uri; try { uri = new URI(value); } catch (final URISyntaxException e) { return FormValidation.error("Malformed Payload URL (%s)", e.getMessage()); } final String hostName = uri.getHost(); if (StringUtils.isBlank(hostName)) { return FormValidation.error("Please provide a host name"); } return FormValidation.ok(); } /** * Validates WebHook Name. * @param value * @return */ @SuppressWarnings("unused") public FormValidation doCheckWebHookName(@QueryParameter final String value) { if (StringUtils.isBlank(value)) { return FormValidation.error("Please provide a value"); } String pattern = "^[A-Za-z0-9_-]+$"; if (!value.matches(pattern)) { return FormValidation.error("Only allowed characters are alphaphetic, numeric, hypen and underscore"); } return FormValidation.ok(); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ReleaseWebHookAction.java ================================================ package hudson.plugins.tfs.rm; import hudson.Extension; import hudson.Launcher; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; import hudson.model.Cause; import hudson.plugins.tfs.JenkinsEventNotifier; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.BuildStepMonitor; import hudson.tasks.Notifier; import hudson.tasks.Publisher; import java.io.IOException; import java.io.Serializable; import java.net.HttpURLConnection; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import org.kohsuke.stapler.DataBoundConstructor; /** * Implements ReleaseWebhook Post build action. * @author Kalyan */ public class ReleaseWebHookAction extends Notifier implements Serializable { private static final Logger logger = Logger.getLogger(ReleaseWebHookAction.class.getName()); private List webHookReferences; private final String apiVersion = "5.0-preview"; @DataBoundConstructor public ReleaseWebHookAction(final List webHookReferences) { this.webHookReferences = webHookReferences; } @Override public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.NONE; } public List getWebHookReferences() { return this.webHookReferences; } public void setWebHookReferences(final List webHookReferences) { this.webHookReferences = webHookReferences; } @Override public boolean perform(final AbstractBuild build, final Launcher launcher, final BuildListener listener) throws InterruptedException, IOException { logger.entering("ReleaseWebhookAction", "Perform"); JSONObject json = new JSONObject(); final String payload = JenkinsEventNotifier.getApiJson(build.getUrl()); if (payload != null) { json = JSONObject.fromObject(payload); } json.put("name", build.getProject().getFullName()); json.put("id", build.getNumber()); json.put("startedBy", getStartedBy(build)); HashMap nameToWebHookMap = new HashMap(); for (ReleaseWebHook webHook : ReleaseWebHookHelper.getReleaseWebHookConfigurations()) { nameToWebHookMap.put(webHook.getWebHookName(), webHook); } List webHookStatus = new ArrayList(); ReleaseWebHook webHook = null; for (ReleaseWebHookReference webHookName : webHookReferences) { if (nameToWebHookMap.containsKey(webHookName.getWebHookName())) { try { webHook = nameToWebHookMap.get(webHookName.getWebHookName()); logger.fine(String.format("Sending payload event to %s", webHook.getPayloadUrl())); ReleaseWebHookStatus status = sendJobCompletedEvent(json, webHook); webHookStatus.add(status); } catch (Exception ex) { logger.log(Level.SEVERE, StringUtils.EMPTY, ex); webHookStatus.add(new ReleaseWebHookStatus(webHook.getPayloadUrl(), HttpURLConnection.HTTP_INTERNAL_ERROR, ex.toString())); } } } build.addAction(new ReleaseWebHookSummaryAction((webHookStatus))); return true; } private ReleaseWebHookStatus sendJobCompletedEvent(final JSONObject json, final ReleaseWebHook webHook) throws IOException, NoSuchAlgorithmException, InvalidKeyException { HttpClient client = HttpClientBuilder.create().build(); final HttpPost request = new HttpPost(webHook.getPayloadUrl()); final String payload = json.toString(); request.addHeader("Content-Type", "application/json"); request.addHeader("Accept", "application/json; api-version=" + apiVersion); if (!StringUtils.isBlank(webHook.getSecret())) { String signature = ReleaseWebHookHelper.getPayloadSignature(webHook.getSecret(), payload); request.addHeader("X-Jenkins-Signature", signature); } request.setEntity(new StringEntity(new String(payload.getBytes("UTF-8")))); final HttpResponse response = client.execute(request); final int statusCode = response.getStatusLine().getStatusCode(); ReleaseWebHookStatus status = null; if (statusCode == HttpURLConnection.HTTP_OK) { logger.log(Level.INFO, "sent event payload successfully"); status = new ReleaseWebHookStatus(webHook.getPayloadUrl(), statusCode); } else { HttpEntity entity = response.getEntity(); String content = EntityUtils.toString(entity); logger.log(Level.WARNING, "Cannot send the event to webhook. Content:" + content); status = new ReleaseWebHookStatus(webHook.getPayloadUrl(), statusCode, content); } return status; } /** * Implementation of DescriptorImpl. */ @Extension public static final class DescriptorImpl extends BuildStepDescriptor { @Override public boolean isApplicable(final Class jobType) { return true; } @Override public String getDisplayName() { return "Azure Devops Server/Services Release Webhook"; } } private String getStartedBy(final AbstractBuild build) { final Cause.UserIdCause cause = (Cause.UserIdCause) build.getCause(Cause.UserIdCause.class); String startedBy = ""; if (cause != null && cause.getUserId() != null) { startedBy = cause.getUserId(); } return startedBy; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ReleaseWebHookHelper.java ================================================ package hudson.plugins.tfs.rm; import hudson.plugins.tfs.JenkinsEventNotifier; import hudson.plugins.tfs.TeamPluginGlobalConfig; import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.List; /** * * @author Kalyan */ public final class ReleaseWebHookHelper { private ReleaseWebHookHelper() { } /** * Gets the release webHook configuration from global config. * @return list of release webHook */ public static List getReleaseWebHookConfigurations() { final TeamPluginGlobalConfig config = TeamPluginGlobalConfig.get(); if (config == null) { throw new InternalError("Cannot load TFS global configuration"); } return config.getReleaseWebHookConfigurations(); } /** * Saves the release webHooks with the global config. * @param releaseWebHooks */ public static void saveReleaseWebHookConfigurations(final List releaseWebHooks) { final TeamPluginGlobalConfig config = TeamPluginGlobalConfig.get(); if (config == null) { throw new InternalError("Cannot load TFS global configuration"); } config.setReleaseWebHookConfigurations(releaseWebHooks); config.save(); } /** * Gets the payload signature for the given event payload. * @param secret * @param payload * @return */ public static String getPayloadSignature(final String secret, final String payload) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException { return JenkinsEventNotifier.getPayloadSignature(secret, payload); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ReleaseWebHookReference.java ================================================ package hudson.plugins.tfs.rm; import com.cloudbees.plugins.credentials.common.StandardListBoxModel; import hudson.Extension; import hudson.model.AbstractDescribableImpl; import hudson.model.Descriptor; import hudson.util.ListBoxModel; import org.kohsuke.stapler.DataBoundConstructor; import java.util.logging.Logger; /** * * @author Kalyan */ public class ReleaseWebHookReference extends AbstractDescribableImpl { private static final Logger logger = Logger.getLogger(ReleaseWebHookReference.class.getName()); private final String webHookName; @DataBoundConstructor public ReleaseWebHookReference(final String webHookName) { this.webHookName = webHookName; } public String getWebHookName() { return this.webHookName; } @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) super.getDescriptor(); } /** * DescriptorImpl. */ @Extension public static class DescriptorImpl extends Descriptor { @Override public String getDisplayName() { return "Release Webhook"; } /** * Fills the webHook name input. * @return */ public ListBoxModel doFillWebHookNameItems() { StandardListBoxModel listBoxModel = new StandardListBoxModel(); for (ReleaseWebHook webHook : ReleaseWebHookHelper.getReleaseWebHookConfigurations()) { listBoxModel.add(webHook.getWebHookName()); } return listBoxModel; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ReleaseWebHookResource.java ================================================ package hudson.plugins.tfs.rm; /** * Implements ReleaseWebHookResource. Model to represent webHook register event. * @author Kalyan */ public class ReleaseWebHookResource { private String operationType; private String webHookName; private String projectName; private String payloadUrl; private String secret; public String getOperationType() { return this.operationType; } public void setOperationType(final String operationType) { this.operationType = operationType; } public String getWebHookName() { return this.webHookName; } public void setWebHookName(final String webHookName) { this.webHookName = webHookName; } public String getProjectName() { return this.projectName; } public void setProjectName(final String jobName) { this.projectName = jobName; } public String getPayloadUrl() { return this.payloadUrl; } public void setPayloadUrl(final String payloadUrl) { this.payloadUrl = payloadUrl; } public String getSecret() { return this.secret; } public void setSecret(final String secret) { this.secret = secret; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ReleaseWebHookStatus.java ================================================ package hudson.plugins.tfs.rm; import java.text.SimpleDateFormat; import java.util.Date; /** * Implements ReleaseWebHookStatus. * @author Kalyan */ public class ReleaseWebHookStatus { private final String payloadUrl; private final String timeStamp; private final int statusCode; private String errorMessage; public ReleaseWebHookStatus(final String payloadUrl, final int status) { this.payloadUrl = payloadUrl; this.statusCode = status; this.timeStamp = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss").format(new Date()); } public ReleaseWebHookStatus(final String payloadUrl, final int status, final String errorMessage) { this(payloadUrl, status); this.errorMessage = errorMessage; } public String getPayloadUrl() { return this.payloadUrl; } public int getStatusCode() { return this.statusCode; } public String getErrorMessage() { return this.errorMessage; } public String getTimeStamp() { return this.timeStamp; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/ReleaseWebHookSummaryAction.java ================================================ package hudson.plugins.tfs.rm; import hudson.model.InvisibleAction; import java.util.List; /** * Implements ReleaseWebhook Post build action. * @author Kalyan */ public class ReleaseWebHookSummaryAction extends InvisibleAction { private List webHookStatus; public ReleaseWebHookSummaryAction(final List webHookStatus) { this.webHookStatus = webHookStatus; } public List getWebHookStatus() { return this.webHookStatus; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/RetentionPolicy.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; import java.util.HashMap; import java.util.Map; /** * @author Ankit Goyal */ public class RetentionPolicy { private Integer daysToKeep; private Map additionalProperties = new HashMap(); /** * * @return * The daysToKeep */ public Integer getDaysToKeep() { return daysToKeep; } /** * * @param daysToKeep * The daysToKeep */ public void setDaysToKeep(Integer daysToKeep) { this.daysToKeep = daysToKeep; } public Map getAdditionalProperties() { return this.additionalProperties; } public void setAdditionalProperty(String name, Object value) { this.additionalProperties.put(name, value); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/rm/Version.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.rm; /** * * @author angoya */ public class Version { private String id; private String name; private String sourceBranch; /** * * @return * The id */ public String getId() { return id; } /** * * @param id * The id */ public void setId(String id) { this.id = id; } /** * * @return * The name */ public String getName() { return name; } /** * * @param name * The name */ public void setName(String name) { this.name = name; } /** * * @return * The sourceBranch */ public String getSourceBranch() { return sourceBranch; } /** * * @param sourceBranch * The sourceBranch */ public void setSourceBranch(String sourceBranch) { this.sourceBranch = sourceBranch; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/telemetry/TelemetryContextInitializer.java ================================================ package hudson.plugins.tfs.telemetry; import com.microsoft.applicationinsights.extensibility.ContextInitializer; import com.microsoft.applicationinsights.extensibility.context.ComponentContext; import com.microsoft.applicationinsights.extensibility.context.ContextTagKeys; import com.microsoft.applicationinsights.extensibility.context.DeviceContext; import com.microsoft.applicationinsights.extensibility.context.SessionContext; import com.microsoft.applicationinsights.extensibility.context.UserContext; import com.microsoft.applicationinsights.telemetry.TelemetryContext; import jenkins.model.Jenkins; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.InetAddress; import java.net.UnknownHostException; import java.text.MessageFormat; import java.util.Locale; import java.util.Map; import java.util.UUID; /** * This class is provided as a ContextInitializer for the application insights TelemetryClient. */ public class TelemetryContextInitializer implements ContextInitializer { private static final Logger logger = LoggerFactory.getLogger(TelemetryContextInitializer.class); private static final String PRODUCT_NAME = "TFS-Jenkins"; private static final String SYS_PROP_OS_NAME = "os.name"; private static final String SYS_PROP_OS_VERSION = "os.version"; private static final String SYS_PROP_USER_NAME = "user.name"; private static final String SYS_PROP_JAVA_RUNTIME = "java.runtime.name"; private static final String SYS_PROP_JAVA_VERSION = "java.version"; private static final String USER_AGENT_FORMAT = "{0}/{1} {2}/{3} {4}/{5} {6}/{7} ({8})"; private static final String PROPERTY_USER_ID = "User.Id"; private static final String PROPERTY_JENKINS_VERSION = "Jenkins.Version"; private static final String PROPERTY_PLUGIN_VERSION = "Plugin.Version"; private static final String PROPERTY_JAVA_NAME = "Java.Name"; private static final String PROPERTY_JAVA_VERSION = "Java.Version"; private static final String PROPERTY_OS_PLATFORM = "VSTS.Core.Machine.OS.Platform"; private static final String PROPERTY_OS_VERSION = "VSTS.Core.Machine.OS.Version"; private static final String PROPERTY_LOCALE = "Locale"; private String hostname = StringUtils.EMPTY; private boolean isInitialized = false; private final boolean isDeveloperMode; public TelemetryContextInitializer(final boolean isDeveloperMode) { this.isDeveloperMode = isDeveloperMode; } @Override public void initialize(final TelemetryContext context) { if (!isInitialized) { logger.info("Starting TelemetryContext initialization"); initializeInstrumentationKey(context, isDeveloperMode); initializeProperties(context.getProperties()); initializeUser(context.getUser()); initializeComponent(context.getComponent()); initializeDevice(context.getDevice()); initializeTags(context.getTags()); initializeSession(context.getSession()); isInitialized = true; logger.info("Ending TelemetryContext initialization"); } } /** * Gets the full User Agent used to make requests to TFS/Team Services. */ public String getUserAgent(final String defaultUserAgent) { try { return MessageFormat.format(USER_AGENT_FORMAT, PRODUCT_NAME, getPluginVersion(), "Jenkins", Jenkins.getVersion(), getPlatformName(), getPlatformVersion(), getJavaName(), getJavaVersion(), defaultUserAgent); } catch (final Throwable t) { logger.warn("Error getting UserAgent", t); return defaultUserAgent; } } private void initializeDevice(final DeviceContext device) { device.setOperatingSystem(getPlatformName()); device.setOperatingSystemVersion(getPlatformVersion()); } private void initializeInstrumentationKey(final TelemetryContext context, final boolean isDeveloperMode) { if (isDeveloperMode) { context.setInstrumentationKey("149da81b-a0ab-4bdf-a7e9-11e5af9e39bd"); } else { context.setInstrumentationKey("0f243a28-b3c3-41f2-b7cc-d10feec45a81"); } } private void initializeUser(final UserContext user) { user.setId(getUserId()); user.setUserAgent(getUserAgent("")); } private String getUserId() { final String computerName = getComputerName(); final String userName = getSystemProperty(SYS_PROP_USER_NAME); final String fakeUserId = MessageFormat.format("{0}@{1}", userName, computerName); //FIXME: was sha1Hex, but this is available only in commons codec 1.7. TFS SDK 14.0.3 bundles older version return DigestUtils.shaHex(fakeUserId); } private String getComputerName() { if (StringUtils.isEmpty(hostname)) { hostname = TelemetryHelper.UNKNOWN; try { // on Mac this call can take > 10 secs so don't call multiple times final InetAddress address = InetAddress.getLocalHost(); hostname = address.getHostName(); } catch (UnknownHostException ex) { // This case is covered by the initial value of hostname above } } return hostname; } private void initializeComponent(final ComponentContext component) { component.setVersion(getPluginVersion()); } private void initializeTags(final Map tags) { tags.put(ContextTagKeys.getKeys().getApplicationId(), PRODUCT_NAME); tags.put(ContextTagKeys.getKeys().getDeviceOS(), getPlatformName()); tags.put(ContextTagKeys.getKeys().getDeviceOSVersion(), getPlatformVersion()); } private void initializeSession(final SessionContext sessionContext) { sessionContext.setId(UUID.randomUUID().toString()); } private void initializeProperties(final Map properties) { properties.put(PROPERTY_USER_ID, getUserId()); // Get Jenkins version info properties.put(PROPERTY_JENKINS_VERSION, Jenkins.getVersion().toString()); properties.put(PROPERTY_PLUGIN_VERSION, getPluginVersion()); // Get OS info properties.put(PROPERTY_LOCALE, getLocaleName()); properties.put(PROPERTY_OS_PLATFORM, getPlatformName()); properties.put(PROPERTY_OS_VERSION, getPlatformVersion()); // Get Java info properties.put(PROPERTY_JAVA_NAME, getJavaName()); properties.put(PROPERTY_JAVA_VERSION, getJavaVersion()); } private String getSystemProperty(final String propertyName) { return System.getProperty(propertyName, StringUtils.EMPTY); } private String getPlatformName() { return getSystemProperty(SYS_PROP_OS_NAME); } private String getPlatformVersion() { return getSystemProperty(SYS_PROP_OS_VERSION); } private String getLocaleName() { return Locale.getDefault().getDisplayName(); } private String getJavaName() { return getSystemProperty(SYS_PROP_JAVA_RUNTIME); } private String getJavaVersion() { return getSystemProperty(SYS_PROP_JAVA_VERSION); } private String getPluginVersion() { final Jenkins instance = Jenkins.getInstance(); if (instance != null && instance.getPluginManager() != null) { return instance.getPluginManager().getPlugin("tfs").getVersion(); } return StringUtils.EMPTY; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/telemetry/TelemetryHelper.java ================================================ package hudson.plugins.tfs.telemetry; import com.microsoft.applicationinsights.TelemetryClient; import com.microsoft.applicationinsights.TelemetryConfiguration; import com.microsoft.applicationinsights.channel.TelemetryChannel; import com.microsoft.applicationinsights.extensibility.ContextInitializer; import com.microsoft.applicationinsights.internal.logger.InternalLogger; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URI; import java.util.HashMap; import java.util.Map; /** * The TelemetryHelper class is a singleton that allows the plugin to capture * telemetry data when the user initiates events. */ public final class TelemetryHelper { public static final String UNKNOWN = "unknown"; private static final String UNIQUE_PREFIX = "ai-log"; private static final String BASE_FOLDER = "AppInsights"; private static final String PROPERTY_VSTS_IS_HOSTED = "VSTS.TeamFoundationServer.IsHostedServer"; private static final String PROPERTY_VSTS_SERVER_ID = "VSTS.TeamFoundationServer.ServerId"; private static final String PROPERTY_VSTS_COLLECTION_ID = "VSTS.TeamFoundationServer.CollectionId"; private static final String ACTION_NAME_FORMAT = "Action/%s"; private static final Logger logger = LoggerFactory.getLogger(TelemetryHelper.class); // Instance members private TelemetryClient telemetryClient; /** * A private static class to allow safe lazy initialization of the singleton. */ private static class TfsTelemetryHelperHolder { private static final TelemetryHelper INSTANCE = new TelemetryHelper(); } /** * The getInstance method returns the singleton instance. Creating it if necessary */ private static TelemetryHelper getInstance() { // Using the Initialization-on-demand holder pattern to make sure this is thread-safe return TfsTelemetryHelperHolder.INSTANCE; } // The private constructor keeps the class from being inherited or misused private TelemetryHelper() { final String skip = System.getProperties().getProperty("hudson.plugins.tfs.telemetry.skipClientInitialization"); if (StringUtils.isNotEmpty(skip) && StringUtils.equalsIgnoreCase(skip, "true")) { // this flag is here for testing purposes in which case we do not want to create a telemetry channel // or client. return; } // Initialize the internal logger final Map loggerData = new HashMap(); loggerData.put("Level", InternalLogger.LoggingLevel.ERROR.toString()); loggerData.put("UniquePrefix", UNIQUE_PREFIX); loggerData.put("BaseFolder", BASE_FOLDER); InternalLogger.INSTANCE.initialize(InternalLogger.LoggerOutputType.FILE.toString(), loggerData); // Initialize the active TelemetryConfiguration final String isDeveloperModeProperty = System.getProperty("hudson.plugins.tfs.telemetry.isDeveloperMode", "false"); boolean isDeveloperMode = StringUtils.equalsIgnoreCase(isDeveloperModeProperty, "true"); ContextInitializer initializer = new TelemetryContextInitializer(isDeveloperMode); TelemetryConfiguration.getActive().getContextInitializers().add(initializer); // Create a channel to AppInsights final TelemetryChannel channel = TelemetryConfiguration.getActive().getChannel(); if (channel != null) { channel.setDeveloperMode(isDeveloperMode); } else { logger.error("Failed to load telemetry channel"); return; } logger.debug("AppInsights telemetry initialized"); logger.debug(" Developer Mode: ", channel.isDeveloperMode()); // Create the telemetry client and cache it for later use telemetryClient = new TelemetryClient(); } /** * Call sendEvent to track an occurrence of a named event. * * @param name is the name of the event to be tracked. * @param properties are additional properties to track with the event. */ public static void sendEvent(final String name, final Map properties) { try { getInstance().sendEventInternal(name, properties); } catch (Exception e) { logger.warn("Error sending event telemetry", e); } } /** * Call sendMetric to track the new value of the named metric. * * @param name is the name of the metric to be tracked. * @param value is the current value of the metric as a double. */ public static void sendMetric(final String name, final double value) { try { getInstance().sendMetricInternal(name, value); } catch (Exception e) { logger.warn("Error sending metric telemetry", e); } } /** * Call sendException to track an exception that occurred that should be tracked. * * @param exception is the exception to track. */ public static void sendException(final Exception exception, final Map properties) { try { getInstance().sendExceptionInternal(exception, properties); } catch (Exception e) { logger.warn("Error sending exception telemetry", e); } } protected void sendMetricInternal(final String name, final double value) { // Log that the event occurred (this log is used in testing) logger.debug(String.format("sendMetric(%s, %f)", name, value)); if (telemetryClient != null) { telemetryClient.trackMetric(name, value); } } protected void sendEventInternal(final String name, final Map properties) { final String eventName = String.format(ACTION_NAME_FORMAT, name); final PropertyMapBuilder builder = new PropertyMapBuilder(properties); // Log that the event occurred (this log is used in testing) logger.debug(String.format("sendEvent(%s, %s)", name, builder.toString())); if (telemetryClient != null) { telemetryClient.trackEvent(eventName, builder.build(), null); } } protected void sendExceptionInternal(final Exception exception, final Map properties) { final PropertyMapBuilder builder = new PropertyMapBuilder(properties); // Log that the event occurred (this log is used in testing) logger.debug(String.format("sendException(%s, %s)", exception.getMessage(), builder.toString())); if (telemetryClient != null) { telemetryClient.trackException(exception, builder.build(), null); } } /** * Builder class to gather all properties to be sent to AppInsights into a Map. */ public static class PropertyMapBuilder { public static final Map EMPTY = new PropertyMapBuilder().build(); private Map properties = new HashMap(); public PropertyMapBuilder() { this(null); } public PropertyMapBuilder(final Map properties) { if (properties != null) { this.properties = new HashMap(properties); } else { this.properties = new HashMap(); } } /** * Returns the map of all key value pairs that were added to the builder. */ public Map build() { // Make a copy and return it return new HashMap(properties); } /** * Adds server context information to the properties sent to AppInsights. */ public PropertyMapBuilder serverContext(final String serverUrl, final String collectionUrl) { if (serverUrl != null) { final boolean isHosted = StringUtils.containsIgnoreCase(serverUrl, ".visualstudio.com"); add(PROPERTY_VSTS_IS_HOSTED, Boolean.toString(isHosted)); add(PROPERTY_VSTS_SERVER_ID, getServerId(serverUrl)); add(PROPERTY_VSTS_COLLECTION_ID, getCollectionId(collectionUrl)); } return this; } /** * Adds a key value pair to the properties sent to AppInsights. */ public PropertyMapBuilder pair(final String key, final String value) { if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(value)) { add(key, value); } return this; } private String getServerId(final String serverUrl) { try { if (serverUrl != null) { return URI.create(serverUrl).getHost(); } } catch (Exception ex) { logger.error("failed to get server URI", ex); } return UNKNOWN; } private String getCollectionId(final String collectionUrl) { try { if (collectionUrl != null) { return URI.create(collectionUrl).getPath(); } } catch (Exception ex) { logger.error("failed to get server URI", ex); } return UNKNOWN; } private void add(final String key, final String value) { if (value != null) { // remove any newlines from the value field. Newlines currently cause the event to be lost in AppInsights properties.put(key, value.replace("\r", "").replace("\n", " ")); } else { properties.put(key, ""); //null values cause exceptions } } @Override public String toString() { return properties.toString(); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/ActionHelper.java ================================================ package hudson.plugins.tfs.util; import hudson.model.Action; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; /** * Helper class to create actions. */ public final class ActionHelper { private ActionHelper() { } /** * Adds actions to an existing collection of actions and returns the new array of Actions. */ public static Action[] create(final Collection actions, final Action... additionalActions) { final int totalCount = actions.size() + additionalActions.length; final List allActions = new ArrayList(totalCount); allActions.addAll(actions); Collections.addAll(allActions, additionalActions); final Action[] result = allActions.toArray(new Action[totalCount]); return result; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/BuildVariableResolver.java ================================================ package hudson.plugins.tfs.util; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.Computer; import hudson.model.Job; import hudson.model.TaskListener; import hudson.util.VariableResolver; /** * A {@link VariableResolver} that resolves certain Build variables. *

* The build variable resolver will resolve the following: *

    *
  • JOB_NAME - The name of the job
  • *
  • USER_NAME - The system property "user.name" on the Node that the Launcher * is being executed on (slave or master)
  • *
  • NODE_NAME - The name of the node that the Launcher is being executed on
  • *
  • Any environment variable that is set on the Node that the Launcher is * being executed on (slave or master)
  • *
* * @author Erik Ramfelt */ public class BuildVariableResolver implements VariableResolver { private Map lazyResolvers = new HashMap(); private List> otherResolvers = new ArrayList>(); private final Computer computer; private static final Logger LOGGER = Logger.getLogger(BuildVariableResolver.class.getName()); public BuildVariableResolver(final Job job) { computer = null; lazyResolvers.put("JOB_NAME", new LazyResolver() { public String getValue() { return job.getName(); } }); } public BuildVariableResolver(final Job project, final Computer computer) { this.computer = computer; lazyResolvers.put("JOB_NAME", new LazyResolver() { public String getValue() { return project.getName(); } }); lazyResolvers.put("NODE_NAME", new LazyComputerResolver() { public String getValue(final Computer computer) { return (Util.fixEmpty(computer.getName()) == null ? "MASTER" : computer.getName()); } }); lazyResolvers.put("USER_NAME", new LazyComputerResolver() { public String getValue(final Computer computer) throws IOException, InterruptedException { return (String) computer.getSystemProperties().get("user.name"); } }); } /** * Constructor that can be used with a {@linkplain AbstractBuild} instance. *

* This constructor should not be called in a method that may be called by * {@link AbstractBuild#getEnvVars()}. * @param build used to get the project and the build env vars * @param computer used to retrieve the name of the remote computer or of the account under which Jenkins runs * * @throws IOException If an I/O error occurs * @throws InterruptedException * If the current thread is interrupted while waiting for the completion. */ public BuildVariableResolver(final AbstractBuild build, final Computer computer) throws IOException, InterruptedException { this(build.getProject(), computer); final Map envVars = build.getEnvironment(TaskListener.NULL); if (envVars != null) { otherResolvers.add(new VariableResolver.ByMap(envVars)); } } /** * @return resolved variable or null if the lookup failed */ public String resolve(final String variable) { try { if (lazyResolvers.containsKey(variable)) { return lazyResolvers.get(variable).getValue(); } else { if (computer != null) { otherResolvers.add(new VariableResolver.ByMap(computer.getEnvironment())); } return new VariableResolver.Union(otherResolvers).resolve(variable); } } catch (Exception e) { LOGGER.warning("Variable name '" + variable + "' look up failed because of " + e); } return null; } /** * Simple lazy variable resolver. */ private interface LazyResolver { String getValue() throws IOException, InterruptedException; } /** * Class to handle cases when a Launcher was not created from a computer. * @see Launcher#getComputer() */ private abstract class LazyComputerResolver implements LazyResolver { protected abstract String getValue(Computer computer) throws IOException, InterruptedException; public String getValue() throws IOException, InterruptedException { return getValue(computer); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/BuildWorkspaceConfigurationRetriever.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.util; import java.io.IOException; import hudson.model.AbstractBuild; import hudson.model.Node; import hudson.model.Run; import hudson.plugins.tfs.model.WorkspaceConfiguration; /** * Class for retrieving the latest build configuration for a certain node. * As the data should be stored together with the workspace, but that is not possible * today so it is stored in the build itself. Latest build on a certain node always * contains the SCM configuration for the workspace on that node. * @author Erik Ramfelt, redsolo */ public class BuildWorkspaceConfigurationRetriever { public BuildWorkspaceConfiguration getLatestForNode(Node needleNode, Run latestRun) { if ((latestRun == null) || !(latestRun instanceof AbstractBuild)) { return null; } AbstractBuild build = (AbstractBuild) latestRun; while (build != null) { Node node = build.getBuiltOn(); if (node != null) { if (node.getNodeName().equals(needleNode.getNodeName())) { break; } } build = build.getPreviousBuild(); } if (build != null) { WorkspaceConfiguration configuration = build.getAction(WorkspaceConfiguration.class); if (configuration != null) { return new BuildWorkspaceConfiguration(configuration, build); } } return null; } public static class BuildWorkspaceConfiguration extends WorkspaceConfiguration { private static final long serialVersionUID = 1L; private final transient AbstractBuild build; public BuildWorkspaceConfiguration(WorkspaceConfiguration configuration, AbstractBuild build) { super(configuration); this.build = build; } public void save() throws IOException { if (!workspaceExists()) { build.getAction(WorkspaceConfiguration.class).setWorkspaceWasRemoved(); } build.save(); } @Override public boolean equals(Object o) { return super.equals(o); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/DateUtil.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.util; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.DateVersionSpec; import java.text.DateFormat; import java.text.FieldPosition; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.SimpleTimeZone; import java.util.TimeZone; public class DateUtil { private DateUtil() { } private static final String ISO_8601_DATE_TIME_MINUS_FRACTIONS = "yyyy-MM-dd'T'HH:mm:ss'Z'"; public static final ThreadLocal TFS_DATETIME_FORMATTER = new ThreadLocal() { @Override protected SimpleDateFormat initialValue() { SimpleDateFormat dateFormat = new SimpleDateFormat(ISO_8601_DATE_TIME_MINUS_FRACTIONS); dateFormat.setTimeZone(new SimpleTimeZone(0,"GMT")); return dateFormat; } }; public static String toString(final DateVersionSpec dateVersionSpec) { final Calendar calendar = dateVersionSpec.getDate(); return toString(calendar); } public static String toString(final Calendar calendar) { final Date dateTime = calendar.getTime(); return toString(dateTime); } public static String toString(final Date dateTime) { final FieldPosition fieldPosition = new FieldPosition(-1); final SimpleDateFormat simpleDateFormat = TFS_DATETIME_FORMATTER.get(); final StringBuffer sb = new StringBuffer(1 + ISO_8601_DATE_TIME_MINUS_FRACTIONS.length()); sb.append('D'); simpleDateFormat.format(dateTime, sb, fieldPosition); final String result = sb.toString(); return result; } public static Date parseDate(String dateString) throws ParseException { return parseDate(dateString, Locale.getDefault(), TimeZone.getDefault()); } @SuppressWarnings("deprecation") public static Date parseDate(String dateString, Locale locale, TimeZone timezone) throws ParseException { Date date = null; dateString = dateString.replaceAll("(p|P)\\.(m|M)\\.", "PM").replaceAll("(a|A)\\.(m|M)\\.", "AM"); try { // Use the deprecated Date.parse method as this is very good at detecting // dates commonly output by the US and UK standard locales of dotnet that // are output by the Microsoft command line client. date = new Date(Date.parse(dateString)); } catch (IllegalArgumentException e) { // ignore - parse failed. } if (date == null) { // The old fashioned way did not work. Let's try it using a more // complex alternative. DateFormat[] formats = createDateFormatsForLocaleAndTimeZone(locale, timezone); return parseWithFormats(dateString, formats); } return date; } static Date parseWithFormats(String input, DateFormat[] formats) throws ParseException { ParseException parseException = null; for (int i = 0; i < formats.length; i++) { try { return formats[i].parse(input); } catch (ParseException ex) { parseException = ex; } } if (parseException == null) { throw new IllegalStateException("No dateformats found that can be used for parsing '" + input + "'"); } throw parseException; } /** * Build an array of DateFormats that are commonly used for this locale * and timezone. */ static DateFormat[] createDateFormatsForLocaleAndTimeZone(Locale locale, TimeZone timeZone) { List formats = new ArrayList(); addDateTimeFormatsToList(locale, timeZone, formats); addDateFormatsToList(locale, timeZone, formats); return formats.toArray(new DateFormat[formats.size()]); } static void addDateFormatsToList(Locale locale, TimeZone timeZone, List formats) { for (int dateStyle = DateFormat.FULL; dateStyle <= DateFormat.SHORT; dateStyle++) { DateFormat df = DateFormat.getDateInstance(dateStyle, locale); df.setTimeZone(timeZone); formats.add(df); } } static void addDateTimeFormatsToList(Locale locale, TimeZone timeZone, List formats) { for (int dateStyle = DateFormat.FULL; dateStyle <= DateFormat.SHORT; dateStyle++) { for (int timeStyle = DateFormat.FULL; timeStyle <= DateFormat.SHORT; timeStyle++) { DateFormat df = DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale); if (timeZone != null) { df.setTimeZone(timeZone); } formats.add(df); } } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/EndpointHelper.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.util; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.kohsuke.stapler.HttpResponses; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import javax.servlet.ServletException; import java.io.IOException; import java.io.PrintWriter; public class EndpointHelper { public static final ObjectMapper MAPPER; static { MAPPER = new ObjectMapper(); MAPPER.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); } public static void error(final int code, final Throwable cause) { throw new HttpResponses.HttpResponseException(cause) { public void generateResponse(final StaplerRequest req, final StaplerResponse rsp, final Object node) throws IOException, ServletException { rsp.setStatus(code); rsp.setHeader("X-Error-Message", cause.getMessage()); rsp.setContentType("text/plain;charset=UTF-8"); final PrintWriter w = new PrintWriter(rsp.getWriter()); // TODO: serialize "cause" to JSON write that, instead cause.printStackTrace(w); w.close(); } }; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/KeyValueTextReader.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.util; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Class that parsers key/values from a reader. * This is used when reading text files that contains keys and value pairs where the * value can be in one or several rows. First used when doing some more intelligent parsing * of the output from the tfs history command. * * @author redsolo */ public class KeyValueTextReader { private static final Pattern KEY_VALUE_PATTERN = Pattern.compile("([\\w\\s]*):(.*)"); private static final String CONTINUED_VALUE_STRING = " "; /** * Parses a map of KeyValue pairs from a string. */ public Map parse(final String string) throws IOException { return parse(new BufferedReader(new StringReader(string))); } /** * Parses a map of KeyValue pairs from a buffered reader. */ public Map parse(final BufferedReader reader) throws IOException { HashMap map = null; String line = reader.readLine(); String value = null; String key = null; while (line != null) { if (line.startsWith(CONTINUED_VALUE_STRING)) { value = value + "\n" + line.trim(); } else { if ((value != null) && (key != null)) { if (map == null) { map = new HashMap(); } map.put(key, value.trim()); key = null; value = null; } Matcher matcher = KEY_VALUE_PATTERN.matcher(line); if (matcher.matches()) { key = matcher.group(1); value = matcher.group(2); } } line = reader.readLine(); } if ((value != null) && (key != null)) { if (map == null) { map = new HashMap(); } map.put(key, value.trim()); } return map; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/License.txt ================================================ Git Credential Manager for Mac and Linux Copyright (c) Microsoft Corporation All rights reserved. MIT License 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. ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/MaskedArgumentListBuilder.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.util; import java.util.Collection; import java.util.HashSet; import hudson.util.ArgumentListBuilder; /** * ArgumentListBuilder that supports marking arguments as masked. * * @author Erik Ramfelt */ public class MaskedArgumentListBuilder extends ArgumentListBuilder{ private static final long serialVersionUID = 1L; private Collection maskedArgumentIndex; @Override public ArgumentListBuilder prepend(String... args) { if (maskedArgumentIndex != null) { Collection newMaskedArgumentIndex = new HashSet(); for (Integer argIndex : maskedArgumentIndex) { newMaskedArgumentIndex.add(argIndex + args.length); } maskedArgumentIndex = newMaskedArgumentIndex; } return super.prepend(args); } /** * Returns true if there are any masked arguments. * @return true if there are any masked arguments; false otherwise */ public boolean hasMaskedArguments() { return (maskedArgumentIndex != null); } /** * Returns an array of booleans where the masked arguments are marked as true * @return an array of booleans. */ public boolean[] toMaskArray() { String[] commands = toCommandArray(); boolean[] mask = new boolean[commands.length]; if (maskedArgumentIndex != null) { for (Integer argIndex : maskedArgumentIndex) { mask[argIndex] = true; } } return mask; } /** * Add a masked argument * @param string the argument */ public void addMasked(String string) { if (maskedArgumentIndex == null) { maskedArgumentIndex = new HashSet(); } maskedArgumentIndex.add(toCommandArray().length); add(string); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/MediaType.java ================================================ package hudson.plugins.tfs.util; import java.nio.charset.Charset; /** * Media Type constants. */ public final class MediaType { private MediaType() { } public static final Charset UTF_8 = Charset.forName("UTF-8"); public static final String APPLICATION_FORM_URLENCODED = "application/x-www-form-urlencoded"; public static final String APPLICATION_JSON = "application/json"; public static final String APPLICATION_JSON_UTF_8 = "application/json; charset=utf-8"; public static final String APPLICATION_JSON_PATCH_JSON = "application/json-patch+json"; public static final String APPLICATION_JSON_PATCH_JSON_UTF_8 = "application/json-patch+json; charset=utf-8"; public static final String APPLICATION_ZIP = "application/zip"; public static final String TEXT_PLAIN = "text/plain"; } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/QueryString.java ================================================ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See sibling License.txt file package hudson.plugins.tfs.util; import java.util.LinkedHashMap; /** * Helper to construct query strings for urls. */ public class QueryString extends LinkedHashMap { public QueryString() { } public QueryString(final String... nameValuePairs) { final int length = nameValuePairs.length; if (length % 2 != 0) { final String message = "This QueryString constructor needs an even number of parameters"; throw new IllegalArgumentException(message); } for (int i = 0; i < length; i += 2) { put(nameValuePairs[i], nameValuePairs[i + 1]); } } @Override public String toString() { return UriHelper.serializeParameters(this); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/ResourceHelper.java ================================================ package hudson.plugins.tfs.util; import org.apache.commons.io.IOUtils; import java.io.IOException; import java.io.InputStream; /** * Helper to get resources. */ public final class ResourceHelper { private ResourceHelper() { } /** * Gets the resource file and returns it as a string. */ public static String fetchAsString(final Class referenceClass, final String fileName) { final InputStream stream = referenceClass.getResourceAsStream(fileName); try { return IOUtils.toString(stream, MediaType.UTF_8); } catch (final IOException e) { throw new Error(e); } finally { IOUtils.closeQuietly(stream); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/StringBodyParameter.java ================================================ package hudson.plugins.tfs.util; import org.apache.commons.io.IOUtils; import org.kohsuke.stapler.AnnotationHandler; import org.kohsuke.stapler.InjectedParameter; import org.kohsuke.stapler.StaplerRequest; import javax.servlet.ServletException; import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.logging.Level; import java.util.logging.Logger; /** * {@link InjectedParameter} annotation to use on * {@link org.kohsuke.stapler.WebMethod} parameters to extract the body of the request * as a single string. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) @Documented @InjectedParameter(StringBodyParameter.StringBodyHandler.class) public @interface StringBodyParameter { /** * Webmethod parameter annotation that extracts the body of the request. */ class StringBodyHandler extends AnnotationHandler { private static final Logger LOGGER = Logger.getLogger(StringBodyHandler.class.getName()); @Override public Object parse(final StaplerRequest request, final StringBodyParameter a, final Class type, final String parameterName) throws ServletException { final String rawContentType = request.getContentType(); final String contentType = StringHelper.determineContentTypeWithoutCharset(rawContentType); if (MediaType.APPLICATION_FORM_URLENCODED.equals(contentType)) { return request.getParameter(parameterName); } // default to application/json final String characterEncoding = request.getCharacterEncoding(); try { return IOUtils.toString(request.getInputStream(), characterEncoding); } catch (final IOException e) { LOGGER.log(Level.SEVERE, "Unable to obtain request body: {}", e.getMessage()); } return null; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/StringHelper.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.util; public class StringHelper { public static boolean endsWithIgnoreCase(final String haystack, final String needle) { if (haystack == null) throw new IllegalArgumentException("Parameter 'haystack' is null."); if (needle == null) throw new IllegalArgumentException("Parameter 'needle' is null."); final int nl = needle.length(); final int hl = haystack.length(); if (nl == hl) { return haystack.equalsIgnoreCase(needle); } if (nl > hl) { return false; } // Inspired by https://stackoverflow.com/a/19154150/ final int toffset = hl - nl; return haystack.regionMatches(true, toffset, needle, 0, nl); } public static boolean equal(final String a, final String b) { return innerEqual(a, b, false); } public static boolean equalIgnoringCase(final String a, final String b) { return innerEqual(a, b, true); } static boolean innerEqual(final String a, final String b, final boolean ignoreCase) { if (a == null) { return b == null; } if (b == null) { return false; } final int length = a.length(); if (length != b.length()) { return false; } return a.regionMatches(ignoreCase, 0, b, 0, length); } public static String determineContentTypeWithoutCharset(final String contentType) { if (contentType == null) { return null; } final int indexOfSemicolon = contentType.indexOf(';'); if (indexOfSemicolon != -1) { final String beforeCharset = contentType.substring(0, indexOfSemicolon); return beforeCharset.trim(); } return contentType; } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/TeamRestClient.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.util; import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; import com.microsoft.tfs.core.httpclient.HttpClient; import com.microsoft.tfs.core.httpclient.NameValuePair; import com.microsoft.tfs.util.StringUtil; import com.microsoft.visualstudio.services.webapi.patch.Operation; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.plugins.tfs.TeamCollectionConfiguration; import hudson.plugins.tfs.model.GitCodePushedEventArgs; import hudson.plugins.tfs.model.HttpMethod; import hudson.plugins.tfs.model.JobCompletionEventArgs; import hudson.plugins.tfs.model.JsonPatchOperation; import hudson.plugins.tfs.model.Link; import hudson.plugins.tfs.model.ListOfGitRepositories; import hudson.plugins.tfs.model.PullRequestMergeCommitCreatedEventArgs; import hudson.plugins.tfs.model.Server; import hudson.plugins.tfs.model.TeamGitStatus; import hudson.plugins.tfs.model.WorkItem; import hudson.util.Secret; import net.sf.json.JSON; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.apache.commons.io.IOUtils; import javax.xml.bind.DatatypeConverter; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URI; public class TeamRestClient { private static final String AUTHORIZATION = "Authorization"; private static final String API_VERSION = "api-version"; private static final String NEW_LINE = System.getProperty("line.separator"); private final URI collectionUri; private final boolean isTeamServices; private final String authorization; private final Server server; public TeamRestClient(final URI collectionUri) throws IOException { this(collectionUri, TeamCollectionConfiguration.findCredentialsForCollection(collectionUri)); } public TeamRestClient(final URI collectionUri, final StandardUsernamePasswordCredentials credentials) throws IOException { this.collectionUri = collectionUri; final String hostName = collectionUri.getHost(); this.server = Server.create(null, null, collectionUri.toString(), credentials, null, null); isTeamServices = TeamCollectionConfiguration.isTeamServices(hostName); if (isTeamServices && credentials != null) { authorization = createAuthorization(credentials); } else { authorization = null; } } public TeamRestClient(final String collectionUri, final StandardUsernamePasswordCredentials credentials) throws IOException { this(URI.create(collectionUri), credentials); } static String createAuthorization(final StandardUsernamePasswordCredentials credentials) { final String username = credentials.getUsername(); final Secret secretPassword = credentials.getPassword(); final String password = secretPassword.getPlainText(); final String credPair = username + ":" + password; final byte[] credBytes = credPair.getBytes(MediaType.UTF_8); final String base64enc = DatatypeConverter.printBase64Binary(credBytes); final String result = "Basic " + base64enc; return result; } protected TResponse request( final Class responseClass, final HttpMethod httpMethod, final URI requestUri, final TRequest requestBody ) throws IOException { return request(responseClass, httpMethod, requestUri, requestBody, null); } protected TResponse request( final Class responseClass, final HttpMethod httpMethod, final URI requestUri, final TRequest requestBody, final NameValuePair[] additionalRequestHeaders ) throws IOException { final HttpClient httpClient = server.getHttpClient(); final String stringRequestBody; if (requestBody != null) { final JSON jsonObject; if (requestBody instanceof JSON) { jsonObject = (JSON) requestBody; } else { jsonObject = JSONObject.fromObject(requestBody); } stringRequestBody = jsonObject.toString(0); } else { stringRequestBody = null; } final com.microsoft.tfs.core.httpclient.HttpMethod clientMethod = httpMethod.createClientMethod(requestUri.toString(), stringRequestBody); if (authorization != null) { clientMethod.addRequestHeader(AUTHORIZATION, authorization); } if (additionalRequestHeaders != null && additionalRequestHeaders.length > 0){ for(NameValuePair pair : additionalRequestHeaders) { clientMethod.addRequestHeader(pair.getName(), pair.getValue()); } } final String stringResponseBody = innerRequest(clientMethod, httpClient); if (responseClass == Void.class) { return null; } if (responseClass == String.class) { return (TResponse) stringResponseBody; } final TResponse result = deserialize(responseClass, stringResponseBody); return result; } public static TResponse deserialize(final Class responseClass, final String stringResponseBody) { try { return EndpointHelper.MAPPER.readValue(stringResponseBody, responseClass); } catch (final IOException e) { throw new Error(e); } } static String innerRequest(final com.microsoft.tfs.core.httpclient.HttpMethod clientMethod, final HttpClient httpClient) throws IOException { final int httpStatus = httpClient.executeMethod(clientMethod); final String stringResult; InputStream responseStream = null; try { if (httpStatus >= HttpURLConnection.HTTP_BAD_REQUEST) { responseStream = clientMethod.getResponseBodyAsStream(); final String responseText = readResponseText(responseStream); final StringBuilder sb = new StringBuilder("HTTP ").append(httpStatus); final String statusText = clientMethod.getStatusText(); if (statusText != null) { sb.append(" (").append(statusText).append(")"); } if (!StringUtil.isNullOrEmpty(responseText)) { sb.append(": ").append(responseText); } throw new IOException(sb.toString()); } responseStream = clientMethod.getResponseBodyAsStream(); stringResult = readResponseText(responseStream); } finally { IOUtils.closeQuietly(responseStream); } return stringResult; } @SuppressFBWarnings(value = "DM_DEFAULT_ENCODING", justification = "Better mot modify charset in case it might raise errors") static String readResponseText(final InputStream inputStream) throws IOException { final InputStreamReader isr = new InputStreamReader(inputStream); final BufferedReader reader = new BufferedReader(isr); final StringBuilder sb = new StringBuilder(); try { String line; while ((line = reader.readLine()) != null) { sb.append(line); sb.append(NEW_LINE); } } finally { IOUtils.closeQuietly(reader); IOUtils.closeQuietly(isr); IOUtils.closeQuietly(inputStream); } return sb.toString(); } public String ping() throws IOException { final URI requestUri; if (isTeamServices) { requestUri = UriHelper.join(collectionUri, "_apis", "connectiondata"); } else { requestUri = UriHelper.join(collectionUri, "_home", "About"); } return request(String.class, HttpMethod.GET, requestUri, null); } public ListOfGitRepositories getRepositories() throws IOException { final QueryString qs = new QueryString(API_VERSION, "1.0"); final URI requestUri = UriHelper.join( collectionUri, "_apis", "git", "repositories", qs ); return request(ListOfGitRepositories.class, HttpMethod.GET, requestUri, null); } public TeamGitStatus addCommitStatus(final GitCodePushedEventArgs args, final TeamGitStatus status) throws IOException { final QueryString qs = new QueryString(API_VERSION, "2.1"); final URI requestUri = UriHelper.join( collectionUri, args.projectId, "_apis", "git", "repositories", args.repoId, "commits", args.commit, "statuses", qs); return request(TeamGitStatus.class, HttpMethod.POST, requestUri, status); } public WorkItem getWorkItem(final int workItemId) throws IOException { final QueryString qs = new QueryString(API_VERSION, "1.0"); final URI requestUri = UriHelper.join( collectionUri, "_apis", "wit", "workitems", workItemId, qs ); return request(WorkItem.class, HttpMethod.GET, requestUri, null); } public void addHyperlinkToWorkItem(final int workItemId, final String hyperlink) throws IOException { final JSONArray doc = new JSONArray(); final WorkItem workItem = getWorkItem(workItemId); final JsonPatchOperation testRev = new JsonPatchOperation(); testRev.setOp(Operation.TEST); testRev.setPath("/rev"); testRev.setValue(workItem.getRev()); doc.add(testRev); // TODO: do we also need to "add" to "/fields/System.History"? final Link link = new Link("Hyperlink", hyperlink); final JsonPatchOperation addRelation = new JsonPatchOperation(); addRelation.setOp(Operation.ADD); addRelation.setPath("/relations/-"); addRelation.setValue(link); doc.add(addRelation); final QueryString qs = new QueryString(API_VERSION, "1.0"); final URI requestUri = UriHelper.join( collectionUri, "_apis", "wit", "workitems", workItemId, qs); // TODO: this call could fail because something else bumped the rev in the meantime; retry? request(Void.class, HttpMethod.PATCH, requestUri, doc); } public TeamGitStatus addPullRequestStatus(final PullRequestMergeCommitCreatedEventArgs args, final TeamGitStatus status) throws IOException { final QueryString qs = new QueryString(API_VERSION, "4.1-preview"); final URI requestUri = UriHelper.join( collectionUri, args.projectId, "_apis", "git", "repositories", args.repoId, "pullRequests", args.pullRequestId, "statuses", qs); return request(TeamGitStatus.class, HttpMethod.POST, requestUri, status); } public TeamGitStatus addPullRequestIterationStatus(final PullRequestMergeCommitCreatedEventArgs args, final TeamGitStatus status) throws IOException { final QueryString qs = new QueryString(API_VERSION, "4.1-preview"); final URI requestUri = UriHelper.join( collectionUri, args.projectId, "_apis", "git", "repositories", args.repoId, "pullRequests", args.pullRequestId, "iterations", args.iterationId, "statuses", qs); return request(TeamGitStatus.class, HttpMethod.POST, requestUri, status); } public void sendJobCompletionEvent(final JobCompletionEventArgs args) throws IOException { final QueryString qs = new QueryString( API_VERSION, "3.2", "publisherId", "jenkins", "channelId", args.getServerKey()); final URI requestUri = UriHelper.join( collectionUri, "_apis", "public", "hooks", "externalEvents", qs); final JSONObject json = JSONObject.fromObject(args.getPayload()); final NameValuePair[] headers = new NameValuePair[3]; headers[0] = new NameValuePair("X-Event-Key", "job:completion"); headers[1] = new NameValuePair("X-Jenkins-Signature", args.getPayloadSignature()); headers[2] = new NameValuePair("X-Jenkins-ServerKey", args.getServerKey()); request(Void.class, HttpMethod.POST, requestUri, json, headers); } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/TeamStatus.java ================================================ package hudson.plugins.tfs.util; import hudson.model.Job; import hudson.model.Run; import hudson.model.TaskListener; import hudson.plugins.tfs.CommitParameterAction; import hudson.plugins.tfs.PullRequestParameterAction; import hudson.plugins.tfs.UnsupportedIntegrationAction; import hudson.plugins.tfs.model.GitCodePushedEventArgs; import hudson.plugins.tfs.model.PullRequestMergeCommitCreatedEventArgs; import hudson.plugins.tfs.model.TeamGitStatus; import hudson.plugins.tfs.telemetry.TelemetryHelper; import javax.annotation.Nonnull; import java.io.IOException; import java.io.PrintStream; import java.net.URI; /** * Creates and adds a TeamGitStatus to the run. */ public final class TeamStatus { private TeamStatus() { } /** * Creates and adds a TeamGitStatus to the run. */ public static void createFromRun(@Nonnull final Run run, @Nonnull final TaskListener listener, final String featureDisplayName) throws IOException { if (!UnsupportedIntegrationAction.isSupported(run, listener)) { final PrintStream logger = listener.getLogger(); logger.print("NOTICE: "); logger.print("You selected '"); logger.print(featureDisplayName); logger.println("' on your Jenkins job, but this option has no effect when calling the job from the 'Jenkins Queue Job' task in TFS/Team Services."); return; } final CommitParameterAction commitParameter = run.getAction(CommitParameterAction.class); final GitCodePushedEventArgs gitCodePushedEventArgs; final PullRequestMergeCommitCreatedEventArgs pullRequestMergeCommitCreatedEventArgs; if (commitParameter != null) { gitCodePushedEventArgs = commitParameter.getGitCodePushedEventArgs(); if (commitParameter instanceof PullRequestParameterAction) { final PullRequestParameterAction prpa = (PullRequestParameterAction) commitParameter; pullRequestMergeCommitCreatedEventArgs = prpa.getPullRequestMergeCommitCreatedEventArgs(); } else { pullRequestMergeCommitCreatedEventArgs = null; } } else { // TODO: try to guess based on what we _do_ have (i.e. RevisionParameterAction) return; } final URI collectionUri = gitCodePushedEventArgs.collectionUri; final TeamGitStatus status = TeamGitStatus.fromRun(run); // Send telemetry TelemetryHelper.sendEvent("team-status", new TelemetryHelper.PropertyMapBuilder() .serverContext(collectionUri.toString(), collectionUri.toString()) .pair("feature", featureDisplayName) .pair("status", status.state.toString()) .build()); addStatus(pullRequestMergeCommitCreatedEventArgs, status); } /** * Create status for a (queued) Job. */ public static void createFromJob(final PullRequestMergeCommitCreatedEventArgs pullRequestMergeCommitCreatedEventArgs, final Job job) throws IOException { final TeamGitStatus status = TeamGitStatus.fromJob(job); addStatus(pullRequestMergeCommitCreatedEventArgs, status); } private static void addStatus(final PullRequestMergeCommitCreatedEventArgs gitCodePushedEventArgs, final TeamGitStatus status) throws IOException { final PullRequestMergeCommitCreatedEventArgs pullRequestMergeCommitCreatedEventArgs; if (gitCodePushedEventArgs instanceof PullRequestMergeCommitCreatedEventArgs) { pullRequestMergeCommitCreatedEventArgs = (PullRequestMergeCommitCreatedEventArgs) gitCodePushedEventArgs; } else { pullRequestMergeCommitCreatedEventArgs = null; } final URI collectionUri = gitCodePushedEventArgs.collectionUri; final TeamRestClient client = new TeamRestClient(collectionUri); // TODO: when code is pushed and polling happens, are we sure we built against the requested commit? if (pullRequestMergeCommitCreatedEventArgs != null) { if (pullRequestMergeCommitCreatedEventArgs.iterationId == -1) { client.addPullRequestStatus(pullRequestMergeCommitCreatedEventArgs, status); } else { client.addPullRequestIterationStatus(pullRequestMergeCommitCreatedEventArgs, status); } } client.addCommitStatus(gitCodePushedEventArgs, status); // TODO: we could contribute an Action to the run, recording the ID of the status we created } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/TextTableParser.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.util; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Parser for the text table output from the TF tool. * The table will always begin with a number of columns that specifies how wide each * column is. The text table parser will use the number of columns and their width to * be able to parse the lines that comes after the column definition. Some columns * are optional, ie they does not contain any data and the line ends before the * optional column begins. *

* An example of how a separator definition may look. The table consists of 3 columns; * column 1 contains a string with at most 5 chars, column 2 contains a string with at most * 2 chars and column 3 may contain a string with at most 4 chars. The third column is optional. *

 * ----- -- ----
 * Data1 A  Data
 * Data2 B
 * Data3 C  Data
 * 
* * TextTableParser t = new TextTableParser(reader, 1); * t.nextRow() // first row * assertEquals("Data1", t.getColumn(0)); * assertEquals("Data", t.getColumn(2)); * t.nextRow(); // Second row * assertEquals("Data2", t.getColumn(0)); * assertNull(t.getColumn(2)); * t.nextRow(); // Third row * assertEquals("Data3", t.getColumn(0)); * assertEquals("Data", t.getColumn(2)); * * @author Erik Ramfelt */ public class TextTableParser { private static final Pattern SEPARATOR_PATTERN = Pattern.compile("(-+)"); private final BufferedReader reader; private List columns; private String currentLine; private final int optionalColumnCount; private int lastMandatoryColumnStart; public TextTableParser(Reader reader) throws IOException { this(reader, 0); } public TextTableParser(Reader reader, int optionalColumnCount) throws IOException { this.reader = new BufferedReader( reader ); this.optionalColumnCount = optionalColumnCount; init(); } private void init() throws IOException { String line = reader.readLine(); columns = new ArrayList(); while (line != null) { if (line.startsWith("-")) { Matcher matcher = SEPARATOR_PATTERN.matcher(line); if (matcher.find()) { do { columns.add(new ColumnRange(matcher.start(), matcher.end())); } while (matcher.find()); break; } } line = reader.readLine(); } if (columns.size() > 0){ lastMandatoryColumnStart = columns.get(columns.size() - 1 - optionalColumnCount).start; } } /** * Returns the number of columns * @return the number of columns */ public int getColumnCount() { return columns.size(); } /** * Return the value in the specified column * @param index the column index * @return the value in the specified column; null if there is no value (the column is optional) */ public String getColumn(int index) { if (currentLine == null) { throw new IllegalStateException("There is no active row."); } ColumnRange columnRange = columns.get(index); if (currentLine.length() < columnRange.start) { return null; } if (currentLine.length() < columnRange.end) { return currentLine.substring(columnRange.start).trim(); } else { return currentLine.substring(columnRange.start, columnRange.end).trim(); } } /** * Move to the next row * @return true, if there was a next row; false, if there is no next row. * @throws IOException If an I/O error occurs */ public boolean nextRow() throws IOException { do { currentLine = reader.readLine(); } while ((currentLine != null) && (currentLine.length() < lastMandatoryColumnStart)); return (currentLine != null); } private static class ColumnRange { private final int start; private final int end; public ColumnRange(int start, int end) { this.start = start; this.end = end; } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/UriHelper.java ================================================ //CHECKSTYLE:OFF // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See sibling License.txt file package hudson.plugins.tfs.util; import org.eclipse.jgit.transport.URIish; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.Scanner; import java.util.TreeMap; public class UriHelper { private static final Map SCHEMES_TO_DEFAULT_PORTS; public static final String UTF_8 = "UTF-8"; private static final String DEFAULT_COLLECTION = "DefaultCollection"; static { final Map defaultPorts = new TreeMap(String.CASE_INSENSITIVE_ORDER); defaultPorts.put("ftp", 21); defaultPorts.put("ssh", 22); defaultPorts.put("http", 80); defaultPorts.put("https", 443); SCHEMES_TO_DEFAULT_PORTS = Collections.unmodifiableMap(defaultPorts); } /** * Compares two {@link URI} instances to determine if they are equivalent. * For example, * {@code HTTP://WWW.EXAMPLE.COM:80/} * and * {@code http://www.example.com} * are considered equivalent. * This method handles a few more cases than {@link URI#equals(Object)}, such that the scheme's * default port number will be considered, as will the default path for hosts. * * @param a the first URI * @param b the second URI * @return {@code true} if a and b represent the same resource; {@code false} otherwise. */ public static boolean areSame(final URI a, final URI b) { if (a == null) { return b == null; } if (b == null) { return false; } if (!StringHelper.equalIgnoringCase(a.getScheme(), b.getScheme())) { return false; } if (!StringHelper.equalIgnoringCase(a.getHost(), b.getHost())) { return false; } final int aPort = normalizePort(a); final int bPort = normalizePort(b); if (aPort != bPort) { return false; } final String aPath = normalizePath(a); final String bPath = normalizePath(b); if (!StringHelper.equal(aPath, bPath)) { return false; } if (!StringHelper.equal(a.getQuery(), b.getQuery())) { return false; } if (!StringHelper.equal(a.getFragment(), b.getFragment())) { return false; } return true; } public static boolean areSameGitRepo(final URIish a, final URIish b) { final URI uriA = a == null ? null : URI.create(a.toString()); final URI uriB = b == null ? null : URI.create(b.toString()); return areSameGitRepo(uriA, uriB); } public static boolean areSameGitRepo(final URI a, final URI b) { if (a == null) { return b == null; } if (b == null) { return false; } if (StringHelper.equalIgnoringCase(a.getScheme(), b.getScheme())) { final int aPort = normalizePort(a); final int bPort = normalizePort(b); if (aPort != bPort) { return false; } } if (!StringHelper.equalIgnoringCase(a.getHost(), b.getHost())) { return false; } final String aPath = normalizePath(a); final String bPath = normalizePath(b); if (StringHelper.equal(aPath, bPath)) { return true; } final Iterator aPathParts = decomposePath(aPath); boolean aSeenDefaultCollection = false; final Iterator bPathParts = decomposePath(bPath); boolean bSeenDefaultCollection = false; while (aPathParts.hasNext() && bPathParts.hasNext()) { String aPart = aPathParts.next(); String bPart = bPathParts.next(); if (StringHelper.equalIgnoringCase(DEFAULT_COLLECTION, aPart) && aPathParts.hasNext() && !aSeenDefaultCollection) { aPart = aPathParts.next(); aSeenDefaultCollection = true; } if (StringHelper.equalIgnoringCase(DEFAULT_COLLECTION, bPart) && bPathParts.hasNext() && !bSeenDefaultCollection) { bPart = bPathParts.next(); bSeenDefaultCollection = true; } if (!StringHelper.equalIgnoringCase(aPart, bPart)) { return false; } } return true; } static int normalizePort(final URI uri) { int port = uri.getPort(); if (port == -1) { final String scheme = uri.getScheme(); if (scheme != null) { if (SCHEMES_TO_DEFAULT_PORTS.containsKey(scheme)) { port = SCHEMES_TO_DEFAULT_PORTS.get(scheme); } } } return port; } static String normalizePath(final URI uri) { String path = uri.getPath(); if (path == null) { path = "/"; } else { if (!path.endsWith("/")) { path = path + "/"; } } return path; } static Iterator decomposePath(final String path) { return new Scanner(path).useDelimiter("/"); } public static boolean hasPath(final URI uri) { final String path = uri.getPath(); if (path != null) { if (path.length() > 0 && !path.equals("/")) { return true; } } return false; } public static boolean isWellFormedUriString(final String uriString) { try { new URI(uriString); return true; } catch (final URISyntaxException ignored) { return false; } } public static URI join(final URI collectionUri, final Object... components) { return join(collectionUri.toString(), components); } public static URI join(final String collectionUrl, final Object... components) { final StringBuilder sb = new StringBuilder(collectionUrl); final boolean baseEndedWithSlash = endsWithSlash(sb); boolean first = true; for (final Object component : components) { boolean hasSlash = false; if (component instanceof QueryString) { final QueryString queryString = (QueryString) component; if (first) { if (!baseEndedWithSlash) { sb.append('/'); } } sb.append("?"); sb.append(queryString.toString()); // a QueryString must be the last of the components break; } else { if (first) { first = false; if (!baseEndedWithSlash) { sb.append('/'); } } else { sb.append('/'); } try { final String encodedComponent = URLEncoder.encode(component.toString(), UTF_8); // URLEncoder#encode() encodes spaces as "+" but they should be "%20" final String correctlyEncodedComponent = encodedComponent.replaceAll("\\+", "%20"); sb.append(correctlyEncodedComponent); } catch (final UnsupportedEncodingException e) { throw new Error(e); } } } final String uriString = sb.toString(); return URI.create(uriString); } static boolean endsWithSlash(final StringBuilder stringBuilder) { final int length = stringBuilder.length(); return length > 0 && stringBuilder.charAt(length - 1) == '/'; } public static String serializeParameters(final Map parameters) { try { final StringBuilder sb = new StringBuilder(); final Iterator> iterator = parameters.entrySet().iterator(); if (iterator.hasNext()) { Map.Entry entry; String key; String encodedKey; String value; String encodedValue; entry = iterator.next(); key = entry.getKey(); encodedKey = URLEncoder.encode(key, UTF_8); sb.append(encodedKey); value = entry.getValue(); if (value != null) { encodedValue = URLEncoder.encode(value, UTF_8); sb.append('=').append(encodedValue); } while (iterator.hasNext()) { sb.append('&'); entry = iterator.next(); key = entry.getKey(); encodedKey = URLEncoder.encode(key, UTF_8); sb.append(encodedKey); value = entry.getValue(); if (value != null) { encodedValue = URLEncoder.encode(value, UTF_8); sb.append('=').append(encodedValue); } } } return sb.toString(); } catch (final UnsupportedEncodingException e) { throw new Error(e); } } } ================================================ FILE: tfs/src/main/java/hudson/plugins/tfs/util/XmlHelper.java ================================================ //CHECKSTYLE:OFF package hudson.plugins.tfs.util; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class XmlHelper { private static final XPathFactory XPF = XPathFactory.newInstance(); private static final DocumentBuilderFactory DBF = DocumentBuilderFactory.newInstance(); private static final TransformerFactory TF = TransformerFactory.newInstance(); public static String peekValue(final Document doc, final String xpathExpression) throws XPathExpressionException { final XPath xPath = XPF.newXPath(); final XPathExpression expression = xPath.compile(xpathExpression); final Node node = (Node) expression.evaluate(doc, XPathConstants.NODE); final String result = (node != null) ? node.getTextContent() : null; return result; } public static String peekValue(final File file, final String xpathExpression) throws ParserConfigurationException, IOException, SAXException, XPathExpressionException { final DocumentBuilder db = DBF.newDocumentBuilder(); final FileInputStream fis = new FileInputStream(file); final Document doc; try { doc = db.parse(fis); } finally { fis.close(); } final String result = peekValue(doc, xpathExpression); return result; } public static void pokeValue(final Document doc, final String xpathExpression, final String value) throws XPathExpressionException { final XPath xPath = XPF.newXPath(); final XPathExpression expression = xPath.compile(xpathExpression); final Node node = (Node) expression.evaluate(doc, XPathConstants.NODE); // or setValue()? node.setTextContent(value); } public static void pokeValue(final File file, final String xpathExpression, final String value) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException, TransformerException { // TODO: it might be better performance to do a SAX read/write final DocumentBuilder db = DBF.newDocumentBuilder(); final FileInputStream fis = new FileInputStream(file); final Document doc; try { doc = db.parse(fis); } finally { fis.close(); } pokeValue(doc, xpathExpression, value); final Transformer t = TF.newTransformer(); final DOMSource source = new DOMSource(doc); final FileOutputStream fos = new FileOutputStream(file); try { final StreamResult result = new StreamResult(fos); t.transform(source, result); } finally { fos.close(); } } } ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TFSLabeler/config.jelly ================================================ ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TFSLabeler/global.jelly ================================================ ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamBuildDetailsAction/index.jelly ================================================

${it.displayName}

Triggered by TFS/Team Services build ${it.buildNumber} of '${it.buildDefinitionName}'
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamBuildDetailsAction/summary.jelly ================================================ Triggered by TFS/Team Services build ${it.buildNumber} of '${it.buildDefinitionName}' ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamBuildEndpoint.html ================================================

The /%1$s/ endpoint

Security

When your Jenkins is secured, you can use HTTP BASIC authentication to authenticate remote API requests. See Authenticating scripted clients for more details.

Cross Site Request Forgery (CSRF) protection

If your Jenkins uses the "Prevent Cross Site Request Forgery exploits" security option (which it should), when you make a POST request, you have to send a CSRF protection token as an HTTP request header. See CSRF Protection for more details.

WARNING

This endpoint SHOULD be reachable only through a secure channel.

Build commands

The following build commands are available to POST to: %2$s
Name Path Sample payload

Example

Suppose we have a secured Jenkins server at %3$s, a dedicated Remote Trigger user named remote that has an API Token of 92e9d8998a8c005697d252f09a2a311b and the following payload.json file:
{
    "parameter":
    [
        {"name":"id","value":"123"},
        {"name":"verbosity","value":"high"}
    ]
}
        
The following example demonstrates a GET to the /crumbIssuer endpoint to retrieve a header and then a POST to the ping command for the job named JOB_NAME using the curl tool:
crumb=$(curl '%3$scrumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)' --user remote:92e9d8998a8c005697d252f09a2a311b)
curl --request POST %3$s%1$s/ping/JOB_NAME --data-urlencode json@payload.json --user remote:92e9d8998a8c005697d252f09a2a311b --header "$crumb"
        
The console output should look like the following when the ping command from the %1$s endpoint returns the provided payload, formatted for compactness:
{"parameter":[{"name":"id","value":"123"},{"name":"verbosity","value":"high"}]}
        
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamCollectResultsPostBuildAction/config.groovy ================================================ package hudson.plugins.tfs.TeamCollectResultsPostBuildAction; def f = namespace(lib.FormTagLib); // this would look/feel nicer if the Add button was a drop-down list, like for build steps f.entry(title: _("Build results to collect")) { f.repeatableProperty(field: "requestedResults") { f.entry { div(align: "right") { f.repeatableDeleteButton() } } } } ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamCollectResultsPostBuildAction/help.html ================================================
At the end of the build, files matching each of the requested result types will be archived for retrieval by TFS/Team Services.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamCollectionConfiguration/config.groovy ================================================ package hudson.plugins.tfs.TeamCollectionConfiguration; def f = namespace(lib.FormTagLib); def c = namespace(lib.CredentialsTagLib); f.entry(title: _("Collection URL"), field: "collectionUrl") { f.textbox() } f.entry(title: _("Credentials"), field: "credentialsId", description: "Depending on the integration features used, the user account or personal access token may need code_read, code_status and/or work_write permissions") { c.select() } f.block() { f.validateButton( title: _("Test connection"), progress: _("Testing..."), method: "testCredentials", with: "collectionUrl,credentialsId" ) } f.entry { div(align: "right") { f.repeatableDeleteButton() } } ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamCollectionConfiguration/help-collectionUrl.html ================================================
The URL to the TFS/Team Services team project collection.
If you are using Team Services, please omit the /DefaultCollection.
Examples:
  • http://tfs.example.com:8080/tfs/DefaultCollection
  • https://tfs.example.com:8080/tfs/CustomCollection
  • https://fabrikam-fiber-inc.visualstudio.com
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamCompletedStatusPostBuildAction/help.html ================================================
At the end of the build, the status of the associated pull request and/or commit will be updated in TFS/Team Services with the results of the build, including a link back to the Jenkins build.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamEventsEndpoint.html ================================================

The /%1$s/ endpoint

Cross Site Request Forgery (CSRF) protection

If your Jenkins uses the "Prevent Cross Site Request Forgery exploits" security option (which it should), when you make a POST request, you have to send a CSRF protection token as an HTTP request header. See CSRF Protection for more details.

Events

The following events are available to POST to: %2$s
Name Path Sample payload

Example

Suppose we have a Jenkins server at %3$s and the following payload.json file:
{
    "eventType": "ping",
    "resource":
    {
        "message": "Hello, world!"
    }
}
        
The following example demonstrates a GET to the /crumbIssuer endpoint to retrieve a header and then a POST to the ping event using the curl tool:
crumb=$(curl '%3$scrumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)')
curl --request POST %3$s%1$s/ping --header 'Content-Type: application/json' --data-binary @payload.json --header "$crumb"
        
The console output should look like the following when the ping event from the %1$s endpoint returns the provided payload, formatted for compactness:
{"message":"Hello, world!"}
        
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamFoundationServerScm/config.jelly ================================================ ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamFoundationServerScm/config_fr.properties ================================================ # The MIT License # # Copyright (c) 2004-2010, Sun Microsystems, 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. Local\ workfolder\ is\ mandatory,\ empty\ field\ will\ use\ job\ workspace\ as\ workfolder.=Le dossier de travail local est obligatoire, si le champ est vide le workspace du job sera utilis\u00E9 Collection\ URL\ is\ mandatory.=L''URL de la collection de projets d''\u00E9quipe est obligatoire ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamFoundationServerScm/help-cloakedPaths.html ================================================

Paths that are cloaked will not be pulled into the local workspace during a GET from the TFVC repository.
Check-ins that contain files only in cloaked folders (in other words, fully cloaked) will not trigger a build, whereas a check-in containing any path that isn't cloaked will trigger a build.

For example, suppose the Project path is $/Example/project/path, and the repository contains the following subfolders:

$/Example/project/path/A
$/Example/project/path/A/1
$/Example/project/path/A/2
$/Example/project/path/B
$/Example/project/path/C
Now, suppose the following paths were entered as Cloaked Paths:
$/Example/project/path/A/2
$/Example/project/path/B
...then the resulting workspace on the Jenkins server would only have the following folders:
$/Example/project/path/A
$/Example/project/path/A/1
$/Example/project/path/C

To continue with the example, the following check-in will not trigger a build, because it only contains changes under cloaked paths:

$/Example/project/path/A/2/alpha.txt
$/Example/project/path/A/2/second.txt
$/Example/project/path/B/bravo.txt
...whereas this check-in will trigger a build, because it contains at least one path that isn't cloaked:
$/Example/project/path/A/1/first.txt
$/Example/project/path/A/2/second.txt
$/Example/project/path/B/bravo.txt

================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamFoundationServerScm/help-credentialsConfigurer.html ================================================
NEW since 5.1.0: Credentials for team project collections can now be configured once instead of once per job.
Automatic
Credentials will be looked up in the global configuration based on the Collection URL. Make sure you have added your credentials at Jenkins > Manage Jenkins > Configure Credentials and then add Team Project Collections that are linked to their corresponding credentials at Jenkins > Manage Jenkins > Configure System.

Consult the Global Configuration section in README.md for a walkthrough.
Manual
Credentials are specified in the job. This is the legacy behavior.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamFoundationServerScm/help-localPath.html ================================================

The folder where all files will be retrieved into. The folder name is a relative path, under the workspace of the current job.

The default setting is to retrieve the files into the workspace (ie. the workfolder is ".".

================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamFoundationServerScm/help-projectPath.html ================================================

The name of the project as it is registered on the server.

================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamFoundationServerScm/help-serverUrl.html ================================================
NEW since 5.1.0: This field now allows you to select from a list that is built from the values configured under Team Project Collections at Jenkins > Manage Jenkins > Configure System.

If you are using Team Services, please omit the /DefaultCollection.
Examples:
  • http://tfs.example.com:8080/tfs/DefaultCollection
  • https://tfs.example.com:8080/tfs/CustomCollection
  • https://fabrikam-fiber-inc.visualstudio.com
Learn more about team project collections at MSDN.
Note:
Historically, this field was labeled "Server URL" because earlier versions of TFS did not support team project collections.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamFoundationServerScm/help-workspaceName.html ================================================

The name of the Workspace under which the source should be retrieved. This workspace is created as needed. You can normally omit the property unless you want to name a workspace to avoid conflicts on the server (i.e. when you have multiple projects on one server talking to TFS/Team Services)

The default value is to create a workspace named Hudson-${JOB_NAME}. The TFS plugin for Jenkins supports the following macros that are replaced in the workspace name:

  • ${JOB_NAME} - The name of the job.
  • ${USER_NAME} - The user name that the Hudson server or slave is running as.
  • ${NODE_NAME} - The name of the node/slave that the plugin currently is executed on. Note that this is not the hostname, this value is Hudson configured name of the slave/node.
  • ${ENV} - The environment variable that is set on the master or slave.

================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamPRPushTrigger/config.jelly ================================================ > ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamPRPushTrigger/help.html ================================================ This job will be triggered if Jenkins receives a notification on code push in a TFS pull request, from TFS/Team Services for a Git repository defined in the Source Code Management section. ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamPendingStatusBuildStep/help.html ================================================
The status of the associated pull request and/or commit will be updated in TFS/Team Services as "pending", including a link back to the Jenkins build.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamPluginGlobalConfig/config.groovy ================================================ package hudson.plugins.tfs.TeamPluginGlobalConfig; def f = namespace(lib.FormTagLib); f.section(title: descriptor.displayName) { f.entry(title: _("Team Project Collections"), field: "collectionConfigurations", help: descriptor.getHelpFile()) { f.repeatableProperty(field: "collectionConfigurations") } f.entry(title: _("Enable Push Trigger for all jobs"), field: "enableTeamPushTriggerForAllJobs", description: "Turning this on is equivalent to adding the 'Build when a change is pushed to TFS/Team Services' trigger to all jobs.") { f.checkbox (default: false) } f.entry(title: _("Enable Team Status for all jobs"), field: "enableTeamStatusForAllJobs", description: "Turning this on is equivalent to adding the 'Set build pending status in TFS/Team Services' build step and the 'Set build completion status in TFS/Team Services' post-build action to all jobs.") { f.checkbox (default: false) } f.entry() { f.dropdownDescriptorSelector( title: _("User account name mapping strategy"), field: "userAccountMapper", descriptors: descriptor.getUserAccountMapperDescriptors() ) } f.advanced() { f.entry(title: _("Store TFVC configuration in computer-specific folders"), field: "configFolderPerNode", description: "Warning: don't turn this on unless you know what you are doing!") { f.checkbox (default: false) } } f.entry(title: _("Release WebHooks"), field: "releaseWebHookConfigurations") { f.repeatableProperty(field: "releaseWebHookConfigurations") } } ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamPluginGlobalConfig/help-configFolderPerNode.html ================================================
In some environments, the "home" directory is mounted over a network and shared between many computers, including Jenkins servers and their associated build nodes, which eventually leads to corruption of the configuration directory used for TFVC workspaces. If you have such an environment, check the box to use a sub-directory for each computer (using the computers's host name).

In other words, turning on this option means the TFS SDK will use the following base path for its Cache, Configuration and Logs directories:
~/.microsoft/Team Foundation/4.0/${HOSTNAME}/

WARNING:

Turning this on is equivalent to setting the TEE_PROFILE_DIRECTORY environment variable and thus any manual operations performed on nodes using the Command-Line Client (CLC) will need to be performed with the TEE_PROFILE_DIRECTORY environment variable set accordingly.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamPluginGlobalConfig/help-enableTeamPushTriggerForAllJobs.html ================================================
If enabled, when an event is received from TFS/Team Services for a Git repository, all the Jenkins jobs that use that Git repository will be automatically triggered, even if they didn't enable the "Build when a change is pushed to TFS/Team Services" trigger.

Individual jobs can opt-out of this global opt-in by enabling the Poll SCM trigger and checking its Ignore post-commit hooks checkbox.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamPluginGlobalConfig/help-enableTeamStatusForAllJobs.html ================================================
If enabled, when an event is received from TFS/Team Services for a Git repository, all the Jenkins jobs that use that Git repository will post their status to the associated pull request and/or commit in TFS/Team Services, even if they didn't add the "Set build pending status in TFS/Team Services" build step nor the "Set build completion status in TFS/Team Services" post-build action.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamPluginGlobalConfig/help-releaseWebhookConfigurations.html ================================================
Adds a Release Webhook. Project can refer to this Webhook in its post build action. When a build completes it will send the event payload to the payload url mentioned.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamPluginGlobalConfig/help-userAccountMapper.html ================================================

When interpreting changesets from TFVC, the TFS plugin will attempt to map domain accounts (i.e. DOMAIN\alias) to Jenkins accounts.

The default mapping strategy (Resolve user using 'DOMAIN\alias') is to provide the string (received from TFVC) as-is to Jenkins, which causes Jenkins to convert the username from DOMAIN\alias to DOMAIN_alias. If your Jenkins users are named with a different convention, try another user mapping strategy.

================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamPluginGlobalConfig/help.jelly ================================================
Add pairs of Team Project Collections and associated credentials, one for each collection and Team Services account you will be using elsewhere in Jenkins with the TFS plugin.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamPullRequestMergedDetailsAction/index.jelly ================================================

${it.displayName}

Pull request
#${it.gitPullRequest.pullRequestId}: '${it.gitPullRequest.title}', created by ${it.gitPullRequest.createdBy.displayName}
Description
${it.gitPullRequest.description}
Message
${it.detailedMessage}
Git repository
${it.gitPullRequest.repository.name}
Project
${it.gitPullRequest.repository.project.name}
Associated work items
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamPullRequestMergedDetailsAction/summary.jelly ================================================ Triggered by TFS/Team Services pull request #${it.gitPullRequest.pullRequestId}: '${it.gitPullRequest.title}', created by ${it.gitPullRequest.createdBy.displayName}
Associated work items:
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamPushTrigger/TeamPollingAction/index.jelly ================================================

${(it.displayName)}

${%Polling has not run yet.}
                        
                        ${it.writeLogTo(output)}
                    
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamPushTrigger/config.jelly ================================================ > ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamPushTrigger/help.html ================================================ This job will be triggered if Jenkins receives a notification from TFS/Team Services for a Git repository defined in the Source Code Management section. ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/TeamUpdateWorkItemPostBuildAction/help.html ================================================
At the end of the build, if any work items in TFS/Team Services have been found to be associated with the Jenkins build, a hyperlink back to the Jenkins build will be added to the affected work items.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/browsers/TeamSystemWebAccessBrowser/config.jelly ================================================ ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/model/AliasOnlyUserAccountMapper/config.groovy ================================================ package hudson.plugins.tfs.model.AliasOnlyUserAccountMapper ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/model/BuildCommand.json ================================================ { "team-build": { "Build.Repository.Provider": "TfGit", "System.TeamFoundationCollectionUri": "https://fabrikam-fiber-inc.visualstudio.com", "Build.Repository.Uri": "https://fabrikam-fiber-inc.visualstudio.com/_git/Fabrikam-Fiber-Git", "System.TeamProject": "Fabrikam-Fiber-Git", "Build.Repository.Name": "Fabrikam-Fiber-Git", "Build.SourceVersion": "33b55f7cb7e7e245323987634f960cf4a6e6bc74", "Build.RequestedFor": "Jamal Hartnett" } } ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/model/BuildWithParametersCommand.json ================================================ { "parameter": [ {"name":"id","value":"123"}, {"name":"verbosity","value":"high"} ], "team-event": { "eventType": "git.push", "resource": { "commits": [ { "commitId": "33b55f7cb7e7e245323987634f960cf4a6e6bc74" } ], "repository": { "name": "Fabrikam-Fiber-Git", "url": "https://fabrikam-fiber-inc.visualstudio.com/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249", "project": { "name": "Fabrikam-Fiber-Git" }, "remoteUrl": "https://fabrikam-fiber-inc.visualstudio.com/_git/Fabrikam-Fiber-Git" }, "pushedBy": { "displayName": "Jamal Hartnett" } } } } ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/model/ChangeLogSet/digest.jelly ================================================ ${%No changes.} Changes
  1. ${cs.msgAnnotated} (detail)
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/model/ChangeLogSet/digest_fr.properties ================================================ # The MIT License # # Copyright (c) 2004-2010, Sun Microsystems, 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. No\ changes.=Aucun changement ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/model/ChangeLogSet/index.jelly ================================================

${%Summary}

${%Version} ${cs.version} by ${cs.user} (checked in by ${cs.checkedInBy}) @${cs.domain} :
${cs.msgAnnotated}
${item.path} (diff)
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/model/ChangeLogSet/index_fr.properties ================================================ # The MIT License # # Copyright (c) 2004-2010, Sun Microsystems, 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. Summary=R\u00E9sum\u00E9 ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/model/DomainUserAccountMapper/config.groovy ================================================ package hudson.plugins.tfs.model.DomainUserAccountMapper ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/model/GitPullRequestMergedEvent.json ================================================ { "id": "6872ee8c-b333-4eff-bfb9-0d5274943566", "eventType": "git.pullrequest.merged", "publisherId": "tfs", "scope": "all", "message": { "text": "Jamal Hartnett has created a pull request merge commit", "html": "Jamal Hartnett has created a pull request merge commit", "markdown": "Jamal Hartnett has created a pull request merge commit" }, "detailedMessage": { "text": "Jamal Hartnett has created a pull request merge commit\r\n\r\n- Merge status: Succeeded\r\n- Merge commit: eef717(https://fabrikam.visualstudio.com/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72)\r\n", "html": "Jamal Hartnett has created a pull request merge commit\r\n
    \r\n
  • Merge status: Succeeded
  • \r\n
  • Merge commit: eef717
  • \r\n
", "markdown": "Jamal Hartnett has created a pull request merge commit\r\n\r\n+ Merge status: Succeeded\r\n+ Merge commit: [eef717](https://fabrikam.visualstudio.com/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72)\r\n" }, "resource": { "repository": { "id": "4bc14d40-c903-45e2-872e-0462c7748079", "name": "Fabrikam", "url": "https://fabrikam.visualstudio.com/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079", "project": { "id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c", "name": "Fabrikam", "url": "https://fabrikam.visualstudio.com/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c", "state": "wellFormed" }, "defaultBranch": "refs/heads/master", "remoteUrl": "https://fabrikam.visualstudio.com/_git/Fabrikam" }, "pullRequestId": 1, "status": "completed", "createdBy": { "id": "54d125f7-69f7-4191-904f-c5b96b6261c8", "displayName": "Jamal Hartnett", "uniqueName": "fabrikamfiber4@hotmail.com", "url": "https://fabrikam.vssps.visualstudio.com/_apis/Identities/54d125f7-69f7-4191-904f-c5b96b6261c8", "imageUrl": "https://fabrikam.visualstudio.com/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8" }, "creationDate": "2014-06-17T16:55:46.589889Z", "closedDate": "2014-06-30T18:59:12.3660573Z", "title": "my first pull request", "description": " - test2\r\n", "sourceRefName": "refs/heads/mytopic", "targetRefName": "refs/heads/master", "mergeStatus": "succeeded", "mergeId": "a10bb228-6ba6-4362-abd7-49ea21333dbd", "lastMergeSourceCommit": { "commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c", "url": "https://fabrikam.visualstudio.com/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c" }, "lastMergeTargetCommit": { "commitId": "a511f535b1ea495ee0c903badb68fbc83772c882", "url": "https://fabrikam.visualstudio.com/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/a511f535b1ea495ee0c903badb68fbc83772c882" }, "lastMergeCommit": { "commitId": "eef717f69257a6333f221566c1c987dc94cc0d72", "url": "https://fabrikam.visualstudio.com/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72" }, "reviewers": [ { "reviewerUrl": null, "vote": 0, "id": "2ea2d095-48f9-4cd6-9966-62f6f574096c", "displayName": "[Mobile]\\Mobile Team", "uniqueName": "vstfs:///Classification/TeamProject/f0811a3b-8c8a-4e43-a3bf-9a049b4835bd\\Mobile Team", "url": "https://fabrikam.vssps.visualstudio.com/_apis/Identities/2ea2d095-48f9-4cd6-9966-62f6f574096c", "imageUrl": "https://fabrikam.visualstudio.com/_api/_common/identityImage?id=2ea2d095-48f9-4cd6-9966-62f6f574096c", "isContainer": true } ], "commits": [ { "commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c", "url": "https://fabrikam.visualstudio.com/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c" } ], "url": "https://fabrikam.visualstudio.com/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1" }, "resourceVersion": "3.0-preview.1", "resourceContainers": { "collection": { "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, "account": { "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, "project": { "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f" } }, "createdDate": "2016-06-10T16:32:22.6325371Z" } ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/model/GitPushEvent.json ================================================ { "id": "03c164c2-8912-4d5e-8009-3707d5f83734", "eventType": "git.push", "publisherId": "tfs", "scope": "all", "message": { "text": "Jamal Hartnett pushed updates to branch master of repository Fabrikam-Fiber-Git.", "html": "Jamal Hartnett pushed updates to branch master of repository Fabrikam-Fiber-Git.", "markdown": "Jamal Hartnett pushed updates to branch `master` of repository `Fabrikam-Fiber-Git`." }, "detailedMessage": { "text": "Jamal Hartnett pushed 1 commit to branch master of repository Fabrikam-Fiber-Git.\n - Fixed bug in web.config file 33b55f7c", "html": "Jamal Hartnett pushed 1 commit to branch master of repository Fabrikam-Fiber-Git.\n
    \n
  • Fixed bug in web.config file 33b55f7c\n
", "markdown": "Jamal Hartnett pushed 1 commit to branch [master](https://fabrikam-fiber-inc.visualstudio.com/_git/Fabrikam-Fiber-Git/#version=GBmaster) of repository [Fabrikam-Fiber-Git](https://fabrikam-fiber-inc.visualstudio.com/_git/Fabrikam-Fiber-Git/).\n* Fixed bug in web.config file [33b55f7c](https://fabrikam-fiber-inc.visualstudio.com/_git/Fabrikam-Fiber-Git/commit/33b55f7cb7e7e245323987634f960cf4a6e6bc74)" }, "resource": { "commits": [ { "commitId": "33b55f7cb7e7e245323987634f960cf4a6e6bc74", "author": { "name": "Jamal Hartnett", "email": "fabrikamfiber4@hotmail.com", "date": "2015-02-25T19:01:00Z" }, "committer": { "name": "Jamal Hartnett", "email": "fabrikamfiber4@hotmail.com", "date": "2015-02-25T19:01:00Z" }, "comment": "Fixed bug in web.config file", "url": "https://fabrikam-fiber-inc.visualstudio.com/_git/Fabrikam-Fiber-Git/commit/33b55f7cb7e7e245323987634f960cf4a6e6bc74" } ], "refUpdates": [ { "name": "refs/heads/master", "oldObjectId": "aad331d8d3b131fa9ae03cf5e53965b51942618a", "newObjectId": "33b55f7cb7e7e245323987634f960cf4a6e6bc74" } ], "repository": { "id": "278d5cd2-584d-4b63-824a-2ba458937249", "name": "Fabrikam-Fiber-Git", "url": "https://fabrikam-fiber-inc.visualstudio.com/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249", "project": { "id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c", "name": "Fabrikam-Fiber-Git", "url": "https://fabrikam-fiber-inc.visualstudio.com/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c", "state": "wellFormed" }, "defaultBranch": "refs/heads/master", "remoteUrl": "https://fabrikam-fiber-inc.visualstudio.com/_git/Fabrikam-Fiber-Git" }, "pushedBy": { "id": "00067FFED5C7AF52@Live.com", "displayName": "Jamal Hartnett", "uniqueName": "Windows Live ID\\fabrikamfiber4@hotmail.com" }, "pushId": 14, "date": "2014-05-02T19:17:13.3309587Z", "url": "https://fabrikam-fiber-inc.visualstudio.com/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249/pushes/14" }, "resourceVersion": "3.0-preview.1", "resourceContainers": { "collection": { "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2" }, "account": { "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e" }, "project": { "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f" } }, "createdDate": "2016-06-10T16:32:21.0569909Z" } ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/model/ManualCredentialsConfigurer/config.groovy ================================================ package hudson.plugins.tfs.model.ManualCredentialsConfigurer; def f = namespace(lib.FormTagLib); f.entry(title: "User name", field:"userName", description:"Domain alias, e-mail address or alternate credentials") { f.textbox() } f.entry(title: "User password", field:"password") { f.password() } ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/model/ManualCredentialsConfigurer/help-userName.html ================================================

The name of the user that will be connecting to TFS/Team Services to query history, checkout files, etc.

Team Foundation Server (on-premises)

For [on-premises] Team Foundation Server, the user name can be specified in two ways:

  1. EXAMPLE-DOMAIN\user
  2. user@domain.example.com

Visual Studio Team Services (previously known as Visual Studio Online)

For Team Services, there are also two options:

  1. Personal access tokens (recommended)
    1. In Team Services, click your name in the top right corner and select Security.
    2. In the Personal access tokens area, select Add.
    3. Describe the token (use something like "Jenkins server at jenkins.example.com"), select an expiry timeframe, double-check the Team Services account the token will be valid for and, until the corresponding defect in Team Services is fixed, select All scopes.
    4. Click [Create Token] and copy the generated personal access token to the clipboard.
    5. Back to Jenkins, enter the e-mail address associated with your Team Services account as the User name and the generated personal access token as the User password.
  2. Alternate credentials
    1. In Team Services, click your name in the top right corner and select Security.
    2. In the Alternate credentials area, select Enable alternate authentication credentials.
    3. Enter a secondary user name and password, then click [Save].
    4. Back to Jenkins, re-enter those credentials in the User name and User password fields.

================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/model/TeamRequestedResult/config.groovy ================================================ package hudson.plugins.tfs.model.TeamRequestedResult; def f = namespace(lib.FormTagLib); f.entry(title: _("Type"), field: "teamResultType") { f.select() } f.entry(title: _("Files to include"), field: "includes", description: "Fileset 'includes' setting that specifies the files to collect. Basedir of the fileset is the workspace root.") { f.textbox() } ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/model/TeamRequestedResult/help-includes.html ================================================
Enter a comma-separated list of file patterns to collect the files relevant to the selected result type. Examples:
  • **/target/surefire-reports/TEST-*.xml, **/target/failsafe-reports/TEST-*.xml
  • reports/jacoco.exec
  • **/TestResults/*.xml
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseManagementCI/config.jelly ================================================ ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseManagementCI/help-collectionUrl.html ================================================
Provide the collection URL for your TFS or VS Team Services account. E.g. https://fabfiber.visualstudio.com/DefaultCollection
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseManagementCI/help-credentialsId.html ================================================
Select the credential for you collection.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseManagementCI/help-destinationPath.html ================================================
Specify the destination path to save logs from VSTS/TFS. The base directory is the workspace. Leave it blank if you don't need to get logs from VSTS/TFS.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseManagementCI/help-isArchiveLog.html ================================================
Check if you need to add log file to the artifact.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseManagementCI/help-projectName.html ================================================
Provide team project name.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseManagementCI/help-releaseDefinitionName.html ================================================
Provide release definition name.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseManagementCI/help.html ================================================
Trigger a release in TFS or VS Team Services, when the build completes successfully. Detailed documentation is available here.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseSummaryAction/summary.jelly ================================================ Release summary: ${it.releaseLink} ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseWebHook/config.groovy ================================================ package hudson.plugins.tfs.rm.ReleaseWebHook; def f = namespace(lib.FormTagLib); f.entry(title: _("WebHook Name"), field: "webHookName") { f.textbox(); } f.entry(title: _("Payload URL"), field: "payloadUrl") { f.textbox(); } f.entry(title: _("Secret"), field: "secret") { f.password(); } f.entry { div(align: "right") { f.repeatableDeleteButton() } } ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseWebHook/help-payloadUrl.html ================================================
Payload URL to send the event to.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseWebHook/help-secret.html ================================================
If secret is provided, event payload hash will be verified in TFS service. Recommended to add secret always for webhook.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseWebHook/help-webhookName.html ================================================
Provide a name for the Webhook.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseWebHookAction/config.groovy ================================================ package hudson.plugins.tfs.rm.ReleaseWebHookAction; def f = namespace(lib.FormTagLib); f.entry(title: _("Release Webhooks"), field: "webHookReferences") { f.repeatableProperty(field: "webHookReferences") } ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseWebHookReference/config.groovy ================================================ package hudson.plugins.tfs.rm.ReleaseWebHookAction; def f = namespace(lib.FormTagLib); f.entry(title: _("Release Webhook"), field: "webHookName") { f.select(); } f.entry { div(align: "right") { f.repeatableDeleteButton() } } ================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseWebHookReference/help-webHookName.html ================================================
Select a webhook. Upon job completion event will be send to the selected webhook's payload url.
================================================ FILE: tfs/src/main/resources/hudson/plugins/tfs/rm/ReleaseWebHookSummaryAction/summary.jelly ================================================ Release WebHook status ${status.status}
Success Failure ${status.payloadUrl}
Failure reason: ${endIndex} ${status.errorMessage.substring(0, status.errorMessage.length() > 3000 ? 3000 : status.errorMessage.length())} Succeeded
================================================ FILE: tfs/src/main/webapp/WEB-INF/lib/ThirdPartyNotices.html ================================================ Third-Party Software Notices

Third-Party Software Notices

Note: While Microsoft is not the author of the files below, Microsoft is offering you a license subject to the terms of the Microsoft Software License Terms for Microsoft Team Foundation Server Software Development Kit for Java (the “Microsoft Program”). Microsoft reserves all other rights. The notices below are provided for informational purposes only and are not the license terms under which Microsoft distributes these files.

Microsoft sets forth a copy of the notices and other information below.


Apache Software Foundation

This product includes software developed by the Apache Software Foundation (http://www.apache.org/).

In particular, this product includes software from the following Apache Software Foundation projects:

Apache Commons Codec

http://commons.apache.org/codec/

Apache Commons Logging

http://commons.apache.org/logging/

Apache HttpClient

http://hc.apache.org/httpclient-3.x/

Apache Commons Lang

http://commons.apache.org/lang

Apache log4j

http://logging.apache.org/log4j/

Apache ServiceMix

http://servicemix.apache.org/

Xerces Java Parser

http://xerces.apache.org/xerces-j/


Apache Commons Codec, Apache Commons Logging, Apache HttpClient, Apache Commons Lang, Apache log4j, and Apache ServiceMix are licensed by the Apache Foundation under the Apache License, Version 2.0.

Apache License, Version 2.0

Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.

"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.

"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.

"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.

"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.

"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.

"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).

"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.

"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."

"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.

2. Grant of Copyright License.

Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.

3. Grant of Patent License.

Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.

4. Redistribution.

You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:

  1. You must give any other recipients of the Work or Derivative Works a copy of this License; and
  2. You must cause any modified files to carry prominent notices stating that You changed the files; and
  3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
  4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.

You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.

5. Submission of Contributions.

Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.

6. Trademarks.

This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty.

Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.

8. Limitation of Liability.

In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability.

While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS


The Apache Foundation's Xerces Java Parser project is licensed by the Apache Foundation under the Apache License, Version 1.1.

The Apache Software License, Version 1.1

Copyright © 2000 The Apache Software Foundation. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment:
"This product includes software developed by the Apache Software Foundation (http://www.apache.org/)."
Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear.

4. The names "Xerces" and "Apache Software Foundation" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact apache@apache.org.

5. Products derived from this software may not be called "Apache", nor may "Apache" appear in their name, without prior written permission of the Apache Software Foundation.

THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


Codehaus Projects Woodstox and StAX

This product includes software from the following Codehaus hosted projects:

Woodstox XML Processor

http://woodstox.codehaus.org/

StAX API

http://stax.codehaus.org/Home

Both Woodstox and the StAX API are licensed by their authors under the Apache License, Version 2.0.

Apache License, Version 2.0

Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.

"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.

"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.

"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.

"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.

"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.

"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).

"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.

"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."

"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.

2. Grant of Copyright License.

Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.

3. Grant of Patent License.

Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.

4. Redistribution.

You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:

  1. You must give any other recipients of the Work or Derivative Works a copy of this License; and
  2. You must cause any modified files to carry prominent notices stating that You changed the files; and
  3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
  4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.

You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.

5. Submission of Contributions.

Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.

6. Trademarks.

This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty.

Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.

8. Limitation of Liability.

In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability.

While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS


Hypersonic SQL DB

This product includes software from the Hypersonic SQL DB http://hsqldb.org/.

Copyright (c) 1995-2000 by the Hypersonic SQL Group.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

Neither the name of the Hypersonic SQL Group nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software consists of voluntary contributions made by many individuals on behalf of the
Hypersonic SQL Group.


Copyright (c) 2001-2005, The HSQL Development Group
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

Neither the name of the HSQL Development Group nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Cryptix

This product includes software from the Cryptix project http://www.cryptix.org/.

Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000
The Cryptix Foundation Limited. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the copyright notice,
   this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in
   the documentation and/or other materials provided with the
   distribution.

THIS SOFTWARE IS PROVIDED BY THE CRYPTIX FOUNDATION LIMITED ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING

IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.

Jackson Version 2.0

This product contains the Jackson Version 2.0 software from FasterXML, LLC. Which is licensed under the Apache 2.0 License and Copyright 2009 © FasterXML, LLC.

Apache License, Version 2.0

Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.

"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.

"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.

"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.

"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.

"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.

"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).

"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.

"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."

"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.

2. Grant of Copyright License.

Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.

3. Grant of Patent License.

Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.

4. Redistribution.

You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:

  1. You must give any other recipients of the Work or Derivative Works a copy of this License; and
  2. You must cause any modified files to carry prominent notices stating that You changed the files; and
  3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
  4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.

You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.

5. Submission of Contributions.

Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.

6. Trademarks.

This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty.

Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.

8. Limitation of Liability.

In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability.

While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

================================================ FILE: tfs/src/main/webapp/WEB-INF/lib/license.html ================================================ MICROSOFT SOFTWARE LICENSE TERMS - MICROSOFT TEAM FOUNDATION SERVER SOFTWARE DEVELOPMENT KIT FOR JAVA

MICROSOFT SOFTWARE LICENSE TERMS

MICROSOFT TEAM FOUNDATION SERVER SOFTWARE DEVELOPMENT KIT FOR JAVA


These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. Please read them. They apply to the software named above, which includes the media on which you received it, if any. The terms also apply to any Microsoft

  • updates,
  • supplements,
  • Internet-based services, and
  • support services

for this software, unless other terms accompany those items. If so, those terms apply.

By using the software, you accept these terms. If you do not accept them, do not use the software.


If you comply with these license terms, you have the rights below.

1. INSTALLATION AND USE RIGHTS.

a. Installation and Use. You may install and use any number of copies of the software on your device to design, develop and test your programs.

b. Third Party Notices.  The software includes third party code.  This third party code included in the software is licensed to you by Microsoft under this license agreement, rather than licensed to you by any third party under some other license terms.  Notices, if any, for this third party code are included with the software and may also be found in the ThirdPartyNotices.html file or in the software documentation.

2. ADDITIONAL LICENSING REQUIREMENTS AND/OR USE RIGHTS.

a. Distributable Code. The software contains code that you are permitted to distribute in programs you develop if you comply with the terms below.

i. Right to Use and Distribute. The code and text files listed below are “Distributable Code.”

  • REDIST.TXT Files. You may copy and distribute the object code form of code listed in REDIST.TXT files.
  • Sample Code. You may modify, copy, and distribute the source and object code form of code marked as “sample.”
  • Third Party Distribution. You may permit distributors of your programs to copy and distribute the Distributable Code as part of those programs.

ii. Distribution Requirements. For any Distributable Code you distribute, you must

  • add significant primary functionality to it in your programs;
  • require distributors and external end users to agree to terms that protect it at least as much as this agreement;
  • display your valid copyright notice on your programs; and
  • indemnify, defend, and hold harmless Microsoft from any claims, including attorneys’ fees, related to the distribution or use of your programs.

iii. Distribution Restrictions. You may not

  • alter any copyright, trademark or patent notice in the Distributable Code;
  • use Microsoft’s trademarks in your programs’ names or in a way that suggests your programs come from or are endorsed by Microsoft;
  • include Distributable Code in malicious, deceptive or unlawful programs; or
  • modify or distribute the source code of any Distributable Code so that any part of it becomes subject to an Excluded License. An Excluded License is one that requires, as a condition of use, modification or distribution, that
  • the code be disclosed or distributed in source code form; or
  • others have the right to modify it.

3. SCOPE OF LICENSE. The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not

  • work around any technical limitations in the software;
  • reverse engineer, decompile or disassemble the software, except and only to the extent that applicable law expressly permits, despite this limitation;
  • make more copies of the software than specified in this agreement or allowed by applicable law, despite this limitation;
  • publish the software for others to copy;
  • rent, lease or lend the software;
  • transfer the software or this agreement to any third party; or
  • use the software for commercial software hosting services.

4. BACKUP COPY. You may make one backup copy of the software. You may use it only to reinstall the software.

5. DOCUMENTATION. Any person that has valid access to your computer or internal network may copy and use the documentation for your internal, reference purposes.

6. EXPORT RESTRICTIONS. The software is subject to United States export laws and regulations. You must comply with all domestic and international export laws and regulations that apply to the software. These laws include restrictions on destinations, end users and end use. For additional information, see www.microsoft.com/exporting.

7. SUPPORT SERVICES. Because this software is “as is,” we may not provide support services for it.

8. ENTIRE AGREEMENT. This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services.

9. PRIVACY STATEMENT. The Microsoft Visual Studio Product Family Privacy Statement describes the privacy statement of this software.

10. APPLICABLE LAW.

a. United States. If you acquired the software in the United States, Washington state law governs the interpretation of this agreement and applies to claims for breach of it, regardless of conflict of laws principles. The laws of the state where you live govern all other claims, including claims under state consumer protection laws, unfair competition laws, and in tort.

b. Outside the United States. If you acquired the software in any other country, the laws of that country apply.

11. LEGAL EFFECT. This agreement describes certain legal rights. You may have other rights under the laws of your country. You may also have rights with respect to the party from whom you acquired the software. This agreement does not change your rights under the laws of your country if the laws of your country do not permit it to do so.

12. DISCLAIMER OF WARRANTY. The software is licensed “as-is.” You bear the risk of using it. Microsoft gives no express warranties, guarantees or conditions. You may have additional consumer rights or statutory guarantees under your local laws which this agreement cannot change. To the extent permitted under your local laws, Microsoft excludes the implied warranties of merchantability, fitness for a particular purpose and non-infringement.

FOR AUSTRALIA - you have statutory guarantees under the Australian Consumer Law and nothing in these terms is intended to affect those rights.

13. LIMITATION ON AND EXCLUSION OF REMEDIES AND DAMAGES. You can recover from Microsoft and its suppliers only direct damages up to U.S. $5.00. You cannot recover any other damages, including consequential, lost profits, special, indirect or incidental damages.

This limitation applies to

  • anything related to the software, services, content (including code) on third party Internet sites, or third party programs; and
  • claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law.

It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages.

Please note: As this software is distributed in Quebec, Canada, some of the clauses in this agreement are provided below in French.

Remarque : Ce logiciel étant distribué au Québec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en français.

EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel ». Toute utilisation de ce logiciel est à votre seule risque et péril. Microsoft n’accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la protection des consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualité marchande, d’adéquation à un usage particulier et d’absence de contrefaçon sont exclues.

LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices.

Cette limitation concerne :

  • tout ce qui est relié au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et
  • les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou d’une autre faute dans la limite autorisée par la loi en vigueur.

Elle s’applique également, même si Microsoft connaissait ou devrait connaître l’éventualité d’un tel dommage. Si votre pays n’autorise pas l’exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l’exclusion ci-dessus ne s’appliquera pas à votre égard.

EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d’autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas.

================================================ FILE: tfs/src/main/webapp/WEB-INF/lib/redist.txt ================================================ ================================= TFS SDK for Java Redistributables ================================= You may redistribute the following files under the Team Foundation Server Software Development Kit for Java Software License Terms. ThirdPartyNotices.html lib/com.microsoft.tfs.sdk-14.0.3.jar native/macosx/libnative_misc.jnilib native/macosx/libnative_keychain.jnilib native/macosx/libnative_synchronization.jnilib native/macosx/libnative_auth.jnilib native/macosx/libnative_console.jnilib native/macosx/libnative_filesystem.jnilib native/aix/ppc/libnative_misc.a native/aix/ppc/libnative_filesystem.a native/aix/ppc/libnative_console.a native/aix/ppc/libnative_auth.a native/aix/ppc/libnative_synchronization.a native/win32/x86_64/native_filesystem.dll native/win32/x86_64/native_synchronization.dll native/win32/x86_64/native_messagewindow.dll native/win32/x86_64/native_credential.dll native/win32/x86_64/native_auth.dll native/win32/x86_64/native_console.dll native/win32/x86_64/native_misc.dll native/win32/x86_64/native_registry.dll native/win32/x86/native_filesystem.dll native/win32/x86/native_synchronization.dll native/win32/x86/native_messagewindow.dll native/win32/x86/native_credential.dll native/win32/x86/native_auth.dll native/win32/x86/native_console.dll native/win32/x86/native_misc.dll native/win32/x86/native_registry.dll native/linux/x86_64/libnative_auth.so native/linux/x86_64/libnative_filesystem.so native/linux/x86_64/libnative_synchronization.so native/linux/x86_64/libnative_misc.so native/linux/x86_64/libnative_console.so native/linux/x86/libnative_auth.so native/linux/x86/libnative_filesystem.so native/linux/x86/libnative_synchronization.so native/linux/x86/libnative_misc.so native/linux/x86/libnative_console.so native/linux/ppc/libnative_auth.so native/linux/ppc/libnative_filesystem.so native/linux/ppc/libnative_synchronization.so native/linux/ppc/libnative_misc.so native/linux/ppc/libnative_console.so native/linux/arm/libnative_auth.so native/linux/arm/libnative_console.so native/linux/arm/libnative_filesystem.so native/linux/arm/libnative_misc.so native/linux/arm/libnative_synchronization.so native/freebsd/x86_64/libnative_auth.so native/freebsd/x86_64/libnative_console.so native/freebsd/x86_64/libnative_filesystem.so native/freebsd/x86_64/libnative_misc.so native/freebsd/x86_64/libnative_synchronization.so native/freebsd/x86/libnative_auth.so native/freebsd/x86/libnative_console.so native/freebsd/x86/libnative_filesystem.so native/freebsd/x86/libnative_misc.so native/freebsd/x86/libnative_synchronization.so native/hpux/ia64_32/libnative_auth.so native/hpux/ia64_32/libnative_filesystem.so native/hpux/ia64_32/libnative_synchronization.so native/hpux/ia64_32/libnative_misc.so native/hpux/ia64_32/libnative_console.so native/hpux/PA_RISC/libnative_console.sl native/hpux/PA_RISC/libnative_synchronization.sl native/hpux/PA_RISC/libnative_misc.sl native/hpux/PA_RISC/libnative_auth.sl native/hpux/PA_RISC/libnative_filesystem.sl native/solaris/x86_64/libnative_auth.so native/solaris/x86_64/libnative_filesystem.so native/solaris/x86_64/libnative_synchronization.so native/solaris/x86_64/libnative_misc.so native/solaris/x86_64/libnative_console.so native/solaris/x86/libnative_auth.so native/solaris/x86/libnative_filesystem.so native/solaris/x86/libnative_synchronization.so native/solaris/x86/libnative_misc.so native/solaris/x86/libnative_console.so native/solaris/sparc/libnative_auth.so native/solaris/sparc/libnative_filesystem.so native/solaris/sparc/libnative_synchronization.so native/solaris/sparc/libnative_misc.so native/solaris/sparc/libnative_console.so ================================================ FILE: tfs/src/main/webapp/browsers/tswa.html ================================================

Enter the base URL to use for browsing changesets, files and diffs. Examples: http://tfs:8080/tfs/DefaultCollection/teamproject/ or https://fabrikam-fiber-inc.visualstudio.com/teamproject/

If the field is empty then the plugin will build URLs using the Collection URL.

One reason for providing a value here is if Jenkins connects to the TFS server on the LAN using the short host name and the Jenkins instance is also accessible through the internet and so links to TFS should use the Fully-Qualified Domain Name (FQDN) of the TFS server.

================================================ FILE: tfs/src/main/webapp/labelname.html ================================================

The name of the label to be created.
It supports the usage of environment variables for example ${BUILD_NUMBER}, ${JOB_NAME} and ${BUILD_TAG}.

================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/ChangeSetReaderTest.java ================================================ package hudson.plugins.tfs; import static org.junit.Assert.*; import hudson.plugins.tfs.model.ChangeLogSet; import hudson.plugins.tfs.model.ChangeSet; import hudson.plugins.tfs.model.ChangeSet.Item; import java.io.Reader; import java.io.StringReader; import org.junit.Test; public class ChangeSetReaderTest { @Test public void assertParsingTwoXmlChangeSets() throws Exception { Reader reader = new StringReader("" + "" + "2009-01-12T00:00:00Z" + "snd\\user" + "comment" + "" + "path" + "path2" + "" + "" + ""); ChangeSetReader changesetReader = new ChangeSetReader(); ChangeLogSet logset = changesetReader.parse(null, null, reader); ChangeSet changeset = logset.iterator().next(); assertEquals("User is incorrect", "user", changeset.getUser()); assertEquals("Domain is incorrect", "snd", changeset.getDomain()); assertEquals("Comment is incorrect", "comment", changeset.getComment()); assertEquals("Version is incorrect", "1122", changeset.getVersion()); assertEquals("Date is incorrect", Util.getCalendar(2009, 1, 12).getTime(), changeset.getDate()); assertEquals("Number of items in change set was incorrect", 2, changeset.getItems().size()); Item item = changeset.getItems().get(0); assertEquals("Action is incorrect", "add", item.getAction()); assertEquals("Path is incorrect", "path", item.getPath()); item = changeset.getItems().get(1); assertEquals("Action is incorrect", "delete", item.getAction()); assertEquals("Path is incorrect", "path2", item.getPath()); } @Test public void assertItemHasParent() throws Exception { Reader reader = new StringReader("" + "" + "2009-01-12T00:00:00Z" + "snd\\user" + "comment" + "" + "path" + "path2" + "" + "" + ""); ChangeSetReader changesetReader = new ChangeSetReader(); ChangeLogSet logset = changesetReader.parse(null, null, reader); ChangeSet changeset = logset.iterator().next(); Item item = changeset.getItems().get(0); assertNotNull("The item's parent change set cant be null", item.getParent()); assertSame("The item's parent is not the same as the change set it belongs to", changeset, item.getParent()); } public void assertXmlWithEscapedCharsIsReadCorrectly() throws Exception { Reader reader = new StringReader("" + "" + "2009-01-12T00:00:00Z" + "snd\\user" + "Just <testing> "what" happens when I use the & character...Hudson does not seem to like it!" + "" + "path" + "path2" + "" + "" + ""); ChangeSetReader changesetReader = new ChangeSetReader(); ChangeLogSet logset = changesetReader.parse(null, null, reader); ChangeSet changeset = logset.iterator().next(); assertEquals("The chage set's comment is incorrect", "Just \"what\" happens when I use the & character...Hudson does not seem to like it!", changeset.getComment()); } @Test public void assertParsingOfKeywordCheckedInByIsParsed() throws Exception { Reader reader = new StringReader("" + "" + "2009-01-12T00:00:00Z" + "snd\\user" + "snd\\other_user" + "comment" + "" + "path" + "path2" + "" + "" + ""); ChangeSetReader changesetReader = new ChangeSetReader(); ChangeLogSet logset = changesetReader.parse(null, null, reader); ChangeSet changeset = logset.iterator().next(); assertEquals("Checked in by user is incorrect", "other_user", changeset.getCheckedInBy()); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/ChangeSetWriterTest.java ================================================ package hudson.plugins.tfs; import static org.custommonkey.xmlunit.XMLAssert.*; import java.io.StringWriter; import java.util.ArrayList; import hudson.plugins.tfs.model.ChangeSet; import org.custommonkey.xmlunit.XMLUnit; import org.junit.Before; import org.junit.Test; public class ChangeSetWriterTest { @Before public void setUp() { XMLUnit.setIgnoreWhitespace(true); XMLUnit.setNormalizeWhitespace(true); XMLUnit.setIgnoreComments(true); } @Test public void assertWriterOutputsCorrectChangeLogXml() throws Exception { ChangeSet changeset = new ChangeSet("1122", Util.getCalendar(2008, 12, 12).getTime(), "rnd\\user", "comment"); changeset.getItems().add(new ChangeSet.Item("path", "add")); changeset.getItems().add(new ChangeSet.Item("path2", "delete")); ArrayList sets = new ArrayList(); sets.add(changeset); ChangeSetWriter changesetWriter = new ChangeSetWriter(); StringWriter output = new StringWriter(); changesetWriter.write(sets, output); assertXMLEqual("" + "" + "2008-12-12T00:00:00Z" + "rnd\\user" + "comment" + "" + "path" + "path2" + "" + "" + "", output.getBuffer().toString()); } @Test public void assertWriterIgnoredNullDomain() throws Exception { ChangeSet changeset = new ChangeSet("1122", Util.getCalendar(2008, 12, 12).getTime(), "user", "comment"); ArrayList sets = new ArrayList(); sets.add(changeset); ChangeSetWriter changesetWriter = new ChangeSetWriter(); StringWriter output = new StringWriter(); changesetWriter.write(sets, output); assertXMLEqual("" + "" + "2008-12-12T00:00:00Z" + "user" + "comment" + "" + "", output.getBuffer().toString()); } @Test public void assertXmlCharsAreEscaped() throws Exception { ChangeSet changeset = new ChangeSet("1122", Util.getCalendar(2008, 12, 12).getTime(), "user", "Just \"what\" happens when I use the & character...Hudson does not seem to like it!"); ArrayList sets = new ArrayList(); sets.add(changeset); ChangeSetWriter changesetWriter = new ChangeSetWriter(); StringWriter output = new StringWriter(); changesetWriter.write(sets, output); assertXMLEqual("" + "" + "2008-12-12T00:00:00Z" + "user" + "Just <testing> "what" happens when I use the & character...Hudson does not seem to like it!" + "" + "", output.getBuffer().toString()); } @Test public void assertCheckedInByUserIsWritten() throws Exception { ChangeSet changeset = new ChangeSet("1122", Util.getCalendar(2008, 12, 12).getTime(), "user", "Just \"what\" happens when I use the & character...Hudson does not seem to like it!"); changeset.setCheckedInBy("another_user"); ArrayList sets = new ArrayList(); sets.add(changeset); ChangeSetWriter changesetWriter = new ChangeSetWriter(); StringWriter output = new StringWriter(); changesetWriter.write(sets, output); assertXMLEqual("" + "" + "2008-12-12T00:00:00Z" + "user" + "another_user" + "Just <testing> "what" happens when I use the & character...Hudson does not seem to like it!" + "" + "", output.getBuffer().toString()); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/CommitParameterActionTest.java ================================================ package hudson.plugins.tfs; import org.junit.Assert; import org.junit.Test; /** * A class to test {@link CommitParameterAction}. */ public class CommitParameterActionTest { } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/EndToEndTfs.java ================================================ package hudson.plugins.tfs; import com.microsoft.tfs.core.clients.versioncontrol.GetOptions; import com.microsoft.tfs.core.clients.versioncontrol.PendChangesOptions; import com.microsoft.tfs.core.clients.versioncontrol.VersionControlConstants; import com.microsoft.tfs.core.clients.versioncontrol.WorkspaceLocation; import com.microsoft.tfs.core.clients.versioncontrol.WorkspaceOptions; import com.microsoft.tfs.core.clients.versioncontrol.WorkspacePermissions; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.LockLevel; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.PendingChange; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.PendingSet; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.RecursionType; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.WorkingFolder; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace; import hudson.plugins.tfs.model.ExtraSettings; import hudson.plugins.tfs.model.MockableVersionControlClient; import hudson.plugins.tfs.model.Server; import hudson.plugins.tfs.util.XmlHelper; import hudson.util.Secret; import hudson.util.SecretOverride; import org.apache.commons.io.FileUtils; import org.junit.Assert; import org.junit.runner.Description; import org.jvnet.hudson.test.JenkinsRecipe; import org.jvnet.hudson.test.JenkinsRule; import java.io.File; import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.net.URISyntaxException; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** * Setup/teardown of the TFS server configured by the {@code tfs_server_name} property, * will create the necessary structure in source control. */ @Documented @JenkinsRecipe(EndToEndTfs.RunnerImpl.class) @Target(METHOD) @Retention(RUNTIME) public @interface EndToEndTfs { /** * Specifies the class that will be given a chance to participate. */ Class value(); /** * The {@link EndToEndTfs} annotation requires a value of type {@link Class}. * This class provides an implementation that does almost nothing. */ class StubRunner extends JenkinsRecipe.Runner { private RunnerImpl parent; private IntegrationTestHelper helper; private String encryptedPassword; public String getEncryptedPassword() { return encryptedPassword; } protected RunnerImpl getParent() { return parent; } private void setParent(final RunnerImpl parent) { this.parent = parent; } protected IntegrationTestHelper getHelper() { return helper; } private void setHelper(final IntegrationTestHelper helper) { this.helper = helper; } @Override public void decorateHome(final JenkinsRule jenkinsRule, final File home) throws Exception { final String jobFolder = parent.getJobFolder(); final String configXmlPath = jobFolder + "config.xml"; final File configXmlFile = new File(home, configXmlPath); final String tfsServerUrl = helper.getServerUrl(); XmlHelper.pokeValue(configXmlFile, "/project/scm/serverUrl", tfsServerUrl); final String projectPath = parent.getPathInTfvc(); XmlHelper.pokeValue(configXmlFile, "/project/scm/projectPath", projectPath); final String workspaceName = "Hudson-${JOB_NAME}-${COMPUTERNAME}"; XmlHelper.pokeValue(configXmlFile, "/project/scm/workspaceName", workspaceName); final String userName = helper.getUserName(); XmlHelper.pokeValue(configXmlFile, "/project/scm/userName", userName); final String userPassword = helper.getUserPassword(); final SecretOverride secretOverride = new SecretOverride(); try { final Secret secret = Secret.fromString(userPassword); encryptedPassword = secret.getEncryptedValue(); } finally { try { secretOverride.close(); } catch (IOException e) { // ignore } } final String projectScmPassword = "/project/scm/password"; final String currentPassword = XmlHelper.peekValue(configXmlFile, projectScmPassword); if (currentPassword != null) { XmlHelper.pokeValue(configXmlFile, projectScmPassword, encryptedPassword); } } } class RunnerImpl extends JenkinsRecipe.Runner { private static final String workspaceComment = "Created by the TFS plugin for Jenkins functional tests."; private final IntegrationTestHelper helper; private final String serverUrl; private File localBaseFolderFile; private StubRunner runner; private Server server = null; private String testClassName; private String testCaseName; private String workspaceName; private String pathInTfvc; private Workspace workspace; public RunnerImpl() throws URISyntaxException { helper = new IntegrationTestHelper(); serverUrl = helper.getServerUrl(); } @Override public void setup(JenkinsRule jenkinsRule, EndToEndTfs recipe) throws Exception { final Description testDescription = jenkinsRule.getTestDescription(); final Class clazz = testDescription.getTestClass(); testClassName = clazz.getSimpleName(); testCaseName = testDescription.getMethodName(); final String hostName = IntegrationTestHelper.tryToDetermineHostName(); final File currentFolder = new File("").getAbsoluteFile(); final File workspaces = new File(currentFolder, "workspaces"); // TODO: Consider NOT using the Server class server = new Server(null, null, serverUrl, helper.getUserName(), helper.getUserPassword(), null, ExtraSettings.DEFAULT); final MockableVersionControlClient vcc = server.getVersionControlClient(); // workspaceName MUST be unique across computers hitting the same server workspaceName = hostName + "-" + testCaseName; workspace = createWorkspace(vcc, workspaceName); pathInTfvc = IntegrationTestHelper.determinePathInTfvcForTestCase(testDescription); final File localTestClassFolder = new File(workspaces, testClassName); localBaseFolderFile = new File(localTestClassFolder, testCaseName); //noinspection ResultOfMethodCallIgnored localBaseFolderFile.mkdirs(); final String localBaseFolder = localBaseFolderFile.getAbsolutePath(); final WorkingFolder workingFolder = new WorkingFolder(pathInTfvc, localBaseFolder); workspace.createWorkingFolder(workingFolder); // TODO: Is this necessary if we're about to delete it, anyway? workspace.get(GetOptions.NONE); // Delete the folder associated with this test in TFVC workspace.pendDelete( new String[]{pathInTfvc}, RecursionType.FULL, LockLevel.UNCHANGED, GetOptions.NONE, PendChangesOptions.NONE); checkIn("Cleaning up for the " + testCaseName + " test."); // we don't need to verify this check-in, because a first run on a server will be a no-op // create the folder in TFVC workspace.pendAdd( new String[]{localBaseFolder}, false, null, LockLevel.UNCHANGED, GetOptions.NONE, PendChangesOptions.NONE); final int changeSet = checkIn("Setting up for the " + testCaseName + " test."); Assert.assertTrue(changeSet >= 0); final Class runnerClass = recipe.value(); if (runnerClass != null) { runner = runnerClass.newInstance(); runner.setParent(this); runner.setHelper(this.helper); runner.setup(jenkinsRule, recipe); } } public File getLocalBaseFolderFile() { return localBaseFolderFile; } public String getPathInTfvc() { return pathInTfvc; } public String getWorkspaceName() { return workspaceName; } public String getTestCaseName() { return testCaseName; } public String getTestClassName() { return testClassName; } public String getServerUrl() { return serverUrl; } public Server getServer() { return server; } public String getJobFolder() { return "jobs/" + testCaseName + "/"; } public T getInnerRunner(final Class type) { return type.cast(runner); } public Workspace getWorkspace() { return workspace; } public int checkIn(final String comment) { return checkIn(workspace, comment); } static int checkIn(Workspace workspace, String comment) { final PendingSet pendingSet = workspace.getPendingChanges(); int result = -1; if (pendingSet != null) { final PendingChange[] pendingChanges = pendingSet.getPendingChanges(); if (pendingChanges != null) { result = workspace.checkIn(pendingChanges, comment); } } return result; } static Workspace createWorkspace(final MockableVersionControlClient vcc, final String workspaceName) { deleteWorkspace(vcc, workspaceName); final Workspace workspace = vcc.createWorkspace( null, workspaceName, VersionControlConstants.AUTHENTICATED_USER, VersionControlConstants.AUTHENTICATED_USER, workspaceComment, WorkspaceLocation.LOCAL, WorkspaceOptions.NONE); return workspace; } static void deleteWorkspace(final MockableVersionControlClient vcc, final String workspaceName) { final Workspace[] workspaces = vcc.queryWorkspaces(workspaceName, null, /* TODO: computer */null, WorkspacePermissions.NONE_OR_NOT_SUPPORTED); for (final Workspace workspace : workspaces) { for (WorkingFolder workingFolder : workspace.getFolders()) { final String localItem = workingFolder.getLocalItem(); if (localItem != null) { final File file = new File(localItem); FileUtils.deleteQuietly(file); } } vcc.deleteWorkspace(workspace); } } @Override public void decorateHome(JenkinsRule jenkinsRule, File home) throws Exception { if (runner != null) { runner.decorateHome(jenkinsRule, home); } } @Override public void tearDown(JenkinsRule jenkinsRule, EndToEndTfs recipe) throws Exception { if (runner != null) { runner.tearDown(jenkinsRule, recipe); } final MockableVersionControlClient vcc = server.getVersionControlClient(); deleteWorkspace(vcc, workspaceName); if (server != null) { server.close(); } } } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/FunctionalTest.java ================================================ package hudson.plugins.tfs; import com.microsoft.tfs.core.clients.versioncontrol.GetOptions; import com.microsoft.tfs.core.clients.versioncontrol.PendChangesOptions; import com.microsoft.tfs.core.clients.versioncontrol.VersionControlConstants; import com.microsoft.tfs.core.clients.versioncontrol.WorkspacePermissions; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.LockLevel; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.RecursionType; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.VersionControlLabel; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.ChangesetVersionSpec; import com.microsoft.tfs.jni.helpers.LocalHost; import hudson.FilePath; import hudson.Functions; import hudson.Launcher; import hudson.ProxyConfiguration; import hudson.console.AnnotatedLargeText; import hudson.model.AbstractBuild; import hudson.model.BuildListener; import hudson.model.Cause; import hudson.model.Computer; import hudson.model.Project; import hudson.model.Queue; import hudson.model.Result; import hudson.model.TaskListener; import hudson.model.labels.LabelAtom; import hudson.plugins.tfs.model.MockableVersionControlClient; import hudson.plugins.tfs.model.Server; import hudson.plugins.tfs.util.DateUtil; import hudson.plugins.tfs.util.XmlHelper; import hudson.remoting.VirtualChannel; import hudson.scm.ChangeLogSet; import hudson.scm.PollingResult; import hudson.scm.SCM; import hudson.slaves.DumbSlave; import hudson.slaves.SlaveComputer; import hudson.triggers.SCMTrigger; import hudson.util.Scrambler; import hudson.util.Secret; import jenkins.model.Jenkins; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.junit.Assert; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.jvnet.hudson.test.JenkinsRecipe; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.TestBuilder; import org.jvnet.hudson.test.recipes.LocalData; import org.littleshoot.proxy.HttpProxyServer; import org.littleshoot.proxy.impl.DefaultHttpProxyServer; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPathExpressionException; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.HashSet; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; /** * Tests that exercise real-world functionality, using temporary Jenkins instances. * These are so-called functional (L3) tests. * Tests may connect to a TFS server identified by the tfs_server_name property. */ @Category(IntegrationTests.class) public class FunctionalTest { /** * A special version of {@link JenkinsRule} that assumes {@link EndToEndTfs} decorates a test, * giving access to the {@link hudson.plugins.tfs.EndToEndTfs.RunnerImpl} and all the cool * stuff it has. */ public class TfsJenkinsRule extends JenkinsRule{ /** * https://wiki.jenkins-ci.org/display/JENKINS/Unit+Test+on+Windows#UnitTestonWindows-UnabletodeleteslaveslaveX.log * */ private void purgeSlaves() { List disconnectingComputers = new ArrayList(); List closingChannels = new ArrayList(); for (Computer computer: jenkins.getComputers()) { if (!(computer instanceof SlaveComputer)) { continue; } // disconnect slaves. // retrieve the channel before disconnecting. // even a computer gets offline, channel delays to close. if (!computer.isOffline()) { VirtualChannel ch = computer.getChannel(); computer.disconnect(null); disconnectingComputers.add(computer); closingChannels.add(ch); } } try { // Wait for all computers disconnected and all channels closed. for (Computer computer: disconnectingComputers) { computer.waitUntilOffline(); } for (VirtualChannel ch: closingChannels) { ch.join(); } } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void after() throws Exception { if (Functions.isWindows()) { purgeSlaves(); } super.after(); } public EndToEndTfs.RunnerImpl getTfsRunner() { EndToEndTfs.RunnerImpl result = null; for (final JenkinsRecipe.Runner recipe : recipes) { if (recipe instanceof EndToEndTfs.RunnerImpl) { result = (EndToEndTfs.RunnerImpl) recipe; break; } } return result; } } @Rule public TfsJenkinsRule j = new TfsJenkinsRule(); /** * Runs the project's {@link SCMTrigger} to poll for changes, which may schedule a build. * * If it does schedule a build, we'll wait for that build to complete and return it; * otherwise we return {@code null}. * * This assumes Jenkins (or the project/job) was configured with a quietPeriod, to give us * time to retrieve the item from the queue (especially when execution is paused in the debugger) * so we can wait on it. * * @param project The {@link Project} for which to poll and build. * @return The {@link AbstractBuild} that resulted from the build, if applicable; otherwise {@code null}. */ public static AbstractBuild runScmPollTrigger(final Project project) throws InterruptedException, ExecutionException { final SCMTrigger scmTrigger = (SCMTrigger) project.getTrigger(SCMTrigger.class); // This is a roundabout way of calling SCMTrigger#run(), // because if we set SCMTrigger#synchronousPolling to true // Trigger#checkTriggers() unconditionally runs the trigger, // even if we set its schedule (spec) to an empty string // (which normally disables the schedule). // Having synchronous polling (& building!) in our tests // is more important than skipping the usual method call chain. // http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html final SCMTrigger.Runner runner = scmTrigger.new Runner(); runner.run(); final AbstractBuild build = waitForQueuedBuild(project); return build; } public static AbstractBuild runUserTrigger(final Project project) throws InterruptedException, ExecutionException { final Cause.UserIdCause cause = new Cause.UserIdCause(); project.scheduleBuild(cause); final AbstractBuild build = waitForQueuedBuild(project); return build; } static AbstractBuild waitForQueuedBuild(final Project project) throws InterruptedException, ExecutionException { final Jenkins jenkins = (Jenkins) project.getParent(); final Queue queue = jenkins.getQueue(); final Queue.Item[] items = queue.getItems(); final boolean buildQueued = items.length == 1; final AbstractBuild build; if (buildQueued) { final Queue.WaitingItem queuedItem = (Queue.WaitingItem) items[0]; // now that we have the queued item, we can "shorten the quiet period to zero" final GregorianCalendar due = new GregorianCalendar(); due.add(Calendar.SECOND, -1); queuedItem.timestamp = due; // force re-evaluation of the queue, which should notice the item shouldn't wait anymore queue.maintain(); queue.scheduleMaintenance(); final Future future = (Future) queuedItem.getFuture(); build = future.get(); } else { build = null; } return build; } @LocalData @EndToEndTfs(CreateLabel.class) @Test public void createLabel() throws ExecutionException, InterruptedException, IOException { final Jenkins jenkins = j.jenkins; final TaskListener taskListener = j.createTaskListener(); final EndToEndTfs.RunnerImpl tfsRunner = j.getTfsRunner(); final CreateLabel innerRunner = tfsRunner.getInnerRunner(CreateLabel.class); final String generatedLabelName = innerRunner.getGeneratedLabelName(); final Server server = tfsRunner.getServer(); final MockableVersionControlClient vcc = server.getVersionControlClient(); final List projects = jenkins.getProjects(); final Project project = projects.get(0); final int latestChangesetID = vcc.getLatestChangesetID(); // setup build runUserTrigger(project); // polling should report no changes final PollingResult pollingResult = project.poll(taskListener); Assert.assertEquals(PollingResult.Change.NONE, pollingResult.change); // trigger build final AbstractBuild build = runUserTrigger(project); // verify new label created against latestChangesetId Assert.assertNotNull(build); assertBuildSuccess(build); final ChangeLogSet changeSet = build.getChangeSet(); Assert.assertEquals(0, changeSet.getItems().length); final TFSRevisionState revisionState = build.getAction(TFSRevisionState.class); Assert.assertEquals(latestChangesetID, revisionState.changesetVersion); final String owner = VersionControlConstants.AUTHENTICATED_USER; final ChangesetVersionSpec spec = new ChangesetVersionSpec(latestChangesetID); final VersionControlLabel[] labels = vcc.queryLabels(generatedLabelName, null, owner, false, null, spec); Assert.assertEquals(1, labels.length); final VersionControlLabel label = labels[0]; Assert.assertFalse(StringUtils.isEmpty(label.getComment())); } public void assertBuildSuccess(final AbstractBuild build) throws IOException { final Result result = build.getResult(); if (!Result.SUCCESS.equals(result)) { final AnnotatedLargeText logText = build.getLogText(); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); logText.writeLogTo(0, baos); final String headerTemplate = "Build result: %s\n---Log Start---\n"; final String header = String.format(headerTemplate, result); final String message = header + baos.toString() + "---Log End---\n\n"; Assert.fail(message); } } public static class CreateLabel extends CurrentChangesetInjector { private final String generatedLabelName; public CreateLabel(){ final Calendar now = Calendar.getInstance(); final String iso8601DateString = DateUtil.toString(now); generatedLabelName = "CreateLabel_" + iso8601DateString.replace(':', '-'); } public String getGeneratedLabelName() { return generatedLabelName; } @Override public void decorateHome(final JenkinsRule jenkinsRule, final File home) throws Exception { super.decorateHome(jenkinsRule, home); final EndToEndTfs.RunnerImpl parent = getParent(); final String jobFolder = parent.getJobFolder(); final String configXmlPath = jobFolder + "config.xml"; final File configXmlFile = new File(home, configXmlPath); final String labelNameXPath = "/project/publishers/hudson.plugins.tfs.TFSLabeler/labelName"; XmlHelper.pokeValue(configXmlFile, labelNameXPath, generatedLabelName); } } @LocalData @EndToEndTfs(CurrentChangesetInjector.class) @Test public void agent() throws Exception { final Jenkins jenkins = j.jenkins; final List projects = jenkins.getProjects(); final Project project = projects.get(0); final EndToEndTfs.RunnerImpl tfsRunner = j.getTfsRunner(); checkInEmptyFile(tfsRunner); final LabelAtom label = new LabelAtom("agent"); final DumbSlave agent = j.createOnlineSlave(label); project.getBuildersList().add(new TestBuilder() { @Override public boolean perform(final AbstractBuild build, final Launcher launcher, final BuildListener listener) throws InterruptedException, IOException { final FilePath workspace = build.getWorkspace(); final FilePath child = workspace.child("TODO.txt"); final boolean result = child.exists(); return result; } }); final AbstractBuild firstBuild = runScmPollTrigger(project); Assert.assertNotNull(firstBuild); assertBuildSuccess(firstBuild); final FilePath workspace = firstBuild.getWorkspace(); final FilePath workspaceParent = workspace.getParent(); final FilePath assumedRootPath = workspaceParent.getParent(); final FilePath agentRootPath = agent.getRootPath(); Assert.assertEquals(agentRootPath, assumedRootPath); assertEmptyFileIsInWorkspace(workspace); } @LocalData @EndToEndTfs(EndToEndTfs.StubRunner.class) @Test public void newJob() throws InterruptedException, ExecutionException, IOException { final Jenkins jenkins = j.jenkins; final TaskListener taskListener = j.createTaskListener(); final EndToEndTfs.RunnerImpl tfsRunner = j.getTfsRunner(); final Server server = tfsRunner.getServer(); final MockableVersionControlClient vcc = server.getVersionControlClient(); final List projects = jenkins.getProjects(); final Project project = projects.get(0); int latestChangesetID; // first poll should queue a build because we were never built latestChangesetID = vcc.getLatestChangesetID(); final AbstractBuild firstBuild = runScmPollTrigger(project); Assert.assertNotNull(firstBuild); assertBuildSuccess(firstBuild); final ChangeLogSet firstChangeSet = firstBuild.getChangeSet(); Assert.assertEquals(true, firstChangeSet.isEmptySet()); final TFSRevisionState firstRevisionState = firstBuild.getAction(TFSRevisionState.class); Assert.assertEquals(latestChangesetID, firstRevisionState.changesetVersion); final List firstCauses = firstBuild.getCauses(); Assert.assertEquals(1, firstCauses.size()); final Cause firstCause = firstCauses.get(0); Assert.assertTrue(firstCause instanceof SCMTrigger.SCMTriggerCause); // second poll should report no changes since last build final PollingResult secondPoll = project.poll(taskListener); Assert.assertEquals(PollingResult.Change.NONE, secondPoll.change); // make a change in source control final int changeSet = checkInEmptyFile(tfsRunner); Assert.assertTrue(changeSet >= 0); // third poll should trigger a build latestChangesetID = vcc.getLatestChangesetID(); final AbstractBuild secondBuild = runScmPollTrigger(project); Assert.assertNotNull(secondBuild); assertBuildSuccess(secondBuild); final ChangeLogSet secondChangeSet = secondBuild.getChangeSet(); Assert.assertEquals(1, secondChangeSet.getItems().length); final TFSRevisionState secondRevisionState = secondBuild.getAction(TFSRevisionState.class); Assert.assertEquals(latestChangesetID, secondRevisionState.changesetVersion); final List secondCauses = secondBuild.getCauses(); Assert.assertEquals(1, secondCauses.size()); final Cause secondCause = secondCauses.get(0); Assert.assertTrue(secondCause instanceof SCMTrigger.SCMTriggerCause); final FilePath jenkinsWorkspace = secondBuild.getWorkspace(); assertEmptyFileIsInWorkspace(jenkinsWorkspace); // force a build via a manual trigger final AbstractBuild thirdBuild = runUserTrigger(project); Assert.assertNotNull(thirdBuild); assertBuildSuccess(thirdBuild); final ChangeLogSet thirdChangeSet = thirdBuild.getChangeSet(); Assert.assertEquals(0, thirdChangeSet.getItems().length); final TFSRevisionState thirdRevisionState = thirdBuild.getAction(TFSRevisionState.class); Assert.assertEquals(latestChangesetID, thirdRevisionState.changesetVersion); final List thirdCauses = thirdBuild.getCauses(); Assert.assertEquals(1, thirdCauses.size()); final Cause thirdCause = thirdCauses.get(0); Assert.assertTrue(thirdCause instanceof Cause.UserIdCause); final FilePath thirdBuildWorkspace = thirdBuild.getWorkspace(); assertEmptyFileIsInWorkspace(thirdBuildWorkspace); // finally, delete the project, which should first remove the workspace final TeamFoundationServerScm scm = (TeamFoundationServerScm) project.getScm(); final Computer computer = Computer.currentComputer(); final String hostName = LocalHost.getShortName(); final String workspaceName = scm.getWorkspaceName(thirdBuild, computer).replace("${COMPUTERNAME}", hostName); Assert.assertTrue(jenkinsWorkspace.exists()); final Workspace[] workspacesBeforeDeletion = vcc.queryWorkspaces(workspaceName, VersionControlConstants.AUTHENTICATED_USER, hostName, WorkspacePermissions.NONE_OR_NOT_SUPPORTED); Assert.assertEquals(1, workspacesBeforeDeletion.length); project.delete(); Assert.assertFalse(jenkinsWorkspace.exists()); final Workspace[] workspacesAfterDeletion = vcc.queryWorkspaces(workspaceName, VersionControlConstants.AUTHENTICATED_USER, hostName, WorkspacePermissions.NONE_OR_NOT_SUPPORTED); Assert.assertEquals(0, workspacesAfterDeletion.length); } public void assertEmptyFileIsInWorkspace(final FilePath workspace) throws IOException, InterruptedException { final FilePath[] workspaceFiles = workspace.list("*.*", "$tf"); Assert.assertEquals(1, workspaceFiles.length); final FilePath workspaceFile = workspaceFiles[0]; Assert.assertEquals("TODO.txt", workspaceFile.getName()); } public static int checkInEmptyFile(final EndToEndTfs.RunnerImpl tfsRunner) throws IOException { return checkInFile(tfsRunner, "Add a file.", null); } public static int checkInFile(final EndToEndTfs.RunnerImpl tfsRunner, final String changeMessage, final String fileContents) throws IOException { final Workspace workspace = tfsRunner.getWorkspace(); final File todoFile = new File(tfsRunner.getLocalBaseFolderFile(), "TODO.txt"); final boolean alreadyExisted = todoFile.isFile(); FileUtils.writeStringToFile(todoFile, fileContents, "UTF-8"); final String[] paths = {todoFile.getAbsolutePath()}; if (alreadyExisted) { workspace.pendEdit( paths, RecursionType.NONE, LockLevel.UNCHANGED, null, GetOptions.NONE, PendChangesOptions.NONE); } else { workspace.pendAdd( paths, false, null, LockLevel.UNCHANGED, GetOptions.NONE, PendChangesOptions.NONE); } return tfsRunner.checkIn(tfsRunner.getTestCaseName() + " " + changeMessage); } @LocalData @EndToEndTfs(CloakedPaths.class) @Test public void cloakedPaths() throws Exception { final Jenkins jenkins = j.jenkins; final TaskListener taskListener = j.createTaskListener(); final EndToEndTfs.RunnerImpl tfsRunner = j.getTfsRunner(); final Server server = tfsRunner.getServer(); final String testCaseName = tfsRunner.getTestCaseName(); final Workspace workspace = tfsRunner.getWorkspace(); final List projects = jenkins.getProjects(); final Project jenkinsProject = projects.get(0); final TeamFoundationServerScm tfsScm = (TeamFoundationServerScm) jenkinsProject.getScm(); Assert.assertNotEquals(StringUtils.EMPTY, tfsScm.getCloakedPaths()); int latestChangesetID; // arrange: create structure final File root = tfsRunner.getLocalBaseFolderFile(); final String[] paths = { createWorkspaceFile(root, "root.txt"), createWorkspaceFile(root, "A/A.txt"), createWorkspaceFile(root, "A/1/A1.txt"), createWorkspaceFile(root, "A/2/A2.txt"), createWorkspaceFile(root, "B/B.txt"), createWorkspaceFile(root, "C/C.txt"), }; workspace.pendAdd( paths, false, null, LockLevel.UNCHANGED, GetOptions.NONE, PendChangesOptions.NONE); final int structureChangeSet = tfsRunner.checkIn(testCaseName + " Create structure."); Assert.assertTrue(structureChangeSet >= 0); // act: poll trigger final AbstractBuild firstBuild = runScmPollTrigger(jenkinsProject); // assert Assert.assertNotNull("First poll should queue a build", firstBuild); assertBuildSuccess(firstBuild); assertCloakedPathsWorkspaceContents(firstBuild.getWorkspace()); // arrange: make a change in a non-cloaked path (fully uncloaked) final File aOne = new File(root, "A/1/A1.txt"); FileUtils.writeStringToFile(aOne, "Now with content!", "UTF-8"); workspace.pendEdit( new String[]{aOne.getAbsolutePath()}, RecursionType.NONE, LockLevel.UNCHANGED, null, GetOptions.NONE, PendChangesOptions.NONE); latestChangesetID = tfsRunner.checkIn(testCaseName + " Add content to A1.txt"); // act: poll trigger final AbstractBuild secondBuild = runScmPollTrigger(jenkinsProject); // assert Assert.assertNotNull("Second poll should queue a build", secondBuild); assertCloakedPathsWorkspaceContents(secondBuild.getWorkspace()); // arrange: make a changeset that has an item in a cloaked path final File aTwo = new File(root, "A/2/A2.txt"); FileUtils.writeStringToFile(aOne, "Now with content!", "UTF-8"); workspace.pendEdit( new String[]{aTwo.getAbsolutePath()}, RecursionType.NONE, LockLevel.UNCHANGED, null, GetOptions.NONE, PendChangesOptions.NONE); latestChangesetID = tfsRunner.checkIn(testCaseName + " Add content to A2.txt"); // act: poll (no need to poll trigger) final PollingResult thirdPoll = jenkinsProject.poll(taskListener); // assert Assert.assertEquals("Third poll should NOT find any significant changes", PollingResult.Change.NONE, thirdPoll.change); // arrange: create a changeset that has changes in both cloaked and uncloaked paths final File a = new File(root, "A/A.txt"); FileUtils.writeStringToFile(a, "Now with content!", "UTF-8"); final File b = new File(root, "B/B.txt"); FileUtils.writeStringToFile(b, "Now with content!", "UTF-8"); workspace.pendEdit( new String[]{ a.getAbsolutePath(), b.getAbsolutePath(), }, RecursionType.NONE, LockLevel.UNCHANGED, null, GetOptions.NONE, PendChangesOptions.NONE); latestChangesetID = tfsRunner.checkIn(testCaseName + " Add content to A.txt and B.txt"); // act: poll trigger final AbstractBuild thirdBuild = runScmPollTrigger(jenkinsProject); // assert Assert.assertNotNull("Fourth poll should queue a build", thirdBuild); assertCloakedPathsWorkspaceContents(thirdBuild.getWorkspace()); } /** workspace should only contain: root.txt A/A.txt A/1/A1.txt C/C.txt */ private static void assertCloakedPathsWorkspaceContents(final FilePath workspace) throws Exception { final FilePath[] workspaceFiles = workspace.list("**", "$tf"); final HashSet expectedFileNames = new HashSet(Arrays.asList("root.txt", "A.txt", "A1.txt", "C.txt")); for (final FilePath workspaceFile : workspaceFiles) { final String actualFileName = workspaceFile.getName(); if (expectedFileNames.contains(actualFileName)) { expectedFileNames.remove(actualFileName); } else { final String message = "Did not expect to find " + actualFileName + " in the workspace."; Assert.fail(message); } } Assert.assertEquals("All expected files should have been found in the workspace", 0, expectedFileNames.size()); } static String createWorkspaceFile(final File root, final String relFilePath) throws IOException { final File file = new File(root, relFilePath); final File folder = file.getParentFile(); //noinspection ResultOfMethodCallIgnored folder.mkdirs(); //noinspection ResultOfMethodCallIgnored file.createNewFile(); return file.getAbsolutePath(); } public static class CloakedPaths extends CurrentChangesetInjector { @Override public void decorateHome(final JenkinsRule jenkinsRule, final File home) throws Exception { super.decorateHome(jenkinsRule, home); final EndToEndTfs.RunnerImpl parent = getParent(); final String jobFolder = parent.getJobFolder(); final String configXmlPath = jobFolder + "config.xml"; final File configXmlFile = new File(home, configXmlPath); final String projectPath = parent.getPathInTfvc(); XmlHelper.pokeValue(configXmlFile, "/project/scm/cloakedPaths/string[1]", projectPath + "/A/2"); XmlHelper.pokeValue(configXmlFile, "/project/scm/cloakedPaths/string[2]", projectPath + "/B"); } } /** * If there's no SCMRevisionState present, we revert to old-school polling, * using the timestamp of the last build to see if there have been any changes in TFVC, * at the project's path, since the specified time. * * Even though the @EndToEndTfs annotation caused some commits, * the OldPollingFallback runner poked the current time (after said commits) * in the build.xml, which should cause polling to not find any changes. */ @LocalData @EndToEndTfs(OldPollingFallback.class) @Ignore @Test public void oldPollingFallback() throws IOException { final Jenkins jenkins = j.jenkins; final List projects = jenkins.getProjects(); final Project project = projects.get(0); final TaskListener taskListener = j.createTaskListener(); final PollingResult actual = project.poll(taskListener); Assert.assertEquals(PollingResult.NO_CHANGES, actual); } /** * Injects the current time in milliseconds into the /build/timestamp element * of the last build.xml. */ public static class OldPollingFallback extends EndToEndTfs.StubRunner { @Override public void decorateHome(final JenkinsRule jenkinsRule, final File home) throws Exception { super.decorateHome(jenkinsRule, home); // Add a small pause to make sure we record the timestamp one second later // than the last check-in, otherwise we have the polling occurring on the same // second as the check-in and finding an SCM change where there should be none. Thread.sleep(1000 /* ms */); final EndToEndTfs.RunnerImpl parent = getParent(); final String jobFolder = parent.getJobFolder(); final String lastBuildXmlPath = jobFolder + "builds/2015-07-15_20-37-42/build.xml"; final File lastBuildXmlFile = new File(home, lastBuildXmlPath); final long rightNowMilliseconds = System.currentTimeMillis(); final String value = String.valueOf(rightNowMilliseconds); XmlHelper.pokeValue(lastBuildXmlFile, "/build/timestamp", value); } } /** * As of version 3.2.0, passwords are no longer encoded but encrypted. * Such a job should have its encoded password upgraded to encrypted * and still be able to poll and build. */ @LocalData @EndToEndTfs(UpgradeEncodedPassword.class) @Test public void upgradeEncodedPassword() throws IOException, XPathExpressionException, ExecutionException, InterruptedException, SAXException, ParserConfigurationException { final Jenkins jenkins = j.jenkins; final TaskListener taskListener = j.createTaskListener(); final EndToEndTfs.RunnerImpl tfsRunner = j.getTfsRunner(); final EndToEndTfs.StubRunner stubRunner = tfsRunner.getInnerRunner(EndToEndTfs.StubRunner.class); final String encryptedPassword = stubRunner.getEncryptedPassword(); final List projects = jenkins.getProjects(); final Project project = projects.get(0); final TeamFoundationServerScm scm = (TeamFoundationServerScm) project.getScm(); final Secret passwordSecret = scm.getPassword(); Assert.assertEquals(encryptedPassword, passwordSecret.getEncryptedValue()); PollingResult actualPollingResult; // setup build runUserTrigger(project); actualPollingResult = project.poll(taskListener); Assert.assertEquals(PollingResult.Change.NONE, actualPollingResult.change); project.save(/* force the project to be written to disk, which should encrypt the password */); actualPollingResult = project.poll(taskListener); Assert.assertEquals(PollingResult.Change.NONE, actualPollingResult.change); final File home = j.jenkins.getRootDir(); final String configXmlPath = "jobs/upgradeEncodedPassword/config.xml"; final File configXmlFile = new File(home, configXmlPath); final String userPassword = XmlHelper.peekValue(configXmlFile, "/project/scm/userPassword"); Assert.assertEquals("Encoded password should no longer be there", null, userPassword); final String password = XmlHelper.peekValue(configXmlFile, "/project/scm/password"); Assert.assertEquals("Encrypted password should be there", encryptedPassword, password); // TODO: Check in & record changeset, poll & assert SIGNIFICANT // TODO: build & assert new last build recorded changeset from above // TODO: poll & assert NONE } public static class UpgradeEncodedPassword extends CurrentChangesetInjector { @Override public void decorateHome(final JenkinsRule jenkinsRule, final File home) throws Exception { super.decorateHome(jenkinsRule, home); final EndToEndTfs.RunnerImpl parent = getParent(); final String jobFolder = parent.getJobFolder(); final IntegrationTestHelper helper = getHelper(); final String userPassword = helper.getUserPassword(); final String scrambledPassword = Scrambler.scramble(userPassword); final String configXmlPath = jobFolder + "config.xml"; final File configXmlFile = new File(home, configXmlPath); XmlHelper.pokeValue(configXmlFile, "/project/scm/userPassword", scrambledPassword); } } /** * Verifies that we can still poll and GET from a server when going through a proxy server. */ @LocalData @EndToEndTfs(UseWebProxyServer.class) @Test public void useWebProxyServer() throws Exception { final Jenkins jenkins = j.jenkins; // double-check proxy configuration was loaded and is available final ProxyConfiguration proxyConfiguration = jenkins.proxy; Assert.assertNotNull(proxyConfiguration); final String proxyServerSetting = hudson.Util.fixEmpty(proxyConfiguration.name); Assert.assertNotNull(proxyServerSetting); final TaskListener taskListener = j.createTaskListener(); final EndToEndTfs.RunnerImpl tfsRunner = j.getTfsRunner(); final UseWebProxyServer innerRunner = tfsRunner.getInnerRunner(UseWebProxyServer.class); final List projects = jenkins.getProjects(); final Project project = projects.get(0); final HttpProxyServer proxyServer = innerRunner.getServer(); final LoggingFiltersSourceAdapter adapter = innerRunner.getAdapter(); final int previousChangeSet; try { Assert.assertFalse(adapter.proxyWasUsed()); // setup build runUserTrigger(project); // first poll should report no changes since last build final PollingResult firstPoll = project.poll(taskListener); Assert.assertEquals(PollingResult.Change.NONE, firstPoll.change); Assert.assertTrue(adapter.proxyWasUsed()); adapter.reset(); // make a change in source control previousChangeSet = checkInEmptyFile(tfsRunner); Assert.assertTrue(previousChangeSet >= 0); Assert.assertFalse(adapter.proxyWasUsed()); // second poll should queue a build final AbstractBuild firstBuild = runScmPollTrigger(project); Assert.assertNotNull(firstBuild); assertBuildSuccess(firstBuild); Assert.assertTrue(adapter.proxyWasUsed()); } finally { proxyServer.stop(); } // make a change in source control final String fileContents = "1. Pick up vegetables.\nPrevious changeset:" + previousChangeSet; final int changeSet = checkInFile(tfsRunner, "Now with content.", fileContents); Assert.assertTrue(changeSet >= 0); adapter.reset(); // third poll should claim "no changes" due to being unable to reach the proxy server final InterceptingTaskListener itl = new InterceptingTaskListener(taskListener); // TODO: this takes over 70 seconds to execute, because there's a retry with backoff // We might be able to inject an interception that turns off the retry for this operation final PollingResult thirdPoll = project.poll(itl); Assert.assertEquals("Error during polling => NO_CHANGES", PollingResult.NO_CHANGES, thirdPoll); final List fatalErrors = itl.getFatalErrors(); Assert.assertEquals(1, fatalErrors.size()); Assert.assertFalse(adapter.proxyWasUsed()); } public static class UseWebProxyServer extends CurrentChangesetInjector { private final LoggingFiltersSourceAdapter adapter; private final HttpProxyServer server; public UseWebProxyServer() { adapter = new LoggingFiltersSourceAdapter(); server = DefaultHttpProxyServer .bootstrap() .withPort(0 /* "...let the system pick up an ephemeral port in a bind operation." */) .withFiltersSource(adapter) .start(); } @Override public void decorateHome(final JenkinsRule jenkinsRule, final File home) throws Exception { super.decorateHome(jenkinsRule, home); final InetSocketAddress proxyAddress = server.getListenAddress(); final File proxyXmlFile = new File(home, "proxy.xml"); XmlHelper.pokeValue(proxyXmlFile, "/proxy/name", proxyAddress.getHostName()); XmlHelper.pokeValue(proxyXmlFile, "/proxy/port", Integer.toString(proxyAddress.getPort(), 10)); } public HttpProxyServer getServer() { return server; } public LoggingFiltersSourceAdapter getAdapter() { return adapter; } } /** * Injects some values into the last build.xml to pretend we're up-to-date with TFS. */ public static class CurrentChangesetInjector extends EndToEndTfs.StubRunner { @Override public void decorateHome(final JenkinsRule jenkinsRule, final File home) throws Exception { super.decorateHome(jenkinsRule, home); final EndToEndTfs.RunnerImpl parent = getParent(); final String jobFolder = parent.getJobFolder(); final String lastBuildXmlPath = jobFolder + "builds/2015-07-15_20-37-42/build.xml"; final File lastBuildXmlFile = new File(home, lastBuildXmlPath); final String projectPath = parent.getPathInTfvc(); final String serverUrl = getHelper().getServerUrl(); final Server server = parent.getServer(); final MockableVersionControlClient vcc = server.getVersionControlClient(); final int latestChangesetID = vcc.getLatestChangesetID(); final String changesetVersion = String.valueOf(latestChangesetID); XmlHelper.pokeValue(lastBuildXmlFile, "/build/actions/hudson.plugins.tfs.model.WorkspaceConfiguration/projectPath", projectPath); XmlHelper.pokeValue(lastBuildXmlFile, "/build/actions/hudson.plugins.tfs.model.WorkspaceConfiguration/serverUrl", serverUrl); XmlHelper.pokeValue(lastBuildXmlFile, "/build/actions/hudson.plugins.tfs.TFSRevisionState/changesetVersion", changesetVersion); XmlHelper.pokeValue(lastBuildXmlFile, "/build/actions/hudson.plugins.tfs.TFSRevisionState/projectPath", projectPath); } } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/IntegrationTestHelper.java ================================================ package hudson.plugins.tfs; import hudson.*; import org.apache.commons.lang.StringUtils; import org.junit.Assert; import org.junit.runner.Description; import javax.xml.bind.DatatypeConverter; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; import java.util.Enumeration; public class IntegrationTestHelper { private final String serverUrl; private final String userName; private final String userPassword; public IntegrationTestHelper() throws URISyntaxException { this( propertyOrFail("tfs_collection_url"), propertyOrFail("tfs_server_name"), propertyOrNull("tfs_user_name"), propertyOrNull("tfs_user_password") ); } public IntegrationTestHelper(final String tfsServerName) throws URISyntaxException { this( (TeamCollectionConfiguration.isTeamServices(tfsServerName)) ? new URI("https", null, tfsServerName, 443, "/", null, null).toString() : new URI("http", null, tfsServerName, 8080, "/tfs/" + "jenkins-tfs-plugin", null, null).toString(), tfsServerName, null, null ); } public IntegrationTestHelper(final String tfsServerUrl, final String tfsServerName, final String tfsUserName, final String tfsUserPassword) throws URISyntaxException { final URI serverUri = new URI(tfsServerUrl); if (TeamCollectionConfiguration.isTeamServices(tfsServerName)) { this.userName = tfsUserName; this.userPassword = tfsUserPassword; } else { this.userName = (tfsUserName != null) ? tfsUserName : "jenkins-tfs-plugin"; this.userPassword = (tfsUserPassword != null) ? tfsUserPassword : "for-test-only"; } serverUrl = serverUri.toString(); } static String propertyOrNull(final String propertyName) { final String value = System.getProperty(propertyName); return hudson.Util.fixEmptyAndTrim(value); } static String propertyOrFail(final String propertyName) { final String result = propertyOrNull(propertyName); if (result == null) { Assert.fail("The '" + propertyName + "' property MUST be provided a [non-empty] value."); } return result; } /** * A string representing the URL to a VSO account or a default TFS server installation. */ public String getServerUrl() { return serverUrl; } public String getUserName() { return userName; } public String getUserPassword() { return userPassword; } /** * Creates a string representing a path in TFVC where the specified {@param testDescription} * will perform its work. * * @param testDescription metadata about the currently executing test method. * @return a string that looks like $/FunctionalTests/TestClass/testMethod */ public static String determinePathInTfvcForTestCase(Description testDescription) { final Class clazz = testDescription.getTestClass(); final String testClassName = clazz.getSimpleName(); final String testCaseName = testDescription.getMethodName(); return "$/FunctionalTests" + "/" + testClassName + "/" + testCaseName; } // Adapted from http://stackoverflow.com/a/20793241 public static String tryToDetermineHostName() { String result; try { result = InetAddress.getLocalHost().getHostName(); if (StringUtils.isNotEmpty(result)) { return result; } } catch (UnknownHostException e) { // Probably failed due to reasons listed here: http://stackoverflow.com/a/7800008 } result = System.getenv("COMPUTERNAME"); if (result != null) { return result; } result = System.getenv("HOSTNAME"); if (result != null) { return result; } result = inventHostName(); if (result == null) { result = "unknown"; } return result; } // Adapted from http://stackoverflow.com/q/8765578 public static String inventHostName() { String result = null; String ipv4Address = null, ipv6Address = null; try { final Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); while (ipv6Address == null && interfaces.hasMoreElements()) { final NetworkInterface current = interfaces.nextElement(); try { if (!current.isUp() || current.isLoopback() || current.isVirtual()) { continue; } } catch (SocketException e) { continue; } final Enumeration addresses = current.getInetAddresses(); while (addresses.hasMoreElements()) { final InetAddress currentAddress = addresses.nextElement(); if (currentAddress.isLoopbackAddress()) { continue; } final byte[] addressBytes = currentAddress.getAddress(); if (currentAddress instanceof Inet6Address) { ipv6Address = formatFriendlyName(addressBytes); break; } if (currentAddress instanceof Inet4Address) { ipv4Address = formatFriendlyName(addressBytes); } } } if (ipv6Address != null) { result = ipv6Address; } else if (ipv4Address != null) { result = ipv4Address; } } catch (SocketException e) { // result will stay null } return result; } public static String formatFriendlyName(final byte[] addressBytes) { final String base64 = DatatypeConverter.printBase64Binary(addressBytes); final String slashToMinus = base64.replace('/', '-'); return slashToMinus; } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/IntegrationTestHelperTest.java ================================================ package hudson.plugins.tfs; import org.junit.Assert; import org.junit.Test; public class IntegrationTestHelperTest { @Test public void buildServerUrl_onPremiseTfsServer() throws Exception { final IntegrationTestHelper cut = new IntegrationTestHelper("tfs2013"); final String actual = cut.getServerUrl(); Assert.assertEquals("http://tfs2013:8080/tfs/jenkins-tfs-plugin", actual); } @Test public void buildServerUrl_designatedVsoAccount() throws Exception { final IntegrationTestHelper cut = new IntegrationTestHelper("automated-testing.visualstudio.com"); final String actual = cut.getServerUrl(); Assert.assertEquals("https://automated-testing.visualstudio.com:443/", actual); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/IntegrationTests.java ================================================ package hudson.plugins.tfs; /** * To be used with the JUnit Category annotation on a method, like so: * @Category(hudson.plugins.tfs.IntegationTests.class) */ public interface IntegrationTests { } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/InterceptingTaskListener.java ================================================ package hudson.plugins.tfs; import hudson.console.ConsoleNote; import hudson.model.TaskListener; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Formatter; import java.util.List; public class InterceptingTaskListener implements TaskListener { private final TaskListener victim; private final List errors = new ArrayList(); private final List fatalErrors = new ArrayList(); public InterceptingTaskListener(final TaskListener victim) { this.victim = victim; } public PrintStream getLogger() { return victim.getLogger(); } public void annotate(final ConsoleNote ann) throws IOException { victim.annotate(ann); } public void hyperlink(final String url, final String text) throws IOException { victim.hyperlink(url, text); } private static void add(final List destination, final String format, final Object... args) { final Formatter formatter = new Formatter(); formatter.format(format, args); final String message = formatter.toString(); destination.add(message); } public PrintWriter error(final String msg) { errors.add(msg); return victim.error(msg); } public PrintWriter error(final String format, final Object... args) { add(errors, format, args); return victim.error(format, args); } public PrintWriter fatalError(final String msg) { fatalErrors.add(msg); return victim.fatalError(msg); } public PrintWriter fatalError(final String format, final Object... args) { add(fatalErrors, format, args); return victim.fatalError(format, args); } public List getErrors() { return errors; } public List getFatalErrors() { return fatalErrors; } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/LoggingFiltersSourceAdapter.java ================================================ package hudson.plugins.tfs; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; import org.littleshoot.proxy.HttpFilters; import org.littleshoot.proxy.HttpFiltersAdapter; import org.littleshoot.proxy.HttpFiltersSourceAdapter; import java.util.ArrayList; import java.util.List; public class LoggingFiltersSourceAdapter extends HttpFiltersSourceAdapter { private final List requests = new ArrayList(); private final List responses = new ArrayList(); /* https://github.com/adamfisk/LittleProxy """ To enable aggregator and inflater you have to return a value greater than 0 in your `HttpFiltersSource#get(Request/Response)BufferSizeInBytes()` methods. This provides to you a `FullHttp(Request/Response)` with the complete content in your filter uncompressed. Otherwise you have to handle the chunks yourself. """ */ @Override public int getMaximumRequestBufferSizeInBytes() { return 10 * 1024 * 1024; } @Override public int getMaximumResponseBufferSizeInBytes() { return 10 * 1024 * 1024; } @Override public HttpFilters filterRequest(final HttpRequest originalRequest, final ChannelHandlerContext ctx) { return new HttpFiltersAdapter(originalRequest, ctx) { @Override public HttpResponse clientToProxyRequest(final HttpObject httpObject) { requests.add((FullHttpRequest) httpObject); return /* "[return] null to continue processing as usual" */ null; } @Override public HttpObject serverToProxyResponse(final HttpObject httpObject) { responses.add((FullHttpResponse) httpObject); return /* [return] the unmodified HttpObject */ httpObject; } }; } public boolean proxyWasUsed() { return requests.size() > 0; } public void reset() { requests.clear(); responses.clear(); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/SwedishLocaleTestCase.java ================================================ package hudson.plugins.tfs; import java.util.Locale; import org.junit.After; import org.junit.Before; /** * Test class that helps setting (and resetting) the locale to swedish so * dates in logs can be parsed properly. * * @author Erik Ramfelt */ public abstract class SwedishLocaleTestCase { Locale defaultLocale; @Before public void setToSwedishLocale() { defaultLocale = Locale.getDefault(); Locale.setDefault(new Locale("sv", "SE")); } @After public void resetDefaultLocale() { Locale.setDefault(defaultLocale); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/TFSRevisionStateTest.java ================================================ package hudson.plugins.tfs; import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.List; import org.junit.Test; import org.kohsuke.stapler.export.Model; import org.kohsuke.stapler.export.ModelBuilder; import org.kohsuke.stapler.export.Property; public class TFSRevisionStateTest { @Test public void exportedProperties() { ModelBuilder mb = new ModelBuilder(); Model m = mb.get(TFSRevisionState.class); List actual = m.getProperties(); List actualPropertyNames = new ArrayList(); for (Property p : actual) { actualPropertyNames.add(p.name); } assertCollectionContains(actualPropertyNames, "changesetVersion", "projectPath"); } private static final String NewLine = System.getProperty("line.separator"); private static void assertCollectionContains(Iterable actual, T... expected) { ArrayList expectedItems = new ArrayList(expected.length); for (T t : expected) { expectedItems.add(t); } ArrayList extraItems = new ArrayList(); for (T a : actual) { if (expectedItems.contains(a)) { expectedItems.remove(a); } else { extraItems.add(a); } } StringBuilder sb = null; if (expectedItems.size() > 0) { sb = new StringBuilder(); sb.append("Expected the following item"); if (expectedItems.size() != 1) { sb.append('s'); } sb.append(':').append(NewLine); for (T t : expectedItems) { sb.append('<').append(t).append('>').append(NewLine); } } if (extraItems.size() > 0) { if (sb == null) { sb = new StringBuilder(); } sb.append("Did not expect the following item"); if (extraItems.size() != 1) { sb.append('s'); } sb.append(':').append(NewLine); for (T t : extraItems) { sb.append('<').append(t).append('>').append(NewLine); } } if (sb != null){ fail(sb.toString()); } } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/TeamBuildEndpointTest.java ================================================ package hudson.plugins.tfs; import org.junit.Assert; import org.junit.Test; /** * A class to test {@link TeamBuildEndpoint}. */ public class TeamBuildEndpointTest { @Test public void decodeCommandAndJobNames_typical() throws Exception { final TeamBuildEndpoint cut = new TeamBuildEndpoint(); final String input = TeamBuildEndpoint.URL_PREFIX + "ping/a"; final boolean actual = cut.decodeCommandAndJobNames(input); Assert.assertEquals(true, actual); Assert.assertEquals("ping", cut.getCommandName()); Assert.assertEquals("a", cut.getJobName()); } @Test public void decodeCommandAndJobNames_withDecoding() throws Exception { final TeamBuildEndpoint cut = new TeamBuildEndpoint(); final String input = TeamBuildEndpoint.URL_PREFIX + "ping/a+job%20name%2Fcontaining%3Dencoded+characters%3F"; final boolean actual = cut.decodeCommandAndJobNames(input); Assert.assertEquals(true, actual); Assert.assertEquals("ping", cut.getCommandName()); Assert.assertEquals("a job name/containing=encoded characters?", cut.getJobName()); } @Test public void decodeCommandAndJobNames_noJob() throws Exception { final TeamBuildEndpoint cut = new TeamBuildEndpoint(); final String input = TeamBuildEndpoint.URL_PREFIX + "ping/"; final boolean actual = cut.decodeCommandAndJobNames(input); Assert.assertEquals(false, actual); Assert.assertEquals("ping", cut.getCommandName()); } @Test public void decodeCommandAndJobNames_noJobNoSlash() throws Exception { final TeamBuildEndpoint cut = new TeamBuildEndpoint(); final String input = TeamBuildEndpoint.URL_PREFIX + "ping"; final boolean actual = cut.decodeCommandAndJobNames(input); Assert.assertEquals(false, actual); Assert.assertEquals("ping", cut.getCommandName()); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/TeamCollectionConfigurationTest.java ================================================ package hudson.plugins.tfs; import hudson.util.FormValidation; import org.junit.Assert; import org.junit.Test; import java.net.URI; /** * A class to test {@link TeamCollectionConfiguration}. */ public class TeamCollectionConfigurationTest { private static void assertSameCollectionUri(final String a, final String b) { areSameCollectionUri(a, b, true); } private static void areSameCollectionUri(final String a, final String b, boolean expected) { final URI uriA = a == null ? null : URI.create(a); final URI uriB = b == null ? null : URI.create(b); final String template = "Expected '%s' and '%s' to be considered%s the same."; final String message = String.format(template, a, b, expected ? "" : " NOT"); Assert.assertEquals(message, expected, TeamCollectionConfiguration.areSameCollectionUri(uriA, uriB)); Assert.assertEquals(message, expected, TeamCollectionConfiguration.areSameCollectionUri(uriB, uriA)); } @Test public void areSameCollectionUri_identity() throws Exception { final String input = "https://fabrikam-fiber-inc.visualstudio.com/"; assertSameCollectionUri(input, input); } @Test public void areSameCollectionUri_typical() throws Exception { final String a = "https://fabrikam-fiber-inc.visualstudio.com/"; final String b = "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection"; assertSameCollectionUri(a, b); } @Test public void areSameCollectionUri_withSlashes() throws Exception { final String a = "https://fabrikam-fiber-inc.visualstudio.com/"; final String b = "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/"; assertSameCollectionUri(a, b); } @Test public void areSameCollectionUri_withoutSlashes() throws Exception { final String a = "https://fabrikam-fiber-inc.visualstudio.com"; final String b = "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection"; assertSameCollectionUri(a, b); } @Test public void checkTeamServices_serverOnly() throws Exception { final URI input = URI.create("https://fabrikam-fiber-inc.visualstudio.com"); final FormValidation actual = TeamCollectionConfiguration.checkTeamServices(input); Assert.assertEquals(FormValidation.Kind.OK, actual.kind); } @Test public void checkTeamServices_serverWithSlash() throws Exception { final URI input = URI.create("https://fabrikam-fiber-inc.visualstudio.com/"); final FormValidation actual = TeamCollectionConfiguration.checkTeamServices(input); Assert.assertEquals(FormValidation.Kind.OK, actual.kind); } @Test public void checkTeamServices_serverWithDefaultCollection() throws Exception { final URI input = URI.create("https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection"); final FormValidation actual = TeamCollectionConfiguration.checkTeamServices(input); Assert.assertEquals(FormValidation.Kind.ERROR, actual.kind); } @Test public void checkTeamServices_serverWithDefaultCollectionSlash() throws Exception { final URI input = URI.create("https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/"); final FormValidation actual = TeamCollectionConfiguration.checkTeamServices(input); Assert.assertEquals(FormValidation.Kind.ERROR, actual.kind); } @Test public void checkTeamServices_gitUrl() throws Exception { final URI input = URI.create("https://fabrikam-fiber-inc.visualstudio.com/_git/Fabrikam"); final FormValidation actual = TeamCollectionConfiguration.checkTeamServices(input); Assert.assertEquals(FormValidation.Kind.ERROR, actual.kind); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/TeamEventsEndpointTest.java ================================================ package hudson.plugins.tfs; import com.fasterxml.jackson.databind.ObjectMapper; import com.microsoft.teamfoundation.common.model.ProjectState; import com.microsoft.teamfoundation.sourcecontrol.webapi.model.GitPush; import hudson.plugins.tfs.model.AbstractHookEvent; import hudson.plugins.tfs.model.servicehooks.Event; import hudson.plugins.tfs.model.servicehooks.ResourceContainer; import hudson.plugins.tfs.util.ResourceHelper; import net.sf.json.JSONObject; import org.junit.Assert; import org.junit.Test; import java.util.HashMap; import java.util.Map; /** * A class to test {@link TeamEventsEndpoint}. */ public class TeamEventsEndpointTest { private static final String GIT_PUSH_SAMPLE_JSON = ResourceHelper.fetchAsString(TeamEventsEndpointTest.class, "git.push-sample.json"); @Test public void deserializeEvent_sample() throws Exception { final Event actual = TeamEventsEndpoint.deserializeEvent(GIT_PUSH_SAMPLE_JSON); Assert.assertEquals("git.push", actual.getEventType()); final Map containers = actual.getResourceContainers(); final ResourceContainer collection = containers.get("collection"); Assert.assertEquals("https://fabrikam-fiber-inc.visualstudio.com/", collection.getBaseUrl()); } @Test public void innerDispatch_fakedGitPushEventHandling() throws Exception { final Map factories = new HashMap(); final String eventName = "fakedGitPush"; factories.put(eventName, FakedGitPush.FACTORY); TeamEventsEndpoint.innerDispatch(GIT_PUSH_SAMPLE_JSON, eventName, factories); } private static class FakedGitPush extends AbstractHookEvent { public static final AbstractHookEvent.Factory FACTORY = new Factory() { @Override public AbstractHookEvent create() { return new FakedGitPush(); } @Override public String getSampleRequestPayload() { return null; } }; @Override public JSONObject perform(final ObjectMapper mapper, final Event serviceHookEvent, final String message, final String detailedMessage) { final Object resource = serviceHookEvent.getResource(); final GitPush actual = mapper.convertValue(resource, GitPush.class); Assert.assertEquals(ProjectState.WELL_FORMED, actual.getRepository().getProject().getState()); return null; } } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/TeamFoundationServerScmIntegrationTest.java ================================================ package hudson.plugins.tfs; import hudson.util.Secret; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import java.net.URISyntaxException; /** * Tests that connect to a TFS server identified by the tfs_server_name property. * These are so-called integration (L2) tests. */ @Category(IntegrationTests.class) public class TeamFoundationServerScmIntegrationTest { private TeamFoundationServerScm scm; @Before public void connectToTfs() throws URISyntaxException { final IntegrationTestHelper helper = new IntegrationTestHelper(); final String serverUrl = helper.getServerUrl(); scm = new TeamFoundationServerScm(serverUrl, "projectPath", "workspaceName", null, null); scm.setLocalPath("localPath"); } @Test public void sample() { Assert.assertNotNull(scm); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/TeamFoundationServerScmTest.java ================================================ package hudson.plugins.tfs; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.VersionSpec; import com.thoughtworks.xstream.XStream; import hudson.FilePath; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; import hudson.model.Computer; import hudson.model.Node; import hudson.model.ParametersAction; import hudson.plugins.tfs.model.Project; import hudson.util.Secret; import hudson.util.SecretOverride; import hudson.util.XStream2; import org.apache.commons.lang.SystemUtils; import org.junit.After; import org.junit.Assert; import org.junit.Test; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @SuppressWarnings("unchecked") public class TeamFoundationServerScmTest { private FilePath workspace; @After public void tearDown() throws Exception { if (workspace != null) { workspace.deleteRecursive(); workspace = null; } } /** Up until version 3.1.1, the plugin used to store the password in a base64-encoded string. As of bd98b91ea614c307a6bb1e0af36d9dd2a5646e29, an encrypted version of the password is stored. This test makes sure a job can be upgraded without loss of the password. */ @Test public void upgradeFromScrambledPassword() { SecretOverride secretOverride = null; try { secretOverride = new SecretOverride(); final String xmlString = "\n" + " http://example.tfs.server.invalid:8080/tfs\n" + " $/example/path\n" + " .\n" + " Hudson-${JOB_NAME}-${NODE_NAME}\n" + " ZXhhbXBsZVBhc3N3b3Jk\n" + " example\\tfsbuilder\n" + " false\n" + ""; final XStream serializer = new XStream2(); final TeamFoundationServerScm tfsScmObject = (TeamFoundationServerScm) serializer.fromXML(xmlString); final String actual = tfsScmObject.getUserPassword(); assertEquals("examplePassword", actual); assertEquals("examplePassword", Secret.toString(tfsScmObject.getPassword())); final String expectedUpgradedXml = "\n" + " http://example.tfs.server.invalid:8080/tfs\n" + " $/example/path\n" + " .\n" + " Hudson-${JOB_NAME}-${NODE_NAME}\n" + " GZ3wK9L4iJXsMwXnJ4NieiVpOlxj0AVrthfe7MIr9w0=\n" + " example\\tfsbuilder\n" + " \n" + " false\n" + " false\n" + ""; final String actualUpgradedXml = serializer.toXML(tfsScmObject); // This assert only works if you are on the same machine that is running the build (i.e. it fails on a Jenkins linux node) //assertEquals(expectedUpgradedXml, actualUpgradedXml); final TeamFoundationServerScm tfsScmObject2 = (TeamFoundationServerScm) serializer.fromXML(actualUpgradedXml); final String actual2 = tfsScmObject2.getUserPassword(); assertEquals("examplePassword", actual2); assertEquals("examplePassword", Secret.toString(tfsScmObject.getPassword())); } finally { if (secretOverride != null) { try { secretOverride.close(); } catch (IOException e) { // ignore } } } } @Test public void assertWorkspaceNameReplacesJobName() { AbstractBuild build = mock(AbstractBuild.class); AbstractProject project = mock(AbstractProject.class); when(build.getProject()).thenReturn(project); when(project.getName()).thenReturn("ThisIsAJob"); TeamFoundationServerScm scm = new TeamFoundationServerScm(null, null, "erik_${JOB_NAME}"); assertEquals("Workspace name was incorrect", "erik_ThisIsAJob", scm.getWorkspaceName(build, mock(Computer.class))); } @Test public void assertDoProjectPathCheckRegexWorks() { assertFalse("Project path regex matched an invalid project path", TeamFoundationServerScm.DescriptorImpl.PROJECT_PATH_REGEX.matcher("tfsandbox").matches()); assertFalse("Project path regex matched an invalid project path", TeamFoundationServerScm.DescriptorImpl.PROJECT_PATH_REGEX.matcher("tfsandbox/with/sub/pathes").matches()); assertFalse("Project path regex matched an invalid project path", TeamFoundationServerScm.DescriptorImpl.PROJECT_PATH_REGEX.matcher("tfsandbox$/with/sub/pathes").matches()); assertTrue("Project path regex did not match a valid project path", TeamFoundationServerScm.DescriptorImpl.PROJECT_PATH_REGEX.matcher("$/tfsandbox").matches()); assertTrue("Project path regex did not match a valid project path", TeamFoundationServerScm.DescriptorImpl.PROJECT_PATH_REGEX.matcher("$/tfsandbox/path with space/subpath").matches()); } @Test public void assertDoWorkspaceNameCheckRegexWorks() { assertFalse("Workspace name regex matched an invalid workspace name", TeamFoundationServerScm.DescriptorImpl.WORKSPACE_NAME_REGEX.matcher("work space ").matches()); assertFalse("Workspace name regex matched an invalid workspace name", TeamFoundationServerScm.DescriptorImpl.WORKSPACE_NAME_REGEX.matcher("work.space.").matches()); assertFalse("Workspace name regex matched an invalid workspace name", TeamFoundationServerScm.DescriptorImpl.WORKSPACE_NAME_REGEX.matcher("work*space").matches()); assertFalse("Workspace name regex matched an invalid workspace name", TeamFoundationServerScm.DescriptorImpl.WORKSPACE_NAME_REGEX.matcher("work/space").matches()); assertFalse("Workspace name regex matched an invalid workspace name", TeamFoundationServerScm.DescriptorImpl.WORKSPACE_NAME_REGEX.matcher("work\"space").matches()); assertFalse("Workspace name regex matched an invalid workspace name", TeamFoundationServerScm.DescriptorImpl.WORKSPACE_NAME_REGEX.matcher("workspace*").matches()); assertFalse("Workspace name regex matched an invalid workspace name", TeamFoundationServerScm.DescriptorImpl.WORKSPACE_NAME_REGEX.matcher("workspace/").matches()); assertFalse("Workspace name regex matched an invalid workspace name", TeamFoundationServerScm.DescriptorImpl.WORKSPACE_NAME_REGEX.matcher("workspace\"").matches()); assertTrue("Workspace name regex dit not match an invalid workspace name", TeamFoundationServerScm.DescriptorImpl.WORKSPACE_NAME_REGEX.matcher("work.space").matches()); assertTrue("Workspace name regex dit not match an invalid workspace name", TeamFoundationServerScm.DescriptorImpl.WORKSPACE_NAME_REGEX.matcher("work space").matches()); } @Test public void assertCloakedPathsCheckRegexWorks() { final String shouldNotMatch = "Cloaked paths regex matched an invalid cloaked path"; assertFalse(shouldNotMatch, isCloakedPathValid("tfsandbox")); assertFalse(shouldNotMatch, isCloakedPathValid("tfsandbox/with/sub/pathes")); assertFalse(shouldNotMatch, isCloakedPathValid("tfsandbox$/with/sub/pathes")); assertFalse(shouldNotMatch, isCloakedPathValid("$/tfsandbox/path1;$/tfsandbox/path2")); assertFalse(shouldNotMatch, isCloakedPathValid("$/tfsandbox/path1 ; $/tfsandbox/path2 ; $/tfsandbox/path3")); assertFalse(shouldNotMatch, isCloakedPathValid("$/foo/;$/bar/;$/baz/")); assertFalse(shouldNotMatch, isCloakedPathValid("$/foo/;\n$/bar/;\n$/baz/")); final String shoudMatch = "Cloaked paths regex did not match a valid cloaked path"; assertTrue(shoudMatch, isCloakedPathValid("$/tfsandbox")); assertTrue(shoudMatch, isCloakedPathValid("$/tfsandbox/path with space/subpath")); assertTrue(shoudMatch, isCloakedPathValid("$/tfsandbox/with/${parameter}/path")); assertTrue(shoudMatch, isCloakedPathValid("$/foo/\n$/bar/\n$/baz/")); assertTrue(shoudMatch, isCloakedPathValid(" $/foo/ \n $/bar/ \n $/baz/ ")); assertTrue(shoudMatch, isCloakedPathValid("\n$/foo/\n\n$/bar/\n\n$/baz/\n")); } private static boolean isCloakedPathValid(final String path) { return TeamFoundationServerScm.DescriptorImpl.CLOAKED_PATHS_REGEX.matcher(path).matches(); } @Test public void serializeCloakedPathCollectionToString_one() { final List cloakedPaths = Collections.singletonList("$/foo"); final String actual = TeamFoundationServerScm.serializeCloakedPathCollectionToString(cloakedPaths); Assert.assertEquals("$/foo", actual); } @Test public void serializeCloakedPathCollectionToString_two() { final List cloakedPaths = Arrays.asList("$/foo", "$/bar"); final String actual = TeamFoundationServerScm.serializeCloakedPathCollectionToString(cloakedPaths); Assert.assertEquals("$/foo\n$/bar", actual); } @Test public void serializeCloakedPathCollectionToString_many() { final List cloakedPaths = Arrays.asList("$/foo/", "$/bar/", "$/baz/"); final String actual = TeamFoundationServerScm.serializeCloakedPathCollectionToString(cloakedPaths); Assert.assertEquals("$/foo/\n$/bar/\n$/baz/", actual); } @Test public void splitCloakedPaths_one() { final String input = "$/foo/"; final Collection actual = TeamFoundationServerScm.splitCloakedPaths(input); areEqual(actual, input); } @Test public void splitCloakedPaths_newlinesMany() { final String input = "$/foo/\n$/bar/\n$/baz/"; final Collection actual = TeamFoundationServerScm.splitCloakedPaths(input); areEqual(actual, "$/foo/", "$/bar/", "$/baz/"); } @Test public void splitCloakedPaths_newlinesWithLiberalSpacing() { final String input = " $/foo/ \n $/bar/ \n $/baz/ "; final Collection actual = TeamFoundationServerScm.splitCloakedPaths(input); areEqual(actual, "$/foo/", "$/bar/", "$/baz/"); } @Test public void splitCloakedPaths_newlinesWithBlankLines() { final String input = "\n$/foo/\n\n$/bar/\n\n$/baz/\n"; final Collection actual = TeamFoundationServerScm.splitCloakedPaths(input); areEqual(actual, "$/foo/", "$/bar/", "$/baz/"); } private static void areEqual(final Collection actual, T... expected) { final Iterator ai = actual.iterator(); int ei = 0; while (ei < expected.length && ai.hasNext()) { final T expectedItem = expected[ei]; final T actualItem = ai.next(); Assert.assertEquals(expectedItem, actualItem); ei++; } if (ei == expected.length) { if (ai.hasNext()) { Assert.fail("There were more elements than expected"); } } else { if (!ai.hasNext()) { Assert.fail("Some elements were missing from actual."); } } } @Test public void assertDefaultValueIsUsedForNullLocalPath() { TeamFoundationServerScm scm = new TeamFoundationServerScm("serverurl", "projectpath", "workspace"); assertEquals("Default value for work folder was incorrect", ".", scm.getLocalPath()); } @Test public void assertDefaultValueIsUsedForEmptyLocalPath() { TeamFoundationServerScm scm = new TeamFoundationServerScm("serverurl", "projectpath", "workspace"); scm.setLocalPath(""); assertEquals("Default value for work folder was incorrect", ".", scm.getLocalPath()); } @Test public void assertDefaultValueIsUsedForEmptyWorkspaceName() { TeamFoundationServerScm scm = new TeamFoundationServerScm("serverurl", "projectpath", ""); assertEquals("Default value for workspace was incorrect", "Hudson-${JOB_NAME}-${NODE_NAME}", scm.getWorkspaceName()); } @Test public void assertGetModuleRootReturnsWorkFolder() throws Exception { workspace = Util.createTempFilePath(); TeamFoundationServerScm scm = new TeamFoundationServerScm("serverurl", "projectpath", ""); scm.setLocalPath("workfolder"); FilePath moduleRoot = scm.getModuleRoot(workspace); assertEquals("Name for module root was incorrect", "workfolder", moduleRoot.getName()); assertEquals("The parent for module root was incorrect", workspace.getName(), moduleRoot.getParent().getName()); } @Test public void assertGetModuleRootWorksForDotWorkFolder() throws Exception { workspace = Util.createTempFilePath(); TeamFoundationServerScm scm = new TeamFoundationServerScm("serverurl", "projectpath", ""); FilePath moduleRoot = scm.getModuleRoot(workspace); assertTrue("The module root was reported as not existing even if its virtually the same as workspace", moduleRoot.exists()); assertEquals("The module root was not the same as workspace", moduleRoot.lastModified(), workspace.lastModified()); } @Test public void assertTeamServicesBuildVariablesAreAddedToEnvVars() throws Exception { //Add a couple of variables that will be present during a TFS/Team Services build Map buildVariables = new HashMap(); buildVariables.put("System.DefaultWorkingDirectory", "C:\\build-agent\\_work\\1\\s"); buildVariables.put("Build.Repository.Git.SubmoduleCheckout", "false"); //These three are needed to create the action's buildUrl buildVariables.put("System.TeamFoundationCollectionUri", "https://RESOLVED.com"); buildVariables.put("System.TeamProject", "TEAM_PROJECT"); buildVariables.put("Build.BuildId", "42"); TeamBuildDetailsAction action = new TeamBuildDetailsAction(buildVariables); AbstractBuild build = mock(AbstractBuild.class); when(build.getAction(TeamBuildDetailsAction.class)).thenReturn(action); TeamFoundationServerScm scm = new TeamFoundationServerScm("serverurl", "projectpath", "WORKSPACE_SAMPLE"); AbstractProject project = mock(AbstractProject.class); when(build.getProject()).thenReturn(project); Map env = new HashMap(); scm.buildEnvVars(build, env); //Ensure . is replaced with _, keys are UPPERCASE assertEquals("The key or value for System.DefaultWorkingDirectory was incorrect", "C:\\build-agent\\_work\\1\\s", env.get("SYSTEM_DEFAULTWORKINGDIRECTORY")); assertEquals("The key or value for Build.Repository.Git.SubmoduleCheckout was incorrect", "false", env.get("BUILD_REPOSITORY_GIT_SUBMODULECHECKOUT")); assertEquals("The key or value for System.TeamFoundationCollectionUri was incorrect", "https://RESOLVED.com", env.get("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI")); assertEquals("The key or value for System.TeamProject was incorrect", "TEAM_PROJECT", env.get("SYSTEM_TEAMPROJECT")); assertEquals("The key or value for Build.BuildId was incorrect", "42", env.get("BUILD_BUILDID")); } @Test public void assertWorkspaceNameIsAddedToEnvVars() throws Exception { TeamFoundationServerScm scm = new TeamFoundationServerScm("serverurl", "projectpath", "WORKSPACE_SAMPLE"); AbstractBuild build = mock(AbstractBuild.class); AbstractProject project = mock(AbstractProject.class); when(build.getProject()).thenReturn(project); scm.getWorkspaceName(build, mock(Computer.class)); Map env = new HashMap(); scm.buildEnvVars(build, env); assertEquals("The workspace name was incorrect", "WORKSPACE_SAMPLE", env.get(TeamFoundationServerScm.WORKSPACE_ENV_STR)); } private TeamFoundationServerScm createForEnvVars() { TeamFoundationServerScm scm = new TeamFoundationServerScm("serverurl", "projectpath", "WORKSPACE_SAMPLE", "user", null); scm.setLocalPath("PATH"); return scm; } @Test public void assertWorksfolderPathIsAddedToEnvVars() throws Exception { final TeamFoundationServerScm scm = createForEnvVars(); Map env = new HashMap(); env.put("WORKSPACE", "/this/is/a"); scm.buildEnvVars(mock(AbstractBuild.class), env ); assertEquals("The workfolder path was incorrect", "/this/is/a" + File.separator + "PATH", env.get(TeamFoundationServerScm.WORKFOLDER_ENV_STR)); } @Test public void assertProjectPathIsAddedToEnvVars() throws Exception { final TeamFoundationServerScm scm = createForEnvVars(); Map env = new HashMap(); scm.buildEnvVars(mock(AbstractBuild.class), env ); assertEquals("The project path was incorrect", "projectpath", env.get(TeamFoundationServerScm.PROJECTPATH_ENV_STR)); } @Test public void assertServerUrlIsAddedToEnvVars() throws Exception { final TeamFoundationServerScm scm = createForEnvVars(); Map env = new HashMap(); scm.buildEnvVars(mock(AbstractBuild.class), env ); assertEquals("The server URL was incorrect", "serverurl", env.get(TeamFoundationServerScm.SERVERURL_ENV_STR)); } @Test public void assertTfsUserNameIsAddedToEnvVars() throws Exception { final TeamFoundationServerScm scm = createForEnvVars(); Map env = new HashMap(); scm.buildEnvVars(mock(AbstractBuild.class), env ); assertEquals("The TFS user name was incorrect", "user", env.get(TeamFoundationServerScm.USERNAME_ENV_STR)); } @Test public void assertTfsWorkspaceChangesetIsAddedToEnvVars() throws Exception { final TeamFoundationServerScm scm = createForEnvVars(); scm.setWorkspaceChangesetVersion("12345"); Map env = new HashMap(); scm.buildEnvVars(mock(AbstractBuild.class), env ); assertEquals("Workspace changeset version was incorrect", "12345", env.get(TeamFoundationServerScm.WORKSPACE_CHANGESET_ENV_STR)); } @Test public void assertTfsWorkspaceChangesetIsNotAddedToEnvVarsIfEmpty() throws Exception { final TeamFoundationServerScm scm = createForEnvVars(); scm.setWorkspaceChangesetVersion(""); Map env = new HashMap(); scm.buildEnvVars(mock(AbstractBuild.class), env ); assertEquals("Workspace changeset version was not null", null, env.get(TeamFoundationServerScm.WORKSPACE_CHANGESET_ENV_STR)); } @Test public void assertTfsWorkspaceChangesetIsNotAddedToEnvVarsIfNull() throws Exception { final TeamFoundationServerScm scm = createForEnvVars(); scm.setWorkspaceChangesetVersion(null); Map env = new HashMap(); scm.buildEnvVars(mock(AbstractBuild.class), env ); assertEquals("Workspace changeset version was not null", null, env.get(TeamFoundationServerScm.WORKSPACE_CHANGESET_ENV_STR)); } @Test public void recordWorkspaceChangesetVersion() throws Exception { final TeamFoundationServerScm scm = new TeamFoundationServerScm("serverUrl", "projectPath", "workspace"); scm.setLocalPath("localPath"); final AbstractBuild build = mock(AbstractBuild.class); when(build.getTimestamp()).thenReturn(new GregorianCalendar(2015, 03, 28, 22, 04)); final BuildListener listener = null; final Project project = mock(Project.class); when(project.getRemoteChangesetVersion(isA(VersionSpec.class))).thenReturn(42); final String projectPath = "projectPath"; final String singleVersionSpec = null; final int actual = scm.recordWorkspaceChangesetVersion(build, listener, project, projectPath, singleVersionSpec); Assert.assertEquals(42, actual); final Map env = new HashMap(); scm.buildEnvVars(build, env); assertEquals("42", env.get(TeamFoundationServerScm.WORKSPACE_CHANGESET_ENV_STR)); } @Test public void recordWorkspaceChangesetVersionWithSingleVersionSpec() throws Exception { final TeamFoundationServerScm scm = new TeamFoundationServerScm("serverUrl", "projectPath", "workspace"); scm.setLocalPath("localPath"); final AbstractBuild build = mock(AbstractBuild.class); final BuildListener listener = null; final Project project = mock(Project.class); when(project.getRemoteChangesetVersion(isA(VersionSpec.class))).thenReturn(42); final String projectPath = "projectPath"; final String singleVersionSpec = "Lfoo"; final int actual = scm.recordWorkspaceChangesetVersion(build, listener, project, projectPath, singleVersionSpec); Assert.assertEquals(42, actual); final Map env = new HashMap(); scm.buildEnvVars(build, env); assertEquals("42", env.get(TeamFoundationServerScm.WORKSPACE_CHANGESET_ENV_STR)); } /** * Workspace name must be less than 64 characters, cannot end with a space or period, and cannot contain any of the following characters: "/:<>|*? */ @Test public void assertWorkspaceNameReplacesInvalidChars() { TeamFoundationServerScm scm = new TeamFoundationServerScm(null, null, "A\"B/C:DF|G*H?I"); assertEquals("Workspace name contained invalid chars", "A_B_C_D_E_F_G_H_I", scm.getWorkspaceName(null, null)); } /** * Workspace name must be less than 64 characters, cannot end with a space or period, and cannot contain any of the following characters: "/:<>|*? */ @Test public void assertWorkspaceNameReplacesEndingPeriod() { TeamFoundationServerScm scm = new TeamFoundationServerScm(null, null, "Workspace.Name."); assertEquals("Workspace name ends with period", "Workspace.Name_", scm.getWorkspaceName(null, null)); } /** * Workspace name must be less than 64 characters, cannot end with a space or period, and cannot contain any of the following characters: "/:<>|*? */ @Test public void assertWorkspaceNameReplacesEndingSpace() { TeamFoundationServerScm scm = new TeamFoundationServerScm(null, null, "Workspace Name "); assertEquals("Workspace name ends with space", "Workspace Name_", scm.getWorkspaceName(null, null)); } @Test public void assertServerUrlResolvesBuildVariables() { ParametersAction action = mock(ParametersAction.class); when(action.substitute(isA(AbstractBuild.class), isA(String.class))).thenReturn("https://RESOLVED.com"); AbstractBuild build = mock(AbstractBuild.class); when(build.getAction(ParametersAction.class)).thenReturn(action); TeamFoundationServerScm scm = new TeamFoundationServerScm("https://${PARAM}.com", null, ""); assertEquals("The server url wasnt resolved", "https://RESOLVED.com", scm.getServerUrl(build)); } @Test public void assertProjectPathResolvesBuildVariables() { ParametersAction action = mock(ParametersAction.class); when(action.substitute(isA(AbstractBuild.class), isA(String.class))).thenReturn("$/RESOLVED/path"); AbstractBuild build = mock(AbstractBuild.class); when(build.getAction(ParametersAction.class)).thenReturn(action); TeamFoundationServerScm scm = new TeamFoundationServerScm(null, "$/$PARAM/path", ""); assertEquals("The project path wasnt resolved", "$/RESOLVED/path", scm.getProjectPath(build)); } @Test public void assertWorkspaceNameResolvesBuildVariables() { ParametersAction action = mock(ParametersAction.class); when(action.substitute(isA(AbstractBuild.class), isA(String.class))).thenReturn("WS-RESOLVED"); AbstractBuild build = mock(AbstractBuild.class); when(build.getAction(ParametersAction.class)).thenReturn(action); TeamFoundationServerScm scm = new TeamFoundationServerScm(null, null, "WS-${PARAM}"); assertEquals("The workspace name wasnt resolved", "WS-RESOLVED", scm.getWorkspaceName(build, mock(Computer.class))); } @Test public void assertTfsWorkspaceIsntRemovedIfThereIsNoBuildWhenProcessWorkspaceBeforeDeletion() throws Exception { AbstractProject project = mock(AbstractProject.class); Node node = mock(Node.class); TeamFoundationServerScm scm = new TeamFoundationServerScm("server", "projectpath", "workspace"); assertThat(scm.processWorkspaceBeforeDeletion(project, workspace, node), is(true)); verify(project).getLastBuild(); verifyNoMoreInteractions(project); } @Test public void assertWorkspaceIsntRemoveIfThereIsNoBuildOnSpecifiedNodeAndHudsonWantsToRemoveWorkspaceOnNode() throws Exception { AbstractProject project = mock(AbstractProject.class); AbstractBuild build = mock(AbstractBuild.class); Node node = mock(Node.class); Node inNode = mock(Node.class); when(project.getLastBuild()).thenReturn(build); when(build.getPreviousBuild()).thenReturn(build).thenReturn(null); when(build.getBuiltOn()).thenReturn(node).thenReturn(node); when(node.getNodeName()).thenReturn("node1").thenReturn("node2"); when(inNode.getNodeName()).thenReturn("needleNode").thenReturn("needleNode"); TeamFoundationServerScm scm = new TeamFoundationServerScm("server", "projectpath", "workspace"); assertThat( scm.processWorkspaceBeforeDeletion(project, workspace, inNode), is(true)); verify(project).getLastBuild(); verify(node, times(2)).getNodeName(); verify(build, times(2)).getBuiltOn(); verify(build, times(2)).getPreviousBuild(); verifyNoMoreInteractions(project); verifyNoMoreInteractions(node); verifyNoMoreInteractions(build); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/Util.java ================================================ package hudson.plugins.tfs; import hudson.FilePath; import java.io.File; import java.util.Calendar; import java.util.TimeZone; public class Util { private Util() { } public static Calendar getCalendar(int year, int month, int day) { return getCalendar(year, month, day, 0, 0, 0, "GMT"); } public static Calendar getCalendar(int year, int month, int day, int hour, int min, int sec) { return getCalendar(year, month, day, hour, min, sec, "GMT"); } public static Calendar getCalendar(int year, int month, int day, int hour, int min, int sec, String timezone) { return getCalendar(year, month, day, hour, min, sec, TimeZone.getTimeZone(timezone)); } public static Calendar getCalendar(int year, int month, int day, int hour, int min, int sec, TimeZone timezone) { Calendar calendar = Calendar.getInstance(); calendar.clear(); calendar.set(Calendar.YEAR, year); calendar.set(Calendar.MONTH, month - 1); calendar.set(Calendar.DATE, day); calendar.set(Calendar.HOUR_OF_DAY, hour); calendar.set(Calendar.MINUTE, min); calendar.set(Calendar.SECOND, sec); calendar.setTimeZone(timezone); return calendar; } public static FilePath createTempFilePath() throws Exception { File parentFile = hudson.Util.createTempDir(); FilePath workspace = new FilePath(parentFile); parentFile.delete(); workspace.mkdirs(); return workspace; } /** * Create a boxed copy of the boolean array since JUnit assertArrayEquals() does not take boolean[] * @param array copy from * @return a boxed copy of the array */ public static Boolean[] toBoxedArray(boolean[] array) { Boolean[] copy = new Boolean[array.length]; for (int i = 0; i < array.length; i++) { copy[i] = array[i]; } return copy; } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/actions/CheckoutActionTest.java ================================================ package hudson.plugins.tfs.actions; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import java.io.FileFilter; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.List; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.DateVersionSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.VersionSpec; import hudson.FilePath; import hudson.model.TaskListener; import hudson.plugins.tfs.Util; import hudson.plugins.tfs.model.ChangeSet; import hudson.plugins.tfs.model.Project; import hudson.plugins.tfs.model.Server; import hudson.plugins.tfs.model.Workspace; import hudson.plugins.tfs.model.Workspaces; import hudson.remoting.VirtualChannel; import org.hamcrest.CustomMatcher; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.jvnet.hudson.test.Bug; import org.mockito.Mock; import org.mockito.MockitoAnnotations; public class CheckoutActionTest { private static final List EMPTY_CLOAKED_PATHS_LIST = Collections.emptyList(); private static final String MY_LABEL = "MyLabel"; private FilePath hudsonWs; private @Mock Server server; private @Mock Workspaces workspaces; private @Mock Workspace workspace; private @Mock Project project; private @Mock TaskListener taskListener; @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); hudsonWs = Util.createTempFilePath(); } @After public void teardown() throws Exception { if (hudsonWs != null) { hudsonWs.deleteRecursive(); } } private void prepareCommonMocks() { when(server.getWorkspaces()).thenReturn(workspaces); when(server.getProject("project")).thenReturn(project); when(server.getListener()).thenReturn(taskListener); when(taskListener.getLogger()).thenReturn(System.out); when(workspaces.getWorkspaceMapping(anyString())).thenReturn("workspace"); } @Test public void assertFirstCheckoutBySingleVersionSpecNotUsingUpdate() throws Exception { prepareCommonMocks(); when(project.getProjectPath()).thenReturn("project"); when(workspaces.exists("workspace")).thenReturn(true).thenReturn(false); when(workspaces.newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class))).thenReturn(workspace); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", false, false).checkoutBySingleVersionSpec(server, hudsonWs, MY_LABEL); verify(workspaces).newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class)); verify(project).getFiles(isA(String.class), eq(MY_LABEL), eq(false)); verify(workspaces).deleteWorkspace(workspace); } @Test public void assertFirstCheckoutNotUsingUpdate() throws Exception { prepareCommonMocks(); when(project.getProjectPath()).thenReturn("project"); when(workspaces.exists("workspace")).thenReturn(true).thenReturn(false); when(workspaces.newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class))).thenReturn(workspace); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", false, false).checkout(server, hudsonWs, null, Util.getCalendar(2009, 9, 24)); verify(workspaces).newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class)); verify(project).getFiles(isA(String.class), eq("D2009-09-24T00:00:00Z"), eq(false)); verify(workspaces).deleteWorkspace(workspace); } @Test public void assertFirstCheckoutBySingleVersionSpecUsingUpdate() throws Exception { prepareCommonMocks(); when(project.getProjectPath()).thenReturn("project"); when(workspaces.exists(new Workspace("workspace"))).thenReturn(false); when(workspaces.newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class))).thenReturn(workspace); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", true, false).checkoutBySingleVersionSpec(server, hudsonWs, MY_LABEL); verify(workspaces).newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class)); verify(project).getFiles(isA(String.class), eq(MY_LABEL), eq(false)); verify(workspaces, never()).deleteWorkspace(isA(Workspace.class)); } @Test public void assertFirstCheckoutUsingUpdate() throws Exception { prepareCommonMocks(); when(project.getProjectPath()).thenReturn("project"); when(workspaces.exists(new Workspace("workspace"))).thenReturn(false); when(workspaces.newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class))).thenReturn(workspace); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", true, false).checkout(server, hudsonWs, null, Util.getCalendar(2009, 9, 24)); verify(workspaces).newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class)); verify(project).getFiles(isA(String.class), eq("D2009-09-24T00:00:00Z"), eq(false)); verify(workspaces, never()).deleteWorkspace(isA(Workspace.class)); } @Test public void assertSecondCheckoutBySingleVersionSpecUsingUpdate() throws Exception { prepareCommonMocks(); when(workspaces.exists("workspace")).thenReturn(true); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); when(workspace.getComputer()).thenReturn("LocalComputer"); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", true, false).checkoutBySingleVersionSpec(server, hudsonWs, MY_LABEL); verify(project).getFiles(isA(String.class), eq(MY_LABEL), eq(false)); verify(workspaces, never()).newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class)); verify(workspaces, never()).deleteWorkspace(isA(Workspace.class)); } @Test public void assertSecondCheckoutUsingUpdate() throws Exception { prepareCommonMocks(); when(workspaces.exists("workspace")).thenReturn(true); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); when(workspace.getComputer()).thenReturn("LocalComputer"); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", true, false).checkout(server, hudsonWs, null, Util.getCalendar(2009, 9, 24)); verify(project).getFiles(isA(String.class), eq("D2009-09-24T00:00:00Z"), eq(false)); verify(workspaces, never()).newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class)); verify(workspaces, never()).deleteWorkspace(isA(Workspace.class)); } @Test public void assertSecondCheckoutBySingleVersionSpecNotUsingUpdate() throws Exception { prepareCommonMocks(); when(project.getProjectPath()).thenReturn("project"); when(workspaces.exists("workspace")).thenReturn(true).thenReturn(false); when(workspaces.newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class))).thenReturn(workspace); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", false, false).checkoutBySingleVersionSpec(server, hudsonWs, MY_LABEL); verify(workspaces).newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class)); verify(project).getFiles(isA(String.class), eq(MY_LABEL), eq(false)); verify(workspaces).deleteWorkspace(workspace); } @Test public void assertSecondCheckoutNotUsingUpdate() throws Exception { prepareCommonMocks(); when(project.getProjectPath()).thenReturn("project"); when(workspaces.exists("workspace")).thenReturn(true).thenReturn(false); when(workspaces.newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class))).thenReturn(workspace); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", false, false).checkout(server, hudsonWs, null, Util.getCalendar(2009, 9, 24)); verify(workspaces).newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class)); verify(project).getFiles(isA(String.class), eq("D2009-09-24T00:00:00Z"), eq(false)); verify(workspaces).deleteWorkspace(workspace); } @Test public void assertDetailedHistoryIsNotRetrievedInFirstBuildCheckingOutByLabel() throws Exception { prepareCommonMocks(); when(workspaces.exists("workspace")).thenReturn(true); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); when(workspace.getComputer()).thenReturn("LocalComputer"); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", true, false).checkoutBySingleVersionSpec(server, hudsonWs, MY_LABEL); verify(project, never()).getDetailedHistory(isA(Calendar.class), isA(Calendar.class)); } @Test public void assertDetailedHistoryIsNotRetrievedInFirstBuild() throws Exception { prepareCommonMocks(); when(workspaces.exists("workspace")).thenReturn(true); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); when(workspace.getComputer()).thenReturn("LocalComputer"); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", true, false).checkout(server, hudsonWs, null, Util.getCalendar(2009, 9, 24)); verify(project, never()).getDetailedHistory(isA(Calendar.class), isA(Calendar.class)); } @Test public void assertDetailedHistoryIsRetrievedInSecondBuildCheckingOutByLabel() throws Exception { List list = new ArrayList(); prepareCommonMocks(); when(workspaces.exists("workspace")).thenReturn(true); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); when(workspace.getComputer()).thenReturn("LocalComputer"); when(project.getDetailedHistory(isA(String.class))).thenReturn(list); CheckoutAction action = new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", true, false); List actualList = action.checkoutBySingleVersionSpec(server, hudsonWs, MY_LABEL); assertSame("The list from the detailed history, was not the same as returned from checkout", list, actualList); verify(project).getDetailedHistory(isA(String.class)); } @Test public void assertDetailedHistoryIsRetrievedInSecondBuild() throws Exception { List list = new ArrayList(); prepareCommonMocks(); when(workspaces.exists("workspace")).thenReturn(true); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); when(workspace.getComputer()).thenReturn("LocalComputer"); when(project.getDetailedHistoryWithoutCloakedPaths(isA(VersionSpec.class), isA(VersionSpec.class), anyCollection())).thenReturn(list); CheckoutAction action = new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", true, false); final Calendar startDate = Util.getCalendar(2008, 9, 24); final Calendar endDate = Util.getCalendar(2008, 10, 24); List actualList = action.checkout(server, hudsonWs, startDate, endDate); assertEquals("The list from the detailed history, was not the same as returned from checkout", list, actualList); final DateVersionSpec startDateVersionSpec = new DateVersionSpec(startDate); verify(project).getDetailedHistoryWithoutCloakedPaths(argThat(new DateVersionSpecMatcher(startDateVersionSpec)), isA(VersionSpec.class), eq(EMPTY_CLOAKED_PATHS_LIST)); } @Test public void assertWorkFolderIsCleanedIfNotUsingUpdate() throws Exception { hudsonWs.createTempFile("temp", "txt"); FilePath tfsWs = hudsonWs.child("tfs-ws"); tfsWs.mkdirs(); tfsWs.createTempFile("temp", "txt"); prepareCommonMocks(); when(workspaces.exists(new Workspace("workspace"))).thenReturn(false); when(workspaces.newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class))).thenReturn(workspace); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, "tfs-ws", false, false).checkout(server, hudsonWs, null, Util.getCalendar(2009, 9, 24)); assertTrue("The local folder was removed", tfsWs.exists()); assertEquals("The local TFS folder was not cleaned", 0, tfsWs.list((FileFilter)null).size()); assertEquals("The Hudson workspace path was cleaned", 2, hudsonWs.list((FileFilter)null).size()); } @Test public void assertWorkFolderIsCleanedIfNotUsingUpdateCheckingOutByLabel() throws Exception { hudsonWs.createTempFile("temp", "txt"); FilePath tfsWs = hudsonWs.child("tfs-ws"); tfsWs.mkdirs(); tfsWs.createTempFile("temp", "txt"); prepareCommonMocks(); when(workspaces.exists(new Workspace("workspace"))).thenReturn(false); when(workspaces.newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class))).thenReturn(workspace); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, "tfs-ws", false, false).checkoutBySingleVersionSpec(server, hudsonWs, MY_LABEL); assertTrue("The local folder was removed", tfsWs.exists()); assertEquals("The local TFS folder was not cleaned", 0, tfsWs.list((FileFilter)null).size()); assertEquals("The Hudson workspace path was cleaned", 2, hudsonWs.list((FileFilter)null).size()); } @Test public void assertWorkspaceIsNotCleanedIfUsingUpdate() throws Exception { FilePath tfsWs = hudsonWs.child("tfs-ws"); tfsWs.mkdirs(); tfsWs.createTempFile("temp", "txt"); prepareCommonMocks(); when(workspaces.exists("workspace")).thenReturn(true); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); when(workspace.getComputer()).thenReturn("LocalComputer"); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, "tfs-ws", true, false).checkout(server, hudsonWs, null, Util.getCalendar(2009, 9, 24)); assertTrue("The local folder was removed", tfsWs.exists()); assertEquals("The TFS workspace path was cleaned", 1, hudsonWs.list((FileFilter)null).size()); } @Bug(3882) @Test public void assertCheckoutBySingleVersionSpecDeletesWorkspaceAtStartIfNotUsingUpdate() throws Exception { prepareCommonMocks(); when(workspaces.exists("workspace")).thenReturn(true).thenReturn(false); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); when(project.getProjectPath()).thenReturn("project"); when(workspaces.newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class))).thenReturn(workspace); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", false, false).checkoutBySingleVersionSpec(server, hudsonWs, MY_LABEL); verify(server).getWorkspaces(); verify(workspaces, times(2)).exists("workspace"); verify(workspaces).getWorkspace("workspace"); verify(workspaces).deleteWorkspace(workspace); verify(workspaces).newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class)); verify(workspaces).getWorkspaceMapping(anyString()); verifyNoMoreInteractions(workspaces); } @Bug(3882) @Test public void assertCheckoutDeletesWorkspaceAtStartIfNotUsingUpdate() throws Exception { prepareCommonMocks(); when(workspaces.exists("workspace")).thenReturn(true).thenReturn(false); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); when(project.getProjectPath()).thenReturn("project"); when(workspaces.newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class))).thenReturn(workspace); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", false, false).checkout(server, hudsonWs, null, Util.getCalendar(2009, 9, 24)); verify(server).getWorkspaces(); verify(workspaces, times(2)).exists("workspace"); verify(workspaces).getWorkspace("workspace"); verify(workspaces).deleteWorkspace(workspace); verify(workspaces).newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class)); verify(workspaces).getWorkspaceMapping(anyString()); verifyNoMoreInteractions(workspaces); } @Bug(3882) @Test public void assertCheckoutDoesNotDeleteWorkspaceAtStartIfUsingUpdate() throws Exception { prepareCommonMocks(); when(workspaces.exists("workspace")).thenReturn(true).thenReturn(true); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", true, false).checkout(server, hudsonWs, null, Util.getCalendar(2009, 9, 24)); verify(server).getWorkspaces(); verify(workspaces, times(2)).exists("workspace"); verify(workspaces).getWorkspace("workspace"); verify(workspaces).getWorkspaceMapping(anyString()); verifyNoMoreInteractions(workspaces); } @Bug(3882) @Test public void assertCheckoutBySingleVersionSpecDoesNotDeleteWorkspaceAtStartIfUsingUpdate() throws Exception { prepareCommonMocks(); when(workspaces.exists("workspace")).thenReturn(true).thenReturn(true); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", true, false).checkoutBySingleVersionSpec(server, hudsonWs, MY_LABEL); verify(server).getWorkspaces(); verify(workspaces, times(2)).exists("workspace"); verify(workspaces).getWorkspace("workspace"); verify(workspaces).getWorkspaceMapping(anyString()); verifyNoMoreInteractions(workspaces); } @Bug(3882) @Test public void assertCheckoutDoesNotDeleteWorkspaceIfNotUsingUpdateAndThereIsNoWorkspace() throws Exception { prepareCommonMocks(); when(workspaces.exists("workspace")).thenReturn(false).thenReturn(false); when(workspaces.newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class))).thenReturn(workspace); when(project.getProjectPath()).thenReturn("project"); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", false, false).checkout(server, hudsonWs, null, Util.getCalendar(2009, 9, 24)); verify(server).getWorkspaces(); verify(workspaces, times(2)).exists("workspace"); verify(workspaces).newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class)); verify(workspaces).getWorkspaceMapping(anyString()); verifyNoMoreInteractions(workspaces); } @Bug(3882) @Test public void assertCheckoutBySingleVersionSpecDoesNotDeleteWorkspaceIfNotUsingUpdateAndThereIsNoWorkspace() throws Exception { prepareCommonMocks(); when(workspaces.exists("workspace")).thenReturn(false).thenReturn(false); when(workspaces.newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class))).thenReturn(workspace); when(project.getProjectPath()).thenReturn("project"); new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", false, false).checkoutBySingleVersionSpec(server, hudsonWs, MY_LABEL); verify(server).getWorkspaces(); verify(workspaces, times(2)).exists("workspace"); verify(workspaces).newWorkspace(eq("workspace"), eq("project"), eq(EMPTY_CLOAKED_PATHS_LIST), isA(String.class)); verify(workspaces).getWorkspaceMapping(anyString()); verifyNoMoreInteractions(workspaces); } @Bug(6596) @Test public void assertCheckoutOnlyRetrievesChangesToTheStartTimestampForCurrentBuild() throws Exception { List list = new ArrayList(); prepareCommonMocks(); when(workspaces.exists("workspace")).thenReturn(true); when(workspaces.getWorkspace("workspace")).thenReturn(workspace); when(workspace.getComputer()).thenReturn("LocalComputer"); when(project.getDetailedHistoryWithoutCloakedPaths(isA(VersionSpec.class), isA(VersionSpec.class), anyCollection())).thenReturn(list); CheckoutAction action = new CheckoutAction("workspace", "project", EMPTY_CLOAKED_PATHS_LIST, ".", true, false); final Calendar startDate = Util.getCalendar(2008, 9, 24); final Calendar endDate = Util.getCalendar(2009, 9, 24); List actualList = action.checkout(server, hudsonWs, startDate, endDate); assertEquals("The list from the detailed history, was not the same as returned from checkout", list, actualList); final DateVersionSpec startDateVersionSpec = new DateVersionSpec(startDate); final DateVersionSpec endDateVersionSpec = new DateVersionSpec(endDate); verify(project).getDetailedHistoryWithoutCloakedPaths( argThat(new DateVersionSpecMatcher(startDateVersionSpec)), argThat(new DateVersionSpecMatcher(endDateVersionSpec)), eq(EMPTY_CLOAKED_PATHS_LIST)); verify(project).getFiles(isA(String.class), eq("D2009-09-24T00:00:00Z"), eq(false)); } private static class DateVersionSpecMatcher extends CustomMatcher { private final DateVersionSpec base; public DateVersionSpecMatcher(final DateVersionSpec base) { super(base == null ? "(null)" : base.toString()); this.base = base; } public boolean matches(final Object item) { if (base == null) { return item == null; } if (item != null && item instanceof DateVersionSpec) { final DateVersionSpec candidate = (DateVersionSpec) item; final Calendar baseDate = base.getDate(); final Calendar candidateDate = candidate.getDate(); return baseDate.equals(candidateDate); } return false; } } @Test public void determineCheckoutPath_absoluteOverrideOnNix() { final VirtualChannel vc = mock(VirtualChannel.class); final String nixPath = "/opt/.jenkins/jobs/tfs-plugin/workspace"; final FilePath workspacePath = new FilePath(vc, nixPath); final String localFolder = "/home/jenkins/tfs-plugin"; final String actual = CheckoutAction.determineCheckoutPath(workspacePath, localFolder); Assert.assertEquals(localFolder, actual); } @Test public void determineCheckoutPath_absoluteOverrideOnWindows() { final VirtualChannel vc = mock(VirtualChannel.class); final String windowsPath = "C:\\.jenkins\\jobs\\tfs-plugin\\workspace"; final FilePath workspacePath = new FilePath(vc, windowsPath); final String localFolder = "C:\\Users\\Jenkins\\tfs-plugin"; final String actual = CheckoutAction.determineCheckoutPath(workspacePath, localFolder); Assert.assertEquals(localFolder, actual); } @Test public void determineCheckoutPath_absoluteOverrideOnWindowsWithForwardSlashes() { final VirtualChannel vc = mock(VirtualChannel.class); final String windowsPath = "C:/.jenkins/jobs/tfs-plugin/workspace"; final FilePath workspacePath = new FilePath(vc, windowsPath); final String localFolder = "C:/Users/Jenkins/tfs-plugin"; final String actual = CheckoutAction.determineCheckoutPath(workspacePath, localFolder); Assert.assertEquals(localFolder, actual); } @Test public void determineCheckoutPath_defaultOnNix() { final VirtualChannel vc = mock(VirtualChannel.class); final String nixPath = "/opt/.jenkins/jobs/tfs-plugin/workspace"; final FilePath workspacePath = new FilePath(vc, nixPath); final String actual = CheckoutAction.determineCheckoutPath(workspacePath, "."); Assert.assertEquals(nixPath, actual); } @Test public void determineCheckoutPath_defaultOnWindows() { final VirtualChannel vc = mock(VirtualChannel.class); final String windowsPath = "C:\\.jenkins\\jobs\\tfs-plugin\\workspace"; final FilePath workspacePath = new FilePath(vc, windowsPath); final String actual = CheckoutAction.determineCheckoutPath(workspacePath, "."); Assert.assertEquals(windowsPath, actual); } @Test public void determineCheckoutPath_defaultOnWindowsWithForwardSlashes() { final VirtualChannel vc = mock(VirtualChannel.class); final String windowsPath = "C:/.jenkins/jobs/tfs-plugin/workspace"; final FilePath workspacePath = new FilePath(vc, windowsPath); final String actual = CheckoutAction.determineCheckoutPath(workspacePath, "."); Assert.assertEquals(windowsPath, actual); } @Test public void determineCheckoutPath_relativeOnNix() { final VirtualChannel vc = mock(VirtualChannel.class); final String nixPath = "/opt/.jenkins/jobs/tfs-plugin/workspace"; final FilePath workspacePath = new FilePath(vc, nixPath); final String actual = CheckoutAction.determineCheckoutPath(workspacePath, "../files"); Assert.assertEquals("/opt/.jenkins/jobs/tfs-plugin/files", actual); } @Test public void determineCheckoutPath_relativeOnWindows() { final VirtualChannel vc = mock(VirtualChannel.class); final String windowsPath = "C:\\.jenkins\\jobs\\tfs-plugin\\workspace"; final FilePath workspacePath = new FilePath(vc, windowsPath); final String actual = CheckoutAction.determineCheckoutPath(workspacePath, "..\\files"); Assert.assertEquals("C:\\.jenkins\\jobs\\tfs-plugin\\files", actual); } @Test public void determineCheckoutPath_relativeOnWindowsWithForwardSlashes() { final VirtualChannel vc = mock(VirtualChannel.class); final String windowsPath = "C:/.jenkins/jobs/tfs-plugin/workspace"; final FilePath workspacePath = new FilePath(vc, windowsPath); final String actual = CheckoutAction.determineCheckoutPath(workspacePath, "../files"); Assert.assertEquals("C:/.jenkins/jobs/tfs-plugin/files", actual); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/actions/RemoveWorkspaceActionTest.java ================================================ package hudson.plugins.tfs.actions; import static org.hamcrest.CoreMatchers.*; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import static org.junit.Assert.*; import hudson.plugins.tfs.model.Server; import hudson.plugins.tfs.model.Workspace; import hudson.plugins.tfs.model.Workspaces; import org.junit.*; import org.mockito.Mock; import org.mockito.MockitoAnnotations; public class RemoveWorkspaceActionTest { @Mock private Server server; @Mock private Workspaces workspaces; @Mock private Workspace workspace; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); } @Test public void assertNoSuchWorkspaceNameDoesNothing() throws Exception { when(server.getWorkspaces()).thenReturn(workspaces); when(workspaces.exists(anyString())).thenReturn(false); RemoveWorkspaceAction action = new RemoveWorkspaceAction("workspace"); assertThat(action.remove(server), is(false)); verify(server).getWorkspaces(); verify(workspaces).exists("workspace"); verifyNoMoreInteractions(workspace); verifyNoMoreInteractions(workspaces); verifyNoMoreInteractions(server); } @Test public void assertWorkspaceIsDeleted() throws Exception { when(server.getWorkspaces()).thenReturn(workspaces); when(workspaces.exists(anyString())).thenReturn(true); when(workspaces.getWorkspace(anyString())).thenReturn(workspace); RemoveWorkspaceAction action = new RemoveWorkspaceAction("workspace"); assertThat(action.remove(server), is(true)); verify(server).getWorkspaces(); verify(workspaces).exists("workspace"); verify(workspaces).getWorkspace("workspace"); verify(workspaces).deleteWorkspace(workspace); verifyNoMoreInteractions(workspace); verifyNoMoreInteractions(workspaces); verifyNoMoreInteractions(server); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/browsers/TeamSystemWebAccessBrowserTest.java ================================================ package hudson.plugins.tfs.browsers; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.plugins.tfs.TeamFoundationServerScm; import hudson.plugins.tfs.model.ChangeLogSet; import hudson.plugins.tfs.model.ChangeSet; import hudson.plugins.tfs.model.ChangeSet.Item; import java.net.URL; import hudson.util.Secret; import org.junit.Test; import org.jvnet.hudson.test.Bug; @SuppressWarnings("rawtypes") public class TeamSystemWebAccessBrowserTest { @Test public void assertChangeSetLinkWithServerUrlWithPort() throws Exception { TeamSystemWebAccessBrowser browser = new TeamSystemWebAccessBrowser("http://tfs:8080/"); ChangeSet changeSet = new ChangeSet("99", null, "user", "comment"); URL actual = browser.getChangeSetLink(changeSet); assertEquals("The change set link was incorrect", "http://tfs:8080/_versionControl/changeset/99", actual.toString()); } @Test public void assertChangeSetLinkWithRealisticServerUrlWithPort() throws Exception { TeamSystemWebAccessBrowser browser = new TeamSystemWebAccessBrowser("http://tfs:8080/tfs/coll"); ChangeSet changeSet = new ChangeSet("99", null, "user", "comment"); URL actual = browser.getChangeSetLink(changeSet); assertEquals("The change set link was incorrect", "http://tfs:8080/tfs/coll/_versionControl/changeset/99", actual.toString()); } @Test public void assertChangeSetLinkWithRealisticServerUrl() throws Exception { TeamSystemWebAccessBrowser browser = new TeamSystemWebAccessBrowser("http://tfs/tfs/coll"); ChangeSet changeSet = new ChangeSet("99", null, "user", "comment"); URL actual = browser.getChangeSetLink(changeSet); assertEquals("The change set link was incorrect", "http://tfs/tfs/coll/_versionControl/changeset/99", actual.toString()); } @Bug(7394) @Test public void assertChangeSetLinkWithOnlyServerUrl() throws Exception { TeamSystemWebAccessBrowser browser = new TeamSystemWebAccessBrowser("http://tfs"); ChangeSet changeSet = new ChangeSet("99", null, "user", "comment"); URL actual = browser.getChangeSetLink(changeSet); assertEquals("The change set link was incorrect", "http://tfs/_versionControl/changeset/99", actual.toString()); } @Bug(7394) @Test public void assertChangeSetLinkWithOnlyServerUrlWithTrailingSlash() throws Exception { TeamSystemWebAccessBrowser browser = new TeamSystemWebAccessBrowser("http://tfs/"); ChangeSet changeSet = new ChangeSet("99", null, "user", "comment"); URL actual = browser.getChangeSetLink(changeSet); assertEquals("The change set link was incorrect","http://tfs/_versionControl/changeset/99", actual.toString()); } private static TeamFoundationServerScm createTestScm(String serverUrl) { serverUrl = serverUrl != null ? serverUrl : "http://server:80/tfs/collection/"; final String projectPath = "$/project/folder/folder/branch"; final Secret password = null; final String userName = null; final TeamFoundationServerScm result = new TeamFoundationServerScm(serverUrl, projectPath, null, userName, password); final TeamFoundationServerRepositoryBrowser repositoryBrowser = mock(TeamFoundationServerRepositoryBrowser.class); result.setRepositoryBrowser(repositoryBrowser); return result; } @Test public void assertChangeSetLinkUsesScmConfiguration() throws Exception { AbstractBuild build = mock(AbstractBuild.class); AbstractProject project = mock(AbstractProject.class); when(build.getProject()).thenReturn(project); final TeamFoundationServerScm testScm = createTestScm(null); when(project.getScm()).thenReturn(testScm); ChangeSet changeset = new ChangeSet("62643", null, "user", "comment"); new ChangeLogSet(build, new ChangeSet[]{ changeset}); TeamSystemWebAccessBrowser browser = new TeamSystemWebAccessBrowser(""); // configured but no URL specified URL actual = browser.getChangeSetLink(changeset); assertEquals("The change set link was incorrect", "http://server:80/tfs/collection/_versionControl/changeset/62643", actual.toString()); } @Test public void assertChangeSetLinkUsesScmConfigurationNoSlash() throws Exception { AbstractBuild build = mock(AbstractBuild.class); AbstractProject project = mock(AbstractProject.class); when(build.getProject()).thenReturn(project); // the server URL has not trailing slash... final TeamFoundationServerScm testScm = createTestScm("http://server:80/tfs/collection"); when(project.getScm()).thenReturn(testScm); ChangeSet changeset = new ChangeSet("62643", null, "user", "comment"); new ChangeLogSet(build, new ChangeSet[]{ changeset}); TeamSystemWebAccessBrowser browser = new TeamSystemWebAccessBrowser(""); // configured but no URL specified URL actual = browser.getChangeSetLink(changeset); assertEquals("The change set link was incorrect", "http://server:80/tfs/collection/_versionControl/changeset/62643", actual.toString()); } @Test public void assertFileLinkUsesScmConfiguration() throws Exception { AbstractBuild build = mock(AbstractBuild.class); AbstractProject project = mock(AbstractProject.class); when(build.getProject()).thenReturn(project); final TeamFoundationServerScm testScm = createTestScm(null); when(project.getScm()).thenReturn(testScm); ChangeSet changeset = new ChangeSet("62643", null, "user", "comment"); ChangeSet.Item item = new Item("$/project/folder/folder/branch/some/path/to/some/file.txt", "action"); changeset.add(item); new ChangeLogSet(build, new ChangeSet[]{ changeset}); TeamSystemWebAccessBrowser browser = new TeamSystemWebAccessBrowser(""); // configured but no URL specified URL actual = browser.getFileLink(item); assertEquals("The file link was incorrect", "http://server:80/tfs/collection/_versionControl/changeset/62643#path=%24%2Fproject%2Ffolder%2Ffolder%2Fbranch%2Fsome%2Fpath%2Fto%2Fsome%2Ffile.txt&version=62643&_a=contents", actual.toString()); } @Test public void assertDiffLinkUsesScmConfiguration() throws Exception { AbstractBuild build = mock(AbstractBuild.class); AbstractProject project = mock(AbstractProject.class); when(build.getProject()).thenReturn(project); final TeamFoundationServerScm testScm = createTestScm(null); when(project.getScm()).thenReturn(testScm); ChangeSet changeset = new ChangeSet("62643", null, "user", "comment"); new ChangeLogSet(build, new ChangeSet[]{ changeset}); ChangeSet.Item item = new Item("$/project/folder/folder/branch/some/path/to/some/file.txt", "action"); changeset.add(item); TeamSystemWebAccessBrowser browser = new TeamSystemWebAccessBrowser(""); // configured but no URL specified URL actual = browser.getDiffLink(item); assertEquals("The diff link was incorrect", "http://server:80/tfs/collection/_versionControl/changeset/62643#path=%24%2Fproject%2Ffolder%2Ffolder%2Fbranch%2Fsome%2Fpath%2Fto%2Fsome%2Ffile.txt&version=62643&_a=compare", actual.toString()); } @Test public void assertFileLink() throws Exception { TeamSystemWebAccessBrowser browser = new TeamSystemWebAccessBrowser("http://tfs:8080/"); ChangeSet changeSet = new ChangeSet("99", null, "user", "comment"); ChangeSet.Item item = new ChangeSet.Item("$/Project/Folder/file.cs", "add"); changeSet.add(item); URL actual = browser.getFileLink(item); assertEquals("The file link was incorrect", "http://tfs:8080/_versionControl/changeset/99#path=%24%2FProject%2FFolder%2Ffile.cs&version=99&_a=contents", actual.toString()); } @Test public void assertFileLinkWithRealisticServerUrl() throws Exception { TeamSystemWebAccessBrowser browser = new TeamSystemWebAccessBrowser("http://tfs:8080/tfs/coll"); ChangeSet changeSet = new ChangeSet("99", null, "user", "comment"); ChangeSet.Item item = new ChangeSet.Item("$/Project/Folder/file.cs", "add"); changeSet.add(item); URL actual = browser.getFileLink(item); assertEquals("The file link was incorrect", "http://tfs:8080/tfs/coll/_versionControl/changeset/99#path=%24%2FProject%2FFolder%2Ffile.cs&version=99&_a=contents", actual.toString()); } @Test public void assertDiffLink() throws Exception { TeamSystemWebAccessBrowser browser = new TeamSystemWebAccessBrowser("http://tfs:8080/"); ChangeSet changeSet = new ChangeSet("99", null, "user", "comment"); ChangeSet.Item item = new ChangeSet.Item("$/Project/Folder/file.cs", "edit"); changeSet.add(item); URL actual = browser.getDiffLink(item); assertEquals("The diff link was incorrect", "http://tfs:8080/_versionControl/changeset/99#path=%24%2FProject%2FFolder%2Ffile.cs&version=99&_a=compare", actual.toString()); } @Test public void assertDiffLinkWithRealisticServerUrl() throws Exception { TeamSystemWebAccessBrowser browser = new TeamSystemWebAccessBrowser("http://tfs:8080/tfs/coll"); ChangeSet changeSet = new ChangeSet("99", null, "user", "comment"); ChangeSet.Item item = new ChangeSet.Item("$/Project/Folder/file.cs", "edit"); changeSet.add(item); URL actual = browser.getDiffLink(item); assertEquals("The diff link was incorrect", "http://tfs:8080/tfs/coll/_versionControl/changeset/99#path=%24%2FProject%2FFolder%2Ffile.cs&version=99&_a=compare", actual.toString()); } @Test public void assertNullDiffLinkForAddedFile() throws Exception { TeamSystemWebAccessBrowser browser = new TeamSystemWebAccessBrowser("http://tfs:8080/"); ChangeSet changeSet = new ChangeSet("99", null, "user", "comment"); ChangeSet.Item item = new ChangeSet.Item("$/Project/Folder/file.cs", "add"); changeSet.add(item); assertNull("The diff link should be null for new files", browser.getDiffLink(item)); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/commands/AbstractCallableCommandTest.java ================================================ package hudson.plugins.tfs.commands; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.ChangesetVersionSpec; import hudson.model.TaskListener; import hudson.plugins.tfs.model.ExtraSettings; import hudson.plugins.tfs.model.MockableVersionControlClient; import hudson.plugins.tfs.model.Server; import hudson.plugins.tfs.model.WebProxySettings; import hudson.remoting.Callable; import hudson.util.Secret; import hudson.util.SecretOverride; import org.apache.commons.collections.IteratorUtils; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.ObjectOutputStream; import java.io.PrintStream; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public abstract class AbstractCallableCommandTest { protected Server server; protected MockableVersionControlClient vcc; protected TaskListener listener; protected ByteArrayOutputStream outputStream; @Before public void configureDefaultMocks() { server = mock(Server.class); vcc = mock(MockableVersionControlClient.class); listener = mock(TaskListener.class); outputStream = new ByteArrayOutputStream(); when(server.getVersionControlClient()).thenReturn(vcc); when(server.getListener()).thenReturn(listener); when(listener.getLogger()).thenReturn(new PrintStream(outputStream)); } protected void assertLog(final String... expectedLines) throws IOException { final byte[] outputBytes = outputStream.toByteArray(); final ByteArrayInputStream inputStream = new ByteArrayInputStream(outputBytes); final InputStreamReader inputStreamReader = new InputStreamReader(inputStream); final BufferedReader bufferedReader = new BufferedReader(inputStreamReader); final Iterator expectedIterator = IteratorUtils.arrayIterator(expectedLines); String line; int lineCounter = 1; while ((line = bufferedReader.readLine()) != null) { if (expectedIterator.hasNext()) { final String expected = expectedIterator.next(); Assert.assertEquals("Lines differ at line #" + lineCounter + ".", expected, line); } else { Assert.fail("Log contained more lines than expected."); } } Assert.assertFalse("Log contained less lines than expected.", expectedIterator.hasNext()); } protected abstract AbstractCallableCommand createCommand(final ServerConfigurationProvider serverConfig); @Test public void verifySerializable() throws IOException { final ServerConfigurationProvider server = new ServerConfigurationProvider() { public String getUrl() { return null; } public String getUserName() { return null; } public String getUserPassword() { return null; } public TaskListener getListener() { return null; } public WebProxySettings getWebProxySettings() { final List patterns = Arrays.asList( Pattern.compile(".+\\.com"), Pattern.compile(".+\\.org") ); final Secret secret; final SecretOverride secretOverride = new SecretOverride(); try { secret = Secret.fromString("password"); } finally { try { secretOverride.close(); } catch (final IOException ignored) { } } return new WebProxySettings("localhost", 8080, patterns, "user", secret); } @Override public ExtraSettings getExtraSettings() { return ExtraSettings.DEFAULT; } }; final AbstractCallableCommand command = createCommand(server); final Callable callable = command.getCallable(); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(callable); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/commands/AbstractCommandTest.java ================================================ package hudson.plugins.tfs.commands; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import hudson.plugins.tfs.util.MaskedArgumentListBuilder; import hudson.util.ArgumentListBuilder; import org.junit.Test; public class AbstractCommandTest { @Test public void assertAddingServerArguments() { ServerConfigurationProvider config = mock(ServerConfigurationProvider.class); when(config.getUrl()).thenReturn("https://tfs02.codeplex.com"); AbstractCommand command = new AbstractCommandImpl(config); ArgumentListBuilder builder = new ArgumentListBuilder(); command.addServerArgument(builder); assertEquals("The server URL was incorrect", "-server:https://tfs02.codeplex.com", builder.toCommandArray()[0]); } @Test public void assertAddingUserCredentials() { ServerConfigurationProvider config = mock(ServerConfigurationProvider.class); when(config.getUserName()).thenReturn("user"); when(config.getUserPassword()).thenReturn("password"); AbstractCommand command = new AbstractCommandImpl(config); MaskedArgumentListBuilder builder = new MaskedArgumentListBuilder(); command.addLoginArgument(builder); assertEquals("The login argument was incorrect", "-login:user,password", builder.toCommandArray()[0]); assertTrue("The login argument was not masked", builder.toMaskArray()[0]); } @Test public void assertNotAddingInvalidUserCredentials() { ServerConfigurationProvider config = mock(ServerConfigurationProvider.class); when(config.getUserName()).thenReturn("user"); when(config.getUserPassword()).thenReturn(null); AbstractCommand command = new AbstractCommandImpl(config); MaskedArgumentListBuilder builder = new MaskedArgumentListBuilder(); command.addLoginArgument(builder); assertTrue("The login argument was added", builder.toCommandArray().length == 0); assertTrue("The login argument was added", builder.toMaskArray().length == 0); } @Test public void assertNotAddingUserCredentialsForEmptyName() { ServerConfigurationProvider config = mock(ServerConfigurationProvider.class); when(config.getUserName()).thenReturn(""); when(config.getUserPassword()).thenReturn(""); AbstractCommand command = new AbstractCommandImpl(config); MaskedArgumentListBuilder builder = new MaskedArgumentListBuilder(); command.addLoginArgument(builder); assertTrue("The login argument was added", builder.toCommandArray().length == 0); assertTrue("The login argument was added", builder.toMaskArray().length == 0); } @Test public void assertAddingUserCredentialsForEmptyPassword() { ServerConfigurationProvider config = mock(ServerConfigurationProvider.class); when(config.getUserName()).thenReturn("aname"); when(config.getUserPassword()).thenReturn(""); AbstractCommand command = new AbstractCommandImpl(config); MaskedArgumentListBuilder builder = new MaskedArgumentListBuilder(); command.addLoginArgument(builder); assertEquals("The login argument was incorrect", "-login:aname,", builder.toCommandArray()[0]); assertTrue("The login argument was not masked", builder.toMaskArray()[0]); } private static class AbstractCommandImpl extends AbstractCommand { public AbstractCommandImpl(ServerConfigurationProvider provider) { super(provider); } public MaskedArgumentListBuilder getArguments() { throw new IllegalStateException(); } } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/commands/DeleteWorkspaceCommandTest.java ================================================ package hudson.plugins.tfs.commands; import com.microsoft.tfs.core.clients.versioncontrol.WorkspacePermissions; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace; import hudson.plugins.tfs.model.Server; import hudson.remoting.Callable; import org.junit.Test; import org.mockito.Matchers; import java.io.IOException; import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.isA; import static org.mockito.Mockito.when; public class DeleteWorkspaceCommandTest extends AbstractCallableCommandTest { @Test public void assertLogging() throws Exception { when(server.getUserName()).thenReturn("snd\\user_cp"); final Workspace[] emptyWorkspaceList = new Workspace[0]; when(vcc.queryWorkspaces(isA(String.class), Matchers.anyObject(), isA(String.class), isA(WorkspacePermissions.class))).thenReturn(emptyWorkspaceList); final DeleteWorkspaceCommand command = new DeleteWorkspaceCommand(server, "TheWorkspaceName", "computerName") { @Override public Server createServer() { return server; } }; final Callable callable = command.getCallable(); callable.call(); assertLog( "Deleting workspaces named 'TheWorkspaceName' from computer 'computerName'...", "Deleted 0 workspace(s) named 'TheWorkspaceName'." ); } @Override protected AbstractCallableCommand createCommand(final ServerConfigurationProvider serverConfig) { return new DeleteWorkspaceCommand(serverConfig, "workspaceName", "computerName"); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/commands/GetFilesToWorkFolderCommandTest.java ================================================ package hudson.plugins.tfs.commands; import static org.mockito.Mockito.*; import java.io.IOException; import java.io.PrintStream; import com.microsoft.tfs.core.clients.versioncontrol.events.GetEvent; import hudson.remoting.Callable; import org.junit.Ignore; import org.junit.Test; public class GetFilesToWorkFolderCommandTest extends AbstractCallableCommandTest { @Ignore("Finish test when we have MockableWorkspace and MockableVersionControlEventEngine") @Test public void assertLogging() throws Exception { when(vcc.queryWorkspace( isA(String.class), isA(String.class))).thenReturn(null); final GetFilesToWorkFolderCommand command = new GetFilesToWorkFolderCommand(server, "c:/jenkins/jobs/newJob/workspace", "C618", false); final Callable callable = command.getCallable(); callable.call(); assertLog( "Getting version 'C618' to 'c:/jenkins/jobs/newJob/workspace'...", "Finished getting version 'C618'." ); } @Test public void onGet_typical() throws IOException { final GetEvent getEvent = mock(GetEvent.class); final String pathToFile = "C:\\.jenkins\\jobs\\typical\\workspace\\TODO.txt"; when(getEvent.getTargetLocalItem()).thenReturn(pathToFile); final GetFilesToWorkFolderCommand cut = new GetFilesToWorkFolderCommand(server, null, null, false, true); cut.setLogger(new PrintStream(this.outputStream)); cut.onGet(getEvent); assertLog( pathToFile ); } @Override protected AbstractCallableCommand createCommand(final ServerConfigurationProvider serverConfig) { return new GetFilesToWorkFolderCommand(serverConfig, "workFolder", "versionSpec", false); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/commands/LabelCommandTest.java ================================================ package hudson.plugins.tfs.commands; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.LabelChildOption; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.LabelResult; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.VersionControlLabel; import com.microsoft.tfs.core.clients.versioncontrol.specs.LabelItemSpec; import hudson.plugins.tfs.model.Server; import hudson.remoting.Callable; import ms.tfs.versioncontrol.clientservices._03._LabelResult; import ms.tfs.versioncontrol.clientservices._03._LabelResultStatus; import org.junit.Test; import org.mockito.Matchers; import static org.mockito.Mockito.when; public class LabelCommandTest extends AbstractCallableCommandTest { @Test public void assertLogging() throws Exception { final LabelResult labelResult = new LabelResult(new _LabelResult("label", "scope", _LabelResultStatus.Created)); final LabelResult[] labelResults = {labelResult}; when(vcc.createLabel( Matchers.anyObject(), Matchers.anyObject(), Matchers.anyObject())).thenReturn(labelResults); final LabelCommand command = new LabelCommand(server, "labelName", "hudson-createLabel-TFS2013", "$/project/path") { @Override public Server createServer() { return server; } }; final Callable callable = command.getCallable(); callable.call(); assertLog( "Creating label 'labelName' on '$/project/path' as of the current version in workspace 'hudson-createLabel-TFS2013'...", "Created label 'labelName'." ); } @Override protected AbstractCallableCommand createCommand(final ServerConfigurationProvider serverConfig) { return new LabelCommand(serverConfig, "labelName", "workspaceName", "$/projectPath"); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/commands/ListWorkspacesCommandTest.java ================================================ package hudson.plugins.tfs.commands; import com.microsoft.tfs.core.TFSTeamProjectCollection; import com.microsoft.tfs.core.clients.versioncontrol.VersionControlClient; import com.microsoft.tfs.core.clients.versioncontrol.WorkspaceLocation; import com.microsoft.tfs.core.clients.versioncontrol.WorkspacePermissions; import com.microsoft.tfs.core.httpclient.UsernamePasswordCredentials; import hudson.plugins.tfs.IntegrationTestHelper; import hudson.plugins.tfs.IntegrationTests; import hudson.plugins.tfs.model.NativeLibraryManager; import hudson.plugins.tfs.model.Server; import hudson.plugins.tfs.model.Workspace; import hudson.remoting.Callable; import org.junit.Test; import org.junit.experimental.categories.Category; import org.jvnet.hudson.test.Bug; import java.io.IOException; import java.io.StringReader; import java.net.URI; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class ListWorkspacesCommandTest extends AbstractCallableCommandTest { @Category(IntegrationTests.class) @Test public void assertLoggingWithComputer() throws Exception { final IntegrationTestHelper helper = new IntegrationTestHelper(); final String serverUrl = helper.getServerUrl(); final URI serverUri = URI.create(serverUrl); NativeLibraryManager.initialize(); final UsernamePasswordCredentials credentials = new UsernamePasswordCredentials( helper.getUserName(), helper.getUserPassword()); final TFSTeamProjectCollection tpc = new TFSTeamProjectCollection(serverUri, credentials); try { final VersionControlClient vcc = tpc.getVersionControlClient(); final com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace[] workspaces = new com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace[1]; workspaces[0] = new com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace( vcc, "Hudson.JOBXXXXXXXXXXXXXX", "First.LastXX", "This is a comment", null, "XXXX-XXXX-007", WorkspaceLocation.SERVER ); when(server.getUrl()).thenReturn("http://tfs.invalid:8080/tfs/DefaultCollection/"); when(this.vcc.queryWorkspaces(null, null, "XXXX-XXXX-007", WorkspacePermissions.NONE_OR_NOT_SUPPORTED)) .thenReturn(workspaces); final ListWorkspacesCommand command = new ListWorkspacesCommand(server, "XXXX-XXXX-007", true) { @Override public Server createServer() { return server; } }; final Callable, Exception> callable = command.getCallable(); callable.call(); assertLog( "Downloading list of workspaces from http://tfs.invalid:8080/tfs/DefaultCollection/...", "Workspace Owner Computer Comment ", "------------------------ ------------ ------------- -----------------", "Hudson.JOBXXXXXXXXXXXXXX First.LastXX XXXX-XXXX-007 This is a comment" ); } finally { tpc.close(); } } @Category(IntegrationTests.class) @Test public void assertLoggingWithoutComputer() throws Exception { final IntegrationTestHelper helper = new IntegrationTestHelper(); final String serverUrl = helper.getServerUrl(); final URI serverUri = URI.create(serverUrl); NativeLibraryManager.initialize(); final UsernamePasswordCredentials credentials = new UsernamePasswordCredentials( helper.getUserName(), helper.getUserPassword()); final TFSTeamProjectCollection tpc = new TFSTeamProjectCollection(serverUri, credentials); try { final VersionControlClient vcc = tpc.getVersionControlClient(); final com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace[] workspaces = new com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace[1]; workspaces[0] = new com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Workspace( vcc, "Hudson.JOBXXXXXXXXXXXXXX", "First.LastXX", "This is a comment", null, "XXXX-XXXX-007", WorkspaceLocation.SERVER ); when(server.getUrl()).thenReturn("http://tfs.invalid:8080/tfs/DefaultCollection/"); when(this.vcc.queryWorkspaces(anyString(), anyString(), anyString(), isA(WorkspacePermissions.class))) .thenReturn(workspaces); final ListWorkspacesCommand command = new ListWorkspacesCommand(server, null, true) { @Override public Server createServer() { return server; } }; final Callable, Exception> callable = command.getCallable(); callable.call(); assertLog( "Downloading list of workspaces from http://tfs.invalid:8080/tfs/DefaultCollection/...", "Workspace Owner Computer Comment ", "------------------------ ------------ ------------- -----------------", "Hudson.JOBXXXXXXXXXXXXXX First.LastXX XXXX-XXXX-007 This is a comment" ); } finally { tpc.close(); } } @Test public void assertEmptyListWithEmptyOutput() throws Exception { ListWorkspacesCommand command = new ListWorkspacesCommand(mock(Server.class)); List list = command.parse(new StringReader("")); assertNotNull("List can not be null", list); assertEquals("Number of workspaces was incorrect", 0, list.size()); } @Test public void assertListWithValidOutput() throws Exception { StringReader reader = new StringReader( "Server: https://tfs02.codeplex.com/\n" + "Workspace Owner Computer Comment\n" + "--------- -------------- -------- ----------------------------------------------------------------------------------------------------------\n" + "\n" + "asterix2 SND\\redsolo_cp ASTERIX\n" + "astreix SND\\redsolo_cp ASTERIX This is a comment\n"); ListWorkspacesCommand command = new ListWorkspacesCommand( mock(Server.class)); List list = command.parse(reader); assertNotNull("List can not be null", list); assertEquals("Number of workspaces was incorrect", 2, list.size()); Workspace workspace = list.get(0); assertEquals("The workspace name is incorrect", "asterix2", workspace.getName()); assertEquals("The owner name is incorrect", "SND\\redsolo_cp", workspace.getOwner()); assertEquals("The computer name is incorrect", "ASTERIX", workspace.getComputer()); workspace = list.get(1); assertEquals("The workspace name is incorrect", "astreix", workspace.getName()); assertEquals("The owner name is incorrect", "SND\\redsolo_cp", workspace.getOwner()); assertEquals("The computer name is incorrect", "ASTERIX", workspace.getComputer()); assertEquals("The comment is incorrect", "This is a comment", workspace.getComment()); } @Test public void assertListWithWorkspaceContainingSpace() throws Exception { StringReader reader = new StringReader( "Server: https://tfs02.codeplex.com/\n" + "Workspace Owner Computer Comment\n" + "------------------ ---------- -------- ----------------------------------------\n" + "Hudson-node lookup redsolo_cp ASTERIX\n"); ListWorkspacesCommand command = new ListWorkspacesCommand( mock(Server.class)); List list = command.parse(reader); assertNotNull("List can not be null", list); assertEquals("Number of workspaces was incorrect", 1, list.size()); Workspace workspace = list.get(0); assertEquals("The workspace name is incorrect", "Hudson-node lookup", workspace.getName()); } @Bug(4666) @Test public void assertNoIndexOutOfBoundsIsThrown() throws Exception { StringReader reader = new StringReader( "Server: teamserver-01\n" + "Workspace Owner Computer Comment\n" + "----------------- ------ ----------- ------------------------------------------\n" + "Hudson-Scrumboard dennis W7-DENNIS-1\n" + "W7-DENNIS-1 dennis W7-DENNIS-1\n"); new ListWorkspacesCommand(mock(Server.class)).parse(reader); } @Bug(4726) @Test public void assertNoIndexOutOfBoundsIsThrownSecondEdition() throws Exception { StringReader reader = new StringReader( "Server: xxxx-xxxx-010\n" + "Workspace Owner Computer Comment\n" + "------------------------ ------------ ------------- ---------------------------\n" + "Hudson.JOBXXXXXXXXXXXXXX First.LastXX XXXX-XXXX-007\n"); new ListWorkspacesCommand(mock(Server.class)).parse(reader); } @Test public void logWithNoWorkspaces() throws IOException { ListWorkspacesCommand.log(new ArrayList(0), listener.getLogger()); assertLog( "Workspace Owner Computer Comment", "--------- ----- -------- -------" ); } @Test public void logWithManyWorkspaces() throws IOException { final ArrayList workspaces = new ArrayList(); workspaces.add(new Workspace("Hudson.JOBXXXXXXXXXXXXXX", "XXXX-XXXX-007", "First.LastXX", "This is a comment")); workspaces.add(new Workspace("Hudson-newJob-MASTER", "COMPUTER", "jenkins-tfs-plugin", "Created by the Jenkins tfs-plugin functional tests.")); ListWorkspacesCommand.log(workspaces, listener.getLogger()); assertLog( "Workspace Owner Computer Comment ", "------------------------ ------------------ ------------- ---------------------------------------------------", "Hudson.JOBXXXXXXXXXXXXXX First.LastXX XXXX-XXXX-007 This is a comment ", "Hudson-newJob-MASTER jenkins-tfs-plugin COMPUTER Created by the Jenkins tfs-plugin functional tests." ); } @Test public void logWithOneWorkspace() throws IOException { final ArrayList workspaces = new ArrayList(1); workspaces.add(new Workspace("asterix", "ASTERIX", "redsolo_cp", "This is a comment")); ListWorkspacesCommand.log(workspaces, listener.getLogger()); assertLog( "Workspace Owner Computer Comment ", "--------- ---------- -------- -----------------", "asterix redsolo_cp ASTERIX This is a comment" ); } @Override protected AbstractCallableCommand createCommand(final ServerConfigurationProvider serverConfig) { return new ListWorkspacesCommand(serverConfig, "computer", true); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/commands/NewWorkspaceCommandTest.java ================================================ package hudson.plugins.tfs.commands; import com.microsoft.tfs.core.TFSTeamProjectCollection; import com.microsoft.tfs.core.clients.versioncontrol.WorkspaceLocation; import com.microsoft.tfs.core.clients.versioncontrol.WorkspaceOptions; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.WorkingFolder; import hudson.plugins.tfs.model.Server; import hudson.remoting.Callable; import org.junit.Test; import static org.mockito.AdditionalMatchers.aryEq; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.when; import java.util.Collections; import java.util.List; public class NewWorkspaceCommandTest extends AbstractCallableCommandTest { private static final List EMPTY_CLOAKED_PATHS = Collections.emptyList(); @Test public void assertLogging() throws Exception { when(server.getUserName()).thenReturn("snd\\user_cp"); when(vcc.createWorkspace(aryEq((WorkingFolder[]) null), isA(String.class), isA(String.class), isA(String.class), isA(String.class), isA(WorkspaceLocation.class), isA(WorkspaceOptions.class))).thenReturn(null); final NewWorkspaceCommand command = new NewWorkspaceCommand(server, "TheWorkspaceName", null, EMPTY_CLOAKED_PATHS, null) { @Override public Server createServer() { return server; } @Override protected void updateCache(final TFSTeamProjectCollection connection) { // no-op for tests } }; final Callable callable = command.getCallable(); callable.call(); assertLog( "Creating workspace 'TheWorkspaceName' owned by 'snd\\user_cp'...", "Created workspace 'TheWorkspaceName'." ); } @Test public void assertLoggingWhenAlsoMapping() throws Exception { final List cloakedPaths = Collections.singletonList("$/Stuff/Hide"); when(server.getUserName()).thenReturn("snd\\user_cp"); when(vcc.createWorkspace(aryEq((WorkingFolder[]) null), isA(String.class), isA(String.class), isA(String.class), isA(String.class), isA(WorkspaceLocation.class), isA(WorkspaceOptions.class))).thenReturn(null); final NewWorkspaceCommand command = new NewWorkspaceCommand(server, "TheWorkspaceName", "$/Stuff", cloakedPaths, "/home/jenkins/jobs/stuff/workspace") { @Override public Server createServer() { return server; } @Override protected void updateCache(final TFSTeamProjectCollection connection) { // no-op for tests } }; final Callable callable = command.getCallable(); callable.call(); assertLog( "Creating workspace 'TheWorkspaceName' owned by 'snd\\user_cp'...", "Mapping '$/Stuff' to local folder '/home/jenkins/jobs/stuff/workspace' in workspace 'TheWorkspaceName'...", "Cloaking '$/Stuff/Hide' in workspace 'TheWorkspaceName'...", "Created workspace 'TheWorkspaceName'." ); } @Override protected AbstractCallableCommand createCommand(final ServerConfigurationProvider serverConfig) { return new NewWorkspaceCommand(serverConfig, "workspaceName", "$/serverPath", EMPTY_CLOAKED_PATHS, "local/path"); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/commands/RemoteChangesetVersionCommandTest.java ================================================ package hudson.plugins.tfs.commands; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.isA; import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Change; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.ChangeType; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Changeset; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Item; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.ItemType; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.RecursionType; import com.microsoft.tfs.core.clients.versioncontrol.specs.LabelSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.ChangesetVersionSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.DateVersionSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.LabelVersionSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.LatestVersionSpec; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.VersionSpec; import hudson.model.User; import hudson.plugins.tfs.Util; import hudson.plugins.tfs.model.Server; import java.io.StringReader; import hudson.plugins.tfs.model.UserLookup; import hudson.remoting.Callable; import org.junit.Assert; import org.junit.Test; public class RemoteChangesetVersionCommandTest extends AbstractCallableCommandTest { private static final DateVersionSpec fixedPointInTime = new DateVersionSpec(Util.getCalendar(2013, 07, 02, 15, 40, 50, "UTC")); @Test public void assertLoggingWhenChangeset() throws Exception { final User user = mock(User.class); when(user.getId()).thenReturn("piedefer"); final UserLookup userLookup = mock(UserLookup.class); when(userLookup.find("piedefer")).thenReturn(user); final Item item = new Item(); item.setServerItem("Arithmetica.iTeX"); item.setItemType(ItemType.FILE); final Change serverChange = new Change(item, ChangeType.EDIT, null); final Changeset serverChangeset = new Changeset( new Change[]{serverChange}, "I have discovered a truly marvellous proof of this, which this margin is too narrow to contain.", null, null, "piedefer", "Pierre de Fermat", fixedPointInTime.getDate(), 1637, "piedefer", "Pierre de Fermat", null ); final Changeset[] serverChangesets = new Changeset[]{serverChangeset}; when(vcc.queryHistory( isA(String.class), isA(VersionSpec.class), anyInt(), isA(RecursionType.class), (String) isNull(), (VersionSpec) isNull(), (VersionSpec) isNull(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(serverChangesets); final RemoteChangesetVersionCommand command = new RemoteChangesetVersionCommand(server, "$/RemotePath", LatestVersionSpec.INSTANCE) { @Override public Server createServer() { return server; } }; final Callable callable = command.getCallable(); final Integer actual = callable.call(); Assert.assertNotNull(actual); Assert.assertEquals(1637, (int)actual); assertLog( "Querying for remote changeset at '$/RemotePath' as of 'T'...", "Query result is: Changeset #1637 by 'piedefer' on '2013-07-02T15:40:50Z'." ); } @Test public void assertLoggingWhenNoResult() throws Exception { when(vcc.queryHistory( isA(String.class), isA(VersionSpec.class), anyInt(), isA(RecursionType.class), (String) isNull(), (VersionSpec) isNull(), (VersionSpec) isNull(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(null); final RemoteChangesetVersionCommand command = new RemoteChangesetVersionCommand(server, "$/RemotePath", LatestVersionSpec.INSTANCE) { @Override public Server createServer() { return server; } }; final Callable callable = command.getCallable(); final Integer result = callable.call(); Assert.assertNull(result); assertLog( "Querying for remote changeset at '$/RemotePath' as of 'T'...", "Query returned no result!" ); } @Test public void assertNoChangesWithEmptyOutput() throws Exception { RemoteChangesetVersionCommand command = new RemoteChangesetVersionCommand(mock(Server.class), "$/tfsandbox", fixedPointInTime); String changesetNumber = command.parse(new StringReader("")); assertEquals("Change set number was incorrect", "", changesetNumber); } @Test public void assertChangesWithEmptyToolOutput() throws Exception { RemoteChangesetVersionCommand command = new RemoteChangesetVersionCommand(mock(Server.class), "$/tfsandbox", fixedPointInTime); StringReader reader = new StringReader("No history entries were found for the item and version combination specified.\n\n"); String changesetNumber = command.parse(reader); assertEquals("Change set number was incorrect", "", changesetNumber); } @Test public void assertChangesWithChangeOutput() throws Exception { StringReader reader = new StringReader( "Changeset User Date Comment\n" + "--------- -------------- -------------------- ----------------------------------------------------------------------------\n" + "\n" + "12495 SND\\redsolo_cp 2008-jun-27 13:21:25 changed and created one\n"); RemoteChangesetVersionCommand command = new RemoteChangesetVersionCommand(mock(Server.class), "$/tfsandbox", fixedPointInTime); String changesetNumber = command.parse(reader); assertEquals("Change set number was incorrect", "12495", changesetNumber); } @Test public void assertChangesWithNoComment() throws Exception { StringReader reader = new StringReader( "Changeset User Date Comment\n" + "--------- -------------- -------------------- ----------------------------------------------------------------------------\n" + "\n" + "12495 SND\\redsolo_cp 2008-jun-27 13:21:25\n"); RemoteChangesetVersionCommand command = new RemoteChangesetVersionCommand(mock(Server.class), "$/tfsandbox", fixedPointInTime); String changesetNumber = command.parse(reader); assertEquals("Change set number was incorrect", "12495", changesetNumber); } @Test public void assertChangesNoEmptyLine() throws Exception { StringReader reader = new StringReader( "Changeset User Date Comment\n" + "--------- -------------- -------------------- ----------------------------------------------------------------------------\n" + "12497 SND\\redsolo_cp 2008-jun-27 13:21:25\n"); RemoteChangesetVersionCommand command = new RemoteChangesetVersionCommand(mock(Server.class), "$/tfsandbox", fixedPointInTime); String changesetNumber = command.parse(reader); assertEquals("Change set number was incorrect", "12497", changesetNumber); } @Test public void getVersionSpecificationWhenDateVersionSpec() { final String actual = RemoteChangesetVersionCommand.toString(fixedPointInTime); assertEquals("D2013-07-02T15:40:50Z", actual); } @Test public void getVersionSpecificationWhenChangesetVersionSpec() { final ChangesetVersionSpec versionSpec = new ChangesetVersionSpec(42); final String actual = RemoteChangesetVersionCommand.toString(versionSpec); assertEquals("C42", actual); } @Test public void getVersionSpecificationWhenLabelVersionSpecWithoutScope() { final LabelVersionSpec versionSpec = new LabelVersionSpec(new LabelSpec("Foo", null)); final String actual = RemoteChangesetVersionCommand.toString(versionSpec); assertEquals("LFoo", actual); } @Test public void getVersionSpecificationWhenLabelVersionSpecWithScope() { final LabelVersionSpec versionSpec = new LabelVersionSpec(new LabelSpec("Foo", "$/Bar")); final String actual = RemoteChangesetVersionCommand.toString(versionSpec); assertEquals("LFoo@$/Bar", actual); } @Override protected AbstractCallableCommand createCommand(final ServerConfigurationProvider serverConfig) { final ChangesetVersionSpec versionSpec = new ChangesetVersionSpec(42); final RemoteChangesetVersionCommand command = new RemoteChangesetVersionCommand(serverConfig, "$/remotePath", versionSpec); return command; } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/AbstractHookEventTest.java ================================================ package hudson.plugins.tfs.model; import org.eclipse.jgit.transport.URIish; import org.junit.Assert; import org.junit.Test; /** * A class to test {@link AbstractHookEvent}. */ public class AbstractHookEventTest { } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/ChangeLogSetIntegrationTest.java ================================================ package hudson.plugins.tfs.model; import org.jvnet.hudson.test.Bug; import org.jvnet.hudson.test.HudsonTestCase; import org.jvnet.hudson.test.recipes.LocalData; import com.gargoylesoftware.htmlunit.html.HtmlPage; public class ChangeLogSetIntegrationTest extends HudsonTestCase { /** * Asserts that polling now longer throws an exception. * @throws Exception thrown if problem */ @LocalData @Bug(4943) public void testThatLogSetContainsCheckedInByUserReference() throws Exception { HtmlPage page = new WebClient().getPage(hudson.getItem("4943"), "2/changes"); assertXPath(page, "//a[@href=\"/user/dude/\"]"); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/ChangeLogSetTest.java ================================================ package hudson.plugins.tfs.model; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.List; import org.junit.Test; public class ChangeLogSetTest { @Test public void assertChangeSetsHaveLogSetParent() throws Exception { List changesets = new ArrayList(); changesets.add(new ChangeSet("version", null, "user", "comment")); ChangeLogSet logset = new ChangeLogSet(null, null, changesets); ChangeSet changeset = logset.iterator().next(); assertNotNull("Log set parent was null change set", changeset.getParent()); } @Test public void assertIsEmptyReturnsFalseWhenNoChangesets() throws Exception { List changesets = new ArrayList(); ChangeLogSet logset = new ChangeLogSet(null, null, changesets); assertTrue("The isEmpty did not return true with an empty log set", logset.isEmptySet()); } @Test public void assertIsEmptyReturnsTrueWithChangesets() throws Exception { List changesets = new ArrayList(); changesets.add(new ChangeSet("version", null, "user", "comment")); ChangeLogSet logset = new ChangeLogSet(null, null, changesets); assertFalse("The isEmpty did not return false with a log set with change sets", logset.isEmptySet()); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/ChangeSetTest.java ================================================ package hudson.plugins.tfs.model; import static org.junit.Assert.*; import java.util.Collection; import java.util.Iterator; import hudson.plugins.tfs.model.ChangeSet.Item; import hudson.scm.EditType; import org.junit.Test; public class ChangeSetTest { @Test public void assertMsgReturnsComment() { ChangeSet changeset = new ChangeSet("0", null, "snd\\user", "comment"); assertSame("The getMsg() did not return the comment", "comment", changeset.getMsg()); } @Test public void assertAffectedPathsReturnsPaths() { ChangeSet changeset = new ChangeSet("0", null, "snd\\user", "comment"); changeset.getItems().add(new Item("filename", "add")); changeset.getItems().add(new Item("filename2", "edit")); Collection paths = changeset.getAffectedPaths(); assertNotNull("Affected paths can not be null", paths); assertEquals("The number of affected paths was incorrect", 2, paths.size()); Iterator iterator = paths.iterator(); assertEquals("The first path is incorrect", "filename", iterator.next()); assertEquals("The first path is incorrect", "filename2", iterator.next()); } @Test public void assertAddedItemReturnsAddEditType() { Item item = new Item("path", "add"); assertSame("Incorrect edit type returned for Add action", EditType.ADD, item.getEditType()); } @Test public void assertDeletedItemReturnsDeleteEditType() { Item item = new Item("path", "delete"); assertSame("Incorrect edit type returned for Delete action", EditType.DELETE, item.getEditType()); } @Test public void assertModifiedItemReturnsEditEditType() { Item item = new Item("path", "edit"); assertSame("Incorrect edit type returned for Edit action", EditType.EDIT, item.getEditType()); } @Test public void assertUserNameIsSetCorrectly() { ChangeSet changeset = new ChangeSet("0", null, "RNO\\_MCLWEB", "comment"); assertEquals("The user name was incorrect", "_MCLWEB", changeset.getUser()); } @Test public void assertDomainNameIsSetCorrectly() { ChangeSet changeset = new ChangeSet("0", null, "RNO\\_MCLWEB", "comment"); assertEquals("The domain name was incorrect", "RNO", changeset.getDomain()); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/GitCodePushedEventArgsTest.java ================================================ package hudson.plugins.tfs.model; import org.junit.Assert; import org.junit.Test; /** * A class to test {@link GitCodePushedEventArgs}. */ public class GitCodePushedEventArgsTest { } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/GitPullRequestMergedEventTest.java ================================================ package hudson.plugins.tfs.model; import net.sf.json.JSONObject; import org.junit.Assert; import org.junit.Test; import java.net.URI; /** * A class to test {@link GitPullRequestMergedEvent}. */ public class GitPullRequestMergedEventTest { } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/GitPushEventTest.java ================================================ package hudson.plugins.tfs.model; import com.fasterxml.jackson.databind.ObjectMapper; import com.microsoft.teamfoundation.core.webapi.model.TeamProjectReference; import com.microsoft.teamfoundation.sourcecontrol.webapi.model.GitRefUpdate; import com.microsoft.visualstudio.services.webapi.model.IdentityRef; import hudson.plugins.tfs.model.servicehooks.Event; import hudson.plugins.tfs.model.servicehooks.ResourceContainer; import hudson.plugins.tfs.util.EndpointHelper; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.junit.Assert; import org.junit.Test; import java.net.URI; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; /** * A class to test {@link GitPushEvent}. */ public class GitPushEventTest { @Test public void determineCollectionUri_sample() throws Exception { final URI input = URI.create("https://fabrikam-fiber-inc.visualstudio.com/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249"); final URI actual = GitPushEvent.determineCollectionUri(input); final URI expected = URI.create("https://fabrikam-fiber-inc.visualstudio.com/"); Assert.assertEquals(expected, actual); } @Test public void perform_noCommitsInPayload() throws Exception { final GitPushEvent gpe = new GitPushEvent(); final ObjectMapper mapper = EndpointHelper.MAPPER; final Event event = new Event(); final ResourceContainer collectionResourceContainer = new ResourceContainer() {{ setId(UUID.fromString("c12d0eb8-e382-443b-9f9c-c52cba5014c2")); }}; final Map resourceContainers = new LinkedHashMap() {{ put("collection", collectionResourceContainer); }}; event.setResourceContainers(resourceContainers); final TeamProjectReference project = new TeamProjectReference() {{ setName("Fabrikam-Fiber-Git"); }}; final Map repository = new LinkedHashMap() {{ put("url", "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249"); put("project", project); put("remoteUrl", "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_git/Fabrikam-Fiber-Git"); }}; final IdentityRef pushedBy = new IdentityRef() {{ setDisplayName("Jamal Hartnett"); }}; final ArrayList refUpdates = new ArrayList(); final GitRefUpdate gitRefUpdate = new GitRefUpdate(); gitRefUpdate.setName("master"); refUpdates.add(gitRefUpdate); final Map resource = new LinkedHashMap() {{ put("commits", null); put("repository", repository); put("pushedBy", pushedBy); put("refUpdates", refUpdates); }}; event.setResource(resource); final JSONObject actual = gpe.perform(mapper, event, null, null); final JSONArray messages = actual.getJSONArray("messages"); Assert.assertEquals(1, messages.size()); final String message = messages.getString(0).trim(); Assert.assertEquals("No commits were pushed, skipping further event processing.", message); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/GitStatusContextTest.java ================================================ package hudson.plugins.tfs.model; import org.junit.Assert; import org.junit.Test; /** * A class to test {@link GitStatusContext}. */ public class GitStatusContextTest { public static final String FORMATTED_INPUT = "{\n" + " \"name\":\"Build123\",\n" + " \"genre\":\"continuous-integration\"\n" + "}"; @Test public void fromJsonString_typical() throws Exception { final GitStatusContext actual = GitStatusContext.fromJsonString(FORMATTED_INPUT); Assert.assertEquals("Build123", actual.name); Assert.assertEquals("continuous-integration", actual.genre); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/NativeLibraryManagerTest.java ================================================ package hudson.plugins.tfs.model; import com.microsoft.tfs.core.persistence.PersistenceStore; import org.junit.Assert; import org.junit.Test; import org.mockito.Matchers; import java.io.ByteArrayOutputStream; import java.io.IOException; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class NativeLibraryManagerTest { @Test public void buildPathToNativeFile_threeComponents() throws Exception { final String actual = NativeLibraryManager.buildPathToNativeFile("win32", "x86", "native_auth.dll"); Assert.assertEquals("native/win32/x86/native_auth.dll", actual); } @Test public void buildPathToNativeFile_twoComponents() throws Exception { final String actual = NativeLibraryManager.buildPathToNativeFile("macosx", null, "libnative_auth.jnilib"); Assert.assertEquals("native/macosx/libnative_auth.jnilib", actual); } @Test public void extractFile() throws IOException { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final PersistenceStore store = mock(PersistenceStore.class); when(store.containsItem(isA(String.class))).thenReturn(false); when(store.getItemOutputStream(isA(String.class))).thenReturn(baos); final NativeLibraryManager manager = new NativeLibraryManager(store); manager.extractFile("win32", "x86", "native_auth.dll"); Assert.assertEquals(67240, baos.size()); } @Test public void extractFile_noArch() throws IOException { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final PersistenceStore store = mock(PersistenceStore.class); when(store.containsItem(isA(String.class))).thenReturn(false); when(store.getItemOutputStream(isA(String.class))).thenReturn(baos); final NativeLibraryManager manager = new NativeLibraryManager(store); manager.extractFile("macosx", null, "libnative_auth.jnilib"); Assert.assertEquals(118960, baos.size()); } @Test public void extractFiles() throws Exception { final NativeLibraryExtractor extractor = mock(NativeLibraryExtractor.class); NativeLibraryManager.extractFiles(extractor); verify(extractor, times(82)).extractFile(isA(String.class), Matchers.anyObject(), isA(String.class)); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/ProjectTest.java ================================================ package hudson.plugins.tfs.model; import static org.junit.Assert.*; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.net.URISyntaxException; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.List; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.ChangesetVersionSpec; import hudson.model.User; import hudson.plugins.tfs.IntegrationTestHelper; import hudson.plugins.tfs.IntegrationTests; import hudson.plugins.tfs.SwedishLocaleTestCase; import hudson.plugins.tfs.Util; import hudson.plugins.tfs.commands.RemoteChangesetVersionCommand; import hudson.plugins.tfs.model.ChangeSet.Item; import hudson.plugins.tfs.util.MaskedArgumentListBuilder; import hudson.tasks.Mailer; import org.junit.Assert; import org.junit.Test; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Change; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.ChangeType; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Changeset; import com.microsoft.tfs.core.clients.versioncontrol.soapextensions.ItemType; import org.junit.experimental.categories.Category; public class ProjectTest extends SwedishLocaleTestCase { private com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Change createServerChange() { final com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Item serverItem = new com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Item(); serverItem.setItemType(ItemType.FILE); serverItem.setServerItem("$/tfsandbox"); final com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Change serverChange = new Change(serverItem, ChangeType.ADD, null); return serverChange; } @Test public void assertConvertServerChange() throws Exception { final com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Change serverChange = createServerChange(); final Item actual = Project.convertServerChange(serverChange); assertEquals("$/tfsandbox", actual.getPath()); assertEquals("add", actual.getAction()); } private UserLookup createMockUserLookup(String accountName, String displayName, String emailAddress) { UserLookup userLookup = mock(UserLookup.class); User user = mock(User.class); // this portion stolen from User.get() final String id = accountName.replace('\\', '_').replace('/', '_').replace('<','_') .replace('>','_'); // 4 replace() still faster than regex // end stolen portion when(user.getId()).thenReturn(id); when(user.getDisplayName()).thenReturn(displayName); when(user.getProperty(Mailer.UserProperty.class)).thenReturn(new Mailer.UserProperty(emailAddress)); when(userLookup.find(accountName)).thenReturn(user); return userLookup; } @Test public void assertConvertServerChangeset() throws Exception { final com.microsoft.tfs.core.clients.versioncontrol.soapextensions.Change serverChange = createServerChange(); final String comment = "Created team project folder $/tfsandbox via the Team Project Creation Wizard"; final Calendar juneTwentySeventh = Util.getCalendar(2008, 06, 27, 11, 16, 06); final String userString = "EXAMPLE\\ljenkins"; Changeset serverChangeset = new Changeset(userString, comment, null, null); serverChangeset.setChangesetID(12472); serverChangeset.setCommitter(userString); serverChangeset.setDate(juneTwentySeventh); final Change[] changes = new Change[] { serverChange }; serverChangeset.setChanges(changes); final String userDisplayName = "Leeroy Jenkins"; final String userEmailAddress = "leeroy.jenkins@example.com"; final UserLookup userLookup = createMockUserLookup(userString, userDisplayName, userEmailAddress); hudson.plugins.tfs.model.ChangeSet actual = Project.convertServerChangeset(serverChangeset, userLookup); final User author = actual.getAuthor(); assertEquals("The version was incorrect", "12472", actual.getVersion()); assertEquals("The author's user ID was incorrect", "EXAMPLE_ljenkins", author.getId()); assertEquals("The author's display name was incorrect", userDisplayName, author.getDisplayName()); final String actualEmailAddress = author.getProperty(Mailer.UserProperty.class).getAddress(); assertEquals("The author's e-mail address was incorrect", userEmailAddress, actualEmailAddress); assertEquals("The date was incorrect", juneTwentySeventh.getTime(), actual.getDate()); assertEquals("The comment was incorrect", comment, actual.getComment()); Item item = actual.getItems().get(0); assertEquals("The item path was incorrect", "$/tfsandbox", item.getPath()); assertEquals("The item action was incorrect", "add", item.getAction()); } @Test public void findLatestUncloakedChangeset_latestIsUncloaked() { final List cloakedPaths = Arrays.asList("$/MyProject/A/2", "$/MyProject/B"); final ChangeSet changeSet42 = createChangeSet(42, "$/MyProject/A/foo", "$/MyProject/A/bar"); final ChangeSet changeSet43 = createChangeSet(43, "$/MyProject/A/bar"); final ChangeSet changeSet44 = createChangeSet(44, "$/MyProject/A/foo"); final List changeSets = Arrays.asList(changeSet44, changeSet43, changeSet42); final ChangeSet actual = Project.findLatestUncloakedChangeset(cloakedPaths, changeSets); Assert.assertEquals("44", actual.getVersion()); } @Test public void findLatestUncloakedChangeset_latestIsCloaked() { final List cloakedPaths = Arrays.asList("$/MyProject/A/2", "$/MyProject/B"); final ChangeSet changeSet42 = createChangeSet(42, "$/MyProject/A/foo", "$/MyProject/A/bar"); final ChangeSet changeSet43 = createChangeSet(43, "$/MyProject/A/bar"); final ChangeSet changeSet44 = createChangeSet(44, "$/MyProject/A/2/foo"); final List changeSets = Arrays.asList(changeSet44, changeSet43, changeSet42); final ChangeSet actual = Project.findLatestUncloakedChangeset(cloakedPaths, changeSets); Assert.assertEquals("43", actual.getVersion()); } @Test public void findLatestUncloakedChangeset_everythingIsCloaked() { final List cloakedPaths = Arrays.asList("$/MyProject/A/2", "$/MyProject/B"); final ChangeSet changeSet42 = createChangeSet(42, "$/MyProject/A/2/foo", "$/MyProject/B/bar"); final ChangeSet changeSet43 = createChangeSet(43, "$/MyProject/B/bar"); final ChangeSet changeSet44 = createChangeSet(44, "$/MyProject/A/2/foo"); final List changeSets = Arrays.asList(changeSet44, changeSet43, changeSet42); final ChangeSet actual = Project.findLatestUncloakedChangeset(cloakedPaths, changeSets); Assert.assertEquals(null, actual); } private static ChangeSet createChangeSet(final int version, final String... itemPaths) { final String stringVersion = Integer.toString(version); final Calendar calendar = Util.getCalendar(2016, 1, 5, 10, version, 0); final Date date = calendar.getTime(); final ChangeSet result = new ChangeSet(stringVersion, date, "ljenkins", "synthetic for testing"); for (final String itemPath : itemPaths) { final Item item = new Item(itemPath, "edit"); result.add(item); } return result; } @Test public void isChangesetFullyCloaked_nullCloakedPaths() { final List changesetPaths = Arrays.asList("$/foo", "$/foo/bar.baz"); final boolean actual = Project.isChangesetFullyCloaked(changesetPaths, null); Assert.assertEquals(false, actual); } @Test public void isChangesetFullyCloaked_independentCloakedPaths() { final List changesetPaths = Arrays.asList("$/foo", "$/foo/bar.baz"); final List cloakedPaths = Arrays.asList("$/fizz", "$/fizz/FizzBuzz.java"); final boolean actual = Project.isChangesetFullyCloaked(changesetPaths, cloakedPaths); Assert.assertEquals(false, actual); } @Test public void isChangesetFullyCloaked_caseInsensitiveCloakedPaths() { final List changesetPaths = Arrays.asList("$/foo/bar/test.baz"); final List cloakedPaths = Arrays.asList("$/fOo/bAr/"); final boolean actual = Project.isChangesetFullyCloaked(changesetPaths, cloakedPaths); Assert.assertEquals(true, actual); } @Test public void isChangesetFullyCloaked_cloakingChild() { final List changesetPaths = Arrays.asList("$/foo", "$/foo/bar.baz"); final List cloakedPaths = Collections.singletonList("$/foo/bar.baz"); final boolean actual = Project.isChangesetFullyCloaked(changesetPaths, cloakedPaths); Assert.assertEquals(false, actual); } @Test public void isChangesetFullyCloaked_partiallyCloakedPaths() { final List changesetPaths = Arrays.asList("$/foo", "$/bar"); final List cloakedPaths = Collections.singletonList("$/foo"); final boolean actual = Project.isChangesetFullyCloaked(changesetPaths, cloakedPaths); Assert.assertEquals(false, actual); } @Test public void isChangesetFullyCloaked_fullyCloakedPath() { final List changesetPaths = Arrays.asList("$/foo", "$/foo/bar.baz"); final List cloakedPaths = Collections.singletonList("$/foo"); final boolean actual = Project.isChangesetFullyCloaked(changesetPaths, cloakedPaths); Assert.assertEquals(true, actual); } @Test public void isChangesetFullyCloaked_fullyCloakedPaths() { final List changesetPaths = Collections.singletonList("$/foo/bar.baz"); final List cloakedPaths = Arrays.asList("$/foo", "$/bar"); final boolean actual = Project.isChangesetFullyCloaked(changesetPaths, cloakedPaths); Assert.assertEquals(true, actual); } @Test public void isChangesetFullyCloaked_manyToMany() { final List changesetPaths = Arrays.asList("$/foo/bar.baz", "$/bar/foo.baz"); final List cloakedPaths = Arrays.asList("$/foo", "$/bar"); final boolean actual = Project.isChangesetFullyCloaked(changesetPaths, cloakedPaths); Assert.assertEquals(true, actual); } @Category(IntegrationTests.class) @Test public void getDetailedHistory_singleVersionSpec() throws URISyntaxException, IOException { final IntegrationTestHelper helper = new IntegrationTestHelper(); final String serverUrl = helper.getServerUrl(); final String userName = helper.getUserName(); final String userPassword = helper.getUserPassword(); final Server server = new Server(null, null, serverUrl, userName, userPassword, null, ExtraSettings.DEFAULT); try { final Project project = new Project(server, "$/FunctionalTests"); final UserLookup userLookup = mock(UserLookup.class); final User fakeUser = mock(User.class); when(userLookup.find(isA(String.class))).thenReturn(fakeUser); project.setUserLookup(userLookup); final MockableVersionControlClient vcc = server.getVersionControlClient(); final int latestChangesetID = vcc.getLatestChangesetID(); final ChangesetVersionSpec spec = new ChangesetVersionSpec(latestChangesetID); final String singleVersionSpecString = RemoteChangesetVersionCommand.toString(spec); final List actual = project.getDetailedHistory(singleVersionSpecString); Assert.assertEquals(1, actual.size()); final ChangeSet changeSet = actual.get(0); final String latestChangesetString = Integer.toString(latestChangesetID, 10); Assert.assertEquals(latestChangesetString, changeSet.getVersion()); } finally { server.close(); } } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/PullRequestMergeCommitCreatedEventArgsTest.java ================================================ package hudson.plugins.tfs.model; import org.junit.Assert; import org.junit.Test; /** * A class to test {@link PullRequestMergeCommitCreatedEventArgs}. */ public class PullRequestMergeCommitCreatedEventArgsTest { } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/ServerIntegrationTest.java ================================================ package hudson.plugins.tfs.model; import org.jvnet.hudson.test.JenkinsRule; import org.junit.Rule; import org.junit.Test; import com.microsoft.tfs.core.TFSTeamProjectCollection; import com.microsoft.tfs.core.exceptions.TECoreException; import java.io.IOException; public class ServerIntegrationTest { @Rule public JenkinsRule j = new JenkinsRule(); @Test(expected = TECoreException.class) /** * It's OK for this test to throw a TECoreException for "unknown host"; * it means we were able to load the native libraries, * otherwise an @link UnsatisfiedLinkError would have been thrown earlier. */ public void canFindTfsSdkNativeLibraries() throws IOException { final Server server = new Server(null, null, "http://tfs.invalid:8080/tfs", "username", "password"); try { server.getVersionControlClient(); } finally { server.close(); } } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/ServerTest.java ================================================ package hudson.plugins.tfs.model; import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; import org.mockito.MockitoAnnotations; import java.io.IOException; public class ServerTest { @Before public void setup() { MockitoAnnotations.initMocks(this); } static Server createServer() throws IOException { return new Server(null, null, "url", null, null, null, ExtraSettings.DEFAULT); } @Test public void assertGetWorkspacesReturnSameObject() throws IOException { Server server = createServer(); assertNotNull("Workspaces object can not be null", server.getWorkspaces()); assertSame("getWorkspaces() returned different objects", server.getWorkspaces(), server.getWorkspaces()); } @Test public void assertGetProjectWithSameProjectPathReturnsSameInstance() throws IOException { Server server = createServer(); assertNotNull("Project object can not be null", server.getProject("$/projectPath")); assertSame("getProject() returned different objects", server.getProject("$/projectPath"), server.getProject("$/projectPath")); } @Test public void assertGetProjectWithDifferentProjectPathReturnsNotSameInstance() throws IOException { Server server = createServer(); assertNotSame("getProject() did not return different objects", server.getProject("$/projectPath"), server.getProject("$/otherPath")); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/TeamGitStatusTest.java ================================================ package hudson.plugins.tfs.model; import hudson.plugins.tfs.util.TeamRestClient; import org.junit.Assert; import org.junit.Test; /** * A class to test {@link TeamGitStatus}. */ public class TeamGitStatusTest { @Test public void toJson_typical() { final TeamGitStatus cut = new TeamGitStatus(); cut.state = GitStatusState.Pending; cut.description = "The build is in progress"; cut.targetUrl = "https://ci.fabrikam.com/my-project/build/124"; cut.context = new GitStatusContext("Build124", "continuous-integration"); final String actual = cut.toJson(); final String expected = "{" + "\"state\":\"Pending\"," + "\"description\":\"The build is in progress\"," + "\"targetUrl\":\"https://ci.fabrikam.com/my-project/build/124\"," + "\"context\":" + "{" + "\"name\":\"Build124\"," + "\"genre\":\"continuous-integration\"" + "}" + "}"; Assert.assertEquals(expected, actual); } @Test public void fromJsonString_server() throws Exception { final String input = "{" + "\"state\":\"succeeded\"," + "\"description\":\"SUCCESS\"," + "\"targetUrl\":\"https://ci.fabrikam.com/my-project/build/124\"," + "\"context\":" + "{" + "\"name\":\"Build124\"," + "\"genre\":\"continuous-integration\"" + "}" + "}"; final TeamGitStatus actual = TeamRestClient.deserialize(TeamGitStatus.class, input); Assert.assertEquals(GitStatusState.Succeeded, actual.state); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/WorkspaceConfigurationTest.java ================================================ package hudson.plugins.tfs.model; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import java.util.Collections; import java.util.List; import org.junit.Test; public class WorkspaceConfigurationTest { private static final List EMPTY_CLOAKED_PATHS_LIST = Collections.emptyList(); @Test public void assertConfigurationsEquals() { final List cloakList = Collections.singletonList("cloak"); WorkspaceConfiguration one = new WorkspaceConfiguration("server", "workspace", "project", cloakList, "workfolder"); WorkspaceConfiguration two = new WorkspaceConfiguration("server", "workspace", "project", cloakList, "workfolder"); assertThat(one, is(two)); assertThat(two, is(one)); assertThat(one, is(one)); assertThat(one, not(new WorkspaceConfiguration("aserver", "workspace", "project", cloakList, "workfolder"))); assertThat(one, not(new WorkspaceConfiguration("server", "aworkspace", "project", cloakList, "workfolder"))); assertThat(one, not(new WorkspaceConfiguration("server", "workspace", "aproject", cloakList, "workfolder"))); assertThat(one, not(new WorkspaceConfiguration("server", "workspace", "project", cloakList, "aworkfolder"))); assertThat(one, not(new WorkspaceConfiguration("server", "workspace", "project", EMPTY_CLOAKED_PATHS_LIST, "workfolder"))); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/WorkspacesTest.java ================================================ package hudson.plugins.tfs.model; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.util.Collections; import java.util.List; import hudson.plugins.tfs.commands.ListWorkspacesCommand; import static org.junit.Assert.*; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import hudson.remoting.Callable; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; public class WorkspacesTest { private static final List EMPTY_CLOAKED_PATHS_LIST = Collections.emptyList(); @Mock private Server server; private ListWorkspacesCommand parser; @Before public void setup() { MockitoAnnotations.initMocks(this); parser = new ListWorkspacesCommand(server); } private List parse(final String s) throws IOException { final Reader reader = new StringReader(s); return parser.parse(reader); } @Test public void assertListFromServerIsParsedProperly() throws Exception { when(server.execute(isA(Callable.class))).thenReturn(parse( "--------- -------------- -------- ----------------------------------------------------------------------------------------------------------\n" + "\n" + "name1 SND\\redsolo_cp COMPUTER\n")); Workspaces workspaces = new Workspaces(server); Workspace workspace = workspaces.getWorkspace("name1"); assertNotNull("Workspace was null", workspace); } @Test public void assertListFromServerIsRetrievedOnce() throws Exception { when(server.execute(isA(Callable.class))).thenReturn(parse( "--------- -------------- -------- ----------------------------------------------------------------------------------------------------------\n" + "\n" + "name1 SND\\redsolo_cp COMPUTER\n")); Workspaces workspaces = new Workspaces(server); Workspace workspace = workspaces.getWorkspace("name1"); assertNotNull("Workspace was null", workspace); workspace = workspaces.getWorkspace("name1"); assertNotNull("Workspace was null", workspace); verify(server, times(1)).execute(isA(Callable.class)); } @Test public void assertExistsWorkspace() throws Exception { when(server.execute(isA(Callable.class))).thenReturn(parse( "--------- -------------- -------- ----------------------------------------------------------------------------------------------------------\n" + "\n" + "name1 SND\\redsolo_cp COMPUTER\n")); Workspaces workspaces = new Workspaces(server); assertTrue("The workspace was reported as non existant", workspaces.exists(new Workspace("name1"))); } @Test public void assertWorkspaceExistsWithOnlyName() throws Exception { when(server.execute(isA(Callable.class))).thenReturn(parse( "--------- -------------- -------- ----------------------------------------------------------------------------------------------------------\n" + "\n" + "name1 SND\\redsolo_cp COMPUTER\n")); Workspaces workspaces = new Workspaces(server); assertTrue("The workspace was reported as non existant", workspaces.exists("name1")); } @Test public void assertNewWorkspaceIsAddedToMap() throws Exception { when(server.execute(isA(Callable.class))).thenReturn(null); Workspaces workspaces = new Workspaces(server); Workspace workspace = workspaces.newWorkspace("name1", null, EMPTY_CLOAKED_PATHS_LIST, null); assertNotNull("The new workspace was null", workspace); assertTrue("The workspace was reported as non existant", workspaces.exists(workspace)); } @Test public void assertGettingNewWorkspaceIsNotRetrievingServerList() throws Exception { when(server.execute(isA(Callable.class))).thenReturn(null); Workspaces workspaces = new Workspaces(server); workspaces.newWorkspace("name1", null, EMPTY_CLOAKED_PATHS_LIST, null); assertNotNull("The get new workspace returned null", workspaces.getWorkspace("name1")); verify(server, times(1)).execute(isA(Callable.class)); } @Test public void assertNewWorkspaceExistsIsNotRetrievingServerList() throws Exception { when(server.execute(isA(Callable.class))).thenReturn(null); Workspaces workspaces = new Workspaces(server); Workspace workspace = workspaces.newWorkspace("name1", null, EMPTY_CLOAKED_PATHS_LIST, null); assertTrue("The get new workspace did not exists", workspaces.exists(workspace)); verify(server, times(1)).execute(isA(Callable.class)); } @Test public void assertWorkspaceIsDeletedFromMap() throws Exception { when(server.execute(isA(Callable.class))).thenReturn(parse("")); Workspaces workspaces = new Workspaces(server); // Populate the map in test object assertFalse("The workspace was reported as existant", workspaces.exists(new Workspace("name"))); Workspace workspace = workspaces.newWorkspace("name", null, EMPTY_CLOAKED_PATHS_LIST, null); assertTrue("The workspace was reported as non existant", workspaces.exists(new Workspace("name"))); workspaces.deleteWorkspace(workspace); assertFalse("The workspace was reported as existant", workspaces.exists(workspace)); } @Test public void assertGetUnknownWorkspaceReturnsNull() throws Exception { when(server.execute(isA(Callable.class))).thenReturn(parse("")); Workspaces workspaces = new Workspaces(server); assertNull("The unknown workspace was not null", workspaces.getWorkspace("name1")); } @Test public void assertUnknownWorkspaceDoesNotExists() throws Exception { when(server.execute(isA(Callable.class))).thenReturn(parse("")); Workspaces workspaces = new Workspaces(server); assertFalse("The unknown workspace was reported as existing", workspaces.exists(new Workspace("name1"))); } @Test public void assertWorkspaceFactory() { ListWorkspacesCommand.WorkspaceFactory factory = new Workspaces(server); Workspace workspace = factory.createWorkspace("name", "computer", "owner", "comment"); assertEquals("Workspace name was incorrect", "name", workspace.getName()); assertEquals("Workspace comment was incorrect", "comment", workspace.getComment()); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/model/servicehooks/EventScopeTest.java ================================================ package hudson.plugins.tfs.model.servicehooks; import hudson.plugins.tfs.util.EndpointHelper; import org.junit.Assert; import org.junit.Test; /** * A class to test {@link EventScope}. */ public class EventScopeTest { @Test public void deserialize_enumCasing() throws Exception { final String input = "{\"scope\": \"all\"}"; final Event actual = EndpointHelper.MAPPER.readValue(input, Event.class); Assert.assertEquals(EventScope.All, actual.getScope()); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/util/BuildVariableResolverTest.java ================================================ package hudson.plugins.tfs.util; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import java.io.IOException; import java.util.HashMap; import java.util.Map; import hudson.EnvVars; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.Computer; import hudson.model.TaskListener; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SuppressWarnings("unchecked") public class BuildVariableResolverTest { @Mock private AbstractProject project; @Mock private Computer computer; @Mock private AbstractBuild build; @Before public void before() throws Exception { MockitoAnnotations.initMocks(this); } @Test public void assertConstructorBuildUsesProject() throws IOException, InterruptedException { when(build.getProject()).thenReturn(project); new BuildVariableResolver(build, computer); verify(build).getProject(); verifyZeroInteractions(project); verifyZeroInteractions(computer); } @Test public void assertJobNameIsResolved() { when(project.getName()).thenReturn("ThisIsAJob"); BuildVariableResolver resolver = new BuildVariableResolver(project, computer); assertEquals("Variable resolution was incorrect", "ThisIsAJob", resolver.resolve("JOB_NAME")); verifyZeroInteractions(computer); } @Test public void assertJobNameWithoutComputerIsResolved() { when(project.getName()).thenReturn("ThisIsAJob"); BuildVariableResolver resolver = new BuildVariableResolver(project); assertEquals("Variable resolution was incorrect", "ThisIsAJob", resolver.resolve("JOB_NAME")); assertNull("Variable resolution was performed", resolver.resolve("NONE_EXISTING_KEY")); } @Test public void assertComputerEnvVarIsResolved() throws Exception { EnvVars map = new EnvVars(); map.put("ENV_VAR", "This is an env var"); when(computer.getEnvironment()).thenReturn(map); BuildVariableResolver resolver = new BuildVariableResolver(project, computer); assertEquals("Variable resolution was incorrect", "This is an env var", resolver.resolve("ENV_VAR")); verifyZeroInteractions(project); } @Test public void assertComputerUserNameIsResolved() throws Exception { Map map = new HashMap(); map.put("user.name", "Other_user"); when(computer.getSystemProperties()).thenReturn(map); BuildVariableResolver resolver = new BuildVariableResolver(project, computer); assertEquals("Variable resolution was incorrect", "Other_user", resolver.resolve("USER_NAME")); verifyZeroInteractions(project); } @Test public void assertNodeNameIsResolved() { when(computer.getName()).thenReturn("AKIRA"); BuildVariableResolver resolver = new BuildVariableResolver(project, computer); assertEquals("Variable resolution was incorrect", "AKIRA", resolver.resolve("NODE_NAME")); verifyZeroInteractions(project); } /** * Asserts that NODE_NAME works on the master computer, as the MasterComputer.getName() returns null. */ @Test public void assertMasterNodeNameIsResolved() { when(computer.getName()).thenReturn(""); BuildVariableResolver resolver = new BuildVariableResolver(project, computer); assertEquals("Variable resolution was incorrect", "MASTER", resolver.resolve("NODE_NAME")); verifyZeroInteractions(project); } @Test public void assertNoComputeraDoesNotThrowNPEWhenResolvingNodeName() { BuildVariableResolver resolver = new BuildVariableResolver(project); assertNull("Variable resolution was incorrect", resolver.resolve("NODE_NAME")); verifyZeroInteractions(project); } @Test public void assertBuildEnvVarIsResolved() throws Exception { EnvVars map = new EnvVars(); map.put("BUILD_ID", "121212"); when(build.getProject()).thenReturn(project); when(build.getEnvironment(TaskListener.NULL)).thenReturn(map); BuildVariableResolver resolver = new BuildVariableResolver(build, computer); assertEquals("Variable resolution was incorrect", "121212", resolver.resolve("BUILD_ID")); verify(build).getEnvironment(TaskListener.NULL); verifyZeroInteractions(project); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/util/BuildWorkspaceConfigurationRetrieverTest.java ================================================ package hudson.plugins.tfs.util; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import java.io.IOException; import java.util.Collections; import java.util.List; import hudson.model.AbstractBuild; import hudson.model.Node; import hudson.model.Run; import hudson.plugins.tfs.model.WorkspaceConfiguration; import hudson.plugins.tfs.util.BuildWorkspaceConfigurationRetriever.BuildWorkspaceConfiguration; import org.junit.Test; import org.jvnet.hudson.test.Bug; @SuppressWarnings("unchecked") public class BuildWorkspaceConfigurationRetrieverTest { private static final List EMPTY_CLOAKED_PATHS_LIST = Collections.emptyList(); @Test public void assertGetLatestConfgiurationOnNode() { AbstractBuild build = mock(AbstractBuild.class); Node node = mock(Node.class); Node needleNode = mock(Node.class); WorkspaceConfiguration configuration = new WorkspaceConfiguration("serverUrl", "workspaceName", "projectPath", EMPTY_CLOAKED_PATHS_LIST, "workfolder"); when(build.getPreviousBuild()).thenReturn(build).thenReturn(null); when(build.getBuiltOn()).thenReturn(node, node, null); when(node.getNodeName()).thenReturn("node1", "needleNode"); when(needleNode.getNodeName()).thenReturn("needleNode", "needleNode"); when(build.getAction(WorkspaceConfiguration.class)).thenReturn(configuration); assertThat( new BuildWorkspaceConfigurationRetriever().getLatestForNode(needleNode, build), equalTo(configuration)); verify(build, times(2)).getBuiltOn(); verify(node, times(2)).getNodeName(); verify(build).getPreviousBuild(); verify(build).getAction(WorkspaceConfiguration.class); verifyNoMoreInteractions(node); verifyNoMoreInteractions(build); } @Test public void assertGetLatestConfgiurationOnNodeWithNoPrevioudBuild() { AbstractBuild build = mock(AbstractBuild.class); Node node = mock(Node.class); Node needleNode = mock(Node.class); when(build.getPreviousBuild()).thenReturn(null); when(build.getBuiltOn()).thenReturn(node); when(node.getNodeName()).thenReturn("node1"); when(needleNode.getNodeName()).thenReturn("needleNode"); BuildWorkspaceConfigurationRetriever retriever = new BuildWorkspaceConfigurationRetriever(); assertThat( retriever.getLatestForNode(needleNode, build), nullValue()); verify(build).getBuiltOn(); verify(node).getNodeName(); verify(build).getPreviousBuild(); verifyNoMoreInteractions(node); verifyNoMoreInteractions(build); } @Test public void assertGetLatestConfigurationOnNodeWithNoPrevioudScmConfiguration() { AbstractBuild build = mock(AbstractBuild.class); Node node = mock(Node.class); Node needleNode = mock(Node.class); when(build.getBuiltOn()).thenReturn(node); when(node.getNodeName()).thenReturn("needleNode"); when(needleNode.getNodeName()).thenReturn("needleNode"); assertThat( new BuildWorkspaceConfigurationRetriever().getLatestForNode(needleNode, build), nullValue()); verify(build).getBuiltOn(); verify(node).getNodeName(); verify(build).getAction(WorkspaceConfiguration.class); verifyNoMoreInteractions(node); verifyNoMoreInteractions(build); } @Test public void assertGetLatestConfgiurationOnNodeWithNoAbstractBuild() { Run run = mock(Run.class); Node node = mock(Node.class); assertThat( new BuildWorkspaceConfigurationRetriever().getLatestForNode(node, run), nullValue()); verifyNoMoreInteractions(run); verifyNoMoreInteractions(node); } @Test public void assertSaveWorkspaceConfigurationUsesSaveOnBuild() throws IOException { AbstractBuild build = mock(AbstractBuild.class); Node node = mock(Node.class); when(build.getPreviousBuild()).thenReturn(build); when(build.getBuiltOn()).thenReturn(node); when(node.getNodeName()).thenReturn("needleNode"); when(build.getAction(WorkspaceConfiguration.class)).thenReturn(new WorkspaceConfiguration("serverUrl", "workspaceName", "projectPath", EMPTY_CLOAKED_PATHS_LIST, "workfolder")); BuildWorkspaceConfiguration configuration = new BuildWorkspaceConfigurationRetriever().getLatestForNode(node, build); assertThat( configuration.getWorkspaceName(), is("workspaceName")); configuration.save(); verify(build).save(); } @Bug(8322) @Test public void assertGetLatestConfgiurationOnPreviousDeletedNode() { AbstractBuild build = mock(AbstractBuild.class); Node node = mock(Node.class); Node needleNode = mock(Node.class); WorkspaceConfiguration configuration = new WorkspaceConfiguration("serverUrl", "workspaceName", "projectPath", EMPTY_CLOAKED_PATHS_LIST, "workfolder"); when(build.getPreviousBuild()).thenReturn(build).thenReturn(null); when(build.getBuiltOn()).thenReturn(null); assertThat( new BuildWorkspaceConfigurationRetriever().getLatestForNode(needleNode, build), nullValue()); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/util/DateUtilTest.java ================================================ package hudson.plugins.tfs.util; import com.microsoft.tfs.core.clients.versioncontrol.specs.version.DateVersionSpec; import hudson.plugins.tfs.Util; import org.junit.Assert; import org.junit.Test; import java.util.Calendar; import java.util.Date; public class DateUtilTest { private static final Calendar fixedPointInTime = Util.getCalendar(2013, 07, 02, 15, 40, 50); private static final DateVersionSpec dateVersionSpec = new DateVersionSpec(fixedPointInTime); @Test public void toString_date() throws Exception { final String actual = DateUtil.toString(new Date(1372779650000L)); Assert.assertEquals("D2013-07-02T15:40:50Z", actual); } @Test public void toString_calendar() throws Exception { final String actual = DateUtil.toString(fixedPointInTime); Assert.assertEquals("D2013-07-02T15:40:50Z", actual); } @Test public void toString_dateVersionSpec() throws Exception { final String actual = DateUtil.toString(dateVersionSpec); Assert.assertEquals("D2013-07-02T15:40:50Z", actual); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/util/KeyValueTextReaderTest.java ================================================ package hudson.plugins.tfs.util; import static org.junit.Assert.*; import static org.hamcrest.CoreMatchers.*; import java.util.Map; import org.junit.Test; public class KeyValueTextReaderTest { @Test public void assertKeysAreRead() throws Exception { Map map = new KeyValueTextReader().parse("Key:Data\nOtherKey: More data"); assertThat(map.get("Key"), is("Data")); assertThat(map.get("OtherKey"), is("More data")); } @Test public void assertValueIsTrimmed() throws Exception { Map map = new KeyValueTextReader().parse("Changeset: 12492"); assertThat(map.get("Changeset"), is("12492")); } @Test public void assertKeyContainsSpace() throws Exception { Map map = new KeyValueTextReader().parse("Change set: 12492"); assertThat(map.get("Change set"), is("12492")); } @Test public void assertMultilineDataIsRead() throws Exception { Map map = new KeyValueTextReader().parse("Key:Data\n Some more information"); assertThat(map.get("Key"), is("Data\nSome more information")); } @Test public void asserOnlyKeyIsReadIfValueContainsColon() throws Exception { Map map = new KeyValueTextReader().parse("Key:Data 23:23:12"); assertThat(map.get("Key"), is("Data 23:23:12")); } @Test public void assertValueBeginingOnNextRowIsParsedWithoutPrefixedEndline() throws Exception { Map map = new KeyValueTextReader().parse("Comment:\n Reviewer: \n Approver: \n"); assertThat(map.get("Comment"), is("Reviewer:\nApprover:")); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/util/MaskedArgumentListBuilderTest.java ================================================ package hudson.plugins.tfs.util; import static org.junit.Assert.*; import hudson.plugins.tfs.Util; import org.junit.Test; public class MaskedArgumentListBuilderTest { @Test public void assertEmptyMask() { MaskedArgumentListBuilder builder = new MaskedArgumentListBuilder(); builder.add("arg"); builder.add("other", "arguments"); assertFalse("There shouldnt be any masked arguments", builder.hasMaskedArguments()); boolean[] array = builder.toMaskArray(); assertNotNull("The mask array should not be null", array); assertArrayEquals("The mask array was incorrect", new Boolean[]{false,false,false}, Util.toBoxedArray(array)); } @Test public void assertLastArgumentIsMasked() { MaskedArgumentListBuilder builder = new MaskedArgumentListBuilder(); builder.add("arg"); builder.addMasked("ismasked"); assertTrue("There should be masked arguments", builder.hasMaskedArguments()); boolean[] array = builder.toMaskArray(); assertNotNull("The mask array should not be null", array); assertArrayEquals("The mask array was incorrect", new Boolean[]{false,true}, Util.toBoxedArray(array)); } @Test public void assertSeveralMaskedArguments() { MaskedArgumentListBuilder builder = new MaskedArgumentListBuilder(); builder.add("arg"); builder.addMasked("ismasked"); builder.add("non masked arg"); builder.addMasked("ismasked2"); assertTrue("There should be masked arguments", builder.hasMaskedArguments()); boolean[] array = builder.toMaskArray(); assertNotNull("The mask array should not be null", array); assertArrayEquals("The mask array was incorrect", new Boolean[]{false,true, false, true}, Util.toBoxedArray(array)); } @Test public void assertPrependAfterAddingMasked() { MaskedArgumentListBuilder builder = new MaskedArgumentListBuilder(); builder.addMasked("ismasked"); builder.add("arg"); builder.prepend("first", "second"); assertTrue("There should be masked arguments", builder.hasMaskedArguments()); boolean[] array = builder.toMaskArray(); assertNotNull("The mask array should not be null", array); assertArrayEquals("The mask array was incorrect", new Boolean[]{false,false,true,false}, Util.toBoxedArray(array)); } @Test public void assertPrependBeforeAddingMasked() { MaskedArgumentListBuilder builder = new MaskedArgumentListBuilder(); builder.prepend("first", "second"); builder.addMasked("ismasked"); builder.add("arg"); assertTrue("There should be masked arguments", builder.hasMaskedArguments()); boolean[] array = builder.toMaskArray(); assertNotNull("The mask array should not be null", array); assertArrayEquals("The mask array was incorrect", new Boolean[]{false,false,true,false}, Util.toBoxedArray(array)); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/util/QueryStringTest.java ================================================ package hudson.plugins.tfs.util; import org.junit.Assert; import org.junit.Test; /** * A class to test {@link QueryString}. */ public class QueryStringTest { @Test public void toString_typical() throws Exception { final QueryString cut = new QueryString(); cut.put("answer", "42"); final String actual = cut.toString(); Assert.assertEquals("answer=42", actual); } @Test public void constructor_typical() throws Exception { //noinspection MismatchedQueryAndUpdateOfCollection final QueryString cut = new QueryString("answer", "42"); final String actual = cut.toString(); Assert.assertEquals("answer=42", actual); } @Test public void constructor_twoPairs() throws Exception { //noinspection MismatchedQueryAndUpdateOfCollection final QueryString cut = new QueryString("answer", "42", "question", "whatdoyoug"); final String actual = cut.toString(); Assert.assertEquals("answer=42&question=whatdoyoug", actual); } @Test(expected = IllegalArgumentException.class) public void constructor_oddParameters() throws Exception { new QueryString("answer"); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/util/StringHelperTest.java ================================================ package hudson.plugins.tfs.util; import org.junit.Assert; import org.junit.Test; /** * A class to test {@link StringHelper}. */ public class StringHelperTest { @Test public void determineContentTypeWithoutCharset_null() throws Exception { final String actual = StringHelper.determineContentTypeWithoutCharset(null); Assert.assertEquals(null, actual); } @Test public void determineContentTypeWithoutCharset_withoutCharset() throws Exception { final String input = "application/json"; final String actual = StringHelper.determineContentTypeWithoutCharset(input); Assert.assertEquals("application/json", actual); } @Test public void determineContentTypeWithoutCharset_withCharset() throws Exception { final String input = "application/json; charset=utf-8"; final String actual = StringHelper.determineContentTypeWithoutCharset(input); Assert.assertEquals("application/json", actual); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/util/TeamRestClientTest.java ================================================ package hudson.plugins.tfs.util; import com.cloudbees.plugins.credentials.CredentialsScope; import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl; import hudson.plugins.tfs.IntegrationTestHelper; import hudson.plugins.tfs.IntegrationTests; import hudson.util.SecretOverride; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; import java.net.URI; /** * A class to test {@link TeamRestClient}. */ public class TeamRestClientTest { private SecretOverride secretOverride = null; @Before public void setUp() throws Exception { secretOverride = new SecretOverride(); } @After public void tearDown() throws Exception { if (secretOverride != null) { secretOverride.close(); } } @Test public void createAuthorization_typical() throws Exception { final String personalAccessToken = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; final StandardUsernamePasswordCredentials creds = new UsernamePasswordCredentialsImpl( CredentialsScope.SYSTEM, "buildAccount", null, "PAT", personalAccessToken); final String actual = TeamRestClient.createAuthorization(creds); Assert.assertEquals("Basic UEFUOmFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWE=", actual); } @Ignore("Only works on visualstudio.com due to the use of the Authorization header") @Category(IntegrationTests.class) @Test public void ping() throws Exception { final IntegrationTestHelper helper = new IntegrationTestHelper(); final URI collectionUri = new URI(helper.getServerUrl()); final StandardUsernamePasswordCredentials creds = new UsernamePasswordCredentialsImpl( CredentialsScope.SYSTEM, "buildAccount", null, helper.getUserName(), helper.getUserPassword()); final TeamRestClient cut = new TeamRestClient(collectionUri, creds); cut.ping(); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/util/TextTableParserTest.java ================================================ package hudson.plugins.tfs.util; import static org.junit.Assert.*; import java.io.StringReader; import org.junit.Test; import org.jvnet.hudson.test.Bug; public class TextTableParserTest { @Test public void assertThatReaderWithoutTableIsParsed() throws Exception { TextTableParser listParser = new TextTableParser(new StringReader("Just some text to be ignored")); assertFalse("There should not be any row", listParser.nextRow()); } @Test public void assertColumnCount() throws Exception { TextTableParser listParser = new TextTableParser(new StringReader("Just some text to be ignored\n" + "----- -- ------\n")); assertEquals("The column count was incorrect", 3, listParser.getColumnCount()); } @Bug(4666) @Test public void assertDashInTextIsIgnored() throws Exception { TextTableParser listParser = new TextTableParser(new StringReader("Server: server-name\n" + "----- -- ------\n")); assertEquals("The column count was incorrect", 3, listParser.getColumnCount()); } @Test public void assertGetColumn() throws Exception { TextTableParser listParser = new TextTableParser(new StringReader("Just some text to be ignored\n" + "----- -- ------\n" + "AAAAA BB CCCCCC")); listParser.nextRow(); assertEquals("The column one was incorrect", "AAAAA", listParser.getColumn(0)); assertEquals("The column two was incorrect", "BB", listParser.getColumn(1)); assertEquals("The column three was incorrect", "CCCCCC", listParser.getColumn(2)); } @Test public void assertNextRow() throws Exception { TextTableParser listParser = new TextTableParser(new StringReader("Just some text to be ignored\n" + "----- -- ------\n" + "AAAAA BB CCCCCC\n" + "LLLLL DD ZZZZZZ")); assertTrue("The nextLine() returned false", listParser.nextRow()); assertTrue("The nextLine() returned false", listParser.nextRow()); assertFalse("The nextLine() returned true", listParser.nextRow()); } @Test public void assertNextRowWithNonsenseLine() throws Exception { TextTableParser listParser = new TextTableParser(new StringReader("Just some text to be ignored\n" + "----- -- ------\n" + "\n" + "AAAAA BB CCCCCC" + "\n" + "AAAAA BB CCCCCC")); assertTrue("The nextLine() returned false", listParser.nextRow()); assertEquals("The column one was incorrect", "AAAAA", listParser.getColumn(0)); assertTrue("The nextLine() returned false", listParser.nextRow()); assertFalse("The nextLine() returned true", listParser.nextRow()); } @Test public void assertGetColumnWorksIfLastcolumnIsShorterThanLastColumnLength() throws Exception { TextTableParser listParser = new TextTableParser(new StringReader("Just some text to be ignored\n" + "----- -- ------\n" + "AAAAA BB CCC")); listParser.nextRow(); assertEquals("The column one was incorrect", "AAAAA", listParser.getColumn(0)); assertEquals("The column two was incorrect", "BB", listParser.getColumn(1)); assertEquals("The column three was incorrect", "CCC", listParser.getColumn(2)); } @Test public void assertGetColumnWorksIfColumnIsShorter() throws Exception { TextTableParser listParser = new TextTableParser(new StringReader( "----- -- ------\n" + "AA BB CCCCCC")); listParser.nextRow(); assertEquals("The column one was incorrect", "AA", listParser.getColumn(0)); } @Test public void assertTableStartsImmediately() throws Exception { TextTableParser listParser = new TextTableParser(new StringReader( "----- -- ------\n" + "AAAAA BB CCCCCC")); listParser.nextRow(); assertEquals("The column one was incorrect", "AAAAA", listParser.getColumn(0)); assertEquals("The column two was incorrect", "BB", listParser.getColumn(1)); assertEquals("The column three was incorrect", "CCCCCC", listParser.getColumn(2)); } @Test public void assertNextRowWorksWithOptionalColumns() throws Exception { TextTableParser listParser = new TextTableParser(new StringReader( "----- -- ------\n" + "AAAAA BB\n" + "AAAAA BB DDDDDD"), 1); listParser.nextRow(); assertEquals("The column one was incorrect", "AAAAA", listParser.getColumn(0)); assertEquals("The column two was incorrect", "BB", listParser.getColumn(1)); assertNull("The column three was incorrect", listParser.getColumn(2)); listParser.nextRow(); assertEquals("The column three was incorrect", "DDDDDD", listParser.getColumn(2)); } @Test(expected=IllegalStateException.class) public void assertGetColumnAfterLastRowThrowsException() throws Exception { TextTableParser listParser = new TextTableParser(new StringReader( "----- -- ------\n" + "AAAAA BB")); listParser.nextRow(); listParser.nextRow(); listParser.getColumn(0); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/util/UriHelperTest.java ================================================ package hudson.plugins.tfs.util; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.net.URI; /** * A class to test {@link UriHelper}. */ public class UriHelperTest { private QueryString lifeUniverseEverything; @Before public void setUp() { lifeUniverseEverything = new QueryString(); lifeUniverseEverything.put("answer", "42"); } private static void assertSame(final String a, final String b) { areSame(a, b, true); } private static void assertNotSame(final String a, final String b) { areSame(a, b, false); } private static void areSame(final String a, final String b, final boolean expected) { final URI uriA = a == null ? null : URI.create(a); final URI uriB = b == null ? null : URI.create(b); final String template = "Expected '%s' and '%s' to be considered%s the same."; final String message = String.format(template, a, b, expected ? "" : " NOT"); Assert.assertEquals(message, expected, UriHelper.areSame(uriA, uriB)); Assert.assertEquals(message, expected, UriHelper.areSame(uriB, uriA)); } @Test public void areSame_bothNull() throws Exception { assertSame(null, null); } @Test public void areSame_sameInstance() throws Exception { final URI uri = URI.create("http://one.example.com"); Assert.assertTrue(UriHelper.areSame(uri, uri)); } @Test public void areSame_identity() throws Exception { assertSame("http://one.example.com", "http://one.example.com"); } @Test public void areSame_endsWithSlash() throws Exception { assertSame("http://one.example.com/", "http://one.example.com"); } @Test public void areSame_schemeCase() throws Exception { assertSame("http://one.example.com", "HTTP://one.example.com"); } @Test public void areSame_hostCase() throws Exception { assertSame("http://ONE.example.com", "http://one.example.com"); } @Test public void areSame_implicitPort() throws Exception { assertSame("http://one.example.com", "http://one.example.com:80"); } @Test public void areSame_withPathSlash() throws Exception { assertSame("http://one.example.com/path/", "http://one.example.com/path/"); } @Test public void areSame_withPathWithoutSlash() throws Exception { assertSame("http://one.example.com/path/", "http://one.example.com/path"); } @Test public void areSame_withPathQuery() throws Exception { assertSame("http://one.example.com/search?q=example", "http://one.example.com/search?q=example"); } @Test public void areSame_withPathQueryFragment() throws Exception { assertSame("http://one.example.com/search?q=example#top", "http://one.example.com/search?q=example#top"); } @Test public void areSame_oneNull() throws Exception { assertNotSame("http://one.example.com/path/", null); } @Test public void areSame_differentScheme() throws Exception { assertNotSame("http://one.example.com/path/", "https://one.example.com/path/"); } @Test public void areSame_differentHost() throws Exception { assertNotSame("http://one.example.com/path/", "http://two.example.com/path/"); } @Test public void areSame_differentPort() throws Exception { assertNotSame("http://one.example.com/path/", "http://one.example.com:8080/path/"); } @Test public void areSame_differentPath() throws Exception { assertNotSame("http://one.example.com/path/", "http://one.example.com/"); } @Test public void areSame_differentQuery() throws Exception { assertNotSame("http://one.example.com/path?q=example", "http://one.example.com/path?q=different"); } @Test public void areSame_differentFragment() throws Exception { assertNotSame("http://one.example.com/path#top", "http://one.example.com/path#bottom"); } @Test public void areSame_differentFragmentAfterQuery() throws Exception { assertNotSame("http://one.example.com/path?q=example#top", "http://one.example.com/path?q=example#bottom"); } private static void assertSameGitRepo(final String a, final String b) { areSameGitRepo(a, b, true); } private static void assertNotSameGitRepo(final String a, final String b) { areSameGitRepo(a, b, false); } private static void areSameGitRepo(final String a, final String b, final boolean expected) { final URI uriA = a == null ? null : URI.create(a); final URI uriB = b == null ? null : URI.create(b); final String template = "Expected '%s' and '%s' to be considered%s the same."; final String message = String.format(template, a, b, expected ? "" : " NOT"); Assert.assertEquals(message, expected, UriHelper.areSameGitRepo(uriA, uriB) ); Assert.assertEquals(message, expected, UriHelper.areSameGitRepo(uriB, uriA)); } @Test public void areSameGitRepo_withoutDefaultCollection() throws Exception { assertSameGitRepo( "https://fabrikam-fiber-inc.visualstudio.com/project/_git/repo", "https://fabrikam-fiber-inc.visualstudio.com/project/_git/repo" ); } @Test public void areSameGitRepo_withDefaultCollection() throws Exception { assertSameGitRepo( "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/project/_git/repo", "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/project/_git/repo" ); } @Test public void areSameGitRepo_withMixedCaseDefaultCollection() throws Exception { assertSameGitRepo( "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/Project/_git/Repo", "https://fabrikam-fiber-inc.visualstudio.com/defaultcollection/project/_git/repo" ); } @Test public void areSameGitRepo_differentProject() throws Exception { assertNotSameGitRepo( "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/myProject/_git/repo", "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/othePproject/_git/repo" ); } @Test public void areSameGitRepo_differentRepo() throws Exception { assertNotSameGitRepo( "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/project/_git/myRepo", "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/project/_git/otherRepo" ); } @Test public void areSameGitRepo_mixTeamServicesDefaultCollection() throws Exception { assertSameGitRepo( "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/project/_git/repo", "https://fabrikam-fiber-inc.visualstudio.com/project/_git/repo" ); } @Test public void areSameGitRepo_mixTfsDefaultCollection() throws Exception { assertSameGitRepo( "http://tfs.example.com:8080/tfs/DefaultCollection/project/_git/repo", "http://tfs.example.com:8080/tfs/project/_git/repo" ); } @Test public void areSameGitRepo_differentPorts() throws Exception { assertNotSameGitRepo( "http://tfs.example.com:8080/tfs/DefaultCollection/project/_git/repo", "http://tfs.example.com:8081/tfs/DefaultCollection/project/_git/repo" ); } @Test public void areSameGitRepo_teamServicesDifferentProtocols() throws Exception { assertSameGitRepo( "https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/project/_git/repo", "ssh://fabrikam-fiber-inc@fabrikam-fiber-inc.visualstudio.com/project/_git/repo" ); } @Test public void areSameGitRepo_tfsDifferentProtocols() throws Exception { assertSameGitRepo( "http://tfs.example.com:8081/tfs/DefaultCollection/project/_git/repo", "ssh://tfs.example.com:22/tfs/project/_git/repo" ); } @Test public void hasPath_hostOnly() throws Exception { final URI input = URI.create("https://fabrikam-fiber-inc.visualstudio.com"); final boolean actual = UriHelper.hasPath(input); Assert.assertEquals(false, actual); } @Test public void hasPath_hostSlash() throws Exception { final URI input = URI.create("https://fabrikam-fiber-inc.visualstudio.com/"); final boolean actual = UriHelper.hasPath(input); Assert.assertEquals(false, actual); } @Test public void hasPath_path() throws Exception { final URI input = URI.create("https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection"); final boolean actual = UriHelper.hasPath(input); Assert.assertEquals(true, actual); } @Test public void hasPath_pathSlash() throws Exception { final URI input = URI.create("https://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/"); final boolean actual = UriHelper.hasPath(input); Assert.assertEquals(true, actual); } @Test public void join_uriNoSlash_pathComponents() throws Exception { final URI collectionUri = URI.create("https://fabrikam-fiber-inc.visualstudio.com"); final URI actual = UriHelper.join(collectionUri, "_home", "About"); Assert.assertEquals(URI.create("https://fabrikam-fiber-inc.visualstudio.com/_home/About"), actual); } @Test public void join_noSlash_pathComponents() throws Exception { final String collectionUrl = "https://fabrikam-fiber-inc.visualstudio.com"; final URI actual = UriHelper.join(collectionUrl, "_home", "About"); Assert.assertEquals(URI.create("https://fabrikam-fiber-inc.visualstudio.com/_home/About"), actual); } @Test public void join_withSlash_pathComponents() throws Exception { final String collectionUrl = "https://fabrikam-fiber-inc.visualstudio.com/"; final URI actual = UriHelper.join(collectionUrl, "_home", "About"); Assert.assertEquals(URI.create("https://fabrikam-fiber-inc.visualstudio.com/_home/About"), actual); } @Test public void join_noSlash_queryString() throws Exception { final String collectionUrl = "https://fabrikam-fiber-inc.visualstudio.com/"; final URI actual = UriHelper.join(collectionUrl, lifeUniverseEverything); Assert.assertEquals(URI.create("https://fabrikam-fiber-inc.visualstudio.com/?answer=42"), actual); } @Test public void join_withSlash_queryString() throws Exception { final String collectionUrl = "https://fabrikam-fiber-inc.visualstudio.com/"; final URI actual = UriHelper.join(collectionUrl, lifeUniverseEverything); Assert.assertEquals(URI.create("https://fabrikam-fiber-inc.visualstudio.com/?answer=42"), actual); } @Test public void join_noSlash_pathAndQueryString() throws Exception { final String collectionUrl = "https://fabrikam-fiber-inc.visualstudio.com/"; final URI actual = UriHelper.join(collectionUrl, "_home", "About", lifeUniverseEverything); Assert.assertEquals(URI.create("https://fabrikam-fiber-inc.visualstudio.com/_home/About?answer=42"), actual); } @Test public void join_withSlash_pathAndQueryString() throws Exception { final String collectionUrl = "https://fabrikam-fiber-inc.visualstudio.com/"; final URI actual = UriHelper.join(collectionUrl, "_home", "About", lifeUniverseEverything); Assert.assertEquals(URI.create("https://fabrikam-fiber-inc.visualstudio.com/_home/About?answer=42"), actual); } @Test public void join_urlEncoding() throws Exception { final String collectionUrl = "https://fabrikam-fiber-inc.visualstudio.com/"; final URI actual = UriHelper.join(collectionUrl, "_git", "Repo Name+With Spaces"); Assert.assertEquals(URI.create("https://fabrikam-fiber-inc.visualstudio.com/_git/Repo%20Name%2BWith%20Spaces"), actual); } @Test public void join_sampleApiCall() throws Exception { final String collectionUrl = "https://fabrikam-fiber-inc.visualstudio.com/"; final QueryString qs = new QueryString("api-version", "2.0"); final URI actual = UriHelper.join(collectionUrl, "_apis", "projects", qs); Assert.assertEquals(URI.create("https://fabrikam-fiber-inc.visualstudio.com/_apis/projects?api-version=2.0"), actual); } } ================================================ FILE: tfs/src/test/java/hudson/plugins/tfs/util/XmlHelperTest.java ================================================ package hudson.plugins.tfs.util; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathFactory; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; public class XmlHelperTest { @Test public void peekValue_Document() throws Exception { final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); final DocumentBuilder db = dbf.newDocumentBuilder(); final Document doc = db.newDocument(); // final Element root = doc.createElement("build"); // 76 final Element queueIdNode = doc.createElement("queueId"); queueIdNode.appendChild(doc.createTextNode("76")); root.appendChild(queueIdNode); // 84 final Element timestampNode = doc.createElement("timestamp"); timestampNode.appendChild(doc.createTextNode("84")); root.appendChild(timestampNode); // doc.appendChild(root); final String actualFound = XmlHelper.peekValue(doc, "/build/timestamp"); Assert.assertEquals("84", actualFound); final String actualNotFound = XmlHelper.peekValue(doc, "/build/startTime"); Assert.assertEquals(null, actualNotFound); } @Test public void peekValue_File() throws Exception { final Class clazz = this.getClass(); final String resourceBase = clazz.getSimpleName() + "/peekValue_File/"; final URL inputUrl = clazz.getResource(resourceBase + "input.xml"); File tmp = null; try { tmp = File.createTempFile("XmlHelperTest", "xml"); FileUtils.copyURLToFile(inputUrl, tmp); final String actual = XmlHelper.peekValue(tmp, "/build/timestamp"); Assert.assertEquals("1436542800239", actual); } finally { FileUtils.deleteQuietly(tmp); } } @Test public void pokeValue_Document() throws Exception { final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); final DocumentBuilder db = dbf.newDocumentBuilder(); final Document doc = db.newDocument(); // final Element root = doc.createElement("build"); // 76 final Element queueIdNode = doc.createElement("queueId"); queueIdNode.appendChild(doc.createTextNode("76")); root.appendChild(queueIdNode); // 84 final Element timestampNode = doc.createElement("timestamp"); timestampNode.appendChild(doc.createTextNode("84")); root.appendChild(timestampNode); // doc.appendChild(root); XmlHelper.pokeValue(doc, "/build/timestamp", "42"); final XPathFactory xpf = XPathFactory.newInstance(); final XPath xp = xpf.newXPath(); final XPathExpression expression = xp.compile("/build/timestamp"); final Node node = (Node) expression.evaluate(doc, XPathConstants.NODE); Assert.assertEquals("42", node.getTextContent()); } @Test public void pokeValue_File() throws Exception { final Class clazz = this.getClass(); final String resourceBase = clazz.getSimpleName() + "/pokeValue_File/"; final URL inputUrl = clazz.getResource(resourceBase + "input.xml"); File tmp = null; BufferedReader expectedReader = null, actualReader = null; try { tmp = File.createTempFile("XmlHelperTest", "xml"); FileUtils.copyURLToFile(inputUrl, tmp); XmlHelper.pokeValue(tmp, "/build/timestamp", "42"); final URL expectedUrl = clazz.getResource(resourceBase + "expected.xml"); expectedReader = new BufferedReader(new InputStreamReader(expectedUrl.openStream())); actualReader = new BufferedReader(new FileReader(tmp)); assertReaders(expectedReader, actualReader); } finally { FileUtils.deleteQuietly(tmp); IOUtils.closeQuietly(expectedReader); IOUtils.closeQuietly(actualReader); } } /* Adapted from http://stackoverflow.com/a/466854/ */ public static void assertReaders(final BufferedReader expected, final BufferedReader actual) throws IOException { String expectedLine; while ((expectedLine = expected.readLine()) != null) { final String actualLine = actual.readLine(); Assert.assertNotNull("Expected had more lines than the actual.", actualLine); Assert.assertEquals(expectedLine, actualLine); } Assert.assertNull("Actual had more lines than the expected.", actual.readLine()); } } ================================================ FILE: tfs/src/test/java/hudson/util/SecretOverride.java ================================================ package hudson.util; import jenkins.security.ConfidentialStore; import jenkins.security.ConfidentialStoreOverride; import java.io.Closeable; import java.io.IOException; /** * Placed in the same package as {@link hudson.util.Secret} to be able to reach * its package-protected {@link hudson.util.Secret#SECRET} field, which allows * testing of encryption-using code without needing to launch all of Jenkins. */ public class SecretOverride implements Closeable { private static ConfidentialStoreOverride confidentialStoreOverride = null; public static void set(final String secretKey) { Secret.SECRET = secretKey; if (confidentialStoreOverride != null) { try { confidentialStoreOverride.close(); } catch (final IOException ignored) { } confidentialStoreOverride = null; } if (secretKey != null) { confidentialStoreOverride = new ConfidentialStoreOverride(); } } public SecretOverride() { this("5e2422dc868f119d5033f4619a6f223d71d132a17f8a63f1056c9a1f57c65006"); } public SecretOverride(final String secretKey) { set(secretKey); } public void close() throws IOException { set(null); } } ================================================ FILE: tfs/src/test/java/jenkins/security/ConfidentialStoreOverride.java ================================================ package jenkins.security; import hudson.Util; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.util.Random; public class ConfidentialStoreOverride implements Closeable { private static final File TEMP_FOLDER; private static final ConfidentialStore TEST_CONFIDENTIAL_STORE; private static final ThreadLocal TEST_THREAD_LOCAL; private static final Random TEST_RANDOM_SOURCE = new Random(4 /* chosen by fair dice roll */); static { try { TEMP_FOLDER = new File(Util.createTempDir(), "jenkins"); TEST_CONFIDENTIAL_STORE = new DefaultConfidentialStore(TEMP_FOLDER) { public byte[] randomBytes(final int size) { byte[] random = new byte[size]; TEST_RANDOM_SOURCE.nextBytes(random); return random; } }; TEST_THREAD_LOCAL = new ThreadLocal(){ protected ConfidentialStore initialValue() { return TEST_CONFIDENTIAL_STORE; } }; } catch (final IOException e) { throw new Error(e); } catch (final InterruptedException e) { throw new Error(e); } } public static void set(final ThreadLocal override) { ConfidentialStore.TEST = override; } public ConfidentialStoreOverride() { this(TEST_THREAD_LOCAL); } public ConfidentialStoreOverride(final ThreadLocal override) { set(override); } public void close() throws IOException { set(null); } } ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/agent/config.xml ================================================ 360 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/agent/hudson.plugins.tfs.TeamFoundationServerScm.xml ================================================ 2 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/agent/jobs/agent/builds/2015-07-15_20-37-42/build.xml ================================================ Hudson-upgradeEncodedPassword-MASTER . true 1437007062000 SUCCESS 8897 windows-1252 false upgradeEncodedPassword/workspace 1.448 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/agent/jobs/agent/builds/2015-07-15_20-37-42/changelog.xml ================================================ ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/agent/jobs/agent/builds/2015-07-15_20-37-42/log ================================================ Started by user (...) Finished: SUCCESS ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/agent/jobs/agent/builds/lastFailedBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/agent/jobs/agent/builds/lastStableBuild ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/agent/jobs/agent/builds/lastSuccessfulBuild ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/agent/jobs/agent/builds/lastUnstableBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/agent/jobs/agent/builds/lastUnsuccessfulBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/agent/jobs/agent/builds/legacyIds ================================================ ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/agent/jobs/agent/config.xml ================================================ false . Hudson-${JOB_NAME}-${COMPUTERNAME} pmJe5VYJg6gr2BdipI1sMGJScFwmT+pZbz7B2jISBrw= jenkins-tfs-plugin false agent false false false false false ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/agent/jobs/agent/nextBuildNumber ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/agent/secret.key ================================================ 5e2422dc868f119d5033f4619a6f223d71d132a17f8a63f1056c9a1f57c65006 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/cloakedPaths/config.xml ================================================ 360 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/cloakedPaths/hudson.plugins.tfs.TeamFoundationServerScm.xml ================================================ 2 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/cloakedPaths/jobs/cloakedPaths/builds/2015-07-15_20-37-42/build.xml ================================================ Hudson-upgradeEncodedPassword-MASTER . true 1437007062000 SUCCESS 8897 windows-1252 false upgradeEncodedPassword/workspace 1.448 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/cloakedPaths/jobs/cloakedPaths/builds/2015-07-15_20-37-42/changelog.xml ================================================ ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/cloakedPaths/jobs/cloakedPaths/builds/2015-07-15_20-37-42/log ================================================ Started by user (...) Finished: SUCCESS ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/cloakedPaths/jobs/cloakedPaths/builds/lastFailedBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/cloakedPaths/jobs/cloakedPaths/builds/lastStableBuild ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/cloakedPaths/jobs/cloakedPaths/builds/lastSuccessfulBuild ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/cloakedPaths/jobs/cloakedPaths/builds/lastUnstableBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/cloakedPaths/jobs/cloakedPaths/builds/lastUnsuccessfulBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/cloakedPaths/jobs/cloakedPaths/builds/legacyIds ================================================ ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/cloakedPaths/jobs/cloakedPaths/config.xml ================================================ false . Hudson-${JOB_NAME}-${COMPUTERNAME} pmJe5VYJg6gr2BdipI1sMGJScFwmT+pZbz7B2jISBrw= jenkins-tfs-plugin false master false false false false false ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/cloakedPaths/jobs/cloakedPaths/nextBuildNumber ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/cloakedPaths/secret.key ================================================ 5e2422dc868f119d5033f4619a6f223d71d132a17f8a63f1056c9a1f57c65006 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/createLabel/config.xml ================================================ 360 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/createLabel/hudson.plugins.tfs.TeamFoundationServerScm.xml ================================================ 2 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/createLabel/jobs/createLabel/builds/2015-07-15_20-37-42/build.xml ================================================ Hudson-upgradeEncodedPassword-MASTER . true 1437007062000 SUCCESS 8897 windows-1252 false upgradeEncodedPassword/workspace 1.448 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/createLabel/jobs/createLabel/builds/2015-07-15_20-37-42/changelog.xml ================================================ ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/createLabel/jobs/createLabel/builds/2015-07-15_20-37-42/log ================================================ Started by user (...) Finished: SUCCESS ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/createLabel/jobs/createLabel/builds/lastFailedBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/createLabel/jobs/createLabel/builds/lastStableBuild ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/createLabel/jobs/createLabel/builds/lastSuccessfulBuild ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/createLabel/jobs/createLabel/builds/lastUnstableBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/createLabel/jobs/createLabel/builds/lastUnsuccessfulBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/createLabel/jobs/createLabel/builds/legacyIds ================================================ ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/createLabel/jobs/createLabel/config.xml ================================================ false . Hudson-${JOB_NAME}-${COMPUTERNAME} pmJe5VYJg6gr2BdipI1sMGJScFwmT+pZbz7B2jISBrw= jenkins-tfs-plugin false master false false false false false success ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/createLabel/jobs/createLabel/nextBuildNumber ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/createLabel/secret.key ================================================ 5e2422dc868f119d5033f4619a6f223d71d132a17f8a63f1056c9a1f57c65006 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/newJob/config.xml ================================================ 360 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/newJob/hudson.plugins.tfs.TeamFoundationServerScm.xml ================================================ 2 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/newJob/jobs/newJob/builds/lastFailedBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/newJob/jobs/newJob/builds/lastStableBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/newJob/jobs/newJob/builds/lastSuccessfulBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/newJob/jobs/newJob/builds/lastUnstableBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/newJob/jobs/newJob/builds/lastUnsuccessfulBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/newJob/jobs/newJob/builds/legacyIds ================================================ ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/newJob/jobs/newJob/config.xml ================================================ false . Hudson-${JOB_NAME}-${COMPUTERNAME} pmJe5VYJg6gr2BdipI1sMGJScFwmT+pZbz7B2jISBrw= jenkins-tfs-plugin false master false false false false false ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/newJob/jobs/newJob/nextBuildNumber ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/newJob/secret.key ================================================ 5e2422dc868f119d5033f4619a6f223d71d132a17f8a63f1056c9a1f57c65006 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/oldPollingFallback/config.xml ================================================ 1.616 1 EXCLUSIVE true ${ITEM_ROOTDIR}/workspace ${ITEM_ROOTDIR}/builds 0 0 All false false All 0 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/oldPollingFallback/jobs/oldPollingFallback/builds/2015-07-10_12-11-34/build.xml ================================================ Hudson-oldPollingFallback-MASTER . $/FunctionalTests/TeamFoundationServerScmFunctionalTest/oldPollingFallback http://${tfs_server_name}:8080/tfs/jenkins-tfs-plugin true 1436542800239 FAILURE 134 windows-1252 false oldPollingFallback/workspace 1.448 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/oldPollingFallback/jobs/oldPollingFallback/builds/2015-07-10_12-11-34/log ================================================ Started by an SCM change (bad stuff happens) Finished: FAILURE ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/oldPollingFallback/jobs/oldPollingFallback/builds/2015-07-10_12-11-34/polling.log ================================================ Started on Jul 10, 2015 11:40:00 AM No existing build. Scheduling a new one. Done. Took 1 ms Changes found ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/oldPollingFallback/jobs/oldPollingFallback/builds/lastFailedBuild ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/oldPollingFallback/jobs/oldPollingFallback/builds/lastStableBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/oldPollingFallback/jobs/oldPollingFallback/builds/lastSuccessfulBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/oldPollingFallback/jobs/oldPollingFallback/builds/lastUnstableBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/oldPollingFallback/jobs/oldPollingFallback/builds/lastUnsuccessfulBuild ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/oldPollingFallback/jobs/oldPollingFallback/builds/legacyIds ================================================ ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/oldPollingFallback/jobs/oldPollingFallback/config.xml ================================================ false . Hudson-${JOB_NAME}-${COMPUTERNAME} pmJe5VYJg6gr2BdipI1sMGJScFwmT+pZbz7B2jISBrw= jenkins-tfs-plugin false master false false false false false ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/oldPollingFallback/jobs/oldPollingFallback/nextBuildNumber ================================================ 2 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/oldPollingFallback/secret.key ================================================ 5e2422dc868f119d5033f4619a6f223d71d132a17f8a63f1056c9a1f57c65006 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/upgradeEncodedPassword/config.xml ================================================ ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/upgradeEncodedPassword/jobs/upgradeEncodedPassword/builds/2015-07-15_20-37-42/build.xml ================================================ Hudson-upgradeEncodedPassword-MASTER . true 1436983436507 SUCCESS 8897 windows-1252 false upgradeEncodedPassword/workspace 1.448 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/upgradeEncodedPassword/jobs/upgradeEncodedPassword/builds/2015-07-15_20-37-42/changelog.xml ================================================ ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/upgradeEncodedPassword/jobs/upgradeEncodedPassword/builds/2015-07-15_20-37-42/log ================================================ Started by user (...) Finished: SUCCESS ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/upgradeEncodedPassword/jobs/upgradeEncodedPassword/builds/lastFailedBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/upgradeEncodedPassword/jobs/upgradeEncodedPassword/builds/lastStableBuild ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/upgradeEncodedPassword/jobs/upgradeEncodedPassword/builds/lastSuccessfulBuild ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/upgradeEncodedPassword/jobs/upgradeEncodedPassword/builds/lastUnstableBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/upgradeEncodedPassword/jobs/upgradeEncodedPassword/builds/lastUnsuccessfulBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/upgradeEncodedPassword/jobs/upgradeEncodedPassword/builds/legacyIds ================================================ ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/upgradeEncodedPassword/jobs/upgradeEncodedPassword/config.xml ================================================ false . Hudson-${JOB_NAME}-${COMPUTERNAME} Zm9yLXRlc3Qtb25seQ== jenkins-tfs-plugin false master false false false false false ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/upgradeEncodedPassword/jobs/upgradeEncodedPassword/nextBuildNumber ================================================ 2 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/upgradeEncodedPassword/secret.key ================================================ 5e2422dc868f119d5033f4619a6f223d71d132a17f8a63f1056c9a1f57c65006 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/config.xml ================================================ 360 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/hudson.plugins.tfs.TeamFoundationServerScm.xml ================================================ 2 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/2015-07-15_20-37-42/build.xml ================================================ Hudson-useWebProxyServer-MASTER . true 1437007062000 SUCCESS 8897 windows-1252 false useWebProxyServer/workspace 1.448 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/2015-07-15_20-37-42/changelog.xml ================================================ ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/2015-07-15_20-37-42/log ================================================ Started by user (...) Finished: SUCCESS ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/lastFailedBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/lastStableBuild ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/lastSuccessfulBuild ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/lastUnstableBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/lastUnsuccessfulBuild ================================================ -1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/legacyIds ================================================ ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/config.xml ================================================ false . Hudson-${JOB_NAME}-${COMPUTERNAME} pmJe5VYJg6gr2BdipI1sMGJScFwmT+pZbz7B2jISBrw= jenkins-tfs-plugin false master false false false false false ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/nextBuildNumber ================================================ 1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/proxy.xml ================================================ zTigAtPm/kPRVsuUZgIXnw== ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/secret.key ================================================ 5e2422dc868f119d5033f4619a6f223d71d132a17f8a63f1056c9a1f57c65006 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/action/tf-changeset-1.log ================================================ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12472 User: RNO\_MCLWEB Date: 2008-jun-27 11:16:06 Comment: Created team project folder $/tfsandbox via the Team Project Creation Wizard Items: add $/tfsandbox ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/action/tf-changeset-2.log ================================================ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12492 User: SND\redsolo_cp Date: 2008-jun-27 13:11:15 Comment: first file Items: add $/tfsandbox/readme.txt -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12472 User: RNO\_MCLWEB Date: 2008-jun-10 11:16:06 Comment: Created team project folder $/tfsandbox via the Team Project Creation Wizard Items: add $/tfsandbox ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/action/tf-changeset-3.log ================================================ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12495 User: SND\redsolo_cp Date: 2008-jun-27 13:21:25 Comment: changed and created one Items: edit $/tfsandbox/readme.txt add $/tfsandbox/readme2.txt -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12493 User: SND\redsolo_cp Date: 2008-jun-27 13:19:41 Comment: changed and created one Items: edit $/tfsandbox/readme.txt -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12492 User: SND\redsolo_cp Date: 2008-jun-27 13:11:15 Comment: first file Items: add $/tfsandbox/readme.txt -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12472 User: RNO\_MCLWEB Date: 2008-jun-27 11:16:06 Comment: Created team project folder $/tfsandbox via the Team Project Creation Wizard Items: add $/tfsandbox ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/commands/issue-3683.txt ================================================ ------------------------------------------------------------------------------- Changeset: 19518 User: im21 Date: 13 May 2009 22:38:55 Comment: Uncommenting lines in IOC.cs Items: edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework/IoC/IoC.cs Check-in Notes: Code Reviewer: Performance Reviewer: Security Reviewer: ------------------------------------------------------------------------------- Changeset: 19517 User: im21 Date: 13 May 2009 22:05:30 Comment: Merging Framework Project file into r2 from MAIN Items: merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework/app.config;X4019 merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework/Framework.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework/Framework.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework/WCF merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework/WCF/WCFProxyBase.cs Check-in Notes: Code Reviewer: Performance Reviewer: Security Reviewer: ------------------------------------------------------------------------------- Changeset: 19516 User: im21 Date: 13 May 2009 21:59:12 Comment: Checking in framework changes Items: merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework.CrossTalk merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework.CrossTalk/Framework.CrossTalk.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework.CrossTalk/Framework.CrossTalk.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework.CrossTalk/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework.CrossTalk/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework.Security merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework.Security/Framework.Security.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework.Security/Framework.Security.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework.Security/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Framework.Security/Properties/AssemblyInfo.cs;X4007 merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/Properties/AssemblyInfo.cs;X3992 merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/SMO.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/SQL.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/SQL.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/SSIS merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/SSIS/Error.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/SSIS/ErrorType.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/SSIS/IPackageDetails.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/SSIS/PackageDetails.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/SSIS/PackageDetailsDTS.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/SSIS/PackageDetailsFILE.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/SSIS/PackageDetailsProvider.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/SSIS/PackageDetailsProviderResult.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/SSIS/PackageDetailsSQL.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/SSIS/PackageHelper.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Helpers/SQL/SSIS/PackageHelperResult.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/AccessControllerTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/AccessControlPolicyCacheTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/ApplicationsNavigatorTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/ContextDataExample.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/GroceryWeb.tesco.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/GroceryWeb.tesco merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/GuidBasedObjectIdentifierTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/Helpers merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/Helpers/TestHelper.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/LoginContextHandlerTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/OperationsTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/PersistedIdentityHandlerTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/PolicyNavigatorTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/ReferenceMonitor.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/ReferenceMonitor.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/RefMonHttpModuleTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/Resources merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/Resources/TestData.Designer.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/Resources/TestData.resx merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/TokenHandlerTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/TypeBasedObjectIdentifierTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor.UnitTests/VSCodeGenAccessors.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/AccessController.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Entities merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Entities/AccessControlDecisionEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/HttpContextKeys.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/IAccessController.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Internal merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Internal/AccessControlPolicyCache.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Internal/ApplicationsNavigator.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Internal/AuthoriseEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Internal/DataRepository.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Internal/IAccessControlPolicyCache.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Internal/ILoginContextHandler.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Internal/ITokenHandler.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Internal/LoggerEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Internal/LoginContextHandler.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Internal/Operations.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Internal/OperatorEnumerations.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Internal/PolicyNavigator.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Internal/TokenHandler.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Internal/Variable.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/IPersistedIdentityHandler.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/ITescoSecureHttpHandler.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/ObjectIdentifiers merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/ObjectIdentifiers/AbstractObjectIdentifier.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/ObjectIdentifiers/GuidBasedObjectIdentifier.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/ObjectIdentifiers/TypeBasedObjectIdentifier.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/ObjectIdentifiers/WebButtonObjectIdentifier.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/ObjectIdentifiers/WebUrlObjectIdentifier.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/PersistedIdentityHandler.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Properties/AssemblyInfo.cs;X3999 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/Properties/AssemblyInfo.Internals..cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/ReferenceMonitor.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/ReferenceMonitor.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/ReferenceMonitor/RefMonHttpModule.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/ConfigurationService.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/ConfigurationService.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/ConfigurationService.UnitTests/Configuration.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/ConfigurationService.UnitTests/Configuration.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/ConfigurationService.UnitTests/ConfigurationServiceReadTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/ConfigurationService.UnitTests/GroceryWebConfigTest.tesco.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/ConfigurationService.UnitTests/GroceryWebConfigTest.tesco merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/ConfigurationService.UnitTests/MockObjects.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/ConfigurationService.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/ConfigurationService.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/ConfigurationService.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/app.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/ClassDiagram1.cd merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/ILocatorService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Locator.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Locator.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Locator.Entities merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Locator.Entities/AddressEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Locator.Entities/AddressesEntity.cs merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Locator.Entities/AddressExceptionEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Locator.Entities/AreaDetailsEntity.cs merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Locator.Entities/Exception.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Locator.Entities/Locator.Entities.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Locator.Entities/Locator.Entities.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Locator.Entities/PafAddressEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Locator.Entities/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Locator.Entities/Properties/AssemblyInfo.cs;X4012 merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorServiceFactory.cs merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorServiceOperations.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorTests/Locator.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorTests/Locator.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorTests/LocatorTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorWcfWebsite merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorWcfWebsite/dummy.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorWcfWebsite/Locator.svc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorWcfWebsite/LocatorWcfWebsite.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorWcfWebsite/LocatorWcfWebsite.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorWcfWebsite/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorWcfWebsite/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorWcfWebsite/Properties/AssemblyInfo.cs;X3988 merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/LocatorWcfWebsite/Web.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/SensitiveDatabaseAccess merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/SensitiveDatabaseAccess/IOperations.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/SensitiveDatabaseAccess/mssccprj.scc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/SensitiveDatabaseAccess/Operations.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/SensitiveDatabaseAccess/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/SensitiveDatabaseAccess/Properties/AssemblyInfo.cs;X3986 merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/SensitiveDatabaseAccess/ResourceAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/SensitiveDatabaseAccess/SensitiveDatabaseAccess.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/SensitiveDatabaseAccess/SensitiveDatabaseAccess.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Service References merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Service References/LocatorSvcSDA merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Service References/LocatorSvcSDA/configuration.svcinfo merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Service References/LocatorSvcSDA/configuration91.svcinfo merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Service References/LocatorSvcSDA/Locator.disco;X4009 merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Service References/LocatorSvcSDA/Locator.xsd;X4011 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Service References/LocatorSvcSDA/Locator1.disco merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Service References/LocatorSvcSDA/Locator1.xsd;X4010 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Service References/LocatorSvcSDA/Locator2.xsd merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Service References/LocatorSvcSDA/Locator21.xsd merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Service References/LocatorSvcSDA/LocatorSvcSDAOperations.wsdl;X4014 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Service References/LocatorSvcSDA/LocatorSvcSDAOperations1.wsdl merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Service References/LocatorSvcSDA/Reference.cs merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Locator/Service References/LocatorSvcSDA/Reference.svcmap merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/app.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/EmailProviderTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/EmailProviderTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/EmailProviderTests/EMailNotificationServiceProviderTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/EmailProviderTests/EmailProvider.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/EmailProviderTests/EmailProvider.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/EmailProviderTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/EmailProviderTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/EmailProviderTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/EmailProviderTests/VSCodeGenAccessors.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/INotificationService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/Notification.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/Notification.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationEntities merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationEntities/EmailEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationEntities/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationEntities/NotificationEntities.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationEntities/NotificationEntities.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationEntities/NotificationEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationEntities/NotificationMethodEnum.cs merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationEntities/NotificationTypeEnum.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationEntities/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationEntities/Properties/AssemblyInfo.cs;X3989 merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationService merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationService/INotificationServiceProvider.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationService/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationService/NotificationProviderFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationService/NotificationProviders merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationService/NotificationProviders/EMailNotificationServiceProvider.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationService/NotificationService.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationService/NotificationService.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationService/NotificationServiceProvider.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationService/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationService/Properties/AssemblyInfo.cs;X3990 merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationTests/Notification.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationTests/Notification.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationTests/NotificationServiceTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationWCFWebsite merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationWCFWebsite/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationWCFWebsite/Notification.svc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationWCFWebsite/NotificationWCFWebsite.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationWCFWebsite/NotificationWCFWebsite.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationWCFWebsite/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationWCFWebsite/Properties/AssemblyInfo.cs;X3993 merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/NotificationWCFWebsite/Web.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/Properties/AssemblyInfo.cs;X3997 merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/Service References merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/Service References/NotificationWCFService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Notification/Service References/NotificationWCFService.map merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests/Auditing.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests/Auditing.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests/AuditingServiceTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests/AuthenticationEventRequestTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests/AuthorisationEventRequestTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests/CryptographyEventRequestTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests/Helpers merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests/Helpers/ObjectBuilderHelper.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests/IdentificationEventRequestTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests/SecurityEventRequestTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing.UnitTests/SecurityFailureEventRequestTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/Auditing.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/Auditing.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/AuditingService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/AuditingServiceFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/IAuditingService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/Internal merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/Internal/EventTypeInstruction.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/Internal/EventTypeInstructions.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/Internal/EventTypeInstructionsCache.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/Internal/ResourceAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/Properties/AssemblyInfo.cs;X4015 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/Properties/AssemblyInfo.Internals.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/SecurityEventEntities merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/SecurityEventEntities/AbstractEvent.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/SecurityEventEntities/AbstractSecurityEvent.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/SecurityEventEntities/AuthenticationEventRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/SecurityEventEntities/AuthorisationEventRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/SecurityEventEntities/AuthorisationManagementEventRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/SecurityEventEntities/CryptographyEventRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/SecurityEventEntities/IdentificationEventRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/SecurityEventEntities/SecurityEventRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/SecurityEventEntities/SecurityFailureEventRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/Service References merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/Service References/AuditingSdaService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/Auditing/Service References/AuditingSdaService.map merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess.UnitTests/AuditingSensitiveDatabaseAccess.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess.UnitTests/AuditingSensitiveDatabaseAccess.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess.UnitTests/AuditingSensitiveDatabaseAccessTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess.UnitTests/Helpers merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess.UnitTests/Helpers/TestHelper.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess/AuditingSensitiveDatabaseAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess/AuditingSensitiveDatabaseAccess.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess/AuditingSensitiveDatabaseAccess.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess/IAuditingSensitiveDatabaseAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess/Properties/AssemblyInfo.cs;X4016 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingSensitiveDatabaseAccess/Properties/AssemblyInfo.Internals.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingService.IntegrationTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingService.IntegrationTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingService.IntegrationTests/AuditingService.IntegrationTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingService.IntegrationTests/AuditingService.IntegrationTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingService.IntegrationTests/IntegrationTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingService.IntegrationTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingService.IntegrationTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/AuditingService.IntegrationTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/SensitiveDataAccessWcfWebsite merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/SensitiveDataAccessWcfWebsite/AuditingSensitiveDataAccess.svc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/SensitiveDataAccessWcfWebsite/dummy.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/SensitiveDataAccessWcfWebsite/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/SensitiveDataAccessWcfWebsite/SensitiveDataAccessWcfWebsite.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/SensitiveDataAccessWcfWebsite/SensitiveDataAccessWcfWebsite.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Auditing/SensitiveDataAccessWcfWebsite/Web.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication.UnitTests/app.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication.UnitTests/Authentication.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication.UnitTests/Authentication.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication.UnitTests/AuthenticationMechanismFactoryMockTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication.UnitTests/AuthenticationMechanismFactoryTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication.UnitTests/AuthenticationServiceMockTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication.UnitTests/AuthenticationServiceTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Authentication.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Authentication.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/AuthenticationChallenge.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/AuthenticationChallengeResponse.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/AuthenticationChallengeResult.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/AuthenticationMechanismCache.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/AuthenticationResourceAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/AuthenticationService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Entities merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Entities/MechanismDetails.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Entities/PasswordAuthenticationData.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Factory merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Factory/AbstractAuthenticationChallengeBuilder.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Factory/AbstractAuthenticationChallengeResponseHandler.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Factory/AbstractAuthenticationManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Factory/AbstractAuthenticationMechanismFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Factory/AbstractAuthenticationResourceAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Factory/CaseInsensitivePasswordAuthenticationChallengeBuilder.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Factory/CaseInsensitivePasswordAuthenticationChallengeResponseHandler.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Factory/CaseInsensitivePasswordAuthenticationManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Factory/CaseInsensitivePasswordAuthenticationMechanismFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Factory/PasswordAuthenticationChallengeBuilder.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Factory/PasswordAuthenticationChallengeResponseHandler.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Factory/PasswordAuthenticationManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Factory/PasswordAuthenticationMechanismFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Factory/PasswordAuthenticationResourceAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/IAuthenticationService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/MechanismType.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Properties/AssemblyInfo.cs;X3995 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Properties/AssemblyInfo.Internals.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Service References merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Service References/AuthenticationSdaService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Service References/AuthenticationSdaService.map merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Service References/PasswordSdaService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/Authentication/Service References/PasswordSdaService.map merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDataAccessWcfWebsite merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDataAccessWcfWebsite/dummy.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDataAccessWcfWebsite/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDataAccessWcfWebsite/PasswordSensitiveDataAccess.svc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDataAccessWcfWebsite/PasswordSensitiveDataAccessWcfWebsite.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDataAccessWcfWebsite/PasswordSensitiveDataAccessWcfWebsite.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDataAccessWcfWebsite/Web.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess.UnitTests/PasswordSensitiveDatabaseAccess.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess.UnitTests/PasswordSensitiveDatabaseAccess.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess.UnitTests/PasswordSensitiveDatabaseAccessTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess/DatabaseAccessService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess/IDatabaseAccessService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess/PasswordSensitiveDatabaseAccess.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess/PasswordSensitiveDatabaseAccess.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess/Properties/AssemblyInfo.cs;X3991 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/PasswordSensitiveDatabaseAccess/Properties/AssemblyInfo.Internals.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDataAccessWcfWebsite merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDataAccessWcfWebsite/AuthenticationSensitiveDataAccess.svc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDataAccessWcfWebsite/dummy.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDataAccessWcfWebsite/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDataAccessWcfWebsite/SensitiveDataAccessWcfWebsite.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDataAccessWcfWebsite/SensitiveDataAccessWcfWebsite.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDataAccessWcfWebsite/Web.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess.UnitTests/PasswordSensitiveDatabaseAccessTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess.UnitTests/SensitiveDatabaseAccess.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess.UnitTests/SensitiveDatabaseAccess.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess/DatabaseAccessService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess/IDatabaseAccessService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess/Properties/AssemblyInfo.cs;X3994 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess/Properties/AssemblyInfo.Internals.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess/SensitiveDatabaseAccess.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authentication/SensitiveDatabaseAccess/SensitiveDatabaseAccess.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AccessControlPolicyService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AccessControlPolicyServiceFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/AccessControlPolicyDataRequestTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/AccessControlPolicyManagementTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/AccessControlPolicyServiceFactoryTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/Authorisation.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/Authorisation.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/AuthorisationAuditingTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/AuthorisationServiceFactoryTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/AuthorisationTokenBuilderTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/Helpers merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/Helpers/TestingHelpers.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/ResourceAccessTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/TestData.Designer.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/TestData.resx merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/UserAuthorisationTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Authorisation.UnitTests/UserPermissionBuilderTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/app.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/AccessControlPolicyManagementTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/ApplicationManagmentTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/AuthorisationManagement.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/AuthorisationManagement.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/AuthorisationManagementAuditingTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/AuthorisationManagementServiceTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/Helpers merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/Helpers/TestingHelpers.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/PermissionManagementTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/ResourceAccessTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/UserGroupManagementTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/UserManagementTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagement.UnitTests/UserRoleManagementTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/AuthorisationManagementService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Entities merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Entities/ApplicationEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Entities/ApplicationGroupEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Entities/AuthorisationManagementRequestEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Entities/IAuditableRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Entities/PermissionContextEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Entities/PermissionEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Entities/UserEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Entities/UserGroupEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Entities/UserRoleEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Helpers merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Helpers/ConversionHelpers.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/IAuthorisationManagementService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/AccessControlPolicyManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/Adapters merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/Adapters/AccessControlPolicyGroupAdapter.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/Adapters/AccessControlPolicyRoleAdapter.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/Adapters/ApplicationEntityAdapter.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/Adapters/ApplicationGroupEntityAdapter.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/Adapters/BaseAccessControlPolicyUserAdapter.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/Adapters/BaseUserEntityAdapter.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/Adapters/PermissionEntityAdapter.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/Adapters/UserGroupEntityAdapter.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/Adapters/UserRoleEntityAdapter.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/ApplicationManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/AuthorisationManagementAuditing.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/IAccessControlPolicyManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/IApplicationManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/IAuthorisationManagementAuditing.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/IPermissionManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/IResourceAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/IUserGroupManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/IUserManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/IUserRoleManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/PermissionManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/ResourceAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/UserGroupManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/UserManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/UserRoleManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/Validators merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/Validators/BaseUserValidator.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/Validators/UserGroupValidator.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Internal/Validators/UserRoleValidator.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Properties/AssemblyInfo.cs;X3987 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Properties/AssemblyInfo.Internals.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Service References merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Service References/AuthorisationManagementSdaService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagement/Service References/AuthorisationManagementSdaService.map merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagementSensitiveDBAccess merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagementSensitiveDBAccess/AuthorisationManagementSensitiveDatabaseAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagementSensitiveDBAccess/AuthorisationManagementSensitiveDatabaseAccessServiceFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagementSensitiveDBAccess/AuthorisationManagementSensitiveDBAccess.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagementSensitiveDBAccess/AuthorisationManagementSensitiveDBAccess.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagementSensitiveDBAccess/IAuthorisationManagementSensitiveDatabaseAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagementSensitiveDBAccess/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagementSensitiveDBAccess/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagementSensitiveDBAccess/Properties/AssemblyInfo.cs;X4000 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationManagementSensitiveDBAccess/Properties/AssemblyInfo.Internals.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationMgtSensitiveDBAccess.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationMgtSensitiveDBAccess.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationMgtSensitiveDBAccess.UnitTests/AuthorisationManagementSensitiveDatabaseAccessTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationMgtSensitiveDBAccess.UnitTests/AuthorisationMgtSensitiveDBAccess.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationMgtSensitiveDBAccess.UnitTests/AuthorisationMgtSensitiveDBAccess.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationMgtSensitiveDBAccess.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationMgtSensitiveDBAccess.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationMgtSensitiveDBAccess.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDataAccessWcfWebsite merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDataAccessWcfWebsite/AuthorisationSensitiveDataAccess.svc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDataAccessWcfWebsite/AuthorisationSensitiveDataAccessWcfWebsite.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDataAccessWcfWebsite/AuthorisationSensitiveDataAccessWcfWebsite.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDataAccessWcfWebsite/dummy.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDataAccessWcfWebsite/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDataAccessWcfWebsite/Web.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDatabaseAccess merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDatabaseAccess/AuthorisationSensitiveDatabaseAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDatabaseAccess/AuthorisationSensitiveDatabaseAccess.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDatabaseAccess/AuthorisationSensitiveDatabaseAccess.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDatabaseAccess/IAuthorisationSensitiveDatabaseAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDatabaseAccess/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDatabaseAccess/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDatabaseAccess/Properties/AssemblyInfo.cs;X3996 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDatabaseAccess/Properties/AssemblyInfo.Internals.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDBAccess.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDBAccess.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDBAccess.UnitTests/AuthorisationSensitiveDatabaseAccessTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDBAccess.UnitTests/AuthorisationSensitiveDBAccess.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDBAccess.UnitTests/AuthorisationSensitiveDBAccess.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDBAccess.UnitTests/AuthorisationSerializers.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDBAccess.UnitTests/Helpers merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDBAccess.UnitTests/Helpers/TestingHelpers.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDBAccess.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDBAccess.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDBAccess.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDBAccess.UnitTests/TestData.Designer.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationSensitiveDBAccess.UnitTests/TestData.resx merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationServicePublic.cd merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationWcfWebsite merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationWcfWebsite/Authorisation.svc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationWcfWebsite/AuthorisationWcfWebsite.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationWcfWebsite/AuthorisationWcfWebsite.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationWcfWebsite/dummy.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationWcfWebsite/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/AuthorisationWcfWebsite/Web.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Entitities merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Entitities/AccessControlPolicy Source merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Entitities/AccessControlPolicy Source/AccessControlPolicy.xml merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Entitities/AccessControlPolicy Source/AccessControlPolicy.xsd merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Entitities/AccessControlPolicy.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Entitities/AccessControlPolicyCommonEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Entitities/AccessControlPolicyRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Entitities/IAuditableRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Entitities/PermissionContextDataEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Entitities/UserAuthorisationRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Entitities/UserPermissionEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Entitities/UserPermissionEntityComparer.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Entitities/UserPermissionsEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Helpers merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Helpers/AccessControlPolicyReader.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Helpers/StringHelpers.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/IAccessControlPolicyService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/IAuthorisationService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Internal merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Internal/AccessControlPolicyDataRequest.cs merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Internal/AccessControlPolicyManagement.cs merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Internal/AuthorisationAuditing.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Internal/Builders merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Internal/Builders/AuthorisationTokenBuilder.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Internal/Builders/UserPermissionBuilder.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Internal/IAccessControlPolicyManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Internal/IAuthorisationAuditing.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Internal/IAuthorisationDataRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Internal/IResourceAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Internal/IUserAuthorisation.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Internal/ObjectIdComparer.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Internal/ResourceAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Internal/UserAuthorisation.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/MgmtSensitiveDataAccessWcfWebsite merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/MgmtSensitiveDataAccessWcfWebsite/AuthMgmtSensitiveDataAccess.svc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/MgmtSensitiveDataAccessWcfWebsite/dummy.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/MgmtSensitiveDataAccessWcfWebsite/MgmtSensitiveDataAccessWcfWebsite.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/MgmtSensitiveDataAccessWcfWebsite/MgmtSensitiveDataAccessWcfWebsite.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/MgmtSensitiveDataAccessWcfWebsite/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/MgmtSensitiveDataAccessWcfWebsite/Web.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Properties/AssemblyInfo.cs;X3998 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Properties/AssemblyInfo.Internals.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Service References merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Service References/AuthorisationSdaService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Authorisation/Service References/AuthorisationSdaService.map merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification.UnitTests/Helpers merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification.UnitTests/Helpers/TestingHelpers.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification.UnitTests/Identification.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification.UnitTests/Identification.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification.UnitTests/IdentificationAuditingTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification.UnitTests/IdentificationServiceFactoryTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification.UnitTests/IdentificationServiceTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Entities merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Entitities merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Entitities/GetIdentityResponse.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Entitities/GetUserLoginInfoResponse.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Entitities/IAuditableRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Entitities/IdentityRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Entitities/LogInState.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Entitities/UserIdentityRelationEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Identification.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Identification.csproj merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/IdentificationService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/IdentificationServiceFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/IIdentificationService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Internal merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Internal/IdentificationAuditing.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Internal/IIdentificationAuditing.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Internal/IResourceAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Internal/ResourceAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Properties/AssemblyInfo.cs;X4005 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Properties/AssemblyInfo.Internals.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Service References merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Service References/IdentificationSdaService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/Identification/Service References/IdentificationSdaService.map merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDatabaseAccess merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDatabaseAccess/IdentificationSensitiveDatabaseAccess.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDatabaseAccess/IdentificationSensitiveDatabaseAccess.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDatabaseAccess/IdentificationSensitiveDatabaseAccesss.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDatabaseAccess/IdentificationSensitiveDatabaseAccessServiceFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDatabaseAccess/IIdentificationSensitiveDatabaseAccesss.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDatabaseAccess/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDatabaseAccess/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDatabaseAccess/Properties/AssemblyInfo.cs;X4006 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDatabaseAccess/Properties/AssemblyInfo.Internals.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDBAccess.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDBAccess.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDBAccess.UnitTests/Helpers merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDBAccess.UnitTests/Helpers/TestingHelpers.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDBAccess.UnitTests/IdentificationSensitiveDBAccess.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDBAccess.UnitTests/IdentificationSensitiveDBAccess.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDBAccess.UnitTests/IdentificationsSensitiveDBAccessTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDBAccess.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDBAccess.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/IdentificationSensitiveDBAccess.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/SensitiveDataAccessWcfWebsite merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/SensitiveDataAccessWcfWebsite/dummy.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/SensitiveDataAccessWcfWebsite/IdentificationSensitiveDataAccess.svc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/SensitiveDataAccessWcfWebsite/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/SensitiveDataAccessWcfWebsite/SensitiveDataAccessWcfWebsite.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/SensitiveDataAccessWcfWebsite/SensitiveDataAccessWcfWebsite.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/AccessControl/Identification/SensitiveDataAccessWcfWebsite/Web.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/ImportLegacyMaterial merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/ImportLegacyMaterial/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/ImportLegacyMaterial/ImportLegacyMaterial.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/ImportLegacyMaterial/ImportLegacyMaterial.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/ImportLegacyMaterial/LegacyDataAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/ImportLegacyMaterial/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/ImportLegacyMaterial/Program.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/ImportLegacyMaterial/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/ImportLegacyMaterial/Properties/AssemblyInfo.cs;X4001 merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/ImportLegacyMaterial/Service References merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/ImportLegacyMaterial/Service References/MaterialStoreService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/ImportLegacyMaterial/Service References/MaterialStoreService.map merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/ImportLegacyMaterial/Service References/MaterialStoreServiceDataContractCtors.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/ImportLegacyMaterial/SetupLegacyKeys.cmd merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore.UnitTests/MaterialStore.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore.UnitTests/MaterialStore.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore.UnitTests/MaterialStoreTest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore.UnitTests/ResourceAccessFactoryMockTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore.UnitTests/Service References merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore.UnitTests/Service References/SensitiveDatabaseAccessService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore.UnitTests/Service References/SensitiveDatabaseAccessService.map merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore.xml merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Cipher.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/CipherSettings.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/CryptographyType.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Entities merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Entities/HashMaterialEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Entities/HashMaterialSetEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Entities/KeyMaterialEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Entities/KeyMaterialSetEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Entities/MaterialEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Entities/MaterialSetEntity.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/AbstractMaterialGenerator.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/AbstractMaterialStoreFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/AbstractMessageEntityGenerator.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/AbstractResourceAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/BaseMaterialGenerator.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/CryptographicParameter.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/HashMaterialGenerator.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/HashMaterialStoreFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/HashMessageEntityGenerator.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/HashParameter.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/HashResourceAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/LegacyMaterialGenerator.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/SymmetricMaterialGenerator.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/SymmetricMaterialStoreFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/SymmetricMessageEntityGenerator.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/SymmetricParameter.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Factory/SymmetricResourceAccess.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/HashAlgorithmType.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/IMaterialStore.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Internal merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Internal/Auditing merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Internal/Auditing/AuditMessage.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Internal/Auditing/MaterialStoreAuditor.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Internal/Auditing/MaterialStoreEventArgs.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Internal/Auditing/MaterialStoreEventHandlers.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Internal/MaterialStoreSettings.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MaterialStore.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MaterialStore.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MaterialStore.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MaterialStore.reg merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MaterialStoreSettings.tesco.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MaterialStoreSettings.tesco merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MessageEntities merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MessageEntities/GetHashMaterialRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MessageEntities/GetHashMaterialResponse.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MessageEntities/GetHashMaterialSetRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MessageEntities/GetHashMaterialSetResponse.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MessageEntities/GetKeyMaterialRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MessageEntities/GetKeyMaterialResponse.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MessageEntities/GetKeyMaterialSetRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MessageEntities/GetKeyMaterialSetResponse.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MessageEntities/GetMaterialRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MessageEntities/GetMaterialResponse.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MessageEntities/GetMaterialSetRequest.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/MessageEntities/GetMaterialSetResponse.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Properties/AssemblyInfo.cs;X4017 merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Properties/AssemblyInfo.Internals.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Service References merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Service References/SensitiveDatabaseAccessService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/Service References/SensitiveDatabaseAccessService.map merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStore/SymmetricAlgorithmType.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStoreAppPool.xml merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStoreSensitiveDatabaseAccess.xml merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStoreSensitiveDatabaseAccessAppPool.xml merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStoreWCFWebsite merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStoreWCFWebsite/dummy.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStoreWCFWebsite/MaterialStore.svc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStoreWCFWebsite/MaterialStoreWCFWebsite.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStoreWCFWebsite/MaterialStoreWCFWebsite.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStoreWCFWebsite/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/MaterialStoreWCFWebsite/Web.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess.UnitTests/SensitiveDatabaseAccess.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess.UnitTests/SensitiveDatabaseAccess.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess.UnitTests/SensitiveDatabaseAccessTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess/DatabaseAccessService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess/IDatabaseAccessService.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess/Properties/AssemblyInfo.cs;X4004 merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess/SensitiveDatabaseAccess.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccess/SensitiveDatabaseAccess.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccessWCFWebsite merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccessWCFWebsite/dummy.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccessWCFWebsite/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccessWCFWebsite/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccessWCFWebsite/Properties/AssemblyInfo.cs;X4003 merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccessWCFWebsite/SensitiveDatabaseAccess.svc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccessWCFWebsite/SensitiveDatabaseAccessWCFWebsite.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccessWCFWebsite/SensitiveDatabaseAccessWCFWebsite.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/Security/KeyManagement/MaterialStore/SensitiveDatabaseAccessWCFWebsite/Web.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices/StateRegisterSvc.Host.IIS merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices/StateRegisterSvc.Host.IIS/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices/StateRegisterSvc.Host.IIS/Properties/AssemblyInfo.cs;X4008 merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices/StateRegisterSvc.Host.IIS/StateRegister.svc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices/StateRegisterSvc.Host.IIS/StateRegisterSvc.Host.IIS.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices/StateRegisterSvc.Host.IIS/StateRegisterSvc.Host.IIS.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices/StateRegisterSvc.Host.IIS/Web.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices/StateRegisterSvc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices/StateRegisterSvc/app.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices/StateRegisterSvc/IStateRegisterSvc.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices/StateRegisterSvc/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices/StateRegisterSvc/Properties/AssemblyInfo.cs;X4018 merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices/StateRegisterSvc/StateRegisterSvc.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices/StateRegisterSvc/StateRegisterSvc.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Services/StateServices/StateRegisterSvc/StateRegisterSvc.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/app.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/IStateRegister.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Properties/AssemblyInfo.cs;X4013 merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/RegisterItem.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/SecuredRegisterItem.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/SecureStateRegister.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Service References merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Service References/StateRegisterSvc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Service References/StateRegisterSvc/configuration.svcinfo merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Service References/StateRegisterSvc/configuration91.svcinfo merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Service References/StateRegisterSvc/Reference.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Service References/StateRegisterSvc/Reference.svcmap merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Service References/StateRegisterSvc/StateRegister.disco merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Service References/StateRegisterSvc/StateRegister.xsd merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Service References/StateRegisterSvc/StateRegister1.disco merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Service References/StateRegisterSvc/StateRegister1.xsd merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Service References/StateRegisterSvc/StateRegister2.xsd merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Service References/StateRegisterSvc/StateRegister21.xsd merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Service References/StateRegisterSvc/StateRegisterSvc.wsdl merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/Service References/StateRegisterSvc/StateRegisterSvc1.wsdl merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/State.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/State.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/StateRegister.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/State/StateRegistryFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/AutoIncrementingCredential.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Credential.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/CredentialCollection.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/CredentialName.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Internal merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Internal/IdentificationTokenBuilderFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Internal/ITokenBuilder.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Internal/TokenBuilder.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Internal/TokenValidationResult.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/ITokenFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/ITokenManagementProvider.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Properties merge, delete $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Properties/AssemblyInfo.cs;X4002 merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/SiteTokenManagement.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/TimerCredential.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/TokenFactory.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/TokenGroupValidationResult.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.csproj.vspscc merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests/AuthorisationTokenTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests/AutoIncrementingCredentialTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests/CredentialCollectionTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests/CredentialTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests/ForgotMyPasswordTokenTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests/IdentificationTokenTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests/SecurityTokenTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests/Tokens.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests/Tokens.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests/TokenTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens.UnitTests/TracerTokenTests.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens/AuthorisationToken.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens/BTCToken.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens/ForgotMyPasswordToken.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens/IAuthorisationToken.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens/IBTCToken.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens/IdentificationToken.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens/IForgotMyPasswordToken.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens/IIdentificationToken.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens/ISecurityToken.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens/IToken.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens/ITracerToken.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens/LegacyIdentificationToken.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens/SecurityToken.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens/Token.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/Tokens/TracerToken.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Tokens/wcfclientendpoints.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/WcfClients merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/WcfClients/readme.txt merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/WcfClients/wcfclientendpoints.config merge, branch $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/WcfClients/wcfclientendpoints2.config merge, edit $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/WcfClients/wcfstandardbinding.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/WcfWebsite merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/WcfWebsite/AppPool.xml merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/WcfWebsite/ImportWcfWebsite.bat merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/WcfWebsite/keepalive.htm merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/WcfWebsite/readme.txt merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/WcfWebsite/RemoveWcfWebsite.bat merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/WcfWebsite/WcfWebsite.xml merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/WcfWebsite/WcfWebsite_pre-refactor.xml merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/WcfWebsite/Web.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Workflow.UnitTests merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Workflow.UnitTests/App.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Workflow.UnitTests/AuthoringTests.txt merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Workflow.UnitTests/GroceryWeb.tesco.config merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Workflow.UnitTests/GroceryWeb.tesco merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Workflow.UnitTests/mssccprj.scc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Workflow.UnitTests/Properties merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Workflow.UnitTests/Properties/AssemblyInfo.cs merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Workflow.UnitTests/Workflow.UnitTests.csproj.vspscc merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Workflow.UnitTests/Workflow.UnitTests.csproj merge $/Tesco_Dotcom/Dev/r2/Tesco/Com/Framework/Workflow.UnitTests/WorkflowTest.cs Check-in Notes: Code Reviewer: Performance Reviewer: Security Reviewer: ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/commands/issue-4184.txt ================================================ ------------------------------------------------------------------------------- Changeset: 5516 User: Lo Date: Monday, 10 August 2009 5:17:36 p.m. Comment: Items: edit $/LD.Tracker/Common/LD.Tracker.Core/Entities/TrackerType.cs Check-in Notes: Code Reviewer: Performance Reviewer: Security Reviewer: ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/commands/issue-4943.txt ================================================ ------------------------------------------------------------------------------- Changeset: 45212 User: USERA Checked in by: USERB Date: Tuesday, November 24, 2009 10:59:13 AM Comment: Sample checkin with /author Items: edit $/PROJECT/PATH/TO/FILE.txt Policy Warnings: Override Reason: XXXX XXXXXXX XX XXXXX Messages: XXXXXXXX XXXXX XX XXXXXXXX XXXXXXX XXXX. XXXXXXXX XXXXX XX XXXXXXXX XX XXXXXXX. ------------------------------------------------------------------------------- Changeset: 15005 User: USERC Date: Monday, April 06, 2009 8:29:28 AM Comment: Sample normal checkin (without /author) Items: edit $/PROJECT/PATH/TO/FILE.txt Check-in Notes: XXXXXXX XXXX: XXXXXXX XXXXXX XXXX XXXXXXXXXXX: XXXXXX, XXXX; XXXXXXX XXXXX XXX: X XXXXXXXX XXXXXXXXX: XX XXXXXXXXX: ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/commands/issue-6454.txt ================================================ Changeset: 146266 User: hschneider Checked in by: vsts-demon Date: Saturday, May 01, 2010 12:17:34 AM Comment: 4/22/2010 3:56:15 PM: Recycling mode disabled; Test changed (Updated by TFS to TFS Connector, server: 192.168.1.208:1666, id 521906. ) Items: edit $/source/Dev/playback/pb_playback/Olymp/LowLevelTests/FileScannerTest.cs edit $/source/Dev/playback/pb_playback/Olymp/MainApplication/Panels/VirtualizingGroupingItemsPanel.cs ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/commands/issue-6870-2.txt ================================================ Changeset: 24 User: GRP\ObendorfH Date: 06.05.2010 13:29:24 Comment: Initialer Check-In Items: add $/TFSJavaBuild/Sources/Maven/DEV/com.haufelexware.project.base add $/TFSJavaBuild/Sources/Maven/DEV/com.haufelexware.project.base/.project add $/TFSJavaBuild/Sources/Maven/DEV/com.haufelexware.project.base/.settings add $/TFSJavaBuild/Sources/Maven/DEV/com.haufelexware.project.base/.tpignore add $/TFSJavaBuild/Sources/Maven/DEV/com.haufelexware.project.base/pom.xml add $/TFSJavaBuild/Sources/Maven/DEV/com.haufelexware.project.base/src add $/TFSJavaBuild/Sources/Maven/DEV/com.haufelexware.project.base/.settings/org.maven.ide.eclipse.prefs add $/TFSJavaBuild/Sources/Maven/DEV/com.haufelexware.project.base/src/site Check-in Notes: Code Reviewer: Performance Reviewer: Security Reviewer: ----------------------------------------------------------------------------------------------------------------------- Changeset: 23 User: GRP\ObendorfH Date: 06.05.2010 13:28:21 Comment: Items: branch $/TFSJavaBuild/Sources/Maven/Current Check-in Notes: Code Reviewer: Performance Reviewer: Security Reviewer: ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/commands/issue-6870.txt ================================================ Changeset: 364235 User: GRP\ObendorfH Date: 28.06.2010 17:25:40 Comment: Versionierung fr EJB EAR Items: edit $/Services/Server/DEV/SP/MavenDefs/pom.xml Check-in Notes: Code Reviewer: Performance Reviewer: Security Reviewer: ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/commands/tf-changeset-1.txt ================================================ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12472 User: RNO\_MCLWEB Date: 2008-jun-27 11:16:06 Comment: Created team project folder $/tfsandbox via the Team Project Creation Wizard Items: add $/tfsandbox ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/commands/tf-changeset-2.txt ================================================ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12492 User: SND\redsolo_cp Date: 2008-jun-27 13:11:15 Comment: first file Items: add $/tfsandbox/readme.txt -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12472 User: RNO\_MCLWEB Date: 2008-jun-10 11:16:06 Comment: Created team project folder $/tfsandbox via the Team Project Creation Wizard Items: add $/tfsandbox ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/commands/tf-changeset-3.txt ================================================ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12495 User: SND\redsolo_cp Date: 2008-jun-27 13:21:25 Comment: changed and created one Items: edit $/tfsandbox/readme.txt add $/tfsandbox/readme2.txt -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12493 User: SND\redsolo_cp Date: 2008-jun-27 13:19:41 Comment: changed and created one Items: edit $/tfsandbox/readme.txt -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12492 User: SND\redsolo_cp Date: 2008-jun-27 13:11:15 Comment: first file Items: add $/tfsandbox/readme.txt -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12472 User: RNO\_MCLWEB Date: 2008-jun-27 11:16:06 Comment: Created team project folder $/tfsandbox via the Team Project Creation Wizard Items: add $/tfsandbox ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/commands/tf-changeset-german-1.txt ================================================ ------------------------------------------------------------------------------- Changeset: 302 Benutzer: georg Datum: Freitag, 15. Juli 2011 16:24:59 Kommentar: Elemente: bearbeiten $/InvestInform/commons/Utils14/src/main/java/de/safir/api/types/ApiSafirBool.java Eincheckhinweise: Codebearbeiter: Leistungsbearbeiter: Sicherheitsbearbeiter: ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/commands/tf-workfold-list.txt ================================================ =========================================================================================================================================== Workspace: workspaceOne (username_cp) Server : https://tfs09.codeplex.com $/tfshudsonplugin: C:\tfshudsonplugin_2\workfolder1 $/tfshudsonplugin/folder: C:\tfshudsonplugin_3\workfolder1 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/git.push-sample.json ================================================ { "id": "03c164c2-8912-4d5e-8009-3707d5f83734", "eventType": "git.push", "publisherId": "tfs", "scope": "all", "message": { "text": "Jamal Hartnett pushed updates to branch master of repository Fabrikam-Fiber-Git.", "html": "Jamal Hartnett pushed updates to branch master of repository Fabrikam-Fiber-Git.", "markdown": "Jamal Hartnett pushed updates to branch `master` of repository `Fabrikam-Fiber-Git`." }, "detailedMessage": { "text": "Jamal Hartnett pushed 1 commit to branch master of repository Fabrikam-Fiber-Git.\n - Fixed bug in web.config file 33b55f7c", "html": "Jamal Hartnett pushed 1 commit to branch master of repository Fabrikam-Fiber-Git.\n
    \n
  • Fixed bug in web.config file 33b55f7c\n
", "markdown": "Jamal Hartnett pushed 1 commit to branch [master](https://fabrikam-fiber-inc.visualstudio.com/_git/Fabrikam-Fiber-Git/#version=GBmaster) of repository [Fabrikam-Fiber-Git](https://fabrikam-fiber-inc.visualstudio.com/_git/Fabrikam-Fiber-Git/).\n* Fixed bug in web.config file [33b55f7c](https://fabrikam-fiber-inc.visualstudio.com/_git/Fabrikam-Fiber-Git/commit/33b55f7cb7e7e245323987634f960cf4a6e6bc74)" }, "resource": { "commits": [ { "commitId": "33b55f7cb7e7e245323987634f960cf4a6e6bc74", "author": { "name": "Jamal Hartnett", "email": "fabrikamfiber4@hotmail.com", "date": "2015-02-25T19:01:00Z" }, "committer": { "name": "Jamal Hartnett", "email": "fabrikamfiber4@hotmail.com", "date": "2015-02-25T19:01:00Z" }, "comment": "Fixed bug in web.config file", "url": "https://fabrikam-fiber-inc.visualstudio.com/_git/Fabrikam-Fiber-Git/commit/33b55f7cb7e7e245323987634f960cf4a6e6bc74" } ], "refUpdates": [ { "name": "refs/heads/master", "oldObjectId": "aad331d8d3b131fa9ae03cf5e53965b51942618a", "newObjectId": "33b55f7cb7e7e245323987634f960cf4a6e6bc74" } ], "repository": { "id": "278d5cd2-584d-4b63-824a-2ba458937249", "name": "Fabrikam-Fiber-Git", "url": "https://fabrikam-fiber-inc.visualstudio.com/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249", "project": { "id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c", "name": "Fabrikam-Fiber-Git", "url": "https://fabrikam-fiber-inc.visualstudio.com/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c", "state": "wellFormed" }, "defaultBranch": "refs/heads/master", "remoteUrl": "https://fabrikam-fiber-inc.visualstudio.com/_git/Fabrikam-Fiber-Git" }, "pushedBy": { "id": "00067FFED5C7AF52@Live.com", "displayName": "Jamal Hartnett", "uniqueName": "Windows Live ID\\fabrikamfiber4@hotmail.com" }, "pushId": 14, "date": "2014-05-02T19:17:13.3309587Z", "url": "https://fabrikam-fiber-inc.visualstudio.com/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249/pushes/14" }, "resourceVersion": "3.0-preview.1", "resourceContainers": { "collection": { "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2", "baseUrl": "https://fabrikam-fiber-inc.visualstudio.com/" }, "account": { "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e", "baseUrl": "https://fabrikam-fiber-inc.visualstudio.com/" }, "project": { "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f", "baseUrl": "https://fabrikam-fiber-inc.visualstudio.com/" } }, "createdDate": "2016-06-10T16:32:21.0569909Z" } ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/util/XmlHelperTest/peekValue_File/input.xml ================================================ Hudson-oldPollingFallback-MASTER . $/FunctionalTests/TeamFoundationServerScmFunctionalTest/oldPollingFallback http://${tfs_server_name}:8080/tfs/jenkins-tfs-plugin true 76 1436542800239 1436542800244 FAILURE 134 windows-1252 false oldPollingFallback/workspace 1.448 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/util/XmlHelperTest/pokeValue_File/expected.xml ================================================ Hudson-oldPollingFallback-MASTER . $/FunctionalTests/TeamFoundationServerScmFunctionalTest/oldPollingFallback http://${tfs_server_name}:8080/tfs/jenkins-tfs-plugin true 76 42 1436542800244 FAILURE 134 windows-1252 false oldPollingFallback/workspace 1.448 ================================================ FILE: tfs/src/test/resources/hudson/plugins/tfs/util/XmlHelperTest/pokeValue_File/input.xml ================================================ Hudson-oldPollingFallback-MASTER . $/FunctionalTests/TeamFoundationServerScmFunctionalTest/oldPollingFallback http://${tfs_server_name}:8080/tfs/jenkins-tfs-plugin true 76 1436542800239 1436542800244 FAILURE 134 windows-1252 false oldPollingFallback/workspace 1.448 ================================================ FILE: tfs/src/test/resources/tf-get.log ================================================ C:\Users\Paulina\Projects\tfs\tfsandbox2: Getting tfsandbox2 Getting readme.txt Getting readme2.txt ================================================ FILE: tfs/src/test/resources/tf-history.log ================================================ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12495 User: SND\redsolo_cp Date: 2008-jun-27 13:21:25 Comment: changed and created one WITH TWO Items: edit $/tfsandbox/readme.txt add $/tfsandbox/readme2.txt -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12493 User: SND\redsolo_cp Date: 2008-jun-27 13:19:41 Comment: changed and created one Items: edit $/tfsandbox/readme.txt -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12492 User: SND\redsolo_cp Date: 2008-jun-27 13:11:15 Comment: first file Items: add $/tfsandbox/readme.txt -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Changeset: 12472 User: RNO\_MCLWEB Date: 2008-jun-27 11:16:06 Comment: Created team project folder $/tfsandbox via the Team Project Creation Wizard Items: add $/tfsandbox ================================================ FILE: tfs/src/test/resources/tf-workfold.log ================================================ ================================================ FILE: tfs/src/test/resources/tf-workspace.log ================================================ Workspace 'asterix2' created. ================================================ FILE: tfs/src/test/resources/tf-workspaces.log ================================================ Server: https://tfs02.codeplex.com/ Workspace Owner Computer Comment --------- -------------- -------- ---------------------------------------------------------------------------------------------------------- asterix2 SND\redsolo_cp ASTERIX astreix SND\redsolo_cp ASTERIX ================================================ FILE: tfs/src/test/resources/tf.bat ================================================ @ECHO OFF IF "%1" == "" GOTO HELP REM REM Determine what command to fake set command-output="%~dp0tf-%1.log" set command=%1 REM REM Find the last argument SETLOCAL SHIFT :LOOP IF {%1}=={} GOTO FOUND SET LAST=%1 SHIFT GOTO :LOOP :FOUND ENDLOCAL&SET LAST=%LAST% IF "%command%" == "mkview" GOTO MKVIEW IF "%command%" == "setcs" GOTO END GOTO PRINT-OUTPUT :MKVIEW mkdir %LAST% :PRINT-OUTPUT IF NOT EXIST %command-output% goto UNKNOWN-COMMAND type %command-output% set errorlevel = 0 GOTO END :UNKNOWN-COMMAND ECHO Unknown command %1, %command-output% set errorlevel = 2 GOTO END :HELP echo Usage: tf [command] [ignored arguments] set errorlevel = 1 GOTO END :END exit /b %errorlevel% ================================================ FILE: tfs/src/test/resources/tf.sh ================================================ #!/bin/sh # # This shell script will cat files named tf-[command].log to the stdout. # # if [ $# = 0 ]; then echo 1>&2 Usage: tf [command] [ignored arguments] exit 1 fi commandfile=`dirname $0`/tf-$1.log case $1 in workfold) # eval last_arg=\$$# mkdir $4 ;; setcs) exit 0 ;; esac if [ -r $commandfile ] then cat $commandfile exit 0 else echo Unknown command $1, $commandfile exit 1 fi ================================================ FILE: tfs-sdk/pom.xml ================================================ 4.0.0 org.jenkins-ci.plugins tfs-parent 5.157.1-SNAPSHOT tfs-sdk TFS SDK installer pom org.apache.maven.plugins maven-install-plugin 2.5.2 install.tfs.sdk.jar.locally validate install-file src/com.microsoft.tfs.sdk-14.0.3.jar com.microsoft.tfs.sdk com.microsoft.tfs.sdk 14.0.3 jar true ================================================ FILE: tfs-sdk/src/com.microsoft.tfs.sdk-14.0.3.jar ================================================ [File too large to display: 18.0 MB]