{"id":15793256,"url":"https://github.com/evaneykelen/msgtrail","last_synced_at":"2025-08-01T03:05:21.768Z","repository":{"id":48761346,"uuid":"176582556","full_name":"evaneykelen/msgtrail","owner":"evaneykelen","description":"A simple blog publication tool","archived":false,"fork":false,"pushed_at":"2022-10-06T04:45:42.000Z","size":81992,"stargazers_count":0,"open_issues_count":11,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-07-12T08:25:30.922Z","etag":null,"topics":["blog-engine","erb","ruby"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/evaneykelen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-19T19:16:11.000Z","updated_at":"2021-02-09T09:18:28.000Z","dependencies_parsed_at":"2022-08-26T23:10:34.186Z","dependency_job_id":null,"html_url":"https://github.com/evaneykelen/msgtrail","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/evaneykelen/msgtrail","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evaneykelen%2Fmsgtrail","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evaneykelen%2Fmsgtrail/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evaneykelen%2Fmsgtrail/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evaneykelen%2Fmsgtrail/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/evaneykelen","download_url":"https://codeload.github.com/evaneykelen/msgtrail/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evaneykelen%2Fmsgtrail/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267464409,"owners_count":24091494,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-28T02:00:09.689Z","response_time":68,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blog-engine","erb","ruby"],"created_at":"2024-10-04T23:10:19.133Z","updated_at":"2025-08-01T03:05:21.722Z","avatar_url":"https://github.com/evaneykelen.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Gem Version](https://img.shields.io/gem/v/msgtrail.svg) ![License](https://img.shields.io/github/license/evaneykelen/msgtrail.svg)\n![Last Commit](https://img.shields.io/github/last-commit/evaneykelen/msgtrail.svg)\n\n## MsgTrail\n\n**MsgTrail is a blog publication tool.**\n\nIt is used to manage and publish [this blog](https://www.msgtrail.com/).\n\n### Features\n\n- Content sources:\n  - Local Markdown files\n  - GitHub [Gists](https://gist.github.com/)\n  - Tweets including support for [tweetstorm](https://en.wiktionary.org/wiki/tweetstorm) stitching\n- User interface: command line\n- End result: self-contained HTML\n- Hosting: no (bring your own)\n- Search: yes\n- Archive page: yes\n- Feed: yes (Atom)\n\n### Simplicity\n\nMsgTrail enables you to write, proofread, and publish your blog entirely from the command line.\n\nHere is how creating and publishing a new blog article works:\n\n1. Save your Markdown article to a directory called `articles`.\n2. Add a reference to the article to `blog.json`.\n3. Invoke `msgtrail {path-to-your-blog-directory}` from the command line.\n4. Upload/sync the output of the `msgtrail` command to e.g. AWS S3 or your static hosting provider of choice.\n\nBesides a local Markdown article you can also use a GitHub Gist or Tweet as an \"input source\" for a blog article.\n\nThe output of the `msgtrail` command is based on a set of simple HTML templates which use Ruby's ERB templating system.\n\nThe standard theme contains a blog index page, an archive page, and an Atom feed.\n\n### Ingredients\n\nYou need three ingredients to publish a blog:\n\n1. You must install the `msgtrail` command line tool.\n2. You need a local copy of the blog theme.\n3. You need a place to host the output (static HTML files).\n\nThis README only covers steps 1 and 2.\n\n### Installing MsgTrail\n\nInvoke `gem install msgtrail` to install the MsgTrail Ruby gem. You need at least Ruby v2.x.\n\nType `msgtrail` without arguments to display its version and usage hint:\n\n```\n\u003emsgtrail\nVersion: 1.0.1\nUsage: msgtrail {theme-directory-name}\n```\n\n### Downloading theme\n\nAfter installing the gem it is time to download and check out the theme.\n\nUnlike many other blog engines the configuration of the MsgTrail theme is dead simple.\n\nThe theme directory structure consists of the following files and directories:\n\n- The `articles` directory contains your blog article in Markdown format.\n- The `blog` directory contains the output of the `msgtrail` command. This directory is empty when you download the theme zipfile. You'll use the `msgtrail` command to generate output for this directory.\n- The `theme` directory contains the actual HTML templates and layouts for your blog.\n- `blog.json` contains a reference to each blog article.\n- `config.json` contains blog settings.\n- `site.json` describes the various pages in your blog such as \"index\", \"archive\", et cetera.\n\n### blog.json\n\nThe default theme contains an example blog with three articles:\n\n```\n[\n  {\n    \"title\": \"First post\",\n    \"date\": \"2019-01-02\",\n    \"time\": \"23:55\",\n    \"file\": \"articles/hello-blog.md\"\n  },\n  {\n    \"title\": \"Markdown example\",\n    \"date\": \"2019-04-07\",\n    \"time\": \"21:34\",\n    \"gist_id\": \"fd8d3b448ea4c2edec93c34baca44ad4\"\n  },\n  {\n    \"title\": \"Writing a programmer-oriented blog engine\",\n    \"date\": \"2019-04-06\",\n    \"time\": \"13:16\",\n    \"tweet_ids\": [\n      1114623023397646337\n    ],\n    \"archived\": true\n  }\n]\n```\n\nYou can add a fourth article by adding the following lines to the top of `blog.json`:\n\n```\n{\n  \"title\": \"This is my latest post\",\n  \"date\": \"2019-03-28\",\n  \"time\": \"22:04\",\n  \"file\": \"articles/this-is-my-latest-post.md\"\n}\n```\n\nAlternative you can replace the `file` directive for a reference to a gist:\n\n```\n\"gist_id\": \"fd8d3b448ea4c2edec93c34baca44ad4\"\n```\n\nOr you can use one or more references to tweet IDs:\n\n```\n\"tweet_ids\": [\n  1114623023397646337\n]\n```\n\n### config.json\n\nThe `msgtrail` command reads its configuration settings from `config.json`:\n\n```\n{\n  \"domain_matter\": {\n    \"site_url\": \"https://www.msgtrail.com/\",\n    \"feed_url\": \"https://www.msgtrail.com/feed/index.xml\",\n    \"permalink_url\": \"https://www.msgtrail.com/articles/%s\",\n    \"search_url\": \"https://www.google.com?q=site%3Awww.msgtrail.com%20%7Bsearch%20phrase%7D\",\n    \"about_url\": \"https://www.bitgain.com/\"\n  },\n  \"file_matter\": {\n    \"blog_manifest_file\": \"blog.json\",\n    \"site_manifest_file\": \"site.json\",\n    \"article_directory\": \"articles\",\n    \"blog_directory\": \"blog\",\n    \"theme_directory\": \"theme\"\n  },\n  \"head_matter\": {\n    \"blog_title\": \"MsgTrail\",\n    \"blog_sub_title\": \"A blog by Erik van Eykelen\",\n    \"language\": \"en-us\"\n  }\n}\n```\n\nAll configuration settings are available through a global variable called `cfg` in the ERB templates. For instance the `site_url` setting can be accessed using `\u003c%= cfg.domain_matter.site_url %\u003e`.\n\n### site.json\n\nThe `msgtrail` command uses `site.json` to generate HTML files by combining layouts, templates, Markdown articles, gists, and tweets and writing the result to an output directory.\n\nAs you can see from `site.json` it is easy to map input (layout/template) to output (HTML):\n\n```\n[\n  {\n    \"layout\": \"layout.html.erb\",\n    \"template\": \"index.html.erb\",\n    \"output_file\": \"index.html\",\n    \"iterator_subject\": false\n  },\n  {\n    \"layout\": \"layout.html.erb\",\n    \"template\": \"archive.html.erb\",\n    \"output_path\": \"archive\",\n    \"output_file\": \"index.html\",\n    \"iterator_subject\": false\n  },\n  {\n    \"layout\": \"layout.html.erb\",\n    \"template\": \"article.html.erb\",\n    \"output_path\": \"articles/%s\",\n    \"output_file\": \"index.html\",\n    \"iterator_subject\": true\n  },\n  {\n    \"layout\": \"layout.xml.erb\",\n    \"template\": \"feed.xml.erb\",\n    \"output_path\": \"feed\",\n    \"output_file\": \"index.xml\",\n    \"iterator_subject\": false\n  }\n]\n```\n\n### Theme\n\nMsgTrail ships with a basic theme. Feel free to adapt it to your own needs.\n\nThe theme consists of just 7 ERB files:\n\n- `_article.html.erb`\n- `archive.html.erb`\n- `article.html.erb`\n- `feed.xml.erb`\n- `index.html.erb`\n- `layout.html.erb`\n- `layout.xml.erb`\n\nNoteworthy:\n\n`_article.html.erb` is a \"partial\" (aka \"include\") which is called by `article.html.erb` and `index.html.erb` in order to DRY-up the code.\n\nThe `_` underscore in front of the file name is a nod to Rails' partial naming convention.\n\nInside `article.html.erb` and `index.html.erb` you'll see that it is easy to embed a partial:\n\n`\u003c%= render_partial('article', { article: article }) %\u003e`\n\nThe `article` variable gets passed to the article partial and made available through a global `variables` hash. Inside `_article.html.erb` you'll see for instance:\n\n`\u003c%= variables[:article][:published][:date] %\u003e`\n\nThis line fetches the `:date` value from the `:published` hash, which is part of the `:article` hash. You may add additional variables to the `{ article: article }` hash e.g. `{ article: article, foo: 'bar' }`.\n\nThe fact that the word `article` is used three times in `render_partial('article', { article: article })` is not a requirement. The following is also correct: `render_partial('alpha', { bravo: charlie })`, provided you rename the partial to `alpha` and rename the variables accordingly.\n\nThe two layout files (`layout.html.erb` and `layout.xml.erb`) each contain `\u003c%= yield %\u003e`. The `yield` method is used by MsgTrail to \"wrap\" the contents of the layout file \"around\" e.g. `index.html.erb` or one of the other templates.\n\nUnlike with Ruby on Rails the `.html.erb` and `.xml.erb` file extensions used by the layouts and templates have no special meaning for MsgTrail. Instead, the file extensions in the final publication are defined by `site.json`.\n\nAs mentioned before, you'll also come across a variable called `cfg`. See above for an explanation.\n\n### Step-by-step instructions\n\n- Install the MsgTrail gem (see above).\n- [Download](https://github.com/evaneykelen/msgtrail/releases/tag/v2) the `sample-blog.zip` file.\n- Unzip the archive to a directory called `sample-blog`.\n- Enter `msgtrail sample-blog/`. The output will be something like:\n\n```\nDeleted '/Users/.../sample-blog/blog'\nCreated '/Users/.../sample-blog/blog'\nCreated '/Users/.../sample-blog/blog/index.html'\nCreated '/Users/.../sample-blog/blog/archive/index.html'\nCreated '/Users/.../sample-blog/blog/articles/20190102-2355-first-post/index.html'\nCreated '/Users/.../sample-blog/blog/articles/20190407-2134-markdown-example/index.html'\nCreated '/Users/.../sample-blog/blog/articles/20190406-1316-writing-a-programmer-oriented-blog-engine/index.html'\nCreated '/Users/.../sample-blog/blog/feed/index.xml'\n```\n\n-  If you open the `blog/index.html` file in your web browser you should see the blog home page.\n\n### Feedback, comments, bugs, or praise?\n\n- Create a GitHub issue if you run into issues or bugs.\n- Ping me on Twitter: [@hackteck](https://twitter.com/hackteck/)\n\n### Similar projects\n\n- [Docusaurus](https://docusaurus.io/)\n- [Hexo](https://hexo.io/)\n- [Hugo](https://gohugo.io/)\n- [Jekyll](https://jekyllrb.com/)\n- [Middleman](https://middlemanapp.com/)\n- [Nanoc](https://nanoc.ws/)\n- [Octopress](http://octopress.org/)\n- [Pelican](https://blog.getpelican.com/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevaneykelen%2Fmsgtrail","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fevaneykelen%2Fmsgtrail","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevaneykelen%2Fmsgtrail/lists"}