ntfy/docs/develop.md
2022-05-16 14:53:51 -04:00

13 KiB

Development

Hurray 🥳 🎉, you are interested in writing code for ntfy! That's awesome. 😎

I tried my very best to write up detailed instructions, but if at any point in time you run into issues, don't hesitate to contact me on Discord or Matrix.

ntfy server

The ntfy server source code is available on GitHub. The codebase for the server consists of three components:

  • The main server/client is written in Go (so you'll need Go). Its main entrypoint is at main.go, and the meat you're likely interested in is in server.go. Notably, the server uses a SQLite library called go-sqlite3, which requires Cgo and CGO_ENABLED=1 to be set. Otherwise things will not work (see below).
  • The documentation is generated by MkDocs and Material for MkDocs, which is written in Python. You'll need Python and MkDocs (via pip) only if you want to build the docs.
  • The web app is written in React, using MUI. It uses Create React App to build the production build. If you want to modify the web app, you need nodejs (for npm) and install all the 100,000 dependencies (sigh).

All of these components are built and then baked into one binary.

Navigating the code

Code:

  • main.go - Main entrypoint into the CLI, for both server and client
  • cmd/ - CLI commands, such as serve or publish
  • server/ - The meat of the server logic
  • docs/ - The MkDocs documentation, also see mkdocs.yml
  • web/ - The React application, also see web/package.json

Build related:

The web/ and docs/ folder are the sources for web app and documentation. During the build process, the generated output is copied to server/site (web app and landing page) and server/docs (documentation).

Build requirements

  • Go (required for main server)
  • gcc (required main server, for SQLite cgo-based bindings)
  • Make (required for convenience)
  • libsqlite3/libsqlite3-dev (required for main server, for SQLite cgo-based bindings)
  • GoReleaser (required for a proper main server build)
  • Python (for pip, only to build the docs)
  • nodejs (for npm, only to build the web app)

Install dependencies

These steps assume Ubuntu. Steps may vary on different Linux distributions.

First, install Go (see official instructions):

wget https://go.dev/dl/go1.18.linux-amd64.tar.gz
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.18.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin
go version   # verifies that it worked

Install GoReleaser (see official instructions):

go install github.com/goreleaser/goreleaser@latest
goreleaser -v   # verifies that it worked

Install nodejs (see official instructions):

curl -fsSL https://deb.nodesource.com/setup_17.x | sudo -E bash -
sudo apt-get install -y nodejs
npm -v   # verifies that it worked

Then install a few other things required:

sudo apt install \
    build-essential \
    libsqlite3-dev \
    gcc-arm-linux-gnueabi \
    gcc-aarch64-linux-gnu \
    python3-pip \
    upx \
    git

Check out code

Now check out via git from the GitHub repository:

=== "via HTTPS" shell git clone https://github.com/binwiederhier/ntfy.git cd ntfy

=== "via SSH" shell git clone git@github.com:binwiederhier/ntfy.git cd ntfy

Build all the things

Now you can finally build everything. There are tons of make targets, so maybe just review what's there first by typing make:

$ make 
Typical commands (more see below):
  make build                   - Build web app, documentation and server/client (sloowwww)
  make cli-linux-amd64         - Build server/client binary (amd64, no web app or docs)
  make install-linux-amd64     - Install ntfy binary to /usr/bin/ntfy (amd64)
  make web                     - Build the web app
  make docs                    - Build the documentation
  make check                   - Run all tests, vetting/formatting checks and linters
...

If you want to build the ntfy binary including web app and docs for all supported architectures (amd64, armv7, and arm64), you can simply run make build:

$ make build
...
# This builds web app, docs, and the ntfy binary (for amd64, armv7 and arm64). 
# This will be SLOW (5+ minutes on my laptop on the first run). Maybe look at the other make targets?

You'll see all the outputs in the dist/ folder afterwards:

$ find dist 
dist
dist/metadata.json
dist/ntfy_arm64_linux_arm64
dist/ntfy_arm64_linux_arm64/ntfy
dist/ntfy_armv7_linux_arm_7
dist/ntfy_armv7_linux_arm_7/ntfy
dist/ntfy_amd64_linux_amd64
dist/ntfy_amd64_linux_amd64/ntfy
dist/config.yaml
dist/artifacts.json

If you also want to build the Debian/RPM packages and the Docker images for all supported architectures, you can use the make release-snapshot target:

$ make release-snapshot
...
# This will be REALLY SLOW (sometimes 5+ minutes on my laptop)

During development, you may want to be more picky and build only certain things. Here are a few examples.

Build the ntfy binary

To build only the ntfy binary without the web app or documentation, use the make cli-... targets:

$ make
Build server & client (not release version):
  make cli                     - Build server & client (all architectures)
  make cli-linux-amd64         - Build server & client (Linux, amd64 only)
  make cli-linux-armv6         - Build server & client (Linux, armv6 only)
  make cli-linux-armv7         - Build server & client (Linux, armv7 only)
  make cli-linux-arm64         - Build server & client (Linux, arm64 only)
  make cli-windows-amd64       - Build client (Windows, amd64 only)

So if you're on an amd64/x86_64-based machine, you may just want to run make cli-linux-amd64 during testing. On a modern system, this shouldn't take longer than 5-10 seconds. I often combine it with install-linux-amd64 so I can run the binary right away:

$ make cli-linux-amd64 install-linux-amd64
$ ntfy serve

During development of the main app, you can also just use go run main.go, as long as you run make cli-deps-static-sitesat least once and CGO_ENABLED=1:

$ export CGO_ENABLED=1
$ make cli-deps-static-sites
$ go run main.go serve
2022/03/18 08:43:55 Listening on :2586[http]
...

If you don't run cli-deps-static-sites, you may see an error pattern ...: no matching files found:

$ go run main.go serve
server/server.go:85:13: pattern docs: no matching files found

This is because we use go:embed to embed the documentation and web app, so the Go code expects files to be present at server/docs and server/site. If they are not, you'll see the above error. The cli-deps-static-sites target creates dummy files that ensures that you'll be able to build.

Build the web app

The sources for the web app live in web/. As long as you have npm installed (see above), building the web app is really simple. Just type make web and you're in business:

$ make web
...

This will build the web app using Create React App and then copy the production build to the server/site folder, so that when you make cli (or make cli-linux-amd64, ...), you will have the web app included in the ntfy binary.

If you're developing on the web app, it's best to just cd web and run npm start manually. This will open your browser at http://127.0.0.1:3000 with the web app, and as you edit the source files, they will be recompiled and the browser will automatically refresh:

$ cd web
$ npm start

Build the docs

The sources for the docs live in docs/. Similarly to the web app, you can simply run make docs to build the documentation. As long as you have mkdocs installed (see above), this should work fine:

$ make docs
...

If you are changing the documentation, you should be running mkdocs serve directly. This will build the documentation, serve the files at http://127.0.0.1:8000/, and rebuild every time you save the source files:

$ mkdocs serve
INFO     -  Building documentation...
INFO     -  Cleaning site directory
INFO     -  Documentation built in 5.53 seconds
INFO     -  [16:28:14] Serving on http://127.0.0.1:8000/

Then you can navigate to http://127.0.0.1:8000/ and whenever you change a markdown file in your text editor it'll automatically update.

Android app

The ntfy Android app source code is available on GitHub. The Android app has two flavors:

  • Google Play: The play flavor includes Firebase (FCM) and requires a Firebase account
  • F-Droid: The fdroid flavor does not include Firebase or Google dependencies

Navigating the code

  • main/ - Main Android app source code
  • play/ - Google Play / Firebase specific code
  • fdroid/ - F-Droid Firebase stubs
  • build.gradle - Main build file

IDE/Environment

You should download Android Studio (or IntelliJ IDEA with the relevant Android plugins). Everything else will just be a pain for you. Do yourself a favor. 😀

Check out the code

First check out the repository:

=== "via HTTPS" shell git clone https://github.com/binwiederhier/ntfy-android.git cd ntfy-android

=== "via SSH" shell git clone git@github.com:binwiederhier/ntfy-android.git cd ntfy-android

Then either follow the steps for building with or without Firebase.

Build F-Droid flavor (no FCM)

!!! info I do build the ntfy Android app using IntelliJ IDEA (Android Studio), so I don't know if these Gradle commands will work without issues. Please give me feedback if it does/doesn't work for you.

Without Firebase, you may want to still change the default app_base_url in values.xml if you're self-hosting the server. Then run:

# Remove Google dependencies (FCM)
sed -i -e '/google-services/d' build.gradle
sed -i -e '/google-services/d' app/build.gradle

# To build an unsigned .apk (app/build/outputs/apk/fdroid/*.apk)
./gradlew assembleFdroidRelease

# To build a bundle .aab (app/fdroid/release/*.aab)
./gradlew bundleFdroidRelease

Build Play flavor (FCM)

!!! info I do build the ntfy Android app using IntelliJ IDEA (Android Studio), so I don't know if these Gradle commands will work without issues. Please give me feedback if it does/doesn't work for you.

To build your own version with Firebase, you must:

  • Create a Firebase/FCM account
  • Place your account file at app/google-services.json
  • And change app_base_url in values.xml
  • Then run:
# To build an unsigned .apk (app/build/outputs/apk/play/*.apk)
./gradlew assemblePlayRelease

# To build a bundle .aab (app/play/release/*.aab)
./gradlew bundlePlayRelease