diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 672cfca..75c4a15 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,6 @@ env: jobs: test: runs-on: ubuntu-latest - container: ${{ matrix.container }} env: MIX_ENV: "test" ELIXIR_VERSION: ${{ matrix.elixir }} @@ -19,55 +18,55 @@ jobs: fail-fast: false matrix: include: - # Modern toolchain — default lockfile (hackney 4.x dependency set) + # Default lockfile (hackney 4.x dependency set), covering each + # supported Elixir (1.17+) and OTP (27+) version at least once + - elixir: "1.20.0" + otp: "29.0" + lockfile: "" + lockfile_path: "mix.lock" - elixir: "1.19.5" otp: "28.5" lockfile: "" lockfile_path: "mix.lock" - # Legacy toolchains — test/lockfiles/legacy.lock (hackney 1.x set). - # erlef/setup-beam can no longer install OTP < 24.2 on current - # GitHub-hosted runners, so these run in hexpm/elixir containers. - - elixir: "1.12.3" - otp: "24.3.4.9" - container: "hexpm/elixir:1.12.3-erlang-24.3.4.9-ubuntu-noble-20260509.1" - lockfile: "legacy" - lockfile_path: "test/lockfiles/legacy.lock" - - elixir: "1.11.4" - otp: "24.3.4.9" - container: "hexpm/elixir:1.11.4-erlang-24.3.4.9-ubuntu-noble-20260509.1" - lockfile: "legacy" - lockfile_path: "test/lockfiles/legacy.lock" - - elixir: "1.10.4" - otp: "23.3.4" # match discovered tag - container: "hexpm/elixir:1.10.4-erlang-23.3.4.16-debian-bullseye-20260518" + # Style checks (format/credo/unused deps) run on the toolchain + # pinned in .tool-versions, so CI matches local development + lint: true + - elixir: "1.18.4" + otp: "27.3" + lockfile: "" + lockfile_path: "mix.lock" + - elixir: "1.17.3" + otp: "27.3" + lockfile: "" + lockfile_path: "mix.lock" + # Legacy lockfile — test/lockfiles/legacy.lock (hackney 1.x set) + - elixir: "1.19.5" + otp: "28.5" lockfile: "legacy" lockfile_path: "test/lockfiles/legacy.lock" - - elixir: "1.9.4" - otp: "22.3.4.9" - container: "hexpm/elixir:1.9.4-erlang-22.3.4.9-ubuntu-impish-20211102" + - elixir: "1.18.4" + otp: "27.3" lockfile: "legacy" lockfile_path: "test/lockfiles/legacy.lock" - - elixir: "1.8.2" - otp: "22.3.4.9" - container: "hexpm/elixir:1.8.2-erlang-22.3.4.9-ubuntu-impish-20211102" + - elixir: "1.17.3" + otp: "27.3" lockfile: "legacy" lockfile_path: "test/lockfiles/legacy.lock" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: erlef/setup-beam@v1 - if: ${{ !matrix.container }} with: otp-version: ${{ matrix.otp }} elixir-version: ${{ matrix.elixir }} - name: Install hex and rebar run: mix local.hex --force && mix local.rebar --force - name: Cache deps - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: deps key: ${{ env.CACHE_VERSION }}-deps-build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles(matrix.lockfile_path) }} - name: Cache _build - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: _build/${{ env.MIX_ENV }} key: ${{ env.CACHE_VERSION }}-_build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles(matrix.lockfile_path) }} @@ -80,15 +79,15 @@ jobs: shell: bash run: bash <(curl -s https://codecov.io/bash) -F UnitTest -e ELIXIR_VERSION,OTP_VERSION || echo "Codecov did not collect coverage reports" - run: mix format --check-formatted - if: matrix.lockfile != 'legacy' + if: matrix.lint - run: mix credo --strict + if: matrix.lint - name: Check for unused dependencies - if: matrix.lockfile != 'legacy' + if: matrix.lint run: mix deps.unlock --check-unused dialyzer: runs-on: ubuntu-latest - container: ${{ matrix.container }} env: MIX_ENV: "dev" LOCKFILE: ${{ matrix.lockfile }} @@ -96,49 +95,47 @@ jobs: fail-fast: false matrix: include: + - elixir: "1.20.0" + otp: "29.0" + lockfile: "" + lockfile_path: "mix.lock" - elixir: "1.19.5" otp: "28.5" lockfile: "" lockfile_path: "mix.lock" - - elixir: "1.12.3" - otp: "24.3.4.9" - container: "hexpm/elixir:1.12.3-erlang-24.3.4.9-ubuntu-noble-20260509.1" - lockfile: "legacy" - lockfile_path: "test/lockfiles/legacy.lock" - - elixir: "1.11.4" - otp: "24.3.4.9" - container: "hexpm/elixir:1.11.4-erlang-24.3.4.9-ubuntu-noble-20260509.1" - lockfile: "legacy" - lockfile_path: "test/lockfiles/legacy.lock" - - elixir: "1.10.4" - otp: "23.3.4" # match discovered tag - container: "hexpm/elixir:1.10.4-erlang-23.3.4.16-debian-bullseye-20260518" + - elixir: "1.18.4" + otp: "27.3" + lockfile: "" + lockfile_path: "mix.lock" + - elixir: "1.17.3" + otp: "27.3" + lockfile: "" + lockfile_path: "mix.lock" + - elixir: "1.19.5" + otp: "28.5" lockfile: "legacy" lockfile_path: "test/lockfiles/legacy.lock" - - elixir: "1.9.4" - otp: "22.3.4.9" - container: "hexpm/elixir:1.9.4-erlang-22.3.4.9-ubuntu-impish-20211102" + - elixir: "1.18.4" + otp: "27.3" lockfile: "legacy" lockfile_path: "test/lockfiles/legacy.lock" - - elixir: "1.8.2" - otp: "22.3.4.9" - container: "hexpm/elixir:1.8.2-erlang-22.3.4.9-ubuntu-impish-20211102" + - elixir: "1.17.3" + otp: "27.3" lockfile: "legacy" lockfile_path: "test/lockfiles/legacy.lock" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: erlef/setup-beam@v1 - if: ${{ !matrix.container }} with: otp-version: ${{ matrix.otp }} elixir-version: ${{ matrix.elixir }} - name: Install hex and rebar run: mix local.hex --force && mix local.rebar --force - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: deps key: ${{ env.CACHE_VERSION }}-deps-build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles(matrix.lockfile_path) }} - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: _build/${{ env.MIX_ENV }} key: ${{ env.CACHE_VERSION }}-_build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-dialyzer-${{ hashFiles(matrix.lockfile_path) }} @@ -159,26 +156,21 @@ jobs: MIX_ENV: "test" ELIXIR_VERSION: ${{ matrix.elixir }} OTP_VERSION: ${{ matrix.otp }} + LOCKFILE: ${{ matrix.lockfile }} strategy: fail-fast: false matrix: - elixir: - - "1.12.3" - - "1.11.4" - - "1.10.4" - - "1.9.4" - - "1.8.2" include: - - elixir: "1.12.3" - otp: "24.0.6" - - elixir: "1.11.4" - otp: "24.0.6" - - elixir: "1.10.4" - otp: "23.3.4.7" - - elixir: "1.9.4" - otp: "22.3.4.21" - - elixir: "1.8.2" - otp: "22.3.4.21" + # One run per supported hackney major. Toolchain coverage is + # handled by the test and dialyzer jobs. + - elixir: "1.19.5" + otp: "28.5" + lockfile: "" + lockfile_path: "mix.lock" + - elixir: "1.19.5" + otp: "28.5" + lockfile: "legacy" + lockfile_path: "test/lockfiles/legacy.lock" services: # Allow accessing localhost ports from docker containers. # See https://github.com/qoomon/docker-host @@ -214,40 +206,55 @@ jobs: --shm-size=2g steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v6 - uses: erlef/setup-beam@v1 with: otp-version: ${{ matrix.otp }} elixir-version: ${{ matrix.elixir }} - run: mkdir -p logs - name: Run PhantomJS - run: phantomjs --wd --webdriver-logfile=logs/phantomjs.log --webdriver-loglevel=DEBUG --debug=true &> logs/phantomjs-output.log & + # PhantomJS is no longer preinstalled on hosted runners (and the + # prebuilt docker images use the deprecated manifest schema 1, which + # Docker refuses to pull), so download the official binary. + # OPENSSL_CONF works around its incompatibility with modern OpenSSL + # config files. + run: | + curl -sSL -o /tmp/phantomjs.tar.bz2 https://github.com/Medium/phantomjs/releases/download/v2.1.1/phantomjs-2.1.1-linux-x86_64.tar.bz2 + tar -xjf /tmp/phantomjs.tar.bz2 -C /tmp + OPENSSL_CONF=/dev/null /tmp/phantomjs-2.1.1-linux-x86_64/bin/phantomjs --wd --webdriver-logfile=logs/phantomjs.log --webdriver-loglevel=DEBUG --debug=true &> logs/phantomjs-output.log & - name: Run chromedriver - run: chromedriver --log-path=logs/chromedriver.log --log-level=DEBUG & - - uses: actions/cache@v4 + # Modern chromedriver picks a random port unless --port is given + run: chromedriver --port=9515 --log-path=logs/chromedriver.log --log-level=DEBUG & + - uses: actions/cache@v5 with: path: deps - key: ${{ env.CACHE_VERSION }}-deps-build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('mix.lock') }} - - uses: actions/cache@v4 + key: ${{ env.CACHE_VERSION }}-deps-build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles(matrix.lockfile_path) }} + - uses: actions/cache@v5 with: path: _build/${{ env.MIX_ENV }} - key: ${{ env.CACHE_VERSION }}-_build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-integration-tests + key: ${{ env.CACHE_VERSION }}-_build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-integration-tests-${{ hashFiles(matrix.lockfile_path) }} restore-keys: | - ${{ env.CACHE_VERSION }}-_build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}- + ${{ env.CACHE_VERSION }}-_build-${{ env.MIX_ENV }}-${{ matrix.otp }}-${{ matrix.elixir }}-integration-tests- - name: Install Dependencies run: mix deps.get - name: Compile app + id: compile run: mix compile --force --warnings-as-errors + # Run every driver scenario even when an earlier one fails, so a single + # broken driver doesn't hide the others' results - run: mix coveralls.json --only integration_test_driver:chromedriver + if: ${{ !cancelled() && steps.compile.outcome == 'success' }} - name: Upload results to CodeCov run: bash <(curl -s https://codecov.io/bash) -F IntegrationTest -F Chromedriver -e ELIXIR_VERSION,OTP_VERSION || echo "Codecov did not collect coverage reports" - run: mix coveralls.json --only integration_test_driver:phantomjs + if: ${{ !cancelled() && steps.compile.outcome == 'success' }} - name: Upload results to CodeCov run: bash <(curl -s https://codecov.io/bash) -F IntegrationTest -F Phantomjs -e ELIXIR_VERSION,OTP_VERSION || echo "Codecov did not collect coverage reports" - run: mix coveralls.json --only integration_test_driver_browser:selenium_3-chrome + if: ${{ !cancelled() && steps.compile.outcome == 'success' }} env: TEST_SERVER_HOSTNAME: dockerhost WEBDRIVER_BASE_URL: http://localhost:4445/wd/hub @@ -255,6 +262,7 @@ jobs: run: bash <(curl -s https://codecov.io/bash) -F IntegrationTest -F Selenium3Chrome -e ELIXIR_VERSION,OTP_VERSION || echo "Codecov did not collect coverage reports" - run: mix coveralls.json --only integration_test_driver_browser:selenium_3-firefox + if: ${{ !cancelled() && steps.compile.outcome == 'success' }} env: TEST_SERVER_HOSTNAME: dockerhost WEBDRIVER_BASE_URL: http://localhost:4446/wd/hub @@ -262,6 +270,7 @@ jobs: run: bash <(curl -s https://codecov.io/bash) -F IntegrationTest -F Selenium3Firefox -e ELIXIR_VERSION,OTP_VERSION || echo "Codecov did not collect coverage reports" - run: mix coveralls.json --only integration_test_driver_browser:selenium_2-chrome + if: ${{ !cancelled() && steps.compile.outcome == 'success' }} env: TEST_SERVER_HOSTNAME: dockerhost WEBDRIVER_BASE_URL: http://localhost:4447/wd/hub @@ -269,6 +278,7 @@ jobs: run: bash <(curl -s https://codecov.io/bash) -F IntegrationTest -F Selenium2Chrome -e ELIXIR_VERSION,OTP_VERSION || echo "Codecov did not collect coverage reports" - run: mix coveralls.json --only integration_test_driver_browser:selenium_2-firefox + if: ${{ !cancelled() && steps.compile.outcome == 'success' }} env: TEST_SERVER_HOSTNAME: dockerhost WEBDRIVER_BASE_URL: http://localhost:4448/wd/hub @@ -278,5 +288,5 @@ jobs: - uses: actions/upload-artifact@v7 if: always() with: - name: Integration test server logs (Elixir ${{matrix.elixir}}) + name: Integration test server logs (Elixir ${{ matrix.elixir }}, ${{ matrix.lockfile || 'default' }} lockfile) path: logs/ diff --git a/mix.exs b/mix.exs index 1098f57..fd47c3e 100644 --- a/mix.exs +++ b/mix.exs @@ -7,7 +7,7 @@ defmodule WebDriverClient.MixProject do [ app: :web_driver_client, version: @version, - elixir: "~> 1.8", + elixir: "~> 1.17", elixirc_paths: elixirc_paths(Mix.env()), test_coverage: [tool: ExCoveralls], preferred_cli_env: [ @@ -56,7 +56,9 @@ defmodule WebDriverClient.MixProject do defp elixirc_paths(_), do: ["lib"] # Allows testing against alternate dependency sets, e.g. - # `LOCKFILE=legacy mix deps.get` for hackney 1.x on older Elixir/OTP. + # `LOCKFILE=legacy mix deps.get` for the hackney 1.x dependency set. + # legacy.lock pins hackney 1.23.0: hackney 1.24+ discards redirect + # response bodies read via `:hackney.body/1`, which Tesla relies on. defp lockfile(nil), do: "mix.lock" defp lockfile(""), do: "mix.lock" defp lockfile(lockfile), do: "test/lockfiles/#{lockfile}.lock" diff --git a/test/integration/status_test.exs b/test/integration/status_test.exs index ab169f7..9766023 100644 --- a/test/integration/status_test.exs +++ b/test/integration/status_test.exs @@ -23,7 +23,8 @@ defmodule WebDriverClient.Integration.StatusTest do assert {:error, %ConnectionError{}} = config - |> struct!(base_url: "http://does-not-exist-123") + # Trailing dot prevents DNS search domain resolution + |> struct!(base_url: "http://does-not-exist-123.") |> WebDriverClient.fetch_server_status() end end diff --git a/test/lockfiles/legacy.lock b/test/lockfiles/legacy.lock index ead0c3f..5f576cd 100644 --- a/test/lockfiles/legacy.lock +++ b/test/lockfiles/legacy.lock @@ -1,33 +1,35 @@ %{ - "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, - "bypass": {:hex, :bypass, "1.0.0", "b78b3dcb832a71aca5259c1a704b2e14b55fd4e1327ff942598b4e7d1a7ad83d", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}], "hexpm", "5a1dc855dfcc86160458c7a70d25f65d498bd8012bd4c06a8d3baa368dda3c45"}, - "certifi": {:hex, :certifi, "2.8.0", "d4fb0a6bb20b7c9c3643e22507e42f356ac090a1dcea9ab99e27e0376d695eba", [:rebar3], [], "hexpm", "6ac7efc1c6f8600b08d625292d4bbf584e14847ce1b6b5c44d983d273e1097ea"}, - "cowboy": {:hex, :cowboy, "2.6.3", "99aa50e94e685557cad82e704457336a453d4abcb77839ad22dbe71f311fcc06", [:rebar3], [{:cowlib, "~> 2.7.3", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "e5580029080f3f1ad17436fb97b0d5ed2ed4e4815a96bac36b5a992e20f58db6"}, - "cowlib": {:hex, :cowlib, "2.7.3", "a7ffcd0917e6d50b4d5fb28e9e2085a0ceb3c97dea310505f7460ff5ed764ce9", [:rebar3], [], "hexpm", "1e1a3d176d52daebbecbbcdfd27c27726076567905c2a9d7398c54da9d225761"}, - "credo": {:hex, :credo, "1.4.0", "92339d4cbadd1e88b5ee43d427b639b68a11071b6f73854e33638e30a0ea11f5", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1fd3b70dce216574ce3c18bdf510b57e7c4c85c2ec9cad4bff854abaf7e58658"}, - "dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"}, - "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.16", "607709303e1d4e3e02f1444df0c821529af1c03b8578dfc81bb9cf64553d02b9", [:mix], [], "hexpm", "69fcf696168f5a274dd012e3e305027010658b2d1630cef68421d6baaeaccead"}, - "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, - "ex_doc": {:hex, :ex_doc, "0.25.3", "3edf6a0d70a39d2eafde030b8895501b1c93692effcbd21347296c18e47618ce", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "9ebebc2169ec732a38e9e779fd0418c9189b3ca93f4a676c961be6c1527913f5"}, - "excoveralls": {:hex, :excoveralls, "0.14.3", "d17dc249ad32e469afd2bc656b58e810109d4367ec6bd467bed57a84dc4a3e02", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "b5aecdfdcf48e9d5e1c210841589b30981a5e7e66055cb8691a6f90b1601c108"}, - "hackney": {:hex, :hackney, "1.18.0", "c4443d960bb9fba6d01161d01cd81173089686717d9490e5d3606644c48d121f", [:rebar3], [{:certifi, "~>2.8.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "9afcda620704d720db8c6a3123e9848d09c87586dc1c10479c42627b905b5c5e"}, - "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, - "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"}, - "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.15.2", "dc72dfe17eb240552857465cc00cce390960d9a0c055c4ccd38b70629227e97c", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "fd23ae48d09b32eff49d4ced2b43c9f086d402ee4fd4fcb2d7fad97fa8823e75"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, + "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, + "bypass": {:hex, :bypass, "2.1.0", "909782781bf8e20ee86a9cabde36b259d44af8b9f38756173e8f5e2e1fabb9b1", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "d9b5df8fa5b7a6efa08384e9bbecfe4ce61c77d28a4282f79e02f1ef78d96b80"}, + "certifi": {:hex, :certifi, "2.14.0", "ed3bef654e69cde5e6c022df8070a579a79e8ba2368a00acf3d75b82d9aceeed", [:rebar3], [], "hexpm", "ea59d87ef89da429b8e905264fdec3419f84f2215bb3d81e07a18aac919026c3"}, + "cowboy": {:hex, :cowboy, "2.15.0", "9cfe86ed7117bf045e10adbedb0170af7be57f2a3637e7be143433d8dd267396", [:make, :rebar3], [{:cowlib, ">= 2.16.0 and < 3.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, ">= 1.8.0 and < 3.0.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "179fb65140fb440a17b767ad53b755081506f9596c4db5c49c0396d8c8643668"}, + "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, + "cowlib": {:hex, :cowlib, "2.16.1", "318d385d55f657e9a5005838c4e426e13dcd724a691438384b6165a69687e531", [:make, :rebar3], [], "hexpm", "58f1e425a9e04176f1d30e20116f57c4e90ef0e187552e9741c465bdf4044f70"}, + "credo": {:hex, :credo, "1.7.19", "cc52129665fc7c15143d47838fda0f9cd6dac9ceced7bf4da6f85fcbfe64b12a", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "2d8bc95d5a7bb99dd2613621d4f08c6a3575c3fd4b62e6a2b48a100352a557b8"}, + "dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, + "erlex": {:hex, :erlex, "0.2.9", "7debbbaa9f4f368b8cd648983e0f1d7963028508e9c59e9d4ed504e94ef52a55", [:mix], [], "hexpm", "8cfffc0ec7159e6d73de2ab28a588064de80f88b2798d5cbe4482cbbc200178b"}, + "ex_doc": {:hex, :ex_doc, "0.40.3", "4a972ffe64bc07dc605af487e98fc19b72a4185f55ca031b94c0552d6071c1d9", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2756e357742fecd9749b489b85d67c9ce99c465f2e75728d9e6dc8d704b973de"}, + "excoveralls": {:hex, :excoveralls, "0.18.5", "e229d0a65982613332ec30f07940038fe451a2e5b29bce2a5022165f0c9b157e", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "523fe8a15603f86d64852aab2abe8ddbd78e68579c8525ae765facc5eae01562"}, + "file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"}, + "hackney": {:hex, :hackney, "1.23.0", "55cc09077112bcb4a69e54be46ed9bc55537763a96cd4a80a221663a7eafd767", [:rebar3], [{:certifi, "~> 2.14.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "6cd1c04cd15c81e5a493f167b226a15f0938a84fc8f0736ebe4ddcab65c0b44e"}, + "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, + "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, + "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, + "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.1.0", "835f7e60792e08824cda445639555d7bf1bbbddb1b60b306e33cb6f6db24dc74", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "1cd6780fb1dd1a03979abaed0fe82712b0625118fd5257d3ebbf73f960c73c3c"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, - "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"}, - "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"}, - "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, - "plug": {:hex, :plug, "1.8.3", "12d5f9796dc72e8ac9614e94bda5e51c4c028d0d428e9297650d09e15a684478", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "164baaeb382d19beee0ec484492aa82a9c8685770aee33b24ec727a0971b34d0"}, - "plug_cowboy": {:hex, :plug_cowboy, "2.1.0", "b75768153c3a8a9e8039d4b25bb9b14efbc58e9c4a6e6a270abff1cd30cbe320", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "6cd8ddd1bd1fbfa54d3fc61d4719c2057dae67615395d58d40437a919a46f132"}, - "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm", "73c1682f0e414cfb5d9b95c8e8cd6ffcfdae699e3b05e1db744e58b7be857759"}, - "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, - "stream_data": {:hex, :stream_data, "0.5.0", "b27641e58941685c75b353577dc602c9d2c12292dd84babf506c2033cd97893e", [:mix], [], "hexpm", "012bd2eec069ada4db3411f9115ccafa38540a3c78c4c0349f151fc761b9e271"}, - "tesla": {:hex, :tesla, "1.4.3", "f5a494e08fb1abe4fd9c28abb17f3d9b62b8f6fc492860baa91efb1aab61c8a0", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.3", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "e0755bb664bf4d664af72931f320c97adbf89da4586670f4864bf259b5750386"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, + "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, + "mimerl": {:hex, :mimerl, "1.5.0", "f35aca6f23242339b3666e0ac0702379e362b469d0aea167f6cc713547e777ed", [:rebar3], [], "hexpm", "db648ce065bae14ea84ca8b5dd123f42f49417cef693541110bf6f9e9be9ecc4"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, + "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, + "plug": {:hex, :plug, "1.19.2", "e4950525b22c6789dfb38a3f95d47171ba159da3fc5a33be9643b43d5e8adb98", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b6fce20a56af5e60fa5dfecf3f907bb98ec981be43c79a3809a499bc3d133de0"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.8.1", "5aa391a5e8d1ac3192e36a3bcaff12b5fd6ef6c7e29b53a38e63a860783e77d0", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "4c200288673d5bc86a0ab7dc6a2a069176a74e5d573ef62740a1c517458a5f26"}, + "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, + "ranch": {:hex, :ranch, "1.8.1", "208169e65292ac5d333d6cdbad49388c1ae198136e4697ae2f474697140f201c", [:make, :rebar3], [], "hexpm", "aed58910f4e21deea992a67bf51632b6d60114895eb03bb392bb733064594dd0"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, + "stream_data": {:hex, :stream_data, "0.6.0", "e87a9a79d7ec23d10ff83eb025141ef4915eeb09d4491f79e52f2562b73e5f47", [:mix], [], "hexpm", "b92b5031b650ca480ced047578f1d57ea6dd563f5b57464ad274718c9c29501c"}, + "telemetry": {:hex, :telemetry, "1.4.2", "a0cb522801dffb1c49fe6e30561badffc7b6d0e180db1300df759faa22062855", [:rebar3], [], "hexpm", "928f6495066506077862c0d1646609eed891a4326bee3126ba54b60af61febb1"}, + "tesla": {:hex, :tesla, "1.18.3", "e60a5f1077406db35aa4ef96457e46bef9b7fe1fbb7e6ad8e43483773ad86347", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.21 or >= 4.0.2 and < 5.0.0-0", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:mox, "~> 1.0", [hex: :mox, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:opentelemetry_semantic_conventions, "~> 1.27", [hex: :opentelemetry_semantic_conventions, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "77f958568eff4681d29692e7d19e0ef7c6974c1f97be16fdc1011d0889211b3c"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, }