Hacker News new | past | comments | ask | show | jobs | submit login
A minimal blog engine written in Go, compatible with Ghost themes (kabukky.github.io)
280 points by Maksadbek on April 25, 2015 | hide | past | favorite | 94 comments



Author of Journey here (thanks Maksadbek for posting it on HN). I'm going to repost my comment from Reddit:

I've been working on this blog engine in my spare time for the last few months. My blog has been running on it without hiccups for about a month now (low volume, 700 visits a day tops).

I'd like some opinions if anyone has the time to try it out. (Worth pursuing? Any ideas for features? Should I die in a fire because my code quality sucks?)

Thanks :)


Author of the post here. I found your blog engine awesome. It inspired me to create my blog. Thanks for such a good project :)


Looks like it is a neat project, thanks for open sourcing it. Do you plan to support static export?


If you need static export, I wrote a project a while back that converts markdown to static output with Ghost theme support: https://github.com/mixu/ghost-render


It's not yet planned, but definitely something that would be useful. I'll add it to the list.

Here's my high priority feature list right now:

Gzip support, hashes for serving static assets, multi-user support, support for all of the Ghost theme helpers, MySQL, Postgres, and Google App Engine support.


One of the reasons I started playing with Ghost is because generating the site statically allows for a lot of performance on a $5 VPS with nothing special but a fat internet pipe. As blogs are typically not heavy on the "interaction" aspects of things, this works really well.

So consider this a vote for "static site support". If I were to express that as a requirement it would be something like "Site is served off a set of static pages that were generated on demand as the content on them or around them changed."


"One of the reasons I started playing with Ghost is because generating the site statically allows for a lot of performance on a $5 VPS with nothing special but a fat internet pipe. As blogs are typically not heavy on the "interaction" aspects of things, this works really well."

Wrote my own engine for exactly this reason.

All python, no templates and only one imported module (markdown) which I'm going to use pythons (why not in the first place?). I concur with the idea. Whole blog is about 1.4Mb of text. Images are hosted so it's AUD3.5/month to host the blog + domain.

It's not Ghost compatible. I based my template on Jeckyl. That is YAML front-matter followed by markdown. All contained in text files. It's a bit slot at the moment but works a charm.

What format are you following?


