为TFS集成平台配置电源外壳适配器


我们有一个客户非常非常希望将源代码从team Foundation Server(TFS)提供给Perforce。为什么你可能会问…嗯,对于许多公司来说,他们不能只是迁移,他们要么需要某种形式的安全网,要么有一个外部要求,他们必须使用特定的系统作为记录。

但他们真的很想使用TFS。


我们必须有某种方法来按顺序从TFS签入,并定期将它们写入另一个系统,而不必为每个新系统编写适配器。这显然不是一个完美的方案,因为它不会直接为另一个系统量身定做,但它应该足以满足我将遇到的90%的情况。

《理论》

我编写的TFS集成平台适配器非常简单,关注的是适应性而不是完美性。这个…最好的工具是什么当然是PowerShell了Smile,而且由于Team Foundation Adapter已经在包装盒中编写并提供,我们只需担心另一面。

如果您想要在集成平台中写作,而不是阅读我最近did for Test Track Pro,然后将重点放在IMgrationProvider接口上,该接口允许您实现ProcessChangeGroup方法。

Public Overrides Function ProcessChangeGroup(changeGroup As Microsoft.TeamFoundation.Migration.Toolkit.ChangeGroup) As Microsoft.TeamFoundation.Migration.Toolkit.ConversionResult
    Dim conversionResult As New ConversionResult(_configurationService.MigrationPeer, _configurationService.SourceId)
    conversionResult.ChangeId = changeGroup.ChangeGroupId
    Dim OutDirectory As String = Me._configurationService.MigrationSource.SourceIdentifier
    Dim SourceRoot As String = Me._configurationService.Filters(0).Path
    TraceManager.TraceInformation(String.Format("Processing {0} actions - {1}", changeGroup.Actions.Count, changeGroup.Comment))
    If Not RunPowershell("ImportChangeGroup_Initial", changeGroup, conversionResult, OutDirectory) Then
        conversionResult.ChangeId = String.Empty
    End If
    For Each action As MigrationAction In changeGroup.Actions
        Dim path As String = ConvertPath(OutDirectory, SourceRoot, action.Path)
        If BuildLocalCache(changeGroup, conversionResult, SourceRoot, OutDirectory, path, action) Then
            conversionResult.ItemConversionHistory.Add(New ItemConversionHistory(changeGroup.Name, action.Version, path, String.Empty))
        End If
    Next
    If Not RunPowershell("ImportChangeGroup_Final", changeGroup, conversionResult, OutDirectory) Then
        conversionResult.ChangeId = String.Empty
    End If
    Return conversionResult
End Function

对于在TFS中找到的每个更改组,集成平台都会调用此方法。该平台将跟踪您已经完成了哪些更改,以及需要按什么顺序执行哪些类型的更改。我们所要做的就是对它们执行一些操作,并传回一个ConversionResult对象,该对象详细说明了属于变更组的每个操作的ItemConversionHistory对象。

您可以从上面的代码中看到,我在过程的开始和结束时都调用了PowerShell脚本。您在这里看不到它,但是也会为每个操作调用PowerShell。在此过程中,我们构建了我们所做工作的记录,并将完成的ConversionResult传递回TFS集成平台,以便它可以检查列表中我们已处理的所有项目。

为了增加系统的灵活性,我允许为每个contentType和ChangeAction组合调用不同的PowerShell:

内容类型 动作  
受版本控制的文件夹 增加  
受版本控制的文件夹 编辑  
受版本控制的文件夹 删除  
受版本控制的文件夹 重命名  
版本控制文件 增加  
版本控制文件 编辑  
版本控制文件 删除  
版本控制文件 重命名  

图:支持的操作

在Adapter的配置中,我添加了一组键,这些键转换为上面的每个条目,允许您配置要使用的脚本。如果您想使用或只使用一个,您可以全部使用它们。

