Skip to content

Instantly share code, notes, and snippets.

@ancillarymagnet
Created October 31, 2025 00:29
Show Gist options
  • Select an option

  • Save ancillarymagnet/d7739bf31a8dbdbd4fd32c76969b2cff to your computer and use it in GitHub Desktop.

Select an option

Save ancillarymagnet/d7739bf31a8dbdbd4fd32c76969b2cff to your computer and use it in GitHub Desktop.
name: Export vault to site
on:
push:
branches: [ main ]
paths:
- 'PROJECTS/PERSONAL_BLOG/Published/**'
- '.github/workflows/export-to-site.yml'
jobs:
export:
runs-on: ubuntu-latest
steps:
- name: Checkout vault
uses: actions/checkout@v4
- name: Checkout site repo
uses: actions/checkout@v4
with:
repository: yourusername/yourrepo
token: ${{ secrets.SITE_REPO_PAT }}
path: site
- name: Install obsidian-export
run: |
set -eux
curl --proto '=https' --tlsv1.2 -LsSf \
https://github.com/zoni/obsidian-export/releases/download/v25.3.0/obsidian-export-installer.sh \
| sh
source $HOME/.cargo/env
obsidian-export --version
- name: Export only the Published subtree
run: |
rm -rf export && mkdir -p export
obsidian-export "$GITHUB_WORKSPACE" \
--start-at "$GITHUB_WORKSPACE/PROJECTS/PERSONAL_BLOG/Published" \
export
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install filter deps
run: |
npm i gray-matter fast-glob fs-extra
- name: Filter by frontmatter and rewrite asset links
run: |
node - <<'NODE'
import fg from 'fast-glob';
import fs from 'fs/promises';
import fse from 'fs-extra';
import matter from 'gray-matter';
import path from 'path';
const exportDir = 'export';
const siteDir = 'site';
const notesOut = path.join(siteDir, 'src/content/notes');
const assetsOut = path.join(siteDir, 'public/attachments');
await fse.remove(notesOut);
await fse.remove(assetsOut);
await fse.ensureDir(notesOut);
await fse.ensureDir(assetsOut);
// Copy ALL non-MD assets to /public/attachments, preserving structure
const nonMd = await fg(['**/*', '!**/*.md'], { cwd: exportDir, onlyFiles: true });
for (const p of nonMd) {
await fse.ensureDir(path.join(assetsOut, path.dirname(p)));
await fse.copy(path.join(exportDir, p), path.join(assetsOut, p));
}
// Also copy files from the vault's ATTACHMENTS folder directly
const vaultAttachments = path.join(process.env.GITHUB_WORKSPACE, 'ATTACHMENTS');
try {
const attachmentFiles = await fg(['*'], { cwd: vaultAttachments, onlyFiles: true });
for (const file of attachmentFiles) {
await fse.copy(path.join(vaultAttachments, file), path.join(assetsOut, file));
}
} catch (e) {
console.log('No ATTACHMENTS folder found or error copying:', e.message);
}
// Copy only published:true Markdown and rewrite relative asset links to /attachments/...
const mds = await fg(['**/*.md'], { cwd: exportDir, onlyFiles: true });
for (const p of mds) {
const full = path.join(exportDir, p);
const raw = await fs.readFile(full, 'utf8');
const fm = matter(raw);
if (fm.data && fm.data.published === true) {
let body = fm.content;
// Rewrite ![...](relative) and [...](relative) → /attachments/<relative>
// Handle complex paths like /attachments/../../../ATTACHMENTS/filename.jpg
body = body.replace(/(\]\(|!\[[^\)]*\]\()(?!(?:https?:|\/\/|#))([^)\s]+)\)/g, (m, open, rel) => {
// If it's already a complex attachments path, extract just the filename
if (rel.includes('ATTACHMENTS/')) {
const filename = rel.split('ATTACHMENTS/').pop();
return `${open}/attachments/${filename})`;
}
// Handle normal relative paths
const newPath = '/attachments/' + rel.replace(/^\.\//, '');
return `${open}${newPath})`;
});
const outPath = path.join(notesOut, p);
await fse.ensureDir(path.dirname(outPath));
const out = matter.stringify(body, fm.data);
await fs.writeFile(outPath, out, 'utf8');
}
}
NODE
- name: Commit to site repo
run: |
cd site
# Remove the gitignore for notes so we can commit them
sed -i '/src\/content\/notes\/\*.md/d' .gitignore
git config user.name "My Fullname"
git config user.email "me@blog.biz"
git pull --no-rebase
git add -A
git commit -m "Export from vault: $GITHUB_SHA" || echo "No changes"
git push
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment