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:
 1#Requires AutoHotkey v2.0
 2
 3ObsidianToHugo() {
 4	; Update these paths to match your setup!
 5	paths := {
 6		obsidianBlog: A_MyDocuments "\Obsidian\\blog", ; Obsidian blog folder
 7		hugoContent: A_MyDocuments "\Hugo\content\posts", ; Hugo posts folder
 8		attachments: A_MyDocuments "\Obsidian\blog\attachments", ; Obsidian attachments folder
 9		hugoImages: A_MyDocuments "\Hugo\static\images", ; Hugo images folder
10		hugoRoot: A_MyDocuments "\Hugo" ; Hugo project root folder
11	}
12
13	; 1. Sync markdown files (excluding the attachments folder)
14	RunWait(Format('robocopy "{1}" "{2}" /MIR /XD "{3}" /Z /W:5 /R:3', paths.obsidianBlog, paths.hugoContent, paths.attachments), , "Hide")
15
16	; 2. Process each markdown file in Hugo content folder
17	loop files paths.hugoContent "\\*.md" {
18		content := FileRead(A_LoopFileFullPath, "UTF-8")
19		newContent := content
20
21		; Find and process image links like [Image](/images/image.png)
22		pos := 1
23		while pos := RegExMatch(content, "\\[\\[([^\\]]*\\.(?:png|jpe?g|gif))\\]\\]", &match, pos) {
24			imageName := match[1]
25			baseName := StrSplit(imageName, "/")[-1]
26
27			; Copy image to Hugo static/images folder
28			sourceImage := paths.attachments "\\" baseName
29			if FileExist(sourceImage)
30				FileCopy(sourceImage, paths.hugoImages "\\" baseName, 1)
31
32			; Update markdown link format
33			newContent := StrReplace(newContent, "[[" imageName "]]", "[Image](/images/" StrReplace(baseName, " ", "%20") ")")
34
35			pos += match.Len
36		}
37
38		; Save changes if markdown content was updated
39		if (newContent != content) {
40			FileDelete(A_LoopFileFullPath)
41			FileAppend(newContent, A_LoopFileFullPath, "UTF-8")
42		}
43	}
44
45	; 3. Preview the site with Hugo
46	RunWait("hugo server -D", paths.hugoRoot)
47
48	msg := MsgBox("Do you want to push changes to git?", "Push changes", "YesNo")
49		if (msg = "No")
50			return
51
52	; 4. Commit and push changes with Git
53	RunWait("git add .", paths.hugoRoot)
54	RunWait('git commit -m "Update: Sync from Obsidian"', paths.hugoRoot)
55	RunWait("git push", paths.hugoRoot)
56	
57}
58
59F1:: 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:

 1name: github pages
 2
 3on:
 4  push:
 5    branches:
 6      - main  # Set a branch to deploy
 7  pull_request:
 8  workflow_dispatch:
 9
10jobs:
11  deploy:
12    runs-on: ubuntu-20.04
13    steps:
14      - uses: actions/checkout@v2
15        with:
16          submodules: true  # Fetch Hugo themes (true OR recursive)
17          fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod
18
19      - name: Setup Hugo
20        uses: peaceiris/actions-hugo@v2
21        with:
22          hugo-version: 'latest'
23          # extended: true
24
25      - name: Build
26        run: hugo --minify
27
28      - name: Deploy
29        uses: peaceiris/actions-gh-pages@v3
30        if: github.ref == 'refs/heads/main'
31        with:
32          github_token: ${{ secrets.GITHUB_TOKEN }}
33          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! 🚀