update makefile & add docs

This commit is contained in:
Mayel 2021-05-22 16:36:40 +02:00
parent b514a165c3
commit 61d195ad51
10 changed files with 726 additions and 197 deletions

454
Makefile
View file

@ -1,12 +1,21 @@
.PHONY: setup updates db-reset build dev shell
BASH := $(shell which bash)
#### Makefile config ####
#### NOTE: do not edit this file, override these in your env instead ####
# what flavour do we want?
FLAVOUR ?= classic
BONFIRE_FLAVOUR ?= flavours/$(FLAVOUR)
LIBS_PATH ?= ./forks/
# do we want to use Docker? set as env var:
# - WITH_DOCKER=total : use docker for everything (default)
# - WITH_DOCKER=partial : use docker for services like the DB
# - WITH_DOCKER=easy : use docker for services like the DB & compiled utilities like messctl
# - WITH_DOCKER=no : please no
WITH_DOCKER ?= total
# other configs
FORKS_PATH ?= ./forks/
ORG_NAME ?= bonfirenetworks
APP_NAME ?= bonfire-$(FLAVOUR)
UID := $(shell id -u)
@ -18,6 +27,8 @@ APP_VSN ?= `grep -m 1 'version:' mix.exs | cut -d '"' -f2`
APP_BUILD ?= `git rev-parse --short HEAD`
APP_DOCKER_REPO="$(ORG_NAME)/$(APP_NAME)"
#### GENERAL SETUP RELATED COMMANDS ####
export UID
export GID
@ -33,8 +44,11 @@ define load_env
$(eval include $(ENV_FILE)) # import env into make
$(eval export) # export env from make
endef
init:
pre-config: pre-init ## Initialise env files, and create some required folders, files and softlinks
@echo "You can now edit your config for flavour '$(FLAVOUR)' in config/dev/secrets.env, config/dev/public.env and ./config/ more generally."
pre-init:
@ln -sfn $(BONFIRE_FLAVOUR)/config ./config
@mkdir -p config/prod
@mkdir -p config/dev
@ -43,212 +57,197 @@ init:
@cp -n config/templates/public.env config/prod/ | true
@cp -n config/templates/not_secret.env config/dev/secrets.env | true
@cp -n config/templates/not_secret.env config/prod/secrets.env | true
@$(call setup_env,dev)
@echo "Light that fire... $(APP_NAME) with $(FLAVOUR) flavour $(APP_VSN) - $(APP_BUILD)"
pre-run:
@mkdir -p forks/
@mkdir -p data/uploads/
@mkdir -p data/search/dev
help: init
@perl -nle'print $& if m{^[a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
mix-%: init ## Run a specific mix command, eg: `make mix-deps.get` or make mix-deps.update args="pointers"
docker-compose run web mix $* $(args)
init: pre-init pre-run
@$(call setup_env,dev)
@echo "Light that fire... $(APP_NAME) with $(FLAVOUR) flavour $(APP_VSN) - $(APP_BUILD)"
@make --no-print-directory pre-init
@make --no-print-directory services
setup: build mix-setup ## First run - prepare environment and dependencies
help: init ## Makefile commands help
@perl -nle'print $& if m{^[a-zA-Z_-~.%]+:.*?## .*$$}' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
db:
docker-compose up -d db search
env.exports: ## Display the vars from dotenv files that you need to load in your environment
@awk 'NF { if( $$1 != "#" ){ print "export " $$0 }}' $(BONFIRE_FLAVOUR)/config/dev/*.env
db-pre-migrations:
touch deps/*/lib/migrations.ex 2> /dev/null || echo "continue"
touch forks/*/lib/migrations.ex 2> /dev/null || echo "continue"
touch priv/repo/* 2> /dev/null || echo "continue"
db-reset: db-pre-migrations mix-ecto.reset ## Reset the DB
#### COMMON COMMANDS ####
db-rollback: ## Rollback last DB migration
make mix-"ecto.rollback"
setup: build mix~setup ## First run - prepare environment and dependencies
db-rollback-all: ## Rollback ALL DB migrations
make mix-"ecto.rollback --all"
dev: init ## Run the app in development
ifeq ($(WITH_DOCKER), total)
@make --no-print-directory docker.stop.web
docker-compose run --name bonfire_web --service-ports web
# docker-compose --verbose run --name bonfire_web --service-ports web
else
iex -S mix phx.server
endif
test-db-reset: db-pre-migrations ## Create or reset the test DB
docker-compose run -e MIX_ENV=test web mix ecto.reset
dev.bg: init ## Run the app in dev mode, as a background service
ifeq ($(WITH_DOCKER), total)
@make --no-print-directory docker.stop.web
docker-compose run --detach --name bonfire_web --service-ports web elixir -S mix phx.server
else
elixir --erl "-detached" -S mix phx.server
echo Running in background...
ps au | grep beam
endif
build: init ## Build the docker image
docker-compose build
db.reset: db.pre-migrations mix~ecto.reset ## Reset the DB (caution: this means DATA LOSS)
shell: init ## Open a shell, in dev mode
docker-compose run --service-ports web bash
db.rollback: mix~ecto.rollback ## Rollback previous DB migration (caution: this means DATA LOSS)
pull:
git pull
db.rollback.all: mix~"ecto.rollback --all" ## Rollback ALL DB migrations (caution: this means DATA LOSS)
update: init pull ## Update/prepare dependencies, without Docker
WITH_FORKS=0 mix updates
make deps-all-git-pull
mix deps.get
mix ecto.migrate
d-update: init pull build ## Update/prepare dependencies, using Docker
docker-compose run -e WITH_FORKS=0 web mix updates
make deps-all-git-pull
make mix-deps.get
make mix-ecto.migrate
#### UPDATE COMMANDS ####
bonfire-updates: init
docker-compose run -e WITH_FORKS=0 web mix bonfire.deps
bonfire-push-all-updates: deps-all-git-commit-push bonfire-push-app-updates
update: init update.app build update.forks mix~deps.get mix~ecto.migrate ## Update the app and all dependencies/extensions/forks, and run migrations
bonfire-push-release: deps-all-git-commit-push bonfire-push-app-release
bonfire-app-updates:
update.app: ## Update the app and Bonfire extensions in ./deps
git add --all .
git diff-index --quiet HEAD || git commit --all --verbose
git pull --rebase
WITH_FORKS=0 mix updates
@make --no-print-directory mix.remote~updates
bonfire-push-app-updates: bonfire-app-updates
make git-publish
bonfire-push-app-release: bonfire-app-updates
cd lib/mix/tasks/release/ && mix escript.build && ./release ../../../../ alpha
make git-publish
bonfire-deps-updates:
git pull --rebase
WITH_FORKS=0 mix updates
make git-publish
d-bonfire-push-all-updates: deps-all-git-commit-push d-bonfire-push-app-updates
d-bonfire-push-app-updates:
git add .
git commit -a
git pull --rebase
docker-compose run -e WITH_FORKS=0 web mix updates
make git-publish
make dev
git-publish:
chmod +x git-publish.sh
./git-publish.sh
dep-hex-%: init ## add/enable/disable/delete a hex dep with messctl command, eg: `make dep-hex-enable dep=pointers version="~> 0.2"
docker-compose run web messctl $* $(dep) $(version) config/deps.hex
dep-git-%: init ## add/enable/disable/delete a git dep with messctl command, eg: `make dep-hex-enable dep=pointers repo=https://github.com/bonfire-networks/pointers#main
docker-compose run web messctl $* $(dep) $(repo) config/deps.git
dep-local-%: init ## add/enable/disable/delete a local dep with messctl command, eg: `make dep-hex-enable dep=pointers path=./libs/pointers
docker-compose run web messctl $* $(dep) $(path) config/deps.path
dep-clone-local: ## Clone a git dep and use the local version, eg: make dep-clone-local dep="bonfire_me" repo=https://github.com/bonfire-networks/bonfire_me
git clone $(repo) $(LIBS_PATH)$(dep) 2> /dev/null || (cd $(LIBS_PATH)$(dep) ; git pull)
make dep-go-local dep=$(dep)
dep-clone-local-all: ## Clone all bonfire deps / extensions
@curl -s https://api.github.com/orgs/bonfire-networks/repos?per_page=500 | ruby -rrubygems -e 'require "json"; JSON.load(STDIN.read).each { |repo| %x[make dep-clone-local dep="#{repo["name"]}" repo="#{repo["ssh_url"]}" ]}'
deps-all-git-commit-push:
chmod +x git-publish.sh
find $(LIBS_PATH) -mindepth 1 -maxdepth 1 -type d -exec ./git-publish.sh {} \;
# make deps-all-git-add
# make deps-all-git-commit
# make deps-all-git-pull
# make deps-all-git-push
deps-all-git-%: ## runs a git command (eg. `make deps-all-git-pull` pulls the latest version of all local deps from its git remote
# chown -R $$USER ./forks
make git-forks-"config core.fileMode false"
make git-forks-$*
deps-prepare-push:
mv deps.path deps.path.disabled
dep-go-local: ## Switch to using a standard local path, eg: make dep-go-local dep=pointers
make dep-go-local-path dep=$(dep) path=$(LIBS_PATH)$(dep)
dep-go-local-%: ## Switch to using a standard local path, eg: make dep-go-local dep=pointers
make dep-go-local dep="$*"
dep-go-local-path: ## Switch to using a local path, eg: make dep-go-local dep=pointers path=./libs/pointers
make dep-local-add dep=$(dep) path=$(path)
make dep-local-enable dep=$(dep) path=""
# make dep-git-disable dep=$(dep) repo=""
# make dep-hex-disable dep=$(dep) version=""
dep-go-git: ## Switch to using a git repo, eg: make dep-go-git dep="pointers" repo=https://github.com/bonfire-networks/pointers (repo is optional if previously specified)
make dep-git-add dep=$(dep) $(repo) 2> /dev/null || true
make dep-git-enable dep=$(dep) repo=""
make dep-hex-disable dep=$(dep) version=""
make dep-local-disable dep=$(dep) path=""
dep-go-hex: ## Switch to using a library from hex.pm, eg: make dep-go-hex dep="pointers" version="~> 0.2" (version is optional if previously specified)
make dep-hex-add dep=$(dep) version=$(version) 2> /dev/null || true
make dep-hex-enable dep=$(dep) version=""
make dep-git-disable dep=$(dep) repo=""
make dep-local-disable dep=$(dep) path=""
deps.get: init
docker-compose run -e WITH_FORKS=0 web mix deps.get
make mix-"deps.get"
deps.update.all:
make deps.update-"--all"
deps.update-%: init
docker-compose run -e WITH_FORKS=0 web mix deps.update $*
dev: init docker-stop-web ## Run the app with Docker
docker-compose --verbose run --name bonfire_web --service-ports web
#docker-compose run --name bonfire_web --service-ports web
dev-bg: init docker-stop-web ## Run the app in dev mode, in the background
docker-compose run --detach --name bonfire_web --service-ports web elixir -S mix phx.server
docker-stop-web:
@docker stop bonfire_web 2> /dev/null || true
@docker rm bonfire_web 2> /dev/null || true
git-forks-add: ## Run a git command on each fork
find $(LIBS_PATH) -mindepth 1 -maxdepth 1 -type d -exec echo add {} \; -exec git -C '{}' add . \;
git-forks-%: ## Run a git command on each fork
find $(LIBS_PATH) -mindepth 1 -maxdepth 1 -type d -exec echo $* {} \; -exec git -C '{}' $* \;
deps-git-fix: ## Run a git command on each dep, to ignore chmod changes
find ./deps -mindepth 1 -maxdepth 1 -type d -exec git -C '{}' config core.fileMode false \;
git-merge-%: ## Draft-merge another branch, eg `make git-merge-with-valueflows-api` to merge branch `with-valueflows-api` into the current one
git merge --no-ff --no-commit $*
git-conflicts:
find $(LIBS_PATH) -mindepth 1 -maxdepth 1 -type d -exec echo add {} \; -exec git -C '{}' diff --name-only --diff-filter=U \;
test: init ## Run tests
docker-compose run web mix test $(args)
test-remote: init ## Run tests (ignoring local forks)
docker-compose run -e WITH_FORKS=0 web mix test $(args)
licenses: init
docker-compose run -e WITH_FORKS=0 web mix licenses
update.deps.bonfire: init mix.remote~bonfire.deps ## Update to the latest Bonfire extensions in ./deps
update.deps.all: ## Update evey single dependency (use with caution)
@make --no-print-directory update.dep~"--all"
cmd-%: init ## Run a specific command in the container, eg: `make cmd-messclt` or `make cmd-"messctl help"` or `make cmd-messctl args="help"`
docker-compose run web $* $(args)
update.dep~%: ## Update a specify dep (eg. `make update.dep~pointers`)
@make --no-print-directory mix.remote~"deps.update $*"
update.forks: git.forks~pull ## Pull the latest commits from all ./forks
deps.get: mix.remote~deps.get mix~deps.get ## Fetch locked version of non-forked deps
#### DEPENDENCY & EXTENSION RELATED COMMANDS ####
assets-prepare:
cp lib/*/*/overlay/* rel/overlays/ 2> /dev/null || true
dep.clone.local: ## Clone a git dep and use the local version, eg: `make dep.clone.local dep="bonfire_me" repo=https://github.com/bonfire-networks/bonfire_me`
git clone $(repo) $(FORKS_PATH)$(dep) 2> /dev/null || (cd $(FORKS_PATH)$(dep) ; git pull)
@make --no-print-directory dep.go.local dep=$(dep)
config-prepare: # copy current flavour's config, without using symlinks
deps.clone.local.all: ## Clone all bonfire deps / extensions
@curl -s https://api.github.com/orgs/bonfire-networks/repos?per_page=500 | ruby -rrubygems -e 'require "json"; JSON.load(STDIN.read).each { |repo| %x[make dep.clone.local dep="#{repo["name"]}" repo="#{repo["ssh_url"]}" ]}'
dep.go.local:
@make --no-print-directory dep.go.local.path dep=$(dep) path=$(FORKS_PATH)$(dep)
dep.go.local~%: ## Switch to using a local path, eg: make dep.go.local~pointers
@make --no-print-directory dep.go.local dep="$*"
dep.go.local.path: ## Switch to using a local path, specifying the path, eg: make dep.go.local dep=pointers path=./libs/pointers
@make --no-print-directory dep.local~add dep=$(dep) path=$(path)
@make --no-print-directory dep.local~enable dep=$(dep) path=""
dep.go.git: ## Switch to using a git repo, eg: make dep.go.git dep="pointers" repo=https://github.com/bonfire-networks/pointers (specifying the repo is optional if previously specified)
@make --no-print-directory dep.git~add dep=$(dep) $(repo) 2> /dev/null || true
@make --no-print-directory dep.git~enable dep=$(dep) repo=""
@make --no-print-directory dep.local~disable dep=$(dep) path=""
dep.go.hex: ## Switch to using a library from hex.pm, eg: make dep.go.hex dep="pointers" version="~> 0.2" (specifying the version is optional if previously specified)
@make --no-print-directory dep.hex~add dep=$(dep) version=$(version) 2> /dev/null || true
@make --no-print-directory dep.hex~enable dep=$(dep) version=""
@make --no-print-directory dep.git~disable dep=$(dep) repo=""
@make --no-print-directory dep.local~disable dep=$(dep) path=""
dep.hex~%: ## add/enable/disable/delete a hex dep with messctl command, eg: `make dep.hex.enable dep=pointers version="~> 0.2"
@make --no-print-directory messctl args="$* $(dep) $(version) config/deps.hex"
dep.git~%: ## add/enable/disable/delete a git dep with messctl command, eg: `make dep.hex.enable dep=pointers repo=https://github.com/bonfire-networks/pointers#main
@make --no-print-directory messctl args="$* $(dep) $(repo) config/deps.git"
dep.local~%: ## add/enable/disable/delete a local dep with messctl command, eg: `make dep.hex.enable dep=pointers path=./libs/pointers
@make --no-print-directory messctl args="$* $(dep) $(path) config/deps.path"
messctl~%: ## Utility to manage the deps in deps.hex, deps.git, and deps.path (eg. `make messctl~help`)
@make --no-print-directory messctl args=$*
messctl: init
ifeq ($(WITH_DOCKER), total)
docker-compose run web messctl $(args)
else ifeq ($(WITH_DOCKER), easy)
docker-compose run web messctl $(args)
else
echo "Make sure you have compiled/installed messctl first: https://github.com/bonfire-networks/messctl"
messctl $(args)
endif
#### CONTRIBUTION RELATED COMMANDS ####
contrib.forks: contrib.forks.publish contrib.app.up ## Push all changes to the app and extensions in ./forks
contrib.release: contrib.forks.publish contrib.app.release ## Push all changes to the app and extensions in ./forks, increment the app version number, and push a new version/release
contrib.app.up: update.app git.publish ## Update ./deps and push all changes to the app
contrib.app.release: update.app contrib.app.release.increment git.publish ## Update ./deps, increment the app version number and push
contrib.app.release.increment:
@cd lib/mix/tasks/release/ && mix escript.build && ./release ../../../../ alpha
contrib.forks.publish:
@chmod +x git-publish.sh
find $(FORKS_PATH) -mindepth 1 -maxdepth 1 -type d -exec ./git-publish.sh {} \;
git.forks.add: deps.git.fix ## Run the git add command on each fork
find $(FORKS_PATH) -mindepth 1 -maxdepth 1 -type d -exec echo add {} \; -exec git -C '{}' add --all . \;
git.forks~%: ## Run a git command on each fork (eg. `make git.forks~pull` pulls the latest version of all local deps from its git remote
find $(FORKS_PATH) -mindepth 1 -maxdepth 1 -type d -exec echo $* {} \; -exec git -C '{}' $* \;
#### TESTING RELATED COMMANDS ####
test: init ## Run tests. You can also run only specific tests, eg: `make test only=forks/bonfire_social/test`
ifeq ($(WITH_DOCKER), total)
docker-compose run web mix test $(only)
else
mix test $(only)
endif
test.stale: init ## Run only stale tests
ifeq ($(WITH_DOCKER), total)
docker-compose run web mix test $(only) --stale
else
mix test $(only) --stale
endif
test.remote: ## Run tests (ignoring changes in local forks)
@make --no-print-directory mix.remote~"test $(only)"
test.watch: init ## Run stale tests, and wait for changes to any module's code, and re-run affected tests
ifeq ($(WITH_DOCKER), total)
docker-compose run web mix test.watch --stale $(only)
else
mix test.watch --stale $(only)
endif
# dev-test-watch: init ## Run tests
# docker-compose run --service-ports -e MIX_ENV=test web iex -S mix phx.server
test.db.reset: init db.pre-migrations ## Create or reset the test DB
ifeq ($(WITH_DOCKER), total)
docker-compose run -e MIX_ENV=test web mix ecto.reset
else
MIX_ENV=test mix ecto.reset
endif
#### RELEASE RELATED COMMANDS (Docker-specific for now) ####
rel.config.prepare: # copy current flavour's config, without using symlinks
cp -rfL $(BONFIRE_FLAVOUR)/config ./data/config
rel-build-no-cache: init config-prepare assets-prepare ## Build the Docker image
rel.build.no-cache: init rel.config.prepare assets.prepare ## Build the Docker image
docker build \
--no-cache \
--build-arg BONFIRE_FLAVOUR=config \
@ -259,7 +258,7 @@ rel-build-no-cache: init config-prepare assets-prepare ## Build the Docker image
-f $(APP_REL_DOCKERFILE) .
@echo Build complete: $(APP_DOCKER_REPO):$(APP_VSN)-release-$(APP_BUILD)
rel-build: init config-prepare assets-prepare ## Build the Docker image using previous cache
rel.build: init rel.config.prepare assets.prepare ## Build the Docker image using previous cache
docker build \
--build-arg BONFIRE_FLAVOUR=config \
--build-arg APP_NAME=$(APP_NAME) \
@ -270,26 +269,95 @@ rel-build: init config-prepare assets-prepare ## Build the Docker image using pr
@echo Build complete: $(APP_DOCKER_REPO):$(APP_VSN)-release-$(APP_BUILD)
@echo "Remember to run make rel-tag-latest or make rel-push"
rel-tag-latest: init ## Add latest tag to last build
rel.tag.latest: init ## Add latest tag to last build
@docker tag $(APP_DOCKER_REPO):$(APP_VSN)-release-$(APP_BUILD) $(APP_DOCKER_REPO):latest
rel-push: init ## Add latest tag to last build and push to Docker Hub
rel.push: init ## Add latest tag to last build and push to Docker Hub
@docker push $(APP_DOCKER_REPO):latest
rel-run: init docker-stop-web ## Run the app in Docker & starts a new `iex` console
rel.run: init docker.stop.web ## Run the app in Docker & starts a new `iex` console
@docker-compose -p $(APP_REL_CONTAINER) -f $(APP_REL_DOCKERCOMPOSE) run --name bonfire_web --service-ports --rm backend bin/bonfire start_iex
rel-run-bg: init docker-stop-web ## Run the app in Docker, and keep running in the background
rel.run.bg: init docker.stop.web ## Run the app in Docker, and keep running in the background
@docker-compose -p $(APP_REL_CONTAINER) -f $(APP_REL_DOCKERCOMPOSE) up -d
rel-stop: ## Run the app in Docker, and keep running in the background
rel.stop: init ## Run the app in Docker, and keep running in the background
@docker-compose -p $(APP_REL_CONTAINER) -f $(APP_REL_DOCKERCOMPOSE) stop
rel-shell: docker-stop-web ## Runs a simple shell inside of the container, useful to explore the image
rel.shell: init docker.stop.web ## Runs a simple shell inside of the container, useful to explore the image
@docker-compose -p $(APP_REL_CONTAINER) -f $(APP_REL_DOCKERCOMPOSE) run --name bonfire_web --service-ports --rm backend /bin/bash
fire: init db
iex -S mix phx.server
sparks: init db
mix test $(only)
#### DOCKER-SPECIFIC COMMANDS ####
services: ## Start background docker services (eg. db and search backends). This is automatically done for you if using Docker.
ifeq ($(WITH_DOCKER), no)
@echo ....
else
docker-compose up -d db search
endif
build: init ## Build the docker image
ifeq ($(WITH_DOCKER), total)
docker-compose build
else
@echo Skip building container...
endif
cmd~%: init ## Run a specific command in the container, eg: `make cmd-messclt` or `make cmd~time` or `make cmd~echo args=hello`
ifeq ($(WITH_DOCKER), total)
docker-compose run --service-ports web $* $(args)
else
@$* $(args)
endif
shell: init ## Open the shell of the Docker web container, in dev mode
@make cmd~bash
docker.stop.web:
@docker stop bonfire_web 2> /dev/null || true
@docker rm bonfire_web 2> /dev/null || true
#### MISC COMMANDS ####
mix~%: init ## Run a specific mix command, eg: `make mix~deps.get` or `make mix~deps.update args=pointers`
ifeq ($(WITH_DOCKER), total)
docker-compose run web mix $* $(args)
else
mix $* $(args)
endif
mix.remote~%: init ## Run a specific mix command, while ignoring any deps cloned into ./forks, eg: `make mix~deps.get` or `make mix~deps.update args=pointers`
ifeq ($(WITH_DOCKER), total)
docker-compose run -e WITH_FORKS=0 web mix $* $(args)
else
WITH_FORKS=0 mix $* $(args)
endif
licenses: init
@make --no-print-directory mix.remote~licenses
assets.prepare:
cp lib/*/*/overlay/* rel/overlays/ 2> /dev/null || true
db.pre-migrations: ## Workaround for some issues running migrations
touch deps/*/lib/migrations.ex 2> /dev/null || echo "continue"
touch forks/*/lib/migrations.ex 2> /dev/null || echo "continue"
touch priv/repo/* 2> /dev/null || echo "continue"
git-publish:
chmod +x git-publish.sh
./git-publish.sh
deps.git.fix: ## Run a git command on each dep, to ignore chmod changes
find ./deps -mindepth 1 -maxdepth 1 -type d -exec git -C '{}' config core.fileMode false \;
find ./forks -mindepth 1 -maxdepth 1 -type d -exec git -C '{}' config core.fileMode false \;
git.merge~%: ## Draft-merge another branch, eg `make git-merge-with-valueflows-api` to merge branch `with-valueflows-api` into the current one
git merge --no-ff --no-commit $*
git.conflicts: ## Find any git conflicts in ./forks
find $(FORKS_PATH) -mindepth 1 -maxdepth 1 -type d -exec echo add {} \; -exec git -C '{}' diff --name-only --diff-filter=U \;
pull:
git pull

134
docs/GRAPHQL.md Normal file
View file

@ -0,0 +1,134 @@
# Bonfire GraphQL Guide
## GraphQL Introduction
Go to http://your-app-url/api/ to start playing with the GraphQL API. The GraphiQL UI should autocomplete types, queries and mutations for you, and you can also explore the schema there.
Let's start with a simple GraphQL thoeretical query:
```graphql
query {
greetings(limit: 10) {
greeting
to {
name
}
}
}
```
Let's break this apart:
- `query {}` is how you retrieve information from GraphQL.
- `greetings` is a `field` within the query.
- `greetings` takes a `limit` argument, a positive integer.
- `greetings` has two fields, `greeting` and `to`.
- `to` has one `field`, `name`.
This query is asking for a list of (up to) 10 greetings and the people
they are for. Notice that the result of both `greetings` and `to` are
map/object structures with their own fields and that if the type has
multiple fields, we can select more than one field.
Here is some possible data we could get returned
```elixir
%{greetings: [
%{greeting: "hello", to: %{ name: "dear reader"}}, # english
%{greeting: "hallo", to: %{ name: "beste lezer"}}, # dutch
]}
```
Where is the magic? Typically an object type will reside in its own
table in the database, so this query is actually querying two tables
in one go. In fact, given a supporting schema, you can nest queries
arbitrarily and the backend will figure out how to run them.
The fact that you can represent arbitrarily complex queries puts quite
a lot of pressure on the backend to make it all efficient. This is
still a work in progress at this time.
## Absinthe Introduction
Every `field` is filled by a resolver. Let's take our existing query
and define a schema for it in Absinthe's query DSL:
```elixir
defmodule MyApp.Schema do
# the schema macro language
use Absinthe.Schema.Notation
# where we will actually resolve the fields
alias MyApp.Resolver
# Our user object is pretty simple, just a name
object :user do
field :name, non_null(:string)
end
# This one is slightly more complicated, we have that nested `to`
object :greeting do
# the easy one
field :greeting, non_null(:string)
# the hard one. `edge` is the term for when we cross an object boundary.
field :to, non_null(:user), do: resolve(&Resolver.to_edge/3)
end
# something to put our top level queries in, because they're just fields too!
object :queries do
field :greetings, non_null(list_of(non_null(:string))) do
arg :limit, :integer # optional
resolve &Resolver.greetings/2 # we need to manually process this one
end
end
end
```
There are a couple of interesting things about this:
- Sprinklings of `not_null` to require that values be present (if you
don't return them, absinthe will get upset).
- Only two fields have explicit resolvers. Anything else will default
to a map key lookup (which is more often than not what you want).
- `greeting.to_edge` has a `/3` resolver and `queries.greetings` a
`/2` resolver.
To understand the last one (and partially the middle one), we must
understand how resolution works and what a parent is. The best way of
doing that is probably to look at the resolver code:
```elixir
defmodule MyApp.Resolver do
# For purposes of this, we will just fake the data out
defp greetings_data() do
[ %{greeting: "hello", to: %{ name: "dear reader"}}, # english
%{greeting: "hallo", to: %{ name: "beste lezer"}}, # dutch
]
end
# the /2 resolver takes only arguments (which in this case is just limit)
# and an info (which we'll explain later)
def greetings(%{limit: limit}, _info) when is_integer(limit) and limit > 0 do
{:ok, Enum.take(greetings_data(), limit)} # absinthe expects an ok/error tuple
end
def greetings(_args, _info), do: {:ok, greetings_data()} # no limit
# the /3 resolver takes an additional parent argument in first position.
# `parent` here will be the `greeting` we are resolving `to` for.
def to_edge(parent, args, info), do: Map.get(parent, :to)
end
```
The keen-eyed amongst you may have noticed I said the default resolver
is a map lookup and our `to_edge/3` is a map lookup too, so
technically we didn't need to write it. But then you wouldn't have an
example of a `/3` resolver! In most of the app, these will be querying
from the database, not looking up in a constant.
So for every field, a resolver function is run. It defaults to a map
lookup, but you can override it with `resolve/1`. It may or may not
have arguments. And all absinthe resolvers return an ok/error tuple.
## Patterns

206
docs/HACKING.md Normal file
View file

@ -0,0 +1,206 @@
# Development guide
_These instructions are for hacking on Bonfire. If you wish to deploy in production, please refer to our [Deployment Guide](./DEPLOY.md)!_
Hello, potential contributor! :-)
This is a work in progress guide to getting up and running as a developer. Please ask questions in the issue tracker if something is not clear.
Happy hacking!
## Getting set up
There are three main options depending on your needs and preferences.
You first need to set set some configuration regardless of which option you choose.
### Configuration
- Run this command to initialise some default config (.env files which won't be checked into git):
`make pre-config`
- Edit the config (especially the secrets) in these files:
- `config/dev/secrets.env`
- `config/dev/public.env`
### Option A - the entry way (fully managed via docker-compose, recommended when you're first exploring)
- Dependencies:
- `make`
- Docker
- Docker Compose (recent version)
- Make sure you've edited your .env files (see above) before getting started and proceed to Hello world!
### Option B - the easy way (with docker-managed database & search index, recommended for active development)
- Dependencies:
- `make`
- Recent versions of elixir (1.11+) and OTP/erlang (23+)
- Docker
- Docker Compose (recent version)
- Set an environment variable to indicate your choice: `export WITH_DOCKER=partial`
- Make sure you've edited your .env files (see above) before getting started and proceed to Hello world!
### Option C - the bare metal (if you don't use docker)
- Dependencies:
- `make`
- Recent versions of elixir (1.11+) and OTP/erlang (23+)
- Postgres 12+ (or Postgis if using the bonfire_geolocate extension)
- Meili Search (optional)
- You will need to set the relevant environment variables in the .env files (see above) to match your local install of Postgres.
- If you want search capabilities, you'll also need to setup a Meili server and set the relevant env variables as well.
- Set an environment variable to indicate your choice: `export WITH_DOCKER=no` and proceed to Hello world!
## Hello world!
- From a fresh checkout of this repository, this command will fetch the app's dependencies and setup the database (the same commands apply for all three options above):
```
make setup
```
- You should then be able to run the app with:
```
make dev
```
- See the `make` commands bellow for more things you may want to do.
## Onboarding
By default, the back-end listens on port 4000 (TCP), so you can access it on http://localhost:4000/
If you haven't set up transactional emails, while in development, you will see a signup confirmation path appear in the iex console.
Note that the first account to be registered is automatically an instance admin.
## Documentation
The code is somewhat documented inline. You can generate HTML docs (using `Exdoc`) by running `mix docs`.
## Make commands
Run `make` followed by any of these commands when appropriate rather than directly using the equivalent commands like `mix`, `docker`, `docker-compose`, etc. For example, `make setup` will get you started, and `make dev` will run the app.
You can first set the env variable DOCKER to control which mode these commands will assume you're using. Here are your options:
- `WITH_DOCKER=total` : use docker for everything (default)
- `WITH_DOCKER=partial` : use docker for services like the DB
- `WITH_DOCKER=easy` : use docker for services like the DB & compiled utilities like messctl
- `WITH_DOCKER=no` : please no
make help Makefile commands help **(run this to get more up-to-date commands and help information than available in this document)**
make env.exports Display the vars from dotenv files that you need to load in your environment
make setup First run - prepare environment and dependencies
make dev Run the app in development
make dev.bg Run the app in dev mode, as a background service
make db.reset Reset the DB (caution: this means DATA LOSS)
make db.rollback Rollback previous DB migration (caution: this means DATA LOSS)
make db.rollback.all Rollback ALL DB migrations (caution: this means DATA LOSS)
make update Update the app and all dependencies/extensions/forks, and run migrations
make update.app Update the app and Bonfire extensions in ./deps
make update.deps.bonfire Update to the latest Bonfire extensions in ./deps
make update.deps.all Update evey single dependency (use with caution)
make update.dep~% Update a specify dep (eg. `make update.dep~pointers`)
make update.forks Pull the latest commits from all ./forks
make deps.get Fetch locked version of non-forked deps
make dep.clone.local Clone a git dep and use the local version, eg: `make dep.clone.local dep="bonfire_me" repo=https://github.com/bonfire-networks/bonfire_me`
make deps.clone.local.all Clone all bonfire deps / extensions
make dep.go.local~% Switch to using a local path, eg: make dep.go.local~pointers
make dep.go.local.path Switch to using a local path, specifying the path, eg: make dep.go.local dep=pointers path=./libs/pointers
make dep.go.git Switch to using a git repo, eg: make dep.go.git dep="pointers" repo=https://github.com/bonfire-networks/pointers (specifying the repo is optional if previously specified)
make dep.go.hex Switch to using a library from hex.pm, eg: make dep.go.hex dep="pointers" version="~> 0.2" (specifying the version is optional if previously specified)
make dep.hex~% add/enable/disable/delete a hex dep with messctl command, eg: `make dep.hex.enable dep=pointers version="~> 0.2"
make dep.git~% add/enable/disable/delete a git dep with messctl command, eg: `make dep.hex.enable dep=pointers repo=https://github.com/bonfire-networks/pointers#main
make dep.local~% add/enable/disable/delete a local dep with messctl command, eg: `make dep.hex.enable dep=pointers path=./libs/pointers
make messctl~% Utility to manage the deps in deps.hex, deps.git, and deps.path (eg. `make messctl~help`)
make contrib.forks Push all changes to the app and extensions in ./forks
make contrib.release Push all changes to the app and extensions in ./forks, increment the app version number, and push a new version/release
make contrib.app.up Update ./deps and push all changes to the app
make contrib.app.release Update ./deps, increment the app version number and push
make git.forks.add Run the git add command on each fork
make git.forks~% Run a git command on each fork (eg. `make git.forks~pull` pulls the latest version of all local deps from its git remote
make test Run tests. You can also run only specific tests, eg: `make test only=forks/bonfire_social/test`
make test.stale Run only stale tests
make test.remote Run tests (ignoring changes in local forks)
make test.watch Run stale tests, and wait for changes to any module's code, and re-run affected tests
make test.db.reset Create or reset the test DB
make rel.build Build the Docker image using previous cache
make rel.tag.latest Add latest tag to last build
make rel.push Add latest tag to last build and push to Docker Hub
make rel.run Run the app in Docker & starts a new `iex` console
make rel.run.bg Run the app in Docker, and keep running in the background
make rel.stop Run the app in Docker, and keep running in the background
make rel.shell Runs a simple shell inside of the container, useful to explore the image
make services Start background docker services (eg. db and search backends). This is automatically done for you if using Docker.
make build Build the docker image
make cmd~% Run a specific command in the container, eg: `make cmd-messclt` or `make cmd~time` or `make cmd~echo args=hello`
make shell Open the shell of the Docker web container, in dev mode
make mix~% Run a specific mix command, eg: `make mix~deps.get` or `make mix~deps.update args=pointers`
make mix.remote~% Run a specific mix command, while ignoring any deps cloned into ./forks, eg: `make mix~deps.get` or `make mix~deps.update args=pointers`
make deps.git.fix Run a git command on each dep, to ignore chmod changes
make git.merge~% Draft-merge another branch, eg `make git-merge-with-valueflows-api` to merge branch `with-valueflows-api` into the current one
make git.conflicts Find any git conflicts in ./forks
## What happens when I get this error?
### (Mix) Package fetch failed
Example:
```
** (Mix) Package fetch failed and no cached copy available (https://repo.hex.pm/tarballs/distillery-2.0.12.tar)
```
In this case, distillery (as an example of a dependency) made a new release and retired the old release from hex. The new version (`2.0.14`) is quite close to the version we were depending on (`2.0.12`), so we chose to upgrade:
```shell
mix deps.update distillery
```
This respects the version bounds in `mix.exs` (`~> 2.0`), so increment that if required.
### `(DBConnection.ConnectionError) tcp recv: closed`
Example:
```
** (DBConnection.ConnectionError) tcp recv: closed (the connection was closed by the pool, possibly due to a timeout or because the pool has been terminated)
```
In this case, the seeds were unable to complete because a query took too long to execute on your machine. You can configure the timeout to be larger in the `dev` environment:
1. Open `config/dev.exs` in your editor.
2. Find the database configuration (search for `Bonfire.Repo`).
3. Add `timeout: 60_000` to the list of options:
```
config :bonfire, Bonfire.Repo,
timeout: 60_000,
[...]
```

110
docs/MRF.md Normal file
View file

@ -0,0 +1,110 @@
# Message Rewrite Facility
**WARNING: Due to how this app currently handles its configuration, MRF is only usable if you're building your own docker image.**
The Message Rewrite Facility (MRF) is a subsystem that is implemented as a series of hooks that allows the administrator to rewrite or discard messages.
Possible uses include:
- marking incoming messages with media from a given account or instance as sensitive
- rejecting messages from a specific instance
- rejecting reports (flags) from a specific instance
- removing/unlisting messages from the public timelines
- removing media from messages
- sending only public messages to a specific instance
The MRF provides user-configurable policies. The default policy is `NoOpPolicy`, which disables the MRF functionality. Bonfire also includes an easy to use policy called `SimplePolicy` which maps messages matching certain pre-defined criterion to actions built into the policy module.
It is possible to use multiple, active MRF policies at the same time.
## Using `SimplePolicy`
`SimplePolicy` is capable of handling most common admin tasks.
To use `SimplePolicy`, you must enable it. Do so by adding the following to your `:instance` config object, so that it looks like this:
```
config :bonfire, :instance,
[...]
rewrite_policy: ActivityPub.MRF.SimplePolicy
```
Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_simple` config object. These groups are:
- `media_removal`: Servers in this group will have media stripped from incoming messages.
- `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media.
- `reject`: Servers in this group will have their messages rejected.
- `report_removal`: Servers in this group will have their reports (flags) rejected.
Servers should be configured as lists.
### Example
This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com`and block reports (flags) from `whiny.whiner`:
```
config :bonfire, :instance,
rewrite_policy: [ActivityPub.MRF.SimplePolicy]
config :bonfire, :mrf_simple,
media_removal: ["illegalporn.biz"],
media_nsfw: ["porn.biz", "porn.business"],
reject: ["spam.com"],
report_removal: ["whiny.whiner"]
```
### Use with Care
The effects of MRF policies can be very drastic. It is important to use this functionality carefully. Always try to talk to an admin before writing an MRF policy concerning their instance.
## Writing your own MRF Policy
As discussed above, the MRF system is a modular system that supports pluggable policies. This means that an admin may write a custom MRF policy in Elixir or any other language that runs on the Erlang VM, by specifying the module name in the `rewrite_policy` config setting.
For example, here is a sample policy module which rewrites all messages to "new message content":
```elixir
# This is a sample MRF policy which rewrites all Notes to have "new message
# content."
defmodule Site.RewritePolicy do
@behavior ActivityPub.MRF
# Catch messages which contain Note objects with actual data to filter.
# Capture the object as `object`, the message content as `content` and the
# message itself as `message`.
@impl true
def filter(%{"type" => Create", "object" => {"type" => "Note", "content" => content} = object} = message)
when is_binary(content) do
# Subject / CW is stored as summary instead of `name` like other AS2 objects
# because of Mastodon doing it that way.
summary = object["summary"]
# Message edits go here.
content = "new message content"
# Assemble the mutated object.
object =
object
|> Map.put("content", content)
|> Map.put("summary", summary)
# Assemble the mutated message.
message = Map.put(message, "object", object)
{:ok, message}
end
# Let all other messages through without modifying them.
@impl true
def filter(message), do: {:ok, message}
end
```
If you save this file as `lib/site/mrf/rewrite_policy.ex`, it will be included when you next rebuild Bonfire. You can enable it in the configuration like so:
```
config :bonfire, :instance,
rewrite_policy: [
ActivityPub.MRF.SimplePolicy,
Site.RewritePolicy
]
```

9
docs/index.html Normal file
View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Bonfire Documentation</title>
<meta http-equiv="refresh" content="0; url=./exdoc/readme.html" />
</head>
<body></body>
</html>

View file

@ -13,7 +13,7 @@ SEARCH_MEILI_INSTANCE=http://search:7700
# require an email address to be invited before being able to sign up
INVITE_ONLY=true
# a name and tagline for your instance
INSTANCE_DESCRIPTION=An instance of Bonfire, a federated app ecosystem for open and cooperative networks
INSTANCE_DESCRIPTION="An instance of Bonfire, a federated app ecosystem for open and cooperative networks"
# uncomment in order to NOT automatically change the database schema when you upgrade the app
# DISABLE_DB_AUTOMIGRATION=true
# max file upload size (default is 20 meg)

View file

@ -13,7 +13,7 @@ SEARCH_MEILI_INSTANCE=http://search:7700
# require an email address to be invited before being able to sign up
INVITE_ONLY=true
# a name and tagline for your instance
INSTANCE_DESCRIPTION=An instance of Bonfire, a federated app ecosystem for open and cooperative networks
INSTANCE_DESCRIPTION="An instance of Bonfire, a federated app ecosystem for open and cooperative networks"
# uncomment in order to NOT automatically change the database schema when you upgrade the app
# DISABLE_DB_AUTOMIGRATION=true
# max file upload size (default is 20 meg)

View file

@ -13,7 +13,7 @@ SEARCH_MEILI_INSTANCE=http://search:7700
# require an email address to be invited before being able to sign up
INVITE_ONLY=true
# a name and tagline for your instance
INSTANCE_DESCRIPTION=An instance of Bonfire, a federated app ecosystem for open and cooperative networks
INSTANCE_DESCRIPTION="An instance of Bonfire, a federated app ecosystem for open and cooperative networks"
# uncomment in order to NOT automatically change the database schema when you upgrade the app
# DISABLE_DB_AUTOMIGRATION=true
# max file upload size (default is 20 meg)

View file

@ -82,6 +82,7 @@ defmodule Bonfire.MixProject do
{:zest, "~> 0.1"},
{:grumble, "~> 0.1.3", only: [:test], override: true},
{:bonfire_api_graphql, git: "https://github.com/bonfire-networks/bonfire_api_graphql", branch: "main", only: [:test]},
{:mix_test_watch, "~> 1.0", only: :dev, runtime: false},
# list dependencies & licenses
{:licensir, only: :dev, runtime: false,

View file

@ -27,7 +27,7 @@
"bonfire_search": {:git, "https://github.com/bonfire-networks/bonfire_search", "8ff913fb0248b591d364e21d81b83171fccf8a2d", [branch: "main"]},
"bonfire_social": {:git, "https://github.com/bonfire-networks/bonfire_social", "8a6180b0b205315985036b54430a446f66508bdc", [branch: "main"]},
"bonfire_tag": {:git, "https://github.com/bonfire-networks/bonfire_tag", "bd7d97ad2d6e41e196f4e2afb232fe07f24b4fd3", [branch: "main"]},
"bonfire_ui_coordination": {:git, "https://github.com/bonfire-networks/bonfire_ui_coordination", "b1714237882293f6fe0f19ac61111bc0871403e5", [branch: "main"]},
"bonfire_ui_coordination": {:git, "https://github.com/bonfire-networks/bonfire_ui_coordination", "bb928613c868c505eb6bdb69c66155aa7dd92e6a", [branch: "main"]},
"bonfire_ui_reflow": {:git, "https://github.com/bonfire-networks/bonfire_ui_reflow", "6400d1858e1686ac9c2e21471df3876839ec18e0", [branch: "main"]},
"bonfire_ui_social": {:git, "https://github.com/bonfire-networks/bonfire_ui_social", "32085859421dcb49c790cc72a0678629ba210d51", [branch: "main"]},
"bonfire_ui_valueflows": {:git, "https://github.com/bonfire-networks/bonfire_ui_valueflows", "8a153fd601190f063f131158732d283b7c09eab1", [branch: "main"]},
@ -88,6 +88,7 @@
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"mix_test_watch": {:hex, :mix_test_watch, "1.0.2", "34900184cbbbc6b6ed616ed3a8ea9b791f9fd2088419352a6d3200525637f785", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "47ac558d8b06f684773972c6d04fcc15590abdb97aeb7666da19fcbfdc441a07"},
"mock": {:hex, :mock, "0.3.7", "75b3bbf1466d7e486ea2052a73c6e062c6256fb429d6797999ab02fa32f29e03", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "4da49a4609e41fd99b7836945c26f373623ea968cfb6282742bcb94440cf7e5c"},
"neuron": {:hex, :neuron, "5.0.0", "64c6b14138e4f6e61a55abb0bb95659aa193145ed9baf80b4e760d4c189f6c6f", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "7597308e22e34f77acaadbae3da63974edc9e4041ef7f7154d45ce80c0e33d00"},
"nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"},