Skip to content

Instantly share code, notes, and snippets.

@chrismccord
Last active April 7, 2023 12:03
Show Gist options
  • Save chrismccord/71ab10d433c98b714b75c886eff17357 to your computer and use it in GitHub Desktop.
Save chrismccord/71ab10d433c98b714b75c886eff17357 to your computer and use it in GitHub Desktop.
Phoenix 1.2.x to 1.3.0 Upgrade Instructions

If you want a run-down of the 1.3 changes and the design decisions behidn those changes, check out the LonestarElixir Phoenix 1.3 keynote: https://www.youtube.com/watch?v=tMO28ar0lW8

To use the new phx.new project generator, you can install the archive with the following command:

$ mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez

Bump your phoenix dep

Phoenix v1.3.0 is a backwards compatible release with v1.2.x. To upgrade your existing 1.2.x project, simply bump your phoenix dependency in mix.exs:

def deps do
  [{:phoenix, "~> 1.3.0-rc"},
   ...]
end

Update your static manifest location

If using the digest task (mix phoenix.digest), the location of the built manifest has changed from priv/static/manifest.json to priv/static/cache_manifest.json. Update your config/prod.exs endpoint config with the following changes:

-cache_static_manifest: "priv/static/manifest.json"
+cache_static_manifest: "priv/static/cache_manifest.json"

Next, run mix deps.get and you're all set! Continue reading to jump on the latest project structure conventions (optional).

Update your project the new 1.3 directory structure (optional)

mix.exs

In mix.exs, update your elixirc_paths/1 clauses to remove "web":

- defp elixirc_paths(:test), do: ["lib", "web", "test/support"]
- defp elixirc_paths(_),     do: ["lib", "web"]
+ defp elixirc_paths(:test), do: ["lib", "test/support"]
+ defp elixirc_paths(_),     do: ["lib"]

If you have a reloadable_paths configuration in your config/dev.exs, you may remove this value as all elixirc_paths are now reloaded in dev by default.

Move your root-level web/ directory to lib/app_name_web, and migrate to new Web namespace

The root level web/ directory for new projects has been removed in favor of lib/my_app_web. Additionally, a Web namespace convention has been added to isolate the web interface from your elixir application.

Migrate your files to the new structure with the following steps:

  1. $ cd my_app
  2. $ mv web/web.ex lib/my_app_web.ex
  3. $ mv web lib/my_app_web
  4. $ mv lib/my_app/endpoint.ex lib/my_app_web/
  5. Update your view root path in lib/my_app_web.ex to point to the new template location, and add the :namespace option:
  def view do
    quote do
-      use Phoenix.View, root: "web/templates"
+      use Phoenix.View, root: "lib/my_app_web/templates",
+                        namespace: MyAppWeb
       ...
  1. Add the namespace option to your controller definition in web.ex:
  def controller do
    quote do
-     use Phoenix.Controller
+     use Phoenix.Controller, namespace: MyAppWeb
  1. Update your aliases in web.ex controller, view, and channel definitions to use the new Web namespace:
  def controller do
    quote do
      ...
-     import MyApp.Router.Helpers
+     import MyAppWeb.Router.Helpers
-     import MyApp.Gettext
+     import MyAppWeb.Gettext
    end
  end

  def view do
    quote do
      ...
-     import MyApp.Router.Helpers
+     import MyAppWeb.Router.Helpers
-     import MyApp.ErrorHelpers
+     import MyAppWebErrorHelpers
-     import MyApp.Gettext
+     import MyAppWeb.Gettext
    end
  end

  def channel do
    quote do
      ...
