diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..d304ff3 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,3 @@ +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..389ed25 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,53 @@ +name: CI + +on: + pull_request: + push: + branches: ["main"] + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + name: Elixir ${{ matrix.elixir }} / OTP ${{ matrix.otp }} + strategy: + fail-fast: false + matrix: + otp: ["27", "28", "29"] + elixir: ["1.18", "1.19", "1.20"] + exclude: + # Elixir 1.18 supports OTP 25-27 + - elixir: "1.18" + otp: "28" + - elixir: "1.18" + otp: "29" + # Elixir 1.19 supports OTP 26-28 + - elixir: "1.19" + otp: "29" + env: + MIX_ENV: test + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1 + with: + otp-version: ${{ matrix.otp }} + elixir-version: ${{ matrix.elixir }} + + - name: Install dependencies + run: mix deps.get + + - name: Compile (warnings as errors) + run: mix compile --warnings-as-errors + + - name: Check formatting + run: mix format --check-formatted + + - name: Run tests + run: mix test --warnings-as-errors diff --git a/.github/workflows/osv-scanner.yaml b/.github/workflows/osv-scanner.yaml new file mode 100644 index 0000000..c85e396 --- /dev/null +++ b/.github/workflows/osv-scanner.yaml @@ -0,0 +1,42 @@ +# OSV-Scanner — scans dependency manifests against the OSV vulnerability DB. +# https://github.com/google/osv-scanner + +name: OSV-Scanner + +on: + pull_request: + branches: ["master"] + schedule: + - cron: "42 20 * * 3" + push: + branches: ["master"] + +permissions: + contents: read + +jobs: + sca-scan: + permissions: + contents: read + security-events: write # upload SARIF to Security tab + actions: read # required by upload-sarif on private repos + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - name: Run OSV-Scanner + continue-on-error: true + uses: google/osv-scanner-action/osv-scanner-action@9a498708959aeaef5ef730655706c5a1df1edbc2 # v2.3.8 + with: + scan-args: |- + --format=sarif + --output-file=osv-scanner.sarif + -r + ./ + + - name: Upload SARIF + if: ${{ !cancelled() }} + uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + with: + sarif_file: osv-scanner.sarif diff --git a/.gitignore b/.gitignore index 9a6ebb3..b062252 100644 --- a/.gitignore +++ b/.gitignore @@ -23,7 +23,6 @@ erl_crash.dump segment-*.tar .DS_Store -.formatter.exs .vscode/ .elixir_ls/ -.dexter +.dexter/ diff --git a/config/dev.exs b/config/dev.exs new file mode 100644 index 0000000..c4cdf38 --- /dev/null +++ b/config/dev.exs @@ -0,0 +1,7 @@ +import Config + +# In development, don't make real calls to Segment. With `:send_to_http` false, +# the no-op adapter (`Segmentry.Http.Noop`) logs each request at `:debug` and +# replies `200` instead of hitting the network. Flip to `true` to exercise the +# real HTTP path locally. +config :segmentry, send_to_http: false diff --git a/test/segmentry/sender_test.exs b/test/segmentry/sender_test.exs index b89ba9e..dbaa070 100644 --- a/test/segmentry/sender_test.exs +++ b/test/segmentry/sender_test.exs @@ -16,7 +16,11 @@ defmodule Segmentry.Analytics.SenderTest do test "starts and registers under the module name" do parent = self() - adapter = fn req -> send(parent, {:req, req}); {req, %Req.Response{status: 200}} end + + adapter = fn req -> + send(parent, {:req, req}) + {req, %Req.Response{status: 200}} + end {:ok, pid} = Sender.start_link("k", adapter: adapter) assert Process.whereis(Sender) == pid @@ -24,7 +28,11 @@ defmodule Segmentry.Analytics.SenderTest do test "call/1 sends each event immediately and asynchronously" do parent = self() - adapter = fn req -> send(parent, {:req, req}); {req, %Req.Response{status: 200}} end + + adapter = fn req -> + send(parent, {:req, req}) + {req, %Req.Response{status: 200}} + end {:ok, _pid} = Sender.start_link("k", adapter: adapter) @@ -37,7 +45,11 @@ defmodule Segmentry.Analytics.SenderTest do test "single-event call uses /track endpoint, not /batch" do parent = self() - adapter = fn req -> send(parent, {:req, req}); {req, %Req.Response{status: 200}} end + + adapter = fn req -> + send(parent, {:req, req}) + {req, %Req.Response{status: 200}} + end {:ok, _pid} = Sender.start_link("k", adapter: adapter) :ok = Sender.call(%Track{userId: "u", event: "one"}) @@ -48,7 +60,11 @@ defmodule Segmentry.Analytics.SenderTest do test "start_link/1 uses default Req options" do parent = self() - adapter = fn req -> send(parent, {:req, req}); {req, %Req.Response{status: 200}} end + + adapter = fn req -> + send(parent, {:req, req}) + {req, %Req.Response{status: 200}} + end Application.put_env(:segmentry, :req_options, adapter: adapter)