Heh, same. Used FrontPage for a while to basically "push" static pages to my site, then played with Blosxom for a bit but never published anything with it, wrote my own in Perl with Template toolkit and Markdown, and as that was going along, heard about and downloaded Ghost. Then built a 'theme' that matched my original skeumorphic notebook theme and basically have what I need to push the button on it (but haven't for some reason).


previous

* hotdog (binary) -> site (worked there)

* blosxom (perl) -> site

* perl -> TT -> ftp -> site

* python -> webpy -> appengine -> site

now * python -> markdown -> ftp/ssh -> site

future

* [some back-end language/Rpi] -> js [front-end/iPad] -> [some transport layer] -> site

I'm trading simplicity for speed and turn-around. The tool-chain is all cli, vim + browser. At some stage soon I'll write either an api/server to allow a simple interface to speed up process.

The basic ideas are based on Dave Winer and Scripting.com


I wish Octopress was this easy to use.


I had a look at the docs on github (I haven't checked the source yet) but it doesn't mention if it supports more than one domain (or SNI, but I'm assuming it would piggy back on Go's support for SNI).

If it doesn't yet support multi domain will it ever?


Interesting. I haven't thought about that. It should be possible, but is not implemented yet. Go does indeed support SNI, but I'm using ListenAndServeTLS at the moment + I'm loading only one SSL certificate right now.

I'll add that to the list as well. I think that might be a useful feature.


How did using Go feel compared with other languages, I'm a php, ruby, python guy. Just wondering your opinion on why you chose Go?


I chose Go because I wanted to learn it ;) In retrospect tough, I couldn't have wished for a better language to write a web server in. I especially like the opinionated handling of code formatting and memory safety (it won't let you compile if you have a unused variable for example) and the consistent error handling (I know, a lot people seem to dislike Go's error-oriented programming style).


Unused variables have nothing to do with memory safety. At most they'll take up some stack or heap space.

https://en.wikipedia.org/wiki/Memory_safety

Memory safety is enforced in Go via best practices–like C and C++ ecosystems. However because of gc and channels race conditions happen less often in Go but there's no explicitly tooling to prevent them from happening:

http://research.swtch.com/gorace


You are right. I was thinking about the general fail-safety of my programs. Not letting me compile saved me from overlooking a (return) value more than once.


You could accomplish the same thing by incorporating a code checker as part of your build process.

That way you would be catching a lot more code issues then just unused imports, variables etc.


It would really great if you could convert it into a modular CMS with ecommerce features similar to Drupal Commerce.


Regarding Golang's if-hell e.g. in https://github.com/kabukky/journey/blob/master/database/upda...

Couldn't you use panic/recover locally, inside functions - just for having a single error handling code?


No, panic/recover is a major hack.

Rob Pike has written ideas here on how to clean up error handling if-blocks: http://blog.golang.org/errors-are-values


No, it's not a "major hack." That blog post is excellent, but it just doesn't always apply.

The panic/recover idiom is extremely useful in parsers. For example, Go's JSON decoder: http://golang.org/src/encoding/json/decode.go


The difference is that in parsers, errors are almost certainly fatal. As opposed to a runtime SQL error, where it could be a user error, value error, or other database error, which can be recovered from.

Panic/recover are usually meant to catch "coding" and "assertion"-type errors (null pointers, array overflows, etc), not runtime errors. The latter is what the "error" values are used for.


I'm pretty sure you've misunderstood. Did you read the code I linked you to? Look at how it handles errors internally. Go's standard JSON decoder isn't going to crash your program if it fails to parse JSON---that would be disastrous. It exposes regular errors to the user. But internally, it uses panic/recover because it elides a lot of manual error checking.


Panic/recover is no problem as long as it does not traverse API boundaries, i.e. affects the user of your package, unless it really is a "fatal user error" in a function that wouldn't otherwise return an error.


Interesting. That's basically the Maybe monad.


I haven't written go in a while, but I recall being rather dissatisfied with the so-called 'if-hell'. I've been wondering lately - is there any reason why you couldn't/shouldn't use monads for this? Nothing obvious popping out of Google.

"gonads" would be my choice of name for this library.


Love the name! But there's no way Go's feeble type system supports anything remotely like a Monad.


Here's what GIlad Bracha might say about that:

http://www.infoq.com/presentations/functional-pros-cons?utm_...


Since they're wrapping things in transactions anyways, they could just defer the error check to the end (or for that matter rework their interface to the database to stop after the first error is detected).



This is a simple way of dealing with repetitive error handling.


I've written a tiny package that does just that in a controlled way, using named returns to bubble the error out.

https://github.com/dagoof/failure

[Docs] https://godoc.org/github.com/dagoof/failure


I have never messed with panic, I read that code and I think I'd make a function that takes functions - the error handling response is the same every time. Maybe even put the list of calls in an actual list and invoke them with an iterator.


Explicit error recovery is not hell, it's a blessing. Much easier to understand and to maintain than exception handling.


There is a third way. See Haskell's Either l r, or Rust's Result<T, E> types. See http://lucumr.pocoo.org/2014/10/16/on-error-handling/ and http://lucumr.pocoo.org/2014/11/6/error-handling-in-rust/

I don't know why so often, the immediate reaction to legitimate criticism of this wart in Go is to argue against exceptions, even when nobody has even brought up exceptions as an alternative.


In fact, Either/Maybe can function like safer versions of both paradigms: Functors allow you to build a program of many functions that will terminate with 'Nothing' or 'Either l' as long as any one of them experiences an error, without thinking about errors, and it still lets you explicitly handle any errors if you want to, this time on the sum type Maybe or Either, meaning that you usually handle both the error and non-error case, or receive a compiler warning.

I agree that, probably universally, the people who fervently argue for either exceptions or explicit Go-style error handling don't know about Maybe/Either/Result. Then again, all this is pretty much impossible to replicate in Go.


Thank you for mentioning this. It's frustrating that every time someone complains about Go's error-handling style, the first assumption is that the complainer then necessarily has to prefer exceptions. In truth, errors-as-values are very elegant in languages like Haskell IMO. And errors-as-values are not at all something that Go has managed to put back on the roadmap in a modern language against the grain of everyone else (the "exception people") as many seem to imply, including Rob Pike in his "errors as values" article.


Option types are nice. The fourth way (and I think the best) is supervision trees, though this is more applicable when you have multiple units of execution... which Go does, so not having something similar out of the box seems strange.

That said, even something as simple as errors being literal values that you can pattern match on is surprisingly versatile. Exceptions don't necessarily have to be awful weights that spill down all over the call stack, either. They can be distilled to a more basic catch/throw mechanism for shuffling around error properties and states.


Personally I love option types but working on a large Scala project at the moment it sometimes results in non ideal behaviour from developers especially those coming from the Java world.

What I've seen a lot of is swallowing of errors. For example readFromFile().orElse("") where there is no indication from an outsider that anything wrong happened. A cool feature would be logging any instance of when orElse was the result.

Would be curious how these work in other languages.


The trick is to make it very ergonomic to return the error. I thought Scala had do notation to make this the case. In Rust, the try! macro (which returns on error) is shorter than almost any other way to handle an error, so people usually use that.


I'm confused what makes exception handling so troublesome ?

Personally I find them essential in particular for larger codebases. They allow for centralised, consistent error handling behaviour. For example paging application support teams only for certain types of errors.

And you can always emulate explicit error recovery with exceptions. So what is so bad about having the flexibilty to accomodate different styles of error handling.


Yeah, I don't understand the objection to exceptions either. I _think_ the reason is because people treat them much the same was as errors in C/Go/etc., i.e., "I have to catch them here and recover right away." If you're doing it that way, you're doing it wrong. Design the system such that exceptions can be rethrown (bubbled up) and defer the handling as long as possible.


Nothing. This is one of those "Go doesn't have it, so it must be bad" things. The way Go handles errors is a throwback to C and was abandoned by literally every single imperative language for good reasons.


Honestly I wish that golang forced you to acknowledge all return values from methods even if you simply ignore them ( _ = Method() ). When I read the docs for any method calls in golang I like being able to see that something returns an error just by looking at the function signature. Certainly I might not know how to handle all the error variations, but not having to decide if I need to wrap something in try/catch... I dunno it just resonates with me.


Are people serious when they say they "love the idea" of copying a binary to their server to run a blog service? It sounds very much like installing a shareware in the 90s.

In the current context (NSA, generalized spying, ...), I hope that everybody realizes it's not an ideal way to distribute software.

Please provide cryptographic signatures or at least sha sums, if you really think this is the best way to distribute software.


Yeah I think people are very serious when they say that, though it is important to consider that they are talking about it from an installation convenience factor.

Being able to drop one binary over to a host (as opposed to the usual "DLL hell" of installing binary packages which, despite not being DLLs, is still quite common on Linux and other modern UNIX systems in the form of package versions) is insanely simpler than the usual methods.

Now, whether we trust that binary is a wholly separate matter. Maybe we do, maybe we don't, maybe it is signed by the original source, maybe we got it from a trusted source, maybe we built it ourselves from source after an extensive security audit (hopefully some variant of Ken Thompson's [part of the go team, though I present this as an interesting curiosity and nothing more] compiler hack isn't in play). But the security concerns surrounding the trust in the origin of the software and the ease of installation concerns are separate concerns. You can have one or both or neither.

Installing this blog software directly from a pre-built unsigned binary you ftp'd off some random site without ever looking at the source would be neither, but that doesn't negate the deployment benefits of "one single binary" which can be provided with both (at least to the degree that you can trust any 3rd party software).


I agree that one shouldn't copy just any binary on their server and run it. I'd like to help with this. Are you just thinking about providing SHA sums next to the binaries/zip releases on the download page? What kind of cryptographic signatures do you have in mind?

Regarding Journey:

You can always compile from source. Go makes that easy, dependencies on GitHub will be downloaded automatically.

If you trust my builds, the releases page on GitHub is served via HTTPS, so no one should be tinkering with the binary on the way from the server to you.


In your case, PGP would be the best and not so hard to implement.

If you don't want/know how to use PGP you can also publish the SHA1 sums of the files available on your download page. It's better than nothing.

The second alternative is weaker because an attacker would simply need to change the binary and the sum on the website. In the PGP case, the attacker must get access to your PGP private key, and provided that you use PGP reasonably (no private key on your web server), this is harder.


You could PGP sign the binaries.


I've never quite understood the practice of putting SHA hashes next to a download. If an attacker has access to the download surely they also can manipulate the hashes as well.


You could always `go get github.com/kabukky/journey` if you'd prefer to build from source.


I don't see how thats any different from copying a .deb/.rpm/add-apt onto your server.

Having source code readily available didn't stop heartbleed.


If you're downloading random unsigned debs/rpms from the internet and installing them, then you're just as wrong as downloading/execing a binary.

If you're downloading rpms/debs, verifying the signatures, and you trust the signer, then you're probably fine. Those signature signing/checking mechanisms are already built into rpm/deb, which is why they're being argued for.


I don't see how a binary that you can't inspect is meaningfully less secure than source code that you don't inspect. I very much doubt most people vet, for example, the source code to Nginx before compiling and running it. If you're one of the few who does that, you still have that option here. I don't think this distribution model is giving up much.


The point is that someone can inspect that code, not that everyone needs to inspect every piece of source code they use. If the source isn't available, no one can inspect it. That is qualitative different than providing the source code.


Huh? The link to the source is literally right under the link to the binary.


But you don't know that the binary is compiled from _that_ source code.


Compile it yourself, problem solved.


It's slightly better than the "wget this script and pipe it to sudo" fad.


We run our blog on Ghost (http://blog.commando.io), and honestly not sure there is much wrong with the node.js implementation. It can handle insane traffic, gzip, expires headers already supported. You are correct, it does not support https, but you could either run nginx in front, or modify the source code a bit.

I do love the idea of just copying a single binary (thanks go) to any server and running Journey. Is Journey's editing/writing interface up to snuff with ghost? Support for tags, SEO metadata, full markdown support, image upload?


There is definitely nothing wrong with Ghost and node.js. I've used it for some time now and I still love it :)

Two major points drove me while developing Journey: I wanted to learn (more) about Go (the bigger point here). And I wanted something that I could just drag and drop on any server to quickly create a temporary blog or micro site without setting up dependencies like nginx or node.


Absolutely. I applaud the effort and enthusiasm to learn go. Go is awesome. My concerns are just feature parity for users wanting a blogging platform to run in production. You'll need rss feed, sitemaps, tags, SEO metadata, etc.


bower (or was it NPM?) required over 256MB of RAM to compile dependencies on ubuntu. So I was not able to run it on a small VPS I had.

At any rate, once everything is setup Node only takes about +3x the memory to run so it's not near as bad as Ruby/PHP.

Like C++, you could run go on a router it's so small and fast.


> Is Journey's editing/writing interface up to snuff with ghost? Support for tags, SEO metadata, full markdown support, image upload?

Yes for everything except for SEO metadata. That's planned tough.


About theme compatibility between CMSes of all kinds, I wrote a post at http://fiatjaf.alhur.es/programming/reusable-pure-css-themes...

And I have a initial implementation of the thing I envisioned at http://fiatjaf.alhur.es/classless/showcase/


Hi, this is really cool.. I'll admit to having never heard of or used Ghost, but I applaud the effort of building this to learn a new language. There is also a hidden value many people underestimate in having a project that is quite similar to others but in a different language.

I'm really curious about your Lua integration. I had also not previously heard of gopher-lua. How has your experience with this been?


Using GopherLua is a pleasant experience. Easily integrated and surprisingly fast. But the documentation is lacking.

If you use it in a web server that executes Lua with requests, take a look at the LState pool pattern[1].

[1] https://github.com/yuin/gopher-lua#the-lstate-pool-pattern


Why would you not name it Goul?


Oh my god. I'm deeply ashamed now. /thread


This


Excellent read, going through the source code this afternoon. How did you work the theme compatibility with Ghost: api?

Worth pursuing?

Yes, can you make money on it? maybe depending on demand. There's always a market for well designed tools, especially if you could make it, 'the key tool' to create and prototype themes.


I pretty much just reverse-engineered the Ghost helpers using the theme documentation. Take a look at https://github.com/kabukky/journey/blob/master/templates/hel... for all helpers that are implemented yet.

I'm not looking to make money off it. It is tempting to expand the functionality into a full CMS, but that should be a task for a fork.


I am really hoping for a good CMS in Go, I am currently using Drupal but the overhead sometimes is really huge.


Source looks well factored to plug in alternate markups. Markdown is great, but sometimes too limiting.


Does it need a database? I see no such information. But, the Github project says

> high priority goals are support of MySQL, PostgreSQL, and Google App Engine.


It uses SQLite internally. One Database file at journey/content/data/journey.db

I'm working on being able to just drag and drop a Ghost db in there and loading that one. Only the timestamps need to be converted for that to work.


Cool. Even cooler: it would create static HTML files. I've been searching for a software like this for long. Jekyll only works on computers I've installed it on. I want an admin interface, but no db and static output. Only thing I've found is http://prose.io


Looks cool. Tried on Alpine Linux and no worky:

jb:~/web/journey_blog$ sh journey

journey: line 1: ELF4LS�4: not found

journey: line 10: syntax error: unexpected word

and using bash:

jb:~/web/journey_blog$ bash journey

journey: journey: cannot execute binary file

jb:~/web/journey_blog$ ls -ahls journey

11716 -rwxr-xr-x 1 jbrown jbrown 11.4M Apr 28 21:25 journey


Thanks for trying it! I would guess this is because I build the linux releases with gnu libc. See here: http://www.blang.io/2015/04/19/golang-alpine-build-golang-bi...

You can also try compiling from source (see the Journey Wiki on GitHub for instructions).


Something I through I would do to learn Go. Thanks it will be a little easy learning :)


this is very well made. i was hoping to make something like it - but way (way!) more minimal. here is the first draft - http://adombic.appspot.com


Dang, this was next on my side projects ideas. Looks good though.

You looking for contributes?


Pull requests are very much welcome. :) Here's my high priority feature list right now:

