OTP release

This commit is contained in:
Mayel 2021-04-08 20:19:52 +02:00
parent bf46e25ed0
commit 647db81d13
29 changed files with 453 additions and 96 deletions

View file

@ -7,7 +7,16 @@ docs/exdoc
.git
.gitignore
.config
Dockerfile
.cache
.elixir_ls
.local
.sobelow
*secrets*
config/dev
config/prod
*.env*
Docker*
docker*
Makefile
README*
priv/static

2
.gitignore vendored
View file

@ -43,6 +43,8 @@ npm-debug.log
# App and user data
/data/
config/.env.secrets
config/dev
config/prod
# user-local overrides for mess
deps.path

104
Dockerfile.release Normal file
View file

@ -0,0 +1,104 @@
# The version of Elixir to use
# ATTENTION: when changing Elixir version, make sure to update the `ALPINE_VERSION` arg
# as well as the Elixir version in mix.exs and .gitlab-ci.yml and Dockerfile.dev and .tool-versions
ARG ELIXIR_VERSION=1.11
# The version of Alpine to use for the final image
# This should match the version of Alpine that the current elixir & erlang images (in Step 1) use.
# To find this you need to:
# 1. Locate the dockerfile for the elixir image to get the erlang image version
# e.g. https://github.com/erlef/docker-elixir/blob/master/1.11/Dockerfile
# 2. Locate the corresponding erlang dockerfile and copy the alpine version indicated there below:
# e.g. https://github.com/erlang/docker-erlang-otp/blob/master/23/alpine/Dockerfile
ARG ALPINE_VERSION=3.13
# The following are build arguments used to change variable parts of the image, they should be set as env variables.
# The name of your application/release (required)
ARG APP_NAME
# The version of the application we are building (required)
ARG APP_VSN
# Step 1 - Build our app
FROM elixir:${ELIXIR_VERSION}-alpine as builder
ENV HOME=/opt/app/ TERM=xterm MIX_ENV=prod
WORKDIR $HOME
# useful deps
RUN apk add --no-cache curl git rust cargo npm
# dependencies for comeonin
RUN apk add --no-cache make gcc libc-dev
# cache JS deps
COPY assets/package* ./assets/
RUN npm install --prefix assets
# Cache elixir deps
COPY mix.exs mess.exs deps.hex mix.lock ./
RUN mix do local.hex --force, local.rebar --force
RUN mix do deps.get --only prod
# Compile elixir deps (warning this makes the assumption that no Bonfire extensions are coming from Hex. otherwise this should be done only after copying config)
# RUN MIX_ENV=prod mix do deps.compile
# git deps (i.e. Bonfire extensions)
COPY deps.git ./
RUN mix do deps.get --only prod
# Uncomment these two lines only if you want to include locally-forked deps in the release
COPY forks/ deps.path ./
RUN mix do deps.get --only prod
# we need config before compiling Bonfire extensions
COPY config/ ./
# Compile Bonfire extensions
RUN MIX_ENV=prod mix do deps.compile
# COPY lib/ assets/ priv/ rel/ ./ # causes a webpack error
COPY . .
# prepare static assets
RUN npm run deploy --prefix ./assets
RUN MIX_ENV=prod RELEASING=1 mix phx.digest
# build final OTP release
RUN MIX_ENV=prod RELEASING=1 mix release
# Step 2 - Prepare the server image
# From this line onwards, we're in a new image, which will be the image used in production
FROM alpine:${ALPINE_VERSION}
# The name of your application/release (required)
ARG APP_NAME
ARG APP_VSN
ARG APP_BUILD
ARG PROXY_FRONTEND_URL
ENV APP_NAME=${APP_NAME} APP_VSN=${APP_VSN} APP_REVISION=${APP_VSN}-${APP_BUILD}
# Essentials
RUN apk add --update --no-cache \
mailcap \
ca-certificates \
openssh-client \
openssl-dev \
# ^ for HTTPS
git \
build-base \
# ^ required by tree_magic
tzdata \
gettext \
# ^ localisation
bash \
curl
WORKDIR /opt/app
# install app
COPY --from=builder /opt/app/_build/prod/rel/bonfire /opt/app
# start
CMD ["./bin/bonfire", "start"]

View file

@ -1,14 +1,29 @@
.PHONY: setup updates db-reset build dev shell
LIBS_PATH=./forks/
ORG_NAME=bonfirenetworks
APP_FLAVOUR ?= `git branch --show-current`
APP_NAME=bonfire-$(APP_FLAVOUR)
UID := $(shell id -u)
GID := $(shell id -g)
APP_REL_CONTAINER="$(ORG_NAME)_$(APP_NAME)_release"
APP_REL_DOCKERFILE=Dockerfile.release
APP_REL_DOCKERCOMPOSE=docker-compose.release.yml
APP_VSN ?= `grep 'version:' mix.exs | cut -d '"' -f2`
APP_BUILD ?= `git rev-parse --short HEAD`
APP_DOCKER_REPO="$(ORG_NAME)/$(APP_NAME)"
export UID
export GID
init:
@echo "Light that fire..."
@echo "Light that fire... $(APP_NAME):$(APP_VSN)-$(APP_BUILD)"
@mkdir -p config/prod
@mkdir -p config/dev
@cp -n config/templates/public.env config/dev/ | true
@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
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}'
@ -152,17 +167,15 @@ deps.update-%: init bonfire-pre-update
docker-compose run web mix deps.update $*
make bonfire-post-updates
dev: init ## Run the app with Docker
docker rm bonfire_web 2> /dev/null || true
dev: init docker-stop-web ## Run the app with Docker
docker-compose run --name bonfire_web --service-ports web
dev-bg: init ## Run the app in dev mode, in the background
docker stop bonfire_web 2> /dev/null || true
docker rm bonfire_web 2> /dev/null || true
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
rm-%:
docker-compose rm -s $*
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 . \;
@ -181,3 +194,45 @@ licenses: init
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)
assets-prepare:
cp lib/*/*/overlay/* rel/overlays/ 2> /dev/null || true
rel-build-no-cache: init assets-prepare ## Build the Docker image
docker build \
--no-cache \
--build-arg APP_NAME=$(APP_NAME) \
--build-arg APP_VSN=$(APP_VSN) \
--build-arg APP_BUILD=$(APP_BUILD) \
-t $(APP_DOCKER_REPO):$(APP_VSN)-release-$(APP_BUILD) \
-f $(APP_REL_DOCKERFILE) .
@echo Build complete: $(APP_DOCKER_REPO):$(APP_VSN)-release-$(APP_BUILD)
rel-build: init assets-prepare ## Build the Docker image using previous cache
docker build \
--build-arg APP_NAME=$(APP_NAME) \
--build-arg APP_VSN=$(APP_VSN) \
--build-arg APP_BUILD=$(APP_BUILD) \
-t $(APP_DOCKER_REPO):$(APP_VSN)-release-$(APP_BUILD) \
-f $(APP_REL_DOCKERFILE) .
@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
@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
@docker push $(APP_DOCKER_REPO):latest
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
@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
@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
@docker-compose -p $(APP_REL_CONTAINER) -f $(APP_REL_DOCKERCOMPOSE) run --name bonfire_web --service-ports --rm backend /bin/bash

View file

@ -7,10 +7,15 @@ module.exports = {
// purgeLayersByDefault: true,
},
purge: {
// enabled: true,
enabled: true,
content: [
'../forks/**/**/**/*.leex',
'./lib/web/**/*.leex',
'../forks/**/lib/**/*.leex',
'../forks/**/lib/**/**/*.leex',
'../forks/**/lib/**/**/**/*.leex',
'../deps/bonfire_**/lib/**/*.leex',
'../deps/bonfire_**/lib/**/**/*.leex',
'../deps/bonfire_**/lib/**/**/**/*.leex',
'../lib/web/**/*.leex',
]
},
darkMode: 'class',

View file

@ -1,6 +0,0 @@
WEB_PUSH_SUBJECT=mailto:administrator@example.com
WEB_PUSH_PUBLIC_KEY=xyz
WEB_PUSH_PRIVATE_KEY=abc
POSTGRES_PASSWORD=postgres
GEOLOCATE_OPENCAGEDATA=
GITHUB_TOKEN=xyz

View file

@ -1,4 +1,4 @@
use Mix.Config
import Config
config :activity_pub, :adapter, Bonfire.Federate.ActivityPub.Adapter
config :activity_pub, :repo, Bonfire.Repo

View file

@ -1,11 +1,11 @@
use Mix.Config
import Config
config :activity_pub, ActivityPub.TestRepo,
adapter: Ecto.Adapters.Postgres,
username: "postgres",
password: System.get_env("POSTGRES_PASSWORD", "postgres"),
database: "bonfire_test#{System.get_env("MIX_TEST_PARTITION")}",
hostname: System.get_env("DATABASE_HOST") || "localhost",
hostname: System.get_env("POSTGRES_HOST") || "localhost",
pool: Ecto.Adapters.SQL.Sandbox,
pool_size: 60

View file

@ -1,4 +1,4 @@
use Mix.Config
import Config
config :bonfire_boundaries,
enabled: true

View file

@ -1,4 +1,4 @@
use Mix.Config
import Config
config :bonfire_classify,
templates_path: "lib"

View file

@ -1,4 +1,4 @@
use Mix.Config
import Config
alias Bonfire.Federate.ActivityPub.Adapter
alias Bonfire.Data.AccessControl.{

View file

@ -1,4 +1,4 @@
use Mix.Config
import Config
config :bonfire_geolocate,
templates_path: "lib"

View file

@ -1,4 +1,4 @@
use Mix.Config
import Config
config :bonfire_quantify,
templates_path: "lib"

View file

@ -1,4 +1,4 @@
use Mix.Config
import Config
config :bonfire_search,
disable_indexing: System.get_env("SEARCH_INDEXING_DISABLED", "false"),

View file

@ -1,4 +1,4 @@
use Mix.Config
import Config
config :bonfire_social,
enabled: true

View file

@ -1,4 +1,4 @@
use Mix.Config
import Config
config :bonfire_tag,
templates_path: "lib"

View file

@ -1,4 +1,4 @@
use Mix.Config
import Config
config :bonfire_valueflows,
valid_agent_schemas: [Bonfire.Data.Identity.User]

49
config/deploy/Caddyfile2 Normal file
View file

@ -0,0 +1,49 @@
# config for Caddy v2
:80 # comment this line and uncomment the next 4 to enable HTTPS/SSL
# :4443
# tls {
# on_demand
# }
# define paths that should be proxied to Elixir backend
@backend {
path / /api* /pub* /oauth* /.well-known/webfinger* /.well-known/nodeinfo* /phoenix* /live* /instance* /~* /!* /@* /&* /+* /css/* /js/* /fonts/* /images/*
}
route @backend {
# backend proxying
reverse_proxy backend:4000
}
route /uploads/* {
root * /frontend/
# fallback to index (for single page site)
try_files {path}
# serve static files
file_server
}
route /frontend/* {
root /frontend /frontend
# fallback to React FE
try_files {path} /frontend/index.html
# serve static frontend
file_server
}
# options
encode gzip
handle_errors
log

View file

@ -1,17 +1,15 @@
import Config
alias Bonfire.{Mailer, Repo, Web.Endpoint}
config :bonfire, Mailer,
config :bonfire, Bonfire.Mailer,
adapter: Bamboo.LocalAdapter
config :bonfire, Bonfire.Repo,
username: "postgres",
username: System.get_env("POSTGRES_USER", "postgres"),
password: System.get_env("POSTGRES_PASSWORD", "postgres"),
database: "bonfire_dev",
hostname: System.get_env("DATABASE_HOST") || "localhost",
show_sensitive_data_on_connection_error: true,
pool_size: 10
database: System.get_env("POSTGRES_DB", "bonfire_dev"),
hostname: System.get_env("POSTGRES_HOST") || "localhost",
# show_sensitive_data_on_connection_error: true,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
config :bonfire, Bonfire.Web.Endpoint,
http: [port: 4000],

View file

@ -1,5 +1,9 @@
import Config
config :bonfire, Bonfire.Mailer,
# set what service you want to use to send emails, from these: https://github.com/thoughtbot/bamboo#available-adapters
adapter: Bamboo.LocalAdapter
# For production, don't forget to configure the url host
# to something meaningful, Phoenix uses this information
# when generating URLs.
@ -10,7 +14,7 @@ import Config
# which you should run after static files are built and
# before starting your production server.
config :bonfire, Bonfire.Web.Endpoint,
url: [host: "example.com", port: 80],
url: [host: "localhost", port: 4000],
cache_static_manifest: "priv/static/cache_manifest.json"
# Do not print debug messages in production
@ -52,4 +56,4 @@ config :logger, level: :info
# Finally import the config/prod.secret.exs which loads secrets
# and configuration from environment variables.
import_config "prod.secret.exs"
# import_config "prod.secret.exs"

View file

@ -1,41 +0,0 @@
# In this file, we load production configuration and secrets
# from environment variables. You can also hardcode secrets,
# although such is generally not recommended and you have to
# remember to add this file to your .gitignore.
import Config
database_url =
System.get_env("DATABASE_URL") ||
raise """
environment variable DATABASE_URL is missing.
For example: ecto://USER:PASS@HOST/DATABASE
"""
config :bonfire, Bonfire.Repo,
# ssl: true,
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
secret_key_base =
System.get_env("SECRET_KEY_BASE") ||
raise """
environment variable SECRET_KEY_BASE is missing.
You can generate one by calling: mix phx.gen.secret
"""
config :bonfire, Bonfire.Web.Endpoint,
http: [
port: String.to_integer(System.get_env("PORT") || "4000"),
transport_options: [socket_opts: [:inet6]]
],
secret_key_base: secret_key_base
# ## Using releases (Elixir v1.9+)
#
# If you are doing OTP releases, you need to instruct Phoenix
# to start each relevant endpoint:
#
# config :bonfire, Bonfire.Web.Endpoint, server: true
#
# Then you can assemble a release by calling `mix release`.
# See `mix help release` for more information.

58
config/runtime.exs Normal file
View file

@ -0,0 +1,58 @@
# In this file, we load production configuration and secrets
# from environment variables. You can also hardcode secrets,
# although such is generally not recommended and you have to
# remember to add this file to your .gitignore.
import Config
System.get_env("RELEASING") || System.get_env("DATABASE_URL") || System.get_env("POSTGRES_PASSWORD") ||
raise """
environment variable DATABASE_URL is missing.
For example: ecto://USER:PASS@HOST/DATABASE
You can also set the host/user/db/password individually.
"""
if System.get_env("DATABASE_URL") do
config :bonfire, Bonfire.Repo,
url: System.get_env("DATABASE_URL"),
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
else
config :bonfire, Bonfire.Repo,
# ssl: true,
username: System.get_env("POSTGRES_USER", "postgres"),
password: System.get_env("POSTGRES_PASSWORD", "postgres"),
database: System.get_env("POSTGRES_DB", "bonfire_prod"),
hostname: System.get_env("POSTGRES_HOST") || "localhost",
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
end
secret_key_base =
System.get_env("RELEASING") || System.get_env("SECRET_KEY_BASE") ||
raise """
environment variable SECRET_KEY_BASE is missing.
You can generate one by calling: mix phx.gen.secret
"""
host = System.get_env("HOSTNAME") || "localhost"
port = String.to_integer(System.get_env("PORT") || "4000")
config :bonfire, Bonfire.Web.Endpoint,
url: [
host: host,
port: port
],
http: [
port: port
],
secret_key_base: secret_key_base
# ## Using releases (Elixir v1.9+)
#
# If you are doing OTP releases, you need to instruct Phoenix
# to start each relevant endpoint:
#
config :bonfire, Bonfire.Web.Endpoint, server: true
#
# Then you can assemble a release by calling `mix release`.
# See `mix help release` for more information.

View file

@ -0,0 +1,32 @@
# COPY this file to /config/{dev|prod}/secrets.env and change ALL the values
# make sure you change everything to your own secrets!
# and do not check this into git or any public host
# for sessions/cookies
SECRET_KEY_BASE="xx/you-should-put-a-secure-string-here/abcd"
# database access
POSTGRES_USER=postgres
POSTGRES_DB=bonfire_db
POSTGRES_PASSWORD=put_a_secure_db_pw_here
# signup to mailgun.com and edit with your domain and API key
MAIL_DOMAIN=mailg.mydomain.net
MAIL_KEY=123
# password for the search index
MEILI_MASTER_KEY=key-to-protect-search-indexing-here
# password for the default admin user if you run the seeds
SEEDS_USER=root
SEEDS_PW=password
# backend stuff
ERLANG_COOKIE=bonfire_cookie
# Bonfire extensions configs
WEB_PUSH_SUBJECT=mailto:administrator@mydomain.net
WEB_PUSH_PUBLIC_KEY=xyz
WEB_PUSH_PRIVATE_KEY=abc
GEOLOCATE_OPENCAGEDATA=
GITHUB_TOKEN=xyz

View file

@ -0,0 +1,27 @@
# COPY this file to /config/{dev|prod}/public.env and change any values as required
# server domain name:
HOSTNAME=localhost
# server port:
PORT=4000
# full backend URL - server protocol + domain name
# include the port if it isn't the default for protocol like 80 or 443
BASE_URL=http://localhost
# hostname and port of meili search index
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 CommonsPub, 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)
UPLOAD_LIMIT=20000000
# ====================================
# You should not have to edit any of the following ones:
APP_NAME=Bonfire
POSTGRES_HOST=db
LANG=en_US.UTF-8
REPLACE_OS_VARS=true
LIVEVIEW_ENABLED=true
CI=false
ACME_AGREE=true

View file

@ -19,12 +19,13 @@ config :logger, level: :warn
# to provide built-in test partitioning in CI environment.
# Run `mix help test` for more information.
config :bonfire, Bonfire.Repo,
username: "postgres",
password: System.get_env("POSTGRES_PASSWORD", "postgres"),
database: "bonfire_test#{System.get_env("MIX_TEST_PARTITION")}",
hostname: System.get_env("DATABASE_HOST") || "localhost",
pool: Ecto.Adapters.SQL.Sandbox,
pool_size: 60
username: System.get_env("POSTGRES_USER", "postgres"),
password: System.get_env("POSTGRES_PASSWORD", "postgres"),
database: "bonfire_test#{System.get_env("MIX_TEST_PARTITION")}",
hostname: System.get_env("POSTGRES_HOST") || "localhost"
# show_sensitive_data_on_connection_error: true,
# We don't run a server during test. If one is required,
# you can enable the server option below.

View file

@ -0,0 +1,56 @@
version: "3.5"
services:
backend:
# You can build your own image from the source running:
# $ make rel-build
# $ make rel-tag-latest
image: "bonfirenetworks/bonfire-main:latest"
container_name: "bonfire_web"
restart: always
ports:
- "4000:4000" # you may want to comment this in production
env_file:
- config/prod/public.env
- config/prod/secrets.env
depends_on:
- db
volumes:
- type: bind
source: ./data/uploads
target: /var/www/uploads
frontend:
image: "caddy:alpine"
restart: always
ports:
- "80:80"
- "443:443"
env_file:
- config/prod/public.env
volumes:
- type: bind
source: ./config/deploy/Caddyfile2
target: /etc/caddy/Caddyfile
read_only: true
- type: bind
source: ./data/uploads
target: /frontend/uploads
db:
image: postgis/postgis:12-3.0-alpine
restart: always
volumes:
- "./data/postgres/prod:/var/lib/postgresql/data"
# ports:
# - "5432:5432"
env_file:
- config/prod/public.env
- config/prod/secrets.env
search:
image: getmeili/meilisearch:latest
# ports:
# - "7700:7700"
volumes:
- "./data/search/prod:/data.ms"
env_file:
- config/prod/public.env
- config/prod/secrets.env

View file

@ -4,15 +4,15 @@ version: "3.5"
services:
web:
container_name: "bonfire_web"
build:
context: .
dockerfile: "Dockerfile.dev"
ports:
- "4000:4000"
environment:
- DATABASE_HOST=db
env_file:
- config/.env.secrets
- config/dev/public.env
- config/dev/secrets.env
depends_on:
- db
# - search
@ -32,7 +32,8 @@ services:
# ports:
# - "5432:5432"
env_file:
- config/.env.secrets
- config/dev/public.env
- config/dev/secrets.env
user: $UID:$GID
search:
image: getmeili/meilisearch:latest
@ -42,5 +43,6 @@ services:
- "./data/search/dev:/data.ms"
- /etc/passwd:/etc/passwd:ro
env_file:
- config/.env.secrets
- config/dev/public.env
- config/dev/secrets.env
user: $UID:$GID

View file

@ -58,7 +58,7 @@ defmodule Bonfire.MixProject do
{:pbkdf2_elixir, "~> 1.2.1", only: [:dev, :test]},
{:argon2_elixir, "~> 2.3.0", only: [:prod]},
## dev conveniences
{:dbg, "~> 1.0", only: [:dev, :test]},
# {:dbg, "~> 1.0", only: [:dev, :test]},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:exsync, "~> 0.2", only: :dev},
# tests
@ -124,6 +124,8 @@ defmodule Bonfire.MixProject do
# NOTE: to_atom (instead of to_existing_atom) is safe because its used at build time, the code might fail
# NOTE: otherwise because of the atom never existing as part of compilation
deps()[String.to_atom(dep)][:path] || "./deps/"<>dep
rescue
CaseClauseError -> "./deps/"<>dep # FIXME
end
# defp existing_dep_path(dep) do

View file

@ -8,20 +8,20 @@
"bamboo": {:hex, :bamboo, "1.7.1", "7f0946e8c9081ce10d347cdba33c247c7c1c4f7dddc194ab0633603ef879bbdf", [:mix], [{:hackney, ">= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.4", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.1", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "5fb34c3ab638fc409deec47c1e91f9d78ad95bf22ccb153588b434e1ff1aa730"},
"bamboo_smtp": {:hex, :bamboo_smtp, "3.0.0", "b7f0c371af96a1cb7131908918b02abb228f9db234910bf10cf4fb177c083259", [:mix], [{:bamboo, "~> 1.2", [hex: :bamboo, repo: "hexpm", optional: false]}, {:gen_smtp, "~> 0.15.0", [hex: :gen_smtp, repo: "hexpm", optional: false]}], "hexpm", "77cb1fa3076b24109e54df622161fe1e5619376b4ecf86d8b99b46f327acc49f"},
"bonfire_boundaries": {:git, "https://github.com/bonfire-ecosystem/bonfire_boundaries", "43650c0745d2978d108eec5a3d1f31602d80983c", [branch: "main"]},
"bonfire_common": {:git, "https://github.com/bonfire-ecosystem/bonfire_common", "9d9ee5631ef73a44639f5d9dac4ca5f98cbe420b", [branch: "main"]},
"bonfire_common": {:git, "https://github.com/bonfire-ecosystem/bonfire_common", "ece4f8674fb6aad52a1c6bc762d03afca9ba7ec3", [branch: "main"]},
"bonfire_data_access_control": {:git, "https://github.com/bonfire-ecosystem/bonfire_data_access_control", "ef8d4bcc29c0536d72bc289269177bca9dd0315d", [branch: "main"]},
"bonfire_data_activity_pub": {:git, "https://github.com/bonfire-ecosystem/bonfire_data_activity_pub", "a9be447605451bf48f07290a253c88da1750ae6c", [branch: "main"]},
"bonfire_data_identity": {:git, "https://github.com/bonfire-ecosystem/bonfire_data_identity", "547550337f0ef25e2704e071db345884bd5114ab", [branch: "main"]},
"bonfire_data_identity": {:git, "https://github.com/bonfire-ecosystem/bonfire_data_identity", "e717d98ef22f1fedc2f52779483298e924281870", [branch: "main"]},
"bonfire_data_social": {:git, "https://github.com/bonfire-ecosystem/bonfire_data_social", "7c0f9f2f8174ab572c9f26612fa3716759270562", [branch: "main"]},
"bonfire_fail": {:git, "https://github.com/bonfire-ecosystem/bonfire_fail", "353484ad3640539350d8f2fbef40ea29cbbebcff", [branch: "main"]},
"bonfire_federate_activitypub": {:git, "https://github.com/bonfire-ecosystem/bonfire_federate_activitypub", "36cdb7fdbad3d30a1c21833aa6f4b2ada643e421", [branch: "main"]},
"bonfire_geolocate": {:git, "https://github.com/bonfire-ecosystem/bonfire_geolocate", "fb66f054bac6b3a813956d7b2d6b1e99e48780b2", [branch: "main"]},
"bonfire_mailer": {:git, "https://github.com/bonfire-ecosystem/bonfire_mailer", "079c8519395ee199dd689dd8ed8df0257210a9ff", [branch: "main"]},
"bonfire_me": {:git, "https://github.com/bonfire-ecosystem/bonfire_me", "e62df5ac70023553161f9acccc916e69ff7b3399", [branch: "main"]},
"bonfire_me": {:git, "https://github.com/bonfire-ecosystem/bonfire_me", "e640611def15e9e54189505082c0e02577f40a60", [branch: "main"]},
"bonfire_quantify": {:git, "https://github.com/bonfire-ecosystem/bonfire_quantify", "8ba494f9c88d8cc16b1e1a7908792168cb2c3fef", [branch: "main"]},
"bonfire_social": {:git, "https://github.com/bonfire-ecosystem/bonfire_social", "385513000041779994da1144ab675abd5e7c479e", [branch: "main"]},
"bonfire_tag": {:git, "https://github.com/bonfire-ecosystem/bonfire_tag", "e677d47e7445c282751135a2331448f20d919479", [branch: "main"]},
"bonfire_ui_social": {:git, "https://github.com/bonfire-ecosystem/bonfire_ui_social", "cef75da37330b8210cd94c94faeb29a7093faac0", [branch: "main"]},
"bonfire_ui_social": {:git, "https://github.com/bonfire-ecosystem/bonfire_ui_social", "b2dd525c288947d81c9eb383df13845833f705f2", [branch: "main"]},
"bonfire_valueflows": {:git, "https://github.com/bonfire-ecosystem/bonfire_valueflows", "72db4e7a3a8fa819a37d44d959d7be347baaa0ca", [branch: "main"]},
"bonfire_website": {:git, "https://github.com/bonfire-ecosystem/bonfire_website", "d7fec240b5059a5d35cf22144b540b48d070439a", [branch: "main"]},
"cachex": {:hex, :cachex, "3.3.0", "6f2ebb8f27491fe39121bd207c78badc499214d76c695658b19d6079beeca5c2", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "d90e5ee1dde14cef33f6b187af4335b88748b72b30c038969176cd4e6ccc31a1"},
@ -100,7 +100,7 @@
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
"postgrex": {:hex, :postgrex, "0.15.8", "f5e782bbe5e8fa178d5e3cd1999c857dc48eda95f0a4d7f7bd92a50e84a0d491", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "698fbfacea34c4cf22c8281abeb5cf68d99628d541874f085520ab3b53d356fe"},
"qr_code": {:hex, :qr_code, "2.2.0", "8445085f57294af832456432b7366a5b6f49ad453f2fe7191a71d7190b1a26da", [:mix], [{:ex_maybe, "~> 1.1.1", [hex: :ex_maybe, repo: "hexpm", optional: false]}, {:matrix_reloaded, "~> 2.2.1", [hex: :matrix_reloaded, repo: "hexpm", optional: false]}, {:result, "~> 1.3.0", [hex: :result, repo: "hexpm", optional: false]}, {:xml_builder, "~> 2.1.1", [hex: :xml_builder, repo: "hexpm", optional: false]}], "hexpm", "e66784d13e0d5f5d978a8801b127b703ac0134af79889c7a26397d331785600b"},
"query_elf": {:git, "https://github.com/bonfire-ecosystem/query_elf", "afce9bd93398456bd7cbf6d738671776e6f5c9c7", [branch: "bonfire"]},
"query_elf": {:git, "https://github.com/bonfire-ecosystem/query_elf", "fd4161de680554ee2a7bbacaf79490e7753045d8", [branch: "bonfire"]},
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
"recase": {:hex, :recase, "0.7.0", "3f2f719f0886c7a3b7fe469058ec539cb7bbe0023604ae3bce920e186305e5ae", [:mix], [], "hexpm", "36f5756a9f552f4a94b54a695870e32f4e72d5fad9c25e61bc4a3151c08a4e0c"},
"result": {:hex, :result, "1.3.0", "fd598eb0e084048157fffca7ef5f53803c6031c99691a78271dade7b4c479661", [:mix], [], "hexpm", "c7aca4eb90a1fc7b571d46c700f36b36e18bef28d9df3630b73f67d6bf60329c"},