├── elixir-generate-dockerfile ├── .dockerignore ├── src │ ├── config │ │ └── config.exs │ ├── test │ │ ├── app_config │ │ │ ├── phoenix_umbrella_1_3 │ │ │ │ ├── apps │ │ │ │ │ ├── blog │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ ├── hello │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── blog_web │ │ │ │ │ │ └── assets │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── brunch-config.js │ │ │ │ ├── mix.exs │ │ │ │ └── mix.lock │ │ │ ├── minimal │ │ │ │ ├── mix.lock │ │ │ │ └── mix.exs │ │ │ ├── phoenix_1_2 │ │ │ │ ├── package.json │ │ │ │ ├── mix.exs │ │ │ │ ├── brunch-config.js │ │ │ │ └── mix.lock │ │ │ ├── phoenix_1_3 │ │ │ │ ├── assets │ │ │ │ │ ├── package.json │ │ │ │ │ └── brunch-config.js │ │ │ │ ├── mix.exs │ │ │ │ └── mix.lock │ │ │ └── phoenix_1_4 │ │ │ │ ├── assets │ │ │ │ ├── package.json │ │ │ │ └── webpack.config.js │ │ │ │ ├── mix.exs │ │ │ │ └── mix.lock │ │ ├── test_helper.exs │ │ └── app_config_test.exs │ ├── .gitignore │ ├── .formatter.exs │ ├── mix.exs │ ├── mix.lock │ └── lib │ │ ├── main.ex │ │ └── generator.ex ├── cloudbuild.yaml ├── app │ ├── generate_dockerfile.sh │ ├── Dockerfile-release.eex │ └── Dockerfile-simple.eex └── Dockerfile ├── test ├── sample_apps │ ├── minimal_plug │ │ ├── config │ │ │ └── config.exs │ │ ├── lib │ │ │ └── minimal_plug.ex │ │ ├── mix.exs │ │ └── mix.lock │ ├── minimal_phoenix │ │ ├── assets │ │ │ ├── js │ │ │ │ └── app.js │ │ │ ├── css │ │ │ │ └── app.css │ │ │ ├── package.json │ │ │ └── brunch-config.js │ │ ├── lib │ │ │ ├── minimal_phoenix.ex │ │ │ ├── minimal_phoenix_web │ │ │ │ ├── router.ex │ │ │ │ ├── controllers │ │ │ │ │ └── page_controller.ex │ │ │ │ └── endpoint.ex │ │ │ ├── minimal_phoenix │ │ │ │ └── application.ex │ │ │ └── minimal_phoenix_web.ex │ │ ├── config │ │ │ ├── dev.exs │ │ │ ├── config.exs │ │ │ ├── prod.exs │ │ │ └── staging.exs │ │ ├── rel │ │ │ └── config.exs │ │ ├── mix.exs │ │ └── mix.lock │ └── minimal_phoenix14 │ │ ├── lib │ │ ├── minimal_phoenix14.ex │ │ ├── minimal_phoenix14_web │ │ │ ├── gettext.ex │ │ │ ├── router.ex │ │ │ ├── controllers │ │ │ │ └── page_controller.ex │ │ │ └── endpoint.ex │ │ ├── minimal_phoenix14 │ │ │ └── application.ex │ │ └── minimal_phoenix14_web.ex │ │ ├── assets │ │ ├── css │ │ │ └── app.css │ │ ├── js │ │ │ └── app.js │ │ ├── .babelrc │ │ ├── static │ │ │ ├── favicon.ico │ │ │ ├── images │ │ │ │ └── phoenix.png │ │ │ └── robots.txt │ │ ├── package.json │ │ └── webpack.config.js │ │ ├── .formatter.exs │ │ ├── .gitignore │ │ ├── priv │ │ └── gettext │ │ │ ├── en │ │ │ └── LC_MESSAGES │ │ │ │ └── errors.po │ │ │ └── errors.pot │ │ ├── config │ │ ├── prod.exs │ │ ├── staging.exs │ │ ├── config.exs │ │ └── dev.exs │ │ ├── rel │ │ └── config.exs │ │ ├── mix-dist20.exs │ │ ├── mix-dist21.exs │ │ ├── mix.exs │ │ ├── mix.lock │ │ ├── mix-dist21.lock │ │ └── mix-dist20.lock ├── image_structure_test.exs ├── generate_dockerfile_src_test.exs ├── base_image_sample_apps_test.exs ├── test_helper.exs └── sample_app_build_test.exs ├── .gitignore ├── .formatter.exs ├── elixir-debian8 ├── cloudbuild.yaml ├── structure-test.json └── Dockerfile ├── elixir-ubuntu16 ├── cloudbuild.yaml ├── structure-test.json └── Dockerfile ├── elixir-ubuntu18 ├── cloudbuild.yaml ├── structure-test.json └── Dockerfile ├── mix.lock ├── elixir-asdf ├── structure-test.json ├── cloudbuild.yaml └── Dockerfile ├── elixir-base ├── structure-test.json ├── cloudbuild.yaml ├── Dockerfile-default.in └── Dockerfile-prebuilt.in ├── elixir-builder ├── cloudbuild.yaml ├── structure-test.json ├── access_cloud_sql └── Dockerfile ├── elixir-prebuilt-erlang ├── cloudbuild.yaml └── Dockerfile ├── elixir-pipeline └── elixir.yaml.in ├── .github └── workflows │ └── ci.yml ├── erlang-versions.txt ├── mix.exs ├── CONTRIBUTING.md ├── erlang-release.sh ├── runtime-release.sh ├── erlang-build.sh ├── lib └── tasks │ └── build_local_images.ex ├── CHANGELOG.md ├── runtime-build.sh └── LICENSE /elixir-generate-dockerfile/.dockerignore: -------------------------------------------------------------------------------- 1 | src/_build 2 | src/deps 3 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/config/config.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_plug/config/config.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix/assets/js/app.js: -------------------------------------------------------------------------------- 1 | import "phoenix_html" 2 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_umbrella_1_3/apps/blog/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_umbrella_1_3/apps/hello/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix/lib/minimal_phoenix.ex: -------------------------------------------------------------------------------- 1 | defmodule MinimalPhoenix do 2 | end 3 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix/assets/css/app.css: -------------------------------------------------------------------------------- 1 | /* This file is for your main application css. */ -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/lib/minimal_phoenix14.ex: -------------------------------------------------------------------------------- 1 | defmodule MinimalPhoenix14 do 2 | end 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_build/ 2 | /cover/ 3 | /deps/ 4 | /doc/ 5 | /tmp/ 6 | /test/tmp 7 | /elixir-base/Dockerfile 8 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/assets/css/app.css: -------------------------------------------------------------------------------- 1 | /* This file is for your main application css. */ 2 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/assets/js/app.js: -------------------------------------------------------------------------------- 1 | import css from "../css/app.css" 2 | import "phoenix_html" 3 | -------------------------------------------------------------------------------- /.formatter.exs: -------------------------------------------------------------------------------- 1 | [ 2 | inputs: [ 3 | "/*.exs", 4 | "lib/**/*.{ex,exs}", 5 | "test/*.exs", 6 | ] 7 | ] 8 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/assets/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/.gitignore: -------------------------------------------------------------------------------- 1 | /_build/ 2 | /cover/ 3 | /deps/ 4 | /doc/ 5 | /.fetch 6 | /erl_crash.dump 7 | *.ez 8 | /test/tmp 9 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/test_helper.exs: -------------------------------------------------------------------------------- 1 | System.put_env("DISABLE_GCP_METADATA_FOR_UNIT_TESTS", "true") 2 | 3 | ExUnit.start() 4 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/.formatter.exs: -------------------------------------------------------------------------------- 1 | [ 2 | import_deps: [:phoenix], 3 | inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/lib/minimal_phoenix14_web/gettext.ex: -------------------------------------------------------------------------------- 1 | defmodule MinimalPhoenix14Web.Gettext do 2 | use Gettext, otp_app: :minimal_phoenix14 3 | end 4 | -------------------------------------------------------------------------------- /elixir-debian8/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'gcr.io/cloud-builders/docker' 3 | args: ['build', '-t', '$_IMAGE:$_TAG', '.'] 4 | 5 | images: 6 | - '$_IMAGE:$_TAG' 7 | -------------------------------------------------------------------------------- /elixir-ubuntu16/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'gcr.io/cloud-builders/docker' 3 | args: ['build', '-t', '$_IMAGE:$_TAG', '.'] 4 | 5 | images: 6 | - '$_IMAGE:$_TAG' 7 | -------------------------------------------------------------------------------- /elixir-ubuntu18/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'gcr.io/cloud-builders/docker' 3 | args: ['build', '-t', '$_IMAGE:$_TAG', '.'] 4 | 5 | images: 6 | - '$_IMAGE:$_TAG' 7 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/assets/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/elixir-runtime/HEAD/test/sample_apps/minimal_phoenix14/assets/static/favicon.ico -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/.formatter.exs: -------------------------------------------------------------------------------- 1 | [ 2 | inputs: [ 3 | "/*.exs", 4 | "{config,lib}/**/*.{ex,exs}", 5 | "test/*.exs", 6 | "test/app_config/**/*.{ex,exs}" 7 | ] 8 | ] 9 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/assets/static/images/phoenix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/elixir-runtime/HEAD/test/sample_apps/minimal_phoenix14/assets/static/images/phoenix.png -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"}, 3 | } 4 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/.gitignore: -------------------------------------------------------------------------------- 1 | /_build/ 2 | /cover/ 3 | /deps/ 4 | /doc/ 5 | /.fetch 6 | erl_crash.dump 7 | *.ez 8 | minimal_phoenix14-*.tar 9 | npm-debug.log 10 | /assets/node_modules/ 11 | /priv/static/ 12 | /config/*.secret.exs 13 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/assets/static/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /elixir-asdf/structure-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "commandTests": [ 4 | { 5 | "name": "test_asdf_installation", 6 | "command": ["asdf", "--version"], 7 | "expectedOutput": ["v[0-9]+\\.[0-9]+\\.[0-9]+"] 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix/config/dev.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :minimal_phoenix, MinimalPhoenixWeb.Endpoint, 4 | http: [port: 4000], 5 | debug_errors: true, 6 | check_origin: false 7 | 8 | config :logger, :console, format: "[$level] $message\n" 9 | 10 | config :phoenix, :stacktrace_depth, 20 11 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'gcr.io/cloud-builders/docker' 3 | args: ['pull', '$_ELIXIR_BASE_IMAGE:$_TAG'] 4 | - name: 'gcr.io/cloud-builders/docker' 5 | args: ['tag', '$_ELIXIR_BASE_IMAGE:$_TAG', 'elixir-base'] 6 | - name: 'gcr.io/cloud-builders/docker' 7 | args: ['build', '-t', '$_IMAGE:$_TAG', '.'] 8 | 9 | images: 10 | - '$_IMAGE:$_TAG' 11 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix/config/config.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :minimal_phoenix, MinimalPhoenixWeb.Endpoint, 4 | url: [host: "localhost"], 5 | secret_key_base: "1MIcAd9Ed+HHVwG5oQhUlBsPa+BPbSdQWtZ3JHDMyRdpZllMAf7xc7piPGjlCrFB" 6 | 7 | config :logger, :console, 8 | format: "$time $metadata[$level] $message\n", 9 | metadata: [:request_id] 10 | 11 | import_config "#{Mix.env}.exs" 12 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix/lib/minimal_phoenix_web/router.ex: -------------------------------------------------------------------------------- 1 | defmodule MinimalPhoenixWeb.Router do 2 | use MinimalPhoenixWeb, :router 3 | 4 | pipeline :browser do 5 | plug :accepts, ["html"] 6 | end 7 | 8 | scope "/", MinimalPhoenixWeb do 9 | pipe_through :browser 10 | get "/", PageController, :index 11 | get "/elixir-version", PageController, :elixir_version 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/lib/minimal_phoenix14_web/router.ex: -------------------------------------------------------------------------------- 1 | defmodule MinimalPhoenix14Web.Router do 2 | use MinimalPhoenix14Web, :router 3 | 4 | pipeline :browser do 5 | plug :accepts, ["html"] 6 | end 7 | 8 | scope "/", MinimalPhoenix14Web do 9 | pipe_through :browser 10 | get "/", PageController, :index 11 | get "/elixir-version", PageController, :elixir_version 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix/config/prod.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :minimal_phoenix, MinimalPhoenixWeb.Endpoint, 4 | load_from_system_env: true, 5 | url: [host: "example.com", port: 80], 6 | cache_static_manifest: "priv/static/cache_manifest.json" 7 | 8 | config :logger, level: :info 9 | 10 | config :phoenix, :serve_endpoints, true 11 | # config :minimal_phoenix, MinimalPhoenixWeb.Endpoint, server: true 12 | -------------------------------------------------------------------------------- /elixir-debian8/structure-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "commandTests": [ 4 | { 5 | "name": "test_locale", 6 | "command": ["locale"], 7 | "expectedOutput": ["LANG=en_US\\.UTF-8\n", "LC_CTYPE=\"en_US\\.UTF-8\"\n"] 8 | }, 9 | { 10 | "name": "test_mix_env_var", 11 | "command": ["sh", "-c", "echo $MIX_ENV"], 12 | "expectedOutput": ["prod\n"] 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /elixir-ubuntu16/structure-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "commandTests": [ 4 | { 5 | "name": "test_locale", 6 | "command": ["locale"], 7 | "expectedOutput": ["LANG=en_US\\.UTF-8\n", "LC_CTYPE=\"en_US\\.UTF-8\"\n"] 8 | }, 9 | { 10 | "name": "test_mix_env_var", 11 | "command": ["sh", "-c", "echo $MIX_ENV"], 12 | "expectedOutput": ["prod\n"] 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /elixir-ubuntu18/structure-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "commandTests": [ 4 | { 5 | "name": "test_locale", 6 | "command": ["locale"], 7 | "expectedOutput": ["LANG=en_US\\.UTF-8\n", "LC_CTYPE=\"en_US\\.UTF-8\"\n"] 8 | }, 9 | { 10 | "name": "test_mix_env_var", 11 | "command": ["sh", "-c", "echo $MIX_ENV"], 12 | "expectedOutput": ["prod\n"] 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_plug/lib/minimal_plug.ex: -------------------------------------------------------------------------------- 1 | defmodule MinimalPlug do 2 | use Application 3 | 4 | def start(_type, _args) do 5 | Plug.Adapters.Cowboy.http(MinimalPlug.Router, [], port: 8080) 6 | end 7 | end 8 | 9 | 10 | defmodule MinimalPlug.Router do 11 | use Plug.Router 12 | 13 | plug :match 14 | plug :dispatch 15 | 16 | get "/" do 17 | conn 18 | |> send_resp(200, "Hello, world!") 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /elixir-asdf/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'gcr.io/cloud-builders/docker' 3 | args: ['pull', '$_OS_BASE_IMAGE:$_TAG'] 4 | - name: 'gcr.io/cloud-builders/docker' 5 | args: ['tag', '$_OS_BASE_IMAGE:$_TAG', 'elixir-os'] 6 | - name: 'gcr.io/cloud-builders/docker' 7 | args: ['build', '-t', '$_IMAGE:$_TAG', 8 | '--build-arg', 'asdf_version=$_ASDF_VERSION', 9 | '.'] 10 | 11 | images: 12 | - '$_IMAGE:$_TAG' 13 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/priv/gettext/en/LC_MESSAGES/errors.po: -------------------------------------------------------------------------------- 1 | ## `msgid`s in this file come from POT (.pot) files. 2 | ## 3 | ## Do not add, change, or remove `msgid`s manually here as 4 | ## they're tied to the ones in the corresponding POT file 5 | ## (with the same domain). 6 | ## 7 | ## Use `mix gettext.extract --merge` or `mix gettext.merge` 8 | ## to merge POT files into PO files. 9 | msgid "" 10 | msgstr "" 11 | "Language: en\n" 12 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/priv/gettext/errors.pot: -------------------------------------------------------------------------------- 1 | ## This is a PO Template file. 2 | ## 3 | ## `msgid`s here are often extracted from source code. 4 | ## Add new translations manually only if they're dynamic 5 | ## translations that can't be statically extracted. 6 | ## 7 | ## Run `mix gettext.extract` to bring this file up to 8 | ## date. Leave `msgstr`s empty as changing them here has no 9 | ## effect: edit them in PO (`.po`) files instead. 10 | 11 | -------------------------------------------------------------------------------- /elixir-base/structure-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "commandTests": [ 4 | { 5 | "name": "test_elixir_installation", 6 | "command": ["elixir", "--version"], 7 | "expectedOutput": ["Elixir [0-9]+\\.[0-9]+\\.[0-9]+"] 8 | }, 9 | { 10 | "name": "test_run_elixir", 11 | "command": ["elixir", "-e", "\"MIX_ENV\" |> System.get_env |> IO.puts"], 12 | "expectedOutput": ["prod\n"] 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix/lib/minimal_phoenix_web/controllers/page_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule MinimalPhoenixWeb.PageController do 2 | use MinimalPhoenixWeb, :controller 3 | 4 | def index(conn, _params) do 5 | default_output = "Hello, world!" 6 | output = Application.get_env(:minimal_phoenix, :test_output, default_output) 7 | text conn, output 8 | end 9 | 10 | def elixir_version(conn, _params) do 11 | text conn, System.version 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/config/prod.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :minimal_phoenix14, MinimalPhoenix14Web.Endpoint, 4 | load_from_system_env: true, 5 | http: [:inet6, port: {:system, "PORT"}], 6 | url: [host: "example.com", port: 80], 7 | cache_static_manifest: "priv/static/cache_manifest.json" 8 | 9 | config :logger, level: :info 10 | 11 | config :phoenix, :serve_endpoints, true 12 | #config :minimal_phoenix14, MinimalPhoenix14Web.Endpoint, server: true 13 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/lib/minimal_phoenix14_web/controllers/page_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule MinimalPhoenix14Web.PageController do 2 | use MinimalPhoenix14Web, :controller 3 | 4 | def index(conn, _params) do 5 | default_output = "Hello, world!" 6 | output = Application.get_env(:minimal_phoenix14, :test_output, default_output) 7 | text conn, output 8 | end 9 | 10 | def elixir_version(conn, _params) do 11 | text conn, System.version 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix/config/staging.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :minimal_phoenix, MinimalPhoenixWeb.Endpoint, 4 | load_from_system_env: true, 5 | url: [host: "example.com", port: 80], 6 | cache_static_manifest: "priv/static/cache_manifest.json" 7 | 8 | config :logger, level: :info 9 | 10 | config :phoenix, :serve_endpoints, true 11 | # config :minimal_phoenix, MinimalPhoenixWeb.Endpoint, server: true 12 | 13 | config :minimal_phoenix, test_output: "from staging" 14 | -------------------------------------------------------------------------------- /elixir-base/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'gcr.io/cloud-builders/docker' 3 | args: ['pull', '$_ASDF_BASE_IMAGE:$_TAG'] 4 | - name: 'gcr.io/cloud-builders/docker' 5 | args: ['tag', '$_ASDF_BASE_IMAGE:$_TAG', 'elixir-asdf'] 6 | - name: 'gcr.io/cloud-builders/docker' 7 | args: ['build', '-t', '$_IMAGE:$_TAG', 8 | '--build-arg', 'erlang_version=$_ERLANG_VERSION', 9 | '--build-arg', 'elixir_version=$_ELIXIR_VERSION', 10 | '.'] 11 | 12 | images: 13 | - '$_IMAGE:$_TAG' 14 | -------------------------------------------------------------------------------- /elixir-builder/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'gcr.io/cloud-builders/docker' 3 | args: ['pull', '$_ASDF_BASE_IMAGE:$_TAG'] 4 | - name: 'gcr.io/cloud-builders/docker' 5 | args: ['tag', '$_ASDF_BASE_IMAGE:$_TAG', 'elixir-asdf'] 6 | - name: 'gcr.io/cloud-builders/docker' 7 | args: ['build', '-t', '$_IMAGE:$_TAG', 8 | '--build-arg', 'nodejs_version=$_NODEJS_VERSION', 9 | '--build-arg', 'gcloud_version=$_GCLOUD_VERSION', 10 | '.'] 11 | 12 | images: 13 | - '$_IMAGE:$_TAG' 14 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix/assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "repository": {}, 3 | "license": "MIT", 4 | "scripts": { 5 | "deploy": "brunch build --production", 6 | "watch": "brunch watch --stdin" 7 | }, 8 | "dependencies": { 9 | "phoenix": "file:../deps/phoenix", 10 | "phoenix_html": "file:../deps/phoenix_html" 11 | }, 12 | "devDependencies": { 13 | "babel-brunch": "6.1.1", 14 | "brunch": "2.10.9", 15 | "clean-css-brunch": "2.10.0", 16 | "uglify-js-brunch": "2.10.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/config/staging.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :minimal_phoenix14, MinimalPhoenix14Web.Endpoint, 4 | http: [:inet6, port: System.get_env("PORT") || 4000], 5 | url: [host: "example.com", port: 80], 6 | cache_static_manifest: "priv/static/cache_manifest.json" 7 | 8 | config :logger, level: :info 9 | 10 | config :phoenix, :serve_endpoints, true 11 | #config :minimal_phoenix14, MinimalPhoenix14Web.Endpoint, server: true 12 | 13 | config :minimal_phoenix14, test_output: "from staging" 14 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/minimal/mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, 3 | "yamerl": {:hex, :yamerl, "0.7.0", "e51dba652dce74c20a88294130b48051ebbbb0be7d76f22de064f0f3ccf0aaf5", [:rebar3], [], "hexpm"}, 4 | "yaml_elixir": {:hex, :yaml_elixir, "2.1.0", "79ec163e0f379dadf4b0e8b5162567dc1feb96c3cfa793105869e2c616ab4342", [:mix], [{:yamerl, "~> 0.7", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm"}, 5 | } 6 | -------------------------------------------------------------------------------- /elixir-prebuilt-erlang/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'gcr.io/cloud-builders/docker' 3 | args: ['pull', '$_ASDF_BASE_IMAGE:$_ASDF_TAG'] 4 | - name: 'gcr.io/cloud-builders/docker' 5 | args: ['tag', '$_ASDF_BASE_IMAGE:$_ASDF_TAG', 'elixir-asdf'] 6 | - name: 'gcr.io/cloud-builders/docker' 7 | args: ['build', '-t', '$_PREBUILT_IMAGE_PREFIX$_ERLANG_VERSION:$_TAG', 8 | '--build-arg', 'erlang_version=$_ERLANG_VERSION', 9 | '.'] 10 | 11 | images: 12 | - '$_PREBUILT_IMAGE_PREFIX$_ERLANG_VERSION:$_TAG' 13 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/lib/minimal_phoenix14/application.ex: -------------------------------------------------------------------------------- 1 | defmodule MinimalPhoenix14.Application do 2 | @moduledoc false 3 | 4 | use Application 5 | 6 | def start(_type, _args) do 7 | children = [MinimalPhoenix14Web.Endpoint] 8 | opts = [strategy: :one_for_one, name: MinimalPhoenix14.Supervisor] 9 | Supervisor.start_link(children, opts) 10 | end 11 | 12 | def config_change(changed, _new, removed) do 13 | MinimalPhoenix14Web.Endpoint.config_change(changed, removed) 14 | :ok 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_1_2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "repository": {}, 3 | "license": "MIT", 4 | "scripts": { 5 | "deploy": "brunch build --production", 6 | "watch": "brunch watch --stdin" 7 | }, 8 | "dependencies": { 9 | "phoenix": "file:../deps/phoenix", 10 | "phoenix_html": "file:../deps/phoenix_html" 11 | }, 12 | "devDependencies": { 13 | "babel-brunch": "6.1.1", 14 | "brunch": "2.10.9", 15 | "clean-css-brunch": "2.10.0", 16 | "uglify-js-brunch": "2.10.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix/lib/minimal_phoenix/application.ex: -------------------------------------------------------------------------------- 1 | defmodule MinimalPhoenix.Application do 2 | use Application 3 | 4 | def start(_type, _args) do 5 | import Supervisor.Spec 6 | children = [supervisor(MinimalPhoenixWeb.Endpoint, [])] 7 | opts = [strategy: :one_for_one, name: MinimalPhoenix.Supervisor] 8 | Supervisor.start_link(children, opts) 9 | end 10 | 11 | def config_change(changed, _new, removed) do 12 | MinimalPhoenixWeb.Endpoint.config_change(changed, removed) 13 | :ok 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_1_3/assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "repository": {}, 3 | "license": "MIT", 4 | "scripts": { 5 | "deploy": "brunch build --production", 6 | "watch": "brunch watch --stdin" 7 | }, 8 | "dependencies": { 9 | "phoenix": "file:../deps/phoenix", 10 | "phoenix_html": "file:../deps/phoenix_html" 11 | }, 12 | "devDependencies": { 13 | "babel-brunch": "6.1.1", 14 | "brunch": "2.10.9", 15 | "clean-css-brunch": "2.10.0", 16 | "uglify-js-brunch": "2.10.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_umbrella_1_3/apps/blog_web/assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "repository": {}, 3 | "license": "MIT", 4 | "scripts": { 5 | "deploy": "brunch build --production", 6 | "watch": "brunch watch --stdin" 7 | }, 8 | "dependencies": { 9 | "phoenix": "file:../deps/phoenix", 10 | "phoenix_html": "file:../deps/phoenix_html" 11 | }, 12 | "devDependencies": { 13 | "babel-brunch": "6.1.1", 14 | "brunch": "2.10.9", 15 | "clean-css-brunch": "2.10.0", 16 | "uglify-js-brunch": "2.10.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix/lib/minimal_phoenix_web.ex: -------------------------------------------------------------------------------- 1 | defmodule MinimalPhoenixWeb do 2 | 3 | def controller do 4 | quote do 5 | use Phoenix.Controller, namespace: MinimalPhoenixWeb 6 | import Plug.Conn 7 | import MinimalPhoenixWeb.Router.Helpers 8 | end 9 | end 10 | 11 | def router do 12 | quote do 13 | use Phoenix.Router 14 | import Plug.Conn 15 | import Phoenix.Controller 16 | end 17 | end 18 | 19 | defmacro __using__(which) when is_atom(which) do 20 | apply(__MODULE__, which, []) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_plug/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule MinimalPlug.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :minimal_plug, 7 | version: "0.1.0", 8 | elixir: "~> 1.5", 9 | start_permanent: Mix.env == :prod, 10 | deps: deps() 11 | ] 12 | end 13 | 14 | def application do 15 | [ 16 | extra_applications: [:logger, :cowboy, :plug], 17 | mod: {MinimalPlug, []} 18 | ] 19 | end 20 | 21 | defp deps do 22 | [ 23 | {:cowboy, "~> 1.0"}, 24 | {:plug, "~> 1.0"} 25 | ] 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /elixir-builder/structure-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "commandTests": [ 4 | { 5 | "name": "test_node_installation", 6 | "command": ["node", "--version"], 7 | "expectedOutput": ["v[0-9]+\\.[0-9]+\\.[0-9]+\n"] 8 | }, 9 | { 10 | "name": "test_cloudsqlproxy_installation", 11 | "command": ["cloud_sql_proxy", "--version"], 12 | "expectedOutput": ["Cloud SQL Auth proxy"] 13 | }, 14 | { 15 | "name": "test_gcloud_installation", 16 | "command": ["gcloud", "--version"], 17 | "expectedOutput": ["Google Cloud SDK"] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/config/config.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :minimal_phoenix14, MinimalPhoenix14Web.Endpoint, 4 | url: [host: "localhost"], 5 | secret_key_base: "02GHEyqgil/1v13KZ02jtj7/fyITF6r/1CsOcWxmGwHPY7BWsIAYEQurgFN8or/q", 6 | render_errors: [view: MinimalPhoenix14Web.ErrorView, accepts: ~w(html json)], 7 | pubsub: [name: MinimalPhoenix14.PubSub, adapter: Phoenix.PubSub.PG2] 8 | 9 | config :logger, :console, 10 | format: "$time $metadata[$level] $message\n", 11 | metadata: [:request_id] 12 | 13 | config :phoenix, :json_library, Jason 14 | 15 | import_config "#{Mix.env()}.exs" 16 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix/rel/config.exs: -------------------------------------------------------------------------------- 1 | use Mix.Releases.Config, 2 | default_release: :default, 3 | default_environment: Mix.env() 4 | 5 | environment :prod do 6 | set include_erts: true 7 | set include_src: false 8 | set cookie: :"fHz/NVBV6lbI 1.7", 9 | compilers: [:phoenix] ++ Mix.compilers, 10 | start_permanent: Mix.env == :prod, 11 | deps: deps() 12 | ] 13 | end 14 | 15 | def application do 16 | [ 17 | mod: {MinimalPhoenix.Application, []}, 18 | extra_applications: [:logger, :runtime_tools] 19 | ] 20 | end 21 | 22 | defp deps do 23 | [ 24 | {:phoenix, "~> 1.3.0"}, 25 | {:phoenix_pubsub, "~> 1.0"}, 26 | {:phoenix_html, "~> 2.10"}, 27 | {:plug_cowboy, "~> 1.0"}, 28 | {:distillery, "~> 1.5"} 29 | ] 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "repository": {}, 3 | "license": "MIT", 4 | "scripts": { 5 | "deploy": "webpack --mode production", 6 | "watch": "webpack --mode development --watch" 7 | }, 8 | "dependencies": { 9 | "phoenix": "file:../deps/phoenix", 10 | "phoenix_html": "file:../deps/phoenix_html" 11 | }, 12 | "devDependencies": { 13 | "@babel/core": "^7.0.0", 14 | "@babel/preset-env": "^7.0.0", 15 | "babel-loader": "^8.0.0", 16 | "copy-webpack-plugin": "^4.5.0", 17 | "css-loader": "^0.28.10", 18 | "mini-css-extract-plugin": "^0.4.0", 19 | "optimize-css-assets-webpack-plugin": "^4.0.0", 20 | "uglifyjs-webpack-plugin": "^1.2.4", 21 | "webpack": "4.4.0", 22 | "webpack-cli": "^2.0.10" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_1_4/assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "repository": {}, 3 | "license": "MIT", 4 | "scripts": { 5 | "deploy": "webpack --mode production", 6 | "watch": "webpack --mode development --watch" 7 | }, 8 | "dependencies": { 9 | "phoenix": "file:../deps/phoenix", 10 | "phoenix_html": "file:../deps/phoenix_html" 11 | }, 12 | "devDependencies": { 13 | "@babel/core": "^7.0.0", 14 | "@babel/preset-env": "^7.0.0", 15 | "babel-loader": "^8.0.0", 16 | "copy-webpack-plugin": "^4.5.0", 17 | "css-loader": "^0.28.10", 18 | "mini-css-extract-plugin": "^0.4.0", 19 | "optimize-css-assets-webpack-plugin": "^4.0.0", 20 | "uglifyjs-webpack-plugin": "^1.2.4", 21 | "webpack": "4.4.0", 22 | "webpack-cli": "^2.0.10" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: "CI tests" 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | push: 8 | branches: 9 | - main 10 | workflow_dispatch: 11 | 12 | jobs: 13 | tests: 14 | if: ${{ github.repository == 'GoogleCloudPlatform/elixir-runtime' }} 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Install Elixir 18 | uses: actions/setup-elixir@v1 19 | with: 20 | otp-version: "23.3.4.11" 21 | elixir-version: "1.13.3" 22 | experimental-otp: true 23 | - name: Checkout repo 24 | uses: actions/checkout@v2 25 | - name: Compile 26 | run: | 27 | mix do deps.get, compile 28 | - name: Build 29 | shell: bash 30 | run: mix build_local_images --prebuilt-images-tag=staging 31 | - name: Test 32 | shell: bash 33 | run: mix test 34 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_plug/mix.lock: -------------------------------------------------------------------------------- 1 | %{"cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, 2 | "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [], [], "hexpm"}, 3 | "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [], [], "hexpm"}, 4 | "plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, 5 | "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [], [], "hexpm"}} 6 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/lib/minimal_phoenix14_web/endpoint.ex: -------------------------------------------------------------------------------- 1 | defmodule MinimalPhoenix14Web.Endpoint do 2 | use Phoenix.Endpoint, otp_app: :minimal_phoenix14 3 | 4 | plug Plug.Static, 5 | at: "/", 6 | from: :minimal_phoenix14, 7 | gzip: false, 8 | only: ~w(css fonts images js favicon.ico robots.txt) 9 | 10 | if code_reloading? do 11 | socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket 12 | plug Phoenix.LiveReloader 13 | plug Phoenix.CodeReloader 14 | end 15 | 16 | plug Plug.RequestId 17 | plug Plug.Logger 18 | 19 | plug Plug.Parsers, 20 | parsers: [:urlencoded, :multipart, :json], 21 | pass: ["*/*"], 22 | json_decoder: Phoenix.json_library() 23 | 24 | plug Plug.MethodOverride 25 | plug Plug.Head 26 | 27 | plug Plug.Session, 28 | store: :cookie, 29 | key: "_minimal_phoenix14_key", 30 | signing_salt: "JDc3EJG2" 31 | 32 | plug MinimalPhoenix14Web.Router 33 | end 34 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/config/dev.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :minimal_phoenix14, MinimalPhoenix14Web.Endpoint, 4 | http: [port: 4000], 5 | debug_errors: true, 6 | code_reloader: true, 7 | check_origin: false, 8 | watchers: [ 9 | node: [ 10 | "node_modules/webpack/bin/webpack.js", 11 | "--mode", 12 | "development", 13 | "--watch-stdin", 14 | cd: Path.expand("../assets", __DIR__) 15 | ] 16 | ] 17 | 18 | config :minimal_phoenix14, MinimalPhoenix14Web.Endpoint, 19 | live_reload: [ 20 | patterns: [ 21 | ~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$}, 22 | ~r{priv/gettext/.*(po)$}, 23 | ~r{lib/minimal_phoenix14_web/views/.*(ex)$}, 24 | ~r{lib/minimal_phoenix14_web/templates/.*(eex)$} 25 | ] 26 | ] 27 | 28 | config :logger, :console, format: "[$level] $message\n" 29 | 30 | config :phoenix, :stacktrace_depth, 20 31 | 32 | config :phoenix, :plug_init_mode, :runtime 33 | -------------------------------------------------------------------------------- /erlang-versions.txt: -------------------------------------------------------------------------------- 1 | 21.0 2 | 21.0.9 3 | 21.1 4 | 21.1.4 5 | 21.2 6 | 21.2.7 7 | 21.3 8 | 21.3.7.1 9 | 21.3.8 10 | 21.3.8.17 11 | 22.0 12 | 22.0.7 13 | 22.1 14 | 22.1.8 15 | 22.1.8.1 16 | 22.2 17 | 22.2.8 18 | 22.3 19 | 22.3.4 20 | 22.3.4.17 21 | 23.0 22 | 23.0.1 23 | 23.0.2 24 | 23.0.3 25 | 23.0.4 26 | 23.1 27 | 23.1.1 28 | 23.1.2 29 | 23.1.3 30 | 23.1.4 31 | 23.1.4.1 32 | 23.1.5 33 | 23.2 34 | 23.2.1 35 | 23.2.2 36 | 23.2.3 37 | 23.2.4 38 | 23.2.5 39 | 23.2.6 40 | 23.2.7 41 | 23.2.7.1 42 | 23.2.7.2 43 | 23.2.7.3 44 | 23.2.7.4 45 | 23.3 46 | 23.3.1 47 | 23.3.2 48 | 23.3.3 49 | 23.3.4 50 | 23.3.4.1 51 | 23.3.4.2 52 | 23.3.4.3 53 | 23.3.4.4 54 | 23.3.4.5 55 | 23.3.4.6 56 | 23.3.4.7 57 | 23.3.4.8 58 | 23.3.4.9 59 | 23.3.4.10 60 | 23.3.4.11 61 | 24.0 62 | 24.0.1 63 | 24.0.2 64 | 24.0.3 65 | 24.0.4 66 | 24.0.5 67 | 24.0.6 68 | 24.1 69 | 24.1.1 70 | 24.1.2 71 | 24.1.3 72 | 24.1.4 73 | 24.1.5 74 | 24.1.6 75 | 24.1.7 76 | 24.2 77 | 24.2.1 78 | 24.2.2 79 | 24.3 80 | 24.3.1 81 | 24.3.2 82 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/app/generate_dockerfile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2017 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | set -e 19 | 20 | # Container Builder changes the home directory, so make sure the global 21 | # .tool-versions file is replicated there. 22 | test "${HOME}" = "/root" || cp /root/.tool-versions ${HOME}/ 23 | 24 | WORKSPACE_DIR=$(/bin/pwd) 25 | cd /app 26 | ./generate_dockerfile --template-dir=/app --workspace-dir=${WORKSPACE_DIR} "$@" 27 | -------------------------------------------------------------------------------- /test/image_structure_test.exs: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | defmodule ImageStructureTest do 16 | use ExUnit.Case 17 | import TestHelper 18 | 19 | structure_tests("elixir-ubuntu18/structure-test.json", "elixir-os") 20 | structure_tests("elixir-asdf/structure-test.json", "elixir-asdf") 21 | structure_tests("elixir-base/structure-test.json", "elixir-base") 22 | structure_tests("elixir-builder/structure-test.json", "elixir-builder") 23 | end 24 | -------------------------------------------------------------------------------- /elixir-prebuilt-erlang/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # Build a prebuilt erlang installer 17 | 18 | FROM elixir-asdf AS builder 19 | ARG erlang_version 20 | RUN asdf update \ 21 | && asdf plugin-update --all \ 22 | && asdf install erlang ${erlang_version} 23 | 24 | 25 | FROM scratch 26 | ARG erlang_version 27 | ARG asdf_dir=/opt/asdf 28 | COPY --from=builder ${asdf_dir}/installs/erlang/${erlang_version} \ 29 | ${asdf_dir}/installs/erlang/${erlang_version} 30 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/mix-dist20.exs: -------------------------------------------------------------------------------- 1 | defmodule MinimalPhoenix14.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :minimal_phoenix14, 7 | version: "0.1.0", 8 | elixir: "~> 1.8", 9 | elixirc_paths: elixirc_paths(Mix.env()), 10 | compilers: [:phoenix, :gettext] ++ Mix.compilers(), 11 | start_permanent: Mix.env() == :prod, 12 | deps: deps() 13 | ] 14 | end 15 | 16 | def application do 17 | [ 18 | mod: {MinimalPhoenix14.Application, []}, 19 | extra_applications: [:logger, :runtime_tools] 20 | ] 21 | end 22 | 23 | defp elixirc_paths(:test), do: ["lib", "test/support"] 24 | defp elixirc_paths(_), do: ["lib"] 25 | 26 | defp deps do 27 | [ 28 | {:phoenix, "~> 1.4.0"}, 29 | {:phoenix_pubsub, "~> 1.1"}, 30 | {:phoenix_html, "~> 2.11"}, 31 | {:phoenix_live_reload, "~> 1.2", only: :dev}, 32 | {:gettext, "~> 0.11"}, 33 | {:jason, "~> 1.0"}, 34 | {:plug_cowboy, "~> 2.0"}, 35 | {:distillery, "~> 2.0.0"} 36 | ] 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/mix-dist21.exs: -------------------------------------------------------------------------------- 1 | defmodule MinimalPhoenix14.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :minimal_phoenix14, 7 | version: "0.1.0", 8 | elixir: "~> 1.9", 9 | elixirc_paths: elixirc_paths(Mix.env()), 10 | compilers: [:phoenix, :gettext] ++ Mix.compilers(), 11 | start_permanent: Mix.env() == :prod, 12 | deps: deps() 13 | ] 14 | end 15 | 16 | def application do 17 | [ 18 | mod: {MinimalPhoenix14.Application, []}, 19 | extra_applications: [:logger, :runtime_tools] 20 | ] 21 | end 22 | 23 | defp elixirc_paths(:test), do: ["lib", "test/support"] 24 | defp elixirc_paths(_), do: ["lib"] 25 | 26 | defp deps do 27 | [ 28 | {:phoenix, "~> 1.4.0"}, 29 | {:phoenix_pubsub, "~> 1.1"}, 30 | {:phoenix_html, "~> 2.11"}, 31 | {:phoenix_live_reload, "~> 1.2", only: :dev}, 32 | {:gettext, "~> 0.11"}, 33 | {:jason, "~> 1.0"}, 34 | {:plug_cowboy, "~> 2.0"}, 35 | {:distillery, "~> 2.1.0"} 36 | ] 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | defmodule ElixirRuntime.Mixfile do 16 | use Mix.Project 17 | 18 | def project do 19 | [ 20 | app: :elixir_runtime, 21 | version: "0.1.0", 22 | elixir: "~> 1.6", 23 | start_permanent: Mix.env() == :prod, 24 | deps: deps() 25 | ] 26 | end 27 | 28 | def application do 29 | [ 30 | applications: [] 31 | ] 32 | end 33 | 34 | def deps do 35 | [ 36 | {:poison, "~> 3.1"} 37 | ] 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /elixir-base/Dockerfile-default.in: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # Base image for Elixir that includes a default Elixir installation. 17 | 18 | FROM elixir-asdf 19 | 20 | ARG erlang_version 21 | ARG elixir_version 22 | 23 | ENV DEFAULT_ERLANG_VERSION=${erlang_version} \ 24 | DEFAULT_ELIXIR_VERSION=${elixir_version} 25 | 26 | # Install Erlang and Elixir via asdf 27 | RUN asdf install erlang ${erlang_version} \ 28 | && asdf global erlang ${erlang_version} \ 29 | && asdf install elixir ${elixir_version} \ 30 | && asdf global elixir ${elixir_version} \ 31 | && mix local.hex --force \ 32 | && mix local.rebar --force 33 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/lib/minimal_phoenix14_web.ex: -------------------------------------------------------------------------------- 1 | defmodule MinimalPhoenix14Web do 2 | def controller do 3 | quote do 4 | use Phoenix.Controller, namespace: MinimalPhoenix14Web 5 | 6 | import Plug.Conn 7 | import MinimalPhoenix14Web.Gettext 8 | alias MinimalPhoenix14Web.Router.Helpers, as: Routes 9 | end 10 | end 11 | 12 | def view do 13 | quote do 14 | use Phoenix.View, 15 | root: "lib/minimal_phoenix14_web/templates", 16 | namespace: MinimalPhoenix14Web 17 | 18 | import Phoenix.Controller, only: [get_flash: 1, get_flash: 2, view_module: 1] 19 | 20 | use Phoenix.HTML 21 | 22 | import MinimalPhoenix14Web.ErrorHelpers 23 | import MinimalPhoenix14Web.Gettext 24 | alias MinimalPhoenix14Web.Router.Helpers, as: Routes 25 | end 26 | end 27 | 28 | def router do 29 | quote do 30 | use Phoenix.Router 31 | import Plug.Conn 32 | import Phoenix.Controller 33 | end 34 | end 35 | 36 | def channel do 37 | quote do 38 | use Phoenix.Channel 39 | import MinimalPhoenix14Web.Gettext 40 | end 41 | end 42 | 43 | defmacro __using__(which) when is_atom(which) do 44 | apply(__MODULE__, which, []) 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_1_2/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Blog.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :blog, 7 | version: "0.0.1", 8 | elixir: "~> 1.4", 9 | elixirc_paths: elixirc_paths(Mix.env()), 10 | compilers: [:phoenix, :gettext] ++ Mix.compilers(), 11 | start_permanent: Mix.env() == :prod, 12 | deps: deps() 13 | ] 14 | end 15 | 16 | # Configuration for the OTP application. 17 | # 18 | # Type `mix help compile.app` for more information. 19 | def application do 20 | [ 21 | mod: {Blog.Application, []}, 22 | extra_applications: [:logger, :runtime_tools] 23 | ] 24 | end 25 | 26 | # Specifies which paths to compile per environment. 27 | defp elixirc_paths(:test), do: ["lib", "test/support"] 28 | defp elixirc_paths(_), do: ["lib"] 29 | 30 | # Specifies your project dependencies. 31 | # 32 | # Type `mix help deps` for examples and options. 33 | defp deps do 34 | [ 35 | {:phoenix, "1.2.5"}, 36 | {:phoenix_pubsub, "~> 1.0"}, 37 | {:phoenix_html, "~> 2.10"}, 38 | {:phoenix_live_reload, "~> 1.0", only: :dev}, 39 | {:gettext, "~> 0.11"}, 40 | {:cowboy, "~> 1.0"} 41 | ] 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_umbrella_1_3/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Blog.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :blog, 7 | version: "0.0.1", 8 | elixir: "~> 1.4", 9 | elixirc_paths: elixirc_paths(Mix.env()), 10 | compilers: [:phoenix, :gettext] ++ Mix.compilers(), 11 | start_permanent: Mix.env() == :prod, 12 | deps: deps() 13 | ] 14 | end 15 | 16 | # Configuration for the OTP application. 17 | # 18 | # Type `mix help compile.app` for more information. 19 | def application do 20 | [ 21 | mod: {Blog.Application, []}, 22 | extra_applications: [:logger, :runtime_tools] 23 | ] 24 | end 25 | 26 | # Specifies which paths to compile per environment. 27 | defp elixirc_paths(:test), do: ["lib", "test/support"] 28 | defp elixirc_paths(_), do: ["lib"] 29 | 30 | # Specifies your project dependencies. 31 | # 32 | # Type `mix help deps` for examples and options. 33 | defp deps do 34 | [ 35 | {:phoenix, "1.3.0"}, 36 | {:phoenix_pubsub, "~> 1.0"}, 37 | {:phoenix_html, "~> 2.10"}, 38 | {:phoenix_live_reload, "~> 1.0", only: :dev}, 39 | {:gettext, "~> 0.11"}, 40 | {:cowboy, "~> 1.0"} 41 | ] 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_1_3/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Blog.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :blog, 7 | version: "0.0.1", 8 | elixir: "~> 1.4", 9 | elixirc_paths: elixirc_paths(Mix.env()), 10 | compilers: [:phoenix, :gettext] ++ Mix.compilers(), 11 | start_permanent: Mix.env() == :prod, 12 | deps: deps() 13 | ] 14 | end 15 | 16 | # Configuration for the OTP application. 17 | # 18 | # Type `mix help compile.app` for more information. 19 | def application do 20 | [ 21 | mod: {Blog.Application, []}, 22 | extra_applications: [:logger, :runtime_tools] 23 | ] 24 | end 25 | 26 | # Specifies which paths to compile per environment. 27 | defp elixirc_paths(:test), do: ["lib", "test/support"] 28 | defp elixirc_paths(_), do: ["lib"] 29 | 30 | # Specifies your project dependencies. 31 | # 32 | # Type `mix help deps` for examples and options. 33 | defp deps do 34 | [ 35 | {:phoenix, "1.3.0"}, 36 | {:phoenix_pubsub, "~> 1.0"}, 37 | {:phoenix_html, "~> 2.10"}, 38 | {:phoenix_live_reload, "~> 1.0", only: :dev}, 39 | {:gettext, "~> 0.11"}, 40 | {:cowboy, "~> 1.0"}, 41 | {:distillery, "~> 2.0.0"} 42 | ] 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule MinimalPhoenix14.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | releases: releases(), 7 | app: :minimal_phoenix14, 8 | version: "0.1.0", 9 | elixir: "~> 1.9", 10 | elixirc_paths: elixirc_paths(Mix.env()), 11 | compilers: [:phoenix, :gettext] ++ Mix.compilers(), 12 | start_permanent: Mix.env() == :prod, 13 | deps: deps() 14 | ] 15 | end 16 | 17 | def application do 18 | [ 19 | mod: {MinimalPhoenix14.Application, []}, 20 | extra_applications: [:logger, :runtime_tools] 21 | ] 22 | end 23 | 24 | defp elixirc_paths(:test), do: ["lib", "test/support"] 25 | defp elixirc_paths(_), do: ["lib"] 26 | 27 | defp deps do 28 | [ 29 | {:phoenix, "~> 1.4.0"}, 30 | {:phoenix_pubsub, "~> 1.1"}, 31 | {:phoenix_html, "~> 2.11"}, 32 | {:phoenix_live_reload, "~> 1.2", only: :dev}, 33 | {:gettext, "~> 0.11"}, 34 | {:jason, "~> 1.0"}, 35 | {:plug_cowboy, "~> 2.0"} 36 | ] 37 | end 38 | 39 | defp releases do 40 | [ 41 | minimal_phoenix14: [ 42 | include_erts: true, 43 | include_executables_for: [:unix], 44 | applications: [runtime_tools: :permanent] 45 | ] 46 | ] 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/assets/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const glob = require('glob'); 3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 4 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 5 | const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); 6 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 7 | 8 | module.exports = (env, options) => ({ 9 | optimization: { 10 | minimizer: [ 11 | new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: false }), 12 | new OptimizeCSSAssetsPlugin({}) 13 | ] 14 | }, 15 | entry: { 16 | './js/app.js': ['./js/app.js'].concat(glob.sync('./vendor/**/*.js')) 17 | }, 18 | output: { 19 | filename: 'app.js', 20 | path: path.resolve(__dirname, '../priv/static/js') 21 | }, 22 | module: { 23 | rules: [ 24 | { 25 | test: /\.js$/, 26 | exclude: /node_modules/, 27 | use: { 28 | loader: 'babel-loader' 29 | } 30 | }, 31 | { 32 | test: /\.css$/, 33 | use: [MiniCssExtractPlugin.loader, 'css-loader'] 34 | } 35 | ] 36 | }, 37 | plugins: [ 38 | new MiniCssExtractPlugin({ filename: '../css/app.css' }), 39 | new CopyWebpackPlugin([{ from: 'static/', to: '../' }]) 40 | ] 41 | }); 42 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_1_4/assets/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const glob = require('glob'); 3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 4 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 5 | const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); 6 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 7 | 8 | module.exports = (env, options) => ({ 9 | optimization: { 10 | minimizer: [ 11 | new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: false }), 12 | new OptimizeCSSAssetsPlugin({}) 13 | ] 14 | }, 15 | entry: { 16 | './js/app.js': ['./js/app.js'].concat(glob.sync('./vendor/**/*.js')) 17 | }, 18 | output: { 19 | filename: 'app.js', 20 | path: path.resolve(__dirname, '../priv/static/js') 21 | }, 22 | module: { 23 | rules: [ 24 | { 25 | test: /\.js$/, 26 | exclude: /node_modules/, 27 | use: { 28 | loader: 'babel-loader' 29 | } 30 | }, 31 | { 32 | test: /\.css$/, 33 | use: [MiniCssExtractPlugin.loader, 'css-loader'] 34 | } 35 | ] 36 | }, 37 | plugins: [ 38 | new MiniCssExtractPlugin({ filename: '../css/app.css' }), 39 | new CopyWebpackPlugin([{ from: 'static/', to: '../' }]) 40 | ] 41 | }); 42 | -------------------------------------------------------------------------------- /test/generate_dockerfile_src_test.exs: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | defmodule GenerateDockerfileSrcTest do 16 | use ExUnit.Case 17 | import TestHelper 18 | 19 | test "Generate dockerfile" do 20 | File.cd!("elixir-generate-dockerfile/src", fn -> 21 | IO.puts("**** Preparing generate-dockerfile tests.") 22 | assert_cmd_succeeds(["mix", "deps.clean", "--all"], show: true) 23 | assert_cmd_succeeds(["mix", "clean"], show: true) 24 | File.rm_rf("mix.lock") 25 | assert_cmd_succeeds(["mix", "deps.get"], show: true) 26 | IO.puts("**** Running generate-dockerfile tests.") 27 | assert_cmd_succeeds(["mix", "test"], show: true, stream: true) 28 | IO.puts("**** Completed generate-dockerfile tests.") 29 | end) 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/minimal/mix.exs: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | defmodule GenerateDockerfile.Mixfile do 16 | use Mix.Project 17 | 18 | def project do 19 | [ 20 | app: :generate_dockerfile, 21 | version: "0.1.0", 22 | elixir: "~> 1.5", 23 | start_permanent: Mix.env() == :prod, 24 | escript: escript(), 25 | deps: deps() 26 | ] 27 | end 28 | 29 | def application do 30 | [ 31 | applications: [ 32 | :logger, 33 | :yaml_elixir 34 | ] 35 | ] 36 | end 37 | 38 | def escript do 39 | [ 40 | main_module: GenerateDockerfile 41 | ] 42 | end 43 | 44 | defp deps do 45 | [ 46 | {:yaml_elixir, "~> 2.0"}, 47 | {:poison, "~> 3.1"} 48 | ] 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/mix.exs: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | defmodule GenerateDockerfile.Mixfile do 16 | use Mix.Project 17 | 18 | def project do 19 | [ 20 | app: :generate_dockerfile, 21 | version: "0.1.0", 22 | elixir: "~> 1.5", 23 | start_permanent: Mix.env() == :prod, 24 | escript: escript(), 25 | deps: deps() 26 | ] 27 | end 28 | 29 | def application do 30 | [ 31 | applications: [ 32 | :logger, 33 | :eex, 34 | :inets, 35 | :yaml_elixir 36 | ] 37 | ] 38 | end 39 | 40 | def escript do 41 | [ 42 | main_module: GenerateDockerfile 43 | ] 44 | end 45 | 46 | defp deps do 47 | [ 48 | {:yaml_elixir, "~> 2.0"}, 49 | {:poison, "~> 3.1"}, 50 | {:tesla, "~> 0.9"} 51 | ] 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /elixir-base/Dockerfile-prebuilt.in: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # Base image for Elixir that includes a default Elixir installation. 17 | 18 | FROM elixir-asdf 19 | 20 | ARG erlang_version 21 | ARG elixir_version 22 | 23 | ENV DEFAULT_ERLANG_VERSION=${erlang_version} \ 24 | DEFAULT_ELIXIR_VERSION=${elixir_version} 25 | 26 | # Install Erlang from prebuilt image 27 | COPY --from=@@PREBUILT_ERLANG_IMAGE@@ \ 28 | /opt/asdf/installs/erlang/${erlang_version} \ 29 | /opt/asdf/installs/erlang/${erlang_version} 30 | 31 | # Set Erlang default and install Elixir via asdf 32 | RUN asdf reshim erlang ${erlang_version} \ 33 | && asdf global erlang ${erlang_version} \ 34 | && asdf install elixir ${elixir_version} \ 35 | && asdf global elixir ${elixir_version} \ 36 | && mix local.hex --force \ 37 | && mix local.rebar --force 38 | -------------------------------------------------------------------------------- /elixir-builder/access_cloud_sql: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2017 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -e 18 | 19 | SQL_TIMEOUT=10 20 | 21 | if [ "$1" == "--lenient" ]; then 22 | ERROR_RESULT=0 23 | else 24 | ERROR_RESULT=1 25 | fi 26 | 27 | if [ -z "${BUILD_CLOUDSQL_INSTANCES}" ]; then 28 | >&2 echo "ERROR: Invoked access_cloud_sql with no CloudSQL instances available" 29 | exit ${ERROR_RESULT} 30 | fi 31 | 32 | rm -f /tmp/cloud_sql_proxy.log 33 | touch /tmp/cloud_sql_proxy.log 34 | cloud_sql_proxy -dir=/cloudsql -instances=${BUILD_CLOUDSQL_INSTANCES} > /tmp/cloud_sql_proxy.log 2>&1 & 35 | if (timeout ${SQL_TIMEOUT}s tail -f --lines=+1 /tmp/cloud_sql_proxy.log &) | grep -qe 'Ready for new connections'; then 36 | echo "Started cloud_sql_proxy." 37 | else 38 | >&2 echo "ERROR: Failed to start cloud_sql_proxy" 39 | >&2 cat /tmp/cloud_sql_proxy.log 40 | exit ${ERROR_RESULT} 41 | fi 42 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"}, 3 | "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"}, 4 | "tesla": {:hex, :tesla, "0.10.0", "e588c7e7f1c0866c81eeed5c38f02a4a94d6309eede336c1e6ca08b0a95abd3f", [:mix], [{:exjsx, ">= 0.1.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "bb10f37d124e2a04950e1642437fc30368cd992bd7370b38eeb3af1ebf1c2029"}, 5 | "yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"}, 6 | "yaml_elixir": {:hex, :yaml_elixir, "2.8.0", "c7ff0034daf57279c2ce902788ce6fdb2445532eb4317e8df4b044209fae6832", [:mix], [{:yamerl, "~> 0.8", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "4b674bd881e373d1ac6a790c64b2ecb69d1fd612c2af3b22de1619c15473830b"}, 7 | } 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | ## Contributor License Agreements 4 | 5 | We'd love to accept your patches! Before we can take them, we 6 | have to jump a couple of legal hurdles. 7 | 8 | Please fill out either the individual or corporate Contributor License Agreement 9 | (CLA). 10 | 11 | * If you are an individual writing original source code and you're sure you 12 | own the intellectual property, then you'll need to sign an [individual CLA] 13 | (https://developers.google.com/open-source/cla/individual). 14 | * If you work for a company that wants to allow you to contribute your work, 15 | then you'll need to sign a [corporate CLA] 16 | (https://developers.google.com/open-source/cla/corporate). 17 | 18 | Follow either of the two links above to access the appropriate CLA and 19 | instructions for how to sign and return it. Once we receive it, we'll be able to 20 | accept your pull requests. 21 | 22 | ## Contributing A Patch 23 | 24 | 1. Submit an issue describing your proposed change to the repo in question. 25 | 2. The repo owner will respond to your issue promptly. 26 | 3. If your proposed change is accepted, and you haven't already done so, sign a 27 | Contributor License Agreement (see details above). 28 | 4. Fork the desired repo, develop and test your code changes. 29 | 5. Ensure that your code adheres to the existing style in the sample to which 30 | you are contributing. 31 | 6. Ensure that your code has an appropriate set of unit tests which all pass. 32 | 7. Submit a pull request. 33 | -------------------------------------------------------------------------------- /elixir-debian8/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # Basic Debian environment for running Elixir web apps. 17 | # Installs dependencies, and sets up locale and environment variables. 18 | # Does not include ERTS or Elixir, however. 19 | 20 | FROM gcr.io/google-appengine/debian8 21 | 22 | # Install key dependencies including locale 23 | ARG DEBIAN_FRONTEND=noninteractive 24 | RUN apt-get update -y \ 25 | && apt-get -y upgrade \ 26 | && apt-get install -y -q --no-install-recommends \ 27 | apt-utils \ 28 | locales \ 29 | unixodbc \ 30 | && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \ 31 | && locale-gen en_US.UTF-8 \ 32 | && apt-get clean \ 33 | && rm -f /var/lib/apt/lists/*_* 34 | 35 | # Set locale and other elements of the production environment 36 | ENV LANG=en_US.UTF-8 \ 37 | LANGUAGE=en_US:en \ 38 | MIX_ENV=prod \ 39 | REPLACE_OS_VARS=true \ 40 | TERM=xterm \ 41 | PORT=8080 42 | 43 | # Initialize entrypoint 44 | WORKDIR /app 45 | EXPOSE 8080 46 | ENTRYPOINT [] 47 | CMD [] 48 | -------------------------------------------------------------------------------- /elixir-ubuntu16/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # Basic Ubuntu environment for running Elixir web apps. 17 | # Installs dependencies, and sets up locale and environment variables. 18 | # Does not include ERTS or Elixir, however. 19 | 20 | FROM gcr.io/gcp-runtimes/ubuntu_16_0_4 21 | 22 | # Install key dependencies including locale 23 | ARG DEBIAN_FRONTEND=noninteractive 24 | RUN apt-get update -y \ 25 | && apt-get -y upgrade \ 26 | && apt-get install -y -q --no-install-recommends \ 27 | apt-utils \ 28 | locales \ 29 | tzdata \ 30 | unixodbc \ 31 | && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \ 32 | && locale-gen en_US.UTF-8 \ 33 | && apt-get clean \ 34 | && rm -f /var/lib/apt/lists/*_* 35 | 36 | # Set locale and other elements of the production environment 37 | ENV LANG=en_US.UTF-8 \ 38 | LANGUAGE=en_US:en \ 39 | MIX_ENV=prod \ 40 | REPLACE_OS_VARS=true \ 41 | TERM=xterm \ 42 | PORT=8080 43 | 44 | # Initialize entrypoint 45 | WORKDIR /app 46 | EXPOSE 8080 47 | ENTRYPOINT [] 48 | CMD [] 49 | -------------------------------------------------------------------------------- /elixir-ubuntu18/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # Basic Ubuntu environment for running Elixir web apps. 17 | # Installs dependencies, and sets up locale and environment variables. 18 | # Does not include ERTS or Elixir, however. 19 | 20 | FROM gcr.io/gcp-runtimes/ubuntu_18_0_4 21 | 22 | # Install key dependencies including locale 23 | ARG DEBIAN_FRONTEND=noninteractive 24 | RUN apt-get update -y \ 25 | && apt-get -y upgrade \ 26 | && apt-get install -y -q --no-install-recommends \ 27 | apt-utils \ 28 | locales \ 29 | tzdata \ 30 | unixodbc \ 31 | && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \ 32 | && locale-gen en_US.UTF-8 \ 33 | && apt-get clean \ 34 | && rm -f /var/lib/apt/lists/*_* 35 | 36 | # Set locale and other elements of the production environment 37 | ENV LANG=en_US.UTF-8 \ 38 | LANGUAGE=en_US:en \ 39 | MIX_ENV=prod \ 40 | REPLACE_OS_VARS=true \ 41 | TERM=xterm \ 42 | PORT=8080 43 | 44 | # Initialize entrypoint 45 | WORKDIR /app 46 | EXPOSE 8080 47 | ENTRYPOINT [] 48 | CMD [] 49 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix/assets/brunch-config.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | // See http://brunch.io/#documentation for docs. 3 | files: { 4 | javascripts: { 5 | joinTo: "js/app.js" 6 | 7 | // To use a separate vendor.js bundle, specify two files path 8 | // http://brunch.io/docs/config#-files- 9 | // joinTo: { 10 | // "js/app.js": /^js/, 11 | // "js/vendor.js": /^(?!js)/ 12 | // } 13 | // 14 | // To change the order of concatenation of files, explicitly mention here 15 | // order: { 16 | // before: [ 17 | // "vendor/js/jquery-2.1.1.js", 18 | // "vendor/js/bootstrap.min.js" 19 | // ] 20 | // } 21 | }, 22 | stylesheets: { 23 | joinTo: "css/app.css" 24 | }, 25 | templates: { 26 | joinTo: "js/app.js" 27 | } 28 | }, 29 | 30 | conventions: { 31 | // This option sets where we should place non-css and non-js assets in. 32 | // By default, we set this to "/assets/static". Files in this directory 33 | // will be copied to `paths.public`, which is "priv/static" by default. 34 | assets: /^(static)/ 35 | }, 36 | 37 | // Phoenix paths configuration 38 | paths: { 39 | // Dependencies and current project directories to watch 40 | watched: ["static", "css", "js", "vendor"], 41 | // Where to compile files to 42 | public: "../priv/static" 43 | }, 44 | 45 | // Configure your plugins 46 | plugins: { 47 | babel: { 48 | // Do not use ES6 compiler in vendor code 49 | ignore: [/vendor/] 50 | } 51 | }, 52 | 53 | modules: { 54 | autoRequire: { 55 | "js/app.js": ["js/app"] 56 | } 57 | }, 58 | 59 | npm: { 60 | enabled: true 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_1_2/brunch-config.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | // See http://brunch.io/#documentation for docs. 3 | files: { 4 | javascripts: { 5 | joinTo: "js/app.js" 6 | 7 | // To use a separate vendor.js bundle, specify two files path 8 | // http://brunch.io/docs/config#-files- 9 | // joinTo: { 10 | // "js/app.js": /^js/, 11 | // "js/vendor.js": /^(?!js)/ 12 | // } 13 | // 14 | // To change the order of concatenation of files, explicitly mention here 15 | // order: { 16 | // before: [ 17 | // "vendor/js/jquery-2.1.1.js", 18 | // "vendor/js/bootstrap.min.js" 19 | // ] 20 | // } 21 | }, 22 | stylesheets: { 23 | joinTo: "css/app.css" 24 | }, 25 | templates: { 26 | joinTo: "js/app.js" 27 | } 28 | }, 29 | 30 | conventions: { 31 | // This option sets where we should place non-css and non-js assets in. 32 | // By default, we set this to "/assets/static". Files in this directory 33 | // will be copied to `paths.public`, which is "priv/static" by default. 34 | assets: /^(static)/ 35 | }, 36 | 37 | // Phoenix paths configuration 38 | paths: { 39 | // Dependencies and current project directories to watch 40 | watched: ["static", "css", "js", "vendor"], 41 | // Where to compile files to 42 | public: "../priv/static" 43 | }, 44 | 45 | // Configure your plugins 46 | plugins: { 47 | babel: { 48 | // Do not use ES6 compiler in vendor code 49 | ignore: [/vendor/] 50 | } 51 | }, 52 | 53 | modules: { 54 | autoRequire: { 55 | "js/app.js": ["js/app"] 56 | } 57 | }, 58 | 59 | npm: { 60 | enabled: true 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_1_3/assets/brunch-config.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | // See http://brunch.io/#documentation for docs. 3 | files: { 4 | javascripts: { 5 | joinTo: "js/app.js" 6 | 7 | // To use a separate vendor.js bundle, specify two files path 8 | // http://brunch.io/docs/config#-files- 9 | // joinTo: { 10 | // "js/app.js": /^js/, 11 | // "js/vendor.js": /^(?!js)/ 12 | // } 13 | // 14 | // To change the order of concatenation of files, explicitly mention here 15 | // order: { 16 | // before: [ 17 | // "vendor/js/jquery-2.1.1.js", 18 | // "vendor/js/bootstrap.min.js" 19 | // ] 20 | // } 21 | }, 22 | stylesheets: { 23 | joinTo: "css/app.css" 24 | }, 25 | templates: { 26 | joinTo: "js/app.js" 27 | } 28 | }, 29 | 30 | conventions: { 31 | // This option sets where we should place non-css and non-js assets in. 32 | // By default, we set this to "/assets/static". Files in this directory 33 | // will be copied to `paths.public`, which is "priv/static" by default. 34 | assets: /^(static)/ 35 | }, 36 | 37 | // Phoenix paths configuration 38 | paths: { 39 | // Dependencies and current project directories to watch 40 | watched: ["static", "css", "js", "vendor"], 41 | // Where to compile files to 42 | public: "../priv/static" 43 | }, 44 | 45 | // Configure your plugins 46 | plugins: { 47 | babel: { 48 | // Do not use ES6 compiler in vendor code 49 | ignore: [/vendor/] 50 | } 51 | }, 52 | 53 | modules: { 54 | autoRequire: { 55 | "js/app.js": ["js/app"] 56 | } 57 | }, 58 | 59 | npm: { 60 | enabled: true 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /elixir-asdf/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # Debian plus asdf for installing and managing ERTS and Elixir. 17 | # Does not include any actual installations of ERTS or Elixir. 18 | 19 | FROM elixir-os 20 | 21 | ARG asdf_dir=/opt/asdf 22 | ARG asdf_version 23 | 24 | # Install prerequisites for building Erlang and Elixir 25 | ARG DEBIAN_FRONTEND=noninteractive 26 | RUN apt-get update -y \ 27 | && apt-get install -y -q --no-install-recommends \ 28 | autoconf \ 29 | automake \ 30 | build-essential \ 31 | curl \ 32 | fop \ 33 | git \ 34 | libncurses-dev \ 35 | libreadline-dev \ 36 | libssl-dev \ 37 | unixodbc-dev \ 38 | unzip \ 39 | xsltproc \ 40 | && apt-get clean \ 41 | && rm -f /var/lib/apt/lists/*_* 42 | 43 | # Install Elixir and Erlang via asdf 44 | ENV ASDF_DIR=${asdf_dir} \ 45 | ASDF_DATA_DIR=${asdf_dir} \ 46 | PATH=${asdf_dir}/bin:${asdf_dir}/shims:${PATH} 47 | RUN mkdir -p ${asdf_dir} \ 48 | && git clone https://github.com/asdf-vm/asdf.git ${asdf_dir} --branch v${asdf_version} \ 49 | && asdf plugin-add erlang \ 50 | && asdf plugin-add elixir 51 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_umbrella_1_3/apps/blog_web/assets/brunch-config.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | // See http://brunch.io/#documentation for docs. 3 | files: { 4 | javascripts: { 5 | joinTo: "js/app.js" 6 | 7 | // To use a separate vendor.js bundle, specify two files path 8 | // http://brunch.io/docs/config#-files- 9 | // joinTo: { 10 | // "js/app.js": /^js/, 11 | // "js/vendor.js": /^(?!js)/ 12 | // } 13 | // 14 | // To change the order of concatenation of files, explicitly mention here 15 | // order: { 16 | // before: [ 17 | // "vendor/js/jquery-2.1.1.js", 18 | // "vendor/js/bootstrap.min.js" 19 | // ] 20 | // } 21 | }, 22 | stylesheets: { 23 | joinTo: "css/app.css" 24 | }, 25 | templates: { 26 | joinTo: "js/app.js" 27 | } 28 | }, 29 | 30 | conventions: { 31 | // This option sets where we should place non-css and non-js assets in. 32 | // By default, we set this to "/assets/static". Files in this directory 33 | // will be copied to `paths.public`, which is "priv/static" by default. 34 | assets: /^(static)/ 35 | }, 36 | 37 | // Phoenix paths configuration 38 | paths: { 39 | // Dependencies and current project directories to watch 40 | watched: ["static", "css", "js", "vendor"], 41 | // Where to compile files to 42 | public: "../priv/static" 43 | }, 44 | 45 | // Configure your plugins 46 | plugins: { 47 | babel: { 48 | // Do not use ES6 compiler in vendor code 49 | ignore: [/vendor/] 50 | } 51 | }, 52 | 53 | modules: { 54 | autoRequire: { 55 | "js/app.js": ["js/app"] 56 | } 57 | }, 58 | 59 | npm: { 60 | enabled: true 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/lib/main.ex: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | defmodule GenerateDockerfile do 16 | require Logger 17 | 18 | def main(args) do 19 | {opts, leftover, unknown} = 20 | OptionParser.parse( 21 | args, 22 | switches: [ 23 | workspace_dir: :string, 24 | os_image: :string, 25 | asdf_image: :string, 26 | builder_image: :string, 27 | prebuilt_erlang_images: :keep, 28 | default_erlang_version: :string, 29 | default_elixir_version: :string, 30 | old_distillery_erlang_version: :string, 31 | old_distillery_elixir_version: :string, 32 | template_dir: :string 33 | ], 34 | aliases: [p: :prebuilt_erlang_images] 35 | ) 36 | 37 | if length(leftover) > 0 do 38 | error("Unprocessed args: #{inspect(leftover)}") 39 | end 40 | 41 | if length(unknown) > 0 do 42 | error("Unrecognized switches: #{inspect(unknown)}") 43 | end 44 | 45 | GenerateDockerfile.Generator.execute(opts) 46 | Logger.flush() 47 | end 48 | 49 | def error(msg) do 50 | Logger.error(msg) 51 | Logger.flush() 52 | System.halt(1) 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /test/base_image_sample_apps_test.exs: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | defmodule BaseImageSampleAppsTest do 16 | use ExUnit.Case 17 | import TestHelper 18 | 19 | test "Minimal plug app" do 20 | dockerfile = """ 21 | FROM elixir-base 22 | COPY . /app/ 23 | RUN mix do deps.get, compile 24 | CMD mix run --no-halt 25 | """ 26 | 27 | run_app_test("minimal_plug", dockerfile) 28 | end 29 | 30 | @apps_dir Path.join(__DIR__, "sample_apps") 31 | @tmp_dir Path.join(__DIR__, "tmp") 32 | 33 | def run_app_test(app_name, dockerfile_content) do 34 | File.rm_rf!(@tmp_dir) 35 | 36 | @apps_dir 37 | |> Path.join(app_name) 38 | |> File.cp_r!(@tmp_dir) 39 | 40 | @tmp_dir 41 | |> Path.join("Dockerfile") 42 | |> File.write!(dockerfile_content) 43 | 44 | File.cd!(@tmp_dir, fn -> 45 | build_docker_image(fn image -> 46 | run_docker_daemon(["-p", "8080:8080", image], fn _container -> 47 | assert_cmd_output( 48 | ["curl", "-s", "-S", "http://localhost:8080"], 49 | ~r{Hello, world!}, 50 | timeout: 10, 51 | show: true, 52 | verbose: true 53 | ) 54 | end) 55 | end) 56 | end) 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # Use the Elixir base image for building. 17 | FROM elixir-base 18 | 19 | # Build the Dockerfile generation script 20 | COPY src/ /src/ 21 | RUN cd /src \ 22 | && mix deps.get \ 23 | && mix escript.build 24 | 25 | 26 | # Use the Elixir base image again for the generator runtime. 27 | FROM elixir-base 28 | 29 | ARG os_image="" 30 | ARG asdf_image="" 31 | ARG builder_image="" 32 | ARG prebuilt_erlang_images="" 33 | ARG default_erlang_version="" 34 | ARG default_elixir_version="" 35 | ARG old_distillery_erlang_version="" 36 | ARG old_distillery_elixir_version="" 37 | 38 | ENV DEFAULT_OS_IMAGE=${os_image} \ 39 | DEFAULT_ASDF_IMAGE=${asdf_image} \ 40 | DEFAULT_BUILDER_IMAGE=${builder_image} \ 41 | DEFAULT_PREBUILT_ERLANG_IMAGES=${prebuilt_erlang_images} \ 42 | DEFAULT_ERLANG_VERSION=${default_erlang_version} \ 43 | DEFAULT_ELIXIR_VERSION=${default_elixir_version} \ 44 | OLD_DISTILLERY_ERLANG_VERSION=${old_distillery_erlang_version} \ 45 | OLD_DISTILLERY_ELIXIR_VERSION=${old_distillery_elixir_version} 46 | 47 | # Install the wrapper script and templates. 48 | COPY app/ /app/ 49 | 50 | # Copy the built generate_dockerfile escript 51 | COPY --from=0 /src/generate_dockerfile /app/generate_dockerfile 52 | 53 | # The entry point runs the generation script. 54 | ENTRYPOINT ["/app/generate_dockerfile.sh"] 55 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_1_4/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Phx14.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :phx14, 7 | version: "0.1.0", 8 | elixir: "~> 1.5", 9 | elixirc_paths: elixirc_paths(Mix.env()), 10 | compilers: [:phoenix, :gettext] ++ Mix.compilers(), 11 | start_permanent: Mix.env() == :prod, 12 | aliases: aliases(), 13 | deps: deps() 14 | ] 15 | end 16 | 17 | # Configuration for the OTP application. 18 | # 19 | # Type `mix help compile.app` for more information. 20 | def application do 21 | [ 22 | mod: {Phx14.Application, []}, 23 | extra_applications: [:logger, :runtime_tools] 24 | ] 25 | end 26 | 27 | # Specifies which paths to compile per environment. 28 | defp elixirc_paths(:test), do: ["lib", "test/support"] 29 | defp elixirc_paths(_), do: ["lib"] 30 | 31 | # Specifies your project dependencies. 32 | # 33 | # Type `mix help deps` for examples and options. 34 | defp deps do 35 | [ 36 | {:phoenix, "~> 1.4.0"}, 37 | {:phoenix_pubsub, "~> 1.1"}, 38 | {:phoenix_ecto, "~> 4.0"}, 39 | {:ecto_sql, "~> 3.0"}, 40 | {:postgrex, ">= 0.0.0"}, 41 | {:phoenix_html, "~> 2.11"}, 42 | {:phoenix_live_reload, "~> 1.2", only: :dev}, 43 | {:gettext, "~> 0.11"}, 44 | {:jason, "~> 1.0"}, 45 | {:plug_cowboy, "~> 2.0"}, 46 | {:distillery, "~> 2.1.0"} 47 | ] 48 | end 49 | 50 | # Aliases are shortcuts or tasks specific to the current project. 51 | # For example, to create, migrate and run the seeds file at once: 52 | # 53 | # $ mix ecto.setup 54 | # 55 | # See the documentation for `Mix` for more info on aliases. 56 | defp aliases do 57 | [ 58 | "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], 59 | "ecto.reset": ["ecto.drop", "ecto.setup"], 60 | test: ["ecto.create --quiet", "ecto.migrate", "test"] 61 | ] 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /elixir-builder/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # Builder image. 17 | 18 | FROM elixir-asdf 19 | 20 | # Parameters 21 | ARG nodejs_version 22 | ARG gcloud_version 23 | 24 | ARG nodejs_dir=/opt/nodejs 25 | ARG gcloud_dir=/opt/gcloud 26 | ARG misc_bin_dir=/opt/bin 27 | 28 | # Install python which is necessary for gcloud 29 | ARG DEBIAN_FRONTEND=noninteractive 30 | RUN apt-get update -y \ 31 | && apt-get install -y -q python2.7 \ 32 | && apt-get clean \ 33 | && rm -f /var/lib/apt/lists/*_* 34 | 35 | # Install build script files. 36 | RUN mkdir -p ${misc_bin_dir} 37 | COPY access_cloud_sql ${misc_bin_dir}/ 38 | 39 | # Install NodeJS 40 | RUN mkdir -p ${nodejs_dir} \ 41 | && curl -s https://nodejs.org/dist/v${nodejs_version}/node-v${nodejs_version}-linux-x64.tar.gz \ 42 | | tar xzf - --directory=${nodejs_dir} --strip-components=1 43 | 44 | # Install CloudSQL Proxy 45 | RUN curl -s https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 > /opt/bin/cloud_sql_proxy \ 46 | && chmod a+x /opt/bin/cloud_sql_proxy \ 47 | && mkdir /cloudsql 48 | 49 | # Install Google Cloud SDK 50 | RUN mkdir -p ${gcloud_dir} \ 51 | && curl -s https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-${gcloud_version}-linux-x86_64.tar.gz \ 52 | | tar xzf - --directory=${gcloud_dir} --strip-components=1 53 | 54 | ENV PATH=${misc_bin_dir}:${nodejs_dir}/bin:${gcloud_dir}/bin:${PATH} 55 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_umbrella_1_3/mix.lock: -------------------------------------------------------------------------------- 1 | %{"cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, 2 | "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [], [], "hexpm"}, 3 | "file_system": {:hex, :file_system, "0.1.5", "4f20ec7d186cd84ad478bd5477061aeb993b36b5458872041bbabba8b09d36ff", [], [], "hexpm"}, 4 | "gettext": {:hex, :gettext, "0.13.1", "5e0daf4e7636d771c4c71ad5f3f53ba09a9ae5c250e1ab9c42ba9edccc476263", [], [], "hexpm"}, 5 | "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [], [], "hexpm"}, 6 | "phoenix": {:hex, :phoenix, "1.3.0", "1c01124caa1b4a7af46f2050ff11b267baa3edb441b45dbf243e979cd4c5891b", [], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, 7 | "phoenix_html": {:hex, :phoenix_html, "2.10.4", "d4f99c32d5dc4918b531fdf163e1fd7cf20acdd7703f16f5d02d4db36de803b7", [], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, 8 | "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.1.0", "975d6bba8d75d20a63659b6f057eecaa1e96b42fa19cebc304031ac3184d744e", [], [{:file_system, "~> 0.1", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2 or ~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"}, 9 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.2", "bfa7fd52788b5eaa09cb51ff9fcad1d9edfeb68251add458523f839392f034c1", [], [], "hexpm"}, 10 | "plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, 11 | "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [], [], "hexpm"}, 12 | "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [], [], "hexpm"}} 13 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_1_2/mix.lock: -------------------------------------------------------------------------------- 1 | %{"cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, 2 | "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [], [], "hexpm"}, 3 | "file_system": {:hex, :file_system, "0.1.5", "4f20ec7d186cd84ad478bd5477061aeb993b36b5458872041bbabba8b09d36ff", [], [], "hexpm"}, 4 | "gettext": {:hex, :gettext, "0.13.1", "5e0daf4e7636d771c4c71ad5f3f53ba09a9ae5c250e1ab9c42ba9edccc476263", [], [], "hexpm"}, 5 | "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [], [], "hexpm"}, 6 | "phoenix": {:hex, :phoenix, "1.2.5", "dbc45a5f8fb522aaa815b003f2d5598bc45e23102ae3e265768848ab23eafcb7", [], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, 7 | "phoenix_html": {:hex, :phoenix_html, "2.10.4", "d4f99c32d5dc4918b531fdf163e1fd7cf20acdd7703f16f5d02d4db36de803b7", [], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, 8 | "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.1.0", "975d6bba8d75d20a63659b6f057eecaa1e96b42fa19cebc304031ac3184d744e", [], [{:file_system, "~> 0.1", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2 or ~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"}, 9 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.2", "bfa7fd52788b5eaa09cb51ff9fcad1d9edfeb68251add458523f839392f034c1", [], [], "hexpm"}, 10 | "plug": {:hex, :plug, "1.3.5", "7503bfcd7091df2a9761ef8cecea666d1f2cc454cbbaf0afa0b6e259203b7031", [], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, 11 | "poison": {:hex, :poison, "2.2.0", "4763b69a8a77bd77d26f477d196428b741261a761257ff1cf92753a0d4d24a63", [], [], "hexpm"}, 12 | "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [], [], "hexpm"}} 13 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix/mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, 3 | "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"}, 4 | "distillery": {:hex, :distillery, "1.5.5", "c6132d5c0152bdce6850fb6c942d58f1971b169b6965d908dc4e8767cfa51a95", [:mix], [], "hexpm"}, 5 | "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"}, 6 | "phoenix": {:hex, :phoenix, "1.3.4", "aaa1b55e5523083a877bcbe9886d9ee180bf2c8754905323493c2ac325903dc5", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, 7 | "phoenix_html": {:hex, :phoenix_html, "2.13.3", "850e292ff6e204257f5f9c4c54a8cb1f6fbc16ed53d360c2b780a3d0ba333867", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, 8 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"}, 9 | "plug": {:hex, :plug, "1.8.2", "0bcce1daa420f189a6491f3940cc77ea7fb1919761175c9c3b59800d897440fc", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"}, 10 | "plug_cowboy": {:hex, :plug_cowboy, "1.0.0", "2e2a7d3409746d335f451218b8bb0858301c3de6d668c3052716c909936eb57a", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, 11 | "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"}, 12 | "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, 13 | "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"}, 14 | } 15 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_1_3/mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "artificery": {:hex, :artificery, "0.4.2", "3ded6e29e13113af52811c72f414d1e88f711410cac1b619ab3a2666bbd7efd4", [:mix], [], "hexpm"}, 3 | "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, 4 | "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"}, 5 | "distillery": {:hex, :distillery, "2.0.14", "25fc1cdad06282334dbf4a11b6e869cc002855c4e11825157498491df2eed594", [:mix], [{:artificery, "~> 0.2", [hex: :artificery, repo: "hexpm", optional: false]}], "hexpm"}, 6 | "file_system": {:hex, :file_system, "0.2.7", "e6f7f155970975789f26e77b8b8d8ab084c59844d8ecfaf58cbda31c494d14aa", [:mix], [], "hexpm"}, 7 | "gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"}, 8 | "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"}, 9 | "phoenix": {:hex, :phoenix, "1.3.0", "1c01124caa1b4a7af46f2050ff11b267baa3edb441b45dbf243e979cd4c5891b", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, 10 | "phoenix_html": {:hex, :phoenix_html, "2.13.3", "850e292ff6e204257f5f9c4c54a8cb1f6fbc16ed53d360c2b780a3d0ba333867", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, 11 | "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.1.7", "425fff579085f7eacaf009e71940be07338c8d8b78d16e307c50c7d82a381497", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2 or ~> 1.3 or ~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"}, 12 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"}, 13 | "plug": {:hex, :plug, "1.8.2", "0bcce1daa420f189a6491f3940cc77ea7fb1919761175c9c3b59800d897440fc", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"}, 14 | "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"}, 15 | "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, 16 | "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"}, 17 | } 18 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "cowboy": {:hex, :cowboy, "2.6.3", "99aa50e94e685557cad82e704457336a453d4abcb77839ad22dbe71f311fcc06", [:rebar3], [{:cowlib, "~> 2.7.3", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, 3 | "cowlib": {:hex, :cowlib, "2.7.3", "a7ffcd0917e6d50b4d5fb28e9e2085a0ceb3c97dea310505f7460ff5ed764ce9", [:rebar3], [], "hexpm"}, 4 | "file_system": {:hex, :file_system, "0.2.7", "e6f7f155970975789f26e77b8b8d8ab084c59844d8ecfaf58cbda31c494d14aa", [:mix], [], "hexpm"}, 5 | "gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"}, 6 | "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, 7 | "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"}, 8 | "phoenix": {:hex, :phoenix, "1.4.9", "746d098e10741c334d88143d3c94cab1756435f94387a63441792e66ec0ee974", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"}, 9 | "phoenix_html": {:hex, :phoenix_html, "2.13.3", "850e292ff6e204257f5f9c4c54a8cb1f6fbc16ed53d360c2b780a3d0ba333867", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, 10 | "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.1", "274a4b07c4adbdd7785d45a8b0bb57634d0b4f45b18d2c508b26c0344bd59b8f", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"}, 11 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"}, 12 | "plug": {:hex, :plug, "1.8.2", "0bcce1daa420f189a6491f3940cc77ea7fb1919761175c9c3b59800d897440fc", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"}, 13 | "plug_cowboy": {:hex, :plug_cowboy, "2.1.0", "b75768153c3a8a9e8039d4b25bb9b14efbc58e9c4a6e6a270abff1cd30cbe320", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, 14 | "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"}, 15 | "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, 16 | "telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"}, 17 | } 18 | -------------------------------------------------------------------------------- /erlang-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2017 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | set -e 19 | 20 | DIRNAME=$(dirname $0) 21 | 22 | OS_NAME=ubuntu18 23 | PROJECT= 24 | NAMESPACE=elixir 25 | IMAGE_TAG=staging 26 | PREBUILT_ERLANG_VERSIONS=() 27 | if [ -f ${DIRNAME}/erlang-versions.txt ]; then 28 | mapfile -t PREBUILT_ERLANG_VERSIONS < ${DIRNAME}/erlang-versions.txt 29 | fi 30 | 31 | show_usage() { 32 | echo "Usage: ./erlang-release.sh [flags...]" >&2 33 | echo "Flags:" >&2 34 | echo ' -e : comma separated versions (defaults to erlang-versions.txt)' >&2 35 | echo ' -n : set the images namespace (defaults to `elixir`)' >&2 36 | echo ' -o : build against the given os base image (defaults to `ubuntu18`)' >&2 37 | echo ' -p : set the images project (defaults to current gcloud config setting)' >&2 38 | echo ' -t : the image tag to release (defaults to `staging`)' >&2 39 | } 40 | 41 | OPTIND=1 42 | while getopts ":e:n:p:t:h" opt; do 43 | case ${opt} in 44 | e) 45 | if [ "${OPTARG}" = "none" ]; then 46 | PREBUILT_ERLANG_VERSIONS=() 47 | else 48 | IFS=',' read -r -a PREBUILT_ERLANG_VERSIONS <<< "${OPTARG}" 49 | fi 50 | ;; 51 | n) 52 | NAMESPACE=${OPTARG} 53 | ;; 54 | o) 55 | OS_NAME=${OPTARG} 56 | ;; 57 | p) 58 | PROJECT=${OPTARG} 59 | ;; 60 | t) 61 | IMAGE_TAG=${OPTARG} 62 | ;; 63 | h) 64 | show_usage 65 | exit 0 66 | ;; 67 | \?) 68 | echo "Invalid option: -${OPTARG}" >&2 69 | echo >&2 70 | show_usage 71 | exit 1 72 | ;; 73 | :) 74 | echo "Option ${OPTARG} requires a parameter" >&2 75 | echo >&2 76 | show_usage 77 | exit 1 78 | ;; 79 | esac 80 | done 81 | shift $((OPTIND-1)) 82 | 83 | if [ "${#PREBUILT_ERLANG_VERSIONS[@]}" = "0" ]; then 84 | echo "No versions to release. Aborting." 85 | exit 1 86 | fi 87 | 88 | if [ -z "${PROJECT}" ]; then 89 | PROJECT=$(gcloud config get-value project) 90 | echo "**** Using project from gcloud config: ${PROJECT}" >&2 91 | fi 92 | 93 | PREBUILT_IMAGE_PREFIX=gcr.io/${PROJECT}/${NAMESPACE}/${OS_NAME}/prebuilt/otp- 94 | 95 | for version in "${PREBUILT_ERLANG_VERSIONS[@]}"; do 96 | gcloud container images add-tag --project ${PROJECT} \ 97 | ${PREBUILT_IMAGE_PREFIX}${version}:${IMAGE_TAG} \ 98 | ${PREBUILT_IMAGE_PREFIX}${version}:latest -q 99 | echo "**** Tagged image ${PREBUILT_IMAGE_PREFIX}${version}:${IMAGE_TAG} as latest" 100 | done 101 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/mix-dist21.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "artificery": {:hex, :artificery, "0.4.2", "3ded6e29e13113af52811c72f414d1e88f711410cac1b619ab3a2666bbd7efd4", [:mix], [], "hexpm"}, 3 | "cowboy": {:hex, :cowboy, "2.6.3", "99aa50e94e685557cad82e704457336a453d4abcb77839ad22dbe71f311fcc06", [:rebar3], [{:cowlib, "~> 2.7.3", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, 4 | "cowlib": {:hex, :cowlib, "2.7.3", "a7ffcd0917e6d50b4d5fb28e9e2085a0ceb3c97dea310505f7460ff5ed764ce9", [:rebar3], [], "hexpm"}, 5 | "distillery": {:hex, :distillery, "2.1.1", "f9332afc2eec8a1a2b86f22429e068ef35f84a93ea1718265e740d90dd367814", [:mix], [{:artificery, "~> 0.2", [hex: :artificery, repo: "hexpm", optional: false]}], "hexpm"}, 6 | "file_system": {:hex, :file_system, "0.2.7", "e6f7f155970975789f26e77b8b8d8ab084c59844d8ecfaf58cbda31c494d14aa", [:mix], [], "hexpm"}, 7 | "gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"}, 8 | "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, 9 | "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"}, 10 | "phoenix": {:hex, :phoenix, "1.4.9", "746d098e10741c334d88143d3c94cab1756435f94387a63441792e66ec0ee974", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"}, 11 | "phoenix_html": {:hex, :phoenix_html, "2.13.3", "850e292ff6e204257f5f9c4c54a8cb1f6fbc16ed53d360c2b780a3d0ba333867", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, 12 | "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.1", "274a4b07c4adbdd7785d45a8b0bb57634d0b4f45b18d2c508b26c0344bd59b8f", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"}, 13 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"}, 14 | "plug": {:hex, :plug, "1.8.2", "0bcce1daa420f189a6491f3940cc77ea7fb1919761175c9c3b59800d897440fc", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"}, 15 | "plug_cowboy": {:hex, :plug_cowboy, "2.1.0", "b75768153c3a8a9e8039d4b25bb9b14efbc58e9c4a6e6a270abff1cd30cbe320", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, 16 | "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"}, 17 | "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, 18 | "telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"}, 19 | } 20 | -------------------------------------------------------------------------------- /test/sample_apps/minimal_phoenix14/mix-dist20.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "artificery": {:hex, :artificery, "0.4.2", "3ded6e29e13113af52811c72f414d1e88f711410cac1b619ab3a2666bbd7efd4", [:mix], [], "hexpm"}, 3 | "cowboy": {:hex, :cowboy, "2.6.3", "99aa50e94e685557cad82e704457336a453d4abcb77839ad22dbe71f311fcc06", [:rebar3], [{:cowlib, "~> 2.7.3", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, 4 | "cowlib": {:hex, :cowlib, "2.7.3", "a7ffcd0917e6d50b4d5fb28e9e2085a0ceb3c97dea310505f7460ff5ed764ce9", [:rebar3], [], "hexpm"}, 5 | "distillery": {:hex, :distillery, "2.0.14", "25fc1cdad06282334dbf4a11b6e869cc002855c4e11825157498491df2eed594", [:mix], [{:artificery, "~> 0.2", [hex: :artificery, repo: "hexpm", optional: false]}], "hexpm"}, 6 | "file_system": {:hex, :file_system, "0.2.7", "e6f7f155970975789f26e77b8b8d8ab084c59844d8ecfaf58cbda31c494d14aa", [:mix], [], "hexpm"}, 7 | "gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"}, 8 | "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, 9 | "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"}, 10 | "phoenix": {:hex, :phoenix, "1.4.9", "746d098e10741c334d88143d3c94cab1756435f94387a63441792e66ec0ee974", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"}, 11 | "phoenix_html": {:hex, :phoenix_html, "2.13.3", "850e292ff6e204257f5f9c4c54a8cb1f6fbc16ed53d360c2b780a3d0ba333867", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, 12 | "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.1", "274a4b07c4adbdd7785d45a8b0bb57634d0b4f45b18d2c508b26c0344bd59b8f", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"}, 13 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"}, 14 | "plug": {:hex, :plug, "1.8.2", "0bcce1daa420f189a6491f3940cc77ea7fb1919761175c9c3b59800d897440fc", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"}, 15 | "plug_cowboy": {:hex, :plug_cowboy, "2.1.0", "b75768153c3a8a9e8039d4b25bb9b14efbc58e9c4a6e6a270abff1cd30cbe320", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, 16 | "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"}, 17 | "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, 18 | "telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"}, 19 | } 20 | -------------------------------------------------------------------------------- /runtime-release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2017 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | set -e 19 | 20 | DIRNAME=$(dirname $0) 21 | 22 | OS_NAME=ubuntu18 23 | PROJECT= 24 | NAMESPACE=elixir 25 | IMAGE_TAG=staging 26 | UPLOAD_BUCKET= 27 | 28 | show_usage() { 29 | echo "Usage: ./runtime-release.sh [flags...]" >&2 30 | echo "Flags:" >&2 31 | echo ' -b : promote the runtime definition in this gcs bucket (defaults to no promote)' >&2 32 | echo ' -n : set the images namespace (defaults to `elixir`)' >&2 33 | echo ' -o : build against the given os base image (defaults to `ubuntu18`)' >&2 34 | echo ' -p : set the images project (defaults to current gcloud config setting)' >&2 35 | echo ' -t : the image tag to release (defaults to `staging`)' >&2 36 | } 37 | 38 | OPTIND=1 39 | while getopts ":b:n:p:t:h" opt; do 40 | case ${opt} in 41 | b) 42 | UPLOAD_BUCKET=${OPTARG} 43 | ;; 44 | n) 45 | NAMESPACE=${OPTARG} 46 | ;; 47 | o) 48 | OS_NAME=${OPTARG} 49 | ;; 50 | p) 51 | PROJECT=${OPTARG} 52 | ;; 53 | t) 54 | IMAGE_TAG=${OPTARG} 55 | ;; 56 | h) 57 | show_usage 58 | exit 0 59 | ;; 60 | \?) 61 | echo "Invalid option: -${OPTARG}" >&2 62 | echo >&2 63 | show_usage 64 | exit 1 65 | ;; 66 | :) 67 | echo "Option ${OPTARG} requires a parameter" >&2 68 | echo >&2 69 | show_usage 70 | exit 1 71 | ;; 72 | esac 73 | done 74 | shift $((OPTIND-1)) 75 | 76 | if [ -z "${PROJECT}" ]; then 77 | PROJECT=$(gcloud config get-value project) 78 | echo "**** Using project from gcloud config: ${PROJECT}" >&2 79 | fi 80 | 81 | OS_BASE_IMAGE=gcr.io/${PROJECT}/${NAMESPACE}/${OS_NAME} 82 | ASDF_BASE_IMAGE=gcr.io/${PROJECT}/${NAMESPACE}/${OS_NAME}/asdf 83 | ELIXIR_BASE_IMAGE=gcr.io/${PROJECT}/${NAMESPACE}/${OS_NAME}/base 84 | BUILDER_IMAGE=gcr.io/${PROJECT}/${NAMESPACE}/${OS_NAME}/builder 85 | GENERATE_DOCKERFILE_IMAGE=gcr.io/${PROJECT}/${NAMESPACE}/${OS_NAME}/generate-dockerfile 86 | 87 | gcloud container images add-tag --project ${PROJECT} \ 88 | ${OS_BASE_IMAGE}:$IMAGE_TAG ${OS_BASE_IMAGE}:latest -q 89 | echo "**** Tagged image ${OS_BASE_IMAGE}:${IMAGE_TAG} as latest" 90 | gcloud container images add-tag --project ${PROJECT} \ 91 | ${ASDF_BASE_IMAGE}:$IMAGE_TAG ${ASDF_BASE_IMAGE}:latest -q 92 | echo "**** Tagged image ${ASDF_BASE_IMAGE}:${IMAGE_TAG} as latest" 93 | gcloud container images add-tag --project ${PROJECT} \ 94 | ${ELIXIR_BASE_IMAGE}:$IMAGE_TAG ${ELIXIR_BASE_IMAGE}:latest -q 95 | echo "**** Tagged image ${ELIXIR_BASE_IMAGE}:${IMAGE_TAG} as latest" 96 | gcloud container images add-tag --project ${PROJECT} \ 97 | ${BUILDER_IMAGE}:$IMAGE_TAG ${BUILDER_IMAGE}:latest -q 98 | echo "**** Tagged image ${BUILDER_IMAGE}:${IMAGE_TAG} as latest" 99 | gcloud container images add-tag --project ${PROJECT} \ 100 | ${GENERATE_DOCKERFILE_IMAGE}:$IMAGE_TAG ${GENERATE_DOCKERFILE_IMAGE}:latest -q 101 | echo "**** Tagged image ${GENERATE_DOCKERFILE_IMAGE}:${IMAGE_TAG} as latest" 102 | 103 | if [ -n "${UPLOAD_BUCKET}" ]; then 104 | gsutil cp gs://${UPLOAD_BUCKET}/elixir-${IMAGE_TAG}.yaml gs://${UPLOAD_BUCKET}/elixir.yaml 105 | echo "**** Promoted runtime config gs://${UPLOAD_BUCKET}/elixir-${IMAGE_TAG}.yaml to gs://${UPLOAD_BUCKET}/elixir.yaml" 106 | fi 107 | -------------------------------------------------------------------------------- /erlang-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2017 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | set -e 19 | 20 | DIRNAME=$(dirname $0) 21 | 22 | OS_NAME=ubuntu18 23 | PROJECT= 24 | NAMESPACE=elixir 25 | IMAGE_TAG= 26 | ASDF_IMAGE_TAG=staging 27 | PREBUILT_ERLANG_VERSIONS=() 28 | if [ -f ${DIRNAME}/erlang-versions.txt ]; then 29 | mapfile -t PREBUILT_ERLANG_VERSIONS < ${DIRNAME}/erlang-versions.txt 30 | fi 31 | STAGING_FLAG= 32 | AUTO_YES= 33 | BUILD_TIMEOUT=90m 34 | 35 | show_usage() { 36 | echo 'Usage: ./erlang-build.sh [flags...]' >&2 37 | echo 'Flags:' >&2 38 | echo ' -a : use this asdf image tag (defaults to `staging`)' >&2 39 | echo ' -e : comma separated versions (defaults to erlang-versions.txt)' >&2 40 | echo ' -n : set the images namespace (defaults to `elixir`)' >&2 41 | echo ' -o : build against the given os base image (defaults to `ubuntu18`)' >&2 42 | echo ' -p : set the images project (defaults to current gcloud config setting)' >&2 43 | echo ' -s: also tag new images as `staging`' >&2 44 | echo ' -t : set the new image tag (creates a new tag if not provided)' >&2 45 | echo ' -y: automatically confirm' >&2 46 | } 47 | 48 | OPTIND=1 49 | while getopts ":a:e:n:p:st:yh" opt; do 50 | case ${opt} in 51 | a) 52 | ASDF_IMAGE_TAG=${OPTARG} 53 | ;; 54 | e) 55 | if [ "${OPTARG}" = "none" ]; then 56 | PREBUILT_ERLANG_VERSIONS=() 57 | else 58 | IFS=',' read -r -a PREBUILT_ERLANG_VERSIONS <<< "${OPTARG}" 59 | fi 60 | ;; 61 | n) 62 | NAMESPACE=${OPTARG} 63 | ;; 64 | o) 65 | OS_NAME=${OPTARG} 66 | ;; 67 | p) 68 | PROJECT=${OPTARG} 69 | ;; 70 | s) 71 | STAGING_FLAG="true" 72 | ;; 73 | t) 74 | IMAGE_TAG=${OPTARG} 75 | ;; 76 | y) 77 | AUTO_YES="true" 78 | ;; 79 | h) 80 | show_usage 81 | exit 0 82 | ;; 83 | \?) 84 | echo "Invalid option: -${OPTARG}" >&2 85 | echo >&2 86 | show_usage 87 | exit 1 88 | ;; 89 | :) 90 | echo "Option $OPTARG requires a parameter" >&2 91 | echo >&2 92 | show_usage 93 | exit 1 94 | ;; 95 | esac 96 | done 97 | shift $((OPTIND-1)) 98 | 99 | if [ "${#PREBUILT_ERLANG_VERSIONS[@]}" = "0" ]; then 100 | echo "No versions to build. Aborting." 101 | exit 1 102 | fi 103 | 104 | if [ -z "${PROJECT}" ]; then 105 | PROJECT=$(gcloud config get-value project) 106 | echo "Using project from gcloud config: ${PROJECT}" >&2 107 | fi 108 | if [ -z "${IMAGE_TAG}" ]; then 109 | IMAGE_TAG=$(date +%Y-%m-%d-%H%M%S) 110 | echo "Creating new IMAGE_TAG: ${IMAGE_TAG}" >&2 111 | fi 112 | 113 | ASDF_BASE_IMAGE=gcr.io/${PROJECT}/${NAMESPACE}/${OS_NAME}/asdf 114 | PREBUILT_IMAGE_PREFIX=gcr.io/${PROJECT}/${NAMESPACE}/${OS_NAME}/prebuilt/otp- 115 | 116 | echo 117 | echo "Using ${ASDF_BASE_IMAGE}:${ASDF_IMAGE_TAG}" 118 | echo "Building images:" 119 | for version in "${PREBUILT_ERLANG_VERSIONS[@]}"; do 120 | echo " ${PREBUILT_IMAGE_PREFIX}${version}:$IMAGE_TAG" 121 | done 122 | if [ "${STAGING_FLAG}" = "true" ]; then 123 | echo "and tagging them as staging." 124 | else 125 | echo "but NOT tagging them as staging." 126 | fi 127 | if [ -z "${AUTO_YES}" ]; then 128 | read -r -p "Ok to build? [Y/n] " response 129 | response=${response,,} # tolower 130 | if [[ "${response}" =~ ^(no|n)$ ]]; then 131 | echo "Aborting." 132 | exit 1 133 | fi 134 | fi 135 | echo 136 | 137 | for version in "${PREBUILT_ERLANG_VERSIONS[@]}"; do 138 | gcloud builds submit ${DIRNAME}/elixir-prebuilt-erlang \ 139 | --config ${DIRNAME}/elixir-prebuilt-erlang/cloudbuild.yaml --project ${PROJECT} --timeout ${BUILD_TIMEOUT} \ 140 | --substitutions _TAG=${IMAGE_TAG},_ASDF_BASE_IMAGE=${ASDF_BASE_IMAGE},_PREBUILT_IMAGE_PREFIX=${PREBUILT_IMAGE_PREFIX},_ASDF_TAG=${ASDF_IMAGE_TAG},_ERLANG_VERSION=${version} 141 | echo "**** Built image: ${PREBUILT_IMAGE_PREFIX}${version}:${IMAGE_TAG}" 142 | if [ "${STAGING_FLAG}" = "true" ]; then 143 | gcloud container images add-tag --project ${PROJECT} \ 144 | ${PREBUILT_IMAGE_PREFIX}${version}:${IMAGE_TAG} \ 145 | ${PREBUILT_IMAGE_PREFIX}${version}:staging -q 146 | echo "**** Tagged image as ${PREBUILT_IMAGE_PREFIX}${version}:staging" 147 | fi 148 | done 149 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | defmodule TestHelper do 2 | require ExUnit.Assertions 3 | require Poison.Parser 4 | 5 | defmacro structure_tests(file, image) do 6 | funcs = 7 | file 8 | |> File.read!() 9 | |> Poison.Parser.parse!() 10 | |> Map.fetch!("commandTests") 11 | |> Enum.map(fn test_definition -> 12 | quote do 13 | test unquote("#{file} : #{test_definition["name"]}") do 14 | [binary | args] = unquote(test_definition["command"]) 15 | 16 | output = 17 | TestHelper.assert_cmd_succeeds([ 18 | "docker", 19 | "run", 20 | "--rm", 21 | "--entrypoint=#{binary}", 22 | unquote(image) | args 23 | ]) 24 | 25 | unquote(test_definition["expectedOutput"]) 26 | |> Enum.each(fn expectation -> 27 | regex = Regex.compile!(expectation) 28 | assert Regex.match?(regex, output) 29 | end) 30 | end 31 | end 32 | end) 33 | 34 | {:__block__, [], funcs} 35 | end 36 | 37 | def assert_cmd_succeeds(cmd = [binary | args], opts \\ []) do 38 | cmd_str = Enum.join(cmd, " ") 39 | failure_message = Keyword.get(opts, :message, "Failed command: #{cmd_str}") 40 | 41 | cmd_opts = 42 | if Keyword.get(opts, :stream, false) do 43 | [into: IO.stream(:stdio, :line)] 44 | else 45 | [] 46 | end 47 | 48 | if Keyword.get(opts, :show, false) do 49 | IO.puts(cmd_str) 50 | end 51 | 52 | {output, status} = System.cmd(binary, args, cmd_opts) 53 | ExUnit.Assertions.assert(status == 0, failure_message) 54 | output 55 | end 56 | 57 | def assert_cmd_output(cmd, expectation, opts \\ []) do 58 | verbose = Keyword.get(opts, :verbose, false) 59 | timeout = Keyword.get(opts, :timeout, 0) 60 | show = Keyword.get(opts, :show, false) 61 | 62 | if show do 63 | cmd |> Enum.join(" ") |> IO.puts() 64 | end 65 | 66 | expectations = List.wrap(expectation) 67 | assert_cmd_output(cmd, expectations, nil, nil, timeout, verbose) 68 | end 69 | 70 | defp assert_cmd_output(cmd = [binary | args], expectations, _actual, _code, timeout, verbose) do 71 | {actual, code} = System.cmd(binary, args) 72 | check_cmd_output_expectations(cmd, expectations, expectations, actual, code, timeout, verbose) 73 | end 74 | 75 | defp check_cmd_output_expectations(_cmd, _expectations, [], actual, 0, _timeout, _verbose), 76 | do: actual 77 | 78 | defp check_cmd_output_expectations( 79 | cmd, 80 | expectations, 81 | [expectation | rest], 82 | actual, 83 | 0, 84 | timeout, 85 | verbose 86 | ) do 87 | if actual =~ expectation do 88 | check_cmd_output_expectations(cmd, expectations, rest, actual, 0, timeout, verbose) 89 | else 90 | assert_cmd_output_next(cmd, expectations, actual, 0, timeout, verbose) 91 | end 92 | end 93 | 94 | defp check_cmd_output_expectations(cmd, expectations, _, actual, code, timeout, verbose) do 95 | assert_cmd_output_next(cmd, expectations, actual, code, timeout, verbose) 96 | end 97 | 98 | defp assert_cmd_output_next(cmd, expectations, actual, code, timeout, _verbose) 99 | when timeout <= 0 do 100 | ExUnit.Assertions.flunk( 101 | "Expected #{inspect(expectations)} but got #{inspect(actual)}" <> 102 | " (exit code #{code}) when executing #{inspect(cmd)}" 103 | ) 104 | end 105 | 106 | defp assert_cmd_output_next(cmd, expectations, actual, code, timeout, verbose) do 107 | if verbose do 108 | IO.puts("...expected result did not arrive yet (#{timeout} tries remaining)...") 109 | end 110 | 111 | Process.sleep(1_000) 112 | assert_cmd_output(cmd, expectations, actual, code, timeout - 1, verbose) 113 | end 114 | 115 | def build_docker_image(args \\ [], fun) do 116 | name = generate_name("test-image") 117 | 118 | try do 119 | assert_cmd_succeeds( 120 | ["docker", "build", "--no-cache", "-t", name | args] ++ ["."], 121 | stream: true, 122 | show: true 123 | ) 124 | 125 | fun.(name) 126 | after 127 | System.cmd("docker", ["rmi", name]) 128 | end 129 | end 130 | 131 | def run_docker_daemon(args \\ [], fun) do 132 | name = generate_name("test-container") 133 | 134 | try do 135 | assert_cmd_succeeds( 136 | ["docker", "run", "--rm", "--name", name, "-d" | args], 137 | stream: true, 138 | show: true 139 | ) 140 | 141 | fun.(name) 142 | after 143 | System.cmd("docker", ["kill", name]) 144 | end 145 | end 146 | 147 | defp generate_name(prefix) do 148 | number = 149 | 0xFFFFFFFF 150 | |> :rand.uniform() 151 | |> Integer.to_string(16) 152 | |> String.downcase() 153 | |> String.pad_leading(8, "0") 154 | 155 | "#{prefix}-#{number}" 156 | end 157 | end 158 | 159 | ExUnit.start() 160 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config/phoenix_1_4/mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "artificery": {:hex, :artificery, "0.4.2", "3ded6e29e13113af52811c72f414d1e88f711410cac1b619ab3a2666bbd7efd4", [:mix], [], "hexpm"}, 3 | "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"}, 4 | "cowboy": {:hex, :cowboy, "2.6.3", "99aa50e94e685557cad82e704457336a453d4abcb77839ad22dbe71f311fcc06", [:rebar3], [{:cowlib, "~> 2.7.3", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, 5 | "cowlib": {:hex, :cowlib, "2.7.3", "a7ffcd0917e6d50b4d5fb28e9e2085a0ceb3c97dea310505f7460ff5ed764ce9", [:rebar3], [], "hexpm"}, 6 | "db_connection": {:hex, :db_connection, "2.1.1", "a51e8a2ee54ef2ae6ec41a668c85787ed40cb8944928c191280fe34c15b76ae5", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"}, 7 | "decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"}, 8 | "distillery": {:hex, :distillery, "2.1.1", "f9332afc2eec8a1a2b86f22429e068ef35f84a93ea1718265e740d90dd367814", [:mix], [{:artificery, "~> 0.2", [hex: :artificery, repo: "hexpm", optional: false]}], "hexpm"}, 9 | "ecto": {:hex, :ecto, "3.1.7", "fa21d06ef56cdc2fdaa62574e8c3ba34a2751d44ea34c30bc65f0728421043e5", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, 10 | "ecto_sql": {:hex, :ecto_sql, "3.1.6", "1e80e30d16138a729c717f73dcb938590bcdb3a4502f3012414d0cbb261045d8", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.1.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, repo: "hexpm", optional: true]}, {:myxql, "~> 0.2.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0 or ~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"}, 11 | "file_system": {:hex, :file_system, "0.2.7", "e6f7f155970975789f26e77b8b8d8ab084c59844d8ecfaf58cbda31c494d14aa", [:mix], [], "hexpm"}, 12 | "gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"}, 13 | "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, 14 | "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"}, 15 | "phoenix": {:hex, :phoenix, "1.4.9", "746d098e10741c334d88143d3c94cab1756435f94387a63441792e66ec0ee974", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"}, 16 | "phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, 17 | "phoenix_html": {:hex, :phoenix_html, "2.13.3", "850e292ff6e204257f5f9c4c54a8cb1f6fbc16ed53d360c2b780a3d0ba333867", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, 18 | "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.1", "274a4b07c4adbdd7785d45a8b0bb57634d0b4f45b18d2c508b26c0344bd59b8f", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"}, 19 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"}, 20 | "plug": {:hex, :plug, "1.8.2", "0bcce1daa420f189a6491f3940cc77ea7fb1919761175c9c3b59800d897440fc", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"}, 21 | "plug_cowboy": {:hex, :plug_cowboy, "2.1.0", "b75768153c3a8a9e8039d4b25bb9b14efbc58e9c4a6e6a270abff1cd30cbe320", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, 22 | "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"}, 23 | "postgrex": {:hex, :postgrex, "0.14.3", "5754dee2fdf6e9e508cbf49ab138df964278700b764177e8f3871e658b345a1e", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, 24 | "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, 25 | "telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"}, 26 | } 27 | -------------------------------------------------------------------------------- /lib/tasks/build_local_images.ex: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | defmodule Mix.Tasks.BuildLocalImages do 16 | @moduledoc """ 17 | Mix task that builds images locally. 18 | """ 19 | 20 | @shortdoc "Build images locally." 21 | 22 | @os_name "ubuntu18" 23 | @base_erlang_version "23.3.4.11" 24 | @base_elixir_version "1.13.3-otp-23" 25 | @old_distillery_erlang_version "22.3.4.17" 26 | @old_distillery_elixir_version "1.8.2-otp-22" 27 | @prebuilt_erlang_versions [@old_distillery_erlang_version, @base_erlang_version] 28 | @asdf_version "0.9.0" 29 | @nodejs_version "16.14.2" 30 | @gcloud_version "378.0.0" 31 | 32 | @prebuilt_erlang_image_prefix "elixir-prebuilt-erlang-" 33 | 34 | use Mix.Task 35 | 36 | def run(args) do 37 | {opts, _leftover} = 38 | OptionParser.parse!( 39 | args, 40 | strict: [ 41 | prebuilt_images_tag: :string 42 | ], 43 | aliases: [i: :prebuilt_images_tag] 44 | ) 45 | 46 | prebuilt_images_tag = Keyword.get(opts, :prebuilt_images_tag, nil) 47 | 48 | File.cd!("elixir-#{@os_name}", fn -> 49 | {_, 0} = 50 | System.cmd( 51 | "docker", 52 | ["build", "--no-cache", "--pull", "-t", "elixir-os", "."], 53 | into: IO.stream(:stdio, :line) 54 | ) 55 | end) 56 | 57 | File.cd!("elixir-asdf", fn -> 58 | {_, 0} = 59 | System.cmd( 60 | "docker", 61 | [ 62 | "build", 63 | "--no-cache", 64 | "-t", 65 | "elixir-asdf", 66 | "--build-arg", 67 | "asdf_version=#{@asdf_version}", 68 | "." 69 | ], 70 | into: IO.stream(:stdio, :line) 71 | ) 72 | end) 73 | 74 | if prebuilt_images_tag == nil do 75 | File.cd!("elixir-prebuilt-erlang", fn -> 76 | Enum.each(@prebuilt_erlang_versions, fn version -> 77 | {_, 0} = 78 | System.cmd( 79 | "docker", 80 | [ 81 | "build", 82 | "--no-cache", 83 | "-t", 84 | "#{@prebuilt_erlang_image_prefix}#{version}", 85 | "--build-arg", 86 | "erlang_version=#{version}", 87 | "." 88 | ], 89 | into: IO.stream(:stdio, :line) 90 | ) 91 | end) 92 | end) 93 | else 94 | Enum.each(@prebuilt_erlang_versions, fn version -> 95 | image = "gcr.io/gcp-elixir/runtime/#{@os_name}/prebuilt/otp-#{version}" 96 | 97 | {_, 0} = 98 | System.cmd( 99 | "docker", 100 | ["pull", "#{image}:#{prebuilt_images_tag}"], 101 | into: IO.stream(:stdio, :line) 102 | ) 103 | 104 | {_, 0} = 105 | System.cmd( 106 | "docker", 107 | [ 108 | "tag", 109 | "#{image}:#{prebuilt_images_tag}", 110 | "#{@prebuilt_erlang_image_prefix}#{version}" 111 | ], 112 | into: IO.stream(:stdio, :line) 113 | ) 114 | end) 115 | end 116 | 117 | File.cd!("elixir-base", fn -> 118 | {dockerfile, 0} = 119 | System.cmd("sed", [ 120 | "-e", 121 | "s|@@PREBUILT_ERLANG_IMAGE@@|#{@prebuilt_erlang_image_prefix}#{@base_erlang_version}|g", 122 | "Dockerfile-prebuilt.in" 123 | ]) 124 | 125 | :ok = File.write!("Dockerfile", dockerfile) 126 | 127 | {_, 0} = 128 | System.cmd( 129 | "docker", 130 | [ 131 | "build", 132 | "--no-cache", 133 | "-t", 134 | "elixir-base", 135 | "--build-arg", 136 | "erlang_version=#{@base_erlang_version}", 137 | "--build-arg", 138 | "elixir_version=#{@base_elixir_version}", 139 | "." 140 | ], 141 | into: IO.stream(:stdio, :line) 142 | ) 143 | end) 144 | 145 | File.cd!("elixir-builder", fn -> 146 | {_, 0} = 147 | System.cmd( 148 | "docker", 149 | [ 150 | "build", 151 | "--no-cache", 152 | "-t", 153 | "elixir-builder", 154 | "--build-arg", 155 | "nodejs_version=#{@nodejs_version}", 156 | "--build-arg", 157 | "gcloud_version=#{@gcloud_version}", 158 | "." 159 | ], 160 | into: IO.stream(:stdio, :line) 161 | ) 162 | end) 163 | 164 | File.cd!("elixir-generate-dockerfile", fn -> 165 | prebuilt_erlang_images_str = 166 | @prebuilt_erlang_versions 167 | |> Enum.map(fn version -> 168 | "#{version}=#{@prebuilt_erlang_image_prefix}#{version}" 169 | end) 170 | |> Enum.join(",") 171 | 172 | {_, 0} = 173 | System.cmd( 174 | "docker", 175 | [ 176 | "build", 177 | "--no-cache", 178 | "-t", 179 | "elixir-generate-dockerfile", 180 | "--build-arg", 181 | "os_image=elixir-os", 182 | "--build-arg", 183 | "asdf_image=elixir-asdf", 184 | "--build-arg", 185 | "builder_image=elixir-builder", 186 | "--build-arg", 187 | "prebuilt_erlang_images=#{prebuilt_erlang_images_str}", 188 | "--build-arg", 189 | "default_erlang_version=#{@base_erlang_version}", 190 | "--build-arg", 191 | "default_elixir_version=#{@base_elixir_version}", 192 | "--build-arg", 193 | "old_distillery_erlang_version=#{@old_distillery_erlang_version}", 194 | "--build-arg", 195 | "old_distillery_elixir_version=#{@old_distillery_elixir_version}", 196 | "." 197 | ], 198 | into: IO.stream(:stdio, :line) 199 | ) 200 | end) 201 | end 202 | end 203 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/app/Dockerfile-release.eex: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | ## This Dockerfile builds an Elixir application for the Google App Engine 3 | ## Flexible Environment. It is meant for an application that uses Distillery 4 | ## releases for deployment. 5 | ## This Dockerfile may be modified and used as an App Engine "custom runtime", 6 | ## or even ported to a different Docker-based hosting environment such as 7 | ## Kubernetes. 8 | ## 9 | ## Generated at: <%= @timestamp %> 10 | ## From configuration: <%= @app_yaml_path %> 11 | ## Project: <%= @project_id_for_display %> 12 | ## Service: <%= @service_name %> 13 | ################################################################################ 14 | 15 | 16 | ################################################################################ 17 | ## 1. Release build 18 | ## From the builder image, installs any dependencies, builds the application 19 | ## release, and runs any additional requested build jobs such as 20 | ## preparation of JavaScript and CSS assets. This stage should prepare a 21 | ## release in the /opt/release directory. 22 | 23 | FROM <%= @builder_image %> AS app-build 24 | 25 | ## If your build requires additional apt packages, install them here. 26 | <%= if @builder_packages == "" do %> 27 | # RUN apt-get update -y \ 28 | # && apt-get install -y -q package-name another-package-name \ 29 | # && apt-get clean \ 30 | # && rm -f /var/lib/apt/lists/*_* 31 | <% else %> 32 | RUN apt-get update -y \ 33 | && apt-get install -y -q <%= @builder_packages %> \ 34 | && apt-get clean \ 35 | && rm -f /var/lib/apt/lists/*_* 36 | <% end %> 37 | 38 | ## Specify the versions of Erlang/OTP and Elixir. 39 | ## If you want to change which versions to use, you may do so, but make sure: 40 | ## 1. You make the corresponding change to the versions copied to the runtime 41 | ## image at the bottom of this Dockerfile. 42 | ## 2. You update your .tool-versions file if you have one. 43 | ## For more information on the asdf tool that is used to manage Erlang and 44 | ## Elixir installation, see https://github.com/asdf-vm/asdf 45 | ARG erlang_version="<%= @erlang_version %>" 46 | ARG elixir_version="<%= @elixir_version %>" 47 | 48 | <%= if @prebuilt_erlang_image do %> 49 | ## Install Erlang <%= @erlang_version %> from a prebuilt image. 50 | ## If a prebuilt image is not available, use the alternate installation method 51 | ## commented below. 52 | COPY --from=<%= @prebuilt_erlang_image %> \ 53 | /opt/asdf/installs/erlang/${erlang_version} \ 54 | /opt/asdf/installs/erlang/${erlang_version} 55 | RUN asdf reshim erlang ${erlang_version} \ 56 | && asdf global erlang ${erlang_version} 57 | ## Alternate installation for Erlang versions that have no prebuilt image. 58 | # RUN asdf plugin-update erlang \ 59 | # && asdf install erlang ${erlang_version} \ 60 | # && asdf global erlang ${erlang_version} 61 | <% else %> 62 | ## Install Erlang <%= @erlang_version %> by building from source using asdf. 63 | ## If a prebuilt image is available, the alternate installation method 64 | ## commented below will probably be significantly faster. 65 | RUN asdf plugin-update erlang \ 66 | && asdf install erlang ${erlang_version} \ 67 | && asdf global erlang ${erlang_version} 68 | ## Alternate installation for Erlang versions that have a prebuilt image. 69 | # COPY --from=<%= @prebuilt_erlang_image %> \ 70 | # /opt/asdf/installs/erlang/${erlang_version} \ 71 | # /opt/asdf/installs/erlang/${erlang_version} 72 | # RUN asdf reshim erlang ${erlang_version} \ 73 | # && asdf global erlang ${erlang_version} 74 | <% end %> 75 | 76 | ## Install Elixir <%= @elixir_version %> using asdf. 77 | RUN asdf plugin-update elixir \ 78 | && asdf install elixir ${elixir_version} \ 79 | && asdf global elixir ${elixir_version} \ 80 | && mix local.hex --force \ 81 | && mix local.rebar --force 82 | 83 | ## Set gcloud project here if your build scripts need to use gcloud. 84 | <%= if @project_id do %> 85 | RUN gcloud config set project <%= @project_id %> 86 | <% else %> 87 | # RUN gcloud config set project <%= @project_id_for_example %> 88 | <% end %> 89 | 90 | ## If your build scripts need any environment variables, set them here. 91 | ## Generally, these will come from the application's app engine configuration. 92 | <%= if @env_variables == "" do %> 93 | # ENV NAME="value" 94 | <% else %> 95 | ENV <%= @env_variables %> 96 | <% end %> 97 | 98 | ## If your build scripts need access to your application's CloudSQL instances, 99 | ## list them here, comma-delimited. This environment variable tells the 100 | ## "access_cloud_sql" script which databases to connect to. 101 | <%= if @cloud_sql_instances == "" do %> 102 | # ENV BUILD_CLOUDSQL_INSTANCES="<%= @project_id_for_example %>:db-region:db-name" 103 | <% else %> 104 | ENV BUILD_CLOUDSQL_INSTANCES="<%= @cloud_sql_instances %>" 105 | <% end %> 106 | 107 | ## Copy the application files. 108 | COPY . /app/ 109 | 110 | ## If your build scripts need credentials for access to cloud resources, you 111 | ## may provide an appropriate JSON credentials file and set the 112 | ## GOOGLE_APPLICATION_CREDENTIALS path accordingly. This is not necessary if 113 | ## you provide the cloudbuild service account with the needed privileges. 114 | # COPY my-build-credentials.json /app/build-credentials.json 115 | # ENV GOOGLE_APPLICATION_CREDENTIALS=/app/build-credentials.json 116 | 117 | ## Install dependencies and compile the application. 118 | RUN mix do deps.get, deps.compile, compile 119 | 120 | ## Run application build scripts here. 121 | ## Scripts that require access to the application CloudSQL instances should 122 | ## run access_cloud_sql first to start the CloudSQL Proxy. e.g. 123 | ## RUN access_cloud_sql && mix my_task 124 | ## Otherwise, simply run each build script in order in a separate RUN command. 125 | <%= if @build_scripts != "" do %> 126 | <%= @build_scripts %> 127 | <% end %> 128 | 129 | ## Create the release. 130 | RUN <%= @release_command %> 131 | 132 | 133 | ################################################################################ 134 | ## 2. Application runtime image 135 | ## From the OS base image, installs the release built above, and sets the 136 | ## entrypoint. This is the final image executed by Google App Engine. 137 | 138 | FROM <%= @os_image %> 139 | 140 | ## If your application needs additional apt packages at runtime, install 141 | ## them here. 142 | <%= if @runtime_packages == "" do %> 143 | # RUN apt-get update -y \ 144 | # && apt-get install -y -q package-name another-package-name \ 145 | # && apt-get clean \ 146 | # && rm -f /var/lib/apt/lists/*_* 147 | <% else %> 148 | RUN apt-get update -y \ 149 | && apt-get install -y -q <%= @runtime_packages %> \ 150 | && apt-get clean \ 151 | && rm -f /var/lib/apt/lists/*_* 152 | <% end %> 153 | 154 | ## Copy built release from build stage into /app 155 | COPY --from=app-build /app/_build/<%= @mix_env %>/rel/<%= @release_app %> /app/ 156 | 157 | ## Command to start the release. 158 | CMD <%= @entrypoint %> 159 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/app/Dockerfile-simple.eex: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | ## This Dockerfile builds an Elixir application for the Google App Engine 3 | ## Flexible Environment. It is meant for an application that does not use 4 | ## Distillery releases for deployment. 5 | ## This Dockerfile may be modified and used as an App Engine "custom runtime", 6 | ## or even ported to a different Docker-based hosting environment such as 7 | ## Kubernetes. 8 | ## 9 | ## Generated at: <%= @timestamp %> 10 | ## From configuration: <%= @app_yaml_path %> 11 | ## Project: <%= @project_id_for_display %> 12 | ## Service: <%= @service_name %> 13 | ################################################################################ 14 | 15 | 16 | ################################################################################ 17 | ## 1. Application build 18 | ## From the builder image, installs any dependencies, builds the application, 19 | ## and runs any additional requested build jobs such as preparation of 20 | ## JavaScript and CSS assets. This stage should prepare the /app directory 21 | ## exactly the way needed to run the final application. 22 | 23 | FROM <%= @builder_image %> AS app-build 24 | 25 | ## If your build requires additional apt packages, install them here. 26 | <%= if @builder_packages == "" do %> 27 | # RUN apt-get update -y \ 28 | # && apt-get install -y -q package-name another-package-name \ 29 | # && apt-get clean \ 30 | # && rm -f /var/lib/apt/lists/*_* 31 | <% else %> 32 | RUN apt-get update -y \ 33 | && apt-get install -y -q <%= @builder_packages %> \ 34 | && apt-get clean \ 35 | && rm -f /var/lib/apt/lists/*_* 36 | <% end %> 37 | 38 | ## Specify the versions of Erlang/OTP and Elixir. 39 | ## If you want to change which versions to use, you may do so, but make sure: 40 | ## 1. You make the corresponding change to the versions copied to the runtime 41 | ## image at the bottom of this Dockerfile. 42 | ## 2. You update your .tool-versions file if you have one. 43 | ## For more information on the asdf tool that is used to manage Erlang and 44 | ## Elixir installation, see https://github.com/asdf-vm/asdf 45 | ARG erlang_version="<%= @erlang_version %>" 46 | ARG elixir_version="<%= @elixir_version %>" 47 | 48 | <%= if @prebuilt_erlang_image do %> 49 | ## Install Erlang <%= @erlang_version %> from a prebuilt image. 50 | ## If a prebuilt image is not available, use the alternate installation method 51 | ## commented below. 52 | COPY --from=<%= @prebuilt_erlang_image %> \ 53 | /opt/asdf/installs/erlang/${erlang_version} \ 54 | /opt/asdf/installs/erlang/${erlang_version} 55 | RUN asdf reshim erlang ${erlang_version} \ 56 | && asdf global erlang ${erlang_version} 57 | ## Alternate installation for Erlang versions that have no prebuilt image. 58 | # RUN asdf plugin-update erlang \ 59 | # && asdf install erlang ${erlang_version} \ 60 | # && asdf global erlang ${erlang_version} 61 | <% else %> 62 | ## Install Erlang <%= @erlang_version %> by building from source using asdf. 63 | ## If a prebuilt image is available, the alternate installation method 64 | ## commented below will probably be significantly faster. 65 | RUN asdf plugin-update erlang \ 66 | && asdf install erlang ${erlang_version} \ 67 | && asdf global erlang ${erlang_version} 68 | ## Alternate installation for Erlang versions that have a prebuilt image. 69 | # COPY --from=<%= @prebuilt_erlang_image %> \ 70 | # /opt/asdf/installs/erlang/${erlang_version} \ 71 | # /opt/asdf/installs/erlang/${erlang_version} 72 | # RUN asdf reshim erlang ${erlang_version} \ 73 | # && asdf global erlang ${erlang_version} 74 | <% end %> 75 | 76 | ## Install Elixir <%= @elixir_version %> using asdf. 77 | RUN asdf plugin-update elixir \ 78 | && asdf install elixir ${elixir_version} \ 79 | && asdf global elixir ${elixir_version} \ 80 | && mix local.hex --force \ 81 | && mix local.rebar --force 82 | 83 | ## Set gcloud project here if your build scripts need to use gcloud. 84 | <%= if @project_id do %> 85 | RUN gcloud config set project <%= @project_id %> 86 | <% else %> 87 | # RUN gcloud config set project <%= @project_id_for_example %> 88 | <% end %> 89 | 90 | ## If your build scripts need any environment variables, set them here. 91 | ## Generally, these will come from the application's app engine configuration. 92 | <%= if @env_variables == "" do %> 93 | # ENV NAME="value" 94 | <% else %> 95 | ENV <%= @env_variables %> 96 | <% end %> 97 | 98 | ## If your build scripts need access to your application's CloudSQL instances, 99 | ## list them here, comma-delimited. This environment variable tells the 100 | ## "access_cloud_sql" script which databases to connect to. 101 | <%= if @cloud_sql_instances == "" do %> 102 | # ENV BUILD_CLOUDSQL_INSTANCES="<%= @project_id_for_example %>:db-region:db-name" 103 | <% else %> 104 | ENV BUILD_CLOUDSQL_INSTANCES="<%= @cloud_sql_instances %>" 105 | <% end %> 106 | 107 | ## Copy the application files. 108 | COPY . /app/ 109 | 110 | ## If your build scripts need credentials for access to cloud resources, you 111 | ## may provide an appropriate JSON credentials file and set the 112 | ## GOOGLE_APPLICATION_CREDENTIALS path accordingly. This is not necessary if 113 | ## you provide the cloudbuild service account with the needed privileges. 114 | # COPY my-build-credentials.json /app/build-credentials.json 115 | # ENV GOOGLE_APPLICATION_CREDENTIALS=/app/build-credentials.json 116 | 117 | ## Install dependencies and compile the application. 118 | RUN mix do deps.get, deps.compile, compile 119 | 120 | ## Run application build scripts here. 121 | ## Scripts that require access to the application CloudSQL instances should 122 | ## run access_cloud_sql first to start the CloudSQL Proxy. e.g. 123 | ## RUN access_cloud_sql && mix my_task 124 | ## Otherwise, simply run each build script in order in a separate RUN command. 125 | <%= if @build_scripts != "" do %> 126 | <%= @build_scripts %> 127 | <% end %> 128 | 129 | 130 | ################################################################################ 131 | ## 2. Runtime image 132 | ## From the base image, installs the application directory built above, and 133 | ## sets the entrypoint. This is the final image executed by Google App Engine. 134 | 135 | FROM <%= @asdf_image %> 136 | 137 | ## If your application needs additional apt packages at runtime, install 138 | ## them here. 139 | <%= if @runtime_packages == "" do %> 140 | # RUN apt-get update -y \ 141 | # && apt-get install -y -q package-name another-package-name \ 142 | # && apt-get clean \ 143 | # && rm -f /var/lib/apt/lists/*_* 144 | <% else %> 145 | RUN apt-get update -y \ 146 | && apt-get install -y -q <%= @runtime_packages %> \ 147 | && apt-get clean \ 148 | && rm -f /var/lib/apt/lists/*_* 149 | <% end %> 150 | 151 | ## Specify the versions of Erlang/OTP and Elixir. 152 | ## Note these versions must match the versions specified in the build image. 153 | ARG erlang_version="<%= @erlang_version %>" 154 | ARG elixir_version="<%= @elixir_version %>" 155 | 156 | ## Copy the installations of Erlang and Elixir from the build image. 157 | COPY --from=app-build /opt/asdf/installs/erlang/${erlang_version} \ 158 | /opt/asdf/installs/erlang/${erlang_version} 159 | COPY --from=app-build /opt/asdf/installs/elixir/${elixir_version} \ 160 | /opt/asdf/installs/elixir/${elixir_version} 161 | RUN asdf reshim erlang ${erlang_version} \ 162 | && asdf reshim elixir ${elixir_version} \ 163 | && asdf global erlang ${erlang_version} \ 164 | && asdf global elixir ${elixir_version} 165 | 166 | ## Copy the built application from the build image. 167 | COPY --from=app-build /app/ /app/ 168 | 169 | ## Command to start application. 170 | CMD <%= @entrypoint %> 171 | -------------------------------------------------------------------------------- /test/sample_app_build_test.exs: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | defmodule SampleAppBuildTest do 16 | use ExUnit.Case 17 | import TestHelper 18 | 19 | @moduletag timeout: 300_000 20 | 21 | @old_elixir_version_short "1.8.2" 22 | @old_elixir_version_long "#{@old_elixir_version_short}-otp-22" 23 | @new_elixir_version_short "1.13.3" 24 | @new_elixir_version_regex @new_elixir_version_short |> Regex.escape() |> Regex.compile!() 25 | 26 | test "Minimal plug app" do 27 | run_app_test("minimal_plug") 28 | end 29 | 30 | test "Minimal phoenix app" do 31 | run_app_test( 32 | "minimal_phoenix", 33 | tool_versions: "elixir #{@old_elixir_version_long}\n", 34 | check_image: fn image -> 35 | assert_cmd_succeeds( 36 | ["docker", "run", "--rm", image, "test", "-f", "/app/priv/static/cache_manifest.json"], 37 | show: true 38 | ) 39 | 40 | assert_cmd_output( 41 | ["docker", "run", "--rm", image, "elixir", "--version"], 42 | ~r{1\.8\.2}, 43 | show: true 44 | ) 45 | end 46 | ) 47 | end 48 | 49 | test "Minimal phoenix app with release" do 50 | config = """ 51 | env: flex 52 | runtime: elixir 53 | runtime_config: 54 | release_app: minimal_phoenix 55 | """ 56 | 57 | run_app_test( 58 | "minimal_phoenix", 59 | tool_versions: "elixir #{@old_elixir_version_long}\n", 60 | config: config, 61 | check_image: fn image -> 62 | assert_cmd_succeeds( 63 | ["docker", "run", "--rm", image, "test", "-x", "/app/bin/minimal_phoenix"], 64 | show: true 65 | ) 66 | end, 67 | check_container: fn _container -> 68 | assert_cmd_output( 69 | ["curl", "-s", "-S", "http://localhost:8080/elixir-version"], 70 | @old_elixir_version_short, 71 | timeout: 10, 72 | show: true, 73 | verbose: true 74 | ) 75 | end 76 | ) 77 | end 78 | 79 | test "Minimal phoenix app in staging environment" do 80 | config = """ 81 | env: flex 82 | runtime: elixir 83 | env_variables: 84 | MIX_ENV: staging 85 | entrypoint: MIX_ENV=staging mix phx.server 86 | """ 87 | 88 | run_app_test( 89 | "minimal_phoenix", 90 | tool_versions: "elixir #{@old_elixir_version_long}\n", 91 | config: config, 92 | expected_output: ~r{from staging} 93 | ) 94 | end 95 | 96 | test "Minimal phoenix app with release in staging environment" do 97 | config = """ 98 | env: flex 99 | runtime: elixir 100 | runtime_config: 101 | release_app: minimal_phoenix 102 | env_variables: 103 | MIX_ENV: staging 104 | """ 105 | 106 | run_app_test( 107 | "minimal_phoenix", 108 | tool_versions: "elixir #{@old_elixir_version_long}\n", 109 | config: config, 110 | expected_output: ~r{from staging} 111 | ) 112 | end 113 | 114 | test "Minimal phoenix 1.4 app" do 115 | run_app_test( 116 | "minimal_phoenix14", 117 | check_image: fn image -> 118 | assert_cmd_succeeds( 119 | ["docker", "run", "--rm", image, "test", "-f", "/app/priv/static/cache_manifest.json"], 120 | show: true 121 | ) 122 | 123 | assert_cmd_output( 124 | ["docker", "run", "--rm", image, "elixir", "--version"], 125 | @new_elixir_version_regex, 126 | show: true 127 | ) 128 | end 129 | ) 130 | end 131 | 132 | test "Minimal phoenix 1.4 app with elixir 1.9 release" do 133 | config = """ 134 | env: flex 135 | runtime: elixir 136 | runtime_config: 137 | release_app: minimal_phoenix14 138 | """ 139 | 140 | run_app_test( 141 | "minimal_phoenix14", 142 | config: config, 143 | check_image: fn image -> 144 | assert_cmd_succeeds( 145 | ["docker", "run", "--rm", image, "test", "-x", "/app/bin/minimal_phoenix14"], 146 | show: true 147 | ) 148 | end, 149 | check_container: fn _container -> 150 | assert_cmd_output( 151 | ["curl", "-s", "-S", "http://localhost:8080/elixir-version"], 152 | @new_elixir_version_short, 153 | timeout: 10, 154 | show: true, 155 | verbose: true 156 | ) 157 | end 158 | ) 159 | end 160 | 161 | test "Minimal phoenix 1.4 app with distillery 2.1 release" do 162 | config = """ 163 | env: flex 164 | runtime: elixir 165 | runtime_config: 166 | release_app: minimal_phoenix14 167 | """ 168 | 169 | run_app_test( 170 | "minimal_phoenix14", 171 | config: config, 172 | postprocess_dir: fn dir -> 173 | File.rename!(Path.join(dir, "mix-dist21.exs"), Path.join(dir, "mix.exs")) 174 | File.rename!(Path.join(dir, "mix-dist21.lock"), Path.join(dir, "mix.lock")) 175 | end, 176 | check_image: fn image -> 177 | assert_cmd_succeeds( 178 | ["docker", "run", "--rm", image, "test", "-x", "/app/bin/minimal_phoenix14"], 179 | show: true 180 | ) 181 | end, 182 | check_container: fn _container -> 183 | assert_cmd_output( 184 | ["curl", "-s", "-S", "http://localhost:8080/elixir-version"], 185 | @new_elixir_version_short, 186 | timeout: 10, 187 | show: true, 188 | verbose: true 189 | ) 190 | end 191 | ) 192 | end 193 | 194 | test "Minimal phoenix 1.4 app with distillery 2.0 release" do 195 | config = """ 196 | env: flex 197 | runtime: elixir 198 | runtime_config: 199 | release_app: minimal_phoenix14 200 | """ 201 | 202 | run_app_test( 203 | "minimal_phoenix14", 204 | config: config, 205 | postprocess_dir: fn dir -> 206 | config_path = Path.join([dir, "rel", "config.exs"]) 207 | 208 | str = 209 | config_path 210 | |> File.read!() 211 | |> String.replace("Distillery.Releases.Config", "Mix.Releases.Config") 212 | 213 | File.write!(config_path, str) 214 | File.rename!(Path.join(dir, "mix-dist20.exs"), Path.join(dir, "mix.exs")) 215 | File.rename!(Path.join(dir, "mix-dist20.lock"), Path.join(dir, "mix.lock")) 216 | end, 217 | check_image: fn image -> 218 | assert_cmd_succeeds( 219 | ["docker", "run", "--rm", image, "test", "-x", "/app/bin/minimal_phoenix14"], 220 | show: true 221 | ) 222 | end, 223 | check_container: fn _container -> 224 | assert_cmd_output( 225 | ["curl", "-s", "-S", "http://localhost:8080/elixir-version"], 226 | @old_elixir_version_short, 227 | timeout: 10, 228 | show: true, 229 | verbose: true 230 | ) 231 | end 232 | ) 233 | end 234 | 235 | @apps_dir Path.join(__DIR__, "sample_apps") 236 | @tmp_dir Path.join(__DIR__, "tmp") 237 | @default_config """ 238 | env: flex 239 | runtime: elixir 240 | """ 241 | 242 | def run_app_test(app_name, opts \\ []) do 243 | check_container = Keyword.get(opts, :check_container, nil) 244 | check_image = Keyword.get(opts, :check_image, nil) 245 | expected_output = Keyword.get(opts, :expected_output, ~r{Hello, world!}) 246 | config = Keyword.get(opts, :config, @default_config) 247 | tool_versions = Keyword.get(opts, :tool_versions, nil) 248 | postprocess_dir = Keyword.get(opts, :postprocess_dir, nil) 249 | 250 | File.rm_rf!(@tmp_dir) 251 | 252 | @apps_dir 253 | |> Path.join(app_name) 254 | |> File.cp_r!(@tmp_dir) 255 | 256 | @tmp_dir 257 | |> Path.join("app.yaml") 258 | |> File.write!(config) 259 | 260 | if tool_versions != nil do 261 | @tmp_dir 262 | |> Path.join(".tool-versions") 263 | |> File.write!(tool_versions) 264 | end 265 | 266 | if postprocess_dir != nil, do: postprocess_dir.(@tmp_dir) 267 | 268 | assert_cmd_succeeds( 269 | [ 270 | "docker", 271 | "run", 272 | "--rm", 273 | "-v", 274 | "#{@tmp_dir}:/workspace", 275 | "-w", 276 | "/workspace", 277 | "elixir-generate-dockerfile" 278 | ], 279 | show: true, 280 | verbose: true 281 | ) 282 | 283 | File.cd!(@tmp_dir, fn -> 284 | build_docker_image(fn image -> 285 | if check_image != nil do 286 | check_image.(image) 287 | end 288 | 289 | run_docker_daemon(["-p", "8080:8080", image], fn container -> 290 | if check_container != nil do 291 | check_container.(container) 292 | end 293 | 294 | assert_cmd_output( 295 | ["curl", "-s", "-S", "http://localhost:8080"], 296 | expected_output, 297 | timeout: 10, 298 | show: true, 299 | verbose: true 300 | ) 301 | end) 302 | end) 303 | end) 304 | end 305 | end 306 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog for Elixir runtime 2 | 3 | This is a history of elixir runtime releases. 4 | 5 | Generally, you can cause Google App Engine to use the latest stable runtime by 6 | choosing the following in your `app.yaml` config file: 7 | 8 | runtime: gs://elixir-runtime/elixir.yaml 9 | 10 | However, you may also pin to a specific version of the runtime by specifying 11 | the version name as the yaml file name. For example, to pin to the 12 | `elixir-2019-10-09-181239` release, use: 13 | 14 | runtime: gs://elixir-runtime/elixir-2019-10-09-181239.yaml 15 | 16 | There is currently no guarantee regarding how long older runtime releases will 17 | continue to be supported. It is generally best not to pin to a specific 18 | release unless absolutely necessary, and then you should return to latest as 19 | soon as possible. 20 | 21 | ## elixir-2022-03-25-173415 22 | 23 | * Update default Elixir to 1.13.3. 24 | * Update default OTP to 23.3.4.11. 25 | * Prebuilt OTP 23.1.4.1, 23.2.7.3, 23.2.7.4, 23.3.2 through 23.3.4.11, and 24.0 through 24.3.2. 26 | * Dropped prebuilt binaries for OTP 22.x except the "latest" patch of each branch. Currently, we provide prebuilt binaries for branch tips of OTP 21 and 22, and all released versions of OTP 23 and 24. 27 | * Update gcloud to 378.0.0. 28 | * Update nodejs to 16.14.2. 29 | * Update asdf to 0.9.0. 30 | 31 | ## elixir-2021-04-26-035921 32 | 33 | * Update default Elixir to 1.11.4. 34 | * Update default OTP to 22.3.4.17. 35 | * Prebuilt OTP 22.3.4.5 through 22.3.4.17, 23.0.4, 23.1 through 23.1.5, 23.2 through 23.2.7.2, and 23.3 through 23.3.1. 36 | * Dropped prebuilt binaries for OTP 20.x. 37 | * Update gcloud to 334.0.0. 38 | * Update nodejs to 14.16.1. 39 | * Update asdf to 0.8.0. 40 | 41 | ## elixir-2020-08-03-131308 42 | 43 | * Update default Elixir to 1.10.4. 44 | * Update default OTP to 22.3.4.4. 45 | * Prebuilt OTP 20.3.8.26, 21.3.8.17, 22.3.1 through 22.3.4.4, and 23.0 through 23.0.3. 46 | * Update gcloud to 303.0.0 47 | * Update nodejs to 12.18.3 48 | 49 | ## elixir-2020-04-07-131853 50 | 51 | * Update default Elixir to 1.10.2 52 | * Update default OTP to 22.2.8 53 | * Prebuilt OTP 21.3.8.13, 21.3.8.14, 22.2.4 through 22.2.8, and 22.3. 54 | * Update gcloud to 287.0.0 55 | * Update nodejs to 12.16.1 56 | * Update asdf to 0.7.8 57 | 58 | ## elixir-2020-01-22-160655 59 | 60 | * Prebuilt OTP 21.3.8.12, 22.1.8.1, and 22.2 through 22.2.3 61 | * Update default OTP to 22.2.3 62 | * Update gcloud to 277.0.0 63 | * Update nodejs to 12.14.1 64 | * Update asdf to 0.7.6 65 | 66 | ## elixir-2019-12-10-142915 67 | 68 | * Prebuilt OTP 21.3.8.11 and 22.1.8 69 | * Update default OTP to 22.1.8 70 | * Update gcloud to 273.0.0 71 | * Update nodejs to 12.13.1 72 | 73 | ## elixir-2019-11-14-223500 74 | 75 | * Prebuilt OTP up to 22.1.7 76 | * Update default elixir to 1.9.4 77 | * Update default OTP to 22.1.7 78 | * Update gcloud to 271.0.0 79 | * Update asdf to 0.7.5 80 | 81 | ## elixir-2019-10-29-183530 82 | 83 | * Prebuilt OTP up to 21.3.8.10 and 22.1.5 84 | * Removed prebuilt OTP 21.0.1 through 21.0.8 (but kept 21.0 and 21.0.9) 85 | * Update default elixir to 1.9.2 86 | * Update default OTP to 22.1.5 87 | * Update gcloud to 268.0.0 88 | * Update nodejs to 12.13.0 89 | 90 | ## elixir-2019-10-09-181239 91 | 92 | * Prebuilt OTP 21.3.8.7, 22.1, and 22.1.1. 93 | * Update default to elixir to 1.9.1. 94 | * Update default OTP to 22.1.1. 95 | * Allow custom debian packages with plus signs in the name. 96 | * Update gcloud to 265.0.0 97 | * Update nodejs to 10.16.3 98 | * Update asdf to 0.7.4 99 | 100 | ## elixir-2019-07-18-112708 101 | 102 | * Supports building releases using the built-in mix release in elixir 1.9. 103 | * Supports the usage changes in distillery 2.1. 104 | * Update default elixir to 1.9.0, unless pre-2.1 distillery is in use in which case it remains on 1.8.2. 105 | * Update default OTP to 22.0.7. 106 | * Prebuilt OTP through 21.3.8.6 and 22.0.7. 107 | * Removed intermediate OTP 20.x versions from the prebuilt list. (The primary and latest versions are still present.) 108 | * Update default gcloud to 253.0.0. 109 | * Update asdf to 0.7.3. 110 | 111 | ## elixir-2019-07-01-065508 112 | 113 | * Prebuilt OTP through 21.3.8.4 and 22.0.4. 114 | * Update default OTP to 22.0.4. 115 | * Update default gcloud to 252.0.0. 116 | * Update nodejs to 10.16.0. 117 | 118 | ## elixir-2019-05-28-222238 119 | 120 | * Prebuilt OTP through 21.3.7.1, 21.3.8.2, and 22.0.1. 121 | * Update default OTP to 21.3.8.2, and default Elixir to 1.8.2. 122 | * Update default gcloud to 247.0.0. 123 | * Update asdf to 0.7.2 124 | 125 | ## elixir-2019-04-19-135626 126 | 127 | * Prebuilt OTP 20.3.8.21, and 21.3.3 thru 21.3.6. 128 | * Update default OTP to 21.3.6. 129 | * Update gcloud to 242.0.0. 130 | * Update asdf to 0.7.1. 131 | 132 | ## elixir-2019-03-21-181846 133 | 134 | * Prebuilt OTP 21.2 up to 21.2.7, and 21.3 up to 21.3.2. 135 | * Update default OTP to 21.3.2. 136 | * Update gcloud to 239.0.0. 137 | 138 | ## elixir-2019-03-04-150411 139 | 140 | * Updated the underlying OS from Ubuntu 16.04 (Xenial) to Ubuntu 18.04 (Bionic). 141 | * Prebuilt OTP 20.3 up to 20.3.8.20, and 21.2 up to 21.2.6. 142 | * Update default OTP to 21.2.6, and default Elixir to 1.8.1 143 | * Update asdf to 0.7.0. 144 | * Update gcloud to 236.0.0. 145 | * Update nodejs to 10.15.2. 146 | 147 | ## elixir-2019-01-19-000446 (elixir-ubuntu16) 148 | 149 | * This is the last planned release on Ubuntu 16.04 (Xenial). Future releases will update to Ubuntu 18.04 (Bionic). To remain on Xenial, pin to this release by setting the runtime to `gs://elixir-runtime/elixir-ubuntu16.yaml` 150 | * Prebuilt OTP 20.3 up to 20.3.8.18, and 21.2 up to 21.2.3. 151 | * Update default OTP to 21.2.3. 152 | * Update gcloud to 230.0.0. 153 | 154 | ## elixir-2019-01-02-104948 155 | 156 | * Prebuilt OTP 21.2, 21.2.1, and 21.2.2. 157 | * Update default OTP to 21.2.2. 158 | * Update gcloud to 228.0.0. 159 | * Update nodejs to 10.15.0. 160 | 161 | ## elixir-2018-12-11-124828 162 | 163 | * The runtime now recognizes Phoenix 1.4 and webpack when building assets. (Brunch is still supported for older projects.) 164 | * Prebuilt OTP 20.3.8.x up to 20.3.8.15, and 21.1.x up to 21.1.4. 165 | * Update default OTP to 21.1.4 and default Elixir to 1.7.4. 166 | * Update gcloud to 227.0.0. 167 | * Update nodejs to 10.14.1. 168 | 169 | ## elixir-2018-10-10-134614 170 | 171 | * Set ASDF_DATA_DIR to fix asdf after being updated to 0.6. 172 | * Prebuilt OTP 20.3.8.9, 21.0.9, and 21.1. 173 | * Update default OTP to 21.0.9. Default Elixir remains 1.7.3. 174 | * Update gcloud to 219.0.1. 175 | * Update nodejs to 8.12.0. 176 | 177 | ## elixir-2018-09-11-102415 178 | 179 | * Update defaults to OTP 21 and Elixir 1.7 (specifically OTP 21.0.8 and Elixir 1.7.3). 180 | * Prebuilt OTP through 20.3.8.8 and 21.0.8 181 | * Update gcloud to 215.0.0 182 | * Update nodejs to 8.11.4 183 | 184 | ## elixir-2018-07-30-141124 185 | 186 | * Prebuilt OTP through 20.3.8.3 and 21.0.4 187 | * Update default OTP to 20.3.8.3. Default Elixir remains 1.6.6. 188 | * Update gcloud to 209.0.0 189 | * Update nodejs to 8.11.3 190 | * Modify pipeline config so there is room for more erlang prebuilts. 191 | 192 | ## elixir-2018-06-22-144237 193 | 194 | * OTP 20.3.8 and 21.0 are now prebuilt. 195 | * Update default OTP to 20.3.8, and default Elixir to 1.6.6 196 | * Update gcloud to 205.0.0 197 | * Build image now includes automake, fop, and xsltproc, to support recent 198 | asdf-erlang changes. 199 | 200 | ## elixir-2018-06-10-064921 201 | 202 | * OTP 20.3.7 is prebuilt and is the default. 203 | * Update gcloud to 204.0.0 204 | * Pin prebuilt OTP image tags in each runtime release. 205 | 206 | ## elixir-2018-06-01-105216 207 | 208 | * OTP 20.3.3, 20.3.4, 20.3.5, and 20.3.6 are now prebuilt. 209 | * Update default OTP to 20.3.6, and default Elixir to 1.6.5 210 | * Update asdf to 0.5.0, nodejs to 8.11.2, and gcloud to 203.0.0 211 | 212 | ## elixir-2018-04-03-180947 213 | 214 | * OTP 20.3.2 is prebuilt and is the default 215 | * Update nodejs to 8.11.1, and gcloud to 196.0.0 216 | 217 | ## elixir-2018-03-19-203701 218 | 219 | * OTP 20.2.4, 20.3, and 20.3.1 are now prebuilt. 220 | * Update default OTP from 20.2.2 to 20.3.1 and Elixir from 1.6.1 to 1.6.4. 221 | * Update asdf to 0.4.3, nodejs to 8.10.0, and gcloud to 193.0.0 222 | 223 | ## elixir-2018-02-25-171329 224 | 225 | * OTP 20.2.3 is now prebuilt. 226 | * Update default Elixir from 1.5.3 to 1.6.1 227 | * Update asdf to 0.4.2 and gcloud to 189.0.0 228 | 229 | ## elixir-2018-01-20-210216 230 | 231 | * Prebuilt patch releases of OTP 20. Update default OTP to 20.2.2. 232 | 233 | ## elixir-2018-01-17-121145 234 | 235 | * Support for building releases in an environment other than `prod`. 236 | * OTP 20.2 is now prebuilt. Update default OTP from 20.1 to 20.2. 237 | * Update default Elixir from 1.5.2 to 1.5.3. 238 | * Update NodeJS to 8.9.4 and GCloud to 185.0.0 in the build image. 239 | * Don't attempt to prepend `exec` to entrypoints that set environment variables 240 | inline. 241 | 242 | ## elixir-2017-11-29-234522 243 | 244 | This is a major overhaul of the runtime with significant new features and 245 | fixes. Among those: 246 | 247 | * Deployments can now be configured to build a release using Distillery, which 248 | yields a more efficient deployment. 249 | * Applications can provide a `.tool-versions` file specifying the particular 250 | Erlang and Elixir versions to use. These are installed at build time. 251 | * The builder image now includes a C compiler, so dependencies with a C 252 | component should now install properly. 253 | * Builds exclude directories that could contain prior development artifacts, 254 | such as deps, _build, and node_modules, to prevent those from leaking into 255 | the production build. 256 | * The builder image now includes gcloud, so build steps can easily do things 257 | like download files from Cloud Storage. 258 | 259 | The test suite has also been fleshed out, and a bunch of minor issues have 260 | been fixed, so the runtime should be more stable moving forward. 261 | 262 | ## elixir-2017-10-17-142851 263 | 264 | * Generate the correct brunch build script for phoenix umbrella apps. 265 | 266 | ## elixir-2017-10-03-154925 267 | 268 | * Update OTP 20.0 to 20.1. 269 | * Update Elixir 1.5.1 to 1.5.2. 270 | * Some internal cleanup 271 | 272 | ## elixir-2017-09-02-192912 273 | 274 | * Initial release 275 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/lib/generator.ex: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | defmodule GenerateDockerfile.Generator do 16 | alias GenerateDockerfile.AppConfig 17 | require Logger 18 | 19 | @default_workspace_dir "/workspace" 20 | @default_template_dir "../app" 21 | @common_dockerignore [ 22 | ".dockerignore", 23 | "Dockerfile", 24 | ".git", 25 | ".hg", 26 | ".svn", 27 | "_build", 28 | "deps", 29 | "erl_crash.dump" 30 | ] 31 | @phoenix_dockerignore [ 32 | "priv/static" 33 | ] 34 | @assets_dockerignore [ 35 | "npm-debug.log", 36 | "node_modules" 37 | ] 38 | 39 | def execute(opts) do 40 | os_image = Keyword.get(opts, :os_image, System.get_env("DEFAULT_OS_IMAGE")) 41 | asdf_image = Keyword.get(opts, :asdf_image, System.get_env("DEFAULT_ASDF_IMAGE")) 42 | builder_image = Keyword.get(opts, :builder_image, System.get_env("DEFAULT_BUILDER_IMAGE")) 43 | 44 | default_erlang_version = 45 | Keyword.get(opts, :default_erlang_version, System.get_env("DEFAULT_ERLANG_VERSION")) 46 | 47 | default_elixir_version = 48 | Keyword.get(opts, :default_elixir_version, System.get_env("DEFAULT_ELIXIR_VERSION")) 49 | 50 | old_distillery_erlang_version = 51 | Keyword.get( 52 | opts, 53 | :old_distillery_erlang_version, 54 | System.get_env("OLD_DISTILLERY_ERLANG_VERSION") 55 | ) 56 | 57 | old_distillery_elixir_version = 58 | Keyword.get( 59 | opts, 60 | :old_distillery_elixir_version, 61 | System.get_env("OLD_DISTILLERY_ELIXIR_VERSION") 62 | ) 63 | 64 | prebuilt_erlang_images = 65 | Keyword.get_values(opts, :prebuilt_erlang_images) ++ 66 | case System.get_env("DEFAULT_PREBUILT_ERLANG_IMAGES") do 67 | nil -> [] 68 | str -> String.split(str, ",") 69 | end 70 | 71 | workspace_dir = Keyword.get(opts, :workspace_dir, @default_workspace_dir) |> Path.expand() 72 | 73 | template_dir = 74 | Keyword.get(opts, :template_dir, @default_template_dir) 75 | |> Path.expand() 76 | 77 | File.cd!(workspace_dir, fn -> 78 | start_app_config( 79 | workspace_dir, 80 | default_erlang_version, 81 | default_elixir_version, 82 | old_distillery_erlang_version, 83 | old_distillery_elixir_version 84 | ) 85 | 86 | assigns = 87 | build_assigns( 88 | prebuilt_erlang_images, 89 | os_image, 90 | asdf_image, 91 | builder_image 92 | ) 93 | 94 | write_dockerfile(workspace_dir, template_dir, assigns) 95 | desired_dockerignore_entries = determine_desired_dockerignore_entries() 96 | write_dockerignore(workspace_dir, desired_dockerignore_entries) 97 | end) 98 | 99 | :ok 100 | end 101 | 102 | defp start_app_config( 103 | _workspace_dir, 104 | "", 105 | _default_elixir_version, 106 | _old_distillery_erlang_version, 107 | _old_distillery_elixir_version 108 | ) do 109 | GenerateDockerfile.error("Missing default erlang version") 110 | end 111 | 112 | defp start_app_config( 113 | _workspace_dir, 114 | _default_erlang_version, 115 | "", 116 | _old_distillery_erlang_version, 117 | _old_distillery_elixir_version 118 | ) do 119 | GenerateDockerfile.error("Missing default elixir version") 120 | end 121 | 122 | defp start_app_config( 123 | _workspace_dir, 124 | _default_erlang_version, 125 | _default_elixir_version, 126 | "", 127 | _old_distillery_elixir_version) do 128 | GenerateDockerfile.error("Missing default erlang version for old distillery") 129 | end 130 | 131 | defp start_app_config( 132 | _workspace_dir, 133 | _default_erlang_version, 134 | _default_elixir_version, 135 | _old_distillery_erlang_version, 136 | "") do 137 | GenerateDockerfile.error("Missing default elixir version for old distillery") 138 | end 139 | 140 | defp start_app_config( 141 | workspace_dir, 142 | default_erlang_version, 143 | default_elixir_version, 144 | old_distillery_erlang_version, 145 | old_distillery_elixir_version 146 | ) do 147 | {:ok, _} = 148 | AppConfig.start_link( 149 | workspace_dir: workspace_dir, 150 | default_erlang_version: default_erlang_version, 151 | default_elixir_version: default_elixir_version, 152 | old_distillery_erlang_version: old_distillery_erlang_version, 153 | old_distillery_elixir_version: old_distillery_elixir_version 154 | ) 155 | 156 | case AppConfig.status() do 157 | {:error, msg} -> GenerateDockerfile.error(msg) 158 | :ok -> nil 159 | end 160 | end 161 | 162 | defp build_assigns( 163 | prebuilt_erlang_images, 164 | os_image, 165 | asdf_image, 166 | builder_image 167 | ) do 168 | timestamp = DateTime.utc_now() |> DateTime.to_iso8601() 169 | packages = AppConfig.get!(:install_packages) |> Enum.join(" ") 170 | erlang_version = AppConfig.get!(:erlang_version) 171 | elixir_version = AppConfig.get!(:elixir_version) 172 | 173 | prebuilt_erlang_image = 174 | Enum.find_value(prebuilt_erlang_images, fn str -> 175 | case String.split(str, "=", parts: 2) do 176 | [^erlang_version, image] -> image 177 | _ -> nil 178 | end 179 | end) 180 | 181 | release_command = 182 | render_release_command( 183 | AppConfig.get!(:distillery_version), 184 | AppConfig.get!(:mix_env), 185 | AppConfig.get!(:release_app) 186 | ) 187 | 188 | [ 189 | os_image: os_image, 190 | asdf_image: asdf_image, 191 | builder_image: builder_image, 192 | timestamp: timestamp, 193 | erlang_version: erlang_version, 194 | elixir_version: elixir_version, 195 | prebuilt_erlang_image: prebuilt_erlang_image, 196 | app_yaml_path: AppConfig.get!(:app_yaml_path), 197 | project_id: AppConfig.get!(:project_id), 198 | project_id_for_display: AppConfig.get!(:project_id_for_display), 199 | project_id_for_example: AppConfig.get!(:project_id_for_example), 200 | service_name: AppConfig.get!(:service_name), 201 | release_app: AppConfig.get!(:release_app), 202 | builder_packages: packages, 203 | runtime_packages: packages, 204 | env_variables: AppConfig.get!(:env_variables) |> render_env, 205 | mix_env: AppConfig.get!(:mix_env), 206 | cloud_sql_instances: AppConfig.get!(:cloud_sql_instances) |> Enum.join(","), 207 | release_command: release_command, 208 | build_scripts: AppConfig.get!(:build_scripts) |> render_commands, 209 | entrypoint: AppConfig.get!(:entrypoint) 210 | ] 211 | end 212 | 213 | defp write_dockerfile(workspace_dir, template_dir, assigns) do 214 | template_name = 215 | if AppConfig.get!(:release_app) == nil do 216 | "Dockerfile-simple.eex" 217 | else 218 | "Dockerfile-release.eex" 219 | end 220 | 221 | template_path = Path.join(template_dir, template_name) 222 | dockerfile = EEx.eval_file(template_path, [assigns: assigns], trim: true) 223 | write_path = Path.join(workspace_dir, "Dockerfile") 224 | 225 | if File.exists?(write_path) do 226 | GenerateDockerfile.error("Unable to generate Dockerfile because one already exists.") 227 | end 228 | 229 | File.write!(write_path, dockerfile) 230 | Logger.info("Generated Dockerfile.") 231 | end 232 | 233 | defp escape_quoted(str) do 234 | str 235 | |> String.replace("\\", "\\\\") 236 | |> String.replace("\"", "\\\"") 237 | |> String.replace("\n", "\\n") 238 | end 239 | 240 | defp render_env(map) do 241 | map 242 | |> Map.keys() 243 | |> Enum.sort() 244 | |> Enum.map(fn key -> 245 | value = Map.fetch!(map, key) 246 | "#{key}=\"#{escape_quoted(value)}\"" 247 | end) 248 | |> Enum.join(" \\\n ") 249 | end 250 | 251 | defp render_release_command(nil, _mix_env, release_app) do 252 | "mix release #{release_app}" 253 | end 254 | 255 | defp render_release_command(distillery_version, mix_env, _release_app) do 256 | if Version.compare(distillery_version, "2.1.0") == :lt do 257 | "mix release --env=#{mix_env} --verbose" 258 | else 259 | "mix distillery.release --env=#{mix_env} --verbose" 260 | end 261 | end 262 | 263 | defp render_commands(cmds) do 264 | cmds 265 | |> Enum.map(fn cmd -> "RUN #{cmd}" end) 266 | |> Enum.join("\n") 267 | end 268 | 269 | defp determine_desired_dockerignore_entries() do 270 | assets_dir = AppConfig.get!(:assets_dir) 271 | 272 | assets_entries = 273 | case assets_dir do 274 | nil -> [] 275 | "." -> @assets_dockerignore 276 | dir -> Enum.map(@assets_dockerignore, &Path.join(dir, &1)) 277 | end 278 | 279 | phoenix_entries = if assets_dir == nil, do: [], else: @phoenix_dockerignore 280 | [AppConfig.get!(:app_yaml_path) | @common_dockerignore] ++ assets_entries ++ phoenix_entries 281 | end 282 | 283 | defp write_dockerignore(workspace_dir, desired_entries) do 284 | write_path = Path.join(workspace_dir, ".dockerignore") 285 | 286 | existing_entries = 287 | if File.exists?(write_path) do 288 | write_path 289 | |> File.read!() 290 | |> String.split("\n", trim: true) 291 | else 292 | [] 293 | end 294 | 295 | File.open!(write_path, [:append], fn io -> 296 | Enum.each(desired_entries -- existing_entries, fn entry -> 297 | IO.puts(io, entry) 298 | end) 299 | end) 300 | 301 | if length(existing_entries) == 0 do 302 | Logger.info("Generated .dockerignore file.") 303 | else 304 | Logger.info("Updated .dockerignore file.") 305 | end 306 | end 307 | end 308 | -------------------------------------------------------------------------------- /runtime-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2017 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | DEFAULT_ERLANG_VERSION=23.3.4.11 19 | DEFAULT_ELIXIR_VERSION=1.13.3-otp-23 20 | OLD_DISTILLERY_ERLANG_VERSION=22.3.4.17 21 | OLD_DISTILLERY_ELIXIR_VERSION=1.8.2-otp-22 22 | ASDF_VERSION=0.9.0 23 | GCLOUD_VERSION=378.0.0 24 | NODEJS_VERSION=16.14.2 25 | 26 | 27 | set -e 28 | 29 | DIRNAME=$(dirname $0) 30 | 31 | OS_NAME=ubuntu18 32 | PROJECT= 33 | NAMESPACE=elixir 34 | IMAGE_TAG= 35 | BASE_IMAGE_DOCKERFILE=default 36 | STAGING_FLAG= 37 | UPLOAD_BUCKET= 38 | AUTO_YES= 39 | BUILD_TIMEOUT=60m 40 | PREBUILT_IMAGE_TAG=latest 41 | PREBUILT_ERLANG_VERSIONS=() 42 | if [ -f ${DIRNAME}/erlang-versions.txt ]; then 43 | mapfile -t PREBUILT_ERLANG_VERSIONS < ${DIRNAME}/erlang-versions.txt 44 | fi 45 | 46 | show_usage() { 47 | echo 'Usage: ./runtime-build.sh [flags...]' >&2 48 | echo 'Flags:' >&2 49 | echo ' -b : upload a new runtime definition to this gcs bucket (defaults to no upload)' >&2 50 | echo ' -e : comma separated prebuilt erlang versions (defaults to erlang-versions.txt)' >&2 51 | echo ' -i: use prebuilt erlang to build base image' >&2 52 | echo ' -n : set the images namespace (defaults to `elixir`)' >&2 53 | echo ' -o : build against the given os base image (defaults to `ubuntu18`)' >&2 54 | echo ' -p : set the images project (defaults to current gcloud config setting)' >&2 55 | echo ' -s: also tag new images as `staging`' >&2 56 | echo ' -t : set the new image tag (creates a new tag if not provided)' >&2 57 | echo ' -y: automatically confirm' >&2 58 | } 59 | 60 | OPTIND=1 61 | while getopts ":b:e:in:p:st:yh" opt; do 62 | case ${opt} in 63 | b) 64 | UPLOAD_BUCKET=${OPTARG} 65 | ;; 66 | e) 67 | if [ "${OPTARG}" = "none" ]; then 68 | PREBUILT_ERLANG_VERSIONS=() 69 | else 70 | IFS=',' read -r -a PREBUILT_ERLANG_VERSIONS <<< "${OPTARG}" 71 | fi 72 | ;; 73 | i) 74 | BASE_IMAGE_DOCKERFILE="prebuilt" 75 | ;; 76 | n) 77 | NAMESPACE=${OPTARG} 78 | ;; 79 | o) 80 | OS_NAME=${OPTARG} 81 | ;; 82 | p) 83 | PROJECT=${OPTARG} 84 | ;; 85 | s) 86 | STAGING_FLAG="true" 87 | ;; 88 | t) 89 | IMAGE_TAG=${OPTARG} 90 | ;; 91 | y) 92 | AUTO_YES="true" 93 | ;; 94 | h) 95 | show_usage 96 | exit 0 97 | ;; 98 | \?) 99 | echo "Invalid option: -${OPTARG}" >&2 100 | echo >&2 101 | show_usage 102 | exit 1 103 | ;; 104 | :) 105 | echo "Option ${OPTARG} requires a parameter" >&2 106 | echo >&2 107 | show_usage 108 | exit 1 109 | ;; 110 | esac 111 | done 112 | shift $((OPTIND-1)) 113 | 114 | if [ -z "${PROJECT}" ]; then 115 | PROJECT=$(gcloud config get-value project) 116 | echo "Using project from gcloud config: ${PROJECT}" >&2 117 | fi 118 | if [ -z "${IMAGE_TAG}" ]; then 119 | IMAGE_TAG=$(date +%Y-%m-%d-%H%M%S) 120 | echo "Creating new IMAGE_TAG: ${IMAGE_TAG}" >&2 121 | fi 122 | 123 | OS_BASE_IMAGE=gcr.io/${PROJECT}/${NAMESPACE}/${OS_NAME} 124 | ASDF_BASE_IMAGE=gcr.io/${PROJECT}/${NAMESPACE}/${OS_NAME}/asdf 125 | ELIXIR_BASE_IMAGE=gcr.io/${PROJECT}/${NAMESPACE}/${OS_NAME}/base 126 | BUILDER_IMAGE=gcr.io/${PROJECT}/${NAMESPACE}/${OS_NAME}/builder 127 | GENERATE_DOCKERFILE_IMAGE=gcr.io/${PROJECT}/${NAMESPACE}/${OS_NAME}/generate-dockerfile 128 | PREBUILT_IMAGE_PREFIX=gcr.io/${PROJECT}/${NAMESPACE}/${OS_NAME}/prebuilt/otp- 129 | 130 | COMMA_ERLANG_VERSIONS=$( IFS=, ; echo "${PREBUILT_ERLANG_VERSIONS[*]}" ) 131 | PREBUILT_IMAGE_ARGS= 132 | for version in "${PREBUILT_ERLANG_VERSIONS[@]}"; do 133 | tag=$(gcloud container images list-tags ${PREBUILT_IMAGE_PREFIX}${version} --filter=tags=${PREBUILT_IMAGE_TAG} --format="get(tags)" | sed -n 's|.*\([0-9]\{4\}-*[01][0-9]-*[0123][0-9][-_]*[0-9]\{6\}\).*|\1|p') 134 | if [ -z "${tag}" ]; then 135 | tag=${PREBUILT_IMAGE_TAG} 136 | fi 137 | echo "Tag for prebuilt erlang ${version}: ${tag}" >&2 138 | PREBUILT_IMAGE_ARGS="${PREBUILT_IMAGE_ARGS} '-p=${version}=${PREBUILT_IMAGE_PREFIX}${version}:${tag}'," 139 | done 140 | 141 | echo 142 | echo "Building images:" 143 | echo " ${OS_BASE_IMAGE}:${IMAGE_TAG}" 144 | echo " ${ASDF_BASE_IMAGE}:${IMAGE_TAG}" 145 | echo " ${ELIXIR_BASE_IMAGE}:${IMAGE_TAG}" 146 | echo " ${BUILDER_IMAGE}:${IMAGE_TAG}" 147 | echo " ${GENERATE_DOCKERFILE_IMAGE}:${IMAGE_TAG}" 148 | if [ "${STAGING_FLAG}" = "true" ]; then 149 | echo "and tagging them as staging." 150 | else 151 | echo "but NOT tagging them as staging." 152 | fi 153 | echo "Base image uses ${BASE_IMAGE_DOCKERFILE} installation of Erlang." 154 | if [ "${#PREBUILT_ERLANG_VERSIONS[@]}" = "0" ]; then 155 | echo "Dockerfile generator does not use any prebuilt Erlang images." 156 | else 157 | echo "Dockerfile generator uses prebuilt Erlang images for versions:" 158 | echo " ${COMMA_ERLANG_VERSIONS}" 159 | echo "with tag ${PREBUILT_IMAGE_TAG}" 160 | fi 161 | if [ -n "${UPLOAD_BUCKET}" ]; then 162 | echo "Also creating and uploading a new runtime config:" 163 | echo " gs://${UPLOAD_BUCKET}/elixir-${IMAGE_TAG}.yaml" 164 | if [ "${STAGING_FLAG}" = "true" ]; then 165 | echo " gs://${UPLOAD_BUCKET}/elixir-staging.yaml" 166 | else 167 | echo "but NOT promoting it to staging." 168 | fi 169 | fi 170 | if [ -z "${AUTO_YES}" ]; then 171 | read -r -p "Ok to build? [Y/n] " response 172 | response=${response,,} # tolower 173 | if [[ "${response}" =~ ^(no|n)$ ]]; then 174 | echo "Aborting." 175 | exit 1 176 | fi 177 | fi 178 | echo 179 | 180 | gcloud builds submit ${DIRNAME}/elixir-${OS_NAME} \ 181 | --config ${DIRNAME}/elixir-${OS_NAME}/cloudbuild.yaml --project ${PROJECT} --timeout ${BUILD_TIMEOUT} \ 182 | --substitutions _TAG=${IMAGE_TAG},_IMAGE=${OS_BASE_IMAGE} 183 | echo "**** Built image: ${OS_BASE_IMAGE}:${IMAGE_TAG}" 184 | if [ "${STAGING_FLAG}" = "true" ]; then 185 | gcloud container images add-tag --project ${PROJECT} \ 186 | ${OS_BASE_IMAGE}:${IMAGE_TAG} ${OS_BASE_IMAGE}:staging -q 187 | echo "**** Tagged image as ${OS_BASE_IMAGE}:staging" 188 | fi 189 | 190 | gcloud builds submit ${DIRNAME}/elixir-asdf \ 191 | --config ${DIRNAME}/elixir-asdf/cloudbuild.yaml --project ${PROJECT} --timeout ${BUILD_TIMEOUT} \ 192 | --substitutions _TAG=${IMAGE_TAG},_OS_BASE_IMAGE=${OS_BASE_IMAGE},_IMAGE=${ASDF_BASE_IMAGE},_ASDF_VERSION=${ASDF_VERSION} 193 | echo "**** Built image: ${ASDF_BASE_IMAGE}:${IMAGE_TAG}" 194 | if [ "${STAGING_FLAG}" = "true" ]; then 195 | gcloud container images add-tag --project ${PROJECT} \ 196 | ${ASDF_BASE_IMAGE}:$IMAGE_TAG ${ASDF_BASE_IMAGE}:staging -q 197 | echo "**** Tagged image as ${ASDF_BASE_IMAGE}:staging" 198 | fi 199 | 200 | sed -e "s|@@PREBUILT_ERLANG_IMAGE@@|${PREBUILT_IMAGE_PREFIX}${DEFAULT_ERLANG_VERSION}:latest|g" \ 201 | < ${DIRNAME}/elixir-base/Dockerfile-${BASE_IMAGE_DOCKERFILE}.in > ${DIRNAME}/elixir-base/Dockerfile 202 | gcloud builds submit ${DIRNAME}/elixir-base \ 203 | --config ${DIRNAME}/elixir-base/cloudbuild.yaml --project ${PROJECT} --timeout ${BUILD_TIMEOUT} \ 204 | --substitutions _TAG=${IMAGE_TAG},_ASDF_BASE_IMAGE=${ASDF_BASE_IMAGE},_IMAGE=${ELIXIR_BASE_IMAGE},_ERLANG_VERSION=${DEFAULT_ERLANG_VERSION},_ELIXIR_VERSION=${DEFAULT_ELIXIR_VERSION} 205 | echo "**** Built image: ${ELIXIR_BASE_IMAGE}:${IMAGE_TAG}" 206 | if [ "${STAGING_FLAG}" = "true" ]; then 207 | gcloud container images add-tag --project ${PROJECT} \ 208 | ${ELIXIR_BASE_IMAGE}:${IMAGE_TAG} ${ELIXIR_BASE_IMAGE}:staging -q 209 | echo "**** Tagged image as ${ELIXIR_BASE_IMAGE}:staging" 210 | fi 211 | 212 | gcloud builds submit ${DIRNAME}/elixir-builder \ 213 | --config ${DIRNAME}/elixir-builder/cloudbuild.yaml --project ${PROJECT} --timeout ${BUILD_TIMEOUT} \ 214 | --substitutions _TAG=${IMAGE_TAG},_ASDF_BASE_IMAGE=${ASDF_BASE_IMAGE},_IMAGE=${BUILDER_IMAGE},_NODEJS_VERSION=${NODEJS_VERSION},_GCLOUD_VERSION=${GCLOUD_VERSION} 215 | echo "**** Built image: ${BUILDER_IMAGE}:${IMAGE_TAG}" 216 | if [ "${STAGING_FLAG}" = "true" ]; then 217 | gcloud container images add-tag --project ${PROJECT} \ 218 | ${BUILDER_IMAGE}:${IMAGE_TAG} ${BUILDER_IMAGE}:staging -q 219 | echo "**** Tagged image as ${BUILDER_IMAGE}:staging" 220 | fi 221 | 222 | gcloud builds submit ${DIRNAME}/elixir-generate-dockerfile \ 223 | --config ${DIRNAME}/elixir-generate-dockerfile/cloudbuild.yaml --project ${PROJECT} --timeout ${BUILD_TIMEOUT} \ 224 | --substitutions _TAG=${IMAGE_TAG},_ELIXIR_BASE_IMAGE=${ELIXIR_BASE_IMAGE},_IMAGE=${GENERATE_DOCKERFILE_IMAGE} 225 | echo "**** Built image: ${GENERATE_DOCKERFILE_IMAGE}:${IMAGE_TAG}" 226 | if [ "${STAGING_FLAG}" = "true" ]; then 227 | gcloud container images add-tag --project ${PROJECT} \ 228 | ${GENERATE_DOCKERFILE_IMAGE}:${IMAGE_TAG} ${GENERATE_DOCKERFILE_IMAGE}:staging -q 229 | echo "**** Tagged image as ${GENERATE_DOCKERFILE_IMAGE}:staging" 230 | fi 231 | 232 | mkdir -p ${DIRNAME}/tmp 233 | sed -e "s|@@GENERATE_DOCKERFILE_IMAGE@@|${GENERATE_DOCKERFILE_IMAGE}|g;\ 234 | s|@@OS_BASE_IMAGE@@|${OS_BASE_IMAGE}|g;\ 235 | s|@@ASDF_BASE_IMAGE@@|${ASDF_BASE_IMAGE}|g;\ 236 | s|@@BUILDER_IMAGE@@|${BUILDER_IMAGE}|g;\ 237 | s|@@DEFAULT_ERLANG_VERSION@@|${DEFAULT_ERLANG_VERSION}|g;\ 238 | s|@@DEFAULT_ELIXIR_VERSION@@|${DEFAULT_ELIXIR_VERSION}|g;\ 239 | s|@@OLD_DISTILLERY_ERLANG_VERSION@@|${OLD_DISTILLERY_ERLANG_VERSION}|g;\ 240 | s|@@OLD_DISTILLERY_ELIXIR_VERSION@@|${OLD_DISTILLERY_ELIXIR_VERSION}|g;\ 241 | s|@@TAG@@|${IMAGE_TAG}|g;\ 242 | s|@@PREBUILT_IMAGE_ARGS@@|${PREBUILT_IMAGE_ARGS}|g" \ 243 | < ${DIRNAME}/elixir-pipeline/elixir.yaml.in > ${DIRNAME}/tmp/elixir-${IMAGE_TAG}.yaml 244 | echo "**** Created runtime config: ${DIRNAME}/tmp/elixir-${IMAGE_TAG}.yaml" 245 | 246 | if [ -n "${UPLOAD_BUCKET}" ]; then 247 | gsutil cp ${DIRNAME}/tmp/elixir-${IMAGE_TAG}.yaml gs://${UPLOAD_BUCKET}/elixir-${IMAGE_TAG}.yaml 248 | echo "**** Uploaded runtime config to gs://${UPLOAD_BUCKET}/elixir-${IMAGE_TAG}.yaml" 249 | if [ "${STAGING_FLAG}" = "true" ]; then 250 | gsutil cp gs://${UPLOAD_BUCKET}/elixir-${IMAGE_TAG}.yaml gs://${UPLOAD_BUCKET}/elixir-staging.yaml 251 | echo "**** Also promoted runtime config to gs://${UPLOAD_BUCKET}/elixir-staging.yaml" 252 | fi 253 | fi 254 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /elixir-generate-dockerfile/src/test/app_config_test.exs: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | defmodule AppConfigTest do 16 | use ExUnit.Case 17 | alias GenerateDockerfile.AppConfig 18 | 19 | @test_dir __DIR__ 20 | @cases_dir Path.join(@test_dir, "app_config") 21 | @tmp_dir Path.join(@test_dir, "tmp") 22 | @default_erlang_version "23.3.4.11" 23 | @default_elixir_version "1.13.3-otp-23" 24 | @old_distillery_erlang_version "22.3.4.17" 25 | @old_distillery_elixir_version "1.8.2-otp-22" 26 | 27 | @minimal_config """ 28 | env: flex 29 | runtime: gs://elixir-runtime/elixir.yaml 30 | """ 31 | 32 | def setup_test(dir, config, args \\ []) do 33 | config_file = Keyword.get(args, :config_file, nil) 34 | project = Keyword.get(args, :project, nil) 35 | 36 | File.cd!(@test_dir) 37 | File.rm_rf!(@tmp_dir) 38 | 39 | if dir do 40 | full_dir = Path.join(@cases_dir, dir) 41 | File.cp_r!(full_dir, @tmp_dir) 42 | else 43 | File.mkdir!(@tmp_dir) 44 | end 45 | 46 | if config_file do 47 | System.put_env("GAE_APPLICATION_YAML_PATH", config_file) 48 | else 49 | System.delete_env("GAE_APPLICATION_YAML_PATH") 50 | end 51 | 52 | if project do 53 | System.put_env("PROJECT_ID", project) 54 | else 55 | System.delete_env("PROJECT_ID") 56 | end 57 | 58 | if config do 59 | config_path = Path.join(@tmp_dir, config_file || "app.yaml") 60 | File.write!(config_path, config) 61 | end 62 | 63 | {:ok, pid} = 64 | AppConfig.start_link( 65 | workspace_dir: @tmp_dir, 66 | default_erlang_version: @default_erlang_version, 67 | default_elixir_version: @default_elixir_version, 68 | old_distillery_erlang_version: @old_distillery_erlang_version, 69 | old_distillery_elixir_version: @old_distillery_elixir_version, 70 | register_module: false 71 | ) 72 | 73 | pid 74 | end 75 | 76 | test "minimal directory with config" do 77 | pid = AppConfigTest.setup_test("minimal", @minimal_config) 78 | assert AppConfig.status(pid) == :ok 79 | assert AppConfig.get!(:workspace_dir, pid) == @tmp_dir 80 | assert AppConfig.get!(:project_id, pid) == nil 81 | assert AppConfig.get!(:project_id_for_display, pid) == "(unknown)" 82 | assert AppConfig.get!(:project_id_for_example, pid) == "my-project-id" 83 | assert AppConfig.get!(:app_yaml_path, pid) == "app.yaml" 84 | assert AppConfig.get!(:runtime_config, pid) == %{} 85 | assert AppConfig.get!(:service_name, pid) == "default" 86 | assert AppConfig.get!(:env_variables, pid) == %{} 87 | assert AppConfig.get!(:mix_env, pid) == "prod" 88 | assert AppConfig.get!(:install_packages, pid) == [] 89 | assert AppConfig.get!(:cloud_sql_instances, pid) == [] 90 | assert AppConfig.get!(:entrypoint, pid) == "exec mix run --no-halt" 91 | assert AppConfig.get!(:phoenix_version, pid) == nil 92 | assert AppConfig.get!(:assets_dir, pid) == nil 93 | assert AppConfig.get!(:assets_builder, pid) == nil 94 | assert AppConfig.get!(:build_scripts, pid) == [] 95 | assert AppConfig.get!(:erlang_version, pid) == @default_erlang_version 96 | assert AppConfig.get!(:elixir_version, pid) == @default_elixir_version 97 | end 98 | 99 | test "custom project" do 100 | pid = AppConfigTest.setup_test("minimal", @minimal_config, project: "actual-project") 101 | assert AppConfig.status(pid) == :ok 102 | assert AppConfig.get!(:project_id, pid) == "actual-project" 103 | assert AppConfig.get!(:project_id_for_display, pid) == "actual-project" 104 | assert AppConfig.get!(:project_id_for_example, pid) == "actual-project" 105 | end 106 | 107 | test "basic app.yaml" do 108 | config = """ 109 | env: flex 110 | runtime: gs://elixir-runtime/elixir.yaml 111 | service: elixir_app 112 | entrypoint: mix app.start 113 | env_variables: 114 | VAR1: value1 115 | VAR2: value2 116 | VAR3: 123 117 | beta_settings: 118 | cloud_sql_instances: 119 | - cloud-sql-instance-name,instance2:hi:there 120 | - instance3 121 | runtime_config: 122 | foo: bar 123 | packages: libgeos 124 | build: 125 | - npm install 126 | - brunch build 127 | """ 128 | 129 | pid = AppConfigTest.setup_test("minimal", config) 130 | assert AppConfig.status(pid) == :ok 131 | assert AppConfig.get!(:service_name, pid) == "elixir_app" 132 | assert AppConfig.get!(:release_app, pid) == nil 133 | 134 | assert AppConfig.get!(:env_variables, pid) == 135 | %{"VAR1" => "value1", "VAR2" => "value2", "VAR3" => "123"} 136 | 137 | assert AppConfig.get!(:cloud_sql_instances, pid) == 138 | ["cloud-sql-instance-name", "instance2:hi:there", "instance3"] 139 | 140 | assert AppConfig.get!(:build_scripts, pid) == ["npm install", "brunch build"] 141 | assert AppConfig.get!(:entrypoint, pid) == "exec mix app.start" 142 | assert AppConfig.get!(:install_packages, pid) == ["libgeos"] 143 | assert AppConfig.get!(:runtime_config, pid)["foo"] == "bar" 144 | end 145 | 146 | test "MIX_ENV set from env_variables" do 147 | config = """ 148 | env: flex 149 | runtime: gs://elixir-runtime/elixir.yaml 150 | env_variables: 151 | MIX_ENV: staging 152 | """ 153 | 154 | pid = AppConfigTest.setup_test("minimal", config) 155 | assert AppConfig.status(pid) == :ok 156 | assert AppConfig.get!(:env_variables, pid) == %{"MIX_ENV" => "staging"} 157 | assert AppConfig.get!(:mix_env, pid) == "staging" 158 | end 159 | 160 | test "release_app set" do 161 | config = """ 162 | env: flex 163 | runtime: gs://elixir-runtime/elixir.yaml 164 | runtime_config: 165 | release_app: my_app 166 | """ 167 | 168 | pid = AppConfigTest.setup_test("minimal", config) 169 | assert AppConfig.status(pid) == :ok 170 | assert AppConfig.get!(:release_app, pid) == "my_app" 171 | assert AppConfig.get!(:entrypoint, pid) == "[\"/app/bin/my_app\",\"start\"]" 172 | end 173 | 174 | test "release_app with custom entrypoint" do 175 | config = """ 176 | env: flex 177 | runtime: gs://elixir-runtime/elixir.yaml 178 | runtime_config: 179 | release_app: my_app 180 | entrypoint: mix app.start 181 | """ 182 | 183 | pid = AppConfigTest.setup_test("minimal", config) 184 | assert AppConfig.status(pid) == :ok 185 | assert AppConfig.get!(:release_app, pid) == "my_app" 186 | assert AppConfig.get!(:entrypoint, pid) == "exec mix app.start" 187 | end 188 | 189 | test "complex entrypoint" do 190 | config = """ 191 | env: flex 192 | runtime: gs://elixir-runtime/elixir.yaml 193 | entrypoint: cd myapp; mix app.start 194 | """ 195 | 196 | pid = AppConfigTest.setup_test("minimal", config) 197 | assert AppConfig.status(pid) == :ok 198 | assert AppConfig.get!(:entrypoint, pid) == "cd myapp; mix app.start" 199 | end 200 | 201 | test "entrypoint including environment variables" do 202 | config = """ 203 | env: flex 204 | runtime: gs://elixir-runtime/elixir.yaml 205 | entrypoint: MIX_ENV=staging mix app.start 206 | """ 207 | 208 | pid = AppConfigTest.setup_test("minimal", config) 209 | assert AppConfig.status(pid) == :ok 210 | assert AppConfig.get!(:entrypoint, pid) == "MIX_ENV=staging mix app.start" 211 | end 212 | 213 | test "entrypoint already including exec" do 214 | config = """ 215 | env: flex 216 | runtime: gs://elixir-runtime/elixir.yaml 217 | entrypoint: exec mix app.start 218 | """ 219 | 220 | pid = AppConfigTest.setup_test("minimal", config) 221 | assert AppConfig.status(pid) == :ok 222 | assert AppConfig.get!(:entrypoint, pid) == "exec mix app.start" 223 | end 224 | 225 | test "phoenix 1.4 defaults" do 226 | pid = AppConfigTest.setup_test("phoenix_1_4", @minimal_config) 227 | assert AppConfig.status(pid) == :ok 228 | assert AppConfig.get!(:entrypoint, pid) == "exec mix phx.server" 229 | assert AppConfig.get!(:phoenix_version, pid) == "1.4.9" 230 | assert AppConfig.get!(:assets_dir, pid) == "assets" 231 | assert AppConfig.get!(:assets_builder, pid) == "webpack" 232 | 233 | assert AppConfig.get!(:build_scripts, pid) == [ 234 | "cd assets && npm install && node_modules/webpack/bin/webpack.js --mode production && cd .. && mix phx.digest" 235 | ] 236 | 237 | assert AppConfig.get!(:erlang_version, pid) == @default_erlang_version 238 | assert AppConfig.get!(:elixir_version, pid) == @default_elixir_version 239 | end 240 | 241 | test "phoenix 1.4 with release" do 242 | config = """ 243 | env: flex 244 | runtime: gs://elixir-runtime/elixir.yaml 245 | runtime_config: 246 | release_app: my_app 247 | """ 248 | 249 | pid = AppConfigTest.setup_test("phoenix_1_4", config) 250 | assert AppConfig.status(pid) == :ok 251 | assert AppConfig.get!(:entrypoint, pid) == "[\"/app/bin/my_app\",\"foreground\"]" 252 | assert AppConfig.get!(:phoenix_version, pid) == "1.4.9" 253 | assert AppConfig.get!(:assets_dir, pid) == "assets" 254 | assert AppConfig.get!(:assets_builder, pid) == "webpack" 255 | 256 | assert AppConfig.get!(:build_scripts, pid) == [ 257 | "cd assets && npm install && node_modules/webpack/bin/webpack.js --mode production && cd .. && mix phx.digest" 258 | ] 259 | 260 | assert AppConfig.get!(:erlang_version, pid) == @default_erlang_version 261 | assert AppConfig.get!(:elixir_version, pid) == @default_elixir_version 262 | end 263 | 264 | test "phoenix 1.3 defaults" do 265 | pid = AppConfigTest.setup_test("phoenix_1_3", @minimal_config) 266 | assert AppConfig.status(pid) == :ok 267 | assert AppConfig.get!(:entrypoint, pid) == "exec mix phx.server" 268 | assert AppConfig.get!(:phoenix_version, pid) == "1.3.0" 269 | assert AppConfig.get!(:assets_dir, pid) == "assets" 270 | assert AppConfig.get!(:assets_builder, pid) == "brunch" 271 | 272 | assert AppConfig.get!(:build_scripts, pid) == [ 273 | "cd assets && npm install && node_modules/brunch/bin/brunch build --production && cd .. && mix phx.digest" 274 | ] 275 | 276 | assert AppConfig.get!(:erlang_version, pid) == @old_distillery_erlang_version 277 | assert AppConfig.get!(:elixir_version, pid) == @old_distillery_elixir_version 278 | end 279 | 280 | test "phoenix umbrella 1.3 defaults" do 281 | pid = AppConfigTest.setup_test("phoenix_umbrella_1_3", @minimal_config) 282 | assert AppConfig.status(pid) == :ok 283 | assert AppConfig.get!(:entrypoint, pid) == "exec mix phx.server" 284 | assert AppConfig.get!(:phoenix_version, pid) == "1.3.0" 285 | assert AppConfig.get!(:assets_dir, pid) == "apps/blog_web/assets" 286 | assert AppConfig.get!(:assets_builder, pid) == "brunch" 287 | 288 | assert AppConfig.get!(:build_scripts, pid) == [ 289 | "cd apps/blog_web/assets && npm install && node_modules/brunch/bin/brunch build --production && cd .. && mix phx.digest" 290 | ] 291 | 292 | assert AppConfig.get!(:erlang_version, pid) == @default_erlang_version 293 | assert AppConfig.get!(:elixir_version, pid) == @default_elixir_version 294 | end 295 | 296 | test "phoenix 1.2 defaults" do 297 | pid = AppConfigTest.setup_test("phoenix_1_2", @minimal_config) 298 | assert AppConfig.status(pid) == :ok 299 | assert AppConfig.get!(:entrypoint, pid) == "exec mix phoenix.server" 300 | assert AppConfig.get!(:phoenix_version, pid) == "1.2.5" 301 | assert AppConfig.get!(:assets_dir, pid) == "." 302 | assert AppConfig.get!(:assets_builder, pid) == "brunch" 303 | 304 | assert AppConfig.get!(:build_scripts, pid) == [ 305 | "npm install && node_modules/brunch/bin/brunch build --production && mix phoenix.digest" 306 | ] 307 | end 308 | 309 | test "missing app engine config" do 310 | pid = AppConfigTest.setup_test("minimal", nil) 311 | assert AppConfig.status(pid) == {:error, "Unable to find required `app.yaml` file."} 312 | end 313 | 314 | test "missing mix config" do 315 | pid = AppConfigTest.setup_test(nil, @minimal_config) 316 | assert AppConfig.status(pid) == {:error, "Unable to find required `mix.lock` file."} 317 | end 318 | 319 | test "illegal env variable name" do 320 | config = """ 321 | env: flex 322 | runtime: gs://elixir-runtime/elixir.yaml 323 | env_variables: 324 | VAR-1: value1 325 | """ 326 | 327 | pid = AppConfigTest.setup_test("minimal", config) 328 | assert AppConfig.status(pid) == {:error, "Illegal environment variable name: `VAR-1`."} 329 | end 330 | 331 | test "illegal debian package name" do 332 | config = """ 333 | env: flex 334 | runtime: gs://elixir-runtime/elixir.yaml 335 | runtime_config: 336 | packages: bad!package 337 | """ 338 | 339 | pid = AppConfigTest.setup_test("minimal", config) 340 | assert AppConfig.status(pid) == {:error, "Illegal debian package name: `bad!package`."} 341 | end 342 | 343 | test "illegal cloud sql instance name" do 344 | config = """ 345 | env: flex 346 | runtime: gs://elixir-runtime/elixir.yaml 347 | beta_settings: 348 | cloud_sql_instances: bad!instance 349 | """ 350 | 351 | pid = AppConfigTest.setup_test("minimal", config) 352 | assert AppConfig.status(pid) == {:error, "Illegal cloud sql instance name: `bad!instance`."} 353 | end 354 | 355 | test "entrypoint contains newline" do 356 | config = """ 357 | env: flex 358 | runtime: gs://elixir-runtime/elixir.yaml 359 | entrypoint: "multiple\\nlines" 360 | """ 361 | 362 | pid = AppConfigTest.setup_test("minimal", config) 363 | assert AppConfig.status(pid) == {:error, "Entrypoint may not contain a newline."} 364 | end 365 | 366 | test "build script contains newline" do 367 | config = """ 368 | env: flex 369 | runtime: gs://elixir-runtime/elixir.yaml 370 | runtime_config: 371 | build: "multiple\\nlines" 372 | """ 373 | 374 | pid = AppConfigTest.setup_test("minimal", config) 375 | assert AppConfig.status(pid) == {:error, "A build script may not contain a newline."} 376 | end 377 | end 378 | --------------------------------------------------------------------------------