hugo

hugo:exts + docsy + giscus 구성

image

  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

image

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 secretsRemarks
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

References