-     import MyApp.Gettext
+     import MyAppWeb.Gettext
    end
  end
  1. Rename all web related modules in lib/my_app_web/ (gettext.ex, controllers/*, views/*, channels/*, endpoint.ex, router.ex) to include a Web namespace, for example:
  • MyApp.Endpoint => MyAppWeb.Endpoint
  • MyApp.Router => MyAppWeb.Router
  • MyApp.PageController => MyAppWeb.PageController
  • MyApp.PageView => MyAppWeb.PageView
  • MyApp.UserSocket => MyAppWeb.UserSocket
  • etc
  1. Update all alias in lib/app_name_web/router.ex to include new Web namespace. Most likely you can accomplish this by adding Web to the second argument of your scope blocks, for example:
- defmodule MyApp.Router do
+ defmodule MyAppWeb.Router do
  ...
-   scope "/", MyApp do
+   scope "/", MyAppWeb do
      pipe_through :browser
     
      resources "/users", UserController
      ...
   end
  1. Update endpoint.ex to use new web modules:
- defmodule MyApp.Endpoint do
+ defmodule MyAppWeb.Endpoint do
    ...
-   socket "/socket", MyApp.UserSocket
+   socket "/socket", MyAppWeb.UserSocket
    ...
-   plug MyApp.Router
+   plug MyAppWeb.Router
  1. in lib/my_app.ex, update your children to reference the new endpoint:
    ...
    children = [
      ...
-     supervisor(MyApp.Endpoint, []),
+     supervisor(MyAppWeb.Endpoint, []),
      ...
    ]
    
  ...
  1. Update all endpoint aliases in config/*.exs (config.exs, prod.exs, prod.secret.exs dev.exs, test.exs, etc) aliases to use new Web namespace:
- config :my_app, MyApp.Endpoint,
+ config :my_app, MyAppWeb.Endpoint,
    ...
-   render_errors: [view: MyApp.ErrorView, accepts: ~w(html json)],
+   render_errors: [view: MyAppWeb.ErrorView, accepts: ~w(html json)],
  ...
  1. Update your live-reload patterns config in config/dev.exs:
- config :my_app, MyApp.Endpoint,
+ config :my_app, MyAppWeb.Endpoint,
    live_reload: [
      patterns: [
        ~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$},
        ~r{priv/gettext/.*(po)$},
-       ~r{web/views/.*(ex)$},
-       ~r{web/templates/.*(eex)$}
+       ~r{lib/my_app_web/views/.*(ex)$},
+       ~r{lib/my_app_web/templates/.*(eex)$}
      ]
    ]
  1. Rename your test/support/conn_case.ex and test/support/channel_case.ex modules to include Web namespace, and update @endpoint and router aliases in each:
-defmodule MyApp.ConnCase do
+defmodule MyAppWeb.ConnCase do
  using do
    quote do
      ...
-     import MyApp.Router.Helpers
+     import MyAppWeb.Router.Helpers

      # The default endpoint for testing
-     @endpoint MyApp.Endpoint
+     @endpoint MyAppWeb.Endpoint
    end
  end
  ...


-defmodule MyApp.ChannelCase do
+defmodule MyAppWeb.ChannelCase do

  using do
    quote do
      ...
-     @endpoint MyApp.Endpoint
+     @endpoint MyAppWeb.Endpoint
    end
  end
  ...
  1. Update all test/*/**.exs references to use MyApp.ConnCase or use MyApp.ChannelCase to use new MyAppWeb.ConnCase and MyAppWeb.ChannelCase aliases.

Move static assets inside self-contained assets/ directory

New projects now include a root-level assets/ directory, which serves as a self-contained location for your asset builder's config, source files, and packages. This changer keeps things like node_modules, package.json, and brunch-config.js from leaking into the root of your elixir application. Update your app to the new structure by following these steps:

  1. move all web/static/ sources into assets/, followed by package.json, node_modules, and brunch-config.js
$ mv mv lib/my_app_web/static assets/
$ mv assets/assets assets/static
$ mv package.json assets/
$ mv brunch-config.js assets/
$ rm -rf node_modules
  1. Update your asset/package.json phoenix and phoenix_html paths:
  "dependencies": {
-   "phoenix": "file:deps/phoenix",
+   "phoenix": "file:../deps/phoenix",
-   "phoenix_html": "file:deps/phoenix_html"
+   "phoenix_html": "file:../deps/phoenix_html"
  },
  1. Update your assets/brunch-config.js to be aware of the new conventions:
  conventions: {
-   // This option sets where we should place non-css and non-js assets in.
-   // By default, we set this to "/web/static/assets". Files in this directory
-   // will be copied to `paths.public`, which is "priv/static" by default.
-   assets: /^(web\/static\/assets)/
+   // This option sets where we should place non-css and non-js assets in.
+   // By default, we set this to "/assets/static". Files in this directory
+   // will be copied to `paths.public`, which is "priv/static" by default.
+   assets: /^(static)/
  },
  
  paths: {
    // Dependencies and current project directories to watch
-   watched: [
-     "web/static",
-     "test/static"
-   ],
+   watched: ["static", "css", "js", "vendor"],

    // Where to compile files to
-   public: "priv/static"
+   public: "../priv/static"
  },

  plugins: {
    babel: {
      // Do not use ES6 compiler in vendor code
-     ignore: [/web\/static\/vendor/]
+     ignore: [/vendor/]
    }
  },

  modules: {
    autoRequire: {
-     "js/app.js": ["web/static/js/app"]
+     "js/app.js": ["js/app"]
    }
  },
  1. Update your config/dev.exs watcher to run in the new assets directory:
config :my_app, MyAppWeb.Endpoint,
  ...
  watchers: [node: ["node_modules/brunch/bin/brunch", "watch", "--stdin",
-                  cd: Path.expand("../", __DIR__)]]
+                  cd: Path.expand("../assets", __DIR__)]]

  1. Install the node deps: $cd assets && npm install