Gzip support, hashes for serving static assets (and/or expires headers), multi-user support, support for all of the Ghost theme helpers, MySQL, Postgres, and Google App Engine support.


A template for getting on the front page:

"X that no one uses, now written in Go"


This looks like a nice side project. Complaining that HN seems to upvote go projects isn't really fair.

Also ghost lists a number of users including NASA, mailgun, and coinbase. https://ghost.org


The name of this is site is "Hacker News". That would necessarily imply a site that encourages people to post things that are maybe off the beaten path.


Rewriting something in some hipster language du jour is not a newsworthy event. Writing yet another blog platform (there must be thousands by now), let alone a "minimal" one, is not a newsworthy event. This froth just pollutes the stream. I've been reading this site for years, but "I wrote X in obscure language Y" garbage finally made me register. It's not the tools you have, it's what you do with the tools you have.


I don't think the claim with such posts is that they're newsworthy, just that they're fun or interesting (for some).

It's better for the emotional aspect of technical work to be out in the open, instead of rationalizing itself surreptitiously, as it usually has to—leading to such unproductive phenomena as (a) the sober engineering analysis that just happens to conclude that the best choice for X is programmer's favorite; and (b) the intellectual depression that comes from prolonged working with tools that give one no pleasure. "There is no play in them," as Thoreau said.

The need for play is deep and should be accommodated, since it affects things whether we want it to or not. I think if one revises one's model of software economics to include this factor then the yet-another projects you're criticizing come out looking pretty different. Also the HN connection is clear—this is a site where gratifying curiosity and creative impulses is celebrated. No justification required.

But I'm glad you've registered anyhow. Hopefully you'll discuss some of the things that gratify you!


"I wrote something in Go" is not off the beaten path for this site.

Before that "I wrote a thing in Haskell", before that "I wrote a thing in Common Lisp".


So your complaint boils down to HN is so far 'off to the side of mainstream, that X,Y,Z is common here so we ought to post A,B,C because thats even more uncommon'.


I think his complain is that hacker news gets a bit cargo culty with trendy languages.

It doesn't really matter how novel, interesting, or well-constructed the project is, if it's implemented in whatever language is popular at the time, it has a good chance of making it to the front page.


This is how sites with an upvote button work.


I'd say more: "X that noone uses, now written in language Y that noone uses".


Upvoted for inaccuracy!




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: