diff --git a/.gitignore b/.gitignore
index 61ead866..90ab5fde 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
/vendor
+/build
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d06e4700..6bdbd118 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -2,51 +2,61 @@
This repository contains the server side and the client side code for Dnote.
-## Set up
+* [Setting up](#setting-up)
+* [Command Linux Interface](#command-line-interface)
+* [Server](#server)
-1. Download and setup the [Go programming language](https://golang.org/dl/).
-2. Download the project
+## Setting up
+
+1. Install the following prerequisites if necessary:
+
+* [Go programming language](https://golang.org/dl/) 1.12+
+* [Node.js](https://nodejs.org/) 10.16+
+* Postgres 10.9+
+
+2. Get the Dnote code:
```sh
go get github.com/dnote/dnote
```
-## CLI
+3. Run `make` to install dependencies
-### Set up
+## Command Line Interface
-Download dependencies using [dep](https://github.com/golang/dep).
+### Build
-```sh
-dep ensure
+You can build either a development version or a production version:
+
+```
+# Build a development version for your platform and place it in your `PATH`.
+make debug=true build-cli
+
+# Build a production version
+make version=v0.1.0 build-cli
```
### Test
-Run:
+* Run all tests for the command line interface:
-```sh
-./cli/scripts/test.sh
+```
+make test-cli
```
### Debug
-Run Dnote with `DNOTE_DEBUG=1` to print debugging statements.
+Run Dnote with `DNOTE_DEBUG=1` to print debugging statements. For instance:
+
+```
+DNOTE_DEBUG=1 dnote sync
+```
### Release
-* Build for all target platforms, tag, push tags
-* Release on GitHub and [Dnote Homebrew tap](https://github.com/dnote/homebrew-dnote).
-
-```sh
-VERSION=0.4.8 make release
-```
-
-* Build, without releasing, for all target platforms
-
-```sh
-VERSION=0.4.8 make
-```
+* Run `make version=v0.1.0 release-cli` to achieve the following:
+ * Build for all target platforms, create a git tag, push all tags to the repository
+ * Create a release on GitHub and [Dnote Homebrew tap](https://github.com/dnote/homebrew-dnote).
**Note**
@@ -54,39 +64,24 @@ VERSION=0.4.8 make
- disable the homebrew release by commenting out relevant code in the release script.
- mark release as pre-release on GitHub release
-## Web
-
-### Set up
-
-Download dependencies using [dep](https://github.com/golang/dep) and npm.
-
-```sh
-dep ensure
-npm install
-```
-
-### Test
-
-Run:
-
-```
-npm run test
-```
-
## Server
-### Set up
+The server consists of the frontend web application and a web server.
-Download dependencies using [dep](https://github.com/golang/dep).
+### Development
-```sh
-dep ensure
-```
+* Create a postgres database by running `createdb -O postgres dnote`
+* If the role does not exist, you can create it by running `sudo -u postgres createuser postgres`
+
+* Run `make dev-server` to start a local server
### Test
-Run:
+```bash
+# Run tests for the frontend web application
+make test-web
+# Run tests for API
+make test-api
```
-./server/api/scripts/test-local.sh
-```
+
diff --git a/Gopkg.lock b/Gopkg.lock
index b50d93d1..bc8e5a53 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -45,6 +45,51 @@
revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4"
version = "v1.7.0"
+[[projects]]
+ digest = "1:d29ee5ef14a7e0253facd0bcebe6a69a7a4e02a67eb24d2aacd8ccb4a7cea6fc"
+ name = "github.com/gobuffalo/envy"
+ packages = ["."]
+ pruneopts = "UT"
+ revision = "043cb4b8af871b49563291e32c66bb84378a60ac"
+ version = "v1.7.0"
+
+[[projects]]
+ digest = "1:6c9088827dcc7a807c97592a55f190e35e0fdaf237c5a074256c260925c59428"
+ name = "github.com/gobuffalo/logger"
+ packages = ["."]
+ pruneopts = "UT"
+ revision = "7c291b53e05b81d77bd43109b4a3c6f84e45c8e1"
+ version = "v1.0.1"
+
+[[projects]]
+ digest = "1:40849e8495ef81a84ff335ef65e23d33671b61e60e9db464fbab55f19f43f120"
+ name = "github.com/gobuffalo/packd"
+ packages = [
+ ".",
+ "internal/takeon/github.com/markbates/errx",
+ ]
+ pruneopts = "UT"
+ revision = "54ea459691466cfb630ccc276723fe3963f3e9d5"
+ version = "v0.3.0"
+
+[[projects]]
+ digest = "1:d5be00af71142774ee6738480e580ae975e8e97158f9334b13af41a77f2f6b0c"
+ name = "github.com/gobuffalo/packr"
+ packages = [
+ "v2",
+ "v2/file",
+ "v2/file/resolver",
+ "v2/file/resolver/encoding/hex",
+ "v2/internal/takeon/github.com/markbates/errx",
+ "v2/internal/takeon/github.com/markbates/oncer",
+ "v2/internal/takeon/github.com/markbates/safe",
+ "v2/jam/parser",
+ "v2/plog",
+ ]
+ pruneopts = "UT"
+ revision = "9eb7a3d310e89e471c2cdf1ea3ec8d7fc1ab969c"
+ version = "v2.5.2"
+
[[projects]]
digest = "1:318f1c959a8a740366fce4b1e1eb2fd914036b4af58fbd0a003349b305f118ad"
name = "github.com/golang/protobuf"
@@ -141,6 +186,22 @@
revision = "23d116af351c84513e1946b527c88823e476be13"
version = "v1.3.0"
+[[projects]]
+ digest = "1:77857b3205f936bdc6928ef347b682ab549cf99454d6c0ca04a49f8df9e418f3"
+ name = "github.com/karrick/godirwalk"
+ packages = ["."]
+ pruneopts = "UT"
+ revision = "73c17a9b9528eb3ce857b782a2816c0cda581e62"
+ version = "v1.10.12"
+
+[[projects]]
+ digest = "1:31e761d97c76151dde79e9d28964a812c46efc5baee4085b86f68f0c654450de"
+ name = "github.com/konsorten/go-windows-terminal-sequences"
+ packages = ["."]
+ pruneopts = "UT"
+ revision = "f55edac94c9bbba5d6182a4be46d86a2c9b5b50e"
+ version = "v1.0.2"
+
[[projects]]
digest = "1:bdd53b87de8185da386bae179c84d4848854c6870bacacf6a154fe63e2e750f7"
name = "github.com/lib/pq"
@@ -154,13 +215,11 @@
version = "v1.1.1"
[[projects]]
- digest = "1:639aaff480d288c3dbc99ae68f00d58ae43c283a85725ae3afaada7b5810c6be"
+ digest = "1:1241b137a50b99f7f395e6d3d917cafa4330bd17c55dacef18bfa8d87707533a"
name = "github.com/markbates/goth"
packages = [
".",
"gothic",
- "providers/github",
- "providers/gplus",
]
pruneopts = "UT"
revision = "3b8012093d951beedd026d120be1792db01a08f6"
@@ -207,7 +266,18 @@
version = "v1.2.0"
[[projects]]
- branch = "master"
+ digest = "1:e09ada96a5a41deda4748b1659cc8953961799e798aea557257b56baee4ecaf3"
+ name = "github.com/rogpeppe/go-internal"
+ packages = [
+ "modfile",
+ "module",
+ "semver",
+ ]
+ pruneopts = "UT"
+ revision = "438578804ca6f31be148c27683afc419ce47c06e"
+ version = "v1.3.0"
+
+[[projects]]
digest = "1:83eb06141fc25c7fd89f8b39717962ac930a7480796aab3d46c7e88507a69173"
name = "github.com/rubenv/sql-migrate"
packages = [
@@ -233,6 +303,14 @@
revision = "1744e2970ca51c86172c8190fadad617561ed6e7"
version = "v1.0.0"
+[[projects]]
+ digest = "1:04457f9f6f3ffc5fea48e71d62f2ca256637dee0a04d710288e27e05c8b41976"
+ name = "github.com/sirupsen/logrus"
+ packages = ["."]
+ pruneopts = "UT"
+ revision = "839c75faf7f98a33d445d181f3018b5c3409a45e"
+ version = "v1.4.2"
+
[[projects]]
digest = "1:e096613fb7cf34743d49af87d197663cfccd61876e2219853005a57baedfa562"
name = "github.com/spf13/cobra"
@@ -385,17 +463,14 @@
"github.com/aymerick/douceur/inliner",
"github.com/dnote/actions",
"github.com/dnote/color",
+ "github.com/gobuffalo/packr/v2",
"github.com/google/go-github/github",
"github.com/gorilla/mux",
- "github.com/gorilla/securecookie",
- "github.com/gorilla/sessions",
"github.com/jinzhu/gorm",
"github.com/joho/godotenv",
"github.com/lib/pq",
"github.com/markbates/goth",
"github.com/markbates/goth/gothic",
- "github.com/markbates/goth/providers/github",
- "github.com/markbates/goth/providers/gplus",
"github.com/mattn/go-sqlite3",
"github.com/pkg/errors",
"github.com/robfig/cron",
diff --git a/Gopkg.toml b/Gopkg.toml
index 11322922..a44f73cc 100644
--- a/Gopkg.toml
+++ b/Gopkg.toml
@@ -45,14 +45,6 @@
name = "github.com/gorilla/mux"
version = "1.7.2"
-[[constraint]]
- name = "github.com/gorilla/securecookie"
- version = "1.1.1"
-
-[[constraint]]
- name = "github.com/gorilla/sessions"
- version = "1.1.3"
-
[[constraint]]
name = "github.com/jinzhu/gorm"
version = "1.9.9"
@@ -120,3 +112,7 @@
[[constraint]]
revision = "f4d34eae5a5cf210693e81c604e6bac5f6727927"
name = "github.com/rubenv/sql-migrate"
+
+[[constraint]]
+ name = "github.com/gobuffalo/packr"
+ version = "2.5.2"
diff --git a/Makefile b/Makefile
index cb6e2fef..4e09fd5b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,7 @@
DEP := $(shell command -v dep 2> /dev/null)
+PACKR2 := $(shell command -v packr2 2> /dev/null)
NPM := $(shell command -v npm 2> /dev/null)
+HUB := $(shell command -v hub 2> /dev/null)
## installation
install: install-go install-js
@@ -11,6 +13,11 @@ ifndef DEP
@curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
endif
+ifndef PACKR2
+ @echo "==> installing packr2"
+ @go get -u github.com/gobuffalo/packr/v2/packr2
+endif
+
@echo "==> installing go dependencies"
@dep ensure
.PHONY: install-go
@@ -30,6 +37,9 @@ endif
.PHONY: install-js
## test
+test: test-cli test-api test-web
+.PHONY: test
+
test-cli:
@echo "==> running CLI test"
@${GOPATH}/src/github.com/dnote/dnote/pkg/cli/scripts/test.sh
@@ -45,26 +55,81 @@ test-web:
@(cd ${GOPATH}/src/github.com/dnote/dnote/web && npm run test)
.PHONY: test-web
-test: test-cli test-api test-web
-.PHONY: test
+# development
+dev-server:
+ @echo "==> running dev environment"
+ @(cd ${GOPATH}/src/github.com/dnote/dnote/web && ./scripts/dev.sh)
+.PHONY: dev-server
## build
build-web:
@echo "==> building web"
- @${GOPATH}/src/github.com/dnote/dnote/web/scripts/build-prod.sh
+ @(cd ${GOPATH}/src/github.com/dnote/dnote/web && ./scripts/build-prod.sh)
.PHONY: build-web
-build-dev-cli:
- @echo "==> building dev cli"
- @${GOPATH}/src/github.com/dnote/dnote/pkg/cli/scripts/dev.sh
-.PHONY: build-dev-cli
-
-## migrate
-migrate:
-ifndef GO_ENV
- $(error "environment variable GO_ENV is required.")
+build-server: build-web
+ifndef version
+ $(error version is required. Usage: make version=v0.1.0 build-server)
endif
- @echo "==> running migrations"
- @(cd ${GOPATH}/src/github.com/dnote/dnote/pkg/server/database/migrate && go run *.go --migrationDir ../migrations)
-.PHONY: migrate
+ @echo "==> building server"
+ @(cd ${GOPATH}/src/github.com/dnote/dnote/pkg/server && ./scripts/build.sh $(version))
+.PHONY: build-server
+
+build-cli:
+ifeq ($(debug), true)
+ @echo "==> building cli in dev mode"
+ @${GOPATH}/src/github.com/dnote/dnote/pkg/cli/scripts/dev.sh
+else
+
+ifndef version
+ $(error version is required. Usage: make version=v0.1.0 build-cli)
+endif
+
+ @echo "==> building cli"
+ @${GOPATH}/src/github.com/dnote/dnote/pkg/cli/scripts/build.sh $(version)
+endif
+.PHONY: build-cli
+
+## release
+release-cli: build-cli
+ifndef version
+ $(error version is required. Usage: make version=v0.1.0 release-cli)
+endif
+ifndef HUB
+ $(error please install hub)
+endif
+
+ @homebrewRepoDir=${GOPATH}/src/github.com/dnote/homebrew-dnote
+ if [ ! -d ${homebrewRepoDir} ]; then
+ @echo "homebrew-dnote not found locally. did you clone it?"
+ @exit 1
+ fi
+
+ @echo "==> releasing cli"
+ @outputDir=${GOPATH}/src/github.com/dnote/dnote/build/cli
+ @${GOPATH}/src/github.com/dnote/dnote/pkg/cli/scripts/release.sh cli $(version) ${outputDir}
+
+ @echo "===> releading on Homebrew"
+ @homebrew_sha256=$(shasum -a 256 "${outputDir}/dnote_$(version)_darwin_amd64.tar.gz" | cut -d ' ' -f 1)
+ @(cd "${homebrewRepoDir}" && ./release.sh "$(version)" "${homebrew_sha256}")
+.PHONY: release-cli
+
+release-server: build-server
+ifndef version
+ $(error version is required. Usage: make version=v0.1.0 release-server)
+endif
+ifndef HUB
+ $(error please install hub)
+endif
+
+ @echo "==> releasing server"
+ @outputDir=${GOPATH}/src/github.com/dnote/dnote/build/server
+ @${GOPATH}/src/github.com/dnote/dnote/pkg/server/scripts/release.sh server $(version) ${outputDir}
+.PHONY: release-server
+
+clean:
+ @git clean -f
+ @rm -rf build
+ @rm -rf web/public
+.PHONY: clean
diff --git a/SELF_HOSTING.md b/SELF_HOSTING.md
new file mode 100644
index 00000000..ac77f343
--- /dev/null
+++ b/SELF_HOSTING.md
@@ -0,0 +1,101 @@
+# Hosting Dnote On Your Machine
+
+This guide documents the steps for installing the Dnote server on your own machine.
+
+**Please note that self hosted version of Dnote server is currently in beta.**
+
+## Installation
+
+1. Install Postgres 10+.
+2. Create a `dnote` database by running `createdb dnote`
+2. Download the official Dnote server release.
+3. Extract the archive and move the `dnote-server` executable to `/usr/local/bin`.
+
+```bash
+tar -xzf dnote-server-$version-$os.tar.gz
+chmod +x ./dnote-server/dnote-server
+mv ./dnote-server/dnote-server /usr/local/bin
+```
+
+4. Run Dnote
+
+```bash
+GO_ENV=PRODUCTION \
+DBHost=localhost \
+DBPort=5432 \
+DBName=dnote \
+DBUser=$user \
+DBPassword=$password \
+ dnote-server start
+```
+
+Replace $user and $password with the credentials of the Postgres user that owns the `dnote` database.
+
+By default, dnote server will run on the port 8080.
+
+## Configuration
+
+By now, Dnote is fully functional in your machine. The API, frontend app, and the background tasks are all in the single binary. Let's take a few more steps to configure Dnote.
+
+### Configure Nginx
+
+To make it accessible from the Internet, you need to configure Nginx.
+
+1. Create a new file in `/etc/nginx/sites-enabled/dnote` with the following contents:
+
+```
+server {
+ server_name my-dnote-server.com;
+
+ location / {
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $remote_addr;
+ proxy_set_header Host $host;
+ proxy_pass http://127.0.0.1:8080;
+ }
+}
+```
+
+2. Reload the nginx configuration by running the following:
+
+```
+sudo service nginx reload
+```
+
+Now you can access the Dnote frontend application on `http://my-dnote-server.com`, and the API on `http://my-dnote-server.com/api`.
+
+### Configure TLS by using LetsEncrypt
+
+TODO
+
+### Run Dnote As a Daemon
+
+We can use `systemctl` to run Dnote in the background as a Daemon, and automatically start it on system reboot.
+
+1. Create a new file at `/etc/systemd/system/dnote.service` with the following content:
+
+```
+[Unit]
+Description=Starts the dnote server
+Requires=network.target
+After=network.target
+
+[Service]
+Type=simple
+User=$user
+Restart=always
+RestartSec=3
+WorkingDirectory=/home/$user
+ExecStart=/home/$user/dnote-server start
+Environment=GO_ENV=PRODUCTION DBHost=localhost DBPort=5432 DBName=dnote DBUser=$DBUser
+Environment=DBPassword=$DBPassword
+
+[Install]
+WantedBy=multi-user.target
+```
+
+Replace `$user`, `$DBUser`, and `$DBPassword` with the actual values.
+
+2. Reload the change by running `sudo systemctl daemon-reload`.
+3. Enable the Daemon by running `sudo systemctl enable dnote`.`
+4. Start the Daemon by running `sudo systemctl start dnote`
diff --git a/pkg/cli/Makefile b/pkg/cli/Makefile
deleted file mode 100644
index 1cfaed29..00000000
--- a/pkg/cli/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-all:
- ./scripts/build.sh $(VERSION)
-.PHONY: all
-
-release:
- ./scripts/build.sh $(VERSION)
- ./scripts/release.sh $(VERSION)
-.PHONY: release
-
-clean:
- @git clean -f
-.PHONY: clean
diff --git a/pkg/cli/install.sh b/pkg/cli/install.sh
index b933942c..3eb19d14 100755
--- a/pkg/cli/install.sh
+++ b/pkg/cli/install.sh
@@ -113,18 +113,19 @@ hash_sha256() {
}
verify_checksum() {
- binary_path=$1
- filename=$2
- checksums=$3
+ filepath=$1
+ checksums=$2
+
+ filename=$(basename "$filepath")
want=$(grep "${filename}" "${checksums}" 2>/dev/null | cut -d ' ' -f 1)
if [ -z "$want" ]; then
print_error "unable to find checksum for '${filename}' in '${checksums}'"
exit 1
fi
- got=$(hash_sha256 "$binary_path")
+ got=$(hash_sha256 "$filepath")
if [ "$want" != "$got" ]; then
- print_error "checksum for '$binary_path' did not verify ${want} vs $got"
+ print_error "checksum for '$filepath' did not verify ${want} vs $got"
exit 1
fi
}
@@ -173,13 +174,13 @@ install_dnote() {
print_step "Downloading the checksum file for v$version"
http_download "$tmpdir/$checksum" "$checksum_url"
+ print_step "Comparing checksums for binaries."
+ verify_checksum "$tmpdir/$tarball" "$tmpdir/$checksum"
+
# unzip tar
print_step "Inflating the binary."
(cd "${tmpdir}" && tar -xzf "${tarball}")
- print_step "Comparing checksums for binaries."
- verify_checksum "${tmpdir}/${binary}" "$filename" "$tmpdir/$checksum"
-
install -d "${bindir}"
install "${tmpdir}/${binary}" "${bindir}/"
diff --git a/pkg/cli/scripts/build.sh b/pkg/cli/scripts/build.sh
index 6fdbb2f1..680173bf 100755
--- a/pkg/cli/scripts/build.sh
+++ b/pkg/cli/scripts/build.sh
@@ -1,16 +1,15 @@
#!/bin/bash
#
-# build.sh compiles dnote binary for target platforms
-# it is resonsible for creating distributable files that can
-# be released by a human or a script
+# build.sh compiles dnote binary for target platforms. It is resonsible for creating
+# distributable files that can be released by a human or a script.
# use: ./scripts/build.sh 0.4.8
-set -eu
+set -eux
-version="$1"
+version=$1
projectDir="$GOPATH/src/github.com/dnote/dnote"
basedir="$GOPATH/src/github.com/dnote/dnote/pkg/cli"
-TMP="$basedir/build"
+outputDir="$projectDir/build/cli"
command_exists () {
command -v "$1" >/dev/null 2>&1;
@@ -29,78 +28,55 @@ if [[ $1 == v* ]]; then
exit 1
fi
+goVersion=1.12.x
+
+get_binary_name() {
+ platform=$1
+
+ if [ "$platform" == "windows" ]; then
+ echo "dnote.exe"
+ else
+ echo "dnote"
+ fi
+}
+
build() {
- # init build dir
- rm -rf "$TMP"
- mkdir "$TMP"
+ platform=$1
+ arch=$2
- # fetch tool
- go get -u github.com/karalabe/xgo
+ # build binary
+ destDir="$outputDir/$platform-$arch"
- pushd "$basedir"
+ mkdir -p "$destDir"
+ xgo \
+ -go "$goVersion" \
+ -ldflags "-X main.apiEndpoint=https://api.dnote.io -X main.versionTag=$version" \
+ --targets="$platform/$arch" \
+ --tags "fts5" \
+ --dest="$destDir" \
+ "$basedir"
- # build linux
- xgo --targets="linux/amd64"\
- --tags "linux fts5"\
- -ldflags "-X main.apiEndpoint=https://api.dnote.io -X main.versionTag=$version" .
- mkdir "$TMP/linux"
- mv cli-linux-amd64 "$TMP/linux/dnote"
+ binaryName=$(get_binary_name "$platform")
+ mv "$destDir/cli-${platform}-"* "$destDir/$binaryName"
- # build darwin
- xgo --targets="darwin/amd64"\
- --tags "darwin fts5"\
- -ldflags "-X main.apiEndpoint=https://api.dnote.io -X main.versionTag=$version" .
- mkdir "$TMP/darwin"
- mv cli-darwin-10.6-amd64 "$TMP/darwin/dnote"
+ # build tarball
+ tarballName="dnote_${version}_${platform}_${arch}.tar.gz"
+ tarballPath="$outputDir/$tarballName"
- # build windows
- xgo --targets="windows/amd64"\
- --tags "fts5"\
- -ldflags "-X main.apiEndpoint=https://api.dnote.io -X main.versionTag=$version" .
- mkdir "$TMP/windows"
- mv cli-windows-4.0-amd64.exe "$TMP/windows/dnote.exe"
+ cp "$projectDir/licenses/GPLv3.txt" "$destDir"
+ cp "$basedir/README.md" "$destDir"
+ tar -C "$destDir" -zcvf "$tarballPath" "."
+ rm -rf "$destDir"
+ # calculate checksum
+ pushd "$outputDir"
+ shasum -a 256 "$tarballName" >> "$outputDir/dnote_${version}_checksums.txt"
popd
}
-get_buildname() {
- os=$1
-
- echo "dnote_${version}_${os}_amd64"
-}
-
-calc_checksum() {
- os=$1
-
- pushd "$TMP/$os"
-
- buildname=$(get_buildname "$os")
- mv dnote "$buildname"
- shasum -a 256 "$buildname" >> "$TMP/dnote_${version}_checksums.txt"
- mv "$buildname" dnote
-
- popd
-}
-
-build_tarball() {
- os=$1
- buildname=$(get_buildname "$os")
-
- pushd "$TMP/$os"
-
- cp "$projectDir/licenses/GPLv3.txt" .
- cp "$basedir/README.md" .
- tar -zcvf "../${buildname}.tar.gz" ./*
-
- popd
-}
-
-build
-
-calc_checksum darwin
-calc_checksum linux
-
-build_tarball windows
-build_tarball darwin
-build_tarball linux
+# fetch tool
+go get -u github.com/karalabe/xgo
+build linux amd64
+build darwin amd64
+build windows amd64
diff --git a/pkg/cli/scripts/release.sh b/pkg/cli/scripts/release.sh
deleted file mode 100755
index 160a5fa2..00000000
--- a/pkg/cli/scripts/release.sh
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/bin/bash
-#
-# release.sh releases the tarballs and checksum in the build directory
-# to GitHub and brew. It is important to build those files using build.sh
-# use: ./scripts/release.sh v0.4.8
-
-set -eu
-
-homebrewRepoDir="$GOPATH"/src/github.com/dnote/homebrew-dnote
-
-command_exists () {
- command -v "$1" >/dev/null 2>&1;
-}
-
-if [ $# -eq 0 ]; then
- echo "no version specified."
- exit 1
-fi
-if [[ $1 == v* ]]; then
- echo "do not prefix version with v"
- exit 1
-fi
-
-if ! command_exists hub; then
- echo "please install hub"
- exit 1
-fi
-
-if [ ! -d "$homebrewRepoDir" ]; then
- echo "homebrew-dnote not found locally. did you clone it?"
- exit 1
-fi
-
-# 1. push tag
-version=$1
-version_tag="cli-v$version"
-
-echo "* tagging and pushing the tag"
-git tag -a "$version_tag" -m "Release $version_tag"
-git push --tags
-
-# 2. release on GitHub
-files=(./build/*.tar.gz ./build/*.txt)
-file_args=()
-for file in "${files[@]}"; do
- file_args+=("--attach=$file")
-done
-
-echo "* creating release"
-set -x
-hub release create \
- "${file_args[@]}" \
- --message="$version_tag"\
- "$version_tag"
-
-# 3. Release on Homebrew
-homebrew_sha256=$(shasum -a 256 "./build/dnote_${version}_darwin_amd64.tar.gz" | cut -d ' ' -f 1)
-(cd "$homebrewRepoDir" && ./release.sh "$version" "$homebrew_sha256")
diff --git a/pkg/server/job/.env.dev b/pkg/server/.env.dev
similarity index 84%
rename from pkg/server/job/.env.dev
rename to pkg/server/.env.dev
index 64565a29..bea40d2d 100644
--- a/pkg/server/job/.env.dev
+++ b/pkg/server/.env.dev
@@ -1,6 +1,6 @@
DB_HOST=localhost
-POSTGRES_USER=sung
POSTGRES_DB=dnote
+POSTGRES_USER=postgres
SmtpUsername=mock-SmtpUsername
SmtpPassword=mock-SmtpPassword
diff --git a/pkg/server/README.md b/pkg/server/README.md
index c41db34f..1f10f8f0 100644
--- a/pkg/server/README.md
+++ b/pkg/server/README.md
@@ -1,3 +1,3 @@
# Dnote Server
-The server side infrastructure for Dnote.
+The Dnote server containing the web interface, the web API, and the background jobs.
diff --git a/pkg/server/api/.gitignore b/pkg/server/api/.gitignore
index 3e3dcbd0..78d0d862 100644
--- a/pkg/server/api/.gitignore
+++ b/pkg/server/api/.gitignore
@@ -4,3 +4,4 @@ application.zip
test-api
/dump
api
+/build
diff --git a/pkg/server/api/handlers/health.go b/pkg/server/api/handlers/health.go
index eb46107e..1cdc03ef 100644
--- a/pkg/server/api/handlers/health.go
+++ b/pkg/server/api/handlers/health.go
@@ -24,4 +24,5 @@ import (
func (a *App) checkHealth(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
+ w.Write([]byte("ok"))
}
diff --git a/pkg/server/api/main.go b/pkg/server/api/main.go
deleted file mode 100644
index 819a326a..00000000
--- a/pkg/server/api/main.go
+++ /dev/null
@@ -1,103 +0,0 @@
-/* Copyright (C) 2019 Monomax Software Pty Ltd
- *
- * This file is part of Dnote.
- *
- * Dnote is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Dnote is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with Dnote. If not, see .
- */
-
-package main
-
-import (
- "flag"
- "fmt"
- "log"
- "net/http"
- "os"
-
- "github.com/dnote/dnote/pkg/server/api/clock"
- "github.com/dnote/dnote/pkg/server/api/handlers"
- "github.com/dnote/dnote/pkg/server/api/logger"
- "github.com/dnote/dnote/pkg/server/database"
- "github.com/dnote/dnote/pkg/server/mailer"
-
- "github.com/gorilla/mux"
- "github.com/gorilla/securecookie"
- "github.com/gorilla/sessions"
- "github.com/markbates/goth"
- "github.com/markbates/goth/gothic"
- "github.com/markbates/goth/providers/github"
- "github.com/markbates/goth/providers/gplus"
- "github.com/pkg/errors"
-)
-
-var (
- emailTemplateDir = flag.String("emailTemplateDir", "../mailer/templates/src", "the path to the template directory")
-)
-
-func getOauthCallbackURL(provider string) string {
- if os.Getenv("GO_ENV") == "PRODUCTION" {
- return fmt.Sprintf("%s/api/auth/%s/callback", os.Getenv("WebHost"), provider)
- }
-
- return fmt.Sprintf("%s:%s/api/auth/%s/callback", os.Getenv("Host"), os.Getenv("WebPort"), provider)
-}
-
-func init() {
- // Set up Oauth
- gothic.Store = sessions.NewCookieStore(securecookie.GenerateRandomKey(32), securecookie.GenerateRandomKey(32))
- goth.UseProviders(
- github.New(
- os.Getenv("GithubClientID"),
- os.Getenv("GithubClientSecret"),
- getOauthCallbackURL("github"),
- ),
- gplus.New(
- os.Getenv("GoogleClientID"),
- os.Getenv("GoogleClientSecret"),
- getOauthCallbackURL("gplus"),
- "https://www.googleapis.com/auth/plus.me",
- ),
- )
-
- gothic.GetProviderName = func(r *http.Request) (name string, err error) {
- vars := mux.Vars(r)
- name = vars["provider"]
- return
- }
-}
-
-func main() {
- flag.Parse()
-
- mailer.InitTemplates(*emailTemplateDir)
-
- database.InitDB()
- database.InitSchema()
- defer database.CloseDB()
-
- if err := logger.Init(); err != nil {
- log.Println(errors.Wrap(err, "initializing logger"))
- }
-
- app := handlers.App{
- Clock: clock.New(),
- StripeAPIBackend: nil,
- }
- r := handlers.NewRouter(&app)
-
- port := os.Getenv("PORT")
- logger.Notice("API listening on port %s", port)
-
- log.Println(http.ListenAndServe(":"+port, r))
-}
diff --git a/pkg/server/api/scripts/build.sh b/pkg/server/api/scripts/build.sh
new file mode 100755
index 00000000..77a332f9
--- /dev/null
+++ b/pkg/server/api/scripts/build.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+set -eux
+
+basePath="$GOPATH/src/github.com/dnote/dnote/pkg/server/api"
+
+cd "$basePath"
+GOOS=linux GOARCH=amd64 go build -o "$basePath/build/api" main.go
diff --git a/pkg/server/api/scripts/dev.sh b/pkg/server/api/scripts/dev.sh
deleted file mode 100755
index 5293b7ee..00000000
--- a/pkg/server/api/scripts/dev.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-# dev.sh starts a development environment for the api.
-# usage: DOTENV_PATH=path_to_dotenv_file EMAIL_TEMPLATE_DIR=path_to_email_templates ./dev.sh
-set -eux
-
-basePath="$GOPATH/src/github.com/dnote/dnote/pkg/server"
-
-# load env
-export $(cat "$DOTENV_PATH" | xargs)
-
-PORT=5000 CompileDaemon \
- -directory="$basePath"/api \
- -command="$basePath/api/api --emailTemplateDir $basePath/mailer/templates/src"
diff --git a/pkg/server/database/main.go b/pkg/server/database/database.go
similarity index 94%
rename from pkg/server/database/main.go
rename to pkg/server/database/database.go
index 7f446f40..0c474935 100644
--- a/pkg/server/database/main.go
+++ b/pkg/server/database/database.go
@@ -28,6 +28,11 @@ import (
_ "github.com/lib/pq"
)
+var (
+ // MigrationTableName is the name of the table that keeps track of migrations
+ MigrationTableName = "migrations"
+)
+
func getPGConnectionString() string {
if os.Getenv("GO_ENV") == "PRODUCTION" {
return fmt.Sprintf(
diff --git a/pkg/server/database/migrate.go b/pkg/server/database/migrate.go
new file mode 100644
index 00000000..8bff7002
--- /dev/null
+++ b/pkg/server/database/migrate.go
@@ -0,0 +1,28 @@
+package database
+
+import (
+ "log"
+
+ "github.com/gobuffalo/packr/v2"
+ "github.com/pkg/errors"
+ "github.com/rubenv/sql-migrate"
+)
+
+// Migrate runs the migrations
+func Migrate() error {
+ migrations := &migrate.PackrMigrationSource{
+ Box: packr.New("migrations", "../database/migrations/"),
+ }
+
+ migrate.SetTable(MigrationTableName)
+
+ db := DBConn.DB()
+ n, err := migrate.Exec(db, "postgres", migrations, migrate.Up)
+ if err != nil {
+ return errors.Wrap(err, "running migrations")
+ }
+
+ log.Printf("Performed %d migrations", n)
+
+ return nil
+}
diff --git a/pkg/server/database/migrate.yml b/pkg/server/database/migrate.yml
deleted file mode 100644
index fa4ddb1b..00000000
--- a/pkg/server/database/migrate.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-# a configuration for sql-migrate tool. We don't need to configure production,
-# because we programmatically connect to the database and run migrations.
-# Its main purpose is to generate migrations using `sql-migrate new`
-
-development:
- dialect: postgres
- datasource: dbname=dnote sslmode=disable
- dir: ./migrations
diff --git a/pkg/server/database/migrate/main.go b/pkg/server/database/migrate/main.go
index cf1207d5..c36dbd98 100644
--- a/pkg/server/database/migrate/main.go
+++ b/pkg/server/database/migrate/main.go
@@ -55,6 +55,8 @@ func main() {
Dir: *migrationDir,
}
+ migrate.SetTable("migrations")
+
n, err := migrate.Exec(db.DB(), "postgres", migrations, migrate.Up)
if err != nil {
panic(errors.Wrap(err, "executing migrations"))
diff --git a/pkg/server/database/scripts/create-migration.sh b/pkg/server/database/scripts/create-migration.sh
index e45af691..f185be82 100755
--- a/pkg/server/database/scripts/create-migration.sh
+++ b/pkg/server/database/scripts/create-migration.sh
@@ -18,4 +18,4 @@ if [ "$#" == 0 ]; then
fi
filename=$1
-sql-migrate new -config=migrate.yml "$filename"
+sql-migrate new -config=./sql-migrate.yml "$filename"
diff --git a/pkg/server/database/sql-migrate.yml b/pkg/server/database/sql-migrate.yml
new file mode 100644
index 00000000..f9c90d83
--- /dev/null
+++ b/pkg/server/database/sql-migrate.yml
@@ -0,0 +1,8 @@
+# A configuration for sql-migrate tool for generating migrations
+# using `sql-migrate new`. This file is not actually used for running
+# migrations because we run them programmatically.
+
+development:
+ dialect: postgres
+ datasource: dbname=dnote sslmode=disable
+ dir: ./migrations
diff --git a/pkg/server/job/.gitignore b/pkg/server/job/.gitignore
deleted file mode 100644
index 1ecf06f3..00000000
--- a/pkg/server/job/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-/tmp
-/dnote-job.tar.gz
-job
diff --git a/pkg/server/job/digest/digest.go b/pkg/server/job/digest.go
similarity index 93%
rename from pkg/server/job/digest/digest.go
rename to pkg/server/job/digest.go
index 5446eb0d..6fb1bb54 100644
--- a/pkg/server/job/digest/digest.go
+++ b/pkg/server/job/digest.go
@@ -16,7 +16,7 @@
* along with Dnote. If not, see .
*/
-package digest
+package job
import (
"log"
@@ -27,8 +27,8 @@ import (
"github.com/pkg/errors"
)
-// Make builds a weekly digest email
-func Make(user database.User, emailAddr string) (*mailer.Email, error) {
+// makeDigest builds a weekly digest email
+func makeDigest(user database.User, emailAddr string) (*mailer.Email, error) {
log.Printf("Sending for %s", emailAddr)
db := database.DBConn
@@ -92,8 +92,8 @@ func Make(user database.User, emailAddr string) (*mailer.Email, error) {
return email, nil
}
-// Send sends the weekly digests to users
-func Send() error {
+// sendDigest sends the weekly digests to users
+func sendDigest() error {
db := database.DBConn
var users []database.User
@@ -111,7 +111,7 @@ func Send() error {
continue
}
- email, err := Make(user, account.Email.String)
+ email, err := makeDigest(user, account.Email.String)
if err != nil {
log.Printf("Error occurred while sending to %s: %s", account.Email.String, err.Error())
continue
diff --git a/pkg/server/job/main.go b/pkg/server/job/job.go
similarity index 57%
rename from pkg/server/job/main.go
rename to pkg/server/job/job.go
index 93f95bae..82a63de0 100644
--- a/pkg/server/job/main.go
+++ b/pkg/server/job/job.go
@@ -16,42 +16,15 @@
* along with Dnote. If not, see .
*/
-package main
+package job
import (
- "flag"
"log"
- "os"
- "github.com/dnote/dnote/pkg/server/database"
- "github.com/dnote/dnote/pkg/server/job/digest"
- "github.com/dnote/dnote/pkg/server/mailer"
-
- "github.com/joho/godotenv"
- _ "github.com/lib/pq"
"github.com/pkg/errors"
"github.com/robfig/cron"
)
-var (
- emailTemplateDir = flag.String("emailTemplateDir", "../mailer/templates/src", "the path to the template directory")
-)
-
-func init() {
- // Load env
- if os.Getenv("GO_ENV") == "PRODUCTION" {
- err := godotenv.Load(".env")
- if err != nil {
- panic(err)
- }
- } else {
- err := godotenv.Load(".env.dev")
- if err != nil {
- panic(err)
- }
- }
-}
-
func scheduleJob(c *cron.Cron, spec string, cmd func()) {
s, err := cron.ParseStandard(spec)
if err != nil {
@@ -61,22 +34,13 @@ func scheduleJob(c *cron.Cron, spec string, cmd func()) {
c.Schedule(s, cron.FuncJob(cmd))
}
-func main() {
- flag.Parse()
-
- mailer.InitTemplates(*emailTemplateDir)
-
- database.InitDB()
- defer database.CloseDB()
-
- // Run jobs on initial start
- log.Println("Job is running")
+// Run starts the background tasks and blocks forever.
+func Run() {
+ log.Println("Started background tasks")
// Schedule jobs
c := cron.New()
-
- scheduleJob(c, "0 20 * * 5", func() { digest.Send() })
-
+ scheduleJob(c, "0 20 * * 5", func() { sendDigest() })
c.Start()
// Block forever
diff --git a/pkg/server/job/scripts/dev.sh b/pkg/server/job/scripts/dev.sh
deleted file mode 100755
index 2d22dcde..00000000
--- a/pkg/server/job/scripts/dev.sh
+++ /dev/null
@@ -1 +0,0 @@
-CompileDaemon -directory=. -command="./job"
diff --git a/pkg/server/mailer/mailer.go b/pkg/server/mailer/mailer.go
index 793ace53..c14b4c88 100644
--- a/pkg/server/mailer/mailer.go
+++ b/pkg/server/mailer/mailer.go
@@ -27,6 +27,7 @@ import (
"path"
"github.com/aymerick/douceur/inliner"
+ "github.com/gobuffalo/packr/v2"
"github.com/pkg/errors"
"gopkg.in/gomail.v2"
)
@@ -54,26 +55,45 @@ func getTemplatePath(templateDirPath, filename string) string {
// initTemplate returns a template instance by parsing the template with the
// given name along with partials
-func initTemplate(templateDirPath, templateFileName string) (*template.Template, error) {
- templatePath := getTemplatePath(templateDirPath, templateFileName)
- headerPath := getTemplatePath(templateDirPath, "header")
- footerPath := getTemplatePath(templateDirPath, "footer")
+func initTemplate(box *packr.Box, templateName string) (*template.Template, error) {
+ filename := fmt.Sprintf("%s.html", templateName)
- t, err := template.ParseFiles(templatePath, headerPath, footerPath)
+ content, err := box.FindString(filename)
if err != nil {
- return nil, errors.Wrap(err, "parseing template")
+ return nil, errors.Wrap(err, "reading template")
+ }
+ headerContent, err := box.FindString("header.html")
+ if err != nil {
+ return nil, errors.Wrap(err, "reading header template")
+ }
+ footerContent, err := box.FindString("footer.html")
+ if err != nil {
+ return nil, errors.Wrap(err, "reading footer template")
+ }
+
+ t := template.New(templateName)
+ if _, err = t.Parse(content); err != nil {
+ return nil, errors.Wrap(err, "parsing template")
+ }
+ if _, err = t.Parse(headerContent); err != nil {
+ return nil, errors.Wrap(err, "parsing template")
+ }
+ if _, err = t.Parse(footerContent); err != nil {
+ return nil, errors.Wrap(err, "parsing template")
}
return t, nil
}
// InitTemplates initializes templates
-func InitTemplates(templateDirPath string) {
- weeklyDigestTmpl, err := initTemplate(templateDirPath, EmailTypeWeeklyDigest)
+func InitTemplates() {
+ box := packr.New("emailTemplates", "../mailer/templates/src")
+
+ weeklyDigestTmpl, err := initTemplate(box, EmailTypeWeeklyDigest)
if err != nil {
panic(errors.Wrap(err, "initializing template"))
}
- emailVerificationTmpl, err := initTemplate(templateDirPath, EmailTypeEmailVerification)
+ emailVerificationTmpl, err := initTemplate(box, EmailTypeEmailVerification)
if err != nil {
panic(errors.Wrap(err, "initializing template"))
}
diff --git a/pkg/server/main.go b/pkg/server/main.go
new file mode 100644
index 00000000..55e0a7c4
--- /dev/null
+++ b/pkg/server/main.go
@@ -0,0 +1,127 @@
+/* Copyright (C) 2019 Monomax Software Pty Ltd
+ *
+ * This file is part of Dnote.
+ *
+ * Dnote is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Dnote is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Dnote. If not, see .
+ */
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "net/http"
+ "strings"
+
+ "github.com/dnote/dnote/pkg/server/api/clock"
+ "github.com/dnote/dnote/pkg/server/api/handlers"
+ "github.com/dnote/dnote/pkg/server/database"
+ "github.com/dnote/dnote/pkg/server/job"
+ "github.com/dnote/dnote/pkg/server/mailer"
+
+ "github.com/gobuffalo/packr/v2"
+ "github.com/gorilla/mux"
+ "github.com/pkg/errors"
+)
+
+var versionTag = "master"
+var port = flag.String("port", "8080", "port to connect to")
+
+func init() {
+}
+
+func getAppHandler() http.HandlerFunc {
+ box := packr.New("web", "../../web/public")
+
+ fs := http.FileServer(box)
+ appShell, err := box.Find("index.html")
+ if err != nil {
+ panic(errors.Wrap(err, "getting index.html content"))
+ }
+
+ return func(w http.ResponseWriter, r *http.Request) {
+ parts := strings.Split(r.URL.Path, "/")
+ if len(parts) >= 2 && parts[1] == "dist" {
+ fs.ServeHTTP(w, r)
+ return
+ }
+
+ // All other requests should serve the index.html file
+ w.Write(appShell)
+ }
+}
+
+func initServer() *mux.Router {
+ srv := mux.NewRouter()
+
+ apiRouter := handlers.NewRouter(&handlers.App{
+ Clock: clock.New(),
+ StripeAPIBackend: nil,
+ })
+
+ srv.PathPrefix("/api").Handler(http.StripPrefix("/api", apiRouter))
+ srv.PathPrefix("/").HandlerFunc(getAppHandler())
+
+ return srv
+}
+
+func startCmd() {
+ mailer.InitTemplates()
+ database.InitDB()
+ database.InitSchema()
+ defer database.CloseDB()
+
+ // Perform database migration
+ if err := database.Migrate(); err != nil {
+ panic(errors.Wrap(err, "running migrations"))
+ }
+
+ // Run job in the background
+ go job.Run()
+
+ srv := initServer()
+
+ log.Printf("Dnote version %s is running on port %s", versionTag, *port)
+ addr := fmt.Sprintf(":%s", *port)
+ log.Println(http.ListenAndServe(addr, srv))
+}
+
+func versionCmd() {
+ fmt.Printf("dnote-server-%s\n", versionTag)
+}
+
+func main() {
+ flag.Parse()
+ cmd := flag.Arg(0)
+
+ switch cmd {
+ case "":
+ fmt.Printf(`Dnote Server - A simple notebook for developers
+
+Usage:
+ dnote-server [command]
+
+Available commands:
+ start: Start the server
+ version: Print the version
+`)
+ case "start":
+ startCmd()
+ case "version":
+ versionCmd()
+ default:
+ fmt.Printf("Unknown command %s", cmd)
+ }
+}
diff --git a/pkg/server/scripts/build.sh b/pkg/server/scripts/build.sh
new file mode 100755
index 00000000..efd7d6b8
--- /dev/null
+++ b/pkg/server/scripts/build.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+set -eux
+
+version=$1
+basePath="$GOPATH/src/github.com/dnote/dnote"
+projectDir="$GOPATH/src/github.com/dnote/dnote"
+basedir="$GOPATH/src/github.com/dnote/dnote/pkg/server"
+outputDir="$projectDir/build/server"
+
+command_exists () {
+ command -v "$1" >/dev/null 2>&1;
+}
+
+if ! command_exists shasum; then
+ echo "please install shasum"
+ exit 1
+fi
+if [ $# -eq 0 ]; then
+ echo "no version specified."
+ exit 1
+fi
+if [[ $1 == v* ]]; then
+ echo "do not prefix version with v"
+ exit 1
+fi
+
+build() {
+ platform=$1
+ arch=$2
+
+ destDir="$outputDir/$platform-$arch"
+ mkdir -p "$destDir"
+
+ # build binary
+ packr2
+
+ GOOS="$platform" \
+ GOARCH="$arch" go build \
+ -o "$destDir/dnote-server" \
+ -ldflags "-X main.versionTag=$version" \
+ "$basePath"/pkg/server/*.go
+
+ packr2 clean
+
+ # build tarball
+ tarballName="dnote_server_${version}_${platform}_${arch}.tar.gz"
+ tarballPath="$outputDir/$tarballName"
+
+ cp "$projectDir/licenses/AGPLv3.txt" "$destDir"
+ cp "$basedir/README.md" "$destDir"
+ tar -C "$destDir" -zcvf "$tarballPath" "."
+ rm -rf "$destDir"
+
+ # calculate checksum
+ pushd "$outputDir"
+ shasum -a 256 "$tarballName" >> "$outputDir/dnote_${version}_checksums.txt"
+ popd
+}
+
+build linux amd64
diff --git a/scripts/release.sh b/scripts/release.sh
new file mode 100755
index 00000000..aa72ea23
--- /dev/null
+++ b/scripts/release.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+#
+# release.sh releases the tarballs and checksum in the build directory
+# to GitHub and brew. A prerequisite is to build those files using build.sh.
+# use: ./scripts/release.sh cli v0.4.8 path/to/assets
+
+set -euxo pipefail
+
+project=$1
+version=$2
+assetPath=$3
+
+if [ "$project" != "cli" ] || [ "$project" != "server" ]; then
+ echo "unrecognized project '$project'"
+ exit 1
+fi
+if [ -z "$version" ]; then
+ echo "no version specified."
+ exit 1
+fi
+if [[ $version == v* ]]; then
+ echo "do not prefix version with v"
+ exit 1
+fi
+
+# 1. push tag
+version=$1
+version_tag="$project-v$version"
+
+echo "* tagging and pushing the tag"
+git tag -a "$version_tag" -m "Release $version_tag"
+git push --tags
+
+# 2. release on GitHub
+files=("$assetPath"/*)
+file_flags=()
+for file in "${files[@]}"; do
+ file_flags+=("--attach=$file")
+done
+
+# mark as prerelease if version is not in a form of major.minor.patch
+# e.g. 1.0.1-beta.1
+flags=()
+if [[ ! "$version" =~ ^[0-9]+.[0-9]+.[0-9]+$ ]]; then
+ flags+=("--prerelease")
+fi
+
+echo "* creating release"
+set -x
+hub release create \
+ "${file_flags[@]}" \
+ "${flags[@]}" \
+ --message="$version_tag"\
+ "$version_tag"
diff --git a/web/.env.dev b/web/.env.dev
deleted file mode 100644
index e954c9d9..00000000
--- a/web/.env.dev
+++ /dev/null
@@ -1 +0,0 @@
-API_HOST=http://localhost:5000/
diff --git a/web/main.go b/web/main.go
deleted file mode 100644
index e44cf075..00000000
--- a/web/main.go
+++ /dev/null
@@ -1,72 +0,0 @@
-/* Copyright (C) 2019 Monomax Software Pty Ltd
- *
- * This file is part of Dnote.
- *
- * Dnote is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Dnote is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with Dnote. If not, see .
- */
-
-package main
-
-import (
- "log"
- "net/http"
- "net/http/httputil"
- "net/url"
- "os"
- "strings"
-
- "github.com/joho/godotenv"
- "github.com/pkg/errors"
-)
-
-var apiProxy *httputil.ReverseProxy
-
-func appHandler(w http.ResponseWriter, r *http.Request) {
- parts := strings.Split(r.URL.Path, "/")
- if len(parts) >= 2 && parts[1] == "dist" {
- fs := http.StripPrefix("/dist/", http.FileServer(http.Dir("./public/dist")))
-
- fs.ServeHTTP(w, r)
- return
- }
-
- // All other requests should go to index.html
- http.ServeFile(w, r, "./public/index.html")
-}
-
-func init() {
- if os.Getenv("GO_ENV") != "PRODUCTION" {
- err := godotenv.Load(".env.dev")
- if err != nil {
- panic(errors.Wrap(err, "loading env vars"))
- }
- }
-
- apiHostURL, err := url.Parse(os.Getenv("API_HOST"))
- if err != nil {
- panic(errors.Wrap(err, "parsing api host url"))
- }
-
- apiProxy = httputil.NewSingleHostReverseProxy(apiHostURL)
-}
-
-func main() {
- http.HandleFunc("/", appHandler)
- http.Handle("/api/", http.StripPrefix("/api/", apiProxy))
-
- port := os.Getenv("PORT")
- log.Printf("Web listening on port %s", port)
-
- log.Println(http.ListenAndServe(":"+port, nil))
-}
diff --git a/web/package-lock.json b/web/package-lock.json
index 96d718b6..c45e29b8 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -5084,8 +5084,7 @@
},
"ansi-regex": {
"version": "2.1.1",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"aproba": {
"version": "1.2.0",
@@ -5103,13 +5102,11 @@
},
"balanced-match": {
"version": "1.0.0",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
- "optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -5122,18 +5119,15 @@
},
"code-point-at": {
"version": "1.1.0",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"concat-map": {
"version": "0.0.1",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"console-control-strings": {
"version": "1.1.0",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"core-util-is": {
"version": "1.0.2",
@@ -5236,8 +5230,7 @@
},
"inherits": {
"version": "2.0.3",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"ini": {
"version": "1.3.5",
@@ -5247,7 +5240,6 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
- "optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -5260,20 +5252,17 @@
"minimatch": {
"version": "3.0.4",
"bundled": true,
- "optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
- "optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -5290,7 +5279,6 @@
"mkdirp": {
"version": "0.5.1",
"bundled": true,
- "optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -5363,8 +5351,7 @@
},
"number-is-nan": {
"version": "1.0.1",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"object-assign": {
"version": "4.1.1",
@@ -5374,7 +5361,6 @@
"once": {
"version": "1.4.0",
"bundled": true,
- "optional": true,
"requires": {
"wrappy": "1"
}
@@ -5450,8 +5436,7 @@
},
"safe-buffer": {
"version": "5.1.2",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -5481,7 +5466,6 @@
"string-width": {
"version": "1.0.2",
"bundled": true,
- "optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -5499,7 +5483,6 @@
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
- "optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -5538,13 +5521,11 @@
},
"wrappy": {
"version": "1.0.2",
- "bundled": true,
- "optional": true
+ "bundled": true
},
"yallist": {
"version": "3.0.3",
- "bundled": true,
- "optional": true
+ "bundled": true
}
}
},
diff --git a/web/scripts/build-prod.sh b/web/scripts/build-prod.sh
index 8ccb352d..0ce6a568 100755
--- a/web/scripts/build-prod.sh
+++ b/web/scripts/build-prod.sh
@@ -6,12 +6,8 @@ basePath="$GOPATH/src/github.com/dnote/dnote"
publicPath="$basePath/web/public"
compiledPath="$basePath/web/compiled"
-
-baseUrl=https://dnote.io
-assetBaseUrl=https://dnote.io
-
-# baseUrl=http://localhost:3000
-# assetBaseUrl=http://localhost:3000
+baseUrl=""
+assetBaseUrl=""
STANDALONE=true \
BASE_URL=$baseUrl \
@@ -19,4 +15,3 @@ ASSET_BASE_URL=$assetBaseUrl \
PUBLIC_PATH=$publicPath \
COMPILED_PATH=$compiledPath \
"$basePath"/web/scripts/build.sh
-
diff --git a/web/scripts/build.sh b/web/scripts/build.sh
index 37b8d8b2..08f95944 100755
--- a/web/scripts/build.sh
+++ b/web/scripts/build.sh
@@ -1,11 +1,15 @@
#!/bin/bash
-# build.sh builds a production bundle
-set -eux
+# build.sh builds a bundle
+set -ex
basePath="$GOPATH/src/github.com/dnote/dnote"
+
standalone=${STANDALONE:-false}
isTest=${IS_TEST:-false}
+baseUrl=$BASE_URL
+assetBaseUrl=$ASSET_BASE_URL
+set -u
rm -rf "$basePath/web/public"
mkdir -p "$basePath/web/public/dist"
@@ -23,11 +27,11 @@ pushd "$basePath/web"
--config "$basePath"/web/webpack/prod.config.js
NODE_ENV=PRODUCTION \
- BASE_URL=$BASE_URL \
- ASSET_BASE_URL=$ASSET_BASE_URL \
+ BASE_URL=$baseUrl \
+ ASSET_BASE_URL=$assetBaseUrl \
PUBLIC_PATH=$PUBLIC_PATH \
COMPILED_PATH=$COMPILED_PATH \
- "$basePath"/web/scripts/placeholder.sh
+ node "$basePath"/web/scripts/placeholder.js
cp "$COMPILED_PATH"/*.js "$COMPILED_PATH"/*.css "$PUBLIC_PATH"/dist
diff --git a/web/scripts/dev.sh b/web/scripts/dev.sh
index dd949dba..e5da5127 100755
--- a/web/scripts/dev.sh
+++ b/web/scripts/dev.sh
@@ -1,11 +1,25 @@
#!/bin/bash
+# shellcheck disable=SC1090
# dev.sh builds and starts development environment for standalone app
set -eux -o pipefail
-basePath="$GOPATH/src/github.com/dnote/dnote"
-appPath="$basePath"/web
+# clean up background processes
+function cleanup {
+ kill "$devServerPID"
+}
+trap cleanup EXIT
-# run webpack-dev-server for js
+basePath="$GOPATH/src/github.com/dnote/dnote"
+appPath="$basePath/web"
+serverPath="$basePath/pkg/server"
+
+# load env
+set -a
+dotenvPath="$serverPath/.env.dev"
+source "$dotenvPath"
+set +a
+
+# run webpack-dev-server for js in the background
(
cd "$appPath" &&
@@ -18,6 +32,7 @@ appPath="$basePath"/web
IS_TEST=true \
"$appPath"/scripts/webpack-dev.sh
) &
+devServerPID=$!
# run server
-(cd "$appPath" && PORT=3000 go run main.go)
+(cd "$serverPath" && go run main.go)
diff --git a/web/scripts/placeholder.js b/web/scripts/placeholder.js
old mode 100644
new mode 100755
index 15b115ac..4ccfe8b4
--- a/web/scripts/placeholder.js
+++ b/web/scripts/placeholder.js
@@ -1,20 +1,4 @@
-/* Copyright (C) 2019 Monomax Software Pty Ltd
- *
- * This file is part of Dnote.
- *
- * Dnote is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Dnote is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with Dnote. If not, see .
- */
+#!/usr/bin/env node
// placeholder.js replaces the placeholders in index.html with real values
// It is needed to load assets whose paths are not fixed because they change
@@ -23,8 +7,16 @@
const fs = require('fs');
const path = require('path');
+const baseURL = process.env.BASE_URL;
+const assetBaseURL = process.env.ASSET_BASE_URL;
const publicPath = process.env.PUBLIC_PATH;
const compiledPath = process.env.COMPILED_PATH;
+if (!publicPath) {
+ throw new Error('No PUBLIC_PATH environment variable found');
+}
+if (!compiledPath) {
+ throw new Error('No COMPILED_PATH environment variable found');
+}
const indexHtmlPath = `${publicPath}/index.html`;
@@ -39,9 +31,6 @@ try {
const isProduction = process.env.NODE_ENV === 'PRODUCTION';
-const baseURL = process.env.BASE_URL;
-const assetBaseURL = process.env.ASSET_BASE_URL;
-
function getJSBundleTag() {
let jsBundleUrl;
if (isProduction) {
diff --git a/web/scripts/placeholder.sh b/web/scripts/placeholder.sh
deleted file mode 100755
index 940ed21e..00000000
--- a/web/scripts/placeholder.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-set -eux
-
-basePath="$GOPATH/src/github.com/dnote/dnote"
-
-if [ -z "$BASE_URL" ]; then
- echo "BASE_URL environment variable is not set"
- exit 1
-fi
-if [ -z "$ASSET_BASE_URL" ]; then
- echo "ASSET_BASE_URL environment variable is not set"
- exit 1
-fi
-if [ -z "$PUBLIC_PATH" ]; then
- echo "PUBLIC_PATH environment variable is not set"
- exit 1
-fi
-if [ -z "$COMPILED_PATH" ]; then
- echo "COMPILED_PATH environment variable is not set"
- exit 1
-fi
-
-BASE_URL=$BASE_URL \
-ASSET_BASE_URL=$ASSET_BASE_URL \
-PUBLIC_PATH=$PUBLIC_PATH \
-COMPILED_PATH=$COMPILED_PATH \
- node "$basePath/web/scripts/placeholder.js"
diff --git a/web/scripts/start.sh b/web/scripts/start.sh
deleted file mode 100755
index 754a8e1c..00000000
--- a/web/scripts/start.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/bash
-# start.sh starts the server
-
-export PORT=3000
-
-go run main.go
diff --git a/web/scripts/webpack-dev.sh b/web/scripts/webpack-dev.sh
index 85181e04..d839775e 100755
--- a/web/scripts/webpack-dev.sh
+++ b/web/scripts/webpack-dev.sh
@@ -15,7 +15,7 @@ appPath="$basePath"/web
COMPILED_PATH=$COMPILED_PATH \
PUBLIC_PATH=$PUBLIC_PATH \
IS_TEST=true \
- "$appPath"/scripts/placeholder.sh &&
+ node "$appPath"/scripts/placeholder.js &&
"$appPath"/node_modules/.bin/webpack-dev-server\
--env.standalone="$STANDALONE"\