birchholt 직접 해보고 남기는 기록

Hugo가 없는 맥에서, 설치하지 않고 정해진 버전으로 한 번만 빌드해 봤습니다

Hugo가 깔려 있지 않은 맥에서 사이트를 딱 한 번 빌드해 볼 일이 생겼습니다. 배포 쪽은 Hugo 0.165.0으로 고정해 뒀으니 확인도 같은 버전으로 해야 의미가 있습니다. brew install hugo를 치면 그 시점의 최신판이 시스템에 남고 저는 그걸 원하지 않았습니다. 설치 없이, 버전을 정해서, 쓰고 나면 지워지게 하는 방법을 정리했습니다.

여행 가방에서 공구 하나만 꺼내 쓰고 다시 넣어 두는 손과, 열지 않은 채 놓인 커다란 공구 상자

아래 명령과 출력은 2026-10-04에 macOS 26.6.2(Apple Silicon)에서 처음부터 다시 돌려 얻은 것입니다.

macOS용은 압축 파일이 아니라 설치 프로그램입니다

압축 끈으로 묶인 꾸러미 대신 봉인 도장이 찍힌 상자만 줄지어 놓인 우체국 선반

다른 운영체제처럼 tar.gz를 받아 풀면 되겠다고 생각했습니다. 릴리스 자산(릴리스 페이지에 올라온 파일 목록)을 GitHub API로 뽑아 봤습니다.

$ curl -sS https://api.github.com/repos/gohugoio/hugo/releases/tags/v0.165.0
# 받은 JSON의 assets 중 darwin 파일 이름만 추린 결과
hugo_0.165.0_darwin-universal.pkg
hugo_extended_0.165.0_darwin-universal.pkg
hugo_extended_withdeploy_0.165.0_darwin-universal.pkg
hugo_withdeploy_0.165.0_darwin-universal.pkg

맥용은 .pkg 네 개뿐입니다. 리눅스용에는 tar.gz가 그대로 있고 최신판 v0.167.0도 같습니다. 릴리스 200개를 거슬러 보니 바뀐 시점이 분명했습니다. v0.152.2(2025-10-24)까지는 darwin-universal.tar.gz였고 v0.153.0(2025-12-19)부터 .pkg입니다. 그 릴리스 노트에 「For MacOS, we now build signed and notarised pkg installers」라고 적혀 있습니다 (v0.153.0). 서명과 공증(애플이 이 파일을 검사했다는 확인)을 받은 설치 프로그램으로 바꾼 것입니다.

Hugo의 macOS 설치 문서는 2026-10-04에도 「Extract the archive」라고만 안내합니다. 예전 스크립트가 받던 darwin-universal.tar.gz는 0.153.0 이후 릴리스 자산에 없습니다.

설치 프로그램을 열지 않고 안에 든 것만 꺼냅니다

포장 상자를 정해진 선반에 올리지 않고 작업대 위에서 조심스럽게 열어 내용물만 꺼내는 사람

더블클릭하기 전에 어디에 깔려는지부터 봤습니다. macOS에 기본으로 있는 pkgutil로 상자를 엽니다.

$ pkgutil --expand-full hugo_extended_0.165.0_darwin-universal.pkg x
$ find x
x
x/Bom
x/Payload
x/PackageInfo
x/Payload/hugo

PackageInfo에 설치 위치가 적혀 있습니다.

install-location="/usr/local/bin" auth="root"

그대로 설치했다면 관리자 권한으로 /usr/local/bin에 들어갔을 겁니다. 저는 x/Payload/hugo 파일 하나만 있으면 됩니다.

--expand-full의 -full이 중요합니다. --expand만 쓰면 Payload가 gzip으로 압축된 묶음 그대로 남습니다. -full을 붙여야 실행 파일이 풀려 나옵니다.

받은 파일이 원본과 같은지도 확인했습니다. 릴리스마다 체크섬 파일(파일 지문 목록)이 같이 올라옵니다.

