Complete Guide to Migrating from Hexo to Hugo

Introduction Hexo and Hugo are both excellent static site generators, but due to performance considerations or personal preference, you might want to migrate from Hexo to Hugo. This article will document the complete migration process in detail, including configuration, article conversion, multilingual support, and more. Preparation Before starting the migration, you need to install Hugo and understand the basic site creation process. Here are the official documentation links, which are recommended to follow: 1. Install Hugo Hugo provides multiple installation methods, including pre-compiled binaries and package managers. Based on your operating system, please refer to the official installation documentation: Hugo Official Installation Guide 2. Create a Hugo Site After installing Hugo, you can create a new Hugo site using the following command: hugo new site your-site-name cd your-site-name For more detailed information about creating a site, please refer to: Hugo Quick Start Guide 3. Install a Theme Hugo has a rich selection of themes to choose from. You can browse and select a suitable theme on the Hugo Themes page. The basic steps to install a theme are as follows: git init git submodule add https://github.com/theme-author/theme-name.git themes/theme-name Then set the theme in your configuration file: echo "theme = 'theme-name'" >> hugo.toml Site Configuration Create a hugo.toml configuration file: baseURL = 'https://www.mfun.ink/' defaultContentLanguage = 'zh-cn' title = "Mengboy's Blog" theme = 'PaperMod' hasCJKLanguage = true # Google Analytics configuration GoogleAnalytics = "G-xxxxxxx" # Please replace with your Google Analytics ID # Add permalink configuration to make link format compatible with Hexo [permalinks] post = '/:year/:month/:day/:contentbasename/' # PaperMod theme parameters [params] # Set environment to production, which is important for Google Analytics and AdSense loading env = "production" # Author information author = "mengboy" # Main sections mainSections = ['post'] defaultTheme = "auto" ShowReadingTime = true ShowShareButtons = true ShowPostNavLinks = true ShowBreadCrumbs = true ShowCodeCopyButtons = true ShowRssButtonInSectionTermList = true disableSpecial1stPost = false disableScrollToTop = false hideMeta = false hideFooter = false # Website favicon settings [params.assets] favicon = "/images/site/favicon.png" favicon16x16 = "/images/site/favicon.png" favicon32x32 = "/images/site/favicon.png" apple_touch_icon = "/images/site/favicon.png" # Google AdSense configuration, requires custom extension or theme support googleAdsense = "ca-pub-xxxxxxxxxx" # Please replace with your Google AdSense publisher ID googleAdsenseSlot = "xxxxxxxx" # Please replace with your Google AdSense ad unit ID # Sidebar configuration [params.profileMode] enabled = false title = "mengboy" subtitle = "Recording Learning and Life" imageUrl = "images/site/favicon.png" imageWidth = 120 imageHeight = 120 # Social icons [[params.socialIcons]] name = "github" url = "https://github.com/mengboy" # Search settings [params.fuseOpts] isCaseSensitive = false shouldSort = true location = 0 distance = 1000 threshold = 0.4 minMatchCharLength = 0 keys = ["title", "permalink", "summary", "content"] # Allow HTML [markup.goldmark.renderer] unsafe = true # Ignore HTML warnings ignoreWarnings = ['raw-html'] ignoreLogs = ['warning-goldmark-raw-html'] [languages] [languages.zh-cn] contentDir = 'content' languageName = '简体中文' weight = 10 title = "Mengboy's Blog" # Chinese homepage information [languages.zh-cn.params] [languages.zh-cn.params.homeInfoParams] Title = "记录学习与生活点滴" # Chinese menu [languages.zh-cn.menu] [[languages.zh-cn.menu.main]] identifier = "home" name = "首页" url = "/" weight = 10 [[languages.zh-cn.menu.main]] identifier = "categories" name = "分类" url = "/categories/" weight = 20 [[languages.zh-cn.menu.main]] identifier = "tags" name = "标签" url = "/tags/" weight = 30 [[languages.zh-cn.menu.main]] identifier = "archives" name = "归档" url = "/archives/" weight = 40 [[languages.zh-cn.menu.main]] identifier = "about" name = "关于我" url = "/about/" weight = 50 [languages.en] contentDir = 'content/english' languageName = 'English' weight = 20 title = "Mengboy's Blog" # English homepage information [languages.en.params] [languages.en.params.homeInfoParams] Title = "Recording Learning and Life" # English menu [languages.en.menu] [[languages.en.menu.main]] identifier = "home" name = "Home" url = "/" weight = 10 [[languages.en.menu.main]] identifier = "categories" name = "Categories" url = "/en/categories/" weight = 20 [[languages.en.menu.main]] identifier = "tags" name = "Tags" url = "/en/tags/" weight = 30 [[languages.en.menu.main]] identifier = "archives" name = "Archives" url = "/en/archives/" weight = 40 [[languages.en.menu.main]] identifier = "about" name = "About Me" url = "/en/about/" weight = 50 Create Content Directories mkdir -p content/post mkdir -p content/english/post # Directory for English articles Create Basic Pages About Page mkdir -p content/about Create Chinese about page content/about/index.md: --- title: "关于我" date: 2023-06-01T12:00:00+08:00 --- 这是关于我的页面内容... Create English about page content/english/about/index.md: --- title: "About Me" date: 2023-06-01T12:00:00+08:00 --- This is about me page... You can create other basic pages using the about page as a reference. Write the Conversion Script Create a Python script to convert Hexo articles to Hugo format: #!/usr/bin/env python3 # Convert Hexo Markdown files to Hugo format import os import re import datetime import shutil from pathlib import Path # Setup paths hexo_posts_dir = "hexo/source/_posts" hugo_posts_dir = "hugo/content/post" hexo_img_dir = "hexo/source/images" hugo_static_dir = "hugo/static/images" # Ensure target directories exist os.makedirs(hugo_posts_dir, exist_ok=True) os.makedirs(hugo_static_dir, exist_ok=True) # Copy image folder def copy_images(): if os.path.exists(hexo_img_dir): print(f"Copying image files from {hexo_img_dir} to {hugo_static_dir}") # Copy folder contents for item in os.listdir(hexo_img_dir): s = os.path.join(hexo_img_dir, item) d = os.path.join(hugo_static_dir, item) if os.path.isdir(s): shutil.copytree(s, d, dirs_exist_ok=True) else: shutil.copy2(s, d) # Process YAML front-matter def convert_front_matter(content): # Extract front-matter match = re.match(r'^---\s+(.*?)\s+---\s*', content, re.DOTALL) if not match: return content front_matter = match.group(1) rest_content = content[match.end():] # Convert tags and categories new_front_matter = [] categories = [] tags = [] date = None for line in front_matter.strip().split('\n'): line = line.strip() # Process date if line.startswith('date:'): date_str = line[5:].strip() try: date = datetime.datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S') new_front_matter.append(f'date: {date.strftime("%Y-%m-%dT%H:%M:%S+08:00")}') # Add lastmod new_front_matter.append(f'lastmod: {date.strftime("%Y-%m-%dT%H:%M:%S+08:00")}') except ValueError: new_front_matter.append(line) # Process tags elif line.startswith('tags:'): tags_str = line[5:].strip() if tags_str.startswith('[') and tags_str.endswith(']'): # Format is tags: [tag1, tag2] tags = [tag.strip(' "\'') for tag in tags_str[1:-1].split(',')] else: # Might be multi-line format continue # Process categories elif line.startswith('categories:'): cats_str = line[11:].strip() if cats_str.startswith('[') and cats_str.endswith(']'): # Format is categories: [cat1, cat2] categories = [cat.strip(' "\'') for cat in cats_str[1:-1].split(',')] else: # Might be multi-line format continue # Copy other lines elif not (line.startswith('- ') and ('tags:' in front_matter or 'categories:' in front_matter)): new_front_matter.append(line) # Add categories and tags if categories: new_front_matter.append(f'categories: {categories}') if tags: new_front_matter.append(f'tags: {tags}') # Add slug if 'slug:' not in front_matter: filename = os.path.splitext(os.path.basename(content))[0] new_front_matter.append(f'slug: "{filename}"') # Combine new front-matter new_content = '---\n' + '\n'.join(new_front_matter) + '\n---\n\n' + rest_content return new_content # Convert Markdown content def convert_markdown_content(content): # Replace image links content = re.sub(r'!\[(.*?)\]\((.*?)/images/(.*?)\)', r'![\1](/images/\3)', content) # Handle the "more" marker content = re.sub(r'<!--\s*more\s*-->', ' ', content) return content # Main conversion function def convert_posts(): print(f"Starting to convert posts from {hexo_posts_dir} to {hugo_posts_dir}") files = os.listdir(hexo_posts_dir) for file in files: if not file.endswith('.md'): continue source_file = os.path.join(hexo_posts_dir, file) target_file = os.path.join(hugo_posts_dir, file) print(f"Converting: {source_file} -> {target_file}") # Read source file with open(source_file, 'r', encoding='utf-8') as f: content = f.read() # Convert content content = convert_front_matter(content) content = convert_markdown_content(content) # Write to target file with open(target_file, 'w', encoding='utf-8') as f: f.write(content) # Detect language and create multilingual files def handle_multilingual(): print("Processing multilingual articles...") for file in os.listdir(hugo_posts_dir): if not file.endswith('.md'): continue filepath = os.path.join(hugo_posts_dir, file) # Read file content with open(filepath, 'r', encoding='utf-8') as f: content = f.read() # Check if there's a language marker lang_match = re.search(r'language:\s*[\'"]?([a-z\-]+)[\'"]?', content) if lang_match: lang = lang_match.group(1) if lang.startswith('en'): # Create English version en_file = os.path.splitext(file)[0] + '.en.md' en_filepath = os.path.join(hugo_posts_dir, en_file) # Remove language marker and write to new file content = re.sub(r'language:\s*[\'"]?[a-z\-]+[\'"]?\n', '', content) with open(en_filepath, 'w', encoding='utf-8') as f: f.write(content) # Delete original file os.remove(filepath) print(f" Created English article: {en_filepath}") if __name__ == "__main__": copy_images() convert_posts() handle_multilingual() print("Conversion complete!") Run the Conversion Script cd ~/dev/book python3 convert_hexo_to_hugo.py Create a Start Script Create start.sh: ...

