Created
October 31, 2025 00:29
-
-
Save ancillarymagnet/d7739bf31a8dbdbd4fd32c76969b2cff to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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  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