Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deployers phoenix improvements #4113

Merged
merged 10 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions deploy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
CREATE_AND_PUSH_BRANCH = !get_env("DEPLOY_CREATE_AND_PUSH_BRANCH").nil?
FLYIO_BRANCH_NAME = "flyio-new-files"

DEPLOY_TRIGGER = get_env("DEPLOY_TRIGGER")
DEPLOYER_FLY_CONFIG_PATH = get_env("DEPLOYER_FLY_CONFIG_PATH")
DEPLOYER_SOURCE_CWD = get_env("DEPLOYER_SOURCE_CWD")
DEPLOY_APP_NAME = get_env("DEPLOY_APP_NAME")
Expand Down Expand Up @@ -268,7 +269,7 @@
ORG_SLUG = manifest["plan"]["org"]
APP_REGION = manifest["plan"]["region"]

DO_GEN_REQS = !DEPLOY_COPY_CONFIG || !HAS_FLY_CONFIG
DO_GEN_REQS = DEPLOY_TRIGGER == "launch"

debug("generate reqs? #{DO_GEN_REQS}")

Expand Down Expand Up @@ -314,7 +315,7 @@
APP_NAME = DEPLOY_APP_NAME || fly_config["app"]

image_ref = in_step Step::BUILD do
image_tag = SecureRandom.hex(16)
image_tag = "deployment-#{SecureRandom.hex(16)}"
if (image_ref = fly_config.dig("build","image")&.strip) && !image_ref.nil? && !image_ref.empty?
info("Skipping build, using image defined in fly config: #{image_ref}")
image_ref
Expand Down
2 changes: 1 addition & 1 deletion deployer.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
dpkg-reconfigure --frontend=noninteractive locales && \
update-locale LANG=en_US.UTF-8

ENV LANG en_US.UTF-8

Check warning on line 16 in deployer.Dockerfile

View workflow job for this annotation

GitHub Actions / build-deployer

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/

# configure git a bit
RUN git config --global advice.detachedHead false && \
Expand Down Expand Up @@ -55,10 +55,10 @@
asdf install nodejs $DEFAULT_NODE_VERSION && asdf global nodejs $DEFAULT_NODE_VERSION && \
# elixir
asdf plugin-add erlang https://github.com/michallepicki/asdf-erlang-prebuilt-ubuntu-20.04.git && \
echo -e "local.hex\nlocal.rebar" > $HOME/.default-mix-commands && \
asdf plugin add elixir https://github.com/asdf-vm/asdf-elixir.git && \
asdf install erlang $DEFAULT_ERLANG_VERSION && asdf global erlang $DEFAULT_ERLANG_VERSION && \
asdf install elixir $DEFAULT_ELIXIR_VERSION && asdf global elixir $DEFAULT_ELIXIR_VERSION && \
mix local.hex --force && mix local.rebar --force && \
# bun
asdf plugin add bun https://github.com/cometkim/asdf-bun.git && \
asdf install bun $DEFAULT_BUN_VERSION && asdf global bun $DEFAULT_BUN_VERSION
Expand Down
9 changes: 0 additions & 9 deletions internal/command/launch/launch.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,3 @@ func (state *launchState) createApp(ctx context.Context) (*fly.App, error) {

return app, nil
}

func (state *launchState) getApp(ctx context.Context) (*fly.App, error) {
apiClient := flyutil.ClientFromContext(ctx)
app, err := apiClient.GetApp(ctx, state.Plan.AppName)
if err != nil {
return nil, err
}
return app, nil
}
28 changes: 28 additions & 0 deletions scanner/phoenix.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,34 @@ func configurePhoenix(sourceDir string, config *ScannerConfig) (*SourceInfo, err
},
}

// This adds support on launch UI for repos with different .tool-versions
deployTrigger := os.Getenv("DEPLOY_TRIGGER")
if deployTrigger == "launch" && helpers.FileExists(filepath.Join(sourceDir, ".tool-versions")) {
cmd := exec.Command("asdf", "install")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
return nil, errors.Wrap(err, "We identified .tool-versions but after running `asdf install` we ran into some errors. Please check that your `asdf install` builds successfully and try again.")
}

cmd = exec.Command("mix", "local.hex", "--force")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return nil, errors.Wrap(err, "After installing your elixir version with asdf we found an error while running `mix local.hex --force`. Please confirm that running this command works locally and try again.")
}

cmd = exec.Command("mix", "local.rebar", "--force")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return nil, errors.Wrap(err, "After installing your elixir version with asdf we found an error while running `mix local.rebar --force`. Please confirm that running this command works locally and try again.")
}
}

// We found Phoenix, so check if the project compiles.
cmd := exec.Command("mix", "do", "deps.get,", "compile")
cmd.Stdout = os.Stdout
Expand Down
4 changes: 0 additions & 4 deletions scanner/rails.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,10 +444,6 @@ The following comand can be used to update your Dockerfile:
}
}

if srcInfo.DatabaseDesired == DatabaseKindSqlite {

}

// add HealthCheck (if found)
srcInfo.HttpCheckPath = <-healthcheck_channel
if srcInfo.HttpCheckPath != "" {
Expand Down
48 changes: 48 additions & 0 deletions test/deployer/deployer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,54 @@ func TestLaunchStatic(t *testing.T) {
require.Contains(t, string(body), "<body>Hello World</body>")
}

func TestDeployPhoenixSqlite(t *testing.T) {
deploy := testDeployer(t,
withFixtureApp("deploy-phoenix-sqlite"),
createRandomApp,
withOverwrittenConfig(func(d *testlib.DeployTestRun) map[string]any {
return map[string]any{
"app": d.Extra["appName"],
"region": d.PrimaryRegion(),
"env": map[string]string{
"TEST_ID": d.ID(),
},
}
}),
testlib.DeployOnly,
testlib.DeployNow,
withWorkDirAppSource,
)

body, err := testlib.RunHealthCheck(fmt.Sprintf("https://%s.fly.dev", deploy.Extra["appName"].(string)))
require.NoError(t, err)

require.Contains(t, string(body), "Phoenix")
}

func TestDeployPhoenixSqliteWithCustomToolVersions(t *testing.T) {
deploy := testDeployer(t,
withFixtureApp("deploy-phoenix-sqlite-custom-tool-versions"),
createRandomApp,
withOverwrittenConfig(func(d *testlib.DeployTestRun) map[string]any {
return map[string]any{
"app": d.Extra["appName"],
"region": d.PrimaryRegion(),
"env": map[string]string{
"TEST_ID": d.ID(),
},
}
}),
testlib.DeployOnly,
testlib.DeployNow,
withWorkDirAppSource,
)

body, err := testlib.RunHealthCheck(fmt.Sprintf("https://%s.fly.dev", deploy.Extra["appName"].(string)))
require.NoError(t, err)

require.Contains(t, string(body), "Phoenix")
}

func createRandomApp(d *testlib.DeployTestRun) {
appName := d.CreateRandomAppName()
require.NotEmpty(d, appName)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This file excludes paths from the Docker build context.
#
# By default, Docker's build context includes all files (and folders) in the
# current directory. Even if a file isn't copied into the container it is still sent to
# the Docker daemon.
#
# There are multiple reasons to exclude files from the build context:
#
# 1. Prevent nested folders from being copied into the container (ex: exclude
# /assets/node_modules when copying /assets)
# 2. Reduce the size of the build context and improve build time (ex. /build, /deps, /doc)
# 3. Avoid sending files containing sensitive information
#
# More information on using .dockerignore is available here:
# https://docs.docker.com/engine/reference/builder/#dockerignore-file

.dockerignore

# Ignore git, but keep git HEAD and refs to access current commit hash if needed:
#
# $ cat .git/HEAD | awk '{print ".git/"$2}' | xargs cat
# d0b8727759e1e0e7aa3d41707d12376e373d5ecc
.git
!.git/HEAD
!.git/refs

# Common development/test artifacts
/cover/
/doc/
/test/
/tmp/
.elixir_ls

# Mix artifacts
/_build/
/deps/
*.ez

# Generated on crash by the VM
erl_crash.dump

# Static artifacts - These should be fetched and built inside the Docker image
/assets/node_modules/
/priv/static/assets/
/priv/static/cache_manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
import_deps: [:ecto, :phoenix],
inputs: ["*.{ex,exs}", "priv/*/seeds.exs", "{config,lib,test}/**/*.{ex,exs}"],
subdirectories: ["priv/*/migrations"]
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# The directory Mix will write compiled artifacts to.
/_build/
/.elixir_ls/

# If you run "mix test --cover", coverage assets end up here.
/cover/

# The directory Mix downloads your dependencies sources to.
/deps/

# Where 3rd-party dependencies like ExDoc output generated docs.
/doc/

# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch

# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump

# Also ignore archive artifacts (built via "mix archive.build").
*.ez

# Ignore package tarball (built via "mix hex.build").
hello_elixir-*.tar

# Ignore assets that are produced by build tools.
/priv/static/assets/

# Ignore digested assets cache.
/priv/static/cache_manifest.json

# In case you use Node.js/npm, you want to ignore these.
npm-debug.log
/assets/node_modules/

hello_elixir_dev.db*
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
erlang 24.3.4.9
elixir 1.12.3-otp-24
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Find eligible builder and runner images on Docker Hub. We use Ubuntu/Debian instead of
# Alpine to avoid DNS resolution issues in production.
#
# https://hub.docker.com/r/hexpm/elixir/tags?page=1&name=ubuntu
# https://hub.docker.com/_/ubuntu?tab=tags
#
#
# This file is based on these images:
#
# - https://hub.docker.com/r/hexpm/elixir/tags - for the build image
# - https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye-20210902-slim - for the release image
# - https://pkgs.org/ - resource for finding needed packages
# - Ex: hexpm/elixir:1.13.3-erlang-24.0.5-debian-bullseye-20210902-slim
#
ARG ELIXIR_VERSION=1.13.3
ARG OTP_VERSION=24.0.5
ARG DEBIAN_VERSION=bullseye-20210902-slim

ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"

FROM ${BUILDER_IMAGE} as builder

# install build dependencies
RUN apt-get update -y && apt-get install -y build-essential git \
&& apt-get clean && rm -f /var/lib/apt/lists/*_*

# prepare build dir
WORKDIR /app

# install hex + rebar
RUN mix local.hex --force && \
mix local.rebar --force

# set build ENV
ENV MIX_ENV="prod"

# install mix dependencies
COPY mix.exs mix.lock ./
RUN mix deps.get --only $MIX_ENV
RUN mkdir config

# copy compile-time config files before we compile dependencies
# to ensure any relevant config change will trigger the dependencies
# to be re-compiled.
COPY config/config.exs config/${MIX_ENV}.exs config/
RUN mix deps.compile

COPY priv priv

COPY lib lib

COPY assets assets

# compile assets
RUN mix assets.deploy

# Compile the release
RUN mix compile

# Changes to config/runtime.exs don't require recompiling the code
COPY config/runtime.exs config/

COPY rel rel
RUN mix release

# start a new build stage so that the final image will only contain
# the compiled release and other runtime necessities
FROM ${RUNNER_IMAGE}

RUN apt-get update -y && apt-get install -y libstdc++6 openssl libncurses5 locales sqlite3 \
&& apt-get clean && rm -f /var/lib/apt/lists/*_*

# Set the locale
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen

ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8

WORKDIR "/app"
RUN chown nobody /app

# set runner ENV
ENV MIX_ENV="prod"

# Only copy the final release from the build stage
COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/hello_elixir ./

USER nobody

CMD ["/app/bin/server"]
# Appended by flyctl
ENV ECTO_IPV6 true
ENV ERL_AFLAGS "-proto_dist inet6_tcp"
Loading
Loading