May 4, 2025 · 4 min · mengboy

从Hexo迁移到Hugo完全指南

前言 Hexo和Hugo都是优秀的静态网站生成器,但由于性能考虑或个人喜好,你可能想从Hexo迁移到Hugo。本文将详细记录完整迁移过程,包括配置、文章转换、多语言支持等方面。 准备工作 在开始迁移前,你需要先安装Hugo并了解基本的站点创建流程。以下是官方文档链接,建议按照官方文档进行操作: 1. 安装Hugo Hugo提供了多种安装方式,包括预编译二进制文件和包管理器。请根据你的操作系统,参考官方安装文档: Hugo官方安装指南 2. 创建Hugo站点 安装完Hugo后,你可以使用以下命令创建一个新的Hugo站点: hugo new site your-site-name cd your-site-name 更多关于创建站点的详细信息,请参考: Hugo快速入门指南 3. 安装主题 Hugo有丰富的主题可供选择,你可以在Hugo主题页面浏览并选择适合的主题。 安装主题的基本步骤如下: git init git submodule add https://github.com/theme-author/theme-name.git themes/theme-name 然后在配置文件中设置主题: echo "theme = 'theme-name'" >> hugo.toml 站点配置 创建hugo.toml配置文件: baseURL = 'https://www.mfun.ink/' defaultContentLanguage = 'zh-cn' title = "Mengboy's Blog" theme = 'PaperMod' hasCJKLanguage = true # Google Analytics配置 GoogleAnalytics = "G-xxxxxxx" # 请替换为您的Google Analytics ID # 添加permalink配置,使得链接格式与Hexo兼容 [permalinks] post = '/:year/:month/:day/:contentbasename/' # PaperMod主题参数 [params] # 设置环境为生产环境,这对于Google Analytics和AdSense加载非常重要 env = "production" # 作者信息 author = "mengboy" # 主要部分 mainSections = ['post'] defaultTheme = "auto" ShowReadingTime = true ShowShareButtons = true ShowPostNavLinks = true ShowBreadCrumbs = true ShowCodeCopyButtons = true ShowRssButtonInSectionTermList = true disableSpecial1stPost = false disableScrollToTop = false hideMeta = false hideFooter = false # 网站favicon设置 [params.assets] favicon = "/images/site/favicon.png" favicon16x16 = "/images/site/favicon.png" favicon32x32 = "/images/site/favicon.png" apple_touch_icon = "/images/site/favicon.png" # Google AdSense配置 需要自定义扩展,或者主题支持 googleAdsense = "ca-pub-xxxxxxxxxx" # 请替换为您的Google AdSense发布商ID googleAdsenseSlot = "xxxxxxxx" # 请替换为您的Google AdSense广告单元ID # 侧边栏配置 [params.profileMode] enabled = false title = "mengboy" subtitle = "记录学习与生活点滴" imageUrl = "images/site/favicon.png" imageWidth = 120 imageHeight = 120 # 社交图标 [[params.socialIcons]] name = "github" url = "https://github.com/mengboy" # 搜索设置 [params.fuseOpts] isCaseSensitive = false shouldSort = true location = 0 distance = 1000 threshold = 0.4 minMatchCharLength = 0 keys = ["title", "permalink", "summary", "content"] # 允许HTML [markup.goldmark.renderer] unsafe = true # 忽略HTML警告 ignoreWarnings = ['raw-html'] ignoreLogs = ['warning-goldmark-raw-html'] [languages] [languages.zh-cn] contentDir = 'content' languageName = '简体中文' weight = 10 title = "Mengboy's Blog" # 中文主页信息 [languages.zh-cn.params] [languages.zh-cn.params.homeInfoParams] Title = "记录学习与生活点滴" # 中文菜单 [languages.zh-cn.menu] [[languages.zh-cn.menu.main]] identifier = "home" name = "首页" url = "/" weight = 10 [[languages.zh-cn.menu.main]] identifier = "categories" name = "分类" url = "/categories/" weight = 20 [[languages.zh-cn.menu.main]] identifier = "tags" name = "标签" url = "/tags/" weight = 30 [[languages.zh-cn.menu.main]] identifier = "archives" name = "归档" url = "/archives/" weight = 40 [[languages.zh-cn.menu.main]] identifier = "about" name = "关于我" url = "/about/" weight = 50 [languages.en] contentDir = 'content/english' languageName = 'English' weight = 20 title = "Mengboy's Blog" # 英文主页信息 [languages.en.params] [languages.en.params.homeInfoParams] Title = "Recording Learning and Life" # 英文菜单 [languages.en.menu] [[languages.en.menu.main]] identifier = "home" name = "Home" url = "/" weight = 10 [[languages.en.menu.main]] identifier = "categories" name = "Categories" url = "/en/categories/" weight = 20 [[languages.en.menu.main]] identifier = "tags" name = "Tags" url = "/en/tags/" weight = 30 [[languages.en.menu.main]] identifier = "archives" name = "Archives" url = "/en/archives/" weight = 40 [[languages.en.menu.main]] identifier = "about" name = "About Me" url = "/en/about/" weight = 50 创建内容目录 mkdir -p content/post mkdir -p content/post/english # 英文文章目录 创建基础页面 关于页面 mkdir -p content/about 创建中文关于页面 content/about/index.md: --- title: "关于我" date: 2023-06-01T12:00:00+08:00 --- 这是关于我的页面内容... 创建英文关于页面 content/english/about/index.md: --- title: "About Me" date: 2023-06-01T12:00:00+08:00 --- This is about me page... 其他基础页面参考about页面。 编写转换脚本 创建一个Python脚本来转换Hexo文章到Hugo格式: #!/usr/bin/env python3 # 将Hexo的Markdown文件转换为Hugo格式 import os import re import datetime import shutil from pathlib import Path # 设置路径 hexo_posts_dir = "hexo/source/_posts" hugo_posts_dir = "hugo/content/post" hexo_img_dir = "hexo/source/images" hugo_static_dir = "hugo/static/images" # 确保目标目录存在 os.makedirs(hugo_posts_dir, exist_ok=True) os.makedirs(hugo_static_dir, exist_ok=True) # 复制图片文件夹 def copy_images(): if os.path.exists(hexo_img_dir): print(f"正在复制图片文件从 {hexo_img_dir} 到 {hugo_static_dir}") # 复制文件夹内容 for item in os.listdir(hexo_img_dir): s = os.path.join(hexo_img_dir, item) d = os.path.join(hugo_static_dir, item) if os.path.isdir(s): shutil.copytree(s, d, dirs_exist_ok=True) else: shutil.copy2(s, d) # 处理YAML front-matter def convert_front_matter(content): # 提取front-matter match = re.match(r'^---\s+(.*?)\s+---\s*', content, re.DOTALL) if not match: return content front_matter = match.group(1) rest_content = content[match.end():] # 转换标签和分类 new_front_matter = [] categories = [] tags = [] date = None for line in front_matter.strip().split('\n'): line = line.strip() # 处理日期 if line.startswith('date:'): date_str = line[5:].strip() try: date = datetime.datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S') new_front_matter.append(f'date: {date.strftime("%Y-%m-%dT%H:%M:%S+08:00")}') # 添加lastmod new_front_matter.append(f'lastmod: {date.strftime("%Y-%m-%dT%H:%M:%S+08:00")}') except ValueError: new_front_matter.append(line) # 处理标签 elif line.startswith('tags:'): tags_str = line[5:].strip() if tags_str.startswith('[') and tags_str.endswith(']'): # 格式为 tags: [tag1, tag2] tags = [tag.strip(' "\'') for tag in tags_str[1:-1].split(',')] else: # 可能是多行格式 continue # 处理分类 elif line.startswith('categories:'): cats_str = line[11:].strip() if cats_str.startswith('[') and cats_str.endswith(']'): # 格式为 categories: [cat1, cat2] categories = [cat.strip(' "\'') for cat in cats_str[1:-1].split(',')] else: # 可能是多行格式 continue # 复制其他行 elif not (line.startswith('- ') and ('tags:' in front_matter or 'categories:' in front_matter)): new_front_matter.append(line) # 添加分类和标签 if categories: new_front_matter.append(f'categories: {categories}') if tags: new_front_matter.append(f'tags: {tags}') # 添加slug if 'slug:' not in front_matter: filename = os.path.splitext(os.path.basename(content))[0] new_front_matter.append(f'slug: "{filename}"') # 组合新的front-matter new_content = '---\n' + '\n'.join(new_front_matter) + '\n---\n\n' + rest_content return new_content # 转换Markdown内容 def convert_markdown_content(content): # 替换图片链接 content = re.sub(r'!\[(.*?)\]\((.*?)/images/(.*?)\)', r'![\1](/images/\3)', content) # 处理更多标记 content = re.sub(r'<!--\s*more\s*-->', ' ', content) return content # 主转换函数 def convert_posts(): print(f"开始转换文章从 {hexo_posts_dir} 到 {hugo_posts_dir}") files = os.listdir(hexo_posts_dir) for file in files: if not file.endswith('.md'): continue source_file = os.path.join(hexo_posts_dir, file) target_file = os.path.join(hugo_posts_dir, file) print(f"转换: {source_file} -> {target_file}") # 读取源文件 with open(source_file, 'r', encoding='utf-8') as f: content = f.read() # 转换内容 content = convert_front_matter(content) content = convert_markdown_content(content) # 写入目标文件 with open(target_file, 'w', encoding='utf-8') as f: f.write(content) # 检测语言并创建多语言文件 def handle_multilingual(): print("处理多语言文章...") for file in os.listdir(hugo_posts_dir): if not file.endswith('.md'): continue filepath = os.path.join(hugo_posts_dir, file) # 读取文件内容 with open(filepath, 'r', encoding='utf-8') as f: content = f.read() # 检查是否有language标记 lang_match = re.search(r'language:\s*[\'"]?([a-z\-]+)[\'"]?', content) if lang_match: lang = lang_match.group(1) if lang.startswith('en'): # 创建英文版本 en_file = os.path.splitext(file)[0] + '.en.md' en_filepath = os.path.join(hugo_posts_dir, en_file) # 移除语言标记并写入新文件 content = re.sub(r'language:\s*[\'"]?[a-z\-]+[\'"]?\n', '', content) with open(en_filepath, 'w', encoding='utf-8') as f: f.write(content) # 删除原文件 os.remove(filepath) print(f" 创建英文文章: {en_filepath}") if __name__ == "__main__": copy_images() convert_posts() handle_multilingual() print("转换完成!") 运行转换脚本 cd ~/dev/book python3 convert_hexo_to_hugo.py 创建启动脚本 创建start.sh: ...

May 4, 2025 · 6 min · mengboy

hexo版本升级

hexo版本升级还是很简单的,先切到hexo目录,使用npm i hexo-cli -g升级全局hexo,接下来再使用npm-check、npm-upgrade,npm-update升级相关依赖。 ...

May 21, 2023 · 2 min · mengboy