Difference between deploying github actions and flyctl

Deployed Phoenix app with tailwindcss from flyctl first. it worded successfully.

Deployed same app with github actions, it worked successfully except tailwindcss (no style were seen when I opened the app).

The versions deployed with flyctl are same as local dev, but with GH actions tailwindcss just doesn’t work.

Maybe i should look more into what is happening under the hood in the phoenix framework.

I wonder what is the speciific diffderence between flyctl and GH actions about deploying and how FLY_API_TOKEN works under the hood in GH actions.

github actions file

name: Fly Deploy

on: [push]

env:
  FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

jobs:
  dependencies:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        elixir: ['1.13.2']
        otp: ['24.2']
    steps:
      - name: Cancel previous runs
        uses: styfle/cancel-workflow-action@0.9.0
        with:
          access_token: ${{ github.token }}
      - name: Checkout Github repo
        uses: actions/checkout@v2
      - name: Sets up an Erlang/OTP environment
        uses: erlef/setup-beam@v1
        with:
          elixir-version: ${{ matrix.elixir }}
          otp-version: ${{ matrix.otp }}
      - name: Retrieve cached dependencies
        uses: actions/cache@v2
        id: mix-cache
        with:
          path: |
            deps
            _build
          key: ${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('mix.lock') }}
      - name: Install dependencies
        if: steps.mix-cache.outputs.cache-hit != 'true'
        run: |
          mix local.rebar --force
          mix local.hex --force
          mix deps.get
          mix deps.compile
  test:
    needs: dependencies
    runs-on: ubuntu-latest
    strategy:
      matrix:
        elixir: ['1.13.2']
        otp: ['24.2']
    services:
      db:
        image: postgres:latest
        ports: ['5432:5432']
        env:
          POSTGRES_USER: root
          POSTGRES_PASSWORD: root
        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
    steps:
      - name: Cancel previous runs
        uses: styfle/cancel-workflow-action@0.9.0
        with:
          access_token: ${{ github.token }}
      - name: Checkout Github repo
        uses: actions/checkout@v2
      - name: Sets up an Erlang/OTP environment
        uses: erlef/setup-beam@v1
        with:
          elixir-version: ${{ matrix.elixir }}
          otp-version: ${{ matrix.otp }}
      - name: Retrieve cached dependencies
        uses: actions/cache@v2
        id: mix-cache
        with:
          path: |
            deps
            _build
          key: ${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('mix.lock') }}
      - run: mix test

  deploy:
      needs: test
      name: Deploy app
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v2
        - uses: superfly/flyctl-actions@1.1
          with:
            args: "deploy"

fly.toml file

app = "my_app"

kill_signal = "SIGTERM"
kill_timeout = 5
processes = []

[deploy]
  release_command = "/app/bin/migrate"

[env]

[experimental]
  allowed_public_ports = []
  auto_rollback = true

[[services]]
  http_checks = []
  internal_port = 4000
  processes = ["app"]
  protocol = "tcp"
  script_checks = []

  [services.concurrency]
    hard_limit = 25
    soft_limit = 20
    type = "connections"

  [[services.ports]]
    handlers = ["http"]
    port = 80

  [[services.ports]]
    handlers = ["tls", "http"]
    port = 443

  [[services.tcp_checks]]
    grace_period = "30s"
    interval = "15s"
    restart_limit = 6
    timeout = "2s"

Dockerfile

ARG BUILDER_IMAGE="hexpm/elixir:1.13.1-erlang-24.2-debian-bullseye-20210902-slim"
ARG RUNNER_IMAGE="debian:bullseye-20210902-slim"

FROM ${BUILDER_IMAGE} as builder

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

WORKDIR /app

RUN mix local.hex --force && \
    mix local.rebar --force

ENV MIX_ENV="prod"

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

COPY config/config.exs config/${MIX_ENV}.exs config/
RUN mix deps.compile

COPY lib lib

COPY priv priv

COPY assets assets

RUN mix assets.deploy

RUN mix compile

COPY config/runtime.exs config/

COPY rel rel
RUN mix release

FROM ${RUNNER_IMAGE}

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

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

COPY --from=builder --chown=nobody:root /app/_build/prod/rel/supac ./

USER nobody

CMD ["/app/bin/server"]

ENV ECTO_IPV6 true
ENV ERL_AFLAGS "-proto_dist inet6_tcp

Thanks

There’s no difference in how flyctl works (the action is just a wrapper around flyctl). There are probably different files on your local system vs GitHub though! Do you have a .dockerignore file?

Also, is this tailwind standalone are are you using it with node?

1 Like

Hi, @kurt !
Thank you for repling!
Sorry for replying back late.
I feel like I’m getting close! I’ll keep trying!


There’s no difference in how flyctl works (the action is just a wrapper around flyctl).

I’m glad to hear that there is no difference!


There are probably different files on your local system vs GitHub though!

local files(folders) can’t be seen in github repo are

  • .
  • ..
  • .elixir_ls
  • Myapp.session.sql (generated when executed sql with sqltools, which is one of vscode extentions )
  • _build
  • deps
  • doc
  • erl_crash.dump (generated when I did something wrong with local server in the past)

ls command for local env is like below!

local dir % ls -a -1
.
..
.dockerignore
.elixir_ls
.formatter.exs
.git
.github
.gitignore
Dockerfile
README.md
Myapp.session.sql
_build
assets
config
deps
doc
erl_crash.dump
fly.toml
lib
mix.exs
mix.lock
priv
rel
test

github repo files are like below!

.github/workflows
assets
config
lib
priv
rel
test
.dockerignore
.fomatter.exs
.gitignore
Dockerfile
README.md
fly.toml
mix.exs
mix.lock

Do you have a .dockerignore file?

Yes, I do have .dockerignore file! Both github repo and local dir have it.

.dockerignore file

.dockerignore
# there are valid reasons to keep the .git, namely so that you can get the
# current commit hash
#.git
.log
tmp

# Mix artifacts
_build
deps
*.ez
releases

# Generate on crash by the VM
erl_crash.dump

# Static artifacts
node_modules

Also my .gitignore file

# The directory Mix will write compiled artifacts to.
/_build/

# 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").
supac-*.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/

# Ignore sql files
*.sql

# Ignore .vscode
/.vscode/

Also, is this tailwind standalone are are you using it with node?

No, I use Taildwind without node. My app’s phoenix version is 1.6.6.


Thanks :relaxed:

1 Like

if it still doesn’t work for you, you can check my app, it works with GitHub Actions and also Macbook M1 manual deploy.
App
Source

3 Likes

Thanks @quatermain !

1 Like

Thanks so much @quatermain ! You app was super helpful. My app was using Tailwind fine locally but was not looking right in production. I spotted your commit in your app:

That was the fix my app needed too. :+1::+1:

1 Like

I got bitten by this yesterday, and there were exactly 3 problems IME. I want to detail it here just in case someone gets bitten by this and has no idea what’s happening.

  1. The reason why it works if you run fly deploy locally is because the processed app.css already exists in priv/static/assets/app.css, which the official Dockerfile copies the entire priv directory over to the image. It exists because of the dev server.
  2. Of course the priv/static/assets/app.css is ignored by .gitignore, this means GitHub Actions has to minify and do all that from scratch, which it can’t because the tailwind build was missing from mix assets.deploy. So I added a Tailwind step in the assets.deploy alias in mix.exs sekun.dev/mix.exs at 4b8561dad026672396ac8250ad7a88b40b3e04f4 · sekunho/sekun.dev · GitHub.
  3. The next problem was it seemed like I only had the default Tailwind styling, none of which included my inline classes. It seemed like my styles were purged, since Tailwind purges things it doesn’t find used. Turns out that this issue was because in the Dockerfile, lib gets copied after the assets get built, which is a problem because the components are in lib! So you need to move COPY lib lib before mix assets.deploy is executed. Add `tailwind` build step. Remove placeholders · sekunho/sekun.dev@4b8561d · GitHub
3 Likes

Huge thanks @sekunho !
I’ll try again!