<CustomSettings>
  <!--
    Powershell Arguments
    $IpChangeGroup = Microsoft.TeamFoundation.Migration.Toolkit.ChangeGroup
    $IpConversionResult = Microsoft.TeamFoundation.Migration.Toolkit.ConversionResult
    $IpMigrationAction = Microsoft.TeamFoundation.Migration.Toolkit.MigrationAction
    $IpNewPath = D:\DataPerforce\localhost\1666\ImportRun1\{localpath}
    $IpLocalRoot = D:\DataPerforce\localhost\1666\ImportRun1

    For Powershell files to import for perticular clients, please refer to:

    Perforce :
  -->
  <CustomSetting SettingKey="ImportChangeGroup_Initial" SettingValue="D:\DataPerforce\localhost\PerforceInitilise.ps1" />
  <CustomSetting SettingKey="ImportChangeGroupAction_VersionControlledFolder_Add" SettingValue="" />
  <CustomSetting SettingKey="ImportChangeGroupAction_VersionControlledFolder_Edit" SettingValue="" />
  <CustomSetting SettingKey="ImportChangeGroupAction_VersionControlledFolder_Delete" SettingValue="" />
  <CustomSetting SettingKey="ImportChangeGroupAction_VersionControlledFolder_Rename" SettingValue="" />
  <CustomSetting SettingKey="ImportChangeGroupAction_VersionControlledFile_Add" SettingValue="" />
  <CustomSetting SettingKey="ImportChangeGroupAction_VersionControlledFile_Edit" SettingValue="" />
  <CustomSetting SettingKey="ImportChangeGroupAction_VersionControlledFile_Rename" SettingValue="" />
  <CustomSetting SettingKey="ImportChangeGroup_Final" SettingValue="D:\DataPerforce\localhost\PerforceCommit.ps1" />
</CustomSettings>

图:配置PowerShell文件的自定义设置

正如上面的参数列表可能会回避的那样,我将其中一些参数传递给PowerShell,这些参数对配置选项的人应该很有用。使用PowerShell的真正好处之一是,您可以传入复杂的对象图,并将其提供给PowerShell用户。您也可以再次取出对象,但在这种情况下,我只需要进去。

Public Function ExecutePowershell(powerShellKey As String, changeGroup As ChangeGroup, conversionResult As ConversionResult, localRoot As String, Optional action As MigrationAction = Nothing, Optional newPath As String = "") As List(Of PSObject)
    _pipeline = _runspace.CreatePipeline()
    Dim psFile As String = GetPowerShellFile(powerShellKey)
    Dim results As New List(Of PSObject)
    If File.Exists(psFile) Then
        Dim command As New Command(psFile)
        command.Parameters.Add(New CommandParameter("IpChangeGroup", changeGroup))
        command.Parameters.Add(New CommandParameter("IpConversionResult", conversionResult))
        If Not action Is Nothing Then
            command.Parameters.Add(New CommandParameter("IpMigrationAction", action))
            command.Parameters.Add(New CommandParameter("IpNewPath", action))
        End If
        command.Parameters.Add(New CommandParameter("IpLocalRoot", localRoot))
        command.MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output)
        _pipeline.Commands.Add(command)
        Dim dt As DateTime = Now
        results.AddRange(_pipeline.Invoke())
    Else
        TraceManager.TraceWarning(String.Format("PowerShell: DOES NOT EXIST for custom setting '{0}'", powerShellKey))
    End If
    Return results
End Function

图:带参数从VB.NET调用PowerShell

这意味着您可以访问TFS中原始更改组以及签入人员的备注等内容。

p4 commit -d $IpChangeGroup.Comment

图:PowerShell签入所有带有原始注释的未完成文件

实践中的实践

要运行此适配器,您需要在某个位置安装并运行TFS Integration Platform。我建议使用TFS服务器本身,但是如果您对此有问题,那么可以找另一台服务器。

TFS Integration Platform可以使用UI运行,因此我们将使用UI来配置它,但它也可以从命令行运行。如果您希望此工具在“一次性”模式以外的模式下运行,那么您将需要安装该服务。即使没有人登录,这也将运行同步。

SNAGHTMLca8aa6

图:如果要同步,请安装该服务

一旦您安装了它,您将需要一个配置文件作为模板。这个文件可以非常宽松,也可以非常严格地限制您可以选择的内容。取决于您现成安装的适配器将取决于您获得的模板。您可以仅具有工作项跟踪、仅具有版本控制或其组合。

在本例中,我们将使用我的魔术配置文件:

<?xml version="1.0" encoding="utf-16"?>
<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               UniqueId="76E320D8-914C-4E5D-9C3A-014A5E582462"
               FriendlyName="NWC - TFS to Powershell">
  <Providers>
    <Provider ReferenceName="566C001E-E476-4A07-8447-B2284C55A20E" FriendlyName="Powershell" />
    <Provider ReferenceName="FEBC091F-82A2-449e-AED8-133E5896C47A" FriendlyName="TFS 2010 Migration VC Provider" />
  </Providers>
  <Addins>
    <Addin ReferenceName="cdde6b6b-72fc-43b6-bbd1-b8a89a788c6f"
          FriendlyName="TFS Active Directory User Id Lookup Service Addin" />
  </Addins>
  <SessionGroup CreationTime="2011-04-14T14:59:58.72-07:00"
                FriendlyName="NWC - TFS to Powershell"
                SessionGroupGUID="B5883A26-5151-4ED3-9F3F-75EFF31E5CBE"
                Creator="MrHinsh"
                SyncIntervalInSeconds="160"
                SyncDurationInMinutes="0">
    <MigrationSources>
      <MigrationSource InternalUniqueId="A3E4BC8C-B1C3-4A8E-B18A-3066FF861E33"
                       FriendlyName="nwc (vc)"
                       ServerIdentifier="346dd524-18b0-4eda-ba05-0f4d6b14e076"
                       ServerUrl="http://tfs.mycompany.com:8080/tfs/DefaultCollection"
                       SourceIdentifier="LabDemo"
                       ProviderReferenceName="FEBC091F-82A2-449e-AED8-133E5896C47A"
                       EndpointSystemName="TFS">
        <Settings>
          <Addins />
          <UserIdentityLookup>

            <LookupAddin Precedence="1" ReferenceName="cdde6b6b-72fc-43b6-bbd1-b8a89a788c6f" />

          </UserIdentityLookup>

          <DefaultUserIdProperty UserIdPropertyName="DisplayName" />
        </Settings>
      <CustomSettings>
          <CustomSetting SettingKey="EnableBypassRuleDataSubmission" SettingValue="True" />
          <CustomSetting SettingKey="DisableAreaPathAutoCreation" SettingValue="False" />
          <CustomSetting SettingKey="DisableIterationPathAutoCreation" SettingValue="False" />
        </CustomSettings>
        <StoredCredential />
      </MigrationSource>
      <MigrationSource InternalUniqueId="5E6BCC38-68F4-4A68-AB1E-8EC9C6D64C33"
                       FriendlyName="Powershell (vc)"
                       ServerIdentifier="346dd524-18b0-4eda-ba05-0f4d6b14e076"
                       ServerUrl="fieldnotused"
                       SourceIdentifier="D:\DataPerforce\localhost\1666\ImportRun1"
                       ProviderReferenceName="566C001E-E476-4A07-8447-B2284C55A20E"
                       >
        <Settings>
          <Addins />
          <UserIdentityLookup>

            <LookupAddin Precedence="1" ReferenceName="cdde6b6b-72fc-43b6-bbd1-b8a89a788c6f" />

          </UserIdentityLookup>

          <DefaultUserIdProperty UserIdPropertyName="DisplayName" />
        </Settings>
        <CustomSettings>
          <!--
            Powershell Arguments
            $IpChangeGroup = Microsoft.TeamFoundation.Migration.Toolkit.ChangeGroup
            $IpConversionResult = Microsoft.TeamFoundation.Migration.Toolkit.ConversionResult
            $IpMigrationAction = Microsoft.TeamFoundation.Migration.Toolkit.MigrationAction
            $IpNewPath = D:\DataPerforce\localhost\1666\ImportRun1\{localpath}
            $IpLocalRoot = D:\DataPerforce\localhost\1666\ImportRun1

            For Powershell files to import for perticular clients, please refer to:

            Perforce :
          -->
          <CustomSetting SettingKey="ImportChangeGroup_Initial" SettingValue="D:\DataPerforce\localhost\PerforceInitilise.ps1" />
          <CustomSetting SettingKey="ImportChangeGroupAction_VersionControlledFolder_Add" SettingValue="" />
          <CustomSetting SettingKey="ImportChangeGroupAction_VersionControlledFolder_Edit" SettingValue="" />
          <CustomSetting SettingKey="ImportChangeGroupAction_VersionControlledFolder_Delete" SettingValue="" />
          <CustomSetting SettingKey="ImportChangeGroupAction_VersionControlledFolder_Rename" SettingValue="" />
          <CustomSetting SettingKey="ImportChangeGroupAction_VersionControlledFile_Add" SettingValue="" />
          <CustomSetting SettingKey="ImportChangeGroupAction_VersionControlledFile_Edit" SettingValue="" />
          <CustomSetting SettingKey="ImportChangeGroupAction_VersionControlledFile_Rename" SettingValue="" />
          <CustomSetting SettingKey="ImportChangeGroup_Final" SettingValue="D:\DataPerforce\localhost\PerforceCommit.ps1" />
        </CustomSettings>
        <StoredCredential />
      </MigrationSource>
    </MigrationSources>
    <Sessions>
      <Session CreationTime="2011-04-14T14:59:58.703-07:00"
               SessionUniqueId="1C1BF9C0-AC3E-48FA-89CE-2A759F0AA8E4"
               FriendlyName="Version Control Session"
               LeftMigrationSourceUniqueId="A3E4BC8C-B1C3-4A8E-B18A-3066FF861E33"
               RightMigrationSourceUniqueId="5E6BCC38-68F4-4A68-AB1E-8EC9C6D64C33"
               SessionType="VersionControl">
        <EventSinks />
        <CustomSettings>
          <SettingXml>

          </SettingXml>
          <SettingXmlSchema />
        </CustomSettings>
        <Filters>
          <FilterPair Neglect="false">
            <FilterItem MigrationSourceUniqueId="A3E4BC8C-B1C3-4A8E-B18A-3066FF861E33" FilterString="$/TeamProject1/Folder1" />
            <FilterItem MigrationSourceUniqueId="5E6BCC38-68F4-4A68-AB1E-8EC9C6D64C33" FilterString="$/TeamProject1/Folder1" />
          </FilterPair>
        </Filters>
      </Session>
    </Sessions>
    <Linking>
      <CustomSettings />
      <LinkTypeMappings />
    </Linking>
    <!-- See http://blogs.msdn.com/b/willy-peter_schaub/archive/2010/01/13/tfs-integration-platform-workflowtypeenum-configuration-element-is-being-deprecated.aspx -->
    <!--<WorkFlowType Frequency="ContinuousManual" DirectionOfFlow="Unidirectional" SyncContext="Disabled" />-->
     <WorkFlowType Frequency="ContinuousAutomatic" DirectionOfFlow="Unidirectional" SyncContext="Disabled" />
    <CustomSettings />
    <UserIdentityMappings EnableValidation="false">
      <UserIdentityLookupAddins />
    </UserIdentityMappings>
    <ErrorManagement>
      <ErrorRouters />
      <ReportingSettings />
    </ErrorManagement>
  </SessionGroup>