Test it all with mix phx.server, and mix test and you should see:

$ mix phx.server
[info] Running MyAppWeb.Endpoint with Cowboy using http://0.0.0.0:4000
01 Mar 15:40:05 - info: compiled 6 files into 2 files, copied 3 in 976ms
@lubien
Copy link

lubien commented Mar 2, 2017

One more thing. Update gitignore.

- /node_modules
+ /assets/node_modules

@br4ndur
Copy link

br4ndur commented Mar 2, 2017

minor detail: MyApp.Web.ErrorHelpers instead of MyApp.WebErrorHelpers

@cohawk
Copy link

cohawk commented Mar 3, 2017

I had to also update assets/package.json to bump up my Brunch version to avoid an error Cannot find module 'js/app' from '/' in app.js. Or at least I think bumping Brunch to 2.10.7 is what resolved the error. I also bumped up the versions of the other supporting Brunch dependencies to match what 1.3 mix phx.new generated.

@rossta
Copy link

rossta commented Mar 4, 2017

Optional change:

When migrating an existing app, you may want to rename MyApp.ModelCase to MyApp.DataCase, mv test/support/model_case.ex to test/support/data_case.ex, and update module references in existing model tests.

The new generators for creating a context and schema (formerly known as "model") will generate schema test cases with use MyApp.DataCase instead of MyApp.ModelCase, so you'll have to handle the missing module name somehow.

@ndarilek
Copy link

ndarilek commented Mar 4, 2017

One point that was missed, which I noticed when generating a new 1.3 application:

  • $ mv lib/my_app.ex lib/my_app/application.ex
  • Change defmodule MyApp to defmodule MyApp.Application.
  • Change the App in the mod parameter in mix.exs to MyApp.Application.

I didn't get that exactly right, but that's vaguely what I did. It isn't a breaking change, but if the goal is to make upgraded apps identical to newly-generated ones, that change should probably be documented.

@technicalcapt
Copy link

@cohawk Thank you for pointing that out.I have to bump up my brunch version to avoid that error too.

Copy link

ghost commented Mar 5, 2017

Beautiful! I've got to check it out right now.

I believe there is a small typo, though:

+     import MyApp.WebErrorHelpers

should probably be (missing dot after Web):

+     import MyApp.Web.ErrorHelpers

@jeregrine
Copy link

One weird thing was I had to update phoenix_ecto from 3.0 to 3.2. Otherwise seems to have gone smoothly.

Maybe need to write a guide on porting "models" into the new style, but thats a longer discussion.

@sbrink
Copy link

sbrink commented Mar 9, 2017

It really helps to use RegEx for renaming.

Here's an example:

Match: MyApplication.(.*)ControllerTest
Replace: MyApplication.Web.$1ControllerTest

Worked perfectly in VSCode.

@chulkilee
Copy link

Check out https://www.phoenixdiff.org/ for changes in generated apps

@svileng
Copy link

svileng commented Mar 9, 2017

