diff --git a/.editorconfig b/.editorconfig index 9d08ceb..5e2af3c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,17 +1,17 @@ root = true -# 기본은 LF +# 기본? LF [*] end_of_line = lf insert_final_newline = true charset = utf-8 -# Windows 전용 스크립트는 CRLF를 원하면(선택 사항): +# Windows ?용 ?크립트??CRLF??하??택 ?항): [*.bat] end_of_line = crlf [*.ps1] end_of_line = crlf -# Bash/셸 스크립트는 반드시 LF +# Bash/???크립트??반드??LF [*.sh] end_of_line = lf \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index dfe0770..dd1a7fc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,9 @@ # Auto detect text files and perform LF normalization -* text=auto +* text=auto eol=lf + +# Windows-specific scripts: CRLF +*.bat text eol=crlf +*.ps1 text eol=crlf + +# Shell scripts: LF +*.sh text eol=lf diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index f0e409a..6e218ac 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,39 +1,39 @@ - -# 코드 어시스턴트를 위한 빠른 안내 (20–50줄) + +# 코드 시나리오에 대한 빠른 이해 (2025년 0월) -이 저장소는 QEMU/libvirt VM 내부에서 qemu-guest-agent를 통해 명령을 실행하는 소형 셸 유틸리티들과 RPM/DEB/MSI 패키징을 제공합니다. AI 코딩 에이전트가 즉시 생산적으로 작업할 수 있도록 발견 가능한 핵심 정보를 간결하게 정리했습니다. +이 저장소는 QEMU/libvirt VM 환경에서 qemu-guest-agent를 통해 명령을 실행하는 형편성 유틸리티와 RPM/DEB/MSI 패키징을 제공합니다. AI 코딩 어시스턴트가 즉시 산출물을 만들 수 있도록 프로젝트의 주요 구조와 발견 가능한 핵심 정보를 간결하게 정리합니다. - 개요 - - 주요 스크립트: `bin/vm_exec.sh`, `bin/agent_policy_fix.sh`, `bin/cloud_init_auto.sh` — 공통 로직은 `lib/`(특히 `common.sh`, `cloud_init_common.sh`, 파서들)에 위치합니다. - - 빌드/패키지: `Makefile`에서 `rpm`, `deb`, `windows` 타깃을 실행합니다. 릴리스와 빌드는 GitHub Actions(`.github/workflows/`)로 자동화되어 있습니다. + - 주요 스크립트: `bin/vm_exec.sh`, `bin/agent_policy_fix.sh`, `bin/cloud_init_auto.sh` 및 공통 로직은 `lib/`(특히 `common.sh`, `cloud_init_common.sh`, 서버측 설치) + - 빌드/패키지: `Makefile`에서 `rpm`, `deb`, `windows` 타깃을 실행합니다. 릴리즈 시 빌드는 GitHub Actions(`.github/workflows/`)에서 자동화됩니다. -- 주요 개발/운영 흐름 - - 로컬 설치: `chmod +x install.sh ; sudo ./install.sh` (자세한 내용은 `INSTALL.md`). +- 주요 개발/운영 워크플로우 + - 로컬 설치: `chmod +x install.sh ; sudo ./install.sh` (자세한 사용은 `INSTALL.md`). - 패키지 빌드: `make rpm`, `make deb`, `make windows` (Windows는 `powershell` 호출 포함). - - 도구 실행 예: `vm_exec -l|-w|-d [options]` (`bin/vm_exec.sh`, `docs/usage_vm_exec.md` 참고). + - 구동 실행 예: `vm_exec -l|-w|-d [options]` (`bin/vm_exec.sh`, `docs/usage_vm_exec.md` 참고). -- 코드/런타임 규약 (구체적) - - 셸 스크립트는 POSIX/Bash 스타일로 작성되어 있습니다. 설치 시 `lib/*`이 `/usr/local/lib/ablestack-qemu-exec-tools`로 복사되고 실행 시 해당 경로에서 `source` 합니다 (Makefile 참조). - - `virsh qemu-agent-command` 호출 결과를 `jq`로 파싱하는 패턴이 많습니다 (`bin/vm_exec.sh`). `jq` 의존성을 염두에 두세요. - - 표/CSV 출력 정규화 파서는 `lib/parse_linux_table.sh`, `lib/parse_windows_table.sh`, `lib/parse_csv.sh` 입니다. 출력 파싱이 필요하면 이들 재사용을 우선시하세요. - - 지역화: `lib/cloud_init_common.sh`는 로케일을 감지해 한국어/영어 메시지를 출력합니다. 사용자 출력 추가 시 `_IS_KO` 플래그를 존중하세요. +- 코드/작성 규약 (구체적) + - 모든 스크립트는 POSIX/Bash 스크립트로 작성되어 있습니다. 설치 시 `lib/*`을 `/usr/local/lib/ablestack-qemu-exec-tools`에 복사하고 실행 시 해당 경로에서 `source` 합니다(Makefile 참조). + - `virsh qemu-agent-command` 호출 결과에 `jq`를 파싱하는 패턴이 많습니다 (`bin/vm_exec.sh`). `jq` 존재를 확인하세요. + - CSV 출력 규칙에서는 `lib/parse_linux_table.sh`, `lib/parse_windows_table.sh`, `lib/parse_csv.sh` 등이 있으며 출력 파싱이 필요하면 이들 라이브러리를 우선 사용하세요. + - 지역화: `lib/cloud_init_common.sh`에서 로케일을 감지하여 한국어 메시지를 출력합니다. 사용자 출력 추출 시 `_IS_KO` 플래그를 존중하세요. -- 검증(실무) 팁 - - 저장소에 자동 단위 테스트는 없습니다. 스크립트 문법 검사는 `bash -n ` 로 수행하세요. 수정 후에는 가능한 경우 테스트 VM에서 `vm_exec`로 스모크 실행 권장. - - 패키징 관련 변경은 `make deb` / `make rpm` / `make windows`로 빌드 확인을 수행하세요. +- 검증 및 품질 + - 프로젝트의 자동화 수준은 높습니다. 스크립트 문법 검증은 `bash -n ` 으로 실행하세요. 특정 에러가 있는 경우 테스트 VM에서 `vm_exec`를 시뮬레이션 실행 권장. + - 패키지 관련 변경 시 `make deb` / `make rpm` / `make windows`로 빌드 검증하세요. -- 버전/릴리스 관련 - - 버전 정보는 `VERSION` 파일에서 읽습니다. 버전을 갱신하고 태그(`git tag vX.Y.Z`)를 푸시하면 빌드/릴리스 워크플로우가 동작합니다. - - Windows MSI 빌드는 `windows/msi/` 내부 스크립트(`build-msi.ps1`)를 사용합니다. +- 버전/릴리즈 관리 + - 버전 정보는 `VERSION` 파일에서 관리합니다. 버전을 갱신하고 태그(`git tag vX.Y.Z`)를 생성 시 빌드/릴리즈 워크플로우가 자동으로 작동합니다. + - Windows MSI 빌드는 `windows/msi/` 내 스크립트(`build-msi.ps1`)를 사용합니다. -- 통합 포인트 및 주의사항 - - 외부 바이너리(예: `virsh`, `jq`, `dpkg-deb`, `rpmbuild`, `powershell`)에 강하게 의존합니다. 절대 경로 하드코딩을 피하고 명령어 이름을 그대로 사용하세요. - - 개발 중에는 `LIBDIR` 변수를 임시로 조정하거나 저장소 루트에서 실행하여 `/usr/local` 설치를 대신할 수 있습니다. +- 통합 및 주의사항 + - 필수 바이너리(예: `virsh`, `jq`, `dpkg-deb`, `rpmbuild`, `powershell`)는 강하게 존재하니 필요 경로 하드코딩 없이 명령어 이름을 그대로 사용하세요. + - 개발 중에는 `LIBDIR` 변수를 임시 조정하거나 저장소 루트에서 실행하여 `/usr/local` 설치와 유사한 환경을 만들 수 있습니다. - 참고 파일 (변경 시 우선 확인) - - `bin/vm_exec.sh` — 핵심 명령 동작과 옵션 - - `lib/common.sh`, `lib/cloud_init_common.sh` — 공용 유틸리티와 cloud-init 관련 로직 - - `docs/usage_vm_exec.md`, `docs/usage_agent_policy_fix.md` — 사용자 예제 및 사용법 - - `Makefile`, `rpm/ablestack-qemu-exec-tools.spec`, `deb/control`, `windows/msi/*` — 패키징 관련 스크립트/템플릿 + - `bin/vm_exec.sh` 주요 명령 작업 설명 + - `lib/common.sh`, `lib/cloud_init_common.sh` 공용 유틸리티 및 cloud-init 관련 로직 + - `docs/usage_vm_exec.md`, `docs/usage_agent_policy_fix.md` 사용법 세부 내용 + - `Makefile`, `rpm/ablestack-qemu-exec-tools.spec`, `deb/control`, `windows/msi/*` 패키지 관련 스크립트/템플릿 -추가로 한국어 예제나 로컬 개발 절차(예: Windows에서 MSI 빌드 상세 단계 등)를 넣고 싶으면 어느 부분을 확장할지 알려주세요. 제가 바로 반영하겠습니다. +추가 한국어 제안이나 로컬 개발 차원(예: Windows에서 MSI 빌드 시 계정 문제 등)에서 느끼는 부분을 알려주세요. 바로 반영하겠습니다. diff --git a/.github/workflows/build-winpe-core.yml b/.github/workflows/build-winpe-core.yml index dc9dfa0..7afdb45 100644 --- a/.github/workflows/build-winpe-core.yml +++ b/.github/workflows/build-winpe-core.yml @@ -58,8 +58,8 @@ jobs: WORKDIR: C:\winpe_work OUTDIR: C:\out - # Microsoft Learn(ADK/WinPE Add-on)에서 안내되는 fwlink를 사용. - # 향후 linkid 변경 가능성이 있으니, 실패 시 이 2개 URL만 갱신하면 됩니다. + # Microsoft Learn(ADK/WinPE Add-on)에서 제공하는 fwlink를 사용. + # 추후 linkid 변경 가능성이 있으므로 실패 시 URL을 갱신하면 됩니다. ADK_SETUP_URL: "https://go.microsoft.com/fwlink/?linkid=2289980" WINPE_ADDON_URL: "https://go.microsoft.com/fwlink/?linkid=2289981" @@ -341,4 +341,4 @@ jobs: path: | C:\out\*.iso C:\out\SHA256SUMS - retention-days: ${{ inputs.retention_days }} \ No newline at end of file + retention-days: ${{ inputs.retention_days }} diff --git a/.github/workflows/build-winpe-release.yml b/.github/workflows/build-winpe-release.yml index 43f7d70..06eb332 100644 --- a/.github/workflows/build-winpe-release.yml +++ b/.github/workflows/build-winpe-release.yml @@ -36,12 +36,12 @@ jobs: - name: Attach assets to GitHub Release uses: softprops/action-gh-release@v2 with: - # 어떤 태그 릴리즈에 붙일지 명시 (tag push 이벤트 기준) + # 어떤 태그 릴리즈에 붙일지 명시 (tag push 이벤트 기반) tag_name: ${{ github.ref_name }} - # 릴리즈 본문/노트는 build.yml이 통합 생성(권장) → 여기서는 건드리지 않음 + # 릴리즈 본문/노트는 build.yml에서 통합 생성(권장) 이므로 건드리지 않음 generate_release_notes: false files: | dist/*.iso dist/SHA256SUMS - # 산출물 누락 시 조기 실패(릴리즈 누락 방지) - fail_on_unmatched_files: true \ No newline at end of file + # 파일 매칭 실패 시 조기 실패(릴리즈 실패 방지) + fail_on_unmatched_files: true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3a55952..d3e1b2d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,14 +11,14 @@ permissions: # NOTE (WinPE ISO) # - WinPE ISO는 별도 워크플로우(WinPE Core/Test/Release)에서 생성/관리되는 것이 기본이다. -# - 본 build.yml에서는 "사전 준비된 WinPE ISO"가 리포지토리 내에 존재하거나, -# 또는 동일 워크플로우 산출물로 준비된 경우에만 통합 ISO에 포함한다. -# - 파일이 없으면 통합 ISO 빌드는 실패시키지 않고 "미포함"으로 진행한다. +# - build.yml에서 "사전 준비된 WinPE ISO"가 리포지터리에 미리 존재하거나 +# 또는 별도 워크플로우의 아티팩트로 준비된 경우에만 통합 ISO를 포함한다. +# - 별도로 통합 ISO 빌드를 실패해도 패키지 생성하고 "미포함"으로 진행한다. jobs: - # ─────────────────────────────────────────────── + # ====================================================================== # 1) RPM BUILD (Rocky 9 + 10 병행) - # ─────────────────────────────────────────────── + # ====================================================================== build-rpm: runs-on: ubuntu-latest strategy: @@ -35,12 +35,12 @@ jobs: - name: Install build deps run: | set -e - # 🔧 Rocky 10.1 이후 rpm-sequoia + OpenSSL 조합이 바뀌면서 - # 컨테이너 베이스 레이어와 라이브러리 버전이 어긋나는 경우가 있어 - # → 먼저 distro-sync 로 rpm / openssl 계열을 리포 상태와 맞춘다. + # 만약 Rocky 10.1 이후 rpm-sequoia + OpenSSL 조합이 바뀌면 + # 컨테이너 베이스에 이미 설치된 라이브러리 버전이 맞지 않는 경우가 있어 + # 그래서 distro-sync로 rpm / openssl 계열의 리포 상태를 맞춘다. dnf -y distro-sync - # 빌드에 필요한 도구 설치 + # 빌드에 필요한 의존성 설치 dnf -y install make rpm-build tar git dnf-plugins-core createrepo_c - name: Build RPM @@ -56,7 +56,7 @@ jobs: cp rpmbuild/RPMS/*/*.rpm "$WORKDIR"/ echo "[INFO] Downloading runtime dependencies..." - # spec의 Requires를 기반으로 의존성 전부 모아 저장 + # spec의 Requires를 기반으로 존재하는 모든 것을 모아둠 dnf download --resolve --alldeps \ --destdir="$WORKDIR" \ bash jq libvirt-client cloud-init || true @@ -64,7 +64,7 @@ jobs: echo "[INFO] Creating repo metadata..." createrepo_c "$WORKDIR" - # 아티팩트 디렉토리 정리 + # 아티팩트 디렉터리 정리 mkdir -p release/rpm mv repo release/rpm/ @@ -73,11 +73,11 @@ jobs: name: rpm-package-${{ matrix.os_version }} path: release/rpm/** - # ─────────────────────────────────────────────── + # ====================================================================== # 1-A) HANGCTL RPM BUILD (Rocky 9 only) - # - 목표: Rocky9 ABLESTACK Host에서만 설치되도록 spec(%pre 가드) 반영 - # - build.yml에서는 "빌드/리포 구성/배포"만 담당 (가드는 spec에서 처리) - # ─────────────────────────────────────────────── + # - 목표: Rocky9 ABLESTACK Host에서만 설치되도록 spec(%pre 가서 반영 + # - build.yml에서는 "빌드/리포 구성/배포"를 담당 (가정은 spec에서 처리) + # ====================================================================== build-hangctl-rpm: runs-on: ubuntu-latest container: @@ -102,7 +102,7 @@ jobs: SPEC="rpm/ablestack_vm_hangctl.spec" test -f "${SPEC}" || (echo "[ERR] Missing spec: ${SPEC}" >&2; exit 2) - # VERSION / RELEASE는 repo 루트의 VERSION 파일을 동일하게 사용 + # VERSION / RELEASE를 repo 루트의 VERSION 파일에서 일괄적으로 사용 VERSION="$(. ./VERSION; printf "%s" "$VERSION" | tr -d '\r\n[:space:]')" RELEASE="$(. ./VERSION; printf "%s" "$RELEASE" | tr -d '\r\n[:space:]')" GIT_HASH="$(git rev-parse --short HEAD 2>/dev/null || echo nogit)" @@ -111,9 +111,9 @@ jobs: mkdir -p rpmbuild_hangctl/{BUILD,RPMS,SOURCES,SPECS,SRPMS} - # tarball 이름은 spec의 Source0 규칙과 일치해야 하므로, - # 기존 패키지들과 동일하게 'spec 파일 basename'을 prefix로 사용 - # (즉, ablestack_vm_hangctl-.tar.gz) + # tarball 이름이 spec의 Source0 규칙에 맞춰야 함 + # 기존 패키지와 동일하게 'spec 파일 basename'의 prefix를 사용 + # (예 ablestack_vm_hangctl-.tar.gz) NAME_HANGCTL="ablestack_vm_hangctl" TMP_TGZ="$(mktemp /tmp/${NAME_HANGCTL}-${VERSION}.tar.gz.XXXXXX)" tar czf "${TMP_TGZ}" \ @@ -164,9 +164,9 @@ jobs: name: hangctl-rpm-package path: release/hangctl-rpm/** - # ─────────────────────────────────────────────── - # 1-B) V2K RPM BUILD (별도 경로, 기존 빌드 프로세스 훼손 금지) - # ─────────────────────────────────────────────── + # ====================================================================== + # 1-B) V2K RPM BUILD (별도 경로, 기존 빌드 프로세스 손상 금지) + # ====================================================================== build-v2k-rpm: runs-on: ubuntu-latest container: @@ -181,12 +181,12 @@ jobs: run: | set -euo pipefail - # ───────────────────────────────────────────── - # Rocky 9.6 vault 고정 (AirGap 수집용) - # - 기존 rocky.repo는 mirrorlist만 있고 baseurl이 없을 수 있음 - # - releasever=9.6만으로는 해결 안됨 (baseurl이 없어서) + # ====================================================================== + # Rocky 9.6 vault 고정 (AirGap 집합) + # - 기존 rocky.repo의 mirrorlist를 고정 baseurl로 변경 + # - releasever=9.6만으로는 해결 안됨 (baseurl이 바뀌어야 함) # - 따라서 9.6 vault baseurl을 가진 repo를 "직접" 생성 - # ───────────────────────────────────────────── + # ====================================================================== cat > /etc/yum.repos.d/rocky-vault-9.6.repo <<'EOF' [rocky-vault-9.6-baseos] name=Rocky Linux 9.6 - BaseOS (vault) @@ -210,8 +210,8 @@ jobs: dnf clean all dnf -y --disablerepo="*" --enablerepo="rocky-vault-9.6-*" makecache - # curl 충돌 회피: curl-minimal ↔ curl - # - 컨테이너 베이스에 curl-minimal이 깔린 경우, 필요 시 allowerasing으로 정리 + # curl 충돌 회피: curl-minimal vs curl + # - 컨테이너 베이스에 curl-minimal이 깔린 경우, 필요 시 allowerasing으로 처리 dnf -y --disablerepo="*" --enablerepo="rocky-vault-9.6-*" install \ --allowerasing ca-certificates curl-minimal || true @@ -222,8 +222,8 @@ jobs: run: | set -euo pipefail - # ❗ 핵심: rocky.repo를 "rewrite"하지 않는다. - # rocky.repo는 mirrorlist만 있고 baseurl이 없는 케이스가 있어 + # 핵심: rocky.repo를 "rewrite"하는 대신 + # rocky.repo의 mirrorlist를 고정 baseurl로 변경하는 케이스가 있어 # mirrorlist를 주석 처리하면 "baseurl 없음"으로 dnf가 즉시 실패한다. # 따라서 9.6 vault baseurl을 가진 repo 파일을 별도로 생성해서 사용한다. @@ -253,16 +253,16 @@ jobs: # vault repo만으로 캐시 생성 dnf -y --disablerepo="*" --enablerepo="rocky-vault-9.6-*" makecache - # EPEL은 build-time only (nbd 등 수집 목적) → 여기서 활성화 가능 - # ❗ vault repo만 켠 상태에서는 epel-release 패키지를 'dnf install'로 찾을 수 없음(No match). - # → EPEL bootstrap rpm을 URL로 직접 설치한다. - # (빌드/릴리즈 단계에서만 외부 접근 허용하고, 결과 rpm은 ISO에 포함) + # EPEL을 build-time only (nbd 패키지 목적) 로기적으로 구성하기 위해 + # vault repo만으로는 캐시 생성 시 epel-release 패키지를 'dnf install'로 찾을 수 없음(No match). + # 그래서 EPEL bootstrap rpm의 URL을 직접 다운로드한다. + # (빌드/릴리즈 계열에서만 임시로 사용하고, 결과 rpm에 ISO에 포함) EPEL_RPM_URL="https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm" echo "[INFO] Installing EPEL release RPM: ${EPEL_RPM_URL}" curl -fL -o /tmp/epel-release.rpm "${EPEL_RPM_URL}" rpm -Uvh --nosignature /tmp/epel-release.rpm - # EPEL repo id 자동 탐지(epel/epel-9/epel-next 등 케이스 대응) + # EPEL repo id 자동 감지(epel/epel-9/epel-next 케이스 모두 고려) EPEL_REPO_ID="$(dnf repolist all 2>/dev/null | awk 'tolower($1) ~ /^epel/ {print $1; exit}')" if [[ -z "${EPEL_REPO_ID}" ]]; then echo "[ERR] EPEL repo id not found after installing epel-release." >&2 @@ -272,14 +272,14 @@ jobs: echo "[INFO] Detected EPEL repo id: ${EPEL_REPO_ID}" echo "EPEL_REPO_ID=${EPEL_REPO_ID}" >> "$GITHUB_ENV" - # vault + epel 캐시 생성(딱 필요한 repo만) + # vault + epel 캐시 생성(필요한 repo만) dnf -y --disablerepo="*" --enablerepo="rocky-vault-9.6-*","${EPEL_REPO_ID}" makecache - name: Build V2K RPM (separate path) run: | set -e - # NOTE: 기존 make rpm 경로와 분리된 V2K 전용 타겟을 사용해야 함 - # (예: make v2k-rpm) + # NOTE: 기존 make rpm 경로를 분리하여 V2K 용 패키지를 사용해야 함 + # (예 make v2k-rpm) make v2k-rpm - name: Verify ablestack_v2k rpm contains bash completion @@ -297,14 +297,14 @@ jobs: WORKDIR="release/v2k/v2k-rpm-rocky9.6" mkdir -p "$WORKDIR" - # V2K 본체 RPM 수집 (프로젝트 산출 경로에 맞춰 필요 시 조정) + # V2K 본체 RPM 수집 (프로젝트 출력 경로에 맞춰 필요 시 조정) find . -type f -name "*.rpm" \( -iname "*ablestack*v2k*.rpm" -o -iname "*ablestack_v2k*.rpm" \) -exec cp -v {} "$WORKDIR"/ \; echo "[INFO] Collecting runtime deps for Air-Gap (Requires closure minus ABLESTACK baseline)..." - # --- 9.6 고정 (중요: 9.7 혼입 차단) --- + # --- 9.6 고정 (중요: 9.7 입력 차단) --- : "${EPEL_REPO_ID:?EPEL_REPO_ID is not set. Configure step must export it to GITHUB_ENV}" - # 옵션 문자열에 따옴표를 넣지 말고, 배열로 전달(파싱 안정성) + # 옵션 문자에 의존하지 말고, 배열로 전달(파싱 정확도) DNF_COMMON_OPTS=( --releasever=9.6 --disablerepo=* @@ -312,11 +312,11 @@ jobs: --setopt=install_weak_deps=False ) - # ablestack-v2k.spec의 Requires와 동일하게 유지 (8개) - # ceph-common: rbd CLI 사용(생성/존재확인) 목적 + # ablestack-v2k.spec의 Requires를 동일하게 처리 (8개) + # ceph-common: rbd CLI 사용(생성/존재 확인) 목적 REQ_PKGS="jq python3 openssl nbd nbdkit nbdkit-vddk-plugin qemu-img libvirt-client" - # baseline 파일(ABLestack 9.6 호스트에서 rpm -qa 기반으로 생성해 커밋) + # baseline 파일(ABLestack 9.6 호스트에서 rpm -qa 기반으로 생성된 커밋) BASELINE_FILE="rpm/v2k_baseline_pkgs_ablestack_9.6.txt" if [[ ! -f "${BASELINE_FILE}" ]]; then echo "[ERR] Missing baseline file: ${BASELINE_FILE}" >&2 @@ -326,10 +326,10 @@ jobs: fi echo "[INFO] Sanity check: required pkgs availability (repo view)" - # nbd/nbdkit/qemu-img가 repo에서 안 보이면 여기서 즉시 실패해야 함(지금 문제 조기 차단) + # nbd/nbdkit/qemu-img가 repo에서 보이지 않으면 즉시 실패해야 하는 이유: 문제 조기 차단) dnf -q "${DNF_COMMON_OPTS[@]}" repoquery --available ${REQ_PKGS} >/dev/null - # Requires 의존성 클로저 산출 (패키지명만, 중복 제거) + # Requires 존재하는 것으로 출력 (패키지명만, 중복 제거) # dnf-plugins-core의 repoquery 사용 dnf -y "${DNF_COMMON_OPTS[@]}" install dnf-plugins-core >/dev/null dnf -q "${DNF_COMMON_OPTS[@]}" repoquery \ @@ -337,15 +337,15 @@ jobs: --qf '%{name}' \ ${REQ_PKGS} | sort -u > /tmp/v2k_dep_closure.txt - # (중요) Direct requires는 baseline subtract와 무관하게 반드시 포함 + # (중요) Direct requires와 baseline subtract를 무조건 반드시 해야 함 printf '%s\n' ${REQ_PKGS} | sort -u > /tmp/v2k_must_have.txt - # baseline에 이미 있는 패키지 제외 (없는 것만 다운로드) + # baseline에 없는 패키지 제외 (있는 것만 다운로드) sort -u "${BASELINE_FILE}" > /tmp/v2k_baseline.txt comm -23 /tmp/v2k_dep_closure.txt /tmp/v2k_baseline.txt > /tmp/v2k_to_download_closure.txt - # (중요) Direct requires는 baseline subtract와 무관하게 반드시 포함 + # (중요) Direct requires와 baseline subtract를 무조건 반드시 해야 함 printf '%s\n' ${REQ_PKGS} | sort -u > /tmp/v2k_must_have.txt # baseline subtract (closure - baseline) @@ -360,18 +360,18 @@ jobs: head -n 50 /tmp/v2k_to_download_candidate.txt || true echo "[INFO] Filtering candidate list by repo availability (STRICT)..." - # ⚠️ 절대 $(cat ...) 쓰지 말 것. 인자 길이/에러/빈값으로 repo 전체가 나오는 사고가 난다. - # xargs로 "실제 repo에 존재하는 패키지명"만 남긴다. + # ⚠️ 주의: $(cat ...) 대신 xargs로 처리해야 하는 이유: 파일 내용이 너무 길거나 빈값으로 repo 전체가 깨지는 경우가 있다. + # xargs로만 repo에 존재하는 패키지를 필터링함 : > /tmp/v2k_to_download_avail.txt cat /tmp/v2k_to_download_candidate.txt \ | xargs -r -n 200 dnf -q "${DNF_COMMON_OPTS[@]}" repoquery --available --qf '%{name}' \ | sort -u > /tmp/v2k_to_download_avail.txt - # langpack 폭발 방지(선제 제거) - 필요 없으면 과감히 제외 + # langpack 관련 방지(완제 제거) - 필요 없으므로 과감히 제외 grep -vE '^glibc-all-langpacks$|^glibc-langpack-' /tmp/v2k_to_download_avail.txt \ | sort -u > /tmp/v2k_to_download.txt - # missing(후보엔 있었는데 repo엔 없는 것) 로깅 + # missing(보고 있었는데 repo에 없는 것) 로깅 comm -23 /tmp/v2k_to_download_candidate.txt /tmp/v2k_to_download_avail.txt > /tmp/v2k_missing_in_repo.txt if [[ -s /tmp/v2k_missing_in_repo.txt ]]; then echo "[WARN] Missing in enabled repos (skipped):" @@ -382,7 +382,7 @@ jobs: echo "[INFO] Final to-download sample (first 50):" head -n 50 /tmp/v2k_to_download.txt || true - # 최종 방어: nbd/nbdkit/qemu-img는 반드시 있어야 한다 + # 최종 방어: nbd/nbdkit/qemu-img가 반드시 있어야 함 for p in nbd nbdkit qemu-img; do if ! grep -qx "$p" /tmp/v2k_to_download.txt && ! grep -qx "$p" /tmp/v2k_baseline.txt; then echo "[ERR] Required package '$p' is neither in baseline nor in final download list." >&2 @@ -390,7 +390,7 @@ jobs: fi done - # download (여기서는 exclude를 걸어도 되지만, 위에서 이미 제거했으니 없어도 됨) + # download (기본적으로 exclude 걸어주지만 에러에 대비해서 중복 제거하도록 처리함) if [[ -s /tmp/v2k_to_download.txt ]]; then xargs -r dnf "${DNF_COMMON_OPTS[@]}" download --destdir="$WORKDIR" < /tmp/v2k_to_download.txt else @@ -409,9 +409,9 @@ jobs: name: v2k-rpm-package path: release/v2k/** - # ─────────────────────────────────────────────── + # ====================================================================== # 2) DEB BUILD (Ubuntu 22.04 / 24.04) - # ─────────────────────────────────────────────── + # ====================================================================== build-deb: strategy: matrix: @@ -435,16 +435,16 @@ jobs: set -e sudo apt-get update -y - # ISO용 로컬 APT 리포 생성 공간 + # ISO를 위한 로컬 APT 리포 생성 공간 mkdir -p repo/pool find build/deb -type f -name "*.deb" -exec cp {} repo/pool/ \; cd repo/pool echo "[INFO] Starting dependency collection..." - # 이미 처리한 패키지 이름 기억 + # 한번에 처리할 패키지 이름 기억 seen_pkgs="" - # 새 .deb이 추가되는 동안 반복 + # 각 .deb의 추가되는 동안 반복 new_pkgs=1 while [ "$new_pkgs" -eq 1 ]; do new_pkgs=0 @@ -456,7 +456,7 @@ jobs: fi seen_pkgs="$seen_pkgs $P" - # Depends 파싱: 콤마 분리, 버전/괄호 제거, 대체(|)는 첫 토큰, :any 제거 + # Depends 파싱: 콤마 분리, 버전/괄호 제거, 또는|를 공백으로 큰, :any 제거 DEPS=$(dpkg-deb -f "$deb" Depends | \ tr ',' '\n' | \ sed -E 's/\(.*\)//g' | \ @@ -469,7 +469,7 @@ jobs: for dep in $DEPS; do [ -n "$dep" ] || continue - # 이미 pool에 같은 패키지 내려받은 경우는 넘어감 + # 이미 pool에 같은 패키지가 다운로드된 경우에는 건너뜀 if ls -1 ${dep}_*.deb >/dev/null 2>&1; then continue fi @@ -490,7 +490,7 @@ jobs: mkdir -p repo/dists/stable/main/binary-amd64 ( cd repo && dpkg-scanpackages pool /dev/null | gzip -9c > dists/stable/main/binary-amd64/Packages.gz ) - # 배포용 위치로 정리 + # 배포판 설정을 정리 mv repo "deb-ubuntu${{ matrix.os_version }}" mkdir -p release/deb mv deb-ubuntu${{ matrix.os_version }} release/deb/ @@ -500,9 +500,9 @@ jobs: name: deb-package-${{ matrix.os_version }} path: release/deb/** - # ─────────────────────────────────────────────── + # ====================================================================== # 3) WINDOWS MSI BUILD - # ─────────────────────────────────────────────── + # ====================================================================== build-windows: runs-on: windows-latest steps: @@ -524,9 +524,9 @@ jobs: name: msi-package path: windows/msi/out/* - # ─────────────────────────────────────────────── + # ====================================================================== # 4) RELEASE + ISO CREATION (VirtIO 통합 포함) - # ─────────────────────────────────────────────── + # ====================================================================== release: needs: [build-rpm, build-hangctl-rpm, build-deb, build-windows, build-v2k-rpm] runs-on: ubuntu-latest @@ -535,8 +535,8 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - # workflow_run에서는 기본 브랜치가 checkout될 수 있으므로, - # WinPE Release Build가 빌드한 동일 커밋으로 고정 + # workflow_run에서 기본 브랜치로 checkout하므로 + # WinPE Release Build가 빌드한 파일 커밋으로 고정 ref: ${{ github.event.workflow_run.head_sha }} - name: Resolve tag from workflow_run @@ -554,7 +554,7 @@ jobs: fi echo "RELEASE_TAG=$TAG" >> "$GITHUB_ENV" - # dist/ 아래에 모든 아티팩트 내려받기 + # dist/ 아래에 모든 아티팩트 다운로드 - uses: actions/download-artifact@v4 with: path: dist @@ -568,8 +568,8 @@ jobs: mkdir -p release/winpe echo "[INFO] Downloading WinPE ISO assets for tag: ${RELEASE_TAG}" - # WinPE workflow가 dist/*.iso 와 SHA256SUMS를 릴리즈에 붙였으므로, 여기서 가져온다. - # 파일명이 고정이 아닐 수 있어 패턴으로 수집 + # WinPE workflow가 dist/*.iso 와 SHA256SUMS를 릴리즈에 붙여놓았으므로 여기서 가져옴 + # 파일명이 고정되지 않을 수 있으므로 패턴으로 수집 gh release download "${RELEASE_TAG}" \ -p "*winpe*.iso" \ -p "SHA256SUMS" \ @@ -592,7 +592,7 @@ jobs: set -e mkdir -p release/rpm release/deb release/msi release/assets/v2k/wheels release/v2k - echo "🔍 Collecting RPM repos..." + echo "모두 Collecting RPM repos..." for ver in 9 10; do SRC_DIR=$(find dist -type d -path "*/rpm-package-${ver}/release/rpm/repo/rpm-rocky${ver}" | head -n 1) if [ -d "$SRC_DIR" ]; then @@ -611,7 +611,7 @@ jobs: fi done - echo "🔍 Collecting HANGCTL RPM repo (Rocky 9)..." + echo "모두 Collecting HANGCTL RPM repo (Rocky 9)..." HANGCTL_DIR="$(find dist -type d \( \ -path "*/hangctl-rpm-package/release/hangctl-rpm/repo/hangctl-rpm-rocky9" -o \ -path "*/hangctl-rpm-package/repo/hangctl-rpm-rocky9" \ @@ -624,7 +624,7 @@ jobs: echo "[WARN] No HANGCTL RPM repo found (hangctl-rpm-package)." fi - echo "🔍 Collecting DEB repos..." + echo "모두 Collecting DEB repos..." for ver in 22.04 24.04; do SRC_DIR=$(find dist -type d \( \ -path "*/deb-package-${ver}/release/deb/deb-ubuntu${ver}" -o \ @@ -639,7 +639,7 @@ jobs: fi done - echo "🔍 Collecting MSI artifacts..." + echo "모두 Collecting MSI artifacts..." find dist -type f -path "*/msi-package/*" -exec cp {} release/msi/ \; - name: Collect V2K RPM repo (Rocky 9.6) into ISO tree @@ -674,10 +674,10 @@ jobs: set -euo pipefail python3 -m pip download -d release/assets/v2k/wheels "pyvmomi==8.0.3.0.1" - # VirtIO Windows Stable ISO 다운로드 후 "amd64 드라이버 + guest tools(qemu-ga 포함)"만 추출 + # VirtIO Windows Stable ISO 다운로드 "amd64 드라이버 + guest tools(qemu-ga 포함)" 추출 # - 중첩 ISO 금지 # - DISM/WinPE에서 바로 INF 경로 지정 가능 - # - 용량 절감(amd64 only) + # - 용량 감소(amd64 only) - name: Download & extract VirtIO (amd64-only + guest tools) run: | set -e @@ -685,9 +685,9 @@ jobs: curl -fL -o dist/virtio-win.iso \ "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso" - # 1) DISM용: 핵심 드라이버 디렉토리의 amd64만 추출 - # (VirtIO ISO 버전에 따라 w11/2k22/2k19 등 하위 폴더명이 달라도, amd64 경로만 살리면 된다) - # 7z는 와일드카드 패턴을 허용하므로, */amd64/* 패턴으로 추출한다. + # 1) DISM을 위한 핵심 드라이버 디렉터리를 amd64로 추출 + # (VirtIO ISO 버전에 따라 w11/2k22/2k19 등 폴더명이 바뀌므로 amd64 경로로 일괄 처리함) + # 7z의 와일드카드 리턴을 활용하여 */amd64/* 패턴으로 추출함 7z x -y -o"dist/virtio-src" dist/virtio-win.iso \ "viostor/*/amd64/*" \ "vioscsi/*/amd64/*" \ @@ -699,12 +699,12 @@ jobs: "vioinput/*/amd64/*" \ "vioser/*/amd64/*" || true - # 2) Guest tools(qemu-ga 포함): exe/msi는 루트 또는 특정 위치에 있을 수 있으므로 '찾아서' 포함 - # - 둘 다 있으면 둘 다 포함(현장 선택 가능) + # 2) Guest tools(qemu-ga 포함): exe/msi를 루트 또는 특정 위치에서 찾아서 포함 + # - 가능한 모든 것을 포함(장점 선택 가능) 7z x -y -o"dist/virtio-src" dist/virtio-win.iso "virtio-win-guest-tools.exe" || true 7z x -y -o"dist/virtio-src" dist/virtio-win.iso "virtio-win-gt-*.msi" || true - # 3) release/virtio로 복사 (존재하는 것만) + # 3) release/virtio에 복사 (존재하는 것만) for d in viostor vioscsi NetKVM Balloon qemupciserial viorng pvpanic vioinput vioser; do if [[ -d "dist/virtio-src/${d}" ]]; then mkdir -p "release/virtio/${d}" @@ -714,7 +714,7 @@ jobs: if [[ -f "dist/virtio-src/virtio-win-guest-tools.exe" ]]; then cp -a "dist/virtio-src/virtio-win-guest-tools.exe" "release/virtio/" fi - # MSI는 버전별 파일명이 달라질 수 있어 glob 복사 + # MSI의 버전으로 파일명이 바뀌므로 glob 복사 shopt -s nullglob for m in dist/virtio-src/virtio-win-gt-*.msi; do cp -a "$m" "release/virtio/" @@ -733,8 +733,8 @@ jobs: mkdir -p release/winpe # 우선순위 - # 1) 동일 워크플로우 아티팩트(dist) 내 winpe*.iso - # 2) 리포지토리 내 사전 준비 파일(assets/winpe/*.iso 또는 winpe/*.iso) + # 1) 별도 워크플로우의 아티팩트(dist) 에서 winpe*.iso + # 2) 리포지터리에 미리 준비된 파일(assets/winpe/*.iso 또는 winpe/*.iso) WINPE_ISO="" WINPE_ISO="$(find dist -type f -iname '*.iso' -iname '*winpe*' 2>/dev/null | head -n 1 || true)" if [[ -z "$WINPE_ISO" ]]; then @@ -815,27 +815,26 @@ jobs: param( [string]$Message = "ABLESTACK tools installation is complete. The system needs to reboot.", [string]$Title = "ABLESTACK", - [int] $Options = 0x0, # 0x0: OK only / 0x4: Yes/No 등 + [int] $Options = 0x0, # 0x0: OK only / 0x4: Yes/No ?? [int] $TimeoutSeconds = 60 # 사용자 입력 최대 대기 시간 (초) ) $shell = New-Object -ComObject WScript.Shell - # 두 번째 인자에 타임아웃(초)을 넣는다. + # 두 번째 호출에서만 사용자에게 물어보는 이유 # 반환값: - # -1 : 타임아웃 + # -1 : 시간 초과됨 # 1 : OK - # 6 : Yes (0x4 옵션일 때) - $result = $shell.Popup($Message, $TimeoutSeconds, $Title, $Options) + # 6 : Yes (0x4 옵션에서) $result = $shell.Popup($Message, $TimeoutSeconds, $Title, $Options) if ($result -eq -1 -or $result -eq 1 -or $result -eq 6) { Write-Host "Prompt_Reboot: user accepted or no response within timeout. Rebooting..." - # 재부팅 바로 수행 (원래 스펙대로) + # 시스템을 바로 재부팅 (원래 의도대로) Restart-Computer -Force - # MSI CustomAction이라면 실제로는 여기서 + # MSI CustomAction에서는 여기서 끝나고, 실제로는 여기까지 실행되지 않음 # msiexec /i ... /norestart + REBOOT=ReallySuppress - # 를 쓰고 시스템 재부팅은 별도 관리자 스크립트에서 처리하는 식도 가능하지만, - # 지금 구조에서는 Restart-Computer 써도 됨. + # 그러고 시스템 재부팅/종료를 별도 관리자 스크립트에서 처리하는 것도 가능하지 않음 + # 지금 구조에서는 Restart-Computer 방식이 적합함 } else { Write-Host "Prompt_Reboot: user canceled reboot (result = $result)." @@ -919,7 +918,7 @@ jobs: # Completion message if ($global:rebootNeeded) { Prompt_Reboot "ABLESTACK tools installation completed. Reboot now?" "ABLESTACK" 0x0 30 - # → 30초만 기다린 뒤 자동 재부팅 + # 그렇지 않으면 30초만 기다리고 자동으로 진행함 } else { Show-Info "Installation completed successfully. No reboot is required." } @@ -943,7 +942,7 @@ jobs: exit /b 1 ) - REM --- 설치 절차 끝 --- + REM --- 설치 차례 --- mkdir "C:\ProgramData\AbleStack" 2>nul echo ok> "C:\ProgramData\AbleStack\autoinstall.done" echo [OK] Installation finished. @@ -1024,11 +1023,11 @@ jobs: sudo rm -f /etc/yum.repos.d/ablestack-qemu-exec-tools.repo sudo dnf clean all - # ─────────────────────────────────────────────── + # ====================================================================== # HANGCTL add-on (ABLESTACK Host only, Rocky 9 only) # - Install ablestack_vm_hangctl from ISO local repo # - If not ABLESTACK host / not Rocky9 -> skip - # ─────────────────────────────────────────────── + # ====================================================================== if [[ "${PRETTY_NAME:-}" == *"ABLESTACK"* && "${MAJOR_VERSION:-}" == "9" ]]; then echo "[INFO] ABLESTACK Host on Rocky 9 detected. Installing HANGCTL add-on..." @@ -1057,12 +1056,12 @@ jobs: echo "[INFO] HANGCTL add-on skipped (not ABLESTACK host or not Rocky 9)." fi - # ─────────────────────────────────────────────── + # ====================================================================== # V2K add-on (ABLESTACK Host only) # - based on bin/v2k_test_install.sh # - Air-Gap: pyvmomi installs from ISO offline wheels # - NO ldconfig / NO global linker path modifications - # ─────────────────────────────────────────────── + # ====================================================================== if [[ "${PRETTY_NAME:-}" == *"ABLESTACK"* && "${VERSION_ID:-}" == 9.6* ]]; then echo "[INFO] ABLESTACK 9.6 detected. Installing V2K add-on..." @@ -1214,7 +1213,7 @@ jobs: echo "[WARN] pyvmomi wheels dir not found in ISO: ${WHEEL_DIR}" fi - # 5) WinPE ISO 배치 (오프라인 드라이버 주입/복구용) + # 5) WinPE ISO 배치 (에어갭 환경에서 드라이버 주입/복구용) # - 통합 ISO에 winpe/*.iso 가 포함된 경우에만 설치 WINPE_SRC_DIR="${ISO_MNT}/winpe" WINPE_DST_DIR="/usr/share/ablestack/v2k/winpe" @@ -1252,7 +1251,7 @@ jobs: ;; esac - # --- 설치 절차 끝 --- + # --- 설치 차례 --- mkdir -p /var/lib/ablestack echo ok > /var/lib/ablestack/autoinstall.done echo "[INFO] Installation complete." @@ -1276,7 +1275,7 @@ jobs: echo "${DESC}" >> release_notes.md echo >> release_notes.md - # 태그 정보 보강 (혹시 몰라서) + # 태그 정보 보강 (직접 몰라도 됨) git fetch --tags --force || true # 직전 태그 찾기 (없으면 최초 릴리즈로 간주) @@ -1306,7 +1305,7 @@ jobs: - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: - # workflow_run 트리거에서는 tag_name을 명시해야 릴리즈 대상이 정확해짐 + # workflow_run 트리거에서는 tag_name을 명시해야 릴리즈가 제대로 생성됨 tag_name: ${{ env.RELEASE_TAG }} body_path: release_notes.md files: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 891a525..3bff3b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,5 +18,5 @@ jobs: - name: Run Make (all targets) run: | - echo "🔍 Running CI build (basic validation)" + echo "? Running CI build (basic validation)" make all diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..86d38e6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "editor.formatOnSave": true, + "files.eol": "\n", + "editorconfig.enabled": true +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 72e20ec..93df6db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,48 +1,60 @@ # Changelog -이 문서는 ablestack-qemu-exec-tools 프로젝트의 모든 변경 내역을 기록합니다. -형식은 [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)를 참고했으며, -버전 표기는 [Semantic Versioning](https://semver.org/)을 따릅니다. +이 문서는 ablestack-qemu-exec-tools 프로젝트의 모든 변경 이력을 기록합니다. +형식은 [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)를 참고하며 +버전 기기는 [Semantic Versioning](https://semver.org/)을 따릅니다. --- ## [Unreleased] + ### Added + - 새로운 기능 추가 예정 목록 ### Changed + - 변경 예정 사항 ### Fixed + - 수정 예정 버그 목록 --- ## [0.3.0] - 2025-09-25 + ### Added -- Windows MSI 빌드 스크립트(`build-msi.ps1`) 버전/릴리즈/깃해시 반영 -- Product.wxs에 설치 버전/릴리즈/깃해시를 MSI 속성과 레지스트리에 기록하는 기능 추가 -- GitHub Actions `build.yml` → 릴리즈 자동화 (RPM/DEB/MSI 빌드 및 Release 업로드) + +- Windows MSI 빌드 스크립트(`build-msi.ps1`) 버전/릴리즈 깃해시 반영 +- Product.wxs에 설치 버전/릴리즈 깃해시를 MSI 생성 시 메트리에 기록하는 기능 추가 +- GitHub Actions `build.yml` 에 릴리즈 자동화(RPM/DEB/MSI 빌드 및 Release 업로드) ### Changed + - Makefile: `VERSION` 파일 참조 방식으로 변경 -- RPM spec/DEB control: 동적으로 버전/릴리즈 반영되도록 수정 +- RPM spec/DEB control: 자동으로 버전/릴리즈 반영하도록 수정 ### Fixed -- Windows 빌드 시 WiX 확장 처리 안정화 -- DEB 패키징 control 파일 버전 하드코딩 문제 수정 + +- Windows 빌드 시 WiX 장치 처리 수정 +- DEB 패키지 control 파일 버전 하드코딩 문제 수정 --- ## [0.2.0] - 2025-09-15 + ### Added -- Linux용 vm_exec.sh, agent_policy_fix.sh 스크립트 안정화 -- DEB/RPM 패키징 초기 지원 + +- Linux용 vm_exec.sh, agent_policy_fix.sh 스크립트 작성 +- DEB/RPM 패키지 초기 지원 - GitHub Actions ci.yml 추가 --- ## [0.1.0] - 2025-08-30 + ### Added + - 프로젝트 초기 생성 -- 기본 VM 원격 명령 실행 기능 (Linux/Windows) +- 기본 VM 게스트 명령 실행 기능 (Linux/Windows) diff --git a/INSTALL.md b/INSTALL.md index 245a1f3..7fa0813 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,11 +1,12 @@ # ablestack-qemu-exec-tools 설치 및 배포 가이드 -## 1. 준비 사항 +## 1. 준비사항 + - **개발 환경** - GitHub 저장소 접근 권한 - `git` 명령어 사용 가능 - **CI/CD** - - GitHub Actions 활성화 (`.github/workflows/ci.yml`, `build.yml` 존재) + - GitHub Actions 설정됨(`.github/workflows/ci.yml`, `build.yml` 존재) - **필수 파일** - `VERSION` (버전 및 릴리즈 번호 정의) - `Makefile` @@ -16,6 +17,7 @@ --- ## 2. 버전 관리 + 버전은 `VERSION` 파일에서 관리합니다. ``` @@ -23,25 +25,29 @@ VERSION=0.3.0 RELEASE=1 ``` -- **VERSION**: 주/부/패치 버전 (`0.3.0`) -- **RELEASE**: 동일 버전 내 빌드 차수 (`1`, `2` …) +- **VERSION**: 메이저/마이너 버전 (`0.3.0`) +- **RELEASE**: 동일 버전 내 빌드 차수 (`1`, `2` 등) --- ## 3. 로컬 빌드 (옵션) + ### RPM 빌드 (RHEL 계열) + ```bash make rpm ls rpmbuild/RPMS/*/*.rpm ``` ### DEB 빌드 (Ubuntu 계열) + ```bash make deb ls build/deb/*.deb ``` ### MSI 빌드 (Windows) + ```powershell make windows Get-ChildItem windows/msi/out/*.msi @@ -49,29 +55,32 @@ Get-ChildItem windows/msi/out/*.msi --- -## 4. GitHub Actions – CI -- **동작 조건**: `push` (main, develop 브랜치), `pull_request` -- **실행 내용**: - - `make all` 실행 (기본 빌드 검증) -- **목적**: 코드가 깨지지 않았는지 확인 +## 4. GitHub Actions 기반 CI + +- **?�작 조건**: `push` (main, develop 브랜�?, `pull_request` +- **?�행 ?�용**: + - `make all` ?�행 (기본 빌드 검�? +- **목적**: 코드가 깨�?지 ?�았?��? ?�인 --- -## 5. GitHub Actions – Release 빌드 -- **동작 조건**: `git tag vX.Y.Z && git push origin vX.Y.Z` -- **실행 내용**: - - RPM 빌드 (Rocky Linux 9 컨테이너) +## 5. GitHub Actions ??Release 빌드 + +- **?�작 조건**: `git tag vX.Y.Z && git push origin vX.Y.Z` +- **?�행 ?�용**: + - RPM 빌드 (Rocky Linux 9 컨테?�너) - DEB 빌드 (Ubuntu 최신) - MSI 빌드 (Windows + WiX v4) -- **결과물**: - - GitHub Release에 `.rpm`, `.deb`, `.msi` 자동 업로드 -- **릴리즈 노트**: - - `git log` 기반 자동 생성 (직전 태그 이후 커밋 내역) +- **결과�?*: + - GitHub Release??`.rpm`, `.deb`, `.msi` ?�동 ?�로??- **릴리�??�트**: + - `git log` 기반 ?�동 ?�성 (직전 ?�그 ?�후 커밋 ?�역) --- -## 6. 릴리즈 절차 +## 6. 릴리�??�차 + 1. **버전 갱신** + ```bash echo "VERSION=0.3.0" > VERSION echo "RELEASE=1" >> VERSION @@ -79,24 +88,25 @@ Get-ChildItem windows/msi/out/*.msi git commit -m "Bump version to 0.3.0-1" ``` -2. **태그 생성** +2. **?�그 ?�성** + ```bash git tag v0.3.0 git push origin main --tags ``` -3. **GitHub Actions 실행** - - Actions 탭에서 워크플로우 확인 - - 빌드 완료 후 Release 페이지 자동 생성 +3. **GitHub Actions ?�행** + - Actions ??��???�크?�로???�인 + - 빌드 ?�료 ??Release ?�이지 ?�동 ?�성 -4. **릴리즈 확인** - - `Release` 페이지에서 `.rpm`, `.deb`, `.msi` 다운로드 가능 - - 릴리즈 노트 = `git log` 자동 생성 내용 +4. **릴리�??�인** + - `Release` ?�이지?�서 `.rpm`, `.deb`, `.msi` ?�운로드 가?? - 릴리�??�트 = `git log` ?�동 ?�성 ?�용 --- -## 7. 설치 후 확인 (Windows) -MSI 설치 후 PowerShell에서: +## 7. ?�치 ???�인 (Windows) + +MSI ?�치 ??PowerShell?�서: ```powershell Get-ItemProperty "HKLM:\SOFTWARE\AbleStack\QemuExecTools" | @@ -105,14 +115,15 @@ Get-ItemProperty "HKLM:\SOFTWARE\AbleStack\QemuExecTools" | --- -## 8. 유지보수 팁 -- **새 버전 배포** 시에는 `VERSION` 파일만 수정 → 태그 푸시 → Actions 자동 실행 -- **긴급 패치** 시에는 `RELEASE` 값을 증가 (`2`, `3` …) -- **릴리즈 노트 관리**를 더 정교하게 하려면 `CHANGELOG.md`를 추가해도 됨 (현재는 `git log` 기반) +## 8. ?��?보수 ??- **??버전 배포** ?�에??`VERSION` ?�일�??�정 ???�그 ?�시 ??Actions ?�동 ?�행 + +- **긴급 ?�치** ?�에??`RELEASE` 값을 증�? (`2`, `3` ?? +- **릴리�??�트 관�?*�????�교?�게 ?�려�?`CHANGELOG.md`�?추�??�도 ??(?�재??`git log` 기반) --- -# ✅ 요약 -- **ci.yml** → 개발 브랜치/PR 빌드 검증 -- **build.yml** → 태그 기반 정식 릴리즈 빌드 & 자동 업로드 -- **사용자는 VERSION 수정 + 태그 푸시만 하면 됨** +# ???�약 + +- **ci.yml** ??개발 브랜�?PR 빌드 검�? +- **build.yml** ???�그 기반 ?�식 릴리�?빌드 & ?�동 ?�로?? +- **?�용?�는 VERSION ?�정 + ?�그 ?�시�??�면 ??* diff --git a/Makefile b/Makefile index d232ab5..9e559a0 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ HANGCTL_SPEC = rpm/$(HANGCTL_NAME).spec VERSION := $(shell . ./VERSION; printf "%s" "$$VERSION" | tr -d '\r\n[:space:]') RELEASE := $(shell . ./VERSION; printf "%s" "$$RELEASE" | tr -d '\r\n[:space:]') -# Git hash는 실행 시점에서 자동으로 추출 +# Extract git hash dynamically at execution time GIT_HASH := $(shell git rev-parse --short HEAD 2>/dev/null || echo "nogit") INSTALL_PREFIX = /usr/local @@ -50,24 +50,24 @@ all: @echo "VERSION: $(VERSION), RELEASE: $(RELEASE), GIT_HASH: $(GIT_HASH)" install: - @echo "🔧 Installing $(NAME)..." + @echo "Installing $(NAME)..." install -d $(BIN_DIR) install -m 0755 bin/vm_exec.sh $(BIN_DIR)/vm_exec install -m 0755 bin/agent_policy_fix.sh $(BIN_DIR)/agent_policy_fix @if [ -f install.sh ]; then install -m 0755 install.sh $(BIN_DIR)/install_ablestack_qemu_exec_tools; fi install -d $(LIB_TARGET) cp -a lib/* $(LIB_TARGET)/ - @echo "✅ Installed to $(INSTALL_PREFIX)" + @echo "Installed to $(INSTALL_PREFIX)" -##### ───────────────────────────────────────────────────────────── +##### ------------------------------------------------------------ ##### ablestack-qemu-exec-tools: uninstall targets (via uninstall.sh) -##### ───────────────────────────────────────────────────────────── +##### ------------------------------------------------------------ -# uninstall.sh 위치 (리포지토리 루트 기준) +# uninstall.sh path, relative to repository root UNINSTALL_SCRIPT ?= ./uninstall.sh -# 플래그를 Make 변수로 제어할 수 있게 매핑 -# 예) make uninstall NO_PROMPT=1 PURGE=1 REMOVE_ISO=1 +# Map Make variables to uninstall.sh flags +# Example: make uninstall NO_PROMPT=1 PURGE=1 REMOVE_ISO=1 UNINSTALL_FLAGS := ifeq ($(strip $(DRY_RUN)),1) UNINSTALL_FLAGS += --dry-run @@ -94,24 +94,24 @@ endif .PHONY: uninstall uninstall-dry-run uninstall-purge uninstall-remove-iso \ uninstall-keep-bins uninstall-keep-profile uninstall-keep-lib -## 기본 제거(무프롬프트 권장) +## Default uninstall target uninstall: @echo ">> Running $(UNINSTALL_SCRIPT) $(UNINSTALL_FLAGS)" @sudo $(UNINSTALL_SCRIPT) $(UNINSTALL_FLAGS) -## 제거 계획만 출력 +## Print uninstall plan only uninstall-dry-run: @$(MAKE) uninstall DRY_RUN=1 -## 라이브러리까지 완전 삭제(백업 없음), 무프롬프트 +## Remove everything including libraries, no backup uninstall-purge: @$(MAKE) uninstall NO_PROMPT=1 PURGE=1 -## ISO 파일까지 삭제(무프롬프트) +## Remove ISO files as well uninstall-remove-iso: @$(MAKE) uninstall NO_PROMPT=1 REMOVE_ISO=1 -## 선택 유지 옵션들(상황별 조합 가능) +## Keep selected components uninstall-keep-bins: @$(MAKE) uninstall KEEP_BINS=1 @@ -123,7 +123,7 @@ uninstall-keep-lib: rpm: - @echo "📦 Building RPM..." + @echo "Building RPM..." mkdir -p rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} @TMP_TGZ="$$(mktemp /tmp/ablestack-qemu-exec-tools-$(VERSION).tar.gz.XXXXXX)"; \ tar czf "$$TMP_TGZ" \ @@ -138,7 +138,7 @@ rpm: . ; \ mv -f "$$TMP_TGZ" "rpmbuild/SOURCES/ablestack-qemu-exec-tools-$(VERSION).tar.gz" - # spec 파일 복사 (rpm 디렉토리에서 가져오기) + # Copy spec file from rpm directory cp rpm/$(NAME).spec rpmbuild/SPECS/ rpmbuild --noplugins -ba --define "_topdir $(shell pwd)/rpmbuild" \ @@ -147,13 +147,13 @@ rpm: --define "githash $(GIT_HASH)" \ rpmbuild/SPECS/$(NAME).spec - # 산출물 정리 + # Collect build artifacts mkdir -p build/rpm cp rpmbuild/RPMS/noarch/*.rpm build/rpm/ - @echo "✅ RPM package created: build/rpm/" + @echo "RPM package created: build/rpm/" hangctl-rpm: - @echo "📦 Building HANGCTL RPM (isolated)..." + @echo "Building HANGCTL RPM (isolated)..." @test -f "$(HANGCTL_SPEC)" || (echo "[ERR] Missing spec: $(HANGCTL_SPEC)" >&2; exit 2) @mkdir -p rpmbuild_hangctl/{BUILD,RPMS,SOURCES,SPECS,SRPMS} @@ -183,10 +183,10 @@ hangctl-rpm: mkdir -p build/rpm-hangctl cp rpmbuild_hangctl/RPMS/noarch/*.rpm build/rpm-hangctl/ 2>/dev/null || true cp rpmbuild_hangctl/RPMS/*/*.rpm build/rpm-hangctl/ 2>/dev/null || true - @echo "✅ HANGCTL RPM package created: build/rpm-hangctl/" + @echo "HANGCTL RPM package created: build/rpm-hangctl/" v2k-rpm: - @echo "📦 Building V2K RPM (isolated)..." + @echo "Building V2K RPM (isolated)..." @test -f "$(V2K_SPEC)" || (echo "[ERR] Missing spec: $(V2K_SPEC)" >&2; exit 2) @test -f "$(COMPLETIONS_FILE)" || (echo "[ERR] Missing completion file: $(COMPLETIONS_FILE)" >&2; exit 2) @@ -225,7 +225,7 @@ v2k-rpm: cp rpmbuild_v2k/RPMS/noarch/*.rpm build/rpm-v2k/ 2>/dev/null || true cp rpmbuild_v2k/RPMS/*/*.rpm build/rpm-v2k/ 2>/dev/null || true - @echo "🔎 Verifying completion file is included in ablestack_v2k RPM..." + @echo "Verifying completion file is included in ablestack_v2k RPM..." @RPM_FILE="$$(ls -1 build/rpm-v2k/$(V2K_NAME)-*.rpm 2>/dev/null | head -n 1)"; \ if [ -z "$$RPM_FILE" ]; then \ echo "[ERR] Built RPM not found under build/rpm-v2k" >&2; exit 2; \ @@ -233,10 +233,10 @@ v2k-rpm: rpm -qlp "$$RPM_FILE" | grep -qE "bash-completion/completions/$(V2K_NAME)$$" || \ (echo "[ERR] completion file missing in RPM: $$RPM_FILE" >&2; exit 2) - @echo "✅ V2K RPM package created: build/rpm-v2k/" + @echo "V2K RPM package created: build/rpm-v2k/" deb: - @echo "📦 Building DEB..." + @echo "Building DEB..." rm -rf $(DEB_BUILD_DIR) mkdir -p $(DEB_DEBIAN_DIR) $(DEB_BIN_DIR) $(DEB_LIB_DIR) $(DEB_DOC_DIR) @@ -249,18 +249,18 @@ deb: # lib cp -a lib/* $(DEB_LIB_DIR)/ 2>/dev/null || : - # docs & examples + # docs and examples cp -a README.md $(DEB_DOC_DIR)/ @if [ -d docs ]; then cp -a docs/* $(DEB_DOC_DIR)/; fi @if [ -f usage_agent_policy_fix.md ]; then cp -a usage_agent_policy_fix.md $(DEB_DOC_DIR)/; fi @if [ -d examples ]; then cp -a examples/* $(DEB_DOC_DIR)/; fi - # control 파일 치환 (템플릿 -> 실제 버전 적용) + # Replace template values in control file sed -e "s/\$${VERSION}/$(VERSION)/" \ -e "s/\$${RELEASE}/$(RELEASE)/" \ deb/control > $(DEB_DEBIAN_DIR)/control - # postinst 파일 추가 + # Add postinst if present @if [ -f deb/postinst ]; then \ cp deb/postinst $(DEB_DEBIAN_DIR)/postinst; \ chmod 755 $(DEB_DEBIAN_DIR)/postinst; \ @@ -271,15 +271,15 @@ deb: dpkg-deb --build $(DEB_BUILD_DIR) mkdir -p build/deb mv $(DEB_BUILD_DIR).deb build/deb/$(DEB_PKG).deb - @echo "✅ DEB package created: build/deb/$(DEB_PKG).deb" + @echo "DEB package created: build/deb/$(DEB_PKG).deb" windows: - @echo "📦 Building Windows MSI..." + @echo "Building Windows MSI..." powershell -ExecutionPolicy Bypass -File windows/msi/build-msi.ps1 \ -Version $(VERSION) -Release $(RELEASE) -GitHash $(GIT_HASH) mkdir -p build/msi cp windows/msi/out/* build/msi/ || echo "[WARN] No MSI files copied" - @echo "✅ Windows MSI built under build/msi/" + @echo "Windows MSI built under build/msi/" clean: diff --git a/README.md b/README.md index 80e4554..202ed04 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,22 @@ # ablestack-qemu-exec-tools -**QEMU / libvirt 기반 가상머신에 대해 `qemu-guest-agent` 및 libguestfs 기반 제어를 활용하여, 원격 명령 실행·정책 자동화·자동 설치를 지원하는 통합 도구 세트입니다.** +**QEMU / libvirt 기반 가상머신에 대해 `qemu-guest-agent` 와 libguestfs 기반 도구를 활용하여, 비대화형 명령 실행·에이전트 자동화·자동 설치 지원하는 통합 관리 툴입니다.** --- -## 📌 주요 기능 +## 🚀 주요 기능 - **VM 내부 명령 실행** (Linux / Windows) - - `virsh qemu-agent-command` 기반 비침입 원격 제어 + - `virsh qemu-agent-command` 기반 비침투적 도구 - JSON, CSV, TABLE, HEADERS 기반 출력 파싱 - - 스크립트 실행(`--file`) 및 병렬 실행(`--parallel`) 지원 -- **에이전트 정책 자동화 (`agent_policy_fix.sh`)** - - VM 내부에서 qemu-guest-agent 서비스 정책 자동화, 활성화, 자동설치 -- **클라우드 초기화 자동화 (`cloud_init_auto.sh`)** - - VM 생성 시 cloud-init 자동 구성 보조 -- **자동 설치 기능 (`vm_autoinstall.sh`)** 🆕 - > 호스트에서 실행 중인 가상머신에 ISO를 자동 연결하고, OS에 맞는 설치 스크립트를 무인으로 실행 - > (vCenter의 “에이전트 자동 설치” 기능과 유사한 원클릭 설치) + - 스크립트 실행(`--file`) 과 병렬 실행(`--parallel`) 지원 +- **에이전트 정책 자동화(`agent_policy_fix.sh`)** + - VM 부팅 시 qemu-guest-agent 서비스 정책 자동화 생성 및 적용 +- **클라우드 초기화 자동화(`cloud_init_auto.sh`)** + - VM 생성 시 cloud-init 구동 구성 보조 +- **자동 설치 기능 (`vm_autoinstall.sh`)** ⭐ + > 호스트에서 실행 중인 가상머신에 ISO를 자동 연결하고, OS에 맞는 설치 스크립트를 무인으로 실행 + > (vCenter에서 제공하는 자동 설치 기능의 오픈소스 클론 버전) --- @@ -29,82 +29,88 @@ chmod +x install.sh sudo ./install.sh ``` -### 의존성 -호스트에는 다음 패키지가 설치되어 있어야 합니다. +### 호스트 시스템에 다음 패키지가 설치되어 있어야 합니다 -| 구분 | 필수 | 설명 | +| 구분 | 패키지 | 설명 | |------|------|------| -| 기본 | `jq`, `virsh` | libvirt 기반 명령 제어 | -| 오프라인 주입 | `libguestfs-tools`, `virt-install` | virt-copy-in, virt-win-reg 등 필요 | -| 선택 | `virt-xml` | XML 조작 시 안정성 향상 | +| 기본 | `jq`, `virsh` | libvirt 기반 명령 도구 | +| 오프라인 주입 | `libguestfs-tools`, `virt-install` | virt-copy-in, virt-win-reg 필요 | +| 선택 | `virt-xml` | XML 조작 편의성 향상 | --- -## 🧩 구성 파일 구조 +## 📁 구성 파일 구조 ``` bin/ - ├─ vm_exec.sh - ├─ agent_policy_fix.sh - ├─ cloud_init_auto.sh - └─ vm_autoinstall.sh # 🆕 ISO 자동 설치 스크립트 + ├── vm_exec.sh + ├── agent_policy_fix.sh + ├── cloud_init_auto.sh + ├── vm_autoinstall.sh # 자동 ISO 연결 설치 스크립트 lib/ - ├─ libvirt_helpers.sh # libvirt 및 guestfs 헬퍼 함수 - ├─ offline_inject_linux.sh # 오프라인 주입 (Linux) - └─ offline_inject_windows.sh # 오프라인 주입 (Windows) + ├── libvirt_helpers.sh # libvirt 와 guestfs 헬퍼 함수 + ├── offline_inject_linux.sh # 오프라인 주입 (Linux) + ├── offline_inject_windows.sh # 오프라인 주입 (Windows) payload/ - ├─ linux/ablestack-install.service # ISO 루트 install-linux.sh 실행용 systemd unit - └─ windows/ablestack-runonce.ps1 # ISO 루트 install.bat 자동 실행용 PowerShell + ├── linux/ablestack-install.service # ISO 루트 install-linux.sh 실행용 systemd unit + ├── windows/ablestack-runonce.ps1 # ISO 루트 install.bat 자동 실행용 PowerShell ``` --- -## 🚀 주요 사용법 +## 📖 주요 사용법 ### 1. VM 명령 실행 (vm_exec) + ```bash vm_exec -l|-w "" ``` + - `-l` 또는 `--linux` : Linux VM - `-w` 또는 `--windows` : Windows VM -- `--headers`, `--json`, `--table`, `--csv` 등 파싱 옵션 제공 -- `--file