배포판 보호 수준 강화 및 single-file 호환 보정
Some checks failed
Release Gate / gate (push) Has been cancelled

배포 스크립트와 앱 Release 설정에 single-file, ReadyToRun, 압축 번들, 최적화 옵션을 추가해 릴리즈 배포 출력의 보호 수준을 한 단계 높였습니다.

WebSearchHandler와 SettingsWindow는 single-file 환경에서 Assembly.Location 경고가 발생하지 않도록 AppContext.BaseDirectory 및 AssemblyInformationalVersionAttribute 기반으로 수정했습니다.

README와 DEVELOPMENT 문서를 갱신했고, dotnet build 검증에서 경고 0 오류 0을 다시 확인했습니다.
This commit is contained in:
2026-04-06 16:09:00 +09:00
parent 8da0a069b7
commit 1ad75b5896
6 changed files with 195 additions and 67 deletions

View File

@@ -1266,3 +1266,10 @@ MIT License
- 코워크 문서 생성이 항상 비슷한 HTML로 수렴하던 원인을 줄이기 위해 [DocumentPlannerTool.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/DocumentPlannerTool.cs), [DocumentAssemblerTool.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/DocumentAssemblerTool.cs)의 기본 포맷/무드 선택 로직을 재정리했다.
- 이제 인자 없이 문서 생성 도구를 호출해도 무조건 `html + professional`로 고정되지 않고, `DefaultOutputFormat`, `DefaultMood`, 문서 유형(`proposal`, `analysis`, `manual`, `minutes` 등), 요청 주제 키워드를 함께 보고 `docx/html/markdown``corporate/dashboard/minimal/creative/professional`을 자동 선택한다.
- 이 변경으로 AX의 문서 생성 체인이 `claw-code`처럼 요청 기반 자유 작성 흐름에 더 가까워졌고, 코워크 결과물이 항상 비슷한 보고서형 HTML로 반복되던 현상을 줄일 기반을 마련했다.
- 업데이트: 2026-04-06 16:14 (KST)
- 배포판 보호 수준을 점검한 뒤 [AxCopilot.csproj](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/AxCopilot.csproj) Release 설정에 `Optimize`, `PublishSingleFile`, `EnableCompressionInSingleFile`, `IncludeNativeLibrariesForSelfExtract`, `PublishReadyToRun`을 추가했다.
- [build.bat](/E:/AX%20Copilot%20-%20Codex/build.bat) 도 같은 publish 속성을 명시적으로 넘기도록 갱신해, 배치 파일로 배포판을 만들 때 실제로 `Release + self-contained + single-file + ReadyToRun` 조합이 적용되도록 맞췄다.
- 현재 저장소에는 외부 난독화기(`tools\\obfuscator`)가 없어서 완전한 디컴파일 방지는 아니지만, 심볼 제거 수준에서 한 단계 더 강화된 배포 출력이 나오도록 정리했다.
- 업데이트: 2026-04-06 16:20 (KST)
- single-file 배포를 켜면서 생긴 호환 경고도 함께 정리했다. [WebSearchHandler.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Handlers/WebSearchHandler.cs)는 `Assembly.Location` 대신 `AppContext.BaseDirectory`를 사용하게 바꿨고, [SettingsWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml.cs)는 버전 표시를 `AssemblyInformationalVersionAttribute` 기준으로 읽도록 수정했다.
- 이 수정까지 반영한 뒤 `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\` 기준 경고 0 / 오류 0을 다시 확인했다.

235
build.bat
View File

@@ -1,90 +1,199 @@
@echo off
setlocal EnableExtensions
chcp 65001 >nul
set "ROOT=%~dp0"
pushd "%ROOT%" >nul
echo.
echo ========================================
echo AX Copilot - Build Script
echo ========================================
echo.
set APP=src\AxCopilot\AxCopilot.csproj
set ENCRYPTOR=src\AxKeyEncryptor\AxKeyEncryptor.csproj
set OFFLINE=src\AxCopilot.Installer\AxCopilot.Installer.csproj
set OUT=dist
set "APP=%ROOT%src\AxCopilot\AxCopilot.csproj"
set "ENCRYPTOR=%ROOT%src\AxKeyEncryptor\AxKeyEncryptor.csproj"
set "INSTALLER=%ROOT%src\AxCopilot.Installer\AxCopilot.Installer.csproj"
set "INSTALLER_DIR=%ROOT%src\AxCopilot.Installer"
set "OUT=%ROOT%dist"
set "APP_OUT=%OUT%\AxCopilot"
set "ENCRYPTOR_OUT=%OUT%\AxKeyEncryptor"
set "PAYLOAD_ZIP=%INSTALLER_DIR%\payload.zip"
set "INSTALLER_EXE=%INSTALLER_DIR%\bin\Release\net48\AxCopilot_Setup.exe"
set "RUNTIME=win-x64"
set "OBFUSCATOR_EXE=%ROOT%tools\obfuscator\obfuscator.exe"
set "OBFUSCATOR_CONFIG=%ROOT%tools\obfuscator\AxCopilot.obfuscation.xml"
:: Kill running app
tasklist /FI "IMAGENAME eq AxCopilot.exe" 2>nul | find /i "AxCopilot.exe" >nul
if %ERRORLEVEL%==0 (
echo [0] Stopping AxCopilot...
taskkill /IM AxCopilot.exe /F >nul 2>nul
timeout /t 2 /nobreak >nul
)
:: Kill legacy process
tasklist /FI "IMAGENAME eq AxCommander.exe" 2>nul | find /i "AxCommander.exe" >nul
if %ERRORLEVEL%==0 (
echo [0] Stopping legacy AxCommander...
taskkill /IM AxCommander.exe /F >nul 2>nul
timeout /t 2 /nobreak >nul
)
call :stop_process "AxCopilot" "AX Copilot"
if errorlevel 1 goto :fail_running
call :stop_process "AxCommander" "legacy AxCommander"
if errorlevel 1 goto :fail_running
if exist "%OUT%" rd /s /q "%OUT%" 2>nul
mkdir "%OUT%"
mkdir "%OUT%\AxCopilot"
mkdir "%OUT%" || goto :fail_dist
mkdir "%APP_OUT%" || goto :fail_dist
mkdir "%ENCRYPTOR_OUT%" || goto :fail_dist
:: ========================================
:: 1. Main app (self-contained, folder)
:: ========================================
echo [1/4] Building main app (self-contained)...
dotnet publish "%APP%" -c Release -o "%OUT%\AxCopilot" --self-contained true --nologo -v quiet
if %ERRORLEVEL% NEQ 0 ( echo [FAILED] Main app build & pause & exit /b 1 )
echo OK - dist\AxCopilot\
if exist "%PAYLOAD_ZIP%" del /q "%PAYLOAD_ZIP%" 2>nul
echo [1/5] Building main app (self-contained %RUNTIME%)...
dotnet publish "%APP%" ^
-c Release ^
-r %RUNTIME% ^
--self-contained true ^
-o "%APP_OUT%" ^
--nologo ^
-v minimal ^
-p:DebugType=None ^
-p:DebugSymbols=false ^
-p:CopyOutputSymbolsToPublishDirectory=false ^
-p:EnableSourceLink=false ^
-p:PublishSingleFile=true ^
-p:EnableCompressionInSingleFile=true ^
-p:IncludeNativeLibrariesForSelfExtract=true ^
-p:PublishReadyToRun=true
if errorlevel 1 goto :fail_app
echo OK - %APP_OUT%
echo.
:: ========================================
:: 2. AxKeyEncryptor (developer tool)
:: ========================================
echo [2/4] Building AxKeyEncryptor (WinForms)...
mkdir "%OUT%\AxKeyEncryptor" 2>nul
dotnet publish "%ENCRYPTOR%" -c Release -o "%OUT%\AxKeyEncryptor" --self-contained false --nologo -v quiet
if %ERRORLEVEL% NEQ 0 ( echo [FAILED] AxKeyEncryptor build & pause & exit /b 1 )
del /q "%OUT%\AxKeyEncryptor\*.pdb" 2>nul
echo OK - dist\AxKeyEncryptor\
echo [2/5] Checking obfuscation / anti-decompile status...
if exist "%OBFUSCATOR_EXE%" (
if exist "%OBFUSCATOR_CONFIG%" (
echo Optional obfuscator found.
echo Running: "%OBFUSCATOR_EXE%"
"%OBFUSCATOR_EXE%" "%OBFUSCATOR_CONFIG%" "%APP_OUT%"
if errorlevel 1 goto :fail_obfuscation
echo OK - obfuscation step completed
) else (
echo WARNING - no external obfuscator configured.
echo Current protection is limited to symbol/source metadata removal only.
)
) else (
echo WARNING - no external obfuscator configured.
echo Current protection is limited to symbol/source metadata removal only.
)
echo.
:: ========================================
:: 3. Create payload ZIP for installer
:: ========================================
echo [3/4] Creating installer payload ZIP...
powershell -NoProfile -Command "Compress-Archive -Path '%OUT%\AxCopilot\*' -DestinationPath 'src\AxCopilot.Installer\payload.zip' -Force"
echo OK - payload.zip
echo [3/5] Building AxKeyEncryptor...
dotnet publish "%ENCRYPTOR%" ^
-c Release ^
-o "%ENCRYPTOR_OUT%" ^
--self-contained false ^
--nologo ^
-v minimal ^
-p:DebugType=None ^
-p:DebugSymbols=false
if errorlevel 1 goto :fail_encryptor
echo OK - %ENCRYPTOR_OUT%
echo.
:: ========================================
:: 4. Build installer (.NET Framework 4.8)
:: ========================================
echo [4/4] Building installer (.NET Framework 4.8)...
dotnet build "%OFFLINE%" -c Release --nologo -v quiet
if %ERRORLEVEL% NEQ 0 ( echo [FAILED] Installer build & pause & exit /b 1 )
copy /Y "src\AxCopilot.Installer\bin\Release\net48\AxCopilot_Setup.exe" "%OUT%\" >nul
echo [4/5] Creating installer payload ZIP...
powershell -NoProfile -Command "Compress-Archive -Path '%APP_OUT%\*' -DestinationPath '%PAYLOAD_ZIP%' -Force"
if errorlevel 1 goto :fail_payload
if not exist "%PAYLOAD_ZIP%" goto :fail_payload
echo OK - %PAYLOAD_ZIP%
echo.
echo [5/5] Building installer (.NET Framework 4.8)...
dotnet build "%INSTALLER%" -c Release --nologo -v minimal
if errorlevel 1 goto :fail_installer
if not exist "%INSTALLER_EXE%" goto :fail_installer_copy
copy /Y "%INSTALLER_EXE%" "%OUT%\" >nul
if errorlevel 1 goto :fail_installer_copy
for %%F in ("%OUT%\AxCopilot_Setup.exe") do echo OK - AxCopilot_Setup.exe (%%~zF bytes)
echo.
:: ========================================
:: Cleanup
:: ========================================
:: Remove debug symbols and metadata (anti-decompile)
del /q "%OUT%\*.pdb" 2>nul
del /q "%OUT%\AxCopilot\*.pdb" 2>nul
del /q "%OUT%\AxCopilot\*.xml" 2>nul
del /q "%OUT%\*.deps.json" 2>nul
del /q "%OUT%\*.runtimeconfig.json" 2>nul
del /q "src\AxCopilot.Installer\payload.zip" 2>nul
echo [Cleanup] Removing debug and metadata files from dist...
call :clean_publish_artifacts "%OUT%"
call :clean_publish_artifacts "%APP_OUT%"
call :clean_publish_artifacts "%ENCRYPTOR_OUT%"
if exist "%PAYLOAD_ZIP%" del /q "%PAYLOAD_ZIP%" 2>nul
echo OK - cleaned
echo.
echo ========================================
echo Build Complete!
echo ========================================
echo.
echo dist\AxCopilot\ Main app (EXE + DLL)
echo dist\AxKeyEncryptor\ Settings Encryptor (dev tool)
echo dist\AxCopilot_Setup.exe Installer (offline, .NET 4.8)
echo %APP_OUT% Main app
echo %ENCRYPTOR_OUT% Settings Encryptor
echo %OUT%\AxCopilot_Setup.exe Installer
echo.
pause
echo Note:
echo - Release/self-contained single-file publish applied
echo - ReadyToRun + compressed single-file bundle enabled
echo - PDB/XML/debug metadata removed from dist output
echo - External obfuscator is only applied when tools\obfuscator is configured
echo.
popd >nul
exit /b 0
:stop_process
set "PROC_NAME=%~1"
set "DISPLAY_NAME=%~2"
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
"$name='%PROC_NAME%';" ^
"$display='%DISPLAY_NAME%';" ^
"$procs = Get-Process -Name $name -ErrorAction SilentlyContinue;" ^
"if (-not $procs) { exit 0 }" ^
"Write-Host ('[0] Stopping ' + $display + '...');" ^
"$imageName = $name + '.exe';" ^
"foreach ($proc in $procs) {" ^
" try { if ($proc.MainWindowHandle -ne 0) { [void]$proc.CloseMainWindow() } } catch { }" ^
"}" ^
"Start-Sleep -Seconds 2;" ^
"& taskkill /IM $imageName /T /F > $null 2> $null;" ^
"Start-Sleep -Seconds 2;" ^
"$stillRunning = Get-Process -Name $name -ErrorAction SilentlyContinue;" ^
"if ($stillRunning) {" ^
" Write-Host ('[FAILED] Could not stop ' + $display + '. Access may be denied or the app may be running with higher privileges.') -ForegroundColor Red;" ^
" exit 1" ^
"}" ^
"exit 0"
if errorlevel 1 exit /b 1
exit /b 0
:clean_publish_artifacts
if not exist "%~1" exit /b 0
del /q "%~1\*.pdb" 2>nul
del /q "%~1\*.xml" 2>nul
del /q "%~1\*.deps.json" 2>nul
del /q "%~1\*.runtimeconfig.json" 2>nul
exit /b 0
:fail_dist
echo [FAILED] dist ??€????밴쉐 ??쎈솭
goto :end_fail
:fail_app
echo [FAILED] main app publish ??쎈솭
goto :end_fail
:fail_obfuscation
echo [FAILED] obfuscation ??€???쎈솭
goto :end_fail
:fail_encryptor
echo [FAILED] AxKeyEncryptor publish ??쎈솭
goto :end_fail
:fail_payload
echo [FAILED] payload.zip ??밴쉐 ??쎈솭
goto :end_fail
:fail_installer
echo [FAILED] installer build ??쎈솭
goto :end_fail
:fail_installer_copy
echo [FAILED] installer exe 癰귣벊沅???쎈솭
goto :end_fail
:fail_running
echo [FAILED] running AX Copilot process could not be stopped cleanly
goto :end_fail
:end_fail
if exist "%PAYLOAD_ZIP%" del /q "%PAYLOAD_ZIP%" 2>nul
popd >nul
exit /b 1

View File

@@ -4969,3 +4969,7 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎.
- Document update: 2026-04-06 15:48 (KST) - The countdown bar still updates through `DispatcherTimer`, but the actual close action is now guaranteed by the background delay path. This reduces cases where encouragement popups remain visible after the configured display duration.
- Document update: 2026-04-06 16:02 (KST) - Relaxed AX Cowork document-generation defaults so the planner/assembler path no longer collapses to `html + professional` whenever explicit arguments are omitted. `DocumentPlannerTool.cs` now resolves output format and mood from `Llm.DefaultOutputFormat`, `Llm.DefaultMood`, document type, and request keywords instead of hardcoding HTML/professional defaults.
- Document update: 2026-04-06 16:02 (KST) - `DocumentAssemblerTool.cs` now mirrors that resolution logic at assembly time, supporting `auto` as a first-class format input and inferring `docx/html/markdown` plus `corporate/dashboard/minimal/creative/professional` mood variants from title/section intent. This reduces repetitive HTML-style outputs and brings AX closer to the more request-driven document flow seen in `claw-code`.
- Document update: 2026-04-06 16:14 (KST) - Strengthened the default deployment hardening profile in `AxCopilot.csproj` Release settings by enabling `Optimize`, `PublishSingleFile`, `EnableCompressionInSingleFile`, `IncludeNativeLibrariesForSelfExtract`, and `PublishReadyToRun`.
- Document update: 2026-04-06 16:14 (KST) - Updated `build.bat` to pass the same publish properties explicitly, so installer/distribution builds made through the batch script now run as Release self-contained single-file ReadyToRun publishes by default. There is still no external obfuscator in the repo, so protection is improved but not equivalent to full obfuscation.
- Document update: 2026-04-06 16:20 (KST) - Fixed single-file compatibility warnings introduced by the hardened publish profile. `WebSearchHandler.cs` now uses `AppContext.BaseDirectory` instead of `Assembly.Location` for bundled asset lookup, and `SettingsWindow.xaml.cs` now reads the displayed version from `AssemblyInformationalVersionAttribute` rather than `FileVersionInfo.GetVersionInfo(asm.Location)`.
- Document update: 2026-04-06 16:20 (KST) - Re-ran the standard verification build after those fixes and restored the zero-warning requirement: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify\ -p:IntermediateOutputPath=obj\verify\` now completes with 0 warnings and 0 errors.

View File

@@ -40,10 +40,16 @@
<!-- Release 빌드 시 추가 난독화 설정 -->
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<Optimize>true</Optimize>
<!-- 사용하지 않는 멤버 제거 (IL trimming) -->
<PublishTrimmed>false</PublishTrimmed>
<!-- PDB 제거 -->
<CopyOutputSymbolsToPublishDirectory>false</CopyOutputSymbolsToPublishDirectory>
<!-- 배포판 보호 수준 강화 -->
<PublishSingleFile>true</PublishSingleFile>
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>
<!-- UseWindowsForms의 암묵적 using(System.Windows.Forms)이 WPF의

View File

@@ -20,7 +20,7 @@ public class WebSearchHandler : IActionHandler
/// <summary>내장 검색 엔진 아이콘 폴더 (Assets\SearchEngines)</summary>
private static readonly string _iconDir = Path.Combine(
Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) ?? ".",
AppContext.BaseDirectory,
"Assets", "SearchEngines");
public WebSearchHandler(SettingsService settings) => _settings = settings;

View File

@@ -1446,9 +1446,11 @@ public partial class SettingsWindow : Window
try
{
var asm = System.Reflection.Assembly.GetExecutingAssembly();
// FileVersionInfo 에서 읽어야 csproj <Version> 이 반영됩니다.
var fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(asm.Location);
var ver = fvi.ProductVersion ?? fvi.FileVersion ?? "?";
var infoAttr = asm.GetCustomAttributes(typeof(System.Reflection.AssemblyInformationalVersionAttribute), false)
.OfType<System.Reflection.AssemblyInformationalVersionAttribute>()
.FirstOrDefault();
var info = infoAttr?.InformationalVersion;
var ver = info ?? asm.GetName().Version?.ToString() ?? "?";
// 빌드 메타데이터 제거 (예: "1.0.3+gitabcdef" → "1.0.3")
var plusIdx = ver.IndexOf('+');
if (plusIdx > 0) ver = ver[..plusIdx];