DEV Community

Alex Towell
Alex Towell

Posted on • Originally published at metafunctor.com

Crier: Cross-Post Your Content Everywhere

I've published crier to PyPI - a command-line tool for cross-posting content to multiple platforms simultaneously.

Like a town crier announcing news across the land, crier broadcasts your content to dev.to, Ghost, WordPress, Hashnode, Medium, Bluesky, Mastodon, Threads, Telegram, Discord, and more.

Installation

pip install crier
Enter fullscreen mode Exit fullscreen mode

The Problem

If you maintain a blog and want to reach audiences on multiple platforms, you face tedious manual work: copy content to dev.to, create a Bluesky post with a link, toot on Mastodon, post to your Discord server, etc. Each platform has its own interface, API, and quirks.

The Solution

Crier lets you publish once and distribute everywhere:

# Publish to multiple platforms at once
crier publish post.md --to devto --to bluesky --to mastodon --to discord
Enter fullscreen mode Exit fullscreen mode

That single command:

  • Publishes the full article to dev.to
  • Creates a Bluesky post with title, description, and link
  • Toots on Mastodon with hashtags from your tags
  • Posts an announcement embed to your Discord server

Supported Platforms

Platform Type Notes
dev.to Blog Full article support
Hashnode Blog Full article support
Medium Blog Publish only
Ghost Blog Full article support
WordPress Blog Self-hosted or .com
Buttondown Newsletter Email subscribers
Bluesky Social Posts with link cards
Mastodon Social Toots with hashtags
Threads Social Short posts
LinkedIn Social Professional network
Twitter/X Social Copy-paste mode
Telegram Channel Bot posts
Discord Channel Webhook embeds

How It Works

Crier reads standard markdown with YAML front matter:

---
title: "My Post Title"
description: "A brief description"
tags: [python, cli, automation]
canonical_url: https://myblog.com/my-post
---

Your content here...
Enter fullscreen mode Exit fullscreen mode

For blog platforms, it publishes the full article. For social platforms, it creates short announcements with links back to your canonical URL.

GitHub Actions: Fully Automated Cross-Posting

The real power comes from combining crier with GitHub Actions. Push a new post to your repo and it automatically gets cross-posted everywhere.

Quick Setup

# In your content repository
crier init-action
Enter fullscreen mode Exit fullscreen mode

This creates a workflow file and sets up your API keys as GitHub secrets automatically.

How It Works

  1. You push a new markdown file to posts/
  2. GitHub Action triggers
  3. Crier checks its registry for what's already published
  4. Crier publishes to any platforms that are missing
  5. The registry is committed back to your repo

Here's what the workflow looks like:

name: Cross-Post Content

on:
  push:
    branches: [main]
    paths:
      - 'posts/**/*.md'

jobs:
  crosspost:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      - run: pip install crier

      - name: Cross-post to platforms
        env:
          CRIER_DEVTO_API_KEY: ${{ secrets.CRIER_DEVTO_API_KEY }}
          CRIER_BLUESKY_API_KEY: ${{ secrets.CRIER_BLUESKY_API_KEY }}
        run: |
          crier backfill ./posts --yes

      - name: Save publication state
        run: |
          git config user.name "github-actions[bot]"
          git add .crier/
          git diff --staged --quiet || git commit -m "Update publication registry"
          git push
Enter fullscreen mode Exit fullscreen mode

The Registry

Crier maintains a registry (.crier/registry.yaml) tracking what's been published:

posts:
  "posts/my-article.md":
    title: "My Article"
    publications:
      devto:
        id: "12345"
        url: https://dev.to/user/my-article
      bluesky:
        id: "abc123"
Enter fullscreen mode Exit fullscreen mode

This means adding a new platform later will automatically backfill your existing content.

CLI Commands

crier publish FILE [--to PLATFORM]...  # Publish to platform(s)
crier backfill PATH [--profile NAME]   # Publish missing content
crier audit PATH                        # Show what needs publishing
crier list PLATFORM                     # List your articles
crier update PLATFORM ID --file FILE   # Update existing
crier platforms                         # Show available platforms
crier config set KEY VALUE              # Configure API keys
crier init-action                       # Set up GitHub Actions
Enter fullscreen mode Exit fullscreen mode

Configuration

Set up API keys once:

crier config set devto.api_key YOUR_DEV_TO_KEY
crier config set bluesky.api_key "handle.bsky.social:app-password"
crier config set mastodon.api_key "mastodon.social:access-token"
Enter fullscreen mode Exit fullscreen mode

Or define profiles for common workflows:

# ~/.config/crier/config.yaml
profiles:
  blogs: [devto, hashnode]
  social: [bluesky, mastodon]
  all: [blogs, social]
Enter fullscreen mode Exit fullscreen mode

Then use: crier publish post.md --profile all

Why I Built This

I maintain content across several platforms and got tired of the manual copy-paste workflow. Crier automates the tedious parts while keeping you in control of what goes where.

The GitHub Actions integration is the key feature: write once in your repo, push, and your content appears everywhere automatically.

Links

Top comments (0)