From 3d5c7c3fadc18c33f33bf11bd20abc81675099b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Thu, 11 Aug 2022 20:13:57 +0200 Subject: [PATCH] Require Elixir 1.14 (#1342) --- .github/scripts/app/bootstrap_mac.sh | 2 +- .github/workflows/deploy.yaml | 19 +++--- .github/workflows/test.yaml | 30 ++++---- Dockerfile | 4 +- .../intellisense/identifier_matcher.ex | 68 +------------------ .../runtime/erl_dist/runtime_server.ex | 3 +- lib/livebook/runtime/evaluator.ex | 14 ++-- lib/livebook/utils.ex | 13 +--- lib/livebook_cli/server.ex | 2 +- mix.exs | 2 +- test/livebook/intellisense_test.exs | 9 +-- test/livebook/runtime/evaluator_test.exs | 14 ++-- 12 files changed, 52 insertions(+), 128 deletions(-) diff --git a/.github/scripts/app/bootstrap_mac.sh b/.github/scripts/app/bootstrap_mac.sh index 8995483c9..bc0f261c4 100755 --- a/.github/scripts/app/bootstrap_mac.sh +++ b/.github/scripts/app/bootstrap_mac.sh @@ -8,7 +8,7 @@ main() { wxwidgets_ref="v3.1.7" otp_repo="wojtekmach/otp" otp_ref="wm-WX_MACOS_NON_GUI_APP" - elixir_vsn="1.13.4" + elixir_vsn="1.14.0-rc.0" target=$(target) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index cf486bf41..5cb209895 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -4,7 +4,10 @@ on: branches: - main tags: - - 'v*.*.*' + - "v*.*.*" +env: + otp: "25.0" + elixir: "1.14.0-rc.0" jobs: assets: outputs: @@ -15,24 +18,24 @@ jobs: - name: Install Erlang & Elixir uses: erlef/setup-beam@v1 with: - otp-version: '24.0' - elixir-version: '1.13.4' + otp-version: ${{ env.otp }} + elixir-version: ${{ env.elixir }} - name: Cache Mix uses: actions/cache@v2 with: path: | deps _build - key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} + key: ${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}-${{ hashFiles('**/mix.lock') }} restore-keys: | - ${{ runner.os }}-mix- + ${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}- # Note: we need to get Phoenix and LV because package.json points to them directly - name: Install mix dependencies run: mix deps.get - name: Install Node uses: actions/setup-node@v2 with: - node-version: '14.x' + node-version: "14.x" - name: Cache npm dependencies uses: actions/cache@v2 with: @@ -59,8 +62,8 @@ jobs: steps: - uses: actions/checkout@v2 with: - # The output is set only if there was a commit, otherwise - # we pass an empty ref and and the default is used + # The output is set only if there was a commit, otherwise + # we pass an empty ref and and the default is used ref: ${{ needs.assets.outputs.sha }} - name: Set up QEMU uses: docker/setup-qemu-action@v1 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f344ff72f..af873aa39 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -4,6 +4,9 @@ on: push: branches: - main +env: + otp: "25.0" + elixir: "1.14.0-rc.0" jobs: main: runs-on: ubuntu-latest @@ -14,21 +17,22 @@ jobs: - name: Install Erlang & Elixir uses: erlef/setup-beam@v1 with: - otp-version: '24.0' - elixir-version: '1.13.4' + otp-version: ${{ env.otp }} + elixir-version: ${{ env.elixir }} - name: Cache Mix uses: actions/cache@v2 with: path: | deps _build - key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} + key: ${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}-${{ hashFiles('**/mix.lock') }} restore-keys: | - ${{ runner.os }}-mix- + ${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}- - name: Install mix dependencies run: mix deps.get - name: Check formatting - run: mix format --check-formatted + # TODO: remove deps.compile on Elixir > 1.14.0-rc.0 + run: mix do deps.compile, format --check-formatted - name: Check warnings run: mix compile --warnings-as-errors - name: Run tests @@ -36,7 +40,7 @@ jobs: - name: Install Node uses: actions/setup-node@v2 with: - node-version: '14.x' + node-version: "14.x" - name: Cache npm dependencies uses: actions/cache@v2 with: @@ -63,11 +67,11 @@ jobs: uses: erlef/setup-beam@v1 with: version-type: strict - otp-version: '24.2' - elixir-version: '1.13.4' + otp-version: ${{ env.otp }} + elixir-version: ${{ env.elixir }} - name: Start epmd run: cmd /c "START /b epmd" - working-directory: ${{ env.INSTALL_DIR_FOR_OTP }}/erts-12.2/bin + working-directory: ${{ env.INSTALL_DIR_FOR_OTP }}/erts-13.0/bin # Add tar that supports symlinks, see https://github.com/actions/virtual-environments/issues/4679 - name: Add tar.exe run: | @@ -78,15 +82,11 @@ jobs: path: | deps _build - key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} + key: ${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}-${{ hashFiles('**/mix.lock') }} restore-keys: | - ${{ runner.os }}-mix- + ${{ runner.os }}-mix-${{ env.elixir }}-${{ env.otp }}- - name: Install mix dependencies run: mix deps.get - - name: Check formatting - run: mix format --check-formatted - - name: Check warnings - run: mix compile --warnings-as-errors - name: Run tests run: mix test - name: Build the app diff --git a/Dockerfile b/Dockerfile index d02241a5a..f69516b74 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # Stage 1 # Builds the Livebook release -FROM hexpm/elixir:1.13.4-erlang-24.3.4.2-debian-bullseye-20210902-slim AS build +FROM hexpm/elixir:1.14.0-rc.0-erlang-24.3.4.2-debian-bullseye-20210902-slim AS build RUN apt-get update && apt-get upgrade -y && \ apt-get install --no-install-recommends -y \ @@ -38,7 +38,7 @@ RUN mix do compile, release livebook # We use the same base image, because we need Erlang, Elixir and Mix # during runtime to spawn the Livebook standalone runtimes. # Consequently the release doesn't include ERTS as we have it anyway. -FROM hexpm/elixir:1.13.4-erlang-24.3.4.2-debian-bullseye-20210902-slim +FROM hexpm/elixir:1.14.0-rc.0-erlang-24.3.4.2-debian-bullseye-20210902-slim RUN apt-get update && apt-get upgrade -y && \ apt-get install --no-install-recommends -y \ diff --git a/lib/livebook/intellisense/identifier_matcher.ex b/lib/livebook/intellisense/identifier_matcher.ex index 6b923a17f..104d3113b 100644 --- a/lib/livebook/intellisense/identifier_matcher.ex +++ b/lib/livebook/intellisense/identifier_matcher.ex @@ -480,7 +480,7 @@ defmodule Livebook.Intellisense.IdentifierMatcher do defp valid_alias_rest?(rest), do: valid_alias_piece?(rest) defp usable_as_unquoted_module?(mod) do - macro_classify_atom(mod) in [:identifier, :unquoted] + Macro.classify_atom(mod) in [:identifier, :unquoted] end defp get_matching_modules(hint, ctx) do @@ -641,70 +641,4 @@ defmodule Livebook.Intellisense.IdentifierMatcher do documentation: {"text/markdown", info.doc} } end - - # --- - - # TODO: use Macro.classify_atom/1 on Elixir 1.14 - - def macro_classify_atom(atom) do - case macro_inner_classify(atom) do - :alias -> :alias - :identifier -> :identifier - type when type in [:unquoted_operator, :not_callable] -> :unquoted - _ -> :quoted - end - end - - defp macro_inner_classify(atom) when is_atom(atom) do - cond do - atom in [:%, :%{}, :{}, :<<>>, :..., :.., :., :"..//", :->] -> - :not_callable - - atom in [:"::"] -> - :quoted_operator - - Macro.operator?(atom, 1) or Macro.operator?(atom, 2) -> - :unquoted_operator - - true -> - charlist = Atom.to_charlist(atom) - - if macro_valid_alias?(charlist) do - :alias - else - case :elixir_config.identifier_tokenizer().tokenize(charlist) do - {kind, _acc, [], _, _, special} -> - if kind == :identifier and not :lists.member(?@, special) do - :identifier - else - :not_callable - end - - _ -> - :other - end - end - end - end - - defp macro_valid_alias?('Elixir' ++ rest), do: macro_valid_alias_piece?(rest) - defp macro_valid_alias?(_other), do: false - - defp macro_valid_alias_piece?([?., char | rest]) when char >= ?A and char <= ?Z, - do: macro_valid_alias_piece?(macro_trim_leading_while_valid_identifier(rest)) - - defp macro_valid_alias_piece?([]), do: true - defp macro_valid_alias_piece?(_other), do: false - - defp macro_trim_leading_while_valid_identifier([char | rest]) - when char >= ?a and char <= ?z - when char >= ?A and char <= ?Z - when char >= ?0 and char <= ?9 - when char == ?_ do - macro_trim_leading_while_valid_identifier(rest) - end - - defp macro_trim_leading_while_valid_identifier(other) do - other - end end diff --git a/lib/livebook/runtime/erl_dist/runtime_server.ex b/lib/livebook/runtime/erl_dist/runtime_server.ex index f6ff8aa36..ea815ee43 100644 --- a/lib/livebook/runtime/erl_dist/runtime_server.ex +++ b/lib/livebook/runtime/erl_dist/runtime_server.ex @@ -566,8 +566,7 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do {:ok, pid} = Task.Supervisor.start_child(state.task_supervisor, fn -> binding = [] - # TODO: Use Code.env_for_eval and eval_quoted_with_env on Elixir v1.14+ - env = :elixir.env_for_eval([]) + env = Code.env_for_eval([]) scan_and_ack.(binding, env) end) diff --git a/lib/livebook/runtime/evaluator.ex b/lib/livebook/runtime/evaluator.ex index 14c20b3f8..bee363dd8 100644 --- a/lib/livebook/runtime/evaluator.ex +++ b/lib/livebook/runtime/evaluator.ex @@ -182,8 +182,7 @@ defmodule Livebook.Runtime.Evaluator do """ @spec intellisense_context() :: Livebook.Intellisense.intellisense_context() def intellisense_context() do - # TODO: Use Code.env_for_eval and eval_quoted_with_env on Elixir v1.14+ - env = :elixir.env_for_eval([]) + env = Code.env_for_eval([]) map_binding = fn fun -> fun.([]) end %{env: env, map_binding: map_binding} end @@ -304,8 +303,7 @@ defmodule Livebook.Runtime.Evaluator do end defp initial_context() do - # TODO: Use Code.env_for_eval and eval_quoted_with_env on Elixir v1.14+ - env = :elixir.env_for_eval([]) + env = Code.env_for_eval([]) %{binding: [], env: env, id: random_id()} end @@ -429,11 +427,7 @@ defmodule Livebook.Runtime.Evaluator do defp eval(code, binding, env) do try do quoted = Code.string_to_quoted!(code, file: env.file) - # TODO: Use Code.eval_quoted_with_env/3 on Elixir v1.14 - {value, binding, env} = :elixir.eval_quoted(quoted, binding, env) - # TODO: Remove this line on Elixir v1.14 as binding propagates to env correctly - {_, binding, env} = :elixir.eval_forms(:ok, binding, env) - + {value, binding, env} = Code.eval_quoted_with_env(quoted, binding, env) {:ok, value, binding, env} catch kind, error -> @@ -475,7 +469,7 @@ defmodule Livebook.Runtime.Evaluator do # Adapted from https://github.com/elixir-lang/elixir/blob/1c1654c88adfdbef38ff07fc30f6fbd34a542c07/lib/iex/lib/iex/evaluator.ex#L355-L372 # TODO: Remove else branch once we depend on the versions below - if System.otp_release() >= "25" and Version.match?(System.version(), "~> 1.14-dev") do + if System.otp_release() >= "25" do defp prune_stacktrace(stack) do stack |> Enum.reverse() diff --git a/lib/livebook/utils.ex b/lib/livebook/utils.ex index af1e46fef..23b51f7dd 100644 --- a/lib/livebook/utils.ex +++ b/lib/livebook/utils.ex @@ -466,7 +466,7 @@ defmodule Livebook.Utils do base_url |> URI.parse() |> Map.replace!(:path, "/import") - |> append_query("url=#{URI.encode_www_form(url)}") + |> URI.append_query("url=#{URI.encode_www_form(url)}") |> URI.to_string() end @@ -484,19 +484,10 @@ defmodule Livebook.Utils do base_url |> URI.parse() |> Map.replace!(:path, "/open") - |> append_query("path=#{URI.encode_www_form(path)}") + |> URI.append_query("path=#{URI.encode_www_form(path)}") |> URI.to_string() end - # TODO: On Elixir v1.14, use URI.append_query/2 - def append_query(%URI{query: query} = uri, query_to_add) when query in [nil, ""] do - %{uri | query: query_to_add} - end - - def append_query(%URI{} = uri, query) do - %{uri | query: uri.query <> "&" <> query} - end - @doc """ Formats the given number of bytes into a human-friendly text. diff --git a/lib/livebook_cli/server.ex b/lib/livebook_cli/server.ex index e725a9916..4cf0ac1a8 100644 --- a/lib/livebook_cli/server.ex +++ b/lib/livebook_cli/server.ex @@ -290,7 +290,7 @@ defmodule LivebookCLI.Server do defp update_query(url, params) do url |> URI.parse() - |> Livebook.Utils.append_query(URI.encode_query(params)) + |> URI.append_query(URI.encode_query(params)) |> URI.to_string() end diff --git a/mix.exs b/mix.exs index 8b9622e95..797adb173 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Livebook.MixProject do use Mix.Project - @elixir_requirement "~> 1.13" + @elixir_requirement "~> 1.14.0-rc.0" @version "0.6.3" @description "Interactive and collaborative code notebooks - made with Phoenix LiveView" diff --git a/test/livebook/intellisense_test.exs b/test/livebook/intellisense_test.exs index 497557d24..2aae893b5 100644 --- a/test/livebook/intellisense_test.exs +++ b/test/livebook/intellisense_test.exs @@ -9,11 +9,8 @@ defmodule Livebook.IntellisenseTest do quote do block = unquote(Macro.escape(block)) binding = [] - # TODO: Use Code.env_for_eval and eval_quoted_with_env on Elixir v1.14+ - env = :elixir.env_for_eval([]) - {_, binding, env} = :elixir.eval_quoted(block, binding, env) - # TODO: Remove this line on Elixir v1.14 as binding propagates to env correctly - {_, binding, env} = :elixir.eval_forms(:ok, binding, env) + env = Code.env_for_eval([]) + {value, binding, env} = Code.eval_quoted_with_env(block, binding, env) %{ env: env, @@ -162,7 +159,7 @@ defmodule Livebook.IntellisenseTest do label: "Enum", kind: :module, detail: "module", - documentation: "Provides a set of algorithms to work with enumerables.", + documentation: "Functions for working with collections (known as enumerables).", insert_text: "Enum" }, %{ diff --git a/test/livebook/runtime/evaluator_test.exs b/test/livebook/runtime/evaluator_test.exs index 4463452d0..689e5ac42 100644 --- a/test/livebook/runtime/evaluator_test.exs +++ b/test/livebook/runtime/evaluator_test.exs @@ -97,8 +97,11 @@ defmodule Livebook.Runtime.EvaluatorTest do Evaluator.evaluate_code(evaluator, code, :code_1, nil, file: "file.ex") assert_receive {:runtime_evaluation_response, :code_1, - {:error, :error, :function_clause, [{List, :first, _arity, _location}]}, - metadata()} + {:error, :error, :function_clause, + [ + {List, :first, _arity, _location1}, + {:elixir_eval, :__FILE__, 1, _location2} + ]}, metadata()} end test "returns additional metadata when there is a syntax error", %{evaluator: evaluator} do @@ -138,7 +141,9 @@ defmodule Livebook.Runtime.EvaluatorTest do Evaluator.evaluate_code(evaluator, code, :code_1, nil, file: "file.ex") - expected_stacktrace = [{Code, :validated_eval_string, 3, [file: 'lib/code.ex', line: 404]}] + expected_stacktrace = [ + {:elixir_eval, :__FILE__, 1, [file: 'file.ex', line: 1]} + ] assert_receive {:runtime_evaluation_response, :code_1, {:error, :error, %CompileError{}, ^expected_stacktrace}, %{code_error: nil}} @@ -169,7 +174,8 @@ defmodule Livebook.Runtime.EvaluatorTest do expected_stacktrace = [ {Livebook.EvaluatorTest.Stacktrace.Math, :bad_math, 0, [file: 'nofile', line: 3]}, - {Livebook.EvaluatorTest.Stacktrace.Cat, :meow, 0, [file: 'nofile', line: 10]} + {Livebook.EvaluatorTest.Stacktrace.Cat, :meow, 0, [file: 'nofile', line: 10]}, + {:elixir_eval, :__FILE__, 1, [file: 'nofile', line: 15]} ] # Note: evaluating module definitions is relatively slow, so we use a higher wait timeout.