Browse Source

baseline for releases.

jackyalcine 6 months ago
parent
commit
e418118f42
Signed by: Jacky Alciné <yo@jacky.wtf> GPG Key ID: 537A4F904B15268D
11 changed files with 236 additions and 66 deletions
  1. 12
    6
      .dockerignore
  2. 82
    0
      Dockerfile.build
  3. 20
    0
      Makefile
  4. 23
    0
      lib/feed/hfeed.ex
  5. 2
    14
      mix.exs
  6. 2
    0
      mix.lock
  7. 33
    0
      rel/config.exs
  8. 3
    0
      rel/plugins/.gitignore
  9. 29
    0
      rel/vm.args
  10. 26
    42
      test/integration/controllers/indie/micropub_controller_test.exs
  11. 4
    4
      web/static/js/fonts.ts

+ 12
- 6
.dockerignore View File

@@ -1,11 +1,17 @@
1
-deps/
1
+.editiorconfig
2
+.elixir-version
3
+.elixir_ls/
4
+.envrc
5
+.git/
6
+.gitignore
7
+Dockerfile
8
+Makefile
9
+README*
2 10
 _build/
11
+deps/
3 12
 docker-compose.yml
4
-.git/*
5
-.elixir_ls/*
6
-.envrc
7
-.elixir-version
8
-.editiorconfig
9 13
 erl_crash.dump
10 14
 node_modules/
15
+priv/static/
16
+test/
11 17
 tmp/

+ 82
- 0
Dockerfile.build View File

@@ -0,0 +1,82 @@
1
+# The version of Alpine to use for the final image
2
+# This should match the version of Alpine that the `elixir:1.7.2-alpine` image uses
3
+ARG ALPINE_VERSION=3.8
4
+
5
+FROM elixir:1.7.2-alpine AS builder
6
+
7
+# The following are build arguments used to change variable parts of the image.
8
+# The name of your application/release (required)
9
+ARG APP_NAME
10
+# The version of the application we are building (required)
11
+ARG APP_VSN
12
+# The environment to build with
13
+ARG MIX_ENV=prod
14
+# Set this to true if this release is not a Phoenix app
15
+ARG SKIP_PHOENIX=false
16
+# If you are using an umbrella project, you can change this
17
+# argument to the directory the Phoenix app is in so that the assets
18
+# can be built
19
+ARG PHOENIX_SUBDIR=.
20
+
21
+ENV SKIP_PHOENIX=${SKIP_PHOENIX} \
22
+  APP_NAME=${APP_NAME} \
23
+  APP_VSN=${APP_VSN} \
24
+  MIX_ENV=${MIX_ENV}
25
+
26
+# By convention, /opt is typically used for applications
27
+WORKDIR /opt/app
28
+
29
+# This step installs all the build tools we'll need
30
+RUN apk update && \
31
+      apk upgrade --no-cache && \
32
+      apk add --no-cache \
33
+      nodejs \
34
+      yarn \
35
+      git \
36
+      build-base && \
37
+      mix local.rebar --force && \
38
+      mix local.hex --force
39
+
40
+# This copies our app source code into the build container
41
+COPY . .
42
+
43
+RUN mix do deps.get, deps.compile, compile
44
+
45
+# This step builds assets for the Phoenix app (if there is one)
46
+# If you aren't building a Phoenix app, pass `--build-arg SKIP_PHOENIX=true`
47
+# This is mostly here for demonstration purposes
48
+RUN if [ ! "$SKIP_PHOENIX" = "true" ]; then \
49
+      cd ${PHOENIX_SUBDIR}/assets && \
50
+      yarn install && \
51
+      yarn deploy && \
52
+      cd .. && \
53
+      mix phx.digest; \
54
+      fi
55
+
56
+RUN \
57
+      mkdir -p /opt/built && \
58
+      mix release --verbose && \
59
+      cp _build/${MIX_ENV}/rel/${APP_NAME}/releases/${APP_VSN}/${APP_NAME}.tar.gz /opt/built && \
60
+      cd /opt/built && \
61
+      tar -xzf ${APP_NAME}.tar.gz && \
62
+      rm ${APP_NAME}.tar.gz
63
+
64
+# From this line onwards, we're in a new image, which will be the image used in production
65
+FROM alpine:${ALPINE_VERSION}
66
+
67
+# The name of your application/release (required)
68
+ARG APP_NAME
69
+
70
+RUN apk update && \
71
+      apk add --no-cache \
72
+      bash \
73
+      openssl-dev
74
+
75
+ENV REPLACE_OS_VARS=true \
76
+  APP_NAME=${APP_NAME}
77
+
78
+WORKDIR /opt/app
79
+
80
+COPY --from=builder /opt/built .
81
+
82
+CMD trap 'exit' INT; /opt/app/bin/${APP_NAME} foreground

+ 20
- 0
Makefile View File

@@ -0,0 +1,20 @@
1
+.PHONY: help
2
+
3
+APP_NAME ?= `grep 'app:' mix.exs | sed -e 's/\[//g' -e 's/ //g' -e 's/app://' -e 's/[:,]//g'`
4
+APP_VSN ?= `grep 'version:' mix.exs | cut -d '"' -f2`
5
+	BUILD ?= `git rev-parse --short HEAD`
6
+
7
+help:
8
+	@echo "$(APP_NAME):$(APP_VSN)-$(BUILD)"
9
+	@perl -nle'print $& if m{^[a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
10
+
11
+build: ## Build the Docker image
12
+	docker build -f Dockerfile.build --build-arg APP_NAME=$(APP_NAME) \
13
+		--build-arg APP_VSN=$(APP_VSN) \
14
+		-t $(APP_NAME):$(APP_VSN)-$(BUILD) \
15
+		-t $(APP_NAME):latest .
16
+
17
+run: ## Run the app in Docker
18
+	docker run --env-file config/docker.env \
19
+		--expose 4000 -p 4000:4000 \
20
+		--rm -it $(APP_NAME):latest

+ 23
- 0
lib/feed/hfeed.ex View File

@@ -0,0 +1,23 @@
1
+# Koype: a IndieWeb-focused, single-tenant website engine for people.
2
+#
3
+# Copyright © 2019 Jacky Alciné <jacky.is@black.af>
4
+#
5
+# This file belongs to the Koype project.
6
+#
7
+# This program is free software: you can redistribute it and/or modify
8
+# it under the terms of the GNU Affero General Public License as published by
9
+# the Free Software Foundation, either version 3 of the License, or
10
+# (at your option) any later version.
11
+#
12
+# This program is distributed in the hope that it will be useful,
13
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
+# GNU Affero General Public License for more details.
16
+#
17
+# You should have received a copy of the GNU Affero General Public License
18
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
+defmodule Koype.Feed.HFeed do
20
+  def generate(_items, _options) do
21
+    # TODO: Generate h-feed HTML using Floki
22
+  end
23
+end

+ 2
- 14
mix.exs View File

@@ -66,20 +66,7 @@ defmodule Koype.Mixfile do
66 66
   def application do
67 67
     [
68 68
       mod: {Koype.Application, []},
69
-      extra_applications: [:logger, :runtime_tools, :cachex, :que, :indieweb, :arc],
70
-      included_applications: [
71
-        :scrivener_ecto,
72
-        :confex,
73
-        :cachex,
74
-        :corsica,
75
-        :indieweb,
76
-        :httpotion,
77
-        :phoenix_ecto,
78
-        :phoenix,
79
-        :logster,
80
-        :liquid,
81
-        :readability
82
-      ]
69
+      extra_applications: [:logger, :runtime_tools, :cachex, :que, :indieweb, :arc]
83 70
     ]
84 71
   end
85 72
 
@@ -100,6 +87,7 @@ defmodule Koype.Mixfile do
100 87
       {:corsica, "~> 1.1.2"},
101 88
       {:credo, "~> 0.10.0", only: [:dev, :test]},
102 89
       {:dialyxir, "~> 1.0.0-rc.4", only: :dev, runtime: false},
90
+      {:distillery, "~> 2.0"},
103 91
       {:double, "~> 0.6.5", only: :test},
104 92
       {:earmark, "~> 1.3.0"},
105 93
       {:ecto, "~> 2.2.0"},

+ 2
- 0
mix.lock View File

@@ -2,6 +2,7 @@
2 2
   "apex": {:hex, :apex, "1.2.1", "297f5dac23fa2a32648b890a0838fce2772114010e0b9ec975cae6021cc5a092", [:mix], [], "hexpm"},
3 3
   "arc": {:hex, :arc, "0.11.0", "ac7a0cc03035317b6fef9fe94c97d7d9bd183a3e7ce1606aa0c175cfa8d1ba6d", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:ex_aws_s3, "~> 2.0", [hex: :ex_aws_s3, repo: "hexpm", optional: true]}, {:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm"},
4 4
   "arc_ecto": {:hex, :arc_ecto, "0.11.1", "27aedf8c236b2097eed09d96f4ae73b43eb4c042a0e2ae42d44bf644cf16115c", [:mix], [{:arc, "~> 0.11.0", [hex: :arc, repo: "hexpm", optional: false]}, {:ecto, "~> 2.1 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm"},
5
+  "artificery": {:hex, :artificery, "0.4.1", "90b1fcedb9034853b36dbea2174f9fc1172bd50b0686a723178103def5c9c1c8", [:mix], [], "hexpm"},
5 6
   "atomex": {:hex, :atomex, "0.3.0", "19b5d1a2aef8706dbd307385f7d5d9f6f273869226d317492c396c7bacf26402", [:mix], [{:xml_builder, "~> 2.0.0", [hex: :xml_builder, repo: "hexpm", optional: false]}], "hexpm"},
6 7
   "base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
7 8
   "blankable": {:hex, :blankable, "0.0.1", "2e0b4667fee684f0614620d31a34bb2731341cccb27ed903e330195819ba3ba0", [:mix], [], "hexpm"},
@@ -22,6 +23,7 @@
22 23
   "db_connection": {:hex, :db_connection, "1.1.3", "89b30ca1ef0a3b469b1c779579590688561d586694a3ce8792985d4d7e575a61", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
23 24
   "decimal": {:hex, :decimal, "1.7.0", "30d6b52c88541f9a66637359ddf85016df9eb266170d53105f02e4a67e00c5aa", [:mix], [], "hexpm"},
24 25
   "dialyxir": {:hex, :dialyxir, "1.0.0-rc.5", "c9c2379c59cf2dfc74690f48866e33ffb55ff660e5e02405c14614d204efdc4f", [:mix], [{:erlex, "~> 0.2.1", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm"},
26
+  "distillery": {:hex, :distillery, "2.0.12", "6e78fe042df82610ac3fa50bd7d2d8190ad287d120d3cd1682d83a44e8b34dfb", [:mix], [{:artificery, "~> 0.2", [hex: :artificery, repo: "hexpm", optional: false]}], "hexpm"},
25 27
   "double": {:hex, :double, "0.6.6", "2280fa0800dd582ec1c542bc3aba134950bebf9281e847ecf8f50ddf19da830c", [:mix], [], "hexpm"},
26 28
   "earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm"},
27 29
   "ecto": {:hex, :ecto, "2.2.9", "031d55df9bb430cb118e6f3026a87408d9ce9638737bda3871e5d727a3594aae", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},

+ 33
- 0
rel/config.exs View File

@@ -0,0 +1,33 @@
1
+~w(rel plugins *.exs)
2
+|> Path.join()
3
+|> Path.wildcard()
4
+|> Enum.map(&Code.eval_file(&1))
5
+
6
+use Mix.Releases.Config,
7
+  default_release: :default,
8
+  default_environment: Mix.env()
9
+
10
+environment :dev do
11
+  set(dev_mode: true)
12
+  set(include_erts: false)
13
+  set(cookie: :"sZl?KdVIyFipT)V%Wc9G4p<Cq&N0}|gQH(T{gLkWrX&cSbw3PyIhG.&DrpXlAatf")
14
+end
15
+
16
+environment :prod do
17
+  set(include_erts: true)
18
+  set(include_src: false)
19
+  set(cookie: :"`k_k|:8[ndq^X,nF`~$>otRy,N|4oy3uF@?o~YB^0e|m.cIg5ii[.EWUu)*u?VA,")
20
+  set(vm_args: "rel/vm.args")
21
+end
22
+
23
+release :koype do
24
+  set(version: current_version(:koype))
25
+
26
+  set(
27
+    applications: [
28
+      :runtime_tools,
29
+      :indieweb,
30
+      :cachex
31
+    ]
32
+  )
33
+end

+ 3
- 0
rel/plugins/.gitignore View File

@@ -0,0 +1,3 @@
1
+*.*
2
+!*.exs
3
+!.gitignore

+ 29
- 0
rel/vm.args View File

@@ -0,0 +1,29 @@
1
+## This file provide the arguments provided to the VM at startup
2
+## You can find a full list of flags and their behaviours at
3
+## http://erlang.org/doc/man/erl.html
4
+
5
+## Name of the node
6
+-name <%= release_name %>@127.0.0.1
7
+
8
+## Cookie for distributed erlang
9
+-setcookie <%= release.profile.cookie %>
10
+
11
+## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive
12
+## (Disabled by default..use with caution!)
13
++heart
14
+
15
+## Enable kernel poll and a few async threads
16
++K true
17
+## For OTP21+, the +A flag is not used anymore,
18
+## +SDio replace it to use dirty schedulers
19
++SDio 5
20
+
21
+## Increase number of concurrent ports/sockets
22
+-env ERL_MAX_PORTS 4096
23
+
24
+## Tweak GC to run more often
25
+-env ERL_FULLSWEEP_AFTER 10
26
+
27
+# Enable SMP automatically based on availability
28
+# On OTP21+, this is not needed anymore.
29
+# -smp auto

+ 26
- 42
test/integration/controllers/indie/micropub_controller_test.exs View File

@@ -232,8 +232,6 @@ defmodule Koype.Web.Indie.MicropubControllerTest do
232 232
     end
233 233
 
234 234
     test "400 fails to creates multiple photo in JSON format without required scope" do
235
-      uri = Faker.Internet.url()
236
-
237 235
       photos =
238 236
         List.duplicate(
239 237
           %{
@@ -243,22 +241,20 @@ defmodule Koype.Web.Indie.MicropubControllerTest do
243 241
           3
244 242
         )
245 243
 
246
-      with_mock(Koype.Storage.Image, [:passthrough], store: fn _ -> {:ok, uri} end) do
247
-        conn =
248
-          build_conn()
249
-          |> indie_sign_in_conn(@client_id, ~w(create))
250
-          |> put_req_header("content-type", "application/json")
251
-          |> post(@route, %{
252
-            "type" => "entry",
253
-            "properties" => %{
254
-              "content" => Faker.Lorem.paragraph(),
255
-              "category" => Faker.Lorem.words(),
256
-              "photo" => photos
257
-            }
258
-          })
244
+      conn =
245
+        build_conn()
246
+        |> indie_sign_in_conn(@client_id, ~w(create))
247
+        |> put_req_header("content-type", "application/json")
248
+        |> post(@route, %{
249
+          "type" => "entry",
250
+          "properties" => %{
251
+            "content" => Faker.Lorem.paragraph(),
252
+            "category" => Faker.Lorem.words(),
253
+            "photo" => photos
254
+          }
255
+        })
259 256
 
260
-        assert json_response(conn, :bad_request)
261
-      end
257
+      assert json_response(conn, :bad_request)
262 258
     end
263 259
 
264 260
     test "204 updates an existing entry's content" do
@@ -353,33 +349,21 @@ defmodule Koype.Web.Indie.MicropubControllerTest do
353 349
       entry = insert(:entry)
354 350
       categories = Faker.Lorem.words() ++ ["test"]
355 351
       entry_json = build(:entry_json) |> with_categories(categories)
352
+      {:ok, "model.json"} = Koype.Repo.Entry.Json.persist(entry, entry_json)
356 353
 
357
-      with_mocks([
358
-        {
359
-          IndieWeb.Micropub.Content,
360
-          [],
361
-          handle: fn "update", _ -> {:ok, state: :updated, model: entry, uri: @entry_uri} end
362
-        },
363
-        {
364
-          Koype.Repo.Entry.Json,
365
-          [:passthrough],
366
-          find: fn ^entry -> {:ok, entry_json} end, persist: fn ^entry, data -> {:ok, data} end
367
-        }
368
-      ]) do
369
-        conn =
370
-          build_conn()
371
-          |> indie_sign_in_conn(@client_id, ~w(create update))
372
-          |> put_req_header("content-type", "application/json")
373
-          |> post(@route, %{
374
-            "action" => "update",
375
-            "url" => @entry_uri,
376
-            "delete" => %{
377
-              "category" => ["test"]
378
-            }
379
-          })
354
+      conn =
355
+        build_conn()
356
+        |> indie_sign_in_conn(@client_id, ~w(create update))
357
+        |> put_req_header("content-type", "application/json")
358
+        |> post(@route, %{
359
+          "action" => "update",
360
+          "url" => Koype.Repo.Entry.get_uri(entry),
361
+          "delete" => %{
362
+            "category" => ["test"]
363
+          }
364
+        })
380 365
 
381
-        assert text_response(conn, :no_content) == ""
382
-      end
366
+      assert text_response(conn, :no_content) == ""
383 367
     end
384 368
 
385 369
     test "204 deletes an entire existing entry" do

+ 4
- 4
web/static/js/fonts.ts View File

@@ -19,13 +19,13 @@
19 19
  * You should have received a copy of the GNU Affero General Public License
20 20
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
21 21
  */
22
-import WebFont from 'webfontloader';
22
+import * as WebFont from "webfontloader";
23 23
 
24
-window.addEventListener('load', () => {
24
+window.addEventListener("load", () => {
25 25
   WebFont.load({
26 26
     custom: {
27
-      families: ['Alegreya Sans', 'Source Code Pro', 'Alegreya'],
28
-      urls: ['/~/core/assets/fonts.css']
27
+      families: ["Alegreya Sans", "Source Code Pro", "Alegreya"],
28
+      urls: ["/~/core/assets/fonts.css"]
29 29
     }
30 30
   });
31 31
 });

Loading…
Cancel
Save