</Configuration>

图:Powershell适配器的配置

我已经强调了上面的重要部分,并且我们已经描述了上面的一些重要部分,但是真正需要配置的只有三个重要的东西:

  • TFS源文件夹-(例如$/TeamProject1/Folder1)

    TFS源文件夹定义您要在TFS中获取数据的位置。适配器当前不支持分支,因此最好选择不包含分支的文件夹。

  • 本地输出文件夹-(例如c:\Enlistment\1995\Depot\)

    这是系统在调用PowerShell之前写出文件和文件夹的文件夹。这通常是另一个系统的实际工作区文件夹,也可以在任何位置。

  • Power Shell文件-(例如c:\Enlistment\1995\SyncPerforceWithDepot.ps1)

    PowerShell文件可按所述方式轻松配置。请记住,对于每个“添加”、“编辑”或“删除”更改,都可以将相应的PowerShell配置为被调用。

既然您已经能够配置配置文件,现在就可以设置运行了。

SNAGHTMLe3e86f

图:为TFS集成平台创建新配置

我们需要创建一个新配置并选择我们构建的配置文件模板。别担心,我们可以稍后更改个别设置。

SNAGHTMLe5b54e

图:使用示例PowerShell配置

 

SNAGHTMLe83fa3

图:您可以重新配置TFS端以指向有效的TFS服务器和项目。

 

SNAGHTMLe8d29e

图:确保您的文件夹不“包含”分支

出于一个非常奇怪的原因(可能是因为我的懒惰),您需要将路径的左侧和右侧设置为相同的东西。我相信这只是临时的工作,但你知道他们是什么样的人!

image

图:路径必须相同

现在,您可以“保存到数据库”配置,然后单击左侧的“Start”按钮,在适配器上开始第一次运行。

SNAGHTMLed1f28

图:适配器随后启动其运行

没有什么可查看的,文件由所选文件夹中的更改集填充,并运行配置的PowerShell脚本。

SNAGHTMLefcfb1

图:某些变更集比其他变更集大

偶尔会处理一些非常大的变更集。这个实际上相当小,只有1846个更改动作。

结论

虽然这个过程可能需要一段时间,但事实上你可以在飞翔上以任何你喜欢的方式配置PowerShell,这使得它成为一个非常通用的适配器。虽然缺点是您必须用PowerShell编写Smile

如果您需要TFS以外的地方的TFS版本控制数据的副本以供后代使用,或者您需要从Test Track Pro to TFS,然后用ping命令告诉我,看看我们能帮上什么忙。