$ grep " hugo_extended_0.165.0_darwin-universal.pkg$" hugo_0.165.0_checksums.txt \
    | shasum -a 256 -c -
hugo_extended_0.165.0_darwin-universal.pkg: OK

pkgutil --check-signature로 보면 애플 공증도 trusted로 나옵니다.

꺼낸 바이너리는 이렇게 확인했습니다.

$ x/Payload/hugo version
hugo v0.165.0-76a5e1880ab46688155b02e99bab9be2a6134492+extended darwin/arm64 BuildDate=2026-08-12T14:26:28Z VendorInfo=gohugoio

$ pkgutil --pkgs | grep -i hugo     # 설치 기록
(출력 없음)
$ command -v hugo                   # PATH
(출력 없음)

버전과 extended 여부가 한 줄에 보이고 설치 기록도 PATH(명령어를 찾는 폴더 목록)에도 없으니 시스템에는 아무것도 들어가지 않았습니다.

받는 데 2.3초, 푸는 데 0.3초 걸렸습니다. .pkg는 42,474,795바이트이고 풀어 낸 hugo는 약 119MB입니다. 인텔·애플 칩용을 한 파일에 담은 유니버설 바이너리라 큽니다(lipo -archs → arm64 x86_64).

PATH는 그 명령 한 줄에만 붙입니다

등불을 든 사람이 한 걸음 앞만 비추며 좁은 길을 지나고, 지나간 자리는 다시 어두워지는 장면

이 사이트는 빌드를 build.sh로 감싸 두었고 그 첫 줄이 hugo가 PATH에 있는지 봅니다. 버전을 실제로 막는 검사도 거기 들어 있습니다. 그래서 그냥 돌리면 이렇게 멈춥니다.

$ ./build.sh
🔴 hugo 가 설치돼 있지 않다.  brew install hugo

셸 설정 파일은 건드리지 않고 그 명령 한 번에만 PATH 앞에 꺼낸 폴더를 붙였습니다.

$ env PATH="$PWD/../run/x/Payload:$PATH" ./build.sh
✅ hugo 0.165.0 (extended) — 최소 0.165.0 충족
 Pages            │ 18
 Paginator pages  │  1
 Static files     │  2
 Aliases          │ 10
Total in 72 ms

빌드가 끝났다고 확인이 끝난 건 아닙니다. 결과물 안을 grep으로 봤습니다.

$ grep -o '<meta name=generator[^>]*>' public/index.html
<meta name=generator content="Hugo 0.165.0">
$ test -f public/404.html && echo 있음
있음
$ grep -o '<loc>' public/sitemap.xml | wc -l
15

generator 줄은 어느 버전이 이 HTML을 만들었는지 결과물이 스스로 밝히는 자리입니다. 404.html이 있는지, sitemap(사이트 주소 목록 파일)에 주소가 몇 개인지도 같이 봤습니다.

버전을 바꿔 끼우면 차이가 바로 보입니다

같은 열쇠 구멍에 모양이 조금씩 다른 열쇠 셋을 차례로 대 보는 손

같은 방식으로 0.164.0과 0.167.0도 꺼냈습니다. 폴더만 다르면 여러 버전이 부딪히지 않습니다. 낮은 버전은 build.sh가 막았습니다.

$ env PATH=".../hugo-0.164.0/x/Payload:$PATH" ./build.sh
🔴 hugo 0.164.0 은 최소 요구 버전 0.165.0 보다 낮다.

최신 0.167.0은 통과했고 두 결과물을 비교하니 다른 파일이 하나였습니다.

$ diff -rq public-0.165.0 public-0.167.0
Files public-0.165.0/index.html and public-0.167.0/index.html differ

< <meta name=generator content="Hugo 0.165.0"
> <meta name=generator content="Hugo 0.167.0"

