spilk@home:~$
~/posts/2025-07-17-streamlining-my-neocities-posts-a.html

Streamlining My Neocities Posts: A Technical Deep Dive

As someone who loves tinkering with code and keeping a digital presence, maintaining my personal blog on Neocities has been a rewarding journey. Recently, I’ve significantly streamlined my workflow for creating and publishing new posts. What used to be a multi-step manual process involving file conversions and Git commands, is now largely automated, letting me focus more on writing and less on the mechanics.

This post will detail the technical “under the hood” changes that power my new, efficient posting system.

The Old Way (and Why It Needed an Upgrade)

Initially, my blog posts were hand-written HTML or Markdown that I manually converted. Publishing involved:

  1. Writing content.
  2. Manually converting Markdown to HTML (if applicable).
  3. Manually renaming files with 2025-07-17-slug.html.
  4. Copying them into the correct directory.
  5. Running node generate_blog.js locally.
  6. Performing git add, git commit, and git push commands.
  7. Hoping the GitLab CI/CD pipeline caught everything correctly.

This was functional, but prone to human error and interruptions to my writing flow. I wanted a more seamless experience, especially for integrating with my Obsidian vault.

The New, Automated Workflow

My improved system introduces two key players: a Python utility script (post.py) and an enhanced Node.js site generator (generate_blog.js), all orchestrated by GitLab CI/CD.

1. The post.py Script: Your New Blog Assistant

The star of the show for post creation is post.py. This Python script is designed to take my raw Markdown notes, prepare them, and even handle the initial Git operations.

Key Responsibilities of post.py:

  • Front Matter Stripping: Obsidian notes often contain YAML front matter at the top (e.g., --- tags: [tech] ---). My generate_blog.js script doesn’t consume this, so post.py now automatically detects and removes these blocks, ensuring only the pure Markdown content is passed through. This is crucial for clean rendering on the site.
    def remove_front_matter(content):
        if content.startswith('---'):
            parts = content.split('---', 2)
            if len(parts) > 2:
                return parts[2].strip()
        return content
    
  • Intelligent File Naming: It generates a standardized filename for each post: 2025-07-17-HHMMSS-post-slug.md.
    • 2025-07-17: The current date.
    • HHMMSS: The exact hour, minute, and second of post creation. This is critical for precise chronological sorting later.
    • post-slug: A clean, URL-friendly slug generated from the post’s main title (the first H1 or H2 found in the Markdown).
  • Automated Git Operations (Optional): With the -c or --commit-and-push flag, post.py will execute a sequence of Git commands: git pull, git add ., git commit -m "New Post YYYY-mm-dd-HHMMSS-slug.md", and git push. This means a single command can now push a new post all the way to the repository, triggering deployment.

Example Usage:

python post.py -f ~/ObsidianVault/MyNotes/MyNextGreatPost.md -c

This command pulls the latest changes, processes the Markdown file (stripping front matter), renames it, copies it to raw_posts/, commits the changes, and pushes them to GitLab.

2. The generate_blog.js Script: The Static Site Engine

The core of the static site generation remains generate_blog.js, but it has been refined to work seamlessly with the new filename convention and ensure perfect sorting.

Key Enhancements in generate_blog.js:

  • Timestamp Parsing: The script’s parsePostFilename function now intelligently extracts not just the date, but also the HHMMSS timestamp from the new filenames. This timestamp is then used to create a precise JavaScript Date object for each post.
// In generate_blog.js, within parsePostFilename
const parts = filename.match(/^(\d{4}-\d{2}-\d{2})(?:-(\d{6}))?-(.+?)\.(html|md)$/);
// ...
const fullTimestampString = `${date}T${time.substring(0,2)}:${time.substring(2,4)}:${time.substring(4,6)}`;
const fullTimestamp = new Date(fullTimestampString);
// ...
return { date: date, fullTimestamp: fullTimestamp, /* ...other properties */ };

Precise Chronological Sorting: The list of all blog posts is now sorted using this fullTimestamp object. This guarantees that posts made on the same calendar day are correctly ordered by the exact time they were created, ensuring the very latest content always appears at the top of the main blog feed.

// In generate_blog.js, in the main generation function
postsMetadata.sort((a, b) => b.fullTimestamp.getTime() - a.fullTimestamp.getTime());
  • Markdown to HTML Conversion: The markdown-it library continues to handle the robust conversion of Markdown content into HTML, respecting HTML elements embedded in the Markdown source.

3. GitLab CI/CD: The Automated Publisher

My .gitlab-ci.yml pipeline ties everything together. When post.py pushes the new file to the main branch, the GitLab Runner springs into action:

  • Linting and Formatting: A new lint stage runs ESLint and Prettier for JavaScript, and Black and Flake8 for Python. This ensures all code adheres to predefined style guides before deployment.

  • Dependency Installation: npm install runs to ensure markdown-it (and other dev dependencies for linting) are available.

  • Site Generation: node generate_blog.js is executed on the runner, processing the new Markdown file (and all others) into the final index.html and individual post HTML files in the posts/ directory.

  • Deployment to Neocities: The generated HTML and CSS files are then automatically uploaded to my Neocities site using the Neocities API, making the new post live.

The Result: More Writing, Less Worrying

This setup has significantly improved my content creation process. I can write my thoughts in Markdown, execute a single command, and know that my post will be correctly formatted, sorted precisely, and deployed automatically. It’s a prime example of how a few thoughtful scripting additions can greatly enhance a static site workflow.

I hope this technical breakdown is useful for anyone looking to optimize their own static blog operations!

← Back to Blog Home