PS腳本範例CODE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# ====================== 多環境平行 Publish 腳本 (強化中文支援) ======================
# 主腳本編碼設定
$OutputEncoding = [System.Text.Encoding]::UTF8
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
[Console]::InputEncoding = [System.Text.Encoding]::UTF8
chcp 65001 | Out-Null
#$projectPath = "C:\SVN_Release\trunk\trunk\APIServices\APIServices\APIServices.csproj"
$projectPath = Join-Path $PSScriptRoot "trunk\APIServices\APIServices\APIServices.csproj"
$environments = @(
@{ Name = "Dev"; Config = "Dev"; OutputPath = "C:\SVN_Release\APIServices_Dev" }
,@{ Name = "Stage"; Config = "Stage"; OutputPath = "C:\SVN_Release\APIServices_Stage" }
)
# 確保資料夾存在,並記錄原本的最後修改時間
$folderOriginalTimes = @{}
foreach ($env in $environments) {
if (-Not (Test-Path -Path $env.OutputPath)) {
New-Item -ItemType Directory -Path $env.OutputPath -Force | Out-Null
}
$folderOriginalTimes[$env.OutputPath] = (Get-Item -Path $env.OutputPath).LastWriteTime
Write-Host "已記錄 $($env.Name) 資料夾原始時間: $($folderOriginalTimes[$env.OutputPath])" -ForegroundColor DarkGray
}
function Invoke-PublishOnce {
param(
[string]$proj,
[string]$config,
[string]$outPath,
[string]$name
)
$OutputEncoding = [System.Text.Encoding]::UTF8
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
chcp 65001 | Out-Null
Write-Host "正在清理 $name 環境輸出目錄..." -ForegroundColor DarkYellow
if (Test-Path -Path $outPath) {
Get-ChildItem -Path $outPath -Recurse -Force | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
}
Write-Host "正在 Publish $name 環境..." -ForegroundColor Yellow
$startTime = Get-Date
dotnet publish $proj --configuration $config --output $outPath --verbosity minimal
$duration = (Get-Date) - $startTime
$fileCount = 0
if (Test-Path -Path $outPath) {
$fileCount = (Get-ChildItem -Path $outPath -Recurse -File -ErrorAction SilentlyContinue | Measure-Object).Count
}
$success = ($LASTEXITCODE -eq 0) -and ($fileCount -gt 0)
return [PSCustomObject]@{
Environment = $name
Status = if ($success) { "✅ 成功" } else { "❌ 失敗" }
Duration = "{0:mm\:ss}" -f $duration
Path = $outPath
FileCount = $fileCount
ExitCode = $LASTEXITCODE
Success = $success
}
}
Write-Host "開始平行 Publish 多個環境..." -ForegroundColor Cyan
$jobs = @()
foreach ($env in $environments) {
$job = Start-Job -ScriptBlock {
param($proj, $config, $outPath, $name)
$OutputEncoding = [System.Text.Encoding]::UTF8
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
chcp 65001 | Out-Null
function Invoke-PublishOnce {
param(
[string]$proj,
[string]$config,
[string]$outPath,
[string]$name
)
Write-Host "正在清理 $name 環境輸出目錄..." -ForegroundColor DarkYellow
if (Test-Path -Path $outPath) {
Get-ChildItem -Path $outPath -Recurse -Force | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
}
Write-Host "正在 Publish $name 環境..." -ForegroundColor Yellow
$startTime = Get-Date
dotnet publish $proj --configuration $config --output $outPath --verbosity minimal
$duration = (Get-Date) - $startTime
$fileCount = 0
if (Test-Path -Path $outPath) {
$fileCount = (Get-ChildItem -Path $outPath -Recurse -File -ErrorAction SilentlyContinue | Measure-Object).Count
}
$success = ($LASTEXITCODE -eq 0) -and ($fileCount -gt 0)
return [PSCustomObject]@{
Environment = $name
Status = if ($success) { "✅ 成功" } else { "❌ 失敗" }
Duration = "{0:mm\:ss}" -f $duration
Path = $outPath
FileCount = $fileCount
ExitCode = $LASTEXITCODE
Success = $success
}
}
# 第一次發布
$result = Invoke-PublishOnce -proj $proj -config $config -outPath $outPath -name $name
# 若資料夾是空的,只再重試一次(避免遞迴)
if (-not $result.Success -or $result.FileCount -eq 0) {
Write-Host "[$name] 發布後資料夾為空或失敗,開始重試一次..." -ForegroundColor Magenta
$result = Invoke-PublishOnce -proj $proj -config $config -outPath $outPath -name $name
if ($result.Success) {
$result.Status = "✅ 成功(重試後)"
} else {
$result.Status = "❌ 失敗(重試後仍失敗)"
}
}
return $result
} -ArgumentList $projectPath, $env.Config, $env.OutputPath, $env.Name
$jobs += $job
}
Write-Host "等待所有 Publish 任務完成..." -ForegroundColor Gray
$jobs | Wait-Job | Out-Null
$results = $jobs | Receive-Job
$results | Format-Table -AutoSize -Property Environment, Status, Duration, FileCount, Path
$jobs | Remove-Job
# ========== 任務完成後檢查並還原空資料夾的最後修改時間 ==========
Write-Host "`n檢查發布結果並還原空資料夾時間..." -ForegroundColor Cyan
foreach ($env in $environments) {
$outPath = $env.OutputPath
if (Test-Path -Path $outPath) {
$fileCount = (Get-ChildItem -Path $outPath -Recurse -File -ErrorAction SilentlyContinue | Measure-Object).Count
if ($fileCount -eq 0) {
if ($folderOriginalTimes.ContainsKey($outPath)) {
$originalTime = $folderOriginalTimes[$outPath]
(Get-Item -Path $outPath).LastWriteTime = $originalTime
Write-Host "[$($env.Name)] 資料夾為空,已還原最後修改時間 → $originalTime" -ForegroundColor Yellow
}
} else {
Write-Host "[$($env.Name)] 發布成功,共有 $fileCount 個檔案" -ForegroundColor Green
}
}
}
Write-Host "`n所有 Publish 任務已完成!" -ForegroundColor Green
GIT版控
Git切成特分支並Pull最新遠端分支的PS腳本範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# ============================================
# 自動拉取最新 Source Code(安全衝突檢查版)
# ============================================
# 變數$ProjectPath路徑需要有 .git資料夾
$ProjectPath = "C:\GIT_Release\OSS2\APIServices"
if (-not (Test-Path $ProjectPath)) {
Write-Host "錯誤:路徑不存在 → $ProjectPath" -ForegroundColor Red
Write-Host "`n視窗將在 5 秒後自動關閉..." -ForegroundColor Gray
Start-Sleep -Seconds 5
exit 1
}
Set-Location $ProjectPath
# ----------------------------------------
# 1. 顯示目前所在分支
# ----------------------------------------
$currentBranch = git branch --show-current
Write-Host "專案路徑:$ProjectPath" -ForegroundColor Cyan
Write-Host "目前分支:$currentBranch" -ForegroundColor Cyan
Write-Host "----------------------------------------"
# ----------------------------------------
# 2. 詢問是否要切換分支
# ----------------------------------------
Write-Host "請輸入要切換的分支名稱(直接按 Enter 則維持目前分支):" -ForegroundColor Yellow
### Dev Stage master
$targetBranch = 'Dev'
#$targetBranch = 'Stage'
# 3. 如果輸入空白 或 與目前分支相同 → 略過切換
if ([string]::IsNullOrWhiteSpace($targetBranch) -or $targetBranch -eq $currentBranch) {
Write-Host "維持目前分支:$currentBranch" -ForegroundColor Green
}
else {
Write-Host "正在切換到分支:$targetBranch ..." -ForegroundColor Yellow
git switch $targetBranch 2>$null
if ($LASTEXITCODE -ne 0) {
Write-Host "✗ 切換分支失敗,請確認分支名稱是否正確" -ForegroundColor Red
Write-Host "`n視窗將在 5 秒後自動關閉..." -ForegroundColor Gray
Start-Sleep -Seconds 5
exit 1
}
Write-Host "✓ 已成功切換到分支:$(git branch --show-current)" -ForegroundColor Green
}
Write-Host "----------------------------------------"
# ----------------------------------------
# 以下是原本的安全拉取邏輯
# ----------------------------------------
# 1. 先 fetch,不改動本地檔案
Write-Host "正在取得遠端最新資訊 (git fetch)..." -ForegroundColor Yellow
git fetch --quiet
if ($LASTEXITCODE -ne 0) {
Write-Host "✗ git fetch 失敗,請檢查網路或權限" -ForegroundColor Red
Write-Host "`n視窗將在 5 秒後自動關閉..." -ForegroundColor Gray
Start-Sleep -Seconds 5
exit 1
}
# 2. 檢查是否已經是最新
$behindCount = git rev-list --count "HEAD..@{u}" 2>$null
if ($behindCount -eq 0) {
Write-Host "✓ 本地已經是最新版本,無需更新" -ForegroundColor Green
Write-Host "完成時間:$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Gray
Write-Host "`n視窗將在 5 秒後自動關閉..." -ForegroundColor Gray
Start-Sleep -Seconds 5
exit 0
}
Write-Host "遠端有 $behindCount 個新的 commit 需要更新" -ForegroundColor Cyan
# 3. 檢查本地是否有未提交的變更
$localChanges = git status --porcelain
if ($localChanges) {
Write-Host "`n目前有未提交的變更:" -ForegroundColor Yellow
git status --short
Write-Host ""
# 使用 git merge-tree 預先檢查是否會產生衝突(不實際修改檔案)
$mergeBase = git merge-base HEAD "@{u}"
$conflictCheck = git merge-tree $mergeBase HEAD "@{u}" 2>&1
if ($conflictCheck -match "changed in both") {
Write-Host "========================================" -ForegroundColor Red
Write-Host "✗ 偵測到會產生衝突!已取消更新" -ForegroundColor Red
Write-Host "========================================" -ForegroundColor Red
Write-Host "原因:本地有修改的檔案與遠端新版本有衝突" -ForegroundColor Yellow
Write-Host "建議:" -ForegroundColor Yellow
Write-Host " 1. 先手動處理或暫存本地修改 (git stash)" -ForegroundColor Yellow
Write-Host " 2. 再重新執行此腳本" -ForegroundColor Yellow
Write-Host " 3. 或手動解決衝突後再更新" -ForegroundColor Yellow
Write-Host "完成時間:$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Gray
Write-Host "`n視窗將在 5 秒後自動關閉..." -ForegroundColor Gray
Start-Sleep -Seconds 5
exit 1
}
else {
Write-Host "本地有修改,但與遠端變更沒有衝突,可以安全更新" -ForegroundColor Green
}
}
# 4. 沒有衝突風險,執行實際更新
Write-Host "`n正在執行 git pull..." -ForegroundColor Yellow
git pull
if ($LASTEXITCODE -eq 0) {
Write-Host "`n✓ 最新 Source Code 已成功拉取!" -ForegroundColor Green
} else {
Write-Host "`n✗ 拉取失敗,請檢查上方錯誤訊息" -ForegroundColor Red
Write-Host "`n視窗將在 5 秒後自動關閉..." -ForegroundColor Gray
Start-Sleep -Seconds 5
exit 1
}
#####################################
Write-Host "完成時間:$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Gray
Write-Host "`n視窗將在 5 秒後自動關閉..." -ForegroundColor Gray
Start-Sleep -Seconds 5
建置時自動將 Git 分支與 Commit 資訊寫入 DLL
用途:這樣才可以在程式發佈到正式環境的時候 顯示發佈當下是什麼分支布板的 且底下範例客製成 若Master分支有打Tag就優先顯示Tat資訊
請將以下範例將檔名取為Directory.Build.props 然後放在跟 副檔名為sln同個路徑底下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<Project>
<!-- 關閉 SDK 自動附加 commit hash 的行為 -->
<PropertyGroup>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
</PropertyGroup>
<Target Name="SetVersionFromGit" BeforeTargets="GenerateAssemblyInfo;GetAssemblyVersion">
<!-- 1. 取得當前分支名稱 -->
<Exec Command="git rev-parse --abbrev-ref HEAD" ConsoleToMSBuild="true" IgnoreExitCode="true">
<Output TaskParameter="ConsoleOutput" PropertyName="BranchName" />
</Exec>
<!-- 2. 取得最新 commit 的時間 (yyyyMMddHHmm) -->
<Exec Command="git show -s --format=%25%25cd --date=format:%25%25Y%25%25m%25%25d%25%25H%25%25M HEAD"
ConsoleToMSBuild="true"
IgnoreExitCode="true">
<Output TaskParameter="ConsoleOutput" PropertyName="CommitTime" />
</Exec>
<!-- 3. 取得最新 commit 的雜湊前 6 碼 -->
<Exec Command="git rev-parse --short=6 HEAD" ConsoleToMSBuild="true" IgnoreExitCode="true">
<Output TaskParameter="ConsoleOutput" PropertyName="ShortHash" />
</Exec>
<!-- 4. 清理並組合版號 -->
<PropertyGroup>
<_BranchName>$([System.String]::Copy('$(BranchName)').Trim())</_BranchName>
<_CommitTime>$([System.String]::Copy('$(CommitTime)').Trim())</_CommitTime>
<_ShortHash>$([System.String]::Copy('$(ShortHash)').Trim())</_ShortHash>
<GitVersion Condition="'$(_BranchName)' != '' And '$(_CommitTime)' != '' And '$(_ShortHash)' != '' And !$(_CommitTime.Contains('fatal'))">
$(_BranchName).$(_CommitTime).$(_ShortHash)
</GitVersion>
<GitVersion Condition="'$(GitVersion)' == ''">$(_BranchName).unknown</GitVersion>
<GitVersion>$([System.String]::Copy('$(GitVersion)').Trim())</GitVersion>
<Version>$(GitVersion)</Version>
<InformationalVersion>$(GitVersion)</InformationalVersion>
<AssemblyVersion>0.0.0.0</AssemblyVersion>
<FileVersion>0.0.0.0</FileVersion>
</PropertyGroup>
</Target>
</Project>