diff --git a/.github/actions/create-github-release/action.yml b/.github/actions/create-github-release/action.yml
new file mode 100644
index 0000000..9197aa0
--- /dev/null
+++ b/.github/actions/create-github-release/action.yml
@@ -0,0 +1,27 @@
+name: Create GitHub Release
+description: 'Create the release on GitHub with a changelog'
+inputs:
+  milestone:
+    description: 'Name of the GitHub milestone for which a release will be created'
+    required: true
+  pre-release:
+    description: 'Whether the release is a pre-release (a milestone or release candidate)'
+    required: false
+    default: 'false'
+  token:
+    description: 'Token to use for authentication with GitHub'
+    required: true
+runs:
+  using: composite
+  steps:
+    - name: Generate Changelog
+      uses: spring-io/github-changelog-generator@185319ad7eaa75b0e8e72e4b6db19c8b2cb8c4c1 #v0.0.11
+      with:
+        config-file: '.github/actions/create-github-release/changelog-generator.yml'
+        milestone: ${{ inputs.milestone }}
+        token: ${{ inputs.token }}
+    - name: Create GitHub Release
+      shell: bash
+      env:
+        GITHUB_TOKEN: ${{ inputs.token }}
+      run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md ${{ inputs.pre-release == 'true' && '--prerelease' || '' }}
diff --git a/.github/actions/create-github-release/changelog-generator.yml b/.github/actions/create-github-release/changelog-generator.yml
new file mode 100644
index 0000000..6a2f463
--- /dev/null
+++ b/.github/actions/create-github-release/changelog-generator.yml
@@ -0,0 +1,14 @@
+changelog:
+  sections:
+    - title: ":star: New Features"
+      labels:
+        - "type: enhancement"
+    - title: ":lady_beetle: Bug Fixes"
+      labels:
+        - "type: bug"
+        - "type: regression"
+    - title: ":notebook_with_decorative_cover: Documentation"
+      labels:
+        - "type: documentation"
+  issues:
+    generate_links: true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..85d320d
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,46 @@
+name: Release
+on:
+  push:
+    tags:
+      - v0.0.[0-9]+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+jobs:
+  build:
+    name: 'Build'
+    runs-on: 'ubuntu-latest'
+    steps:
+      - name: Check out code
+        uses: actions/checkout@v4
+      - name: Build
+        uses: ./.github/actions/build
+  integration-test:
+    name: 'Integration test'
+    runs-on: ubuntu-latest
+    services:
+      artifactory:
+        image: docker.bintray.io/jfrog/artifactory-oss:7.12.10
+        ports:
+          - 8081:8081
+    steps:
+      - name: Check out code
+        uses: actions/checkout@v4
+      - name: Integration test
+        uses: ./.github/actions/integration-test
+        with:
+          signing-key: ${{ secrets.INTEGRATION_TEST_SIGNING_KEY }}
+          signing-passphrase: ${{ secrets.INTEGRATION_TEST_SIGNING_PASSPHRASE }}
+  create-github-release:
+    name: Create GitHub Release
+    needs:
+      - build
+      - integration-test
+    runs-on: 'ubuntu-latest'
+    steps:
+      - name: Check Out Code
+        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+      - name: Create GitHub Release
+        uses: ./.github/actions/create-github-release
+        with:
+          milestone: ${{ needs.build.outputs.version }}
+          token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}