SeriesGetting started10

What you need

Getting started — part 1 of 10

From zero to a deployed site. The main README is the quick tour; this is the complete path, including the server side. Day-to-day usage lives in Writing.

In a hurry? The Quick start below is a complete copy-paste path to a site running locally on your machine -- one block per platform. The numbered sections after it are the full reference, including picking a deploy target to put the site on the internet.

Read more

Quick start

Getting started — part 2 of 10

Each block below is the whole path for one platform: prerequisites, clone, config, first post, local preview. They end at the same place -- a site you can see at http://localhost:8000/ -- and from there, section 6 takes it to the internet.

./setup.sh is the config step: it asks for the settings a site needs, checks the answers as it goes, and writes config/site.yml and env.sh for you. Most questions can be skipped with Enter, which keeps what is there -- the ones that cannot are the site's title, short name, description, author and address for as long as they still hold the template's examples -- and nothing is written until you have seen the diff and confirmed it -- so it is also the way to change any of this later. If you would rather edit the files yourself, the numbered sections below are the full reference and section 2 still starts with the two cp commands; the wizard leaves both files commented and hand-editable either way.

macOS

The system Ruby is 2.6 from 2019 and Apple treats it as frozen, so the one real step is a current Ruby via Homebrew:

Read more

Configure the site -- config/site.yml

Getting started — part 3 of 10

git clone https://github.com/DanielSnor/blog.sh.git myblog
cd myblog

One clone = one site. Everything personal (posts, media, config, secrets) is gitignored, so pulling engine updates later never touches your content -- see Updating the engine.

cp config/site.yml.example config/site.yml

Or let ./setup.sh do it: it seeds the file from this same example -- comments and all -- and fills in the answers you give it, which is the same file you would have written by hand, minus the chance of a tab where a space belongs. It covers the site block, the comments network and the deploy target; ./style.sh covers banner, about, footer, social, colors, fonts, widgets and nav. Both are re-runnable and neither takes anything away from editing the file directly.

Read more

Configure the environment -- env.sh

Getting started — part 4 of 10

cp env.sh.example env.sh
chmod 600 env.sh

env.sh holds secrets and per-environment values; it is gitignored and mode 600 because live credentials go in it. An unedited copy is enough to try everything locally -- with no deploy target configured, uploads are skipped (logged, not an error).

The one value worth knowing about right away is SITE_BASE_URL: the canonical URL normally comes from site.base_url in config/site.yml (step 2), and this env.sh value overrides it when set -- it exists so a staging or local environment can point somewhere other than production while building from the same config. One site, one URL? Set site.base_url and leave this out.

Watch the order they start in: the example above ships SITE_BASE_URL active and pointing at https://example.com, so filling in site.base_url carefully and leaving this file alone gives a site that still calls itself example.com in its feed, its sitemap and every share preview. Either comment this line out or give it the same address. ./setup.sh writes both to the same value for exactly this reason, and ./blog.sh doctor reports the address that would actually be used rather than the one in the config.

Banner and favicon

Getting started — part 5 of 10

Both ship with the engine as assets/images/defaults/ -- the first build copies whatever is missing to the live names assets/images/header.png (the path banner.src defaults to) and assets/images/favicon.png, so a fresh clone renders before you've drawn anything. The live names are gitignored: replace them with your own artwork and neither git pull nor a rebuild will touch it. That also means nothing else keeps a copy -- put both files in your backup (Backup), or a restore brings the site back wearing the engine's default artwork. Set banner.width/height to the real dimensions of your image: those attributes are what reserves space before it loads, and a mismatch makes the page jump.

./style.sh does that part for you -- give it the path to an image and it copies the file into place, reads its real dimensions and writes them, so the pair can never drift from the file. ./blog.sh doctor reports it if they ever do.

The favicon is used three ways from that one file: the <link rel="icon">, an apple-touch-icon (iOS scales it down for a home-screen bookmark), and a generated /favicon.ico for clients that request the root path without reading the link. A square PNG of 180 px or more covers all three.

Read more

First build and local preview

Getting started — part 6 of 10

