Add gopher documentation to README and execution notes

- Update README with gopher/phlog section, publishing commands, URLs,
  and first-time SDF setup instructions
- Add gopher workflow documentation to execution-notes.txt
- Include planning docs in docs/ directory
This commit is contained in:
mnw
2026-01-14 16:24:42 -06:00
parent b7ac21093a
commit 69d6b370b2
4 changed files with 1019 additions and 4 deletions
+55 -4
View File
@@ -1,6 +1,6 @@
# marcus-web
Personal blog powered by Hugo. Many references to I Saw The TV Glow its a fantastic movie.
Personal blog powered by Hugo with gopher/phlog support. Many references to I Saw The TV Glow its a fantastic movie.
## Clone
@@ -9,7 +9,7 @@ git clone ssh://git@git.sdf.org/mnw/marcus-web.git
cd marcus-web
```
## Setup Scripts
## Setup
```bash
./scripts/setup.sh
@@ -34,6 +34,57 @@ python scripts/new_lunajuice.py
python scripts/new_techpost.py
```
## Build
## Publishing
Hugo builds happen on SDF, but using this repo and the scripts I can work on it locally where I am.
```bash
# Hugo site only
./scripts/remote_publish.sh
# Hugo + Gopher
./scripts/remote_publish.sh --gopher
# Gopher only
./scripts/remote_publish.sh --gopher-only
```
Hugo builds happen on SDF via git pull. Gopher content is built locally and rsynced to `~/gopher/blog/`.
## Gopher / Phlog
Posts can be published to SDF gopherspace by adding `phlog: true` to the frontmatter:
```yaml
---
title: 'My Post'
phlog: true
---
```
### First-Time Gopher Setup on SDF
Before the first gopher deploy, make sure the `blog/` directory exists on SDF:
```bash
ssh mnw@sdf.org "mkdir -p ~/gopher/blog"
```
Then run the initial deploy:
```bash
./scripts/remote_publish.sh --gopher-only
```
### Gopher URLs
| Location | URL |
|----------|-----|
| Gopher root | `gopher://sdf.org/1/users/mnw/` |
| Blog index | `gopher://sdf.org/1/users/mnw/blog/` |
| Frank's Couch | `gopher://sdf.org/1/users/mnw/blog/franks-couch/` |
| Fun Center | `gopher://sdf.org/1/users/mnw/blog/fun-center/` |
| Web proxy | [Floodgap Proxy](https://gopher.floodgap.com/gopher/gw?a=gopher://sdf.org/1/users/mnw/) |
### References
- [SDF Gopher Setup](https://wiki.sdf.org/doku.php?id=gopher_site_setup_and_hosting_features)
- [Tilde.town Gopher Guide](https://tilde.town/wiki/editing-your-homepage/gopher.html)
+224
View File
@@ -0,0 +1,224 @@
# Gopher Integration Code Review
> **Date:** 2026-01-13
> **Reviewer:** Claude (Opus 4.5)
> **Status:** Ready for deployment
---
## Summary
Implementation of gopher integration for marcus-web is complete. The system converts Hugo blog posts with `phlog: true` frontmatter to gopher-friendly plain text and deploys them to SDF gopherspace.
**7 commits total** | **All tests passing** | **1 critical bug found and fixed**
---
## Files Created
### scripts/gopher/ascii_art.py
ASCII art assets and helper functions for gopher content.
**Contents:**
- Post header (MNW owl logo)
- Category headers (Fun Center, Frank's Couch, Beercalls)
- Post footer (SDF box)
- Series-to-directory mapping
- Movie metadata ASCII table generator
- Text wrapping utilities
**Review:** Clean, well-organized. All art fits within 70-char line width constraint.
---
### scripts/gopher/convert_to_gopher.py
Main conversion script: markdown to gopher text.
**Key functions:**
- `parse_frontmatter()` - YAML extraction
- `extract_links()` - Converts `[text](url)` to `text [1]` with footer references
- `convert_headings()` - `#` → section headers, `##` → subheadings
- `convert_formatting()` - `**bold**``*bold*`, `*italic*``_italic_`
- `convert_code_blocks()` - Fenced blocks → 4-space indented
- `handle_imdbposter()` - Hugo shortcode → ASCII movie table
- `wrap_paragraphs()` - 70-char line wrapping
**Review:** Solid implementation. Handles edge cases well. The imdbposter conversion extracts viewing info tables and preserves them as plain text.
**Fixed bug:** Added `sys.path.insert()` for imports to work when called from shell script.
---
### scripts/gopher/generate_gophermaps.py
Generates gophermap index files for categories.
**Outputs:**
- `blog/gophermap` - Main index with category links and post counts
- `blog/fun-center/gophermap` - Tech posts listing
- `blog/franks-couch/gophermap` - Movie reviews listing
- `blog/beercalls/gophermap` - Beer logs listing (when posts exist)
**Review:** Properly formats gophermap syntax with tab-separated fields. Sorts posts by date (newest first). Includes ASCII art headers per category.
**Fixed bug:** Same `sys.path` fix as convert_to_gopher.py.
---
### scripts/gopher/update_root_gophermap.py
Generates updated root gophermap with "FROM THE WEB" section.
**Review:** Contains full gophermap template. One-time use script for initial setup. Includes dynamic `=echo` directives for latest post detection.
**Note:** This needs to be run manually after first deployment:
```bash
python3 scripts/gopher/update_root_gophermap.py
scp gopher_build/gophermap mnw@sdf.org:~/gopher/gophermap
```
---
## Files Modified
### scripts/remote_publish.sh
Added `--gopher` and `--gopher-only` flags.
**Changes:**
- Argument parsing with help text
- Conditional Hugo deployment (skipped with `--gopher-only`)
- Gopher build: runs convert + generate scripts locally
- rsync with `--delete` to sync to SDF
- Permission fixing on remote (644 files, 755 dirs)
**Review:** Good error handling via `set -e`. Clear output messages. The `--delete` flag means removing `phlog: true` from a post will remove it from gopher on next deploy.
---
### scripts/new_techpost.py
Added phlog prompt after summary input.
**Changes:**
- New prompt: "Publish to gopher phlog? (y/N)"
- Added `phlog` parameter to `create_post()`
- Frontmatter now includes `phlog: true/false`
**Review:** Clean integration. Defaults to "no" which is appropriate.
---
### scripts/import_letterboxd.py
Added phlog prompt after viewing details.
**Changes:**
- Prompt added in `import_movie()` function
- `phlog` parameter passed to `create_draft_post()`
- Frontmatter updated
**Review:** Good placement - asks after all other inputs are collected.
---
### scripts/new_movie.py
Added phlog prompt after TMDB fetch.
**Changes:**
- Same pattern as other scripts
- Frontmatter includes phlog field
**Review:** Consistent with other scripts.
---
### .gitignore
Added `gopher_build/` to ignore list.
---
### requirements.txt
Added `pyyaml` dependency.
---
## Content Changes
Three posts enabled for gopher with `phlog: true`:
- `content/posts/blog-posting.md` (Fun Center)
- `content/posts/recovering-failed-ubuntu-upgrade.md` (Fun Center)
- `content/posts/megalopolis.md` (Frank's Couch)
---
## Bug Found and Fixed
### Critical: Import Path Issue
**Problem:** `from ascii_art import ...` failed when scripts were invoked from `remote_publish.sh` because Python couldn't find the module.
**Root cause:** When running `python3 scripts/gopher/convert_to_gopher.py` from the project root, Python's working directory doesn't include `scripts/gopher/`.
**Fix:** Added to both convert and generate scripts:
```python
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
```
**Commit:** `b5bb06e`
---
## Minor Issues Fixed
1. **No-op regex removed** - `re.sub(r"_([^_]+)_", r"_\1_", text)` did nothing
2. **Unused variable removed** - `in_code_block = False` was never used
---
## Testing Performed
1. **Conversion test:** `python3 scripts/gopher/convert_to_gopher.py --all` - 3 posts converted
2. **Gophermap test:** `python3 scripts/gopher/generate_gophermaps.py` - All maps generated
3. **Output verification:** Manually inspected generated .txt files and gophermaps
4. **Import fix verification:** Re-ran after fix, all working
---
## Deployment Checklist
- [ ] `git push` to sync with origin
- [ ] Run `./scripts/remote_publish.sh --gopher` for first deployment
- [ ] Run `python3 scripts/gopher/update_root_gophermap.py` to generate root map
- [ ] `scp gopher_build/gophermap mnw@sdf.org:~/gopher/gophermap` to update root
- [ ] Verify at `gopher://sdf.org/1/users/mnw/blog/`
- [ ] Test via web proxy: https://gopher.floodgap.com/gopher/gw?a=gopher://sdf.org/1/users/mnw/blog/
---
## Future Considerations
1. **Beercalls:** No beercall posts have `phlog: true` yet. The category exists but is empty.
2. **Movie metadata:** The `megalopolis.md` post doesn't have `year`, `director`, `runtime`, or `genres` in frontmatter, so the ASCII movie table only shows title and web link. Consider backfilling metadata for better gopher output.
3. **Link handling:** External links become numbered references. Very long URLs may look awkward in the links section but are functional.
4. **Line wrapping:** Code blocks preserve formatting. Very long single words won't wrap (expected behavior).
---
## Commit History
```
b5bb06e Fix import path issue in gopher scripts
2f20e02 Add pyyaml to requirements.txt (Phase 6)
96e350c Add phlog: true to existing posts for gopher (Phase 5)
2941c7b Add phlog prompt to post creation scripts (Phase 4)
99495eb Add gopher deployment to remote_publish.sh (Phase 3)
468d238 Add gophermap generation scripts (Phase 2)
c4c18ed Add gopher integration core infrastructure (Phase 1)
```
---
## Verdict
**Ready for deployment.** All functionality implemented per the integration plan. One critical bug was found during review and fixed. Code is clean, well-structured, and tested.
+697
View File
@@ -0,0 +1,697 @@
# Gopher Integration Plan
> **Status:** Planning
> **Created:** 2025-01-13
> **Goal:** Publish Hugo blog posts to SDF gopherspace with `phlog: true` frontmatter flag
---
## Overview
Enhance the marcus-web project to deploy selected blog posts to both the Hugo site (mnw.sdf.org) and the SDF gopherspace (gopher://sdf.org/1/users/mnw/). Posts are opted-in via frontmatter, converted from markdown to gopher-friendly plain text, and organized by series/category.
---
## Current State
### Hugo Site (mnw.sdf.org)
- **Series:** "Frank's Couch" (movies), "Fun Center" (tech), beercall content
- **Frontmatter:** title, date, series, summary, tags, draft
- **Deploy:** `remote_publish.sh` → SSH to SDF → git pull && hugo && mkhomepg -p
### Existing Gopherspace (/sdf/arpa/gm/m/mnw/gopher)
- Rich structure with ASCII art headers and footers
- Categories as directories: `journal/`, `older/`, `LISA19/`, `hope_2024/`, etc.
- Dynamic CGI: latest phlog, user info via `=echo` directives
- Template style established in `templates/phlogpost.txt`
---
## Design Decisions
| Decision | Choice | Rationale |
|----------|--------|-----------|
| Opt-in mechanism | `phlog: true` frontmatter | Explicit control per post |
| Category mapping | Hugo series → gopher directories | Natural organization |
| Beercall handling | One gopher file per year | Matches existing yearly logs |
| Movie poster | Link to web blog post | Gopher can't display images |
| Movie metadata | ASCII table | Old-school aesthetic |
| ASCII art headers | Category-specific art | Visual distinction, same size as current |
| Root gophermap | Script-managed with makeover | Cohesive, automated experience |
| Sync strategy | rsync with --delete | Git-tracked, reversible |
---
## Directory Structure
### On SDF (~/gopher aka /sdf/arpa/gm/m/mnw/gopher)
```
~/gopher/
├── gophermap # Root menu (UPDATED by script)
├── journal/ # Existing daily phlogs
├── older/ # Existing historical
├── LISA19/ # Existing conference notes
├── hope_2024/ # Existing conference notes
├── blog/ # NEW: Hugo-sourced content
│ ├── gophermap # Blog index
│ ├── franks-couch/ # Movie reviews
│ │ ├── gophermap # Category index with summaries
│ │ ├── megalopolis.txt
│ │ ├── joker-folie-a-deux.txt
│ │ └── ...
│ ├── fun-center/ # Tech posts
│ │ ├── gophermap
│ │ ├── is-it-me.txt
│ │ └── ...
│ └── beercalls/ # Yearly beer logs
│ ├── gophermap
│ ├── 2023.txt
│ └── 2024.txt
└── templates/ # Existing templates
```
### Local Build Directory
```
marcus-web/
├── gopher_build/ # Staging area (gitignored)
│ └── blog/
│ ├── gophermap
│ ├── franks-couch/
│ ├── fun-center/
│ └── beercalls/
```
---
## Frontmatter Extension
Add `phlog` field to any post:
```yaml
---
title: 'Megalopolis'
date: 2024-10-05T00:03:15Z
draft: false
series: "Frank's Couch"
summary: "A sprawling mess of ambition"
phlog: true # <-- NEW: publish to gopher
imdb: "tt10128846"
poster: "/images/posters/megalopolis.jpg"
year: 2024
runtime: 138
director: "Francis Ford Coppola"
genres:
- Drama
- Sci-Fi
tags:
- alamo-drafthouse
---
```
**Behavior:**
- `phlog: true` → Convert and deploy to gopher
- `phlog: false` or absent → Hugo only (web exclusive)
---
## Content Conversion
### Markdown → Gopher Text
| Markdown | Gopher |
|----------|--------|
| `# Heading` | `═══════════════════════════════════════`<br>` HEADING`<br>`═══════════════════════════════════════` |
| `## Subheading` | `--- Subheading ---` |
| `**bold**` | `*bold*` |
| `*italic*` | `_italic_` |
| `[text](url)` | `text [1]` + links section at bottom |
| `{{< imdbposter >}}` | ASCII metadata table (see below) |
| Code blocks | Preserve, indent 4 spaces |
| `---` (hr) | `────────────────────────────────────────` |
### Line Wrapping
- Wrap at 70 characters
- Preserve code block formatting
- Handle URLs gracefully (collect for footer)
### Movie Metadata Table
Convert the `{{< imdbposter >}}` shortcode to ASCII table:
```
┌─────────────────────────────────────────────────────────────────┐
│ MEGALOPOLIS (2024) │
├─────────────────────────────────────────────────────────────────┤
│ Director: Francis Ford Coppola │
│ Runtime: 138 minutes │
│ Genres: Drama, Sci-Fi │
│ │
│ View on web: https://mnw.sdf.org/posts/megalopolis/ │
└─────────────────────────────────────────────────────────────────┘
```
---
## ASCII Art Headers
Each category gets unique ASCII art. Size constraint: **No larger than current template (~12 lines max)**.
### Fun Center (Tech)
```
___ ___ _
| __| _ _ _ / __|___ _ _| |_ ___ _ _
| _| || | ' \ | (__/ -_) ' \ _/ -_) '_|
|_| \_,_|_||_| \___\___|_||_\__\___|_|
Tech posts from the Double Lunch Dispatch
```
### Frank's Couch (Movies)
```
___ _ _ ___ _
| __| _ __ _ _ _| |_( )___ / __|___ _ _ __| |_
| _| '_/ _` | ' \ / /|_-< | (__/ _ \ || (_-< ' \
|_||_| \__,_|_||_\_\ /__/ \___\___/\_,_/__/_||_|
Movie reviews from the couch
```
### Beercalls (Beer)
```
___ _ _
| _ ) ___ ___ _ _ __ __ _| | |___
| _ \/ -_) -_) '_/ _/ _` | | (_-<
|___/\___\___|_| \__\__,_|_|_/__/
Thursday night adventures in Austin
```
### Blog Index
```
___ _ ___ _ _
| _ )| |___ __ | __|_ _ | |_ _ _ (_)___ ___
| _ \| / _ \/ _|| _|| ' \| _| '_|| / -_|_-<
|___/|_\___/\__||___|_||_|\__|_| |_\___/__/
Posts from mnw.sdf.org
```
---
## Gophermap Formats
### Category Gophermap (e.g., `blog/franks-couch/gophermap`)
```
i ___ _ _ ___ _ fake (NULL) 0
i | __| _ __ _ _ _| |_( )___ / __|___ _ _ __| |_ fake (NULL) 0
i | _| '_/ _` | ' \ / /|_-< | (__/ _ \ || (_-< ' \ fake (NULL) 0
i |_||_| \__,_|_||_\_\ /__/ \___\___/\_,_/__/_||_| fake (NULL) 0
i fake (NULL) 0
i Movie reviews from the couch fake (NULL) 0
i fake (NULL) 0
i──────────────────────────────────────────────────── fake (NULL) 0
i fake (NULL) 0
0[2024-10-05] Megalopolis megalopolis.txt
i A sprawling mess of ambition fake (NULL) 0
i fake (NULL) 0
0[2024-10-07] Joker: Folie a Deux joker-folie-a-deux.txt
i Not what you'd expect fake (NULL) 0
i fake (NULL) 0
0[2024-10-12] Terrifier 3 terrifier-3.txt
i Art the Clown returns fake (NULL) 0
i fake (NULL) 0
i──────────────────────────────────────────────────── fake (NULL) 0
1Back to Blog Index ../
1Back to Main Menu /users/mnw/
```
### Blog Index Gophermap (`blog/gophermap`)
```
i ___ _ ___ _ _ fake (NULL) 0
i | _ )| |___ __ | __|_ _ | |_ _ _ (_)___ ___ fake (NULL) 0
i | _ \| / _ \/ _|| _|| ' \| _| '_|| / -_|_-< fake (NULL) 0
i |___/|_\___/\__||___|_||_|\__|_| |_\___/__/ fake (NULL) 0
i fake (NULL) 0
i Posts from mnw.sdf.org fake (NULL) 0
i The Double Lunch Dispatch fake (NULL) 0
i fake (NULL) 0
i──────────────────────────────────────────────── fake (NULL) 0
i fake (NULL) 0
1Frank's Couch - Movie Reviews franks-couch/
i X posts about films watched from the couch fake (NULL) 0
i fake (NULL) 0
1Fun Center - Tech Posts fun-center/
i X posts about technology and open source fake (NULL) 0
i fake (NULL) 0
1Beercalls - Thursday Night Adventures beercalls/
i Yearly logs of Austin beer adventures fake (NULL) 0
i fake (NULL) 0
i──────────────────────────────────────────────── fake (NULL) 0
hView on the web URL:https://mnw.sdf.org/posts/
1Back to Main Menu /users/mnw/
```
---
## Root Gophermap Makeover
Update the main gophermap to incorporate blog content while preserving existing sections.
### Proposed Structure
```
i fake (NULL) 0
i ''~`` fake (NULL) 0
i ( o o ) fake (NULL) 0
i+-------------------.oooO--(_)--Oooo.---------------+ fake (NULL) 0
i| | fake (NULL) 0
i| THE DOUBLE LUNCH DISPATCH | fake (NULL) 0
i| mnw @ sdf | fake (NULL) 0
i| | fake (NULL) 0
i+---------------------------------------------------+ fake (NULL) 0
i fake (NULL) 0
imnw(at)sdf.org | SDF VOIP Ext. 1908 fake (NULL) 0
i fake (NULL) 0
i═══════════════════════════════════════════════════ fake (NULL) 0
i DAILY PHLOGS fake (NULL) 0
i═══════════════════════════════════════════════════ fake (NULL) 0
i fake (NULL) 0
1Daily Journal journal/
=echo "0Latest Entry journal/`ls -t journal/ | grep -v gophermap | head -1`"
1Historical Phlogs (2018-2019) older/
i fake (NULL) 0
i═══════════════════════════════════════════════════ fake (NULL) 0
i FROM THE WEB fake (NULL) 0
i═══════════════════════════════════════════════════ fake (NULL) 0
i fake (NULL) 0
iPosts from mnw.sdf.org fake (NULL) 0
i fake (NULL) 0
1Blog Categories blog/
=echo "0Latest Blog Post `find ~/gopher/blog -name '*.txt' -type f -printf '%T@ %p\n' 2>/dev/null | sort -rn | head -1 | awk '{print $2}' | sed 's|.*/gopher/||'`"
i fake (NULL) 0
i═══════════════════════════════════════════════════ fake (NULL) 0
i CONFERENCE NOTES fake (NULL) 0
i═══════════════════════════════════════════════════ fake (NULL) 0
i fake (NULL) 0
iPhlogs from conferences (rough notes) fake (NULL) 0
i fake (NULL) 0
1HOPE 2024 hope_2024/
1FOSSY 2024 fossy_2024/
1LISA 2019 LISA19/
1SeaGL 2019 seagl_2019/
1Texas Linux Fest 2019 txlf_2019/
1HOPE 2018 hope_2018/
1SeaGL 2018 seagl_2018/
i fake (NULL) 0
i═══════════════════════════════════════════════════ fake (NULL) 0
i OTHER THINGS fake (NULL) 0
i═══════════════════════════════════════════════════ fake (NULL) 0
i fake (NULL) 0
1Comedy comedy/
1Short Stories stories/
i fake (NULL) 0
i .oooO fake (NULL) 0
i ( ) Oooo. fake (NULL) 0
i+-------------------\ (----( )-----------------+ fake (NULL) 0
i \_) ) / fake (NULL) 0
i (_/ fake (NULL) 0
i fake (NULL) 0
iMy SDF User Info fake (NULL) 0
=echo "`/usr/pkg/bin/uinfo mnw | tr -d '\-=><'`"
i fake (NULL) 0
i __^__ __^__ fake (NULL) 0
i( ___ )--------------------------------------------( ___ ) fake (NULL) 0
i | / | This gopher space proudly hosted by SDF | \ | fake (NULL) 0
i | / | mnw on pixelfed.social and tilde.zone | \ | fake (NULL) 0
i |___| Web: mnw.sdf.org |___| fake (NULL) 0
i(_____)--------------------------------------------(_____) fake (NULL) 0
i fake (NULL) 0
iThanks for reading! Enjoy an SDF oneliner: fake (NULL) 0
=echo "`/usr/pkg/bin/oneliner | tr -d '\-=><'`"
```
---
## Post Template
Each converted post follows this structure:
```
___ ___ ___
/\ \ /\ \ /\ \
|::\ \ \:\ \ _\:\ \
|:|:\ \ \:\ \ /\ \:\ \
__|:|\:\ \ _____\:\ \ _\:\ \:\ \
/::::|_\:\__\ /::::::::\__\ /\ \:\ \:\__\
\:\~~\ \/__/ \:\~~\~~\/__/ \:\ \:\/:/ /
\:\ \ \:\ \ \:\ \::/ /
\:\ \ \:\ \ \:\/:/ /
\:\__\ \:\__\ \::/ /
\/__/ \/__/ \/__/
mnw(at)sdf.org | SDF VOIP Ext. 1908
────────────────────────────────────────────────────────────────────
2024-11-19 | Fun Center
────────────────────────────────────────────────────────────────────
IS IT ME?
Marcus shares his blog writing process.
────────────────────────────────────────────────────────────────────
I've always loved talking to people. Whether it's at work, at
home, or out with friends, connecting with others and hearing
about their passions brings me so much joy. There's nothing
better than a conversation with someone excited to share what
they care about.
[... content continues, wrapped at 70 characters ...]
═══════════════════════════════════════════════════════════════════
LINKS
═══════════════════════════════════════════════════════════════════
[1] https://example.com/macwhisper
[2] https://openai.com/chatgpt
__^__ __^__
( ___ )------------------------------------------------------( ___ )
| / | | \ |
| / | This gopher space proudly hosted by SDF | \ |
| \ | mnw on pixelfed.social and tilde.zone | / |
| \ | | / |
(_____)------------------------------------------------------(_____)
Web version: https://mnw.sdf.org/posts/is-it-me/
```
---
## New Scripts
### 1. `scripts/gopher/convert_to_gopher.py`
**Purpose:** Convert Hugo markdown to gopher-formatted text
**Usage:**
```bash
python scripts/gopher/convert_to_gopher.py content/posts/blog-posting.md
python scripts/gopher/convert_to_gopher.py --all
python scripts/gopher/convert_to_gopher.py --all --output gopher_build/
```
**Responsibilities:**
- Parse YAML frontmatter
- Check `phlog: true` flag
- Determine output directory from series
- Convert markdown syntax
- Handle `{{< imdbposter >}}` → ASCII table
- Word wrap to 70 chars
- Apply header/footer templates
- Collect and format links section
### 2. `scripts/gopher/generate_gophermaps.py`
**Purpose:** Generate all gophermap files
**Usage:**
```bash
python scripts/gopher/generate_gophermaps.py
python scripts/gopher/generate_gophermaps.py --output gopher_build/
```
**Responsibilities:**
- Scan converted posts by category
- Generate category gophermaps with titles + summaries
- Generate blog index gophermap
- Count posts per category
- Sort by date (newest first)
### 3. `scripts/gopher/ascii_art.py`
**Purpose:** ASCII art assets and helper functions
**Contents:**
- Category header art (Fun Center, Frank's Couch, Beercalls)
- Post header template (MNW owl)
- Footer template (SDF box)
- Section dividers
- ASCII table generator for movie metadata
### 4. `scripts/gopher/update_root_gophermap.py`
**Purpose:** Update the main gophermap with blog section
**Usage:**
```bash
python scripts/gopher/update_root_gophermap.py
```
**Responsibilities:**
- Read existing root gophermap (preserve conference notes, etc.)
- Insert/update "FROM THE WEB" section
- Update dynamic CGI for latest blog post
- Optionally apply full makeover template
---
## Modified Scripts
### `scripts/remote_publish.sh`
Add gopher deployment support:
```bash
#!/bin/bash
# Remote publish script for marcus-web
GOPHER=false
GOPHER_ONLY=false
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--gopher)
GOPHER=true
shift
;;
--gopher-only)
GOPHER_ONLY=true
GOPHER=true
shift
;;
*)
shift
;;
esac
done
# ... existing git/commit logic ...
if [[ "$GOPHER_ONLY" != "true" ]]; then
# Existing Hugo deployment
ssh "$SDF_HOST" "cd $REMOTE_DIR && git pull && hugo && mkhomepg -p"
echo "Site is live at: https://mnw.sdf.org/"
fi
if [[ "$GOPHER" == "true" ]]; then
echo -e "${GREEN}=== Deploying to Gopher ===${NC}"
# Build gopher content locally
python scripts/gopher/convert_to_gopher.py --all --output gopher_build/blog/
python scripts/gopher/generate_gophermaps.py --output gopher_build/blog/
# Sync to SDF (--delete removes posts where phlog was set to false)
rsync -avz --delete gopher_build/blog/ "$SDF_HOST:~/gopher/blog/"
# Fix permissions on SDF
ssh "$SDF_HOST" "find ~/gopher/blog -type f -exec chmod 644 {} \; && find ~/gopher/blog -type d -exec chmod 755 {} \;"
echo "Gopher updated at: gopher://sdf.org/1/users/mnw/blog/"
fi
```
### `scripts/new_techpost.py`
Add phlog prompt:
```python
# After getting summary...
phlog_input = input("\nPublish to gopher phlog? (y/N): ").strip().lower()
phlog = phlog_input == 'y'
# In frontmatter:
content = f'''---
title: '{title}'
date: {now}
draft: true
series: "Fun Center"
summary: "{summary}"
phlog: {str(phlog).lower()}
tags:
{tags_yaml}
---
'''
```
### `scripts/new_movie.py` / `scripts/import_letterboxd.py`
Same pattern - add phlog prompt after other inputs.
---
## Deployment Workflow
### Standard Deploy (Hugo only)
```bash
./scripts/remote_publish.sh
```
### Deploy Hugo + Gopher
```bash
./scripts/remote_publish.sh --gopher
```
### Deploy Gopher Only
```bash
./scripts/remote_publish.sh --gopher-only
```
### One-time Root Gophermap Update
```bash
# After first blog/ deployment
python scripts/gopher/update_root_gophermap.py
scp gopher_build/gophermap mnw@sdf.org:~/gopher/gophermap
```
---
## Implementation Order
### Phase 1: Core Infrastructure
1. [ ] Create `scripts/gopher/` directory
2. [ ] Create `scripts/gopher/ascii_art.py` - All ASCII art assets
3. [ ] Create `scripts/gopher/convert_to_gopher.py` - Markdown conversion
4. [ ] Add `gopher_build/` to `.gitignore`
### Phase 2: Gophermap Generation
5. [ ] Create `scripts/gopher/generate_gophermaps.py` - Category + index maps
6. [ ] Create `scripts/gopher/update_root_gophermap.py` - Root gophermap
### Phase 3: Deployment Integration
7. [ ] Modify `scripts/remote_publish.sh` - Add --gopher flags
8. [ ] Test with single post locally
### Phase 4: Post Creation Updates
9. [ ] Modify `scripts/new_techpost.py` - Add phlog prompt
10. [ ] Modify `scripts/import_letterboxd.py` - Add phlog prompt
11. [ ] Modify `scripts/new_movie.py` - Add phlog prompt (if separate)
### Phase 5: Content & Testing
12. [ ] Add `phlog: true` to existing posts to backfill
13. [ ] Run full deployment to SDF
14. [ ] Verify gopher content via `gopher gopher://sdf.org/1/users/mnw/blog/`
15. [ ] Update root gophermap on SDF
### Phase 6: Polish
16. [ ] Fine-tune ASCII art sizing
17. [ ] Adjust line wrapping edge cases
18. [ ] Document in README or execution-notes.txt
---
## File Checklist
### New Files
- [ ] `scripts/gopher/ascii_art.py`
- [ ] `scripts/gopher/convert_to_gopher.py`
- [ ] `scripts/gopher/generate_gophermaps.py`
- [ ] `scripts/gopher/update_root_gophermap.py`
- [ ] `gopher_build/` directory (gitignored)
### Modified Files
- [ ] `scripts/remote_publish.sh`
- [ ] `scripts/new_techpost.py`
- [ ] `scripts/import_letterboxd.py`
- [ ] `.gitignore` (add gopher_build/)
### Content Files (add phlog: true)
- [ ] `content/posts/blog-posting.md`
- [ ] `content/posts/recovering-failed-ubuntu-upgrade.md`
- [ ] Movie posts as desired
- [ ] Beercall yearly files as desired
---
## URLs
After deployment:
| Type | URL |
|------|-----|
| Gopher root | `gopher://sdf.org/1/users/mnw/` |
| Blog index | `gopher://sdf.org/1/users/mnw/blog/` |
| Fun Center | `gopher://sdf.org/1/users/mnw/blog/fun-center/` |
| Frank's Couch | `gopher://sdf.org/1/users/mnw/blog/franks-couch/` |
| Beercalls | `gopher://sdf.org/1/users/mnw/blog/beercalls/` |
| Web proxy | `https://gopher.floodgap.com/gopher/gw?a=gopher://sdf.org/1/users/mnw/` |
---
## References
- SDF Gopher Setup: https://wiki.sdf.org/doku.php?id=gopher_site_setup_and_hosting_features
- SDF Web Spaces: https://wiki.sdf.org/doku.php?id=accessing_web_spaces#gopher
- Tilde.town Gopher Guide: https://tilde.town/wiki/editing-your-homepage/gopher.html
- Floodgap Gopher Proxy: https://gopher.floodgap.com/gopher/gw
---
## Gophermap Quick Reference
From tilde.town wiki - item types we'll use:
| Code | Type | Usage |
|------|------|-------|
| `0` | Text file | Blog posts (.txt) |
| `1` | Directory | Category folders, navigation |
| `i` | Inline text | ASCII art, descriptions, spacing |
| `h` | HTML link | Link to web version (prefix with `URL:`) |
| `7` | Search | Future: search posts (CGI) |
**Syntax:** `{type}{description}\t{path}\t{host}\t{port}`
- Tab character separates columns
- Host/port optional for local resources
- For inline text (`i`): use `fake` as path, `(NULL)` as host, `0` as port
- For HTML links: path should be `URL:https://...`
**Example entries:**
```
i fake (NULL) 0
0[2024-10-05] Megalopolis megalopolis.txt
1Back to Blog Index ../
hView on web URL:https://mnw.sdf.org/posts/
```
---
## Notes
- Gophermaps must NOT have execute permission (causes errors)
- Files need 644, directories need 755
- CGI scripts (=echo) must use full paths on SDF
- ASCII art should stay under 12 lines for headers
- The `--delete` flag on rsync will remove posts where `phlog` was changed to `false`
- All work is git-tracked, so deletions are recoverable
+43
View File
@@ -98,4 +98,47 @@ To block a reply:
- "https://tilde.zone/@someone/123456789"
GOPHER / PHLOG (SDF Gopherspace)
--------------------------------
Posts can be published to gopher by adding `phlog: true` to frontmatter.
Deploy to gopher (after --gopher-only for first run, then use --gopher):
./scripts/remote_publish.sh --gopher # Hugo + Gopher
./scripts/remote_publish.sh --gopher-only # Gopher only
What it does:
1. Converts posts with `phlog: true` to plain text (70 char wrap)
2. Generates gophermaps for each category
3. Rsyncs to ~/gopher/blog/ on SDF
URLs after deployment:
- Gopher root: gopher://sdf.org/1/users/mnw/
- Blog index: gopher://sdf.org/1/users/mnw/blog/
- Frank's Couch: gopher://sdf.org/1/users/mnw/blog/franks-couch/
- Fun Center: gopher://sdf.org/1/users/mnw/blog/fun-center/
- Web proxy: https://gopher.floodgap.com/gopher/gw?a=gopher://sdf.org/1/users/mnw/
Gopher scripts (in scripts/gopher/):
- convert_to_gopher.py Markdown → gopher plain text
- generate_gophermaps.py Build category menus
- ascii_art.py Headers/footers
- update_root_gophermap.py Update main gophermap (one-time)
References:
- SDF Gopher Setup: https://wiki.sdf.org/doku.php?id=gopher_site_setup_and_hosting_features
- Tilde.town Guide: https://tilde.town/wiki/editing-your-homepage/gopher.html
PUBLISHING / REMOTE DEPLOY
--------------------------
Push changes to SDF and build:
./scripts/remote_publish.sh # Hugo only
./scripts/remote_publish.sh --gopher # Hugo + Gopher
./scripts/remote_publish.sh --gopher-only # Gopher only
The script handles git commit/push prompts automatically.
================================================================================