# MergeHub 배포 묶음 만들기 (11차 B-1, 2026-09-22) # # Release 로 빌드한 출력(src\MergeHub\bin\Release\net48)을 통째로 옮기면 97MB · 65개다. # 그 가운데 Windows PC 에서 MergeHub 를 켜는 데 쓰지 않는 것을 빼고 새 폴더에 옮긴 뒤, # 무엇이 왜 들어갔는지를 묶음 안의 "배포묶음.txt" 에 적는다. # # 빼는 것은 아래 $Excluded 에 까닭과 함께 적는다. 무엇을 넣을지 바꾸려면 여기만 고친다. # 뺀 묶음으로 빈 폴더에서 켜 네 탭 · 병합 한 길(C1Repo)을 끝까지 해 본 결과는 # doc\history\2026-09-18-11차.md 의 B-1 에 있다. # # 쓰는 법: # powershell -ExecutionPolicy Bypass -File scripts\make-bundle.ps1 # powershell -ExecutionPolicy Bypass -File scripts\make-bundle.ps1 -Out D:\배포\MergeHub_베타 -NoBuild # # 런처 · 셸 · 플랫폼 dll 은 빌드할 때 플랫폼 배포 폴더(D:\UbisamPlatform\bin)에서 복사되어 온다. # 플랫폼을 새로 배포했다면 이 스크립트를 다시 돌려야 묶음에 반영된다. # # 켜는 것은 UbisamBase.Launcher.exe 다(2026-10-01, 공용 플랫폼의 런처 방식). 런처는 켤 때마다 # D:\UbisamPlatform\bin 을 자기 폴더로 복사해 오고(업데이트도 확인) 같은 프로세스 안에서 셸을 띄운다. # 셸을 직접 눌러도 같은 폴더의 런처로 넘긴다. 그 폴더가 없는 PC 에서는 경고 창 뒤 묶음 안의 파일로 뜬다. param( # 묶음을 만들 폴더. 이미 있으면 이 스크립트가 만든 묶음일 때만 비우고 다시 채운다. [string]$Out = (Join-Path $env:TEMP "mergehub-beta"), # 빌드를 건너뛰고 지금 있는 Release 출력으로 묶는다. [switch]$NoBuild ) $ErrorActionPreference = "Stop" $root = Split-Path -Parent $PSScriptRoot $project = Join-Path $root "src\MergeHub\MergeHub.csproj" $release = Join-Path $root "src\MergeHub\bin\Release\net48" $marker = "배포묶음.txt" # ---- 빼는 것 (까닭) -------------------------------------------------------- # 경로는 묶음 폴더 기준. 끝이 \ 이면 폴더 통째. $Excluded = [ordered]@{ "GLWpfControl.dll" = "어디서도 참조하지 않음 (SkiaSharp.Views.WPF 만 부르고, 그것도 안 쓰임)" "OpenTK.dll" = "어디서도 참조하지 않음 (GLWpfControl 만 부름)" "SkiaSharp.Views.WPF.dll" = "어디서도 참조하지 않음 (LiveCharts WPF 컨트롤용)" "SkiaSharp.Views.Desktop.Common.dll" = "어디서도 참조하지 않음" "LiveChartsCore.SkiaSharpView.WPF.dll" = "어디서도 참조하지 않음 (플랫폼 Core 는 WPF 컨트롤이 아니라 LiveChartsCore 만 부름)" "Microsoft.Extensions.Options.dll" = "어디서도 참조하지 않음" "Microsoft.Extensions.Options.ConfigurationExtensions.dll" = "어디서도 참조하지 않음" "libSkiaSharp.dylib" = "맥용" "libHarfBuzzSharp.dylib" = "맥용" "arm64\" = "ARM PC 용 Skia · HarfBuzz (사내 PC 는 x64)" "lib\linux-arm\" = "리눅스용 libgit2" "lib\linux-arm64\" = "리눅스용 libgit2" "lib\linux-musl-arm\" = "리눅스용 libgit2" "lib\linux-musl-arm64\" = "리눅스용 libgit2" "lib\linux-musl-x64\" = "리눅스용 libgit2" "lib\linux-ppc64le\" = "리눅스용 libgit2" "lib\linux-x64\" = "리눅스용 libgit2" "lib\osx-arm64\" = "맥용 libgit2" "lib\osx-x64\" = "맥용 libgit2" "lib\win32\arm64\" = "ARM PC 용 libgit2 (사내 PC 는 x64)" "MergeHub.pdb" = "디버그 심볼" "LibGit2Sharp.dll.config" = "Mono 용 dll 대응표 - .NET Framework 는 lib\win32 를 스스로 찾는다" "Lang\" = "비어 있음 - 없으면 켤 때 플랫폼(LanguageService)이 만든다" "README.txt" = "플랫폼 배포 폴더 안내문 - 묶음에는 맞지 않아 배포묶음.txt 로 바꿈(런처가 켤 때 플랫폼 배포 폴더에서 다시 가져온다)" } # ---- 넣는 것이 왜 필요한지 (배포묶음.txt 에 적음) ------------------------- function Why([string]$path) { # switch 는 걸리는 규칙을 모두 돌려준다 - 위에 적은 것(더 좁은 것) 하나만 쓴다. @(Reason $path)[0] } function Reason([string]$path) { switch -Regex ($path) { '^UbisamBase\.Launcher\.exe$' { "런처 - 이것을 켠다. 플랫폼 배포 폴더를 복사해 온 뒤 같은 프로세스에서 셸을 띄운다" } '^UbisamBase\.Launcher\.exe\.config$' { "런처 설정 - 셸이 런처 프로세스 안에서 돌아 실제로 쓰이는 바인딩 리디렉트(없으면 켜자마자 죽는다)" } '^platform-version\.json$' { "플랫폼 버전 도장 - 런처가 업데이트를 확인할 때 견준다" } '^UbisamBase\.Shell\.exe$' { "셸 - 런처가 띄운다(직접 눌러도 런처로 넘긴다)" } '^UbisamBase\.Shell\.exe\.config$' { "셸 설정 - .NET 4.8 · 어셈블리 버전 맞춤" } '^UbisamBase\.Core\.dll$' { "플랫폼" } '^MergeHub\.dll$' { "MergeHub - 셸이 폴더의 dll 을 훑어 찾는다" } '^LibGit2Sharp\.dll$' { "Git 조작" } '^lib\\win32\\' { "Git 네이티브(libgit2)" } '^CommunityToolkit\.Mvvm\.dll$' { "MVVM (플랫폼 · MergeHub)" } '^System\.Text\.Encoding\.CodePages' { "CP949 판별 (MergeHub)" } '^Microsoft\.Extensions\.' { "플랫폼 설정 · DI" } '^(LiveChartsCore|SkiaSharp|HarfBuzzSharp)' { "플랫폼 Core 가 참조하는 차트 (ChartTheme)" } '^(x64|x86)\\|^lib(SkiaSharp|HarfBuzzSharp)\.dll$' { "차트 네이티브 (플랫폼 몫)" } '^(System|Microsoft\.Bcl)\.' { "플랫폼 · Json 이 쓰는 .NET 보강" } default { "(까닭 미상 - 확인 필요)" } } } # ---- 1. 빌드 ---------------------------------------------------------------- if (-not $NoBuild) { $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" if (-not (Test-Path $vswhere)) { throw "vswhere 가 없습니다: $vswhere (-NoBuild 로 지금 있는 Release 출력을 쓸 수 있습니다)" } $msbuild = & $vswhere -latest -requires Microsoft.Component.MSBuild -find "MSBuild\**\Bin\MSBuild.exe" | Select-Object -First 1 if (-not $msbuild) { throw "MSBuild 를 찾지 못했습니다." } Write-Host "Release 빌드: $project" & $msbuild $project -p:Configuration=Release -v:m -nologo if ($LASTEXITCODE -ne 0) { throw "빌드 실패 (종료 코드 $LASTEXITCODE)" } } if (-not (Test-Path (Join-Path $release "MergeHub.dll"))) { throw "Release 출력이 없습니다: $release" } # ---- 2. 묶음 폴더 준비 - 이 스크립트가 만든 것만 비운다 --------------------- if (Test-Path $Out) { $items = @(Get-ChildItem $Out -Force) if ($items.Count -gt 0 -and -not (Test-Path (Join-Path $Out $marker))) { throw "'$Out' 에 다른 파일이 있습니다. 이 스크립트가 만든 묶음($marker 있음)이거나 빈 폴더여야 합니다." } $items | Remove-Item -Recurse -Force } else { New-Item -ItemType Directory -Path $Out | Out-Null } # ---- 3. 옮기기 -------------------------------------------------------------- $files = Get-ChildItem $release -Recurse -File | ForEach-Object { $_.FullName.Substring($release.Length + 1) } $kept = @(); $dropped = @() foreach ($rel in $files) { $reason = $null foreach ($key in $Excluded.Keys) { if (($key.EndsWith("\") -and $rel.StartsWith($key, [StringComparison]::OrdinalIgnoreCase)) -or ($rel -ieq $key)) { $reason = $Excluded[$key]; break } } if ($reason) { $dropped += [pscustomobject]@{ Path = $rel; Why = $reason }; continue } $dest = Join-Path $Out $rel New-Item -ItemType Directory -Path (Split-Path $dest) -Force | Out-Null Copy-Item (Join-Path $release $rel) $dest $kept += [pscustomobject]@{ Path = $rel; Size = (Get-Item $dest).Length; Why = (Why $rel) } } # 꼭 있어야 하는 것 foreach ($must in "UbisamBase.Launcher.exe", "UbisamBase.Launcher.exe.config", "UbisamBase.Shell.exe", "UbisamBase.Shell.exe.config", "UbisamBase.Core.dll", "MergeHub.dll", "LibGit2Sharp.dll") { if (-not (Test-Path (Join-Path $Out $must))) { throw "묶음에 $must 가 없습니다." } } if (-not (Get-ChildItem (Join-Path $Out "lib\win32\x64") -Filter "git2-*.dll" -ErrorAction SilentlyContinue)) { throw "묶음에 lib\win32\x64\git2-*.dll 이 없습니다." } # 셸은 런처 프로세스 안에서 돈다 - 런처 설정에 바인딩 리디렉트가 없으면 켜자마자 FileLoadException 으로 죽는다(플랫폼 publish-platform.ps1 과 같은 검사). if (-not (Select-String -Path (Join-Path $Out "UbisamBase.Launcher.exe.config") -Pattern "assemblyBinding" -Quiet)) { throw "런처 설정에 바인딩 리디렉트가 없습니다." } # ---- 4. 배포묶음.txt -------------------------------------------------------- $total = ($kept | Measure-Object Size -Sum).Sum $commit = try { (& git -C $root rev-parse --short HEAD 2>$null) } catch { "-" } $dll = Get-Item (Join-Path $Out "MergeHub.dll") $shell = Get-Item (Join-Path $Out "UbisamBase.Shell.exe") $platformVersion = try { (Get-Content (Join-Path $Out "platform-version.json") -Raw | ConvertFrom-Json).version } catch { "-" } $mb = { param($b) "{0:N1}MB" -f ($b / 1MB) } $lines = @() $lines += "MergeHub 배포 묶음" $lines += "==================" $lines += "" $lines += "UbisamBase.Launcher.exe 를 켠다. 설치 없이 폴더째 복사해 쓴다." $lines += "런처는 켤 때마다 D:\UbisamPlatform\bin(공용 플랫폼)을 이 폴더로 복사해 오고 업데이트를 확인한 뒤 셸을 띄운다." $lines += "그 폴더가 없는 PC 에서는 '플랫폼 파일을 갱신하는 중 문제' 경고 뒤 이 폴더 안의 파일로 그대로 뜬다." $lines += "UbisamBase.Shell.exe 를 직접 눌러도 런처로 넘어간다." $lines += "설정 · 로그는 D:\UbisamConfig 에 쓴다(플랫폼)." $lines += "" $lines += "만든 시각 : " + (Get-Date -Format "yyyy-MM-dd HH:mm:ss") $lines += "MergeHub : " + $dll.LastWriteTime.ToString("yyyy-MM-dd HH:mm") + " (커밋 $commit)" $lines += "셸 · 플랫폼 : " + $shell.LastWriteTime.ToString("yyyy-MM-dd HH:mm") + " (D:\UbisamPlatform\bin 에서 온 것 · 플랫폼 버전 $platformVersion)" $lines += ("파일 {0}개 · {1}" -f $kept.Count, (& $mb $total)) $lines += "" $lines += "[ 들어 있는 것 ]" foreach ($k in $kept | Sort-Object Path) { $lines += (" {0,-58} {1,9} {2}" -f $k.Path, (& $mb $k.Size), $k.Why) } $lines += "" $lines += "[ 뺀 것 ] (Release 출력에는 있음)" foreach ($d in $dropped | Sort-Object Path) { $lines += (" {0,-58} {1}" -f $d.Path, $d.Why) } $lines += "" $lines += "이 파일은 scripts\make-bundle.ps1 이 만든다. 이 파일이 있어야 다음에 같은 폴더를 다시 채운다." [IO.File]::WriteAllLines((Join-Path $Out $marker), $lines, (New-Object Text.UTF8Encoding($true))) Write-Host ("묶음: {0} · 파일 {1}개 · {2} (뺀 것 {3}개)" -f $Out, $kept.Count, (& $mb $total), $dropped.Count) $unknown = @($kept | Where-Object { $_.Why -like "(까닭 미상*" }) if ($unknown.Count) { Write-Warning ("까닭을 모르는 파일: " + (($unknown | ForEach-Object { $_.Path }) -join ", ")) }