fireproofsocks

fireproofsocks

Migration file not found during Github action

I’m missing something and I hope someone can point it out to me. Long story short, the first migration in my app needs to slurp up a dump file, e.g. init.sql. It sets does stuff like SET statement_timeout = 0 and CREATE SCHEMA core etc. (There are some historical reasons why we are using this).

Two things made this tricky:

  1. The Ecto repo didn’t use the normal name.
  2. The app that controls the repo is sometimes used as a dependency.

But we figured this out. The trick was a migration file that could point to the init.sql file, both when the app was used as the primary app OR when it gets used as a dependency. It boiled down to this (comments left in for transparency):

defmodule MyApp.PGRepo.Migrations.InitDb do
  use Ecto.Migration

  require Logger

  @doc """
  This is a "Mixless" way of running a custom migration.

  This migration must be flexible enough so we can run it from inside `my_app`
  or inside some other app that uses `my_app` as a dependency. We rely on
  `Ecto.Migration.repo/0` to tell us which repo is triggering the migration, and
  we use `:code.priv_dir/1` to infer the environment.
  """
  def change do
    # This magically knows which Repo is running the migrations
    # (it is important that we do not hard-code `MyApp.Repo`)
    repo = Ecto.Migration.repo()

    # [`c:Ecto.Adapter.Structure.structure_load/2`](https://hexdocs.pm/ecto_sql/Ecto.Adapter.Structure.html#c:structure_load/2)
    # REQUIRES the configuration to be broken out with individual keys
    # for username, password, etc.
    x = repo.__adapter__().structure_load(repo_path(), config(repo))
    Logger.debug(inspect(x))
  end

  # This migration must be flexible enough so we can run it from inside this app
  # or inside some other app that uses this as a dependency.
  defp repo_path do
    "#{:code.priv_dir(:my_app)}/pg_repo"
  end

  # This fetches the repo config and overrides the `:dump_path` to point to
  # the sql dump used to define the initial structure of the database.
  #
  # Do NOT use `Application.get_env(:my_app, Ecto.Migration.repo())`
  # because it will NOT parse out individual settings from a db URL!
  # Instead, use the [`c:Ecto.Repo.config/0`](https://hexdocs.pm/ecto/3.5.2/Ecto.Repo.html#c:config/0)
  # because this DOES parse out a db URL into its component parts (`:username`, `:password`, etc.).
  defp config(repo) do
    repo.config()
    |> Keyword.put(:dump_path, "#{repo_path()}/migrations/init.sql")
  end
end

The migrations get executed when we run tests on a PR on Github via Github actions. The github workflow is pretty straightforward, it contains (in part):

      - name: Compile
        run: mix compile

      - name: Create PostGres database
        run: mix ecto.create

      - name: Migrate PostGres database
        run: mix ecto.migrate

This has all been working fine.

Until… I copied this structure over to a new repo. And suddenly the init.sql file is not found when the Github workflow executes on the new repo. I added an inspection to the problematic line:

    x = repo.__adapter__().structure_load(repo_path(), config(repo))
    Logger.debug(inspect(x))

When I run tests etc. locally, it works fine. On Github, however, I get an error because the external file is not found:

_build/test/lib/my_app/priv/pg_repo/migrations/init.sql NOT FOUND

I’m not seeing anything between the 2 repos that would explain why the old app finds its init.sql file but the new app does not. Does anyone have any ideas of why the .sql file isn’t being found when the tests run externally in Github?

And, just to be thorough, I tried rewriting this first migration WITHOUT this weird .sql file, with something like this:

def change do
  # ...
  execute("CREATE SCHEMA core")
end

But this fails with a different error:

** (Postgrex.Error) ERROR 42P01 (undefined_table) relation "schema_migrations" does not exist

These are 2 different errors… I need to solve one of them to make migrations work (I don’t really care which one shakes out).

Thanks for any ideas!

Marked As Solved

christhekeele

christhekeele

Does your release configuration copy all /priv/migrations over? Or maybe just .exs one?

Also Liked

fireproofsocks

fireproofsocks

Ah, this was the reality check I needed. I had created a global gitignore file that for some reason was ignoring .sql files, so that thing never made it up to Github. As soon as I committed that, things worked as expected.

The problem was between the keyboard and the chair.

Where Next?

Popular in Questions Top

vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

Other popular topics Top

Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40165 209
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 44265 214
New

We're in Beta

About us Mission Statement