Publish to GitHub
MagicMD's GitHub workflow is not meant to make users type a long command every time. The natural path is to put the repository, target directory, branch, and commit message in .magicmd.toml, put your local token in .env, then publish daily with only the article URL.
configure the content repo once -> save the local token once -> preview with dry-run -> push a branch -> optionally open a Pull RequestRecommended path: configure first, publish later
If this is your first time publishing with GitHub, follow these four steps. The “content repository” is not the MagicMD project repository. It is the repository where your converted Markdown articles will live.
1. Create a content repository
Create a GitHub repository, for example:
didilili/blog-contentThis repository stores the files generated by MagicMD. It can be a new empty repository, or an existing Hugo, Docusaurus, blog, or knowledge-base repository.
Suggested choices:
- Use a clear name such as
blog-content,knowledge-base, orarticles. - Public and private repositories both work, as long as your token has access.
- Adding a README is fine. MagicMD publishes to a new branch, not directly to the default branch.
For Hugo blogs, the common article directory is content/posts. For Docusaurus, it is often docs. For a simple archive, articles is a good starting point.
2. Create a GitHub token
Real publishing needs GITHUB_TOKEN. Think of it as the key that lets MagicMD push a branch and optionally create a Pull Request in the selected repository. The recommended choice is a GitHub fine-grained personal access token:
GitHub profile menu -> Settings -> Developer settings -> Personal access tokens -> Fine-grained tokens -> Generate new tokenRecommended settings:
| Setting | Recommendation |
|---|---|
| Token name | Something clear, such as MagicMD publish. |
| Expiration | Choose a lifetime you are comfortable with, such as 30 or 90 days. |
| Repository access | Choose Only selected repositories, then select only your content repository. |
| Contents permission | Choose Read and write, so MagicMD can clone, commit, and push article files. |
| Pull requests | Choose Read and write if you want MagicMD to create PRs. You can skip this for branch-only publishing. |
GitHub shows the token only once. Copy it into .env at your project root:
GITHUB_TOKEN=github_pat_xxx.env is a private local file. Do not commit it to git. MagicMD reads it automatically for real publishing; dry-run does not need a token.
3. Write .magicmd.toml
Use the Config Builder, or put this block in .magicmd.toml at your project root:
[publish.github]
repo = "owner/content"
target_dir = "content/posts/{date}-{slug}"
branch = "magicmd/{slug}"
commit_message = "Add article: {title}"
create_pr = true
overwrite = falseReplace owner/content with your real content repository, such as didilili/blog-content.
What each field means:
| Field | Meaning | Beginner recommendation |
|---|---|---|
repo | Target GitHub repository in owner/name format. owner is your username or organization, and name is the repository name. | Example: didilili/blog-content. Do not use the MagicMD repository unless you really want articles there. |
target_dir | Directory inside the target repository. Supports {date}, {slug}, {platform}, and {short_hash}. | Use content/posts/{date}-{slug} so each article gets its own folder. |
branch | Temporary branch that MagicMD pushes to GitHub. | Use magicmd/{slug}. Avoid pushing directly to main; review through a PR first. |
commit_message | Commit message in the target repository. Supports variables such as {title}. | The default Add article: {title} is enough for most users. |
create_pr | Whether MagicMD creates a Pull Request after pushing. | Use true so you can inspect the generated article on GitHub before merging. |
overwrite | Whether MagicMD may overwrite existing target files. | Keep false. Use --overwrite only when intentionally republishing the same path. |
target_dir = "content/posts/{date}-{slug}" produces a folder like this:
content/posts/2026-06-19-my-article/
├── article.md
├── metadata.json
├── extraction-report.json
└── images/Common choices:
| Scenario | Suggested value |
|---|---|
| Hugo blog | content/posts/{date}-{slug} |
| Docusaurus docs | docs/{date}-{slug} |
| General knowledge base | articles/{date}-{slug} |
| Testing only | magicmd-test/{date}-{slug} |
4. Dry-run first, publish after checking
magicmd publish github "https://mp.weixin.qq.com/s/example" --dry-runAfter dry-run looks right, real publishing is short too:
magicmd publish github "https://mp.weixin.qq.com/s/example" --pr--pr creates a Pull Request after pushing the publish branch. If your config already has create_pr = true, you can omit --pr.
When to use it
If you only want a local archive, keep using the normal conversion command:
magicmd "https://mp.weixin.qq.com/s/example"If you want to publish the converted package to a Hugo, Docusaurus, blog, knowledge-base, or content archive repository, use:
magicmd publish github "https://mp.weixin.qq.com/s/example" --dry-runMagicMD first creates a local package, then plans article.md, metadata.json, extraction-report.json, and media files into your GitHub content repository.
Config Builder entry point
If you do not want to write .magicmd.toml by hand, start in the Config Builder:
Config Builder -> Show advanced settings -> Generate GitHub publishing configtarget_dir, branch, and commit_message support these common template variables:
| Variable | Meaning |
|---|---|
{title} | Article title |
{slug} | URL-friendly title slug |
{date} | Article publish date, or undated when missing |
{platform} | Platform name, such as wechat, juejin, csdn, or generic |
{short_hash} | First 6 characters of the content hash |
Step 2: Preview with dry-run
After the config file is ready, run dry-run:
magicmd publish github "https://mp.weixin.qq.com/s/example" --dry-runIf .magicmd.toml is not in the current directory, pass it explicitly:
magicmd publish github "https://mp.weixin.qq.com/s/example" \
--config path/to/.magicmd.toml \
--dry-runDry-run prints the plan only. It does not create branches, commits, pushes, or Pull Requests, and it does not need GITHUB_TOKEN.
Example output:
Publish plan
Repository: owner/content
Title: A Real Article Title
Platform: wechat
Source URL: https://mp.weixin.qq.com/s/example
Package directory: output/2026-06-19-article-title
Branch: magicmd/article-title
Target directory: content/posts/2026-06-19-article-title
Commit message: Add article: A Real Article Title
Create PR: True
Overwrite: False
Files:
- content/posts/2026-06-19-article-title/article.md (12000 bytes)
- content/posts/2026-06-19-article-title/extraction-report.json (320 bytes)
- content/posts/2026-06-19-article-title/metadata.json (820 bytes)
- content/posts/2026-06-19-article-title/images/img_001.png (43120 bytes)
Dry run only: no remote writes were performed.Read the dry-run output
| Field | Meaning | What to check |
|---|---|---|
Repository | Target GitHub repository | Use a real repository, such as didilili/blog-content, not the sample owner/content. |
Title | Article title extracted by MagicMD | If this is still the URL, the sample URL may be invalid, blocked, or not parsed correctly. |
Platform | Detected platform | WeChat articles usually show wechat; generic pages may show generic. |
Package directory | Local package directory | Open it and inspect article.md and extraction-report.json. |
Branch | Branch that real publishing will push | Confirm it will not conflict with a branch you already use. |
Target directory | Path inside the target repo | Prefer {date}-{slug} in the path so each article gets its own folder. |
Commit message | Commit message for real publishing | It should be readable and clearly match the article. |
Create PR | Whether MagicMD will create a Pull Request | This becomes True when you pass --pr or set create_pr = true. |
Overwrite | Whether planned target files may be overwritten | Defaults to False to avoid accidental overwrites. |
Files | Files that would be written to the target repo | A healthy package usually includes article.md, metadata.json, extraction-report.json, and media files. |
Spot a bad publish plan
The docs use https://mp.weixin.qq.com/s/example as a placeholder, not as a real article. If you run dry-run with that sample URL, you may see:
Title: https://mp.weixin.qq.com/s/example
Package directory: output/undated-...
Commit message: Add article: https://mp.weixin.qq.com/s/example
Files:
- content/posts/undated-example/debug.htmlThose are warning signs:
| Signal | Likely cause | What to do |
|---|---|---|
Title is the URL | MagicMD did not extract a real title | Use a real public article URL and run dry-run again. |
undated | MagicMD did not extract a publish date | This is not always fatal, but inspect metadata.json. |
debug.html appears in Files | MagicMD saved a debug page, often because extraction needs review | Open the local package and inspect article.md and extraction-report.json before publishing. |
article.md is very small | The article body may be missing | Do not publish yet. Try another URL or inspect the troubleshooting data. |
This is the main value of dry-run: you can catch a bad conversion before it reaches GitHub.
When publishing for real, MagicMD checks these risks again. By default, it stops before pushing if extraction failed, the title still looks like the URL, the article Markdown is missing, or the plan includes debug.html.
Step 3: Publish for real
After dry-run looks right, remove --dry-run to publish for real. The recommended path is to put the token in .env at the project root:
GITHUB_TOKEN=ghp_xxxThen run:
magicmd publish github "https://mp.weixin.qq.com/s/example"Real publishing does this:
1. Convert the article and create a local package
2. Temporarily clone the target GitHub repository
3. Create or check out the publish branch
4. Copy package files into target_dir
5. Create a git commit
6. Push the branch to GitHubGITHUB_TOKEN is not written to .magicmd.toml or stored in the git remote URL. MagicMD reads the current environment first; if it is missing, it reads .env. If you pass --config path/to/.magicmd.toml, MagicMD reads .env from that config file's directory.
If you understand the risk and still need to publish the package, pass --force. This is mainly for internal debugging or one-off migrations:
magicmd publish github "https://mp.weixin.qq.com/s/example" --forceAvoid --force for normal publishing.
Create a Pull Request
Pass --pr when you want MagicMD to create a Pull Request after pushing:
magicmd publish github "https://mp.weixin.qq.com/s/example" --prOn success, MagicMD prints the branch, commit, and Pull Request URL. The PR targets the repository default branch. If .magicmd.toml already has create_pr = true, you can omit --pr.
For one-off publishing, you can skip .env and set the token only for the current terminal session:
export GITHUB_TOKEN=ghp_xxx
magicmd publish github "https://mp.weixin.qq.com/s/example" --prTemporarily override config
Most users should rely on .magicmd.toml. Use CLI options only when you temporarily need a different repository, directory, or branch.
For example, publish to another repository for one run:
magicmd publish github "https://mp.weixin.qq.com/s/example" \
--repo owner/another-content \
--dry-runOr use a different branch template for one run:
magicmd publish github "https://mp.weixin.qq.com/s/example" \
--branch magicmd/{date}-{slug} \
--dry-runCLI options override .magicmd.toml.
Token permissions
Real publishing needs a token that can access and write to the target repository. Usually it must be able to:
- clone the target repository
- push to the publish branch
- create a Pull Request when
--pris enabled
If you use a fine-grained token, authorize only the target repository and grant the smallest permissions needed for content writes and Pull Request creation. Do not write the token into .magicmd.toml, README files, shell history, or the repository. Put it in local .env and make sure .env is not committed.
Common problems
Missing repo or target_dir
If [publish.github].repo is missing from .magicmd.toml and you do not pass --repo:
--repo is required unless [publish.github].repo is setIf [publish.github].target_dir is missing from .magicmd.toml and you do not pass --target-dir:
--target-dir is required unless [publish.github].target_dir is setUse the Config Builder to complete the config, or pass the CLI option temporarily.
Missing GITHUB_TOKEN
Dry-run does not need a token. Real publishing does. If the token is missing:
GITHUB_TOKEN is required for real GitHub publishing. Set it in the environment or project .env. Use --dry-run to preview only.Create .env at the project root and retry:
GITHUB_TOKEN=ghp_xxxIf you pass --config path/to/.magicmd.toml, put .env next to that config file.
Target directory already has files
By default, overwrite = false. Publishing stops when the target directory already has unplanned files or when a planned target file already exists. This avoids accidental overwrites in your content repository.
Only use overwrite when you are sure:
magicmd publish github "https://mp.weixin.qq.com/s/example" --overwriteWhat to check before publishing
Before real publishing, check:
Titleis a real article title, not the URL.Package directoryis notundated-..., or you are fine with a missing publish date.Filesdoes not include debug files you do not want to publish.- The local
article.mdcontains the expected body. extraction-report.jsonhas no severe warnings.Repository,Branch, andTarget directoryare exactly what you expect.
Recommended workflow
1. Generate .magicmd.toml with the Config Builder
2. Confirm [publish.github] points to the real content repository
3. Create .env at the project root and add GITHUB_TOKEN
4. Copy a real public article URL
5. Run magicmd publish github URL --dry-run
6. Check title, directory, branch, and file list
7. Open local article.md and review the body
8. Run magicmd publish github URL --pr
9. Review and merge the Pull Request on GitHub