Do you write blog posts in Obsidian and want a seamless way to publish them using Hugo? With this AutoHotkey script, you can automate the process in just a few clicks!

This guide will help you:

  • Sync Obsidian markdown files to Hugo.
  • Convert image links automatically.
  • Preview your site locally with Hugo before pushing changes to your live site
  • Commit and push changes via Git.

Let’s get started!

Quick Setup

  1. Save the following code as ObsidianHugoSync.ahk:
#Requires AutoHotkey v2.0

ObsidianToHugo() {
	; Update these paths to match your setup!
	paths := {
		obsidianBlog: A_MyDocuments "\Obsidian\\blog", ; Obsidian blog folder
		hugoContent: A_MyDocuments "\Hugo\content\posts", ; Hugo posts folder
		attachments: A_MyDocuments "\Obsidian\blog\attachments", ; Obsidian attachments folder
		hugoImages: A_MyDocuments "\Hugo\static\images", ; Hugo images folder
		hugoRoot: A_MyDocuments "\Hugo" ; Hugo project root folder
	}

	; 1. Sync markdown files (excluding the attachments folder)
	RunWait(Format('robocopy "{1}" "{2}" /MIR /XD "{3}" /Z /W:5 /R:3', paths.obsidianBlog, paths.hugoContent, paths.attachments), , "Hide")

	; 2. Process each markdown file in Hugo content folder
	loop files paths.hugoContent "\\*.md" {
		content := FileRead(A_LoopFileFullPath, "UTF-8")
		newContent := content

		; Find and process image links like [Image](/images/image.png)
		pos := 1
		while pos := RegExMatch(content, "\\[\\[([^\\]]*\\.(?:png|jpe?g|gif))\\]\\]", &match, pos) {
			imageName := match[1]
			baseName := StrSplit(imageName, "/")[-1]

			; Copy image to Hugo static/images folder
			sourceImage := paths.attachments "\\" baseName
			if FileExist(sourceImage)
				FileCopy(sourceImage, paths.hugoImages "\\" baseName, 1)

			; Update markdown link format
			newContent := StrReplace(newContent, "[[" imageName "]]", "[Image](/images/" StrReplace(baseName, " ", "%20") ")")

			pos += match.Len
		}

		; Save changes if markdown content was updated
		if (newContent != content) {
			FileDelete(A_LoopFileFullPath)
			FileAppend(newContent, A_LoopFileFullPath, "UTF-8")
		}
	}

	; 3. Preview the site with Hugo
	RunWait("hugo server -D", paths.hugoRoot)

	msg := MsgBox("Do you want to push changes to git?", "Push changes", "YesNo")
		if (msg = "No")
			return

	; 4. Commit and push changes with Git
	RunWait("git add .", paths.hugoRoot)
	RunWait('git commit -m "Update: Sync from Obsidian"', paths.hugoRoot)
	RunWait("git push", paths.hugoRoot)
	
}

F1:: ObsidianToHugo()

How to Use

  1. Update Paths: Edit the paths section in the script to match your local directories.
  2. Run the Script: Save the script as ObsidianHugoSync.ahk and execute it.
  3. Use the Shortcut: Press F1 to start “Sync to Hugo”

Example Directory Structure

Here’s what your file structure should look like for this script to work:

📁 Documents
├── 📁 Obsidian
│   └── 📁 blog
│       ├── 📄 post1.md
│       └── 📁 attachments
│           └── 🖼️ image1.png
└── 📁 Hugo
    ├── 📁 content
    │   └── 📁 posts
    │       └── 📄 post1.md
    └── 📁 static
        └── 📁 images
            └── 🖼️ image1.png

Automating Deployment with GitHub Actions

For automated deployment, add the following .yml file to .github/workflows/ in your Hugo repository:

name: github pages

on:
  push:
    branches:
      - main  # Set a branch to deploy
  pull_request:
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: true  # Fetch Hugo themes (true OR recursive)
          fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2
        with:
          hugo-version: 'latest'
          # extended: true

      - name: Build
        run: hugo --minify

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        if: github.ref == 'refs/heads/main'
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./public

Benefits of This Workflow

  • Streamlined Process: Write in Obsidian, sync effortlessly to Hugo, and deploy to your live site.
  • Automation: Reduce repetitive tasks like copying files or fixing image links.
  • Preview First: Test locally with Hugo’s server before committing changes.

Conclusion

This AutoHotkey script and GitHub Actions workflow simplify your blogging workflow by integrating Obsidian with Hugo. Whether you’re a casual writer or a tech-savvy blogger, this setup ensures a smooth and efficient publishing experience.

Happy blogging! 🚀