What
This repo contains the source code and content for my website at wincent.com.
Content is authored in plain-text-friendly markup formats like Markdown and
served using a dynamic stack (described below). New code can be deployed and
content added or updated via git push
.
Stack
- React: Declarative, component-oriented view layer.
- Relay: Declarative data-management.
- GraphQL: Hierarchical data querying language, type system and server.
- Git: Main content storage.
- Redis: Indexing and caching.
- memcached: Ephemeral caching.
Supporting tools and technologies:
Questions
Why not use a static site generator?
A static site generator would very much be the right tool for this job, however, building the site on a custom React/Relay/GraphQL stack was much more fun, so I did that instead.
Why the name "Masochist"?
Please see the introductory blog post, "Introducing Masochist".
Development
Quickstart
Prerequisites
brew install git memcached redis
Webpack-based hot-loading workflow
git clone https://github.com/wincent/masochist.git
cd masochist
yarn
yarn update-schema
yarn update-indices # Whenever content changes.
yarn start
Running in production-like environment
yarn run build # Builds files under `dist/`.
yarn start-prod
Running in production
export NODE_ENV=production
yarn
yarn build
node dist/bin/updateIndices.js # Whenever content changes.
node dist/server/main.js
Configuration
In __DEV__
, Masochist will look for content in the current repo (ie. .
).
In production, it expects to find a content repo at /srv/masochist/content
.
In __DEV__
, you can override this with npm config set
. For example, in my local development environment, I have the Masochist Git repo checked out in one folder, and a second copy of it with the content
branch checked out within it (using git-worktree
) at ./content
(see below for more details on this set-up). I can override the __DEV__
default of .
with:
# Use npm, not yarn, for this:
npm config set masochist:content-repo './content'
Deployment cheatsheet
You could do this in any number of ways but the way I'm doing it is using two local repositories as follows:
Local "masochist" repository
Structure
main
branch checked out.origin
remote pointing at git.wincent.com.github
remote pointing at GitHub.masochist
remote set up to do Heroku-style deploy-on-push, pointing at an Amazon EC2 instance configured using Ansible.content
remote set up to do a Heroku-style update-on-push for content changes, pointing at the corresponding repository on EC2.
Commands
$ git push masochist main # Deploy app (after initial provisioning).
$ git push masochist # Subsequent deployments.
$ git push origin # Propagate code, but no deploy.
$ git push # Shorthand for `git push origin`.
$ git push github # If you can't be bothered waiting for it to auto-replicate.
Local "content" worktree
Structure
content
branch checked out.content
remote configured to do Heroku-style push-to-publish.origin
remote pointing at git.wincent.com.github
remote pointing at GitHub.
Commands
$ git push content content # First push after initial provisioning.
$ git push content # Subsequent pushes.
$ git config branch.content.merge refs/heads/content # For laziness.
$ git push # Simple.
$HASH
Rollback to a prior rev $ git push masochist +$HASH:main
Or just switch symlinks and sudo monit restart masochist
.
Force a deploy without actual code changes
$ git commit -m Deploy --allow-empty
$ git push masochist