MSBuild와 NuGet
Visual Studio 사랑해요
범 MS계(?) 개발은 언어를 불문하고 항상 비주얼 스튜디오를 사용하여 개발을 진행해왔다. IDE 위에서 개발을 하다보니 NuGet Package Manager를 통해 패키지를 관리하고, Debug, Release로 타겟을 정해여 빌드 후 최종 Publish까지 모두 GUI 위에서 진행하고 있다.
CI? CD? 무엇을 연속적으로 통합하고 배포할 것인가?
CI/CD 이야기가 팀에서 나오면서 소스와 빌드 바이너리를 포함한 모든 아티펙트를 통으로 Git에서 관리하는 형태의 구성을 생각했었다. 기존에는 로컬 컴퓨터에서 개발하고 개발이 완료되었다고 생각하면 그냥 xcopy로 운영서버에 밀어 넣어버렸기 때문에(!) 가장 유사한 형태로 사용할 수 있지 않읗까 하는 기대가 있었다. Push가 들어오면 들어온 지정된 폴더의 바이너리를 운영 서버로 바로 배포해버리면
빌드 서버가 필요가 없고
빌드 스크립트를 짤 필요가 없고
더 빨리 배포가 되는 장점이 있다. 과연 장점인지 명확하지 않은 부분이 있는데 관점에 따라
환경에 따른 아티펙트를 빌드할 수 없고(예를 들어 스테이징, 운영을 별도 빌드하여 두 바이너리를 별도 빌드하여 푸쉬를 날려야 함)
테스트를 돌리기가 애매하며
빌드 실패에 따른 리스트가 걸러지지 않는 다고도 볼 수 있겠다. 사실 이러면 소스야 형상관리가 되겠지만 xcopy로 배포하던 시절과 큰 차이가 없을 수(사실 이렇게만 해도 어떤 파일을 누가 배포한지는 알 수 있으니 굉장히 큰 장점이기는 하다)도 있다. 그래서 소스 통합과 배포 사이에 어떻게든 빌드와 테스트를 쑤셔 넣기 위한 첫 걸음으로 빌드 스크립트를 구성해보려고 한다.
패키지 복원과 빌드. Nuget & MSBuild
빌드를 수행하기 위해서는
- 빌드에 필요한 파일을 모으고(=소스 파일 외에 패키지 등을 다운로드 받고)
- 파일을 이용하여 빌드를 수행 하는 두 가지 과정이 필요하다. .Net Framework 프로젝트에서는 Nuget으로 패키지를 관리하므로 1번을 Nuget.exe로 2번을 msbuild.exe로 수행할 수 있다.
Nuget
Nuget은 패키지 관리자다. npm, maven 등의 다른 프레임워크의 패키지 관리자와 얼추 동일하며 다음과 같은 특징이 있다
- 공용 저장소 : nuget.org
- packages.config에서 참조 패키지 관리(.NETCore, NuGet4.0+에서는 PackageReference 사용)
기본 명령
참조 패키지 복원을 위한 기본 명령은 다음과 같다.
nuget.exe restore project.sln
npm install과 동일한 동작으로 이해하고 있으며 nuget.exe의 경우 packages.config를 참조하지만 ??packages.config에 대한 설정이 project.sln에 포함되어 있다? 좀 더 살펴보자.
프로젝트가 packages.config대신 PakageReference를 사용하고 있다면 nuget대신 msbuild를 사용하여 바로 복원할 수 있다.
msbuild -t:restore
MSBuild
빌드하기 위한 리소스를 다 모았으니 빌드를 진행해보자. Visual Studio를 이용하여 프로젝트를 생성하면 기본적으로 Debug와 Release 타겟이 생성된다. 이를 이용하여 Debug와 Release용 바이너리를 빌드할 수 있는데 배포를 위한 Release빌드 생성 커맨드는 다음과 같다.
msbuild.exe project.csproj -t:release
robocopy
빌드가 이루어졌으니 robocopy를 통하여 배포를 진행해보자.
robocopy /mir source dest
.gitlab-ci
이제 이 모든 과정을 gitlab에서 매 Push마다 자동 수행될 수 있도록 하자.
deploy failed : robocopy 리턴값과 unix exit code
robocopy를 돌리면 정상적으로 카피가 끝나면 exit code로 1을 던진다. GitLab은 CI/CD 동작 판단에 unix exit code를 사용하는데, 1은 general error이다. 즉, 카피가 정상적으로 이루어지면 error가 뜨면서 build failed가 올라온다! 그래서 빌드 스크립트에서 결과 확인 부분을 조금 만져줘야 성공을 시킬 수 있다. robocopy의 exit code중 0(0x00), 1(0x01), 2(0x10), 4(0x100)는 카피가 정상종료되었다고 볼 수 있으므로 8미만(0x111)의 값이 오면 0으로 강제 설정하여 빌드를 성공으로 만들 수 있다. 이를 배포 스크립트에 반영해보면,
(robocopy /mir source dest /w:1 /r:1); if( $lastexitcode -lt 8 ) { $lastexitcode = 0 }
최종 .gitlab-ci
위 배포 스크립트를 반영한 최종 스크립트는 다음과 같다.
좀 더 볼 것. 단일 솔루션, 복수 프로젝트
nuget package 위치가 꼬인다ㅜㅠ 같이 참조가 걸려있더라도 쓰는게 좋은 것 같기도하고.
참고
- [펌]실전! 지속적인 통합 10편: MSBuild 따라하기 : https://megustaria.tistory.com/3
- NuGet : https://docs.microsoft.com/ko-kr/nuget/what-is-nuget
- Stop gitlab runner to not remove a directory : https://stackoverflow.com/questions/42948062/stop-gitlab-runner-to-not-remove-a-directory
- Specify project file of a solution using msbuild : https://stackoverflow.com/questions/13915636/specify-project-file-of-a-solution-using-msbuild
- Robocopy exit alwsy with status code 1 and hence failing the job even if with all success : https://gitlab.com/gitlab-org/gitlab/-/issues/31956
- https://wiki.kldp.org/HOWTO/html/Adv-Bash-Scr-HOWTO/exitcodes.html
- How to stop robocopy from exiting the build? : https://stackoverflow.com/questions/44504795/how-to-stop-robocopy-from-exiting-the-build
- robocopy exit codes : https://ss64.com/nt/robocopy-exit.html