./blog.sh add                  # write your first post (opens $EDITOR)
ruby build/build_blog.rb       # build into public.nosync/ (one language; ./blog.sh rebuild builds all and deploys)
./blog.sh preview               # preview at http://localhost:8000 (Ctrl-C stops it)

add always creates a draft and offers publishing interactively -- see Writing for the full authoring flow.

Replacing an existing blog? Bring the old content in before you deploy, so the first published version of the site is already complete. ./import.sh walks you through it -- Bluesky, Mastodon, Pixelfed, Tumblr, a Twitter/X archive export, or WordPress and any RSS/Atom feed -- and always previews what it would write before writing anything. See The archive → Importing, and ./import.sh --help for the scriptable equivalents.

Pick a deploy target

Getting started — part 7 of 10

Set DEPLOY_BACKEND in env.sh plus the values for your choice, then:

./scripts/deploy-web.sh --dry-run   # shows what would upload, touches nothing
./scripts/deploy-web.sh             # first real deploy (uploads everything once)

Every later deploy uploads only new/changed files -- a SHA-256 manifest (.deploy_manifest*.json, one per backend) tracks what the target already has, while .deploy_baseline.json records the shape of the last build the safety guards accepted. Both are gitignored and both are disposable.

The first deploy is rarely the one you run by hand. From here on the engine deploys as part of writing: saving a draft builds the site and deploys it, because a draft's preview is a real address on the real site (see Writing → Writing and publishing). So the target is written to the first time you save anything -- not the first time you type deploy-web.sh. Whatever is already at that address under a name the build also uses is replaced, and a placeholder page is exactly such a name.

Read more

Running on a server

Getting started — part 8 of 10

The engine runs wherever the content lives -- typically either on your laptop (deploying to a remote target) or on the server itself (SSH in to write; this is how the reference deployment on sean.cz works, inside a Cloudron/Docker container). For the server variant:

  1. Clone the repo on the server, repeat steps 2-6 there. env.sh stays on the server only -- it never syncs anywhere.
  2. Install the widget cron (see Running it).
  3. For writing from a phone, prepare the incoming/ staging directory: any SSH/SFTP account that can write into <repo>/incoming/ works. A dedicated user without sudo, limited to SFTP, is the safe shape -- photos are uploaded there by name and referenced as bare filenames in posts (see Writing).
  4. For a post sent whole from a phone, add the key the shortcut uses to authorized_keys with a forced command pointing at scripts/receive.sh -- the line, the RSA requirement and the size ceiling are in Writing. Nothing new listens on the network; it travels over the SSH the server already has.
  5. In a container setup (Cloudron and similar), the engine lives inside the container's persistent data directory and commands run via docker exec / the platform's terminal -- the engine itself doesn't care, it's just Ruby + bash in a directory.

Comments network (optional): Mastodon or Bluesky

Getting started — part 9 of 10

Every published post is announced on the configured network, and replies to that announcement are the post's comments. A reply's pictures appear under its words -- thumbnails linking out to the full image, loaded from the commenter's own network the same way their avatar already is; a reply marked sensitive keeps its pictures to itself. Configure exactly one of the two -- the build refuses a config with both, since a discussion split across two networks serves nobody. Without either section, everything else still works; publishing just skips the announcement (logged, not an error).

Mastodon:

Read more

Updating the engine

Getting started — part 10 of 10

git pull
./blog.sh rebuild

rebuild, not ruby build/build_blog.rb: the build script renders one language per run, and rebuild runs it for every language the site publishes and then deploys. On a site in two languages the bare script followed by a deploy leaves the second language's pages as the previous version built them -- and uploads them that way.

main is the release branch: a clone on it gets each release by git pull. A clone that was put on a release tag (git checkout v1.8) is on no branch, and git pull refuses with "You are not currently on a branch"; move it to the next release by name instead:

git fetch --tags && git checkout v1.9

The first build after an upgrade may be a full one -- the engine's own fingerprint changed, so the record of the last build is thrown away -- and it writes .build_cache.json in the installation directory, which is per-machine, gitignored and always safe to delete. Deploy the assets with it: a page that cannot fetch assets/js/share.js logs a 404 on every load.

Read more