如何建立 Team Foundation Server Extension
1. 环境準备
(1) 安装 Node.js
a. 请到 https://nodejs.org/en/ 下载 Node.js 安装, 若已经有 Node.js 环境 , 可以忽略此步骤 (检查 Node.js 版本, 可以使用 node --version 命令)
(2) 安装 TFS 延伸套件
请从命令提示执行以下命令安装 TFS 延伸套件
npm install -g tfx-cli
2. 建立 Extension 目录与 Manifest 档案
(1) 从命令提示 建立一个目录存放 Extension
md c:\temp\tfs-extension
(2) 使用 VS Code 建立一下的档案结构
a. Task 目录 - 存放 Task Script 与内容
(a) icon.png - task 的 icon (32x32)
(b) task.json - task 的 manifest 档案
(c) task.ps1 - 实际执行的 task PowerShell script
b. icon128.png - TFS extension 列表的 icon (128x128)
c. icon512.png - Extension detail 的 Icon (512x512)
d. overview.md - Extension 详细介绍的说明 (Markdown格式)
e. vss-extension.json - TFS extension 打包时的延伸档
3. 编辑 Task manifest 档 (task.json)
a. id: guid 作为 task 的识别
b. name: Task 名称
c. friendlyName: 好辨识的名称
d. author: 作者名称, 作者名称必须要注册于 MarketPlace 中
e. category: 显示于 Task 类别
f. version: 定义版本资讯
g. instanceNameFormat: 初始的 task 名称
h. inputs: 参数定义
i. execution: 执行方式
{ "id": "7AE8DD31-8197-4C6E-B3A5-5D2052CFA59F", "name": "BackupFilesViaPSSession", "friendlyName": "Backup Files via PS Session", "description": "Backup files using a Powershell Session.", "author": "SamLin", "helpMarkDown": "", "category": "Utility", "visibility": [ "Build", "Release" ], "demands": [ "DotNetFramework" ], "version": { "Major": "0", "Minor": "2", "Patch": "19" }, "minimumAgentVersion": "1.83.0", "instanceNameFormat": "Powershell Backup files to $(targetpath)", "groups": [], "inputs": [ { "name": "sourcepath", "type": "string", "label": "Source Path", "defaultValue": "", "required": true, "helpMarkDown": "A local source path on the remote server." }, { "name": "targetpath", "type": "string", "label": "Target Path", "defaultValue": "", "required": true, "helpMarkDown": "A local target path on the remote server." }, { "name": "backupname", "type": "string", "label": "Backup Name", "defaultValue": "", "required": true, "helpMarkDown": "A backup name will append with date(yyyyMMdd).zip file name." }, { "name": "server", "type": "string", "label": "Remote Machine", "defaultValue": "", "required": true, "helpMarkDown": "FQDN of the remote machine you want to reach (or the IP Address)." } ], "execution": { "PowerShell": { "target": "$(currentDirectory)\\task.ps1", "argumentFormat": "", "workingDirectory": "$(currentDirectory)" } }}
4. 编辑 Task.ps1
以下範例为远端备份档案 script 请自行修改为需要的 script 内容
param ( [string] $sourcepath, # 来源端路径 [string] $targetpath, # 备份目标路径 [string] $backupname, # 备份名称 (将会以备份名称_yyyyMMdd.zip 为备份档案名称) [string] $server # 远端主机名称 (需支援并开启 PSRemoting))Write-Host "Entering script task.ps1";Write-Verbose "[server] --> [$server]" -Verbose;Write-Verbose "[sourcepath] --> [$sourcepath]" -Verbose;Write-Verbose "[targetpath] --> [$targetpath]" -Verbose;Write-Verbose "[backupname] --> [$backupname]" -Verbose;### Validates all paths ###function ValidatePath ([string]$type, [string]$path) { Write-Host "Validating $type Path variable."; if (-not $path.EndsWith("\")) { $path = "$path\"; } Write-Host "$type Path is $path."; return $path;}$sourcepath = ValidatePath -type "Source" -path $sourcepath;$targetpath = ValidatePath -type "Target" -path $targetpath;[System.Management.Automation.Runspaces.PSSession] $session = $null;$sc = { Add-Type -AssemblyName System.IO.Compression.FileSystem $destination = $args[1] + $args[2] + "_" + $(Get-Date -Format 'yyyyMMdd') + ".zip" if(Test-Path $destination) { # 备份目的档案存在, 移除档案 Write-Host "Remove destination file..." Remove-Item $destination } if(-Not(Test-Path $args[1])) { Write-Host "Create destination folder..." New-Item -Path $args[1] -ItemType Directory } if(Test-Path $args[0]) { # 备份压缩原始档案, 备份完成后移除原始档案 Write-Host "Backup source files to destination with [$($args[2])_$(Get-Date -Format 'yyyyMMdd').zip] name..." [IO.Compression.ZipFile]::CreateFromDirectory($args[0], $destination) Write-Host "Delete source files..." Remove-Item $args[0] -Recurse -Confirm:$false }}Try { $session = New-PSSession -ComputerName $server Write-Host "Backing up files via remote session."; Invoke-Command -Session $session -ScriptBlock $sc -ArgumentList $sourcepath, $targetpath, $backupname}Catch { Write-Host; Write-Error $_;}Finally { Write-Host "Closing Powershell remote session on $server."; if ($null -ne $session) { $session | Disconnect-PSSession | Remove-PSSession };}Write-Host "Leaving script task.ps1";
5. 编辑 TFS extension manifest 档 (vss-extension.json)
a. version: 版本号
b. name: Extension 显示名称
c. publisher: 发行者 (需注册于 MarketPlace)
d. icon: 档案名称定义
e. content: 说明档
f. files: task 档案路径
{ "manifestVersion": 1, "id": "backupfilesviapssession-task", "version": "0.1.19", "name": "Backup Files via PS Session Task", "publisher": "SamLin", "targets": [ { "id": "Microsoft.VisualStudio.Services" } ], "description": "Backup Files via PS Session Task for TFS", "icons": { "default": "icon128.png", "large": "icon512.png" }, "categories": [ "Build and release" ], "tags": [ "utility", "tasks" ], "screenshots": [], "content": { "details": { "path": "overview.md" } }, "links": { "getstarted": { "uri": "https://www.anisv.com" }}, "branding": { "color": "black", "theme": "dark" }, "galleryFlags": [ "Public" ], "scopes": [ "vso.build_execute", "vso.serviceendpoint_manage" ], "files": [ { "path": "Task" } ], "contributions": [ { "id": "backupfilesviapssession-task", "type": "ms.vss-distributed-task.task", "targets": [ "ms.vss-distributed-task.tasks" ], "properties": { "name": "Task" } } ]}
6. 打包 TFS extension
使用以下的命令将档案打包
tfx extension create --manifest-globs vss-extension.json
产出档案格式如下:
7. 安装 TFS Extension
a. 请将档案打包后的档案 (SamLin.backupfilesviapssession-task-0.1.19.vsix) 複製到 TFS 上
b. 开启 TFS extension 管理页面 (http://tfs:8080/tfs/_gallery/manage)
c. 上传 TFS extension
d. 安装 Extension 到 Project Collection
8. 执行 TFS Extension Task
(1) 在 Build definition 新增一个 Task, 可以看到 Utility Category 中出现新加入的 Task
(2) 本範例的使用方式:
a. Display name: Task 名称
b. Source Path: 备份的来源目录
c. Target Path: 备份的目的目录
d. Backup Name: 备份名称
e. Remote Machine: 可以通过 PSRemote 由 Build Agent 连线 PS Remote Session 执行工作的目标主机名称
(3) 执行结果:
注: 目标主机开启 PSRemoting 的 PowerShell 语法如下, 其中 TrustedHosts为 Build Agent 所在的主机名称
Get-Service -Name WinRMEnable-PSRemoting -Forcewinrm s winrm/config/client '@{TrustedHosts="BUILD"}'winrm quickconfigRestart-Service -Name WinRM