generator 한 줄 말고는 결과물이 같았습니다. 이 사이트는 0.167.0으로 올려도 결과가 바뀌지 않는다는 걸 설치 없이 확인한 셈입니다. 버전을 올리기 전에 이렇게 비교해 보는 데 쓰기 좋습니다.

설치하지 않아도 흔적은 남습니다

빈 방을 떠나며 문가에 놓인 작은 상자 하나를 챙겨 가방에 넣는 손님

시스템에 깔지는 않았지만 Hugo가 빌드하면서 만드는 것들이 있습니다.

$ "$HUGO" config | grep -i cachedir
cachedir = '/Users/<사용자>/Library/Caches/hugo_cache'

이번 사이트에서는 빈 하위 폴더만 생겼지만 모듈 캐시(:cacheDir/modules)도 이 아래를 씁니다. HUGO_CACHEDIR 환경 변수를 주면 cachedir가 그 경로로 바뀌는 걸 확인했고 작업 폴더 안으로 넣었습니다. 결과물도 -d로 작업 폴더에 쓰게 했습니다.

brew로 깔았다면 달랐을 것

가게 진열대에서 오늘 들어온 새 물건만 집을 수 있는 손님과, 창고 선반에서 날짜 붙은 상자를 골라 꺼내는 사람

brew는 이번에 설치하지 않았습니다. formula(brew의 설치 정의 파일)와 공개 API만 읽어서 비교했습니다.

이번 방식brew install hugo
버전0.153.0 이후 릴리스 중 원하는 것(그 전은 tar.gz)그 시점 stable 하나(읽은 날 0.167.0)
버전 고정용 formula해당 없음hugo@… 없음
바이너리 출처Hugo 공식 빌드(VendorInfo=gohugoio)Homebrew가 소스에서 빌드
남는 것작업 폴더(지우면 끝)brew가 관리하는 설치본

formulae.brew.sh/api/formula/hugo.json에서 stable: 0.167.0, versioned_formulae: []를 확인했고, formula 소스는 extended withdeploy 태그로 빌드합니다. 설치 시간과 용량은 재지 않았습니다.

계속 쓸 거면 brew가 편합니다. 특정 버전으로 한 번 확인하고 끝낼 거면 이번 방식이 더 맞았습니다.

다음에 꺼내 쓸 한 파일

작업이 끝난 뒤 쓰던 공구를 천에 말아 끈으로 묶어 두는 손

위 과정을 스크립트 하나로 묶었습니다. 인자는 버전과 사이트 경로입니다.

#!/usr/bin/env bash
# 사용: ./hugo-once.sh 0.165.0 /path/to/site
set -euo pipefail
V="$1"; SITE="$2"
WORK="$(mktemp -d)"
PKG="hugo_extended_${V}_darwin-universal.pkg"
BASE="https://github.com/gohugoio/hugo/releases/download/v${V}"

cd "$WORK"
curl -sSLfO "$BASE/$PKG"
curl -sSLfO "$BASE/hugo_${V}_checksums.txt"
grep " $PKG\$" "hugo_${V}_checksums.txt" | shasum -a 256 -c -
pkgutil --expand-full "$PKG" x
HUGO="$WORK/x/Payload/hugo"
"$HUGO" version

cd "$SITE"
HUGO_CACHEDIR="$WORK/cache" "$HUGO" --gc --minify -d "$WORK/public"
echo "결과물: $WORK/public"

빈 상태에서 세 번 돌린 전체 시간은 3.54.1초, 그중 Hugo 빌드는 7983ms였습니다. 작업 폴더는 162MB(pkg 41MB, 바이너리 119MB, 결과물 2.7MB)였고 확인 뒤 rm -rf "$WORK"로 지웠습니다.

없는 버전을 넣으면 curl: (56) The requested URL returned error: 404로 바로 멈춥니다. -f를 빼 보니 curl은 성공으로 끝나고 Not Found 9바이트짜리 .pkg가 남았습니다. 실패는 pkgutil 단계(Could not open product archive)에서야 납니다. -f는 빼지 않는 게 좋습니다.