hugo
hugo:exts + docsy + giscus 구성
Categories:

graph LR subgraph gvp6nx1a B[dev-server] <-- proxy --> C[nginx] end C1[prod-server] <-- https --> D[client] C2[github-action] --- C3[contents/<br/>security-properties] C <-- https --> D B -- git --> C2 B -- ssh --> C3 C3 -- ssh --> C1

container 구성
docker-compose.yml
- 배포 폴더
/opt/hugo/data/public/ - 개발 서버이며 여기서 편집 후 운영 적용
- 서버 시작 시 배포 폴더 초기화
- 실시간 새로고침
- baseURL 옵션은 운영 서버 URL 적용 (
sitemap.xml생성에 관여) - environment 옵션 production만 googleanalytics 적용
vi /opt/hugo/docker-compose.yml
services:
hugo:
image: hugomods/hugo:exts
container_name: hugo
networks:
- dev
ports:
- 1313/tcp
user: 1000:1000
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /opt/hugo/data:/src:rw
command: hugo server --cleanDestinationDir --poll 700ms --disableFastRender --noHTTPCache --environment production --minify --baseURL https://dntco43u.github.io --appendPort=false
restart: unless-stopped
networks:
dev:
external: true
docsy 테마 1
- 템플릿은 exts 모듈이 적용된 docsy-example 테마
- 폴더 구조 변경. 기존 구조가 2-depth(
/content/blog/)인데 1-depth(/content/)로 바꾸고 blog로만 사용 /content/search.md는 삭제. 1-depth 구조에서 노출을 막으려면 이것저것 수정해야 하므로- 이미지 중앙 정렬 스타일 추가 2
sudo rm -rf /opt/hugo/data && \
docker run --rm -it \
-p1313/tcp \
-v /opt/hugo/data:/src:rw \
hugomods/hugo:exts \
/bin/sh
hugo mod graph && \
cd / && git clone --depth 1 https://github.com/google/docsy-example.git src && \
cd /src && npm install && \
hugo --minify && \
exit
sudo chown dev:dev -R /opt/hugo/data/ && \
sed -i 's/enableGitInfo: true/enableGitInfo: false/' /opt/hugo/data/hugo.yaml && \
grep "enableGitInfo" /opt/hugo/data/hugo.yaml && \
rm /opt/hugo/data/LICENSE && \
rm /opt/hugo/data/*.md && \
rm /opt/hugo/data/{Dockerfile,docker-compose.yaml} && \
find /opt/hugo/data/ -name ".git*" -exec rm -rf {} \;
vi /opt/hugo/data/hugo.yaml
...
module:
hugoVersion:
extended: true
min: 0.146.0
imports:
- path: github.com/google/docsy
disable: false
...
vi /opt/hugo/data/assets/scss/_styles_project.scss
...
img[src$='#center'] {
display: block;
margin: 1.0rem auto;
max-width: 100%;
height: auto;
}
giscus 댓글 3
- 테마 템플릿의 구성이 구 버전이라면 새 버전에 맞게 폴더 구조 변경
/layouts/partials/->/layouts/_partials/4 - 댓글을 비활성화하고 싶다면 markdown 파라미터에 no_comment: true 추가
mv /opt/hugo/data/layouts/partials /opt/hugo/data/layouts/_partials && \
vi /opt/hugo/data/layouts/_partials/disqus-comment.html
{{ if not .Params.no_comment }}
<style type="text/css">
.giscus, .giscus-frame { width: 90%; }
.container-fluid { height: auto; }
</style>
<div class="page-blank">
<div class="giscus"></div>
<script src="https://giscus.app/client.js"
data-repo="{{site.Params.comments.giscus.repo}}"
data-repo-id="{{site.Params.comments.giscus.repo_id}}"
data-category="{{site.Params.comments.giscus.category}}"
data-category-id="{{site.Params.comments.giscus.category_id}}"
data-mapping="{{site.Params.comments.giscus.mapping}}"
data-strict="{{site.Params.comments.giscus.reactions_enabled}}"
data-reactions-enabled="{{site.Params.comments.giscus.reactions_enabled}}"
data-emit-metadata="0"
data-input-position="{{site.Params.comments.giscus.data_input_position}}"
data-theme="{{site.Params.comments.giscus.theme}}"
data-lang="{{site.Params.comments.giscus.data_lang}}"
crossorigin="anonymous"
async>
</script>
<noscript>Please enable JavaScript to view the <a href="https://giscus.app/">comments powered by Giscus.</a></noscript>
</div>
{{ end }}
vi /opt/hugo/data/hugo.yaml
...
params:
comments:
provider: giscus
giscus:
repo: dntco43u/dntco43u.github.io
repo_id: R***********
category: General
category_id: D*******************
mapping: pathname
reactions_enabled: 0
data_input_position: bottom
# theme: light
theme: catppuccin_frappe
data_lang: en
services:
disqus:
shortname: giscus
기타
vi /opt/hugo/data/hugo.yaml
...
imaging:
quality: 100
...
outputs:
section: [HTML, RSS]
...
services:
googleAnalytics:
ID: G-G********* #https://dntco43u.github.io
...
# 제목 파스칼 케이스 변환 금지
titleCaseStyle: none
# prod-server 캐시
caches:
images:
dir: :cacheDir/images
...
favicon
/opt/hugo/data/static/favicons/favicon.ico
host 구성
deploy_hugo.sh 5
vi /home/dev/.local/bin/deploy_hugo.sh
#!/bin/bash
# hugo clean -> build -> publsh
source /home/dev/.bashrc
source /home/dev/.local/bin/utils.sh
container_name=hugo
hugo_env=$(docker exec -it $container_name hugo version)
hugo_version=$(echo "$hugo_env" | grep -o "hugo v[0-9.]*" | grep -o "[0-9.]*")
if echo "$hugo_env" | grep -q "+extended"; then
hugo_extended=true
else
hugo_extended=false
fi
rm -rf /opt/$container_name/data/public/*
cd /opt/$container_name && docker compose rm -f -s && docker compose up -d && docker exec -it $container_name date +"%Z"
sudo tail -fn0 /var/lib/docker/containers/"$(docker inspect --format="{{.Id}}" $container_name)/local-logs/container.log" | \
# 정적 파일 생성까지 대기
while read -r line; do
echo "$line"
if [[ "$line" == *"Web Server is available"* ]]; then
break
fi
done
# prod-server에서 참조하도록 dev-server 기준의 .env를 생성
echo "HUGO_VERSION=$hugo_version" > /opt/$container_name/data/.env
echo "HUGO_EXTENDED=$hugo_extended" >> /opt/$container_name/data/.env
cat /opt/$container_name/data/.env
# github pages는 속도를 위해 history 항상 삭제
cd /opt/$container_name/data/public || exit
#git rm -r --cached .
rm -rf .git && git init
git remote add origin git@github.com:dntco43u/dntco43u.github.io.git
git add . && git commit -m "update #$HOSTNAME" && git push -u -f origin main
github 구성
deploy-hugo.yml
| Repository secrets | Remarks |
|---|---|
| secrets.GVP6NX1A_KEY | 개발 서버 ssh 키 |
| secrets.GVP6NX1A_ADDR | 개발 서버 ssh 주소 |
| secrets.GVP6NX1A_PORT | 개발 서버 ssh 포트 |
vi /opt/hugo/data/public/.github/workflows/deploy-hugo.yml
name: deploy-hugo
on:
push:
branches:
- main
pull_request:
jobs:
build:
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
steps:
- name: Debug host
run: dig +short txt ch whoami.cloudflare @1.1.1.1 | sed 's/"//g'
- name: Set SSH key
uses: shimataro/ssh-key-action@v2
with:
key: ${{ secrets.GVP6NX1A_KEY }}
name: id_ed25519
known_hosts: 'placeholder'
- name: Adding known hosts
run: ssh-keyscan -H -t ed25519 -p ${{ secrets.GVP6NX1A_PORT }} ${{ secrets.GVP6NX1A_ADDR }} >> ~/.ssh/known_hosts
- name: Debug SSH
run: |
ssh -Q key
ls -alh ~/.ssh
ssh -T -p ${{ secrets.GVP6NX1A_PORT }} dev@${{ secrets.GVP6NX1A_ADDR }} -i ~/.ssh/id_ed25519
- name: Get artifacts from dev-server
run: |
rsync \
-avz \
--delete \
--exclude=".git/" \
-e "ssh -p ${{ secrets.GVP6NX1A_PORT }} -i ~/.ssh/id_ed25519" \
dev@${{ secrets.GVP6NX1A_ADDR }}:/opt/hugo/data/ \
./
- name: Debug rsync
run: |
rsync -V
sudo apt-get update -y && sudo apt-get install -y tree && sudo tree -L 2 -f
- name: Set .env
run: |
. ./.env
echo "HUGO_VERSION=${HUGO_VERSION}" >> $GITHUB_ENV
echo "HUGO_EXTENDED=${HUGO_EXTENDED}" >> $GITHUB_ENV
- name: Setup hugo
uses: peaceiris/actions-hugo@v3
with:
hugo-version: '${{ env.HUGO_VERSION }}'
extended: '${{ env.HUGO_EXTENDED }}'
- name: Install Node.js dependencies
run: |
hugo mod graph
[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true
- name: Restore cache
id: cache-restore
uses: actions/cache/restore@v4
with:
path: ${{ runner.temp }}/hugo_cache
key: hugo-${{ github.run_id }}
restore-keys:
hugo-
- name: Build hugo
env:
TZ: 'Asia/Seoul'
run: |
hugo \
--gc \
--minify \
--environment production \
--baseURL "https://dntco43u.github.io" \
--cacheDir "${{ runner.temp }}/hugo_cache"
- name: Save cache
id: cache-save
uses: actions/cache/save@v4
with:
path: ${{ runner.temp }}/hugo_cache
key: ${{ steps.cache-restore.outputs.cache-primary-key }}
- name: Upload pages
uses: actions/upload-pages-artifact@v3
with:
path: ./public
deploy:
needs: build
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy pages
id: deployment
uses: actions/deploy-pages@v4
SEO
Troubleshooting
posts.md 파일명에 /와 _의 대체로 -사용 6
네이버 서치 어드바이저 SEO
<meta name="description">설명 누락
색인을 포함한 모든 페이지에 description 파라미터 추가
google search console: Duplicate without user-selected canonical
운영이 아닌 개발 서버가 색인되고 있어 원본 주소를 명시적으로 선언. 레이아웃에 canonical 메타 추가 7 (hugo.yaml 구성은 추후 삭제 예정임 8)
vi /opt/hugo/data/layouts/_partials/taxonomy_terms_article_wrapper.html
{{ if .Params.canonical_url }}
<link rel="canonical" href="{{ .Params.canonical_url }}">
{{ else }}
<link rel="canonical" href="{{ .Permalink }}">
{{ end }}
ERROR Failed to read Git log: fatal: not a git repository (or any parent up to mount point /)
git repo 수정 또는 enableGitInfo: false (hugo.yaml)
17:02:08 gvp6nx1a sshd[2432098]: Connection closed by 1... port 60416 [preauth]
ssh-keyscan 지문 추가 시 fail2ban에서 공격으로 집계. 지문을 secret으로 빼거나 fail2ban maxretry 9 구성 변경 필요
sudo fail2ban-client set sshd unbanip 1**.***.***.***
References
- https://github.com/google/docsy
- https://github.com/google/docsy-example
- https://github.com/gwatts/mostlydocs
- https://blog.concannon.tech/tech-talk/hugo-canonical-url/
- https://gohugo.io/templates/new-templatesystem-overview/#changes-to-the-layouts-folder
- https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls?hl=ko
- https://gohugo.io/host-and-deploy/host-on-github-pages/
- https://forums.opensuse.org/t/rsync-ed25519/175136
- https://www.reddit.com/r/rust/comments/wy3j50/psa_if_youre_using_ghpages_to_host_your/
- https://linux.die.net/man/1/rsync
- https://github.com/actions/checkout
- https://github.com/peaceiris/actions-hugo
- https://github.com/marketplace/actions/install-ssh-key
- https://blog.popey.com/2023/09/publishing-hugo-site-via-github-actions/
- https://belief-driven-design.com/deploying-hugo-with-github-actions-a78c2117aae/
- https://medium.com/@pooyan_razian/automatically-delete-old-github-actions-runs-5e1f7286fd81