Build WriteFreely from source
WriteFreely is open source, lightweight blogging platform written in Go programming language.
Motivation to build WriteFreely
While absolute king of blogs is WordPress, which in fact powers somewhat 30% of all websites, some say it is overloaded, choosing perfect theme is extremely difficult and some (especially free) plugins are poisoned with different kinds of tracking. I do use WordPress on my main site as well, but over the years I' ve figured it seriously takes time to post an article (and I'm not yet even aquainted with Gutenberg).
WriteFreely here is a nice alternative claiming to offer a distraction-free writing experience. While supporting HTML, software mainly relies and endorses markdown. I do not plan ditching WordPress anytime soon, but I was very curious to try out lightweight platform that natively federates.
If you already have WriteFreely installed, see build and upgrade from source guide.
Prerequisites
This guide details how to build decent version of WriteFreely from source on CentOS 7 machine.
Instructions should work for other Linux distributions as well but bear in mind that some paths and details (yum vs. apt, etc.) might differ across systems.
Git version 2
It turns out having Git version 2 is required to build WriteFreely successfully. I was able to pull updates using version 1.8, but doing fresh sync didn't work afterall. So make sure you install Git version 2 on CentOS 7 machine.
First remove existing version:
yum remove git*
Note: this will remove Go as well because it is dependent on Git (therefore I am detailing Git first).
Now set up IUS repository (created by folks at Rackpace) based on this instruction:
yum install https://repo.ius.io/ius-release-el7.rpm https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
Notice it also adds EPEL repository.
Previously it was git2u-all
package, but as of November 11th, 2020 it was not found:
No package git2u-all available.
Version is actually now added into package so use following:
yum install git224-all
Also make sure to install Git support for large files:
yum install git-lfs
Check version:
git --version
And if something like git version 2.24.3
is shown, you are good to go.
Node.js
Make sure EPEL repository is installed:
yum install epel-release
No need to repeat – EPEL was already installed with IUS setup command.
Install Node.js and package manager npm:
yum install nodejs npm
Note: as of WriteFreely v0.13.0 Prose editor is included – it requires recent NodeJS version, therefore on CentOS 7 following steps must be performed to avoid below error showing up when trying to make ui
later:
npm ERR! Linux 3.10.0-1160.25.1.el7.x86_64
npm ERR! argv "/usr/bin/node" "/bin/npm" "run-script" "build"
npm ERR! node v6.17.1
npm ERR! npm v3.10.10
npm ERR! code ELIFECYCLE
npm ERR! prose@1.0.0 build: `webpack --mode production`
npm ERR! Exit status 2
npm ERR!
npm ERR! Failed at the prose@1.0.0 build script 'webpack --mode production'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the prose package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! webpack --mode production
npm ERR! You can get information on how to open an issue for this project with:
npm ERR! npm bugs prose
npm ERR! Or if that isn't available, you can get their info via:
npm ERR! npm owner ls prose
npm ERR! There is likely additional logging output above.
First, check current versions on system:
# node --version
v6.17.1
# npm --version
3.10.10
# which node
/usr/bin/node
# which npm
/usr/bin/npm
Too old, need to upgrade using official repository:
# Node.js v16.x
# As root
curl -fsSL https://rpm.nodesource.com/setup_16.x | bash -
# Or as no root privileges
curl -fsSL https://rpm.nodesource.com/setup_16.x | sudo bash -
Remove old, install new:
yum remove nodejs npm
yum install nodejs
And check versions:
# node --version
v16.3.0
# npm --version
7.15.1
Good to go now – make ui
should not fail afterwards.
Go
Version 1.10+ is required. Download from Go website or (preferably) install using package manager:
yum install golang go-bindata
Because you have already added epel repository, version should be decent (at the time of writing 1.13.6):
go version go1.13.6 linux/amd64
Less.js
Less.js (source) is needed to convert Less stylesheets to CSS.
npm install -g less
/usr/bin/lessc -> /usr/lib/node_modules/less/bin/lessc
- ycssmin@1.0.1 node_modules/less/node_modules/ycssmin
/usr/lib
└── less@3.9.0
npm install -g less-plugin-clean-css
/usr/lib
└── less-plugin-clean-css@1.5.1
From above output make sure to remember /usr/bin/lessc
– we will need it later.
MySQL or MariaDB
WriteFreely requires MySQL (MariaDB) 5.6+. This can be an issue with CentOS / RHEL as at the time of writing it was shipping version 5.5 which fails database initialization script on tables accesstokens, appcontent, posts and users. See my comment on issue #22.
Possible solution would be adding official MariaDB repository and installing decent version (v. 10 at the time of writing).
Create system user to run WriteFreely
For security reasons it is well worth creating unprivileged system user to run Write freely. Create new user executing:
adduser writefreely
Up to this point we've been using root or sudo user to install prerequisites. Below commands should be run under unprivileged user. Switch to it by running:
su - writefreely
Make sure user has following paths in ~/.bash_profile
:
export GOBIN="$HOME/go/bin"
export GOPATH="$HOME/go"
If not, add them and propagate those changes:
source /etc/profile && source ~/.bash_profile
Ready to start building
ArchLinux has WriteFreely packaged – page provides good summary of dependencies. Anyhow at this point you should be good to go with prerequisites to start building.
Download WriteFreely sources
Pull sources from GitHub:
go get -d -u -v github.com/writeas/writefreely
Parameters explained below:
-d - do not build after downloading
-u - update
-v - verbose for details
Sources of WriteFreely and included libraries should now be available at ~/go/src/github.com/writeas/writefreely
– cd
into this folder.
If you would like to use my fork, initialize sources with following command:
go get -d -u -v code.gyt.is/writefreely
In this case afterwards cd
into folder ~/go/src/code.gyt.is/writefreely
.
Starting version 0.9 it is needed to explicitly tell to build with modules:
# If running Go 1.11+
export GO111MODULE=on
Otherwise such error would come up with next step:
../../sitemap.go:23:22: not enough arguments in call to stm.NewSitemap
have ()
want (int)
../../sitemap.go:78:4: index must be non-negative integer constant
../../sitemap.go:78:4: cannot use p.Post.Slug.NullString.String (type string) as type []interface {} in array or slice literal
../../sitemap.go:79:4: index must be non-negative integer constant
../../sitemap.go:79:18: cannot use "weekly" (type string) as type []interface {} in array or slice literal
../../sitemap.go:80:4: index must be non-negative integer constant
../../sitemap.go:80:4: cannot use true (type bool) as type []interface {} in array or slice literal
../../sitemap.go:81:4: index must be non-negative integer constant
../../sitemap.go:81:4: cannot use p.Post.Updated (type time.Time) as type []interface {} in array or slice literal
../../sitemap.go:86:33: index must be non-negative integer constant
../../sitemap.go:86:33: too many errors
make: *** [build] Error 2
Go ahead and make:
make build # Compile the application
At this point writefreely
executable should be created at ~/go/src/github.com/writeas/writefreely/cmd/writefreely
. Don't cd
into that directory directly but run config.ini
creation wizard:
./cmd/writefreely/writefreely --config # Create configuration file
Above step is not needed if you are updating from previous version – in such case keep config.ini file from ~/go/src/github.com/writeas/writefreely
!
If doing fresh install, initialize database:
# (if you chose Multi-user setup) Import the schema with:
./cmd/writefreely/writefreely --init-db
Or upgrade if was running WriteFreely before:
./cmd/writefreely/writefreely --migrate
If you are upgrading from previous version, follow much shorter guide how to upgrade WriteFreely from source.
Proceed with installation:
make install # Generates encryption keys; installs LESS compiler
At this point we need to generate CSS files. Problem is that LESS compiler Makefile needs little commenting – edit file less/Makefile
leaving lessc
path:
# ifeq ($(shell which lessc),/usr/bin/lessc)
LESSC=/usr/bin/lessc
# else
# LESSC=node_modules/.bin/lessc
#endif
I didn't have time to fix script yet, but on my system it is good to go:make ui
was referring to node_modules/.bin/lessc
instead of /usr/bin/lessc
therefore error was thrown. So with modification
make ui # Generates CSS (run this whenever you update your styles)
Update on lessc issue: I have filled a pull request to include /bin/lessc
to less/Makefile
. It was merged and above manual fix is not needed.
And to check if application runs:
make run # Runs the application
If application is running, quit it by hitting CTRL+C and set up system service. Before that just make sure writefreely
binary appears at ~/go/bin
folder. Command make run
should have placed it there already but if not, copy binary from cmd/writefreely
.
Install system service
Define system service in file /etc/systemd/system/writefreely.service
:
[Unit]
Description=Write Freely Instance
After=syslog.target network.target mariadb.service
[Service]
Type=simple
StandardOutput=syslog
StandardError=syslog
WorkingDirectory=/home/writefreely/go/src/github.com/writeas/writefreely
ExecStart=/home/writefreely/go/bin/writefreely
Restart=always
; run as writefreely user
User=writefreely
Group=writefreely
[Install]
WantedBy=multi-user.target
Mind WorkingDirectory
– in this folder WriteFreely will expect config.ini
file as well as folders containing templates and static files.
Pay attention – service should run under unprivileged user!
Refresh services configuration:
systemctl daemon-reload
Now service can be started:
systemctl start writefreely
And enable service to start on next system restart:
systemctl enable writefreely
Setup reverse proxy (Nginx)
Nginx texmplate from WriteFreely guide should be good to go:
server {
listen 80;
listen [::]:80;
server_name example.com;
gzip on;
gzip_types
application/javascript
application/x-javascript
application/json
application/rss+xml
application/xml
image/svg+xml
image/x-icon
application/vnd.ms-fontobject
application/font-sfnt
text/css
text/plain;
gzip_min_length 256;
gzip_comp_level 5;
gzip_http_version 1.1;
gzip_vary on;
location ~ ^/.well-known/(webfinger|nodeinfo|host-meta) {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://127.0.0.1:8080;
proxy_redirect off;
}
location ~ ^/(css|img|js|fonts)/ {
root /home/writefreely/go/src/github.com/writeas/writefreely/static;
# Optionally cache these files in the browser:
# expires 12M;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://127.0.0.1:8080;
proxy_redirect off;
}
}
This is default for unsecure connection on port 80. I recomment setting up WriteFreely on secure server
block and redirect port 80 connections (both with and without www.
) as well as secure connection with www.
to straightforward instance domain – in my case it is fedi.dev.
SELinux
If SELinux is enabled in your CentOS / RHEL installation (default), below error will pop up in Nginx error log:
2019/03/02 01:19:46 [crit] 4508#0: *1 connect() to 127.0.0.1:8080 failed (13: Permission denied) while connecting to upstream, client: xxx.xxx.xxx.xxx, server: fedi.dev, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:8080/",host: "fedi.dev"
In /var/log/audit/audit.log
this message will follow:
type=AVC msg=audit(1551482556.752:6031): avc: denied { name_connect } for pid=4576 comm="nginx" dest=8080 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket permissive=0
Create an exception by running:
setsebool httpd_can_network_connect on -P
As of April, 2020 I discovered that fresh CentOS 7 installation would cause more issues when trying to setup WriteFreely. In default nginx log /var/log/nginx/error.log
(or another depending on your server
config) following errors started popping up:
2020/04/02 15:02:19 [error] 9409#0: *12 open() "/home/writefreely/go/src/code.gyt.is/writefreely/static/css/write.css" failed (13: Permission denied), client: 10.0.2.2, server: _, request: "GET /css/write.css HTTP/1.1", host: "127.0.0.1:9980"
As Linux file permissions were set properly, this suggested SELinux is still kicking in. After a lot of messing around I figured it is easier to generate rules based on errors and warning generated.
First install some utilities:
yum install -y policycoreutils-python
On fresh CentOS 7 machine I could see following in the logs:
# cat /var/log/audit/audit.log | grep nginx | grep denied
type=AVC msg=audit(1585828926.295:187): avc: denied { read } for pid=9411 comm="nginx" name="write.css" dev="dm-2" ino=2150102457 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1585828926.296:188): avc: denied { read } for pid=9411 comm="nginx" name="h.js" dev="dm-2" ino=3221225570 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1585828926.418:189): avc: denied { read } for pid=9411 comm="nginx" name="h.js" dev="dm-2" ino=3221225570 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1585828926.424:190): avc: denied { read } for pid=9411 comm="nginx" name="webfont.js" dev="dm-2" ino=3221225576 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1585828932.741:191): avc: denied { read } for pid=9409 comm="nginx" name="write.css" dev="dm-2" ino=2150102457 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1585828933.608:192): avc: denied { read } for pid=9409 comm="nginx" name="write.css" dev="dm-2" ino=2150102457 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1585828934.757:193): avc: denied { read } for pid=9409 comm="nginx" name="write.css" dev="dm-2" ino=2150102457 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
type=AVC msg=audit(1585828939.390:194): avc: denied { read } for pid=9409 comm="nginx" name="write.css" dev="dm-2" ino=2150102457 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0
Now as those errors are present, run several times following commands:
cat /var/log/audit/audit.log | grep nginx | grep denied | audit2allow -M nginx
semodule -i nginx.pp
This website had good suggestion to run above commands in case 403 forbiddden error
still comes up several times to make sure all forbids are captured by audit2allow
.
There is a guide how to create SELinux rules from audit log events I've put together, check it out.
That's it, you have successfully built and launched WriteFreely on CentOS :)
Make sure to create admin user:
~/go/bin/writefreely --create-admin username:password
And start writing!
My fork
I do have a fork of WriteFreely on GitHub for pull request purposes. While there is no good reason you might clone from my repository instead of official one, for the reference here it is:
go get -d -u -v code.gyt.is/writefreely
Notice I use code.gyt.is as placeholder. It is nothing but static website with few meta tags to tell go get
where to pull code from. In essence it actually points to my repository on GitHub, but just in case I want to switch to (most likely) on-premises Gitea, I won't need to update this documentation. Sources are pulled to following folder then:
~/go/src/code.gyt.is/writefreely
In case certain branch is preferred, just checkout it, e.g.:
git checkout oauth-gitea
And start building from here.
Update: my pull request to add Gitea as OAuth client in WriteFreely was merged into develop
!
Further reading
- Getting started
- Production guide
- Development setup – will pull-request my guide there
Feedback
Comments or any feedback? Reply me @gytis@mastodon.lt on fediverse or e-mail g@gyt.is.
Thanks
- Folks at @writeas_dev@abunchtell.com for making and open sourcing WriteFreely
- @matt@writing.exchange for quick replies
Updates
- 2020-11-11: Updated Git version 2 installation details – added IUS repository installation command and changed
git
package name; updated details regarding IUS and EPEL repositories; mentioned my Pull Request for Gitea OAuth. - 2021-06-15: Updated Node.js section as starting from v0.13.0 Prose editor is included which requires latest Node version.
Tags: #server #go #golang #less #build #writefreely #nginx #centos #rhel #selinux
My name is Gytis Repečka, I am Solution Architect and data professional. I enjoy using, promoting and contributing to open source software and love communicating about tech to both advanced and non-tech people. Visit Inretio for consulting services. Comment by mentioning me @gytisrepecka@social.gyt.is on Fediverse.