diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..474bf9124 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +_build/ +.git/ +webapp/node_modules/ +priv/static/webapp +deps/ +test/ + +.* +docker-compose.yml +Makefile +README.md +Dockerfile diff --git a/.gitignore b/.gitignore index d48b7cc50..5eacc3b88 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,5 @@ test.json priv/static/webapp /webapp/node_modules /webapp/tmp +/webapp/dist .elixir_ls diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..8b3bfa533 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,77 @@ +# +# Step 1 - Build the OTP binary +# +FROM elixir:1.7.4-alpine AS builder + +ARG APP_NAME +ARG APP_VERSION +ARG MIX_ENV=prod + +ENV APP_NAME=${APP_NAME} \ + APP_VERSION=${APP_VERSION} \ + MIX_ENV=${MIX_ENV} + +WORKDIR /build + +# This step installs all the build tools we'll need +RUN apk update && \ + apk upgrade --no-cache && \ + apk add --no-cache nodejs npm git build-base python yaml-dev + +RUN mix local.rebar --force && \ + mix local.hex --force + +# This copies our app source code into the build container +COPY mix.* ./ +RUN mix deps.get --only ${MIX_ENV} +RUN mix deps.compile + +COPY . . +RUN mix compile +RUN mix phx.digest + +RUN mkdir -p /opt/build && \ + mix release --verbose && \ + cp _build/${MIX_ENV}/rel/${APP_NAME}/releases/${APP_VERSION}/${APP_NAME}.tar.gz /opt/build + +RUN cd /opt/build && \ + tar -xzf ${APP_NAME}.tar.gz && \ + rm ${APP_NAME}.tar.gz + +COPY webapp /opt/build/webapp + +RUN cd /opt/build && \ + npm ci --prefix webapp --no-audit --no-color + +# +# Step 2 - Build a lean runtime container +# +FROM alpine:3.8 + +ARG APP_NAME +ARG APP_VERSION +ENV APP_NAME=${APP_NAME} \ + APP_VERSION=${APP_VERSION} + +# Update kernel and install runtime dependencies +RUN apk --no-cache update && \ + apk --no-cache upgrade && \ + apk --no-cache add bash openssl yaml-dev nodejs npm + +WORKDIR /opt/accent + +# Copy the OTP binary from the build step +COPY --from=builder /opt/build . + +# Copy the entrypoint script +COPY priv/scripts/docker-entrypoint.sh /usr/local/bin +RUN chmod a+x /usr/local/bin/docker-entrypoint.sh + +# Create a non-root user +RUN adduser -D accent && \ + chown -R accent: /opt/accent + +USER accent + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["foreground"] diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..e39943f30 --- /dev/null +++ b/Makefile @@ -0,0 +1,122 @@ +# Configuration +# ------------- + +APP_NAME ?= `grep 'app:' mix.exs | sed -e 's/\[//g' -e 's/ //g' -e 's/app://' -e 's/[:,]//g'` +APP_VERSION ?= `grep 'version:' mix.exs | cut -d '"' -f2` +DOCKER_IMAGE_TAG ?= latest +GIT_REVISION ?= `git rev-parse HEAD` + +# Introspection targets +# --------------------- + +.PHONY: help +help: header targets + +.PHONY: header +header: + @echo "\033[34mEnvironment\033[0m" + @echo "\033[34m---------------------------------------------------------------\033[0m" + @printf "\033[33m%-23s\033[0m" "APP_NAME" + @printf "\033[35m%s\033[0m" $(APP_NAME) + @echo "" + @printf "\033[33m%-23s\033[0m" "APP_VERSION" + @printf "\033[35m%s\033[0m" $(APP_VERSION) + @echo "" + @printf "\033[33m%-23s\033[0m" "GIT_REVISION" + @printf "\033[35m%s\033[0m" $(GIT_REVISION) + @echo "" + @printf "\033[33m%-23s\033[0m" "DOCKER_IMAGE_TAG" + @printf "\033[35m%s\033[0m" $(DOCKER_IMAGE_TAG) + @echo "\n" + +.PHONY: targets +targets: + @echo "\033[34mTargets\033[0m" + @echo "\033[34m---------------------------------------------------------------\033[0m" + @perl -nle'print $& if m{^[a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-22s\033[0m %s\n", $$1, $$2}' + +# Build targets +# ------------- + +.PHONY: dependencies +dependencies: dependencies-mix dependencies-npm ## Install dependencies required by the application + +.PHONY: dependencies-mix +dependencies-mix: + mix deps.get --force + +.PHONY: dependencies-npm +dependencies-npm: + npm install --prefix webapp + +.PHONY: build +build: ## Build the Docker image for the OTP release + docker build --build-arg APP_NAME=$(APP_NAME) --build-arg APP_VERSION=$(APP_VERSION) --rm --tag $(APP_NAME):$(DOCKER_IMAGE_TAG) . + +# CI targets +# ---------- + +.PHONY: lint +lint: lint-compile lint-format lint-credo lint-eslint lint-stylelint lint-prettier ## Run lint tools on the code + +.PHONY: lint-compile +lint-compile: + mix compile --warnings-as-errors --force + +.PHONY: lint-format +lint-format: + mix format --dry-run --check-formatted + +.PHONY: lint-credo +lint-credo: + mix credo --strict + +.PHONY: lint-eslint +lint-eslint: + ./assets/node_modules/.bin/eslint --ignore-path webapp/.eslintignore --config webapp/.eslintrc webapp + +.PHONY: lint-stylelint +lint-stylelint: + ./assets/node_modules/.bin/stylelint --syntax scss --config webapp/.stylelintrc webapp/css + +.PHONY: lint-prettier +lint-prettier: + ./assets/node_modules/.bin/prettier --single-quote --list-different --no-bracket-spacing --print-width 130 './webapp/app/**/*.{js,gql}' + +.PHONY: test +test: ## Run the test suite + mix test + +.PHONY: test-coverage +test-coverage: ## Generate the code coverage report + mix coveralls + +.PHONY: format +format: format-elixir format-prettier ## Run formatting tools on the code + +.PHONY: format-elixir +format-elixir: + mix format + +.PHONY: format-prettier +format-prettier: + ./assets/node_modules/.bin/prettier --single-quote --write --no-bracket-spacing --print-width 130 './webapp/app/**/*.{js,gql}' + +# Development targets +# ------------------- + +.PHONY: dev-start-postgresql +dev-start-postgresql: ## Run a PostgreSQL server inside of a Docker Compose environment + docker-compose up --detach postgresql + +.PHONY: dev-start-application +dev-start-application: ## Run the OTP release inside of a Docker Compose environment + docker-compose up application + +.PHONY: dev-start +dev-start: ## Start every service of in the Docker Compose environment + docker-compose up + +.PHONY: dev-stop +dev-stop: ## Stop every service of in the Docker Compose environment + docker-compose down diff --git a/README.md b/README.md index ca8ebbece..f3fa84fbe 100644 --- a/README.md +++ b/README.md @@ -18,15 +18,15 @@ The Accent API provides a powerful abstraction around the process of translating ## Contents -| Section | Description | -|---------------------------------------------------------|------------------------------------------------------------------------| -| [🚧 Requirements](#-requirements) | Dependencies required to run Accent’ stack | -| [🎛 Mix commands](#-executing-mix-commands) | How to execute mix task with the Twelve-Factor pattern | -| [🏎 Quickstart](#-quickstart) | Steps to run the project, from API to webapp | -| [🌳 Environment variables](#-environment-variables) | Required and optional env var used | -| [✅ Tests](#-tests) | How to run the extensive tests suite | -| [🚀 Heroku](#-heroku) | Easy deployment setup with Heroku | -| [🌎 Contribute](#-contribute) | How to contribute to this repo | +| Section | Description | +|---------------------------------------------------------|---------------------------------------------------------------------------| +| [🚧 Requirements](#-requirements) | Dependencies required to run Accent’ stack | +| [🎛 Mix commands](#-executing-mix-commands) | How to execute mix task with the Twelve-Factor pattern | +| [🏎 Quickstart](#-quickstart) | Steps to run the project, from API to webapp, with or without Docker | +| [🌳 Environment variables](#-environment-variables) | Required and optional env var used | +| [✅ Tests](#-tests) | How to run the extensive tests suite | +| [🚀 Heroku](#-heroku) | Easy deployment setup with Heroku | +| [🌎 Contribute](#-contribute) | How to contribute to this repo | ## 🚧 Requirements @@ -56,15 +56,34 @@ $ nv .env mix ## 🏎 Quickstart +_This is the full development setup. To simply run the app, see the *Docker* instructions_ + 1. If you don’t already have it, install `nodejs` with `brew install nodejs` 2. If you don’t already have it, install `elixir` with `brew install elixir` 3. If you don’t already have it, install `libyaml` with `brew install libyaml` 4. If you don’t already have it, install `postgres` with `brew install postgres` or the [macOS app](https://postgresapp.com/) -5. Install dependencies with `mix deps.get` and `npm --prefix webapp install` +5. Install dependencies with `make dependencies` 6. Create and migrate your database with `mix ecto.setup` 7. Start Phoenix endpoint with `mix phx.server` -8. Start Ember server with `npm --prefix webapp run start` -9. That’s it! +8. Start Ember server with `npm run start --prefix webapp` + +*That’s it!* + +### Makefile + +The Makefile should be the main entry for common tasks such as tests, linting, Docker, etc. This simplify the developpement process since you don’t have to search for which service provides which command. `mix`, `npm`, `prettier`, `docker`, `stylelint`, etc are all used in the Makefile. + +### Docker + +For the production setup, we use Docker to build an OTP release of the app. With docker-compose, you can run the image locally. Here are the steps to have a working app running locally with Docker: + +_When running the production env, you need to provide a valid GOOGLE_API_CLIENT_ID in the `docker-compose.yml` file._ + +1. Run `make build` to build the OTP release with Docker +2. Run `make dev-start-postgresql` to start an instance of Postgresql. The instance will run on port 5432 with the `postgres` user. You can change those values in the `docker-compose.yml` file. +3. Run `make dev-start-application` to start the app! The release hook of the release will execute migrations and seeds before starting the webserver on port 4000 (again you can change the settings in `docker-compose.yml`) + +*That’s it! You now have a working Accent instance without installing Elixir or Node!* ## 🌳 Environment variables diff --git a/config/config.exs b/config/config.exs index f9c14a0b7..da3eed774 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1,65 +1,7 @@ use Mix.Config -defmodule Utilities do - def string_to_boolean("true"), do: true - def string_to_boolean("1"), do: true - def string_to_boolean(_), do: false -end - -# Used to extract schema json with the absinthe’s mix task -config :absinthe, :schema, Accent.GraphQL.Schema - -# Configures the endpoint -config :accent, Accent.Endpoint, - root: Path.expand("..", __DIR__), - http: [port: System.get_env("PORT")], - url: [host: System.get_env("CANONICAL_HOST") || "localhost"], - secret_key_base: System.get_env("SECRET_KEY_BASE"), - render_errors: [accepts: ~w(json)], - pubsub: [name: Accent.PubSub, adapter: Phoenix.PubSub.PG2] - -# Configure your database -config :accent, :ecto_repos, [Accent.Repo] - -config :accent, Accent.Repo, - adapter: Ecto.Adapters.Postgres, - timeout: 30_000, - url: System.get_env("DATABASE_URL") || "postgres://localhost/accent_development" - -config :accent, - force_ssl: Utilities.string_to_boolean(System.get_env("FORCE_SSL")), - hook_broadcaster: Accent.Hook.Broadcaster, - dummy_provider_enabled: true, - restricted_domain: System.get_env("RESTRICTED_DOMAIN") - -# Configures canary custom handlers and repo -config :canary, - repo: Accent.Repo, - unauthorized_handler: {Accent.ErrorController, :handle_unauthorized}, - not_found_handler: {Accent.ErrorController, :handle_not_found} - -# Configures Elixir's Logger -config :logger, :console, - format: "$time $metadata[$level] $message\n", - metadata: [:request_id] - -# Configure Phoenix -config :phoenix, Accent.Router, host: System.get_env("CANONICAL_HOST") -config :phoenix, :json_library, Jason - -config :phoenix, :generators, - migration: true, - binary_id: false - -# Configures sentry to report errors -config :sentry, - dsn: System.get_env("SENTRY_DSN"), - environment_name: System.get_env("SENTRY_ENVIRONMENT_NAME") || Mix.env(), - included_environments: [:prod], - root_source_code_path: File.cwd!() - -# Configure mailer -import_config "mailer.exs" +# Import release config +import_config "../rel/config/config.exs" # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. diff --git a/config/prod.exs b/config/prod.exs index 645419a9c..a5ba8a231 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -1,6 +1,11 @@ use Mix.Config -config :accent, Accent.Endpoint, check_origin: false +config :accent, Accent.Endpoint, + check_origin: false, + server: true, + root: ".", + cache_static_manifest: "priv/static/cache_manifest.json" + config :accent, dummy_provider_enabled: false config :logger, level: :info diff --git a/coveralls.json b/coveralls.json new file mode 100644 index 000000000..3786d93ca --- /dev/null +++ b/coveralls.json @@ -0,0 +1,5 @@ +{ + "skip_files": [ + "lib/accent/release_tasks.ex" + ] +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..4b74351cd --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,24 @@ +version: '3.3' +services: + application: + image: accent + container_name: accent + ports: + - 4000:4000 + depends_on: + - postgresql + environment: + - PORT=4000 + - DATABASE_URL=postgres://postgres@postgresql:5432/accent_development + - GOOGLE_API_CLIENT_ID=xxxxxxxx.apps.googleusercontent.com + postgresql: + image: postgres:10.3 + container_name: accent-postgres + environment: + - POSTGRES_DB=accent_development + ports: + - 5432:5432 + volumes: + - accent_psql:/var/lib/postgresql/data +volumes: + accent_psql: diff --git a/lib/accent/release_tasks.ex b/lib/accent/release_tasks.ex new file mode 100644 index 000000000..9db6912e2 --- /dev/null +++ b/lib/accent/release_tasks.ex @@ -0,0 +1,63 @@ +defmodule Accent.ReleaseTasks do + @start_apps ~w(crypto ssl postgrex ecto_sql)a + + def migrate do + start_services() + run_migrations() + run_seeds() + stop_services() + end + + defp repos, do: Application.get_env(:accent, :ecto_repos, []) + + defp start_services do + IO.puts("Starting dependencies…") + Enum.each(@start_apps, &Application.ensure_all_started/1) + + IO.puts("Starting repos…") + Enum.each(repos(), & &1.start_link(pool_size: 2)) + end + + defp run_seeds do + Enum.each(repos(), &run_seeds_for/1) + end + + defp run_seeds_for(repo) do + app = Keyword.get(repo.config, :otp_app) + seed_script = priv_path_for(app, repo, "seeds.exs") + + if File.exists?(seed_script) do + IO.puts("Running seed script for #{app}") + Code.eval_file(seed_script) + end + end + + defp run_migrations do + Enum.each(repos(), &run_migrations_for/1) + end + + defp run_migrations_for(repo) do + app = Keyword.get(repo.config, :otp_app) + migrations_path = priv_path_for(app, repo, "migrations") + + IO.puts("Running migrations for #{app}") + Ecto.Migrator.run(repo, migrations_path, :up, all: true) + end + + defp priv_path_for(app, repo, filename) do + priv_dir = "#{:code.priv_dir(app)}" + + repo_underscore = + repo + |> Module.split() + |> List.last() + |> Macro.underscore() + + Path.join([priv_dir, repo_underscore, filename]) + end + + defp stop_services do + IO.puts("Success!") + :init.stop() + end +end diff --git a/lib/web/controllers/webapp_controller.ex b/lib/web/controllers/webapp_controller.ex index 0f158ec5d..6515abe1f 100644 --- a/lib/web/controllers/webapp_controller.ex +++ b/lib/web/controllers/webapp_controller.ex @@ -1,11 +1,38 @@ defmodule Accent.WebAppController do use Plug.Builder + import Phoenix.Controller, only: [put_view: 2, render: 2] + + alias Accent.WebappView + + plug(:ensure_file_exists) plug(:index) + @doc """ + Serves the static app built from the webapp folder. + + Since the build operation is done asynchronously on deploy, we need a maintenance page until + the index.html is available. + """ def index(conn, _) do conn |> put_resp_header("content-type", "text/html; charset=utf-8") - |> Plug.Conn.send_file(200, Application.app_dir(:accent, "priv/static/webapp/index.html")) + |> send_file(200, conn.assigns[:file]) + end + + def ensure_file_exists(conn, _) do + file = Application.app_dir(:accent, "priv/static/webapp/index.html") + + file + |> File.read() + |> case do + {:error, _} -> + conn + |> put_view(WebappView) + |> render("maintenance.html") + + _ -> + assign(conn, :file, file) + end end end diff --git a/lib/web/templates/webapp/maintenance.html.eex b/lib/web/templates/webapp/maintenance.html.eex new file mode 100644 index 000000000..1ed96f3a3 --- /dev/null +++ b/lib/web/templates/webapp/maintenance.html.eex @@ -0,0 +1,99 @@ + + + Maintenance – Accent + + + + + +
+ + + Accent + +
+ +
+
+ The app is in maintenance at the moment. +
+ + +
Refresh
+
+
+ + diff --git a/lib/web/views/webapp_view.ex b/lib/web/views/webapp_view.ex new file mode 100644 index 000000000..eac2ad22c --- /dev/null +++ b/lib/web/views/webapp_view.ex @@ -0,0 +1,3 @@ +defmodule Accent.WebappView do + use Phoenix.View, root: "lib/web/templates" +end diff --git a/mix.exs b/mix.exs index 42aaa1d6a..3dfe078bc 100644 --- a/mix.exs +++ b/mix.exs @@ -80,7 +80,7 @@ defmodule Accent.Mixfile do {:gen_stage, "~> 0.11"}, # Mock testing - {:mox, "~> 0.3"}, + {:mox, "~> 0.3", only: :test}, {:mock, "~> 0.3.0", only: :test}, # Dev @@ -88,7 +88,10 @@ defmodule Accent.Mixfile do {:credo, ">= 0.0.0", only: ~w(dev test)a}, {:credo_envvar, "~> 0.1.0", only: ~w(dev test)a, runtime: false}, {:excoveralls, "~> 0.8", only: :test}, - {:phoenix_live_reload, "~> 1.0", only: :dev} + {:phoenix_live_reload, "~> 1.0", only: :dev}, + + # OTP Release + {:distillery, "~> 2.0", runtime: false} ] end diff --git a/mix.lock b/mix.lock index 984e9e05e..a26bdb0a0 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,7 @@ %{ "absinthe": {:hex, :absinthe, "1.4.13", "81eb2ff41f1b62cd6e992955f62c22c042d1079b7936c27f5f7c2c806b8fc436", [:mix], [{:dataloader, "~> 1.0.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, "absinthe_plug": {:hex, :absinthe_plug, "1.4.6", "ac5d2d3d02acf52fda0f151b294017ab06e2ed1c6c15334e06aac82c94e36e08", [:mix], [{:absinthe, "~> 1.4.11", [hex: :absinthe, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.2 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, + "artificery": {:hex, :artificery, "0.2.6", "f602909757263f7897130cbd006b0e40514a541b148d366ad65b89236b93497a", [:mix], [], "hexpm"}, "backoff": {:hex, :backoff, "1.1.1"}, "bamboo": {:hex, :bamboo, "0.8.0", "573889a3efcb906bb9d25a1c4caa4ca22f479235e1b8cc3260d8b88dabeb4b14", [:mix], [{:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, "bamboo_smtp": {:hex, :bamboo_smtp, "1.4.0", "a01d91406f3a46b3452c84d345d50f75d6facca5e06337358287a97da0426240", [:mix], [{:bamboo, "~> 0.8.0", [hex: :bamboo, repo: "hexpm", optional: false]}, {:gen_smtp, "~> 0.12.0", [hex: :gen_smtp, repo: "hexpm", optional: false]}], "hexpm"}, @@ -20,6 +21,7 @@ "db_connection": {:hex, :db_connection, "2.0.3", "b4e8aa43c100e16f122ccd6798cd51c48c79fd391c39d411f42b3cd765daccb0", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"}, "decimal": {:hex, :decimal, "1.6.0", "bfd84d90ff966e1f5d4370bdd3943432d8f65f07d3bab48001aebd7030590dcc", [:mix], [], "hexpm"}, "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm"}, + "distillery": {:hex, :distillery, "2.0.12", "6e78fe042df82610ac3fa50bd7d2d8190ad287d120d3cd1682d83a44e8b34dfb", [:mix], [{:artificery, "~> 0.2", [hex: :artificery, repo: "hexpm", optional: false]}], "hexpm"}, "earmark": {:hex, :earmark, "0.2.1", "ba6d26ceb16106d069b289df66751734802777a3cbb6787026dd800ffeb850f3", [:mix], [], "hexpm"}, "ecto": {:hex, :ecto, "3.0.4", "5d0e2b89baaa03eac37ec49f9018c39a4e2fb6501dc3ff5a839de742e171a09f", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"}, "ecto_sql": {:hex, :ecto_sql, "3.0.3", "dd17f2401a69bb2ec91d5564bd259ad0bc63ee32c2cb2e616d04f1559801dba6", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.0.4", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.2.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"}, diff --git a/priv/scripts/docker-entrypoint.sh b/priv/scripts/docker-entrypoint.sh new file mode 100644 index 000000000..3e774530e --- /dev/null +++ b/priv/scripts/docker-entrypoint.sh @@ -0,0 +1,14 @@ +#!/bin/sh +set -e + +# Run the migration first using the custom release task +/opt/$APP_NAME/bin/$APP_NAME migrate + +# Since an EmberJs app can't be built without its environment, we build it here instead of the Dockerfile. +# This makes the image completly dependent of the deployed instance’s environment. +cd webapp +./node_modules/ember-cli/bin/ember build --prod --output-path=/opt/$APP_NAME/lib/$APP_NAME-$APP_VERSION/priv/static/webapp & +cd .. + +# Launch the OTP release and replace the caller as Process #1 in the container +exec /opt/$APP_NAME/bin/$APP_NAME "$@" diff --git a/rel/commands/migrate.sh b/rel/commands/migrate.sh new file mode 100644 index 000000000..716ced43a --- /dev/null +++ b/rel/commands/migrate.sh @@ -0,0 +1,2 @@ +#!/bin/sh +release_ctl eval --mfa "Accent.ReleaseTasks.migrate/0" diff --git a/rel/config.exs b/rel/config.exs new file mode 100644 index 000000000..d3ceeded8 --- /dev/null +++ b/rel/config.exs @@ -0,0 +1,53 @@ +# Import all plugins from `rel/plugins` +~w(rel plugins *.exs) +|> Path.join() +|> Path.wildcard() +|> Enum.map(&Code.eval_file(&1)) + +use Mix.Releases.Config, + default_release: :default, + default_environment: Mix.env() + +# For a full list of config options for both releases +# and environments, visit https://hexdocs.pm/distillery/config/distillery.html + +environment :dev do + set(dev_mode: true) + set(include_erts: false) + set(cookie: "_this_is_a_development_only_magic_secret_") +end + +environment :prod do + set(include_erts: true) + set(include_src: false) + set(cookie: "${ERLANG_COOKIE}") +end + +release :accent do + set(version: current_version(:accent)) + + set( + applications: [ + :runtime_tools + ] + ) + + set( + config_providers: [ + {Mix.Releases.Config.Providers.Elixir, ["${RELEASE_ROOT_DIR}/etc/config.exs"]} + ] + ) + + set( + overlays: [ + {:copy, "rel/config/config.exs", "etc/config.exs"}, + {:copy, "rel/config/mailer.exs", "etc/mailer.exs"} + ] + ) + + set( + commands: [ + migrate: "rel/commands/migrate.sh" + ] + ) +end diff --git a/rel/config/config.exs b/rel/config/config.exs new file mode 100644 index 000000000..bfeb162dc --- /dev/null +++ b/rel/config/config.exs @@ -0,0 +1,67 @@ +use Mix.Config + +defmodule Utilities do + def string_to_boolean("true"), do: true + def string_to_boolean("1"), do: true + def string_to_boolean(_), do: false +end + +# Used to extract schema json with the absinthe’s mix task +config :absinthe, :schema, Accent.GraphQL.Schema + +# Configures the endpoint +config :accent, Accent.Endpoint, + version: Application.spec(:accent, :vsn), + root: Path.expand("..", __DIR__), + http: [port: System.get_env("PORT") || "4000"], + url: [host: System.get_env("CANONICAL_HOST") || "localhost"], + secret_key_base: System.get_env("SECRET_KEY_BASE"), + render_errors: [accepts: ~w(json)], + pubsub: [name: Accent.PubSub, adapter: Phoenix.PubSub.PG2] + +# Configure your database +config :accent, :ecto_repos, [Accent.Repo] + +config :accent, Accent.Repo, + adapter: Ecto.Adapters.Postgres, + timeout: 30_000, + url: System.get_env("DATABASE_URL") || "postgres://localhost/accent_development" + +config :accent, + force_ssl: Utilities.string_to_boolean(System.get_env("FORCE_SSL")), + hook_broadcaster: Accent.Hook.Broadcaster, + dummy_provider_enabled: true, + restricted_domain: System.get_env("RESTRICTED_DOMAIN") + +# Configures canary custom handlers and repo +config :canary, + repo: Accent.Repo, + unauthorized_handler: {Accent.ErrorController, :handle_unauthorized}, + not_found_handler: {Accent.ErrorController, :handle_not_found} + +# Configures Elixir's Logger +config :logger, :console, + format: "$time $metadata[$level] $message\n", + metadata: [:request_id] + +# Configure Phoenix +config :phoenix, Accent.Router, host: System.get_env("CANONICAL_HOST") +config :phoenix, :json_library, Jason + +config :phoenix, :generators, + migration: true, + binary_id: false + +# Configures sentry to report errors +config :sentry, + dsn: System.get_env("SENTRY_DSN"), + environment_name: System.get_env("SENTRY_ENVIRONMENT_NAME") || Mix.env(), + included_environments: [:prod], + root_source_code_path: File.cwd!() + +if !System.get_env("SENTRY_DSN") do + config :sentry, included_environments: [] +end + +# Configure mailer +import_config "mailer.exs" diff --git a/config/mailer.exs b/rel/config/mailer.exs similarity index 100% rename from config/mailer.exs rename to rel/config/mailer.exs diff --git a/webapp/.ember-cli b/webapp/.ember-cli index 89201c275..aeb9593ce 100644 --- a/webapp/.ember-cli +++ b/webapp/.ember-cli @@ -1,5 +1,4 @@ { "disableAnalytics": false, - "usePods": true, - "output-path": "../priv/static/webapp" + "usePods": true } diff --git a/webapp/app/pods/components/project-settings/links-list/styles.scss b/webapp/app/pods/components/project-settings/links-list/styles.scss index 71b4ac946..e84329776 100644 --- a/webapp/app/pods/components/project-settings/links-list/styles.scss +++ b/webapp/app/pods/components/project-settings/links-list/styles.scss @@ -15,7 +15,7 @@ flex-direction: column; padding: 30px; margin: 0 30px 30px 0; - min-width: 160px; + min-width: 200px; text-align: center; background: #fff; border-radius: 3px; diff --git a/webapp/config/environment.js b/webapp/config/environment.js index d7227a0d7..c81879a15 100644 --- a/webapp/config/environment.js +++ b/webapp/config/environment.js @@ -42,8 +42,8 @@ module.exports = function(environment) { CLIENT_ID: process.env.GOOGLE_API_CLIENT_ID }; - ENV.GOOGLE_LOGIN_ENABLED = Boolean(process.env.GOOGLE_API_CLIENT_ID); - ENV.DUMMY_LOGIN_ENABLED = !Boolean(process.env.GOOGLE_API_CLIENT_ID); + ENV.GOOGLE_LOGIN_ENABLED = environment === 'production'; + ENV.DUMMY_LOGIN_ENABLED = environment !== 'production'; ENV.SENTRY = { DSN: process.env.WEBAPP_SENTRY_DSN diff --git a/webapp/package.json b/webapp/package.json index f86b6ded1..3f3dc7ff6 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -15,7 +15,7 @@ "scripts": { "start": "ember server --port $WEBAPP_PORT", "build": "ember build", - "build-production": "ember build --prod", + "build-production": "ember build --prod --output-path=../priv/static/webapp", "test": "ember test", "lint": "eslint .", "lint-fix": "eslint . --fix", diff --git a/webapp/public/favicon.ico b/webapp/public/favicon.ico new file mode 100644 index 000000000..8454af6ad Binary files /dev/null and b/webapp/public/favicon.ico differ