-
Notifications
You must be signed in to change notification settings - Fork 0
Upgrade and Modernize #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| name: Build, Test & Publish | ||
| permissions: | ||
| contents: write | ||
|
|
||
| on: | ||
| push: | ||
| branches: [master] | ||
| pull_request: | ||
|
|
||
| defaults: | ||
| run: | ||
| shell: bash | ||
|
|
||
| jobs: | ||
| build: | ||
| name: Build & Test (${{ matrix.mssql_image }}) | ||
| runs-on: ubuntu-latest | ||
|
|
||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| mssql_image: | ||
| - mcr.microsoft.com/mssql/server:2025-latest | ||
| - mcr.microsoft.com/mssql/server:2022-latest | ||
| - mcr.microsoft.com/mssql/server:2019-latest | ||
| - mcr.microsoft.com/mssql/server:2017-latest | ||
|
|
||
| env: | ||
| MIX_ENV: test | ||
| MSSQL_IMAGE: ${{ matrix.mssql_image }} | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: jdx/mise-action@v2 | ||
|
|
||
| - name: Install dependencies | ||
| run: mix deps.get | ||
|
|
||
| - name: Compile | ||
| run: mix compile --warnings-as-errors | ||
|
|
||
| - name: Static analysis | ||
| run: mix credo | ||
|
|
||
| - name: Run integration tests | ||
| run: mix test --only integration | ||
|
|
||
| benchmark: | ||
| name: Benchmark | ||
| runs-on: ubuntu-latest | ||
| needs: build | ||
| if: github.ref_name == github.event.repository.default_branch && !failure() && !cancelled() | ||
| # Non-blocking: a flaky benchmark must never prevent a release from shipping. | ||
| continue-on-error: true | ||
|
|
||
| env: | ||
| MIX_ENV: dev | ||
| MSSQL_IMAGE: mcr.microsoft.com/mssql/server:2022-latest | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - uses: jdx/mise-action@v2 | ||
|
|
||
| - name: Cache Mix dependencies | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: | | ||
| deps | ||
| _build | ||
| key: ${{ runner.os }}-mix-dev-${{ hashFiles('mix.lock') }} | ||
| restore-keys: ${{ runner.os }}-mix-dev- | ||
|
|
||
| - name: Install dependencies | ||
| run: mix deps.get | ||
|
|
||
| - name: Compile | ||
| run: mix compile | ||
|
|
||
| - name: Run benchmarks | ||
| # stderr carries Logger/Testcontainers debug noise; stdout has the results. | ||
| run: mix run bench/benchmarks.exs > bench_results.txt 2>/dev/null | ||
|
|
||
| - name: Upload benchmark results | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: benchmark-results | ||
| path: bench_results.txt | ||
|
|
||
| publish: | ||
| name: Publish | ||
| runs-on: ubuntu-latest | ||
| needs: [build, benchmark] | ||
| if: github.ref_name == github.event.repository.default_branch && !failure() && !cancelled() | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - uses: jdx/mise-action@v2 | ||
|
|
||
| - name: Cache Mix dependencies | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: | | ||
| deps | ||
| _build | ||
| key: ${{ runner.os }}-mix-${{ hashFiles('mix.lock') }} | ||
| restore-keys: ${{ runner.os }}-mix- | ||
|
|
||
| - name: Install dependencies | ||
| run: mix deps.get | ||
|
|
||
| - name: Get version | ||
| id: version | ||
| run: | | ||
| BASE=$(cat VERSION) | ||
| BASE_COMMIT=$(git log -1 --format="%H" -- VERSION) | ||
| HEIGHT=$(git rev-list --count "${BASE_COMMIT}..HEAD") | ||
| VERSION="${BASE}.${HEIGHT}" | ||
| sed -i "s/version: \"[^\"]*\"/version: \"${VERSION}\"/" mix.exs | ||
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Generate release notes | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| gh api repos/${{ github.repository }}/releases/generate-notes \ | ||
| -f tag_name="v${{ steps.version.outputs.version }}" \ | ||
| --jq .body > release_notes.md | ||
|
|
||
| - name: Download benchmark results | ||
| uses: actions/download-artifact@v4 | ||
| # Non-blocking: publish proceeds even if the benchmark artifact is absent. | ||
| continue-on-error: true | ||
| with: | ||
| name: benchmark-results | ||
|
|
||
| - name: Append benchmarks to release notes | ||
| run: | | ||
| if [ -f bench_results.txt ]; then | ||
| { | ||
| echo "" | ||
| echo "## Benchmarks" | ||
| echo "" | ||
| echo "Measured against \`mcr.microsoft.com/mssql/server:2022-latest\` on \`ubuntu-latest\`." | ||
| echo "" | ||
| echo '```' | ||
| cat bench_results.txt | ||
| echo '```' | ||
| } >> release_notes.md | ||
| fi | ||
|
|
||
| - name: Publish to Hex.pm | ||
| env: | ||
| HEX_API_KEY: ${{ secrets.HEX_API_KEY }} | ||
| run: mix hex.publish --yes | ||
|
|
||
| - name: Create GitHub Release | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| gh release create "v${{ steps.version.outputs.version }}" \ | ||
| --title "v${{ steps.version.outputs.version }}" \ | ||
| --notes-file release_notes.md |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Disable the Ryuk reaper container. | ||
| # Ryuk requires privileged mode and does not work with rootless Podman. | ||
| # Containers are still stopped when the test process exits cleanly. | ||
| ryuk.disabled=true |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| # AGENTS.md | ||
|
|
||
| Guidelines for AI agents and automated tools contributing to **ex_sql_client**. | ||
|
|
||
| --- | ||
|
|
||
| ## What this project is | ||
|
|
||
| `ex_sql_client` is an Elixir Microsoft SQL Server driver. It is a thin Elixir | ||
| wrapper over a .NET worker process (via [Netler](https://github.com/svan-jansson/netler)) | ||
| that uses `Microsoft.Data.SqlClient` to communicate with SQL Server. | ||
|
|
||
| The library requires a running SQL Server instance for its integration tests. | ||
|
|
||
| --- | ||
|
|
||
| ## Repository layout | ||
|
|
||
| ``` | ||
| lib/ | ||
| ex_sql_client.ex # Public API – the main entry point | ||
| ex_sql_client/ | ||
| connection.ex # DBConnection behaviour implementation | ||
| protocol.ex # Netler RPC call helpers | ||
| query.ex # Query struct | ||
| result.ex # Result struct | ||
| dotnet/dotnet_sql_client/ | ||
| DotnetSqlClient.csproj # .NET 8 project file | ||
| Program.cs # Netler.NET server bootstrap | ||
| SqlAdapter.cs # SQL Server operations via Microsoft.Data.SqlClient | ||
| test/ | ||
| query_test.exs | ||
| transaction_test.exs | ||
| prepared_statement_test.exs | ||
| data_type_test.exs | ||
| test_helper.exs | ||
| mix.exs # Build config and project metadata | ||
| docker-compose.yml # Local SQL Server for integration tests | ||
| .github/workflows/ # GitHub Actions CI (build + test + publish) | ||
|
svan-jansson marked this conversation as resolved.
|
||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Building | ||
|
|
||
| Prerequisites: Elixir ≥ 1.9, Erlang/OTP, .NET 8 SDK, running SQL Server instance. | ||
|
|
||
| ```bash | ||
| mix deps.get | ||
| mix compile --warnings-as-errors | ||
| ``` | ||
|
|
||
| The `--warnings-as-errors` flag is enforced in CI; treat compiler warnings as | ||
| bugs. | ||
|
|
||
| The Netler compiler (`mix compile.netler`) automatically builds the .NET project | ||
| in `dotnet/dotnet_sql_client/` and places the binary in `priv/`. | ||
|
svan-jansson marked this conversation as resolved.
|
||
|
|
||
| --- | ||
|
|
||
| ## Testing | ||
|
|
||
| Integration tests spin up a SQL Server container automatically via | ||
| [Testcontainers](https://hex.pm/packages/testcontainers). Docker (or a | ||
| compatible runtime) must be available on the machine. | ||
|
|
||
| ```bash | ||
| mix test --only integration | ||
| ``` | ||
|
|
||
| The container is started once in `test/test_helper.exs` and the connection | ||
| string is shared with all test modules via `Application.put_env`. All tests are | ||
| tagged with `@tag :integration`. There are no unit tests that run without a | ||
| live database. | ||
|
|
||
| --- | ||
|
|
||
| ## Code conventions | ||
|
|
||
| ### Elixir | ||
|
|
||
| - Format every file with `mix format` before committing. The formatter config | ||
| lives in `.formatter.exs`. | ||
| - Follow standard Elixir naming: `snake_case` functions, `CamelCase` modules. | ||
| - Public functions should have `@doc` and `@spec` annotations. | ||
| - Return values use tagged tuples (`{:ok, value}` / `{:error, reason}`). Do not | ||
| raise exceptions across module boundaries. | ||
| - The Netler route names in `Program.cs` must match the atoms used in the | ||
| Elixir protocol layer exactly. | ||
|
|
||
| ### C\# | ||
|
|
||
| - The .NET project targets `net8.0`. Do not downgrade the target framework. | ||
| - Use `Microsoft.Data.SqlClient` (not the deprecated `System.Data.SqlClient`). | ||
| - Follow existing naming conventions: `PascalCase` for methods and classes, | ||
| `camelCase` for locals. | ||
| - Route handler methods in `SqlAdapter.cs` must have the signature | ||
| `public object MethodName(params object[] parameters)`. | ||
|
|
||
|
svan-jansson marked this conversation as resolved.
|
||
| --- | ||
|
|
||
| ## Architecture notes | ||
|
|
||
| The call chain for a query is: | ||
|
|
||
| ``` | ||
| Elixir caller | ||
| → ExSqlClient (DBConnection behaviour) | ||
| → Netler RPC (TCP socket, port process) | ||
| → Program.cs (Netler.NET server) | ||
| → SqlAdapter.cs | ||
| → Microsoft.Data.SqlClient | ||
| → SQL Server | ||
| ``` | ||
|
|
||
| Key invariants: | ||
| - The .NET process is started by Netler as a port. It listens on a TCP port | ||
| passed as `args[0]`; the Elixir PID is passed as `args[1]`. | ||
| - All route names in `Program.cs` must be registered and match what the | ||
| Elixir layer calls via Netler. | ||
| - Connection and transaction state is held in `SqlAdapter` — one instance per | ||
| connection process. | ||
|
|
||
| --- | ||
|
|
||
| ## What to work on | ||
|
|
||
| Good first contributions: | ||
| - Expanding test coverage for edge-case data types. | ||
| - Adding `@spec` / `@type` annotations to the Elixir modules. | ||
| - Improving error propagation from the .NET layer to Elixir. | ||
| - Updating dependencies as new versions are released. | ||
|
|
||
| Areas requiring extra care: | ||
| - Anything touching `Program.cs` or `SqlAdapter.cs` — changes must compile | ||
| with the pinned .NET version and be verified against a live SQL Server. | ||
| - Netler version upgrades — the RPC protocol may change between major versions; | ||
| always check `Program.cs` against the new Netler.NET API. | ||
| - `DBConnection` behaviour callbacks — maintain compatibility with the | ||
| `db_connection` contract. | ||
|
|
||
| --- | ||
|
|
||
| ## Pull request checklist | ||
|
|
||
| - [ ] `mix compile --warnings-as-errors` passes. | ||
| - [ ] `mix test --only integration` passes against a local SQL Server. | ||
| - [ ] `mix format --check-formatted` passes. | ||
| - [ ] `mix credo` passes. | ||
| - [ ] New public functions have `@doc` and `@spec`. | ||
| - [ ] Commit messages are concise and in the imperative mood. | ||
|
|
||
| --- | ||
|
|
||
| ## Out of scope | ||
|
|
||
| - Windows CI — the SQL Server Docker container is Linux-only in GitHub Actions; | ||
| the library itself is cross-platform but CI runs on Linux. | ||
| - Supporting databases other than Microsoft SQL Server. | ||
| - Changing the `DBConnection` protocol — maintain compatibility with standard | ||
| Elixir database tooling (Ecto, etc.). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 0.2 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.