diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..51ebfae --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Review ownership belongs to human maintainers rather than individual or automation accounts. +* @TrogonStack/trogon-eventstore-maintainers diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml new file mode 100644 index 0000000..3da9a49 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml @@ -0,0 +1,58 @@ +name: Bug report +description: Help us improve the Elixir client +labels: + - triage +body: + - type: textarea + id: current-behavior + attributes: + label: Current behavior + description: Describe the behavior you observed. + validations: + required: true + - type: textarea + id: reproduction-steps + attributes: + label: Steps to reproduce + description: Provide the smallest reliable reproduction. + placeholder: | + 1. Configure the client with ... + 2. Call ... + 3. Observe ... + validations: + required: true + - type: textarea + id: expected-behavior + attributes: + label: Expected behavior + description: Describe what you expected to happen. + validations: + required: true + - type: input + id: client-version + attributes: + label: Client version + placeholder: e.g. v0.1.0 + validations: + required: true + - type: input + id: server-version + attributes: + label: TrogonEventStore version + placeholder: e.g. v0.1.0 + validations: + required: true + - type: input + id: elixir-version + attributes: + label: Elixir and OTP versions + placeholder: e.g. Elixir 1.16.2, OTP 26.2 + validations: + required: true + - type: textarea + id: environment + attributes: + label: Environment + description: Include the operating system and deployment details without credentials or other secrets. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..23c3a7e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Feature request + url: https://github.com/TrogonStack/TrogonEventStore-Client-Elixir/discussions/new?category=ideas + about: Request a feature for the Elixir client + - name: Ask a question + url: https://github.com/TrogonStack/TrogonEventStore-Client-Elixir/discussions/new?category=q-a + about: Ask questions and discuss the client with the community diff --git a/.github/release-please-config.json b/.github/release-please-config.json new file mode 100644 index 0000000..fef5636 --- /dev/null +++ b/.github/release-please-config.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "bootstrap-sha": "2f5476ae73b35e5a2192eda88aefcf71198a7bfe", + "signoff": "SHT Bot <61149376+sht-bot@users.noreply.github.com>", + "packages": { + ".": { + "release-type": "simple", + "package-name": "TrogonEventStore-Client-Elixir", + "component": "trogon-eventstore-elixir", + "include-component-in-tag": true, + "include-v-in-tag": true, + "tag-separator": "@", + "pull-request-title-pattern": "chore${scope}: release${component} ${version}", + "bump-minor-pre-major": false, + "bump-patch-for-minor-pre-major": false, + "extra-files": [ + { + "type": "generic", + "path": ".version" + } + ] + } + } +} diff --git a/.github/release-please-manifest.json b/.github/release-please-manifest.json new file mode 100644 index 0000000..e18ee07 --- /dev/null +++ b/.github/release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.0.0" +} diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..08c75e7 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,23 @@ +changelog: + exclude: + labels: + - ignore-for-release + categories: + - title: Added + labels: + - enhancement + - title: Fixed + labels: + - bug + - title: Changed + labels: + - "*" + - title: Deprecated + labels: + - deprecated + - title: Breaking Changes + labels: + - breaking + - title: Documentation + labels: + - documentation diff --git a/.github/settings.yml b/.github/settings.yml deleted file mode 100644 index 8186d02..0000000 --- a/.github/settings.yml +++ /dev/null @@ -1,106 +0,0 @@ -repository: - name: spear - description: | - A sharp EventStore 20+ client backed by mint :yum: - private: false - has_issues: true - has_wiki: false - has_downloads: true - default_branch: main - allow_squash_merge: true - allow_merge_commit: true - allow_rebase_merge: true - -teams: - - name: devs - permission: admin - -collaborators: - - username: cuatrobuild - permission: push - -labels: - - name: Good Example - color: 1d76db - - name: p:Critical - color: e11d21 - - name: p:High - color: eb6420 - - name: p:Low - color: 009800 - - name: p:Medium - color: fbca04 - - name: s:Abandoned - color: '000000' - - name: s:Accepted - color: 009800 - - name: s:Accept? - color: ededed - - name: s:Available - color: bfe5bf - - name: s:Blocked - color: e11d21 - - name: s:Completed - color: 006b75 - - name: s:In Progress - color: bfd4f2 - - name: s:On Hold - color: d4c5f9 - - name: s:Pending - color: fef2c0 - - name: s:Review Needed - color: fbca04 - - name: s:Revision Needed - color: e99695 - - name: t:Bug - color: e11d21 - - name: t:Enhancement - color: 84b6eb - - name: t:Feature - color: c2e0c6 - - name: t:Maintenance - color: fbca04 - - name: t:Question - color: cc317c - - name: t:Redesign - color: d93f0b - - name: t:Refactor - color: 5319e7 - - name: t:Test - color: d4c5f9 - - name: t:Tech Debt - color: fef2c0 - - name: t:Beer Money - color: cc8e47 - - name: dfm - color: dbb127 - - name: baemax - color: fef2c0 - - name: team-money - color: 26b5a9 - - name: team-food - color: e2d14f - - name: team-yer-welcome - color: fc9c8f - - name: corporal-llama - color: f99ab1 - - name: platform - color: 7010b5 - - name: pinned - color: 0e8a16 - - name: stale - color: ffffff - -branches: - - name: main - protection: - required_pull_request_reviews: - required_approving_review_count: 1 - dismiss_stale_reviews: false - require_code_owner_reviews: false - dismissal_restrictions: {} - required_status_checks: - strict: true - contexts: ["Bless"] - enforce_admins: false - restrictions: null diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1ca7a2..25e4f1a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,234 +1,104 @@ name: CI on: - push: pull_request: + push: + branches: + - main + +permissions: + contents: read + +env: + ELIXIR_VERSION: 1.16.2 + OTP_VERSION: 26.2 jobs: - bless: - name: Bless - runs-on: ubuntu-22.04 + test: + name: Build and test + runs-on: ubuntu-latest + timeout-minutes: 15 env: - MIX_ENV: test - EVENTSTORE_HOST: localhost EVENTSTORE_VERSION: 23.10.8 - + MIX_ENV: test steps: - - name: Checkout - uses: actions/checkout@v7 - - - name: Generate EventStoreDB certificates - run: ./eventstoredb/gen-certs.sh - - - name: Spawn docker compose EventStoreDB container - run: docker compose up --detach eventstore - - - name: Determine the elixir version - run: echo "ELIXIR_VERSION=$(grep -h elixir .tool-versions | awk '{ print $2 }' | awk -F - '{print $1}')" >> $GITHUB_ENV - - - name: Determine the otp version - run: echo "OTP_VERSION=$(grep -h erlang .tool-versions | awk '{ print $2 }')" >> $GITHUB_ENV - - - name: Setup Elixir and Erlang versions - uses: erlef/setup-beam@v1 - with: - otp-version: ${{ env.OTP_VERSION }} - elixir-version: ${{ env.ELIXIR_VERSION }} - - - name: Restore the deps cache - uses: actions/cache@v6 - id: deps-cache - with: - path: deps - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }} - restore-keys: | - ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps- - - - name: Restore the _build cache - uses: actions/cache@v6 - id: build-cache - with: - path: _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-build-mixlockhash-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }} - restore-keys: | - ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-build- - - - name: Fetch mix dependencies - if: steps.deps-cache.outputs.cache-hit != 'true' - run: | - mix local.rebar --force - mix local.hex --force - mix deps.get - - - name: Compile dependencies - if: steps.deps-cache.outputs.cache-hit != 'true' - run: mix deps.compile - - - name: Compile - run: mix compile --warnings-as-errors --force - - - name: Test that the EventStoreDB container is alive - run: docker compose exec --no-TTY eventstore true - - - name: Coveralls - # Ordinary operations occasionally exceed Spear's 5s timeout under CI - # runner jitter, failing a different test each run. --max-cases 2 keeps - # the shared EventStoreDB from being overwhelmed; if a test still trips - # the timeout, re-run only the failures serially so transient flakes - # self-heal without disabling real tests. - run: | - mix coveralls.github --max-cases 2 \ - || mix test --failed --max-cases 1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Check formatting - run: mix format --check-formatted - - - name: Credo - run: mix credo + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Install Elixir and Erlang + uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1 + with: + elixir-version: ${{ env.ELIXIR_VERSION }} + otp-version: ${{ env.OTP_VERSION }} + - name: Cache dependencies and build + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: | + deps + _build + key: ${{ runner.os }}-elixir-${{ env.ELIXIR_VERSION }}-otp-${{ env.OTP_VERSION }}-test-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-elixir-${{ env.ELIXIR_VERSION }}-otp-${{ env.OTP_VERSION }}-test- + - name: Install package managers + run: | + mix local.hex --force + mix local.rebar --force + - name: Install dependencies + run: mix deps.get + - name: Compile with warnings as errors + run: mix compile --warnings-as-errors --force + - name: Run server-free tests + run: >- + mix test + test/spear/acl_test.exs + test/spear/client_test.exs + test/spear/connection/configuration_test.exs + test/spear/connection/keep_alive_timer_test.exs + test/spear/event_test.exs + test/spear/filter_test.exs + test/spear/grpc/response_test.exs + test/spear/uuid_test.exs + - name: Check formatting + run: mix format --check-formatted + - name: Run Credo + run: mix credo + - name: Build Hex package + run: mix hex.build dialyzer: name: Dialyzer - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest + timeout-minutes: 20 env: MIX_ENV: dev - steps: - - name: Checkout - uses: actions/checkout@v7 - - - name: Setup the latest Elixir and Erlang versions - uses: erlef/setup-beam@v1 - id: beam - with: - otp-version: latest - elixir-version: latest - - - name: Restore the deps cache - uses: actions/cache@v6 - id: deps-cache - with: - path: deps - key: ${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-${{ env.MIX_ENV }}-deps-mixlockhash-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }} - restore-keys: | - ${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-${{ env.MIX_ENV }}-deps- - - - name: Restore the _build cache - uses: actions/cache@v6 - id: build-cache - with: - path: _build - key: ${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-${{ env.MIX_ENV }}-build-mixlockhash-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }} - restore-keys: | - ${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-${{ env.MIX_ENV }}-build- - - - name: Fetch mix dependencies - if: steps.deps-cache.outputs.cache-hit != 'true' - run: | - mix local.rebar --force - mix local.hex --force - mix deps.get - - - name: Restore the PLT cache - uses: actions/cache/restore@v6 - id: plt-cache - with: - path: priv/plts - key: ${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-plt-mixlockhash-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }} - restore-keys: | - ${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-plt- - - - name: Build the PLTs - if: steps.plt-cache.outputs.cache-hit != 'true' - run: mix dialyzer --plt - - - name: Save the PLT cache - # Saved separately from the restore so the PLTs are cached even when - # the Dialyzer step below fails. - uses: actions/cache/save@v6 - if: steps.plt-cache.outputs.cache-hit != 'true' - with: - path: priv/plts - key: ${{ steps.plt-cache.outputs.cache-primary-key }} - - - name: Dialyzer - run: mix dialyzer --format github - - publish-hex-package: - name: Publish Hex Package ⬆️☁️ - runs-on: ubuntu-22.04 - if: startsWith(github.ref, 'refs/tags/v') - # Needed for `gh release create` - permissions: - contents: write - env: - MIX_ENV: dev - needs: - - bless - - steps: - - name: Checkout - uses: actions/checkout@v7 - - - name: Determine the elixir version - run: echo "ELIXIR_VERSION=$(grep -h elixir .tool-versions | awk '{ print $2 }' | awk -F - '{print $1}')" >> $GITHUB_ENV - - - name: Determine the otp version - run: echo "OTP_VERSION=$(grep -h erlang .tool-versions | awk '{ print $2 }')" >> $GITHUB_ENV - - - name: Setup Elixir and Erlang versions - uses: erlef/setup-beam@v1 - with: - otp-version: ${{ env.OTP_VERSION }} - elixir-version: ${{ env.ELIXIR_VERSION }} - - - name: Restore the deps cache - uses: actions/cache@v6 - id: deps-cache - with: - path: deps - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }} - restore-keys: | - ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps- - - - name: Restore the _build cache - uses: actions/cache@v6 - id: build-cache - with: - path: _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-build-mixlockhash-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }} - restore-keys: | - ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-build- - - - name: Fetch mix dependencies - if: steps.deps-cache.outputs.cache-hit != 'true' - run: | - mix local.rebar --force - mix local.hex --force - mix deps.get - - - name: Compile dependencies - if: steps.deps-cache.outputs.cache-hit != 'true' - run: mix deps.compile - - - name: Generate documentation - run: mix docs - - - name: Determine the tag version - env: - GITHUB_REF: ${{ github.ref }} - run: echo "TAG=$(echo "$GITHUB_REF" | awk -F / '{print $3}')" >> $GITHUB_ENV - - - name: Write the tag version to .version - run: echo $TAG > .version - - - name: Create a GitHub Release - run: gh release create "$TAG" --title "$TAG" --verify-tag --generate-notes - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Publish library - run: mix hex.publish --yes - env: - HEX_API_KEY: ${{ secrets.HEX_PUBLISH_KEY }} + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Install Elixir and Erlang + uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1 + with: + elixir-version: ${{ env.ELIXIR_VERSION }} + otp-version: ${{ env.OTP_VERSION }} + - name: Cache dependencies, build, and PLTs + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: | + deps + _build + priv/plts + key: ${{ runner.os }}-elixir-${{ env.ELIXIR_VERSION }}-otp-${{ env.OTP_VERSION }}-dev-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-elixir-${{ env.ELIXIR_VERSION }}-otp-${{ env.OTP_VERSION }}-dev- + - name: Install package managers + run: | + mix local.hex --force + mix local.rebar --force + - name: Install dependencies + run: mix deps.get + - name: Run Dialyzer + run: mix dialyzer --format github + - name: Build documentation + run: mix docs --warnings-as-errors diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml deleted file mode 100644 index 6bdf512..0000000 --- a/.github/workflows/compatibility.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: Compatibility - -on: [push, pull_request] - -jobs: - compatibility: - name: Bless - runs-on: ubuntu-22.04 - strategy: - fail-fast: false - matrix: - beam: - - elixir: "1.16.2" - otp: "26.2" - eventstore: - # 20.10.2 (Dec 2020) was dropped: it is long EOL and its old server-side - # $all event-type filtering made the global-subscription tests flaky. - # The primary CI workflow covers the current target (23.10.8). - - 22.6.0 - env: - MIX_ENV: test - EVENTSTORE_HOST: localhost - EVENTSTORE_VERSION: ${{ matrix.eventstore }} - - steps: - - name: Checkout - uses: actions/checkout@v7 - - - name: Set the EventStoreDB version - run: sed -i 's|eventstore/eventstore:23.10.8-bookworm-slim|eventstore/eventstore:${{ matrix.eventstore }}-buster-slim|g' docker-compose.yml - - - name: Generate EventStoreDB certificates - run: ./eventstoredb/gen-certs.sh - - - name: Spawn docker compose EventStoreDB container - run: docker compose up --detach eventstore - - - name: Setup Elixir and Erlang versions - uses: erlef/setup-beam@v1 - with: - otp-version: ${{ matrix.beam.otp }} - elixir-version: ${{ matrix.beam.elixir }} - - - name: Restore the deps cache - uses: actions/cache@v6 - id: deps-cache - with: - path: deps - key: ${{ runner.os }}-${{ matrix.beam.elixir }}-${{ matrix.beam.otp }}-${{ env.MIX_ENV }}-deps-mixlockhash-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }} - restore-keys: | - ${{ runner.os }}-${{ matrix.beam.elixir }}-${{ matrix.beam.otp }}-${{ env.MIX_ENV }}-deps- - - - name: Restore the _build cache - uses: actions/cache@v6 - id: build-cache - with: - path: _build - key: ${{ runner.os }}-${{ matrix.beam.elixir }}-${{ matrix.beam.otp }}-${{ env.MIX_ENV }}-build-mixlockhash-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }} - restore-keys: | - ${{ runner.os }}-${{ matrix.beam.elixir }}-${{ matrix.beam.otp }}-${{ env.MIX_ENV }}-build- - - - name: Fetch mix dependencies - if: steps.deps-cache.outputs.cache-hit != 'true' - run: | - mix local.rebar --force - mix local.hex --force - mix deps.get - - - name: Compile dependencies - if: steps.deps-cache.outputs.cache-hit != 'true' - run: mix deps.compile - - - name: Compile - run: mix compile --warnings-as-errors --force - - - name: Dump EventStoreDB container logs - if: always() - run: docker compose logs eventstore - - - name: Test that the EventStoreDB container is alive - run: docker compose exec --no-TTY eventstore true - - - name: Run tests - # Ordinary operations occasionally exceed Spear's 5s timeout under CI - # runner jitter. --max-cases 2 limits load on the shared EventStoreDB; - # if a test still trips the timeout, re-run only the failures serially so - # transient flakes self-heal without disabling real tests. - run: | - mix test --exclude version_incompatible --max-cases 2 \ - || mix test --exclude version_incompatible --failed --max-cases 1 - - - name: Credo - run: mix credo diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml new file mode 100644 index 0000000..518f995 --- /dev/null +++ b/.github/workflows/integration.yml @@ -0,0 +1,63 @@ +name: Integration + +on: + workflow_dispatch: + inputs: + server_image: + description: Fully qualified TrogonEventStore container image + required: false + default: ghcr.io/trogonstack/trogoneventstore:ci + type: string + +permissions: + contents: read + packages: read + +env: + EVENTSTORE_HOST: 127.0.0.1 + EVENTSTORE_VERSION: 23.10.8 + MIX_ENV: test + TROGON_EVENTSTORE_IMAGE: ${{ inputs.server_image || 'ghcr.io/trogonstack/trogoneventstore:ci' }} + +jobs: + integration: + name: TrogonEventStore + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Install Elixir and Erlang + uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1 + with: + elixir-version: 1.16.2 + otp-version: 26.2 + - name: Cache dependencies and build + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: | + deps + _build + key: ${{ runner.os }}-elixir-1.16.2-otp-26.2-test-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-elixir-1.16.2-otp-26.2-test- + - name: Install package managers + run: | + mix local.hex --force + mix local.rebar --force + - name: Install dependencies + run: mix deps.get + - name: Generate test certificates + run: ./eventstoredb/gen-certs.sh + - name: Start TrogonEventStore + run: docker compose up --detach --wait eventstore + - name: Run integration tests + run: mix test --exclude version_incompatible --max-cases 2 + - name: Show TrogonEventStore logs + if: failure() + run: docker compose logs eventstore + - name: Stop TrogonEventStore + if: always() + run: docker compose down --volumes --remove-orphans diff --git a/.github/workflows/refresh-dev-cache.yml b/.github/workflows/refresh-dev-cache.yml deleted file mode 100644 index ab3e8d3..0000000 --- a/.github/workflows/refresh-dev-cache.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: Refresh Dev Cache ♻️ - -# We need this workflow to ensure that MIX_ENV=dev caches are available when -# the CI workflow runs for a tagged build. This is due to a combination of -# several factors, so it's worthy of a wordy explanation. -# -# The actions/cache step stores the caches per-branch and if the cache key misses -# for a branch, it will try to find a cache from its default branch (main). -# So, when we run CI for a new branch or a new tag, there will be a cache miss -# and the cache step will try to find a cache from the main branch. -# The problem is, we only build with MIX_ENV=dev when a tag is pushed. That -# means our main CI workflow never builds a dev cache in the main branch. -# When we push a new tag for a release, the actions/cache step will always miss -# and then check main for a dev cache. We need this workflow to ensure that -# it will find a recent dev cache. -# -# Many of the steps are duplicated between this workflow and the -# publish-hex-package job in the Actions CI workflow. - -on: - push: - branches: - - main - -# Would like to run this as a scheduled workflow, but that is not currently -# supported by the actions/cache action. - -jobs: - refresh-dev-cache: - name: Refresh Docs Cache - runs-on: ubuntu-22.04 - env: - MIX_ENV: dev - - steps: - - name: Checkout - uses: actions/checkout@v7 - - - name: Determine the elixir version - run: echo "ELIXIR_VERSION=$(grep -h elixir .tool-versions | awk '{ print $2 }' | awk -F - '{print $1}')" >> $GITHUB_ENV - - - name: Determine the otp version - run: echo "OTP_VERSION=$(grep -h erlang .tool-versions | awk '{ print $2 }')" >> $GITHUB_ENV - - - name: Setup Elixir and Erlang versions - uses: erlef/setup-beam@v1 - with: - otp-version: ${{ env.OTP_VERSION }} - elixir-version: ${{ env.ELIXIR_VERSION }} - - - name: Restore the deps cache - uses: actions/cache@v6 - id: deps-cache - with: - path: deps - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }} - restore-keys: | - ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps- - - - name: Restore the _build cache - uses: actions/cache@v6 - id: build-cache - with: - path: _build - key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-build-mixlockhash-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }} - restore-keys: | - ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-build- - - - name: Fetch mix dependencies - if: steps.deps-cache.outputs.cache-hit != 'true' - run: | - mix local.rebar --force - mix local.hex --force - mix deps.get - - - name: Compile dependencies - if: steps.deps-cache.outputs.cache-hit != 'true' - run: mix deps.compile - - - name: Compile application - run: mix compile diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..e61f84d --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,24 @@ +name: Release Please + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + release-please: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Create release pull request or release + uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1 + with: + token: ${{ secrets.GH_PAT_RELEASE_PLEASE_ACTION }} + config-file: .github/release-please-config.json + manifest-file: .github/release-please-manifest.json diff --git a/.github/workflows/semconv.yml b/.github/workflows/semconv.yml new file mode 100644 index 0000000..9c56ffc --- /dev/null +++ b/.github/workflows/semconv.yml @@ -0,0 +1,70 @@ +name: SemConv + +on: + pull_request_target: + types: [opened, edited, synchronize, reopened] + +permissions: + pull-requests: read + +jobs: + validate: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Validate pull request title and commits + shell: bash + env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_TITLE: ${{ github.event.pull_request.title }} + REPOSITORY: ${{ github.repository }} + run: | + pattern='^(feat|fix|chore)(\([A-Za-z0-9][A-Za-z0-9._/-]*\))?!?: .+' + + if [[ ! "$PR_TITLE" =~ $pattern ]]; then + echo "Invalid pull request title: $PR_TITLE" >&2 + exit 1 + fi + + if ! commit_records=$(gh api --paginate \ + "repos/$REPOSITORY/pulls/$PR_NUMBER/commits" \ + --jq '.[] | { + parent_count: (.parents | length), + subject: (.commit.message | split("\n")[0]) + }'); then + echo "Unable to enumerate pull request commits." >&2 + exit 1 + fi + + if [ -z "$commit_records" ]; then + echo "Pull request contains no commits." >&2 + exit 1 + fi + + invalid_commits=0 + non_merge_commits=0 + while IFS= read -r record; do + if ! parent_count=$(jq -er '.parent_count' <<< "$record") || + ! subject=$(jq -er '.subject' <<< "$record"); then + echo "Unable to parse pull request commit metadata." >&2 + exit 1 + fi + + if [ "$parent_count" -gt 1 ]; then + continue + fi + + non_merge_commits=$((non_merge_commits + 1)) + if [[ ! "$subject" =~ $pattern ]]; then + echo "Invalid commit subject: $subject" >&2 + invalid_commits=1 + fi + done <<< "$commit_records" + + if [ "$non_merge_commits" -eq 0 ]; then + echo "Pull request contains no non-merge commits." >&2 + exit 1 + fi + + exit "$invalid_commits" diff --git a/.tool-versions b/.tool-versions deleted file mode 100644 index 02083d0..0000000 --- a/.tool-versions +++ /dev/null @@ -1,2 +0,0 @@ -elixir 1.16.2-otp-26 -erlang 26.2 diff --git a/.version b/.version index bd87164..94f0fff 100644 --- a/.version +++ b/.version @@ -1,4 +1,4 @@ -v0.0.0 +v0.0.0 # x-release-please-version # this file will be clobbered by github actions. # to version bump, only create new tag in git, with v + semver. diff --git a/README.md b/README.md index 053cbd3..146b63d 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,18 @@ # Spear -![CI](https://github.com/NFIBrokerage/spear/workflows/CI/badge.svg) -[![Coverage Status](https://coveralls.io/repos/github/NFIBrokerage/spear/badge.svg)](https://coveralls.io/github/NFIBrokerage/spear) +![CI](https://github.com/TrogonStack/TrogonEventStore-Client-Elixir/actions/workflows/ci.yml/badge.svg) +[![Integration](https://github.com/TrogonStack/TrogonEventStore-Client-Elixir/actions/workflows/integration.yml/badge.svg)](https://github.com/TrogonStack/TrogonEventStore-Client-Elixir/actions/workflows/integration.yml) [![hex.pm version](https://img.shields.io/hexpm/v/spear.svg)](https://hex.pm/packages/spear) -[![hex.pm license](https://img.shields.io/hexpm/l/spear.svg)](https://github.com/NFIBrokerage/spear/blob/master/LICENSE) -[![Last Updated](https://img.shields.io/github/last-commit/NFIBrokerage/spear.svg)](https://github.com/NFIBrokerage/spear/commits/main) +[![hex.pm license](https://img.shields.io/hexpm/l/spear.svg)](https://github.com/TrogonStack/TrogonEventStore-Client-Elixir/blob/main/LICENSE) +[![Last Updated](https://img.shields.io/github/last-commit/TrogonStack/TrogonEventStore-Client-Elixir.svg)](https://github.com/TrogonStack/TrogonEventStore-Client-Elixir/commits/main) A sharp EventStoreDB 20+ client backed by mint :yum: +This repository is TrogonStack's downstream fork of +[`CuatroElixir/spear`](https://github.com/CuatroElixir/spear). It preserves the +Spear API and package identity while compatibility is established against +TrogonEventStore. + ## FAQ **What's EventStoreDB?** @@ -78,7 +83,7 @@ spear We `@NFIBrokerage` already use Spear for some production connections to Event Store Cloud. See the roadmap in -[#7](https://github.com/NFIBrokerage/spear/issues/7) with the plans for +[#7](https://github.com/CuatroElixir/spear/issues/7) with the plans for reaching the v1.0.0 release. ## Installation @@ -255,4 +260,4 @@ via [GenStage](https://github.com/elixir-lang/gen_stage) and [Broadway](https://github.com/dashbitco/broadway) producers? Check out [Volley](https://github.com/NFIBrokerage/volley). -[^1]: https://github.com/NFIBrokerage/spear/issues/66 +[^1]: https://github.com/CuatroElixir/spear/issues/66 diff --git a/docker-compose.yml b/docker-compose.yml index 0da21ac..b30a39d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,10 @@ services: eventstore: - image: eventstore/eventstore:23.10.8-bookworm-slim + image: ${TROGON_EVENTSTORE_IMAGE:-ghcr.io/trogonstack/trogoneventstore:ci} environment: + - EVENTSTORE_ADVERTISE_HOST_TO_CLIENT_AS=localhost + - EVENTSTORE_ADVERTISE_NODE_PORT_TO_CLIENT_AS=2113 + - EVENTSTORE_NODE_IP=0.0.0.0 - EVENTSTORE_START_STANDARD_PROJECTIONS=True - EVENTSTORE_RUN_PROJECTIONS=All volumes: @@ -20,7 +23,7 @@ services: environment: - 'ERL_AFLAGS=-kernel shell_history enabled' - EVENTSTORE_HOST=eventstore - - EVENTSTORE_VERSION=23.10.8 + - EVENTSTORE_VERSION=${EVENTSTORE_VERSION:-0.0.0} volumes: - ./:/app working_dir: /app diff --git a/eventstoredb/README.md b/eventstoredb/README.md index 855933b..1c0b615 100644 --- a/eventstoredb/README.md +++ b/eventstoredb/README.md @@ -1,6 +1,7 @@ -# EventStoreDB config +# TrogonEventStore test configuration -This directory contains EventStoreDB configuration used for testing. +This directory contains TrogonEventStore configuration used for integration +testing. ## Certificates @@ -19,8 +20,8 @@ You can inspect a generated certificate with `openssl`: openssl x509 -in eventstoredb/certs/node.crt -text ``` -## EventStoreDB version +## Server image -We pin the test container to EventStoreDB `23.10.x`, the last release under a -true open-source license. Starting with `24.10`, EventStoreDB (now "KurrentDB") -is licensed under the non-OSS Event Store License v2. +The Docker Compose stack defaults to +`ghcr.io/trogonstack/trogoneventstore:ci`. Set `TROGON_EVENTSTORE_IMAGE` to +verify the client against another fully qualified image. diff --git a/eventstoredb/eventstore.conf b/eventstoredb/eventstore.conf index 3ed3d9c..8d40484 100644 --- a/eventstoredb/eventstore.conf +++ b/eventstoredb/eventstore.conf @@ -9,12 +9,5 @@ CertificateFile: /etc/eventstore/certs/node.crt CertificatePrivateKeyFile: /etc/eventstore/certs/node.key TrustedRootCertificatesPath: /etc/eventstore/certs/ca -# Network configuration -IntIp: 0.0.0.0 -ExtIp: 0.0.0.0 -HttpPort: 2113 -IntTcpPort: 1112 -EnableAtomPubOverHTTP: true - # Projections configuration RunProjections: All diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..ea9923f --- /dev/null +++ b/mise.toml @@ -0,0 +1,3 @@ +[tools] +elixir = "1.16.2-otp-26" +erlang = "26.2" diff --git a/mix.exs b/mix.exs index f9736df..ce7a8af 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Spear.MixProject do use Mix.Project - @source_url "https://github.com/NFIBrokerage/spear" + @source_url "https://github.com/TrogonStack/TrogonEventStore-Client-Elixir" @version_file Path.join(__DIR__, ".version") @external_resource @version_file @version (case Regex.run(~r/^v([\d\.\w-]+)/, File.read!(@version_file), capture: :all_but_first) do @@ -17,16 +17,6 @@ defmodule Spear.MixProject do elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, test_coverage: [tool: ExCoveralls], - preferred_cli_env: [ - credo: :test, - coveralls: :test, - "coveralls.html": :test, - "coveralls.github": :test, - inch: :dev, - bless: :test, - test: :test, - dialyzer: :dev - ], name: "Spear", source_url: @source_url, deps: deps(), @@ -41,6 +31,21 @@ defmodule Spear.MixProject do ] end + def cli do + [ + preferred_envs: [ + credo: :test, + coveralls: :test, + "coveralls.html": :test, + "coveralls.github": :test, + inch: :dev, + bless: :test, + test: :test, + dialyzer: :dev + ] + ] + end + def application do [ extra_applications: [:crypto] @@ -73,7 +78,7 @@ defmodule Spear.MixProject do defp package do [ name: "spear", - files: ~w(lib .formatter.exs mix.exs README.md .version), + files: ~w(lib .formatter.exs mix.exs README.md CHANGELOG.md LICENSE .version), licenses: ["Apache-2.0"], links: %{ "GitHub" => @source_url, @@ -134,7 +139,7 @@ defmodule Spear.MixProject do Spear.Records.ServerFeatures ] ], - groups_for_functions: [ + groups_for_docs: [ "Utility Functions": &(&1[:api] == :utils), Streams: &(&1[:api] == :streams), Users: &(&1[:api] == :users),