I think the manifest file generated by phoenix.digest has been renamed to cache_manifest.json. I had to change

  cache_static_manifest: "priv/static/manifest.json",

to

  cache_static_manifest: "priv/static/cache_manifest.json",

The hash id disappeared from the links in the templates and is now back 👌

@sinnedh
Copy link

sinnedh commented Mar 10, 2017

Just a typo in the assets part in 1)
duplicate mv command: mv mv

@nlfiedler
Copy link

If you get the Cannot find module 'js/app' from '/' in app.js error in your browser (I was using Safari), then chances are you have a leading slash in your brunch-config.js that should not be there.

  modules: {
    autoRequire: {
      "js/app.js": ["/js/app"]
    }
  },

Remove the leading slash in "/js/app", like so:

modules: {
    autoRequire: {
      "js/app.js": ["js/app"]
    }
  },

When I had tried a simple search and replace, this was the result, and it took me a long time to see the mistake (my excuse is that I am new to Brunch).

@andrewtimberlake
Copy link

andrewtimberlake commented Mar 14, 2017

  1. There’s a typo in the diff for Update your static manifest location
    -cache_static_manifest: "priv/static/cache_manifest.json"
    should be
    -cache_static_manifest: "priv/static/manifest.json"

  2. Shouldn’t Update all test/*/**.exs be Update all test/**/*.exs

@nekath
Copy link

nekath commented Mar 19, 2017

Like @cohawk says, to fix Uncaught Error: Cannot find module 'js/app' from '/' all you need is to update brunch to version 2.10.7 or greater.
(2.10.7 is shipped by default in phoenix 1.3)

- "brunch": "2.7.4",
+ "brunch": "2.10.7",

@jonathan-soifer
Copy link

Hi @chrismccord !

I had some issues trying to update an application that uses guardian, thought it might impact other people:

ueberauth/guardian#273

@hedgesky
Copy link

Maybe import MyApp.WebErrorHelpers should be import MyApp.Web.ErrorHelpers in the 5th item of namespace migration list?

@gemantzu
Copy link

gemantzu commented Jun 17, 2017

What has changed in mix.exs#application function? Should we still add a dependency application on the extra_applications list? I just saw that this is an elixir 1.4 change, not a phoenix change.

@leikind
Copy link

leikind commented Jul 4, 2017

Going through the upgrade process I had to upgrade the version of Brunch from 2.7.4 to 2.10.7.

With 2.7.4 and the default settings of modules/autoRequire in brunch-config.js app.js was registered as module app.js, and then require('js/app'); failed. With 2.10.7 app.js is correctly registered as module js/app.js.

@andrewtimberlake
Copy link

@chrismccord, your instructions here say that config_change/3 should be removed from the application, but that is needed for asset updates when hot reloading (bitwalker/exrm#206) is there another way to trigger Phoenix to update the assets?

@gustf
Copy link

gustf commented Jul 17, 2017

Missing a "to" in subheadline "Update your project the new 1.3 directory structure (optional)"

@GildedHonour
Copy link

@ndarilek

"Change the App in the mod parameter in mix.exs to MyApp.Application." ----> where is this?

@ijunaid8989
Copy link

warning: no configuration found for otp_app :evercam_media and module EvercamMediaWeb.Endpoint

do anyone get a warning like this?

@mertonium
Copy link

nit: seems like the code in Bump your phoenix dep could have the -rc removed from "~> 1.3.0-rc" 😸

@sailxjx
Copy link

sailxjx commented Aug 16, 2017

I find mix phoenix.gen.model should replace by mix phx.gen.schema after upgrade to 1.3, the document don't mention it. Another opinion is that put all models/schemas in the first level of lib/my_app directory is not a good idea.

@corck
Copy link

corck commented Mar 1, 2018

@ijunaid8989, I get the same error, did you solve it?

@igbanam
Copy link

igbanam commented Apr 29, 2018

@corck (cc @ijunaid8989) ensure you renamed the module to YourAppWeb.Endpoint after moving the file to lib/your_app_web/endpoint.ex

This is step 8 under "Move your root level web/ directory..." section.

@mager
Copy link

mager commented Aug 27, 2018

Worked well for me. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment