# 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](https://discord.gg/cT7ECsZj9w) or [Matrix](https://matrix.to/#/#ntfy:matrix.org)**.

## ntfy server
The ntfy server source code is available [on GitHub](https://github.com/binwiederhier/ntfy). The codebase for the
server consists of three components:

* **The main server/client** is written in [Go](https://go.dev/) (so you'll need Go). Its main entrypoint is at 
  [main.go](https://github.com/binwiederhier/ntfy/blob/main/main.go), and the meat you're likely interested in is 
  in [server.go](https://github.com/binwiederhier/ntfy/blob/main/server/server.go). Notably, the server uses a 
  [SQLite](https://sqlite.org) library called [go-sqlite3](https://github.com/mattn/go-sqlite3), which requires 
  [Cgo](https://go.dev/blog/cgo) and `CGO_ENABLED=1` to be set. Otherwise things will not work (see below).
* **The documentation** is generated by [MkDocs](https://www.mkdocs.org/) and [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/),
  which is written in [Python](https://www.python.org/). You'll need Python and MkDocs (via `pip`) only if you want to
  build the docs.
* **The web app** is written in [React](https://reactjs.org/), using [MUI](https://mui.com/). It uses [Create React App](https://create-react-app.dev/)
  to build the production build. If you want to modify the web app, you need [nodejs](https://nodejs.org/en/) (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](https://github.com/binwiederhier/ntfy/blob/main/main.go) - Main entrypoint into the CLI, for both server and client
* [cmd/](https://github.com/binwiederhier/ntfy/tree/main/cmd) - CLI commands, such as `serve` or `publish`
* [server/](https://github.com/binwiederhier/ntfy/tree/main/server) - The meat of the server logic
* [docs/](https://github.com/binwiederhier/ntfy/tree/main/docs) - The [MkDocs](https://www.mkdocs.org/) documentation, also see `mkdocs.yml`
* [web/](https://github.com/binwiederhier/ntfy/tree/main/web) - The [React](https://reactjs.org/) application, also see `web/package.json`

Build related:

* [Makefile](https://github.com/binwiederhier/ntfy/blob/main/Makefile) - Main entrypoint for all things related to building
* [.goreleaser.yml](https://github.com/binwiederhier/ntfy/blob/main/.goreleaser.yml) - Describes all build outputs (for [GoReleaser](https://goreleaser.com/))
* [go.mod](https://github.com/binwiederhier/ntfy/blob/main/go.mod) - Go modules dependency file
* [mkdocs.yml](https://github.com/binwiederhier/ntfy/blob/main/mkdocs.yml) - Config file for the docs (for [MkDocs](https://www.mkdocs.org/))
* [web/package.json](https://github.com/binwiederhier/ntfy/blob/main/web/package.json) - Build and dependency file for web app (for npm)


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](https://go.dev/) (required for main server)
* [gcc](https://gcc.gnu.org/) (required main server, for SQLite cgo-based bindings)
* [Make](https://www.gnu.org/software/make/) (required for convenience)
* [libsqlite3/libsqlite3-dev](https://www.sqlite.org/) (required for main server, for SQLite cgo-based bindings)
* [GoReleaser](https://goreleaser.com/) (required for a proper main server build)
* [Python](https://www.python.org/) (for `pip`, only to build the docs) 
* [nodejs](https://nodejs.org/en/) (for `npm`, only to build the web app)

### Install dependencies
These steps **assume Ubuntu**. Steps may vary on different Linux distributions.

First, install [Go](https://go.dev/) (see [official instructions](https://go.dev/doc/install)):
``` shell
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](https://goreleaser.com/) (see [official instructions](https://goreleaser.com/install/)):
``` shell
go install github.com/goreleaser/goreleaser@latest
goreleaser -v   # verifies that it worked
```

Install [nodejs](https://nodejs.org/en/) (see [official instructions](https://nodejs.org/en/download/package-manager/)):
``` shell
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:
``` shell
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](https://github.com/binwiederhier/ntfy):

=== "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`:

``` shell
$ make 
Typical commands (more see below):
  make build                   - Build web app, documentation and server/client (sloowwww)
  make server-amd64            - Build server/client binary (amd64, no web app or docs)
  make install-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 amd64), 
you can simply run `make build`:

``` shell
$ 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:

``` bash
$ 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:

``` shell
$ 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 server-...` targets:

``` shell
$ make
Build server & client (not release version):
  make server                  - Build server & client (all architectures)
  make server-amd64            - Build server & client (amd64 only)
  make server-armv7            - Build server & client (armv7 only)
  make server-arm64            - Build server & client (arm64 only)
```

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

``` shell
$ make server-amd64 install-amd64
$ ntfy serve
```

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

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

If you don't run `server-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 `server-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:

``` shell
$ 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 server` (or `make server-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:

``` shell
$ 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:

``` shell
$ 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](https://github.com/binwiederhier/ntfy-android).
The Android app has two flavors:

* **Google Play:** The `play` flavor includes [Firebase (FCM)](https://firebase.google.com/) and requires a Firebase account
* **F-Droid:** The `fdroid` flavor does not include Firebase or Google dependencies

### Navigating the code
* [main/](https://github.com/binwiederhier/ntfy-android/tree/main/app/src/main) - Main Android app source code
* [play/](https://github.com/binwiederhier/ntfy-android/tree/main/app/src/play) - Google Play / Firebase specific code
* [fdroid/](https://github.com/binwiederhier/ntfy-android/tree/main/app/src/fdroid) - F-Droid Firebase stubs
* [build.gradle](https://github.com/binwiederhier/ntfy-android/blob/main/app/build.gradle) - Main build file

### IDE/Environment
You should download [Android Studio](https://developer.android.com/studio) (or [IntelliJ IDEA](https://www.jetbrains.com/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 [strings.xml](https://github.com/binwiederhier/ntfy-android/blob/main/app/src/main/res/values/strings.xml)
if you're self-hosting the server. Then run:
```
# 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 [strings.xml](https://github.com/binwiederhier/ntfy-android/blob/main/app/src/main/res/values/strings.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
```