This is an automated email from the ASF dual-hosted git repository.
spetz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git
The following commit(s) were added to refs/heads/master by this push:
new 89dc4a269 feat(swift): publish the SDK to SwiftPM from the repository
root (#4714)
89dc4a269 is described below
commit 89dc4a2699ff3b8abcec3118f3ddf942eaa23ad9
Author: Piotr Gankiewicz <[email protected]>
AuthorDate: Tue Nov 3 16:52:36 2026 +0100
feat(swift): publish the SDK to SwiftPM from the repository root (#4714)
The Swift SDK had no release path. SwiftPM resolves packages only from
a repository root with semver tags, the README pointed users at an
apache/iggy-swift mirror that does not exist, foreign/swift carried no
LICENSE or NOTICE, and its version was not on the edge scheme the
other SDKs release from.
A root Package.swift now publishes the Iggy library from
foreign/swift, and the publish workflow tags releases as v<version>,
the way it tags the Go module. Post-merge publishes X.Y.Z-edge.N
snapshots, and a plain X.Y.Z is tagged only by a manual dispatch after
the release vote. The publish step builds the root manifest without a
lockfile before tagging, as a consumer would. Pre-merge fails when the
root manifest drifts from foreign/swift/Package.swift, and
bump-version.sh learns sdk-swift and regenerates the golden vectors
that embed the version.
A root manifest keeps one repository, one tag namespace for SwiftPM
and the existing GITHUB_TOKEN tag push. A mirror repository would need
a cross-repository credential, Infra authorization for automated
pushes and recovery for publishes spanning two repositories, only to
save consumers the first full clone.
Relates to #4001
---
.github/actions/swift/post-merge/action.yml | 94 ++++++++++
.github/actions/swift/pre-merge/action.yml | 28 +++
.github/actions/utils/setup-swift/action.yml | 36 ++--
.github/config/components.yml | 2 +
.github/config/publish.yml | 7 +
.github/workflows/_test_examples.yml | 8 +
.github/workflows/post-merge.yml | 5 +-
.github/workflows/publish.yml | 89 ++++++++-
.gitignore | 4 +
.spi.yml | 22 +++
AGENTS.md | 4 +-
foreign/swift/Package.swift => Package.swift | 28 +--
foreign/swift/LICENSE | 201 +++++++++++++++++++++
foreign/swift/NOTICE | 12 ++
foreign/swift/Package.swift | 2 +-
foreign/swift/README.md | 35 +++-
foreign/swift/Sources/Iggy/Iggy.swift | 5 +-
foreign/swift/Tests/IggyTests/Fixtures/golden.json | 8 +-
scripts/bump-version.sh | 41 ++++-
scripts/prepare-release.sh | 1 +
20 files changed, 565 insertions(+), 67 deletions(-)
diff --git a/.github/actions/swift/post-merge/action.yml
b/.github/actions/swift/post-merge/action.yml
new file mode 100644
index 000000000..80ae46352
--- /dev/null
+++ b/.github/actions/swift/post-merge/action.yml
@@ -0,0 +1,94 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: swift-post-merge
+description: Swift post-merge tag preparation github iggy actions
+
+inputs:
+ version:
+ description: "Version for tagging (without the 'v' prefix)"
+ required: true
+ tag:
+ description: "Git tag the publish workflow creates for this version"
+ required: true
+ dry_run:
+ description: "Dry run mode"
+ required: false
+ default: "false"
+
+runs:
+ using: "composite"
+ steps:
+ # Build metadata cannot distinguish versions with the same SwiftPM
precedence.
+ - name: Validate version and tag
+ shell: bash
+ env:
+ VERSION: ${{ inputs.version }}
+ TAG: ${{ inputs.tag }}
+ run: |
+ set -euo pipefail
+ PRERELEASE='(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)'
+
SEMVER="^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-${PRERELEASE}(\.${PRERELEASE})*)?$"
+ if ! [[ "$VERSION" =~ $SEMVER ]]; then
+ echo "❌ Invalid Swift SDK version: $VERSION"
+ echo "Expected X.Y.Z or X.Y.Z-prerelease, without build metadata"
+ exit 1
+ fi
+ if [ "$TAG" != "v${VERSION}" ]; then
+ echo "❌ Tag $TAG does not match version $VERSION, expected
v${VERSION}"
+ exit 1
+ fi
+ echo "✅ Version $VERSION, tag $TAG"
+
+ # SwiftPM consumers receive the root manifest and legal files with the
repository.
+ - name: Check package and legal files
+ shell: bash
+ run: |
+ set -euo pipefail
+ for file in Package.swift LICENSE NOTICE foreign/swift/LICENSE
foreign/swift/NOTICE; do
+ if [ ! -f "$file" ]; then
+ echo "❌ Missing $file"
+ exit 1
+ fi
+ done
+ echo "✅ Root manifest and legal files present"
+
+ - name: Summary
+ shell: bash
+ env:
+ VERSION: ${{ inputs.version }}
+ TAG: ${{ inputs.tag }}
+ DRY_RUN: ${{ inputs.dry_run }}
+ run: |
+ {
+ echo "## 🏷️ Swift Package Tag"
+ echo ""
+ echo "| Property | Value |"
+ echo "|----------|-------|"
+ echo "| **Version** | \`${VERSION}\` |"
+ echo "| **Git Tag** | \`${TAG}\` |"
+ echo "| **Package URL** | \`https://github.com/${{ github.repository
}}.git\` |"
+ echo ""
+ echo "\`\`\`swift"
+ echo ".package(url: \"https://github.com/${{ github.repository
}}.git\", exact: \"${VERSION}\")"
+ echo ".product(name: \"Iggy\", package: \"iggy\")"
+ echo "\`\`\`"
+ echo ""
+ if [ "$DRY_RUN" = "true" ]; then
+ echo "**Note:** Dry run, no tag will be created"
+ fi
+ } >> "$GITHUB_STEP_SUMMARY"
diff --git a/.github/actions/swift/pre-merge/action.yml
b/.github/actions/swift/pre-merge/action.yml
index 2043b5e1a..112a4b1b2 100644
--- a/.github/actions/swift/pre-merge/action.yml
+++ b/.github/actions/swift/pre-merge/action.yml
@@ -29,6 +29,7 @@ runs:
# The macOS lane uses the Swift that ships with Xcode on the runner, and
# the golden lane only runs the Rust generator.
- name: Setup Swift
+ id: setup-swift
if: inputs.task != 'build-macos' && inputs.task != 'golden'
uses: ./.github/actions/utils/setup-swift
@@ -45,6 +46,24 @@ runs:
for package in examples/swift/getting-started bdd/swift/tests; do
swift format lint --strict --recursive --configuration
foreign/swift/.swift-format "$package/Sources" "$package/Package.swift"
done
+ swift format lint --strict --configuration foreign/swift/.swift-format
Package.swift
+
+ # Consumers and CI must build the same library from the two manifests.
+ - name: Check the root manifest matches the SDK manifest
+ shell: bash
+ if: inputs.task == 'lint'
+ run: |
+ set -euo pipefail
+ # Each side maps its expected Iggy path to one sentinel, so an extra
root product or target, or a moved path, shows in the diff.
+ root_manifest=$(swift package --package-path . dump-package \
+ | jq -S 'del(.name, .packageKind) | .targets |= map(if .name ==
"Iggy" and .path == "foreign/swift/Sources/Iggy" then .path = "SDK" else .
end)')
+ sdk_manifest=$(swift package --package-path foreign/swift dump-package
\
+ | jq -S 'del(.name, .packageKind) | .products |= map(select(.name ==
"Iggy")) | .targets |= map(select(.name == "Iggy") | if (.path //
"Sources/Iggy") == "Sources/Iggy" then .path = "SDK" else . end)')
+ if ! diff <(echo "$root_manifest") <(echo "$sdk_manifest"); then
+ echo "❌ Package.swift and foreign/swift/Package.swift disagree, see
the diff above"
+ exit 1
+ fi
+ echo "✅ Root Package.swift matches foreign/swift/Package.swift"
# A warning-free build is the lint gate the Swift compiler provides: the
# SDK builds in Swift 6 language mode with strict concurrency checking.
@@ -64,6 +83,15 @@ runs:
cd foreign/swift
swift test --parallel
+ # The lanes share one cache key, so only the test lane saves: the e2e lane
+ # builds the same debug products, and every lane reuses the checkouts.
+ - name: Save SwiftPM build directory
+ if: inputs.task == 'test' && steps.setup-swift.outputs.cache-hit !=
'true'
+ uses: actions/cache/[email protected]
+ with:
+ path: foreign/swift/.build
+ key: ${{ steps.setup-swift.outputs.cache-key }}
+
- name: Build
shell: bash
if: inputs.task == 'build'
diff --git a/.github/actions/utils/setup-swift/action.yml
b/.github/actions/utils/setup-swift/action.yml
index 06cd9734f..e6daddf2c 100644
--- a/.github/actions/utils/setup-swift/action.yml
+++ b/.github/actions/utils/setup-swift/action.yml
@@ -16,7 +16,7 @@
# under the License.
name: setup-swift
-description: Install the Swift toolchain and cache the SwiftPM dependency
checkouts and build products
+description: Install the Swift toolchain and restore the SwiftPM build
directory
inputs:
cache-key:
description: "Cache key segment naming the package that builds in the job;
one constant per package so every lane shares one cache"
@@ -26,6 +26,18 @@ inputs:
description: "Directory of the SwiftPM package whose build directory is
cached"
required: false
default: "foreign/swift"
+ read-cache:
+ description: "Whether to restore the SwiftPM build directory"
+ required: false
+ default: "true"
+
+outputs:
+ cache-key:
+ description: "Key the caller saves the build directory under with
actions/cache/save after its build"
+ value: ${{ steps.cache.outputs.cache-primary-key }}
+ cache-hit:
+ description: "Whether the restored build directory matched the key exactly"
+ value: ${{ steps.cache.outputs.cache-hit }}
runs:
using: "composite"
@@ -80,18 +92,18 @@ runs:
shell: bash
run: swift --version
- # Package.resolved pins the dependency graph, so it keys the cache; the
- # build directories hold the compiled dependencies, and SwiftPM's
- # incremental build reuses them across source changes.
- - name: Cache SwiftPM dependencies and build products
- uses: actions/[email protected]
+ # Package.resolved pins the dependency graph, so it keys the cache. The
+ # whole build directory is restored: without workspace-state.json SwiftPM
+ # recreates every checkout, and .build/debug is only a symlink into
+ # .build/out. A nested composite action hands the post step of
+ # actions/cache the wrong context (actions/runner#2009), so the caller
+ # saves with actions/cache/save once its build succeeds.
+ - name: Restore SwiftPM build directory
+ id: cache
+ if: inputs.read-cache == 'true'
+ uses: actions/cache/[email protected]
with:
- path: |
- ${{ inputs.package-path }}/.build/checkouts
- ${{ inputs.package-path }}/.build/repositories
- ${{ inputs.package-path }}/.build/artifacts
- ${{ inputs.package-path }}/.build/debug
- ${{ inputs.package-path }}/.build/release
+ path: ${{ inputs.package-path }}/.build
key: swiftpm-${{ runner.os }}-${{ steps.swift.outputs.version }}-${{
inputs.cache-key }}-${{ hashFiles(format('{0}/Package.resolved',
inputs.package-path)) }}
restore-keys: |
swiftpm-${{ runner.os }}-${{ steps.swift.outputs.version }}-${{
inputs.cache-key }}-
diff --git a/.github/config/components.yml b/.github/config/components.yml
index 7a217cb4a..619243efb 100644
--- a/.github/config/components.yml
+++ b/.github/config/components.yml
@@ -357,6 +357,8 @@ components:
- "foreign/swift/**"
- "bdd/swift/**"
- "examples/swift/**"
+ - "Package.swift"
+ - ".spi.yml"
tasks: ["lint", "test", "build", "build-macos", "golden", "e2e"]
# Shared BDD infrastructure that affects all BDD tests
diff --git a/.github/config/publish.yml b/.github/config/publish.yml
index c54ce712d..a889834d7 100644
--- a/.github/config/publish.yml
+++ b/.github/config/publish.yml
@@ -136,3 +136,10 @@ components:
registry: none
version_file: "foreign/go/contracts/version.go"
version_regex: 'const\s+Version\s*=\s*"([^"]+)"'
+
+ # ── Swift (tag only, SwiftPM resolves the root Package.swift) ──────────────
+ sdk-swift:
+ tag_pattern: "^v([0-9]+\\.[0-9]+\\.[0-9]+(?:-[0-9A-Za-z.-]+)?)$"
+ registry: swiftpm
+ version_file: "foreign/swift/Sources/Iggy/Iggy.swift"
+ version_regex: 'sdkVersion\s*=\s*"([^"]+)"'
diff --git a/.github/workflows/_test_examples.yml
b/.github/workflows/_test_examples.yml
index f6b022061..fd160960a 100644
--- a/.github/workflows/_test_examples.yml
+++ b/.github/workflows/_test_examples.yml
@@ -92,6 +92,7 @@ jobs:
download-deps: "false"
- name: Setup Swift for examples
+ id: setup-swift
if: matrix.task == 'examples-swift'
uses: ./.github/actions/utils/setup-swift
with:
@@ -103,6 +104,13 @@ jobs:
if: matrix.task == 'examples-swift'
run: swift build --package-path examples/swift/getting-started
-Xswiftc -warnings-as-errors
+ - name: Save Swift build directory
+ if: matrix.task == 'examples-swift' &&
steps.setup-swift.outputs.cache-hit != 'true'
+ uses: actions/cache/[email protected]
+ with:
+ path: examples/swift/getting-started/.build
+ key: ${{ steps.setup-swift.outputs.cache-key }}
+
- name: Test Go examples
if: matrix.task == 'examples-go'
working-directory: examples/go
diff --git a/.github/workflows/post-merge.yml b/.github/workflows/post-merge.yml
index 4cfe07c3b..fa1b610ca 100644
--- a/.github/workflows/post-merge.yml
+++ b/.github/workflows/post-merge.yml
@@ -16,7 +16,8 @@
# under the License.
# Auto-publish: detects pre-release crate/SDK versions without tags
-# and publishes them to crates.io, Docker Hub, PyPI, npm, Maven, NuGet.
+# and publishes them to crates.io, Docker Hub, PyPI, npm, Maven, NuGet,
+# or as git tags for Go and Swift.
# Runs on every push to master.
name: Post-merge
@@ -138,7 +139,7 @@ jobs:
# Check SDKs for pre-release versions without tags
SDKS_TO_PUBLISH=""
- for sdk in sdk-python sdk-node sdk-java sdk-csharp sdk-go; do
+ for sdk in sdk-python sdk-node sdk-java sdk-csharp sdk-go sdk-swift;
do
VERSION=$(scripts/extract-version.sh "$sdk")
TAG=$(scripts/extract-version.sh "$sdk" --tag)
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 9ba31d109..0dab46d0c 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -49,7 +49,7 @@ on:
required: false
default: ""
publish_other:
- description: "Other SDKs to publish (comma-separated: python, node,
java, csharp, go)"
+ description: "Other SDKs to publish (comma-separated: python, node,
java, csharp, go, swift)"
type: string
required: false
default: ""
@@ -185,6 +185,7 @@ jobs:
docker_components: ${{ steps.mk.outputs.docker_components }}
count: ${{ steps.mk.outputs.count }}
has_python: ${{ steps.mk.outputs.has_python }}
+ has_swift: ${{ steps.mk.outputs.has_swift }}
has_docker: ${{ steps.mk.outputs.has_docker }}
steps:
- name: Download latest copy script from master
@@ -258,7 +259,7 @@ jobs:
// Parse other SDKs
('${{ inputs.publish_other }}').split(',').map(s =>
s.trim()).filter(Boolean).forEach(sdk => {
- if (['python','node','java','csharp','go'].includes(sdk)) {
+ if
(['python','node','java','csharp','go','swift'].includes(sdk)) {
wants.push(`sdk-${sdk}`);
} else {
core.warning(`Unknown SDK: ${sdk}`);
@@ -272,7 +273,8 @@ jobs:
npm: 'node',
maven: 'java',
nuget: 'csharp',
- none: 'go'
+ none: 'go',
+ swiftpm: 'swift'
}[entry.registry] || 'unknown');
const targets = [];
@@ -364,6 +366,8 @@ jobs:
core.setOutput('has_python', 'false');
}
+ core.setOutput('has_swift', String(targets.some(t => t.key ===
'sdk-swift')));
+
check-tags:
name: Existing tags
needs: [validate, plan]
@@ -661,6 +665,50 @@ jobs:
use_latest_ci: ${{ inputs.use_latest_ci }}
commit: ${{ needs.validate.outputs.commit }}
+ # Without a lockfile SwiftPM runs the newest dependency manifests,
unsandboxed
+ # on Linux, so the build a consumer would get runs without a write token.
+ build-swift-package:
+ name: Build Swift package
+ needs: [validate, plan, check-tags]
+ if: |
+ needs.validate.outputs.has_targets == 'true' &&
+ needs.plan.outputs.has_swift == 'true'
+ runs-on: ubuntu-26.04
+ permissions:
+ contents: read
+ steps:
+ - name: Download latest copy script from master
+ if: inputs.use_latest_ci
+ run: |
+ curl -fsSL --retry 5 --retry-all-errors
"https://raw.githubusercontent.com/${{ github.repository
}}/master/scripts/copy-latest-from-master.sh" \
+ -o /tmp/copy-latest-from-master.sh
+ chmod +x /tmp/copy-latest-from-master.sh
+ echo "✅ Downloaded latest copy script from master"
+
+ - name: Checkout at commit
+ uses: actions/[email protected]
+ with:
+ ref: ${{ needs.validate.outputs.commit }}
+ persist-credentials: false
+
+ - name: Save and apply latest CI from master
+ if: inputs.use_latest_ci
+ run: |
+ /tmp/copy-latest-from-master.sh save \
+ .github \
+ scripts
+
+ /tmp/copy-latest-from-master.sh apply
+
+ # A cold resolve is what a consumer gets, so this build reads no cache.
+ - name: Setup Swift
+ uses: ./.github/actions/utils/setup-swift
+ with:
+ read-cache: "false"
+
+ - name: Build the published package
+ run: swift build -c release --product Iggy
+
# Sequential Rust crate publishing to handle dependencies properly
publish-rust-crates:
name: Publish Rust crates
@@ -987,17 +1035,18 @@ jobs:
Commit: ${{ needs.validate.outputs.commit }}
Released by: GitHub Actions (workflow ${{ github.run_id }})
- # Non-Docker, non-Rust publishing (Python, Node, Java, C#, Go SDKs)
+ # Non-Docker, non-Rust publishing (Python, Node, Java, C#, Go, Swift SDKs)
# Note: This job runs in parallel with Docker publishing - no dependency
between them
publish:
name: ${{ matrix.name }}
- needs: [validate, plan, check-tags, build-python-wheels,
publish-rust-crates]
+ needs: [validate, plan, check-tags, build-python-wheels,
build-swift-package, publish-rust-crates]
if: |
!cancelled() &&
needs.check-tags.result == 'success' &&
needs.validate.outputs.has_targets == 'true' &&
fromJson(needs.plan.outputs.non_docker_targets).include[0].key != 'noop'
&&
(needs.build-python-wheels.result == 'success' ||
needs.build-python-wheels.result == 'skipped') &&
+ (needs.build-swift-package.result == 'success' ||
needs.build-swift-package.result == 'skipped') &&
(needs.publish-rust-crates.result == 'success' ||
needs.publish-rust-crates.result == 'skipped')
runs-on: ubuntu-26.04
permissions:
@@ -1156,14 +1205,25 @@ jobs:
version: ${{ steps.ver.outputs.version }}
dry_run: ${{ inputs.dry_run }}
+ # ─────────────────────────────────────────
+ # Swift Package (Tag-only, root Package.swift)
+ # ─────────────────────────────────────────
+ - name: Prepare Swift tag
+ if: matrix.type == 'swift'
+ uses: ./.github/actions/swift/post-merge
+ with:
+ version: ${{ steps.ver.outputs.version }}
+ tag: ${{ steps.ver.outputs.tag }}
+ dry_run: ${{ inputs.dry_run }}
+
# ─────────────────────────────────────────
# Wait gates: do not push the git tag until the registry is actually
# serving the new version. The Rust path already does this via
# wait-for-crate; mirroring it here closes the "artifact published,
# tag never created" rc1 failure class for foreign SDKs.
#
- # Go has no registry wait: the tag IS the release (the Go proxy
- # fetches from git on demand).
+ # Go and Swift have no registry wait: the tag IS the release (the Go
+ # proxy and SwiftPM fetch from git on demand).
# ─────────────────────────────────────────
- name: Wait for PyPI availability
if: |
@@ -1265,7 +1325,7 @@ jobs:
# releases) and the "no tag_pattern" skip in one place — keeping the
# rule symmetric with the docker-manifests job above. The wait gates
# above ensure the tag is never pushed before the registry is serving
- # the artifact (Go is exempt: the tag IS the release).
+ # the artifact (Go and Swift are exempt: the tag IS the release).
- name: Tag SDK release (${{ matrix.key }})
if: |
success() &&
@@ -1298,6 +1358,7 @@ jobs:
plan,
check-tags,
build-python-wheels,
+ build-swift-package,
publish-rust-crates,
publish-docker,
docker-manifests,
@@ -1389,6 +1450,7 @@ jobs:
maven) REGISTRY_DISPLAY="Maven" ;;
nuget) REGISTRY_DISPLAY="NuGet" ;;
none) REGISTRY_DISPLAY="Tag only" ;;
+ swiftpm) REGISTRY_DISPLAY="Tag only (SwiftPM)" ;;
*) REGISTRY_DISPLAY="$REGISTRY" ;;
esac
@@ -1430,6 +1492,16 @@ jobs:
echo
fi
+ if [ "${{ needs.plan.outputs.has_swift }}" = "true" ]; then
+ echo "### Swift Package Build"
+ case "${{ needs.build-swift-package.result }}" in
+ success) echo "✅ **Swift package built from the root
manifest**" ;;
+ failure) echo "❌ **Swift package build failed**" ;;
+ skipped) echo "⏭️ **Swift package build was skipped**" ;;
+ esac
+ echo
+ fi
+
# Rust crates publishing status
if [ -n "${{ inputs.publish_crates }}" ]; then
echo "### Rust Crates Publishing (Sequential)"
@@ -1489,6 +1561,7 @@ jobs:
validate,
plan,
build-python-wheels,
+ build-swift-package,
publish-rust-crates,
publish-docker,
docker-manifests,
diff --git a/.gitignore b/.gitignore
index 944c044bc..5d46d1f38 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,6 +27,10 @@ foreign/java/**/out
foreign/node/**/dist
foreign/swift/.build
foreign/swift/.swiftpm
+# The root manifest publishes the Swift SDK. Consumers resolve their own pins.
+/.build
+/.swiftpm
+/Package.resolved
examples/swift/.build
examples/swift/**/.build
examples/swift/**/.swiftpm
diff --git a/.spi.yml b/.spi.yml
new file mode 100644
index 000000000..d6b1db253
--- /dev/null
+++ b/.spi.yml
@@ -0,0 +1,22 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Swift Package Index builds and hosts the DocC reference for the SDK.
+version: 1
+builder:
+ configs:
+ - documentation_targets: [Iggy]
diff --git a/AGENTS.md b/AGENTS.md
index 36f97a997..708a7ba45 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -3,7 +3,7 @@
**Apache Iggy** is a persistent message streaming platform
in Rust. Thread-per-core shared-nothing, `io_uring` + `compio`.
Transports: QUIC, WebSocket, TCP (custom binary), HTTP (REST). SDKs:
-Rust, .NET, Java, Python, Go, C++, Node.js. A connectors subsystem
+Rust, .NET, Java, Python, Go, C++, Node.js, Swift. A connectors subsystem
ingests from / egresses to external systems via dlopened plugins.
> Skills live under `.claude/skills/`, with per-skill links under
@@ -115,7 +115,7 @@ iggy/
│ ├── shard/ Shard runtime
│ ├── harness_derive/ #[iggy_harness] proc macro
│ └── metadata, partitions, server_common, configs_derive, clock, tools
-├── foreign/ Java / .NET / Python / Go / C++ / Node.js SDKs
+├── foreign/ Java / .NET / Python / Go / C++ / Node.js / Swift
SDKs
├── bdd/ Cross-language BDD (Gherkin)
├── examples/ Per-SDK runnable examples
├── helm/ Helm charts
diff --git a/foreign/swift/Package.swift b/Package.swift
similarity index 75%
copy from foreign/swift/Package.swift
copy to Package.swift
index 413814b1a..e73930e11 100644
--- a/foreign/swift/Package.swift
+++ b/Package.swift
@@ -16,10 +16,12 @@
// specific language governing permissions and limitations
// under the License.
+// SwiftPM resolves packages only from a repository root. CI keeps this in
sync with foreign/swift/Package.swift.
+
import PackageDescription
let package = Package(
- name: "apache-iggy",
+ name: "iggy",
platforms: [
.macOS(.v13),
.iOS(.v16),
@@ -34,7 +36,7 @@ let package = Package(
.package(url: "https://github.com/apple/swift-nio.git", from:
"2.80.0"),
.package(url: "https://github.com/apple/swift-nio-ssl.git", from:
"2.29.0"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.6.0"),
- .package(url: "https://github.com/apple/swift-crypto.git",
"3.0.0"..<"4.0.0"),
+ .package(url: "https://github.com/apple/swift-crypto.git",
"3.0.0"..<"6.0.0"),
],
targets: [
.target(
@@ -47,27 +49,9 @@ let package = Package(
.product(name: "NIOSSL", package: "swift-nio-ssl"),
.product(name: "Logging", package: "swift-log"),
.product(name: "Crypto", package: "swift-crypto"),
- ]
- ),
- .testTarget(
- name: "IggyTests",
- dependencies: [
- "Iggy",
- .product(name: "NIOCore", package: "swift-nio"),
- .product(name: "NIOPosix", package: "swift-nio"),
- .product(name: "Logging", package: "swift-log"),
],
- resources: [.copy("Fixtures")]
- ),
- .testTarget(
- name: "IggyE2ETests",
- dependencies: ["Iggy"]
- ),
- .executableTarget(
- name: "reader-example",
- dependencies: ["Iggy"],
- path: "Examples/Reader"
- ),
+ path: "foreign/swift/Sources/Iggy"
+ )
],
swiftLanguageModes: [.v6]
)
diff --git a/foreign/swift/LICENSE b/foreign/swift/LICENSE
new file mode 100644
index 000000000..8dada3eda
--- /dev/null
+++ b/foreign/swift/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/foreign/swift/NOTICE b/foreign/swift/NOTICE
new file mode 100644
index 000000000..346bbd9c1
--- /dev/null
+++ b/foreign/swift/NOTICE
@@ -0,0 +1,12 @@
+Apache Iggy
+Copyright 2026 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+================================================================
+
+The Iggy project code was originally created, designed, developed by Piotr
Gankiewicz in April 2023.
+It was released as an open-source project under MIT License, later converted
to Apache 2.0 License,
+and donated by LaserData, Inc. to the Apache Software Foundation (ASF) in
February 2025.
+Copyright April 2023 - February 2025 Piotr Gankiewicz, LaserData, Inc.
diff --git a/foreign/swift/Package.swift b/foreign/swift/Package.swift
index 413814b1a..eae3d7120 100644
--- a/foreign/swift/Package.swift
+++ b/foreign/swift/Package.swift
@@ -34,7 +34,7 @@ let package = Package(
.package(url: "https://github.com/apple/swift-nio.git", from:
"2.80.0"),
.package(url: "https://github.com/apple/swift-nio-ssl.git", from:
"2.29.0"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.6.0"),
- .package(url: "https://github.com/apple/swift-crypto.git",
"3.0.0"..<"4.0.0"),
+ .package(url: "https://github.com/apple/swift-crypto.git",
"3.0.0"..<"6.0.0"),
],
targets: [
.target(
diff --git a/foreign/swift/README.md b/foreign/swift/README.md
index a9d604000..e804516ef 100644
--- a/foreign/swift/README.md
+++ b/foreign/swift/README.md
@@ -36,28 +36,32 @@ The SDK depends on
[SwiftNIO](https://github.com/apple/swift-nio),
## Installation
-Swift Package Manager fetches a package from the root of a git repository, so
the SDK
-cannot be depended on straight from this monorepo's subdirectory. Add it
through the
-package mirror, which carries every release as a `v<version>` tag:
+Swift Package Manager resolves a package from the root of a git repository, so
the Iggy repository root carries a `Package.swift` that publishes this SDK.
Depend on the Iggy repository itself.
+
+There is no stable Swift release yet. Until there is, pin a development
snapshot exactly, replacing `0.1.10-edge.1` with the newest `vX.Y.Z-edge.N` tag:
```swift
// Package.swift
dependencies: [
- .package(url: "https://github.com/apache/iggy-swift.git", from: "0.1.1")
+ .package(url: "https://github.com/apache/iggy.git", exact: "0.1.10-edge.1")
],
targets: [
- .target(name: "MyApp", dependencies: [.product(name: "Iggy", package:
"iggy-swift")])
+ .target(name: "MyApp", dependencies: [.product(name: "Iggy", package:
"iggy")])
]
```
-From a checkout of this repository, or a vendored copy, depend on it by path:
+Stable Swift releases use `vX.Y.Z` tags after PMC approval. An `X.Y.Z-edge.N`
tag is a development snapshot of unreleased changes, not an Apache release, and
a version range never selects one. Once a stable release exists, depend on it
with `.upToNextMinor(from: "X.Y.Z")`: while the SDK is 0.x, `from:` would also
take the next minor release. A requirement must start at the first stable
`v0.1.x` tag, because the repository's `0.0.4` to `0.0.52` tags predate the
Swift SDK and have no root [...]
+
+The first resolve mirror-clones the whole Iggy repository, about 145 MiB with
every pull request ref, and checks out its 67 MiB tree for a 0.8 MiB SDK.
SwiftPM keeps the clone in its cache, so later resolves fetch only new commits.
+
+From a checkout of this repository, depend on its root by path. SwiftPM names
a path dependency after its directory, so the package is `iggy` for a checkout
in `../iggy`:
```swift
dependencies: [
- .package(name: "apache-iggy", path: "../iggy/foreign/swift")
+ .package(path: "../iggy")
],
targets: [
- .target(name: "MyApp", dependencies: [.product(name: "Iggy", package:
"apache-iggy")])
+ .target(name: "MyApp", dependencies: [.product(name: "Iggy", package:
"iggy")])
]
```
@@ -537,10 +541,23 @@ swift format lint --strict --recursive Sources Tests
Package.swift
swift build -Xswiftc -warnings-as-errors
```
-The same configuration covers the examples and the BDD runner, from the
repository root:
+The same configuration covers the examples, the BDD runner and the root
manifest, from the repository root:
```bash
for package in examples/swift/getting-started bdd/swift/tests; do
swift format lint --strict --recursive --configuration
foreign/swift/.swift-format "$package/Sources" "$package/Package.swift"
done
+swift format lint --strict --configuration foreign/swift/.swift-format
Package.swift
```
+
+### Releasing
+
+`IggyVersion.sdkVersion` in `Sources/Iggy/Iggy.swift` is the SDK version.
Change it only with `scripts/bump-version.sh sdk-swift` from the repository
root, which also regenerates the golden vectors that embed it: `--edge` for the
next development snapshot, `--patch --edge` for the first snapshot after a
stable release, `--strip-edge` for the version that goes to a release vote.
+
+After a merge to master, CI tags an untagged `X.Y.Z-edge.N` version as
`vX.Y.Z-edge.N`.
+
+For a stable release, the PMC vote must cover the Swift version and source
archive, including the root manifest. The release manager verifies the vote
result and publishes the signed source release through Apache's release
process. The workflow does not verify the vote automatically. Merging a plain
`X.Y.Z` version to master does not publish a stable Swift tag.
+
+After that, run the Publish workflow on the voted commit with
`publish_other=swift` and `create_edge_docker_tag=false`. Run with
`dry_run=true` first, then `dry_run=false` and `skip_tag_creation=false` to
publish the tag.
+
+The root `Package.swift` declares only the library. Its tools version,
platforms, dependencies and `Iggy` target must match this package's manifest,
and CI fails when they differ.
diff --git a/foreign/swift/Sources/Iggy/Iggy.swift
b/foreign/swift/Sources/Iggy/Iggy.swift
index 3addcb8ad..eef3218f7 100644
--- a/foreign/swift/Sources/Iggy/Iggy.swift
+++ b/foreign/swift/Sources/Iggy/Iggy.swift
@@ -19,7 +19,6 @@
public enum IggyVersion {
/// Name of this SDK as reported to the server.
public static let sdkName = "swift-sdk"
- /// Version of this SDK as reported to the server. Provisional until the
- /// release wiring lands; the release tooling reads this constant.
- public static let sdkVersion = "0.1.9"
+ /// Version of this SDK as reported to the server and named by its
`v<version>` release tag.
+ public static let sdkVersion = "0.1.10-edge.1"
}
diff --git a/foreign/swift/Tests/IggyTests/Fixtures/golden.json
b/foreign/swift/Tests/IggyTests/Fixtures/golden.json
index 579d35918..c27aefec0 100644
--- a/foreign/swift/Tests/IggyTests/Fixtures/golden.json
+++ b/foreign/swift/Tests/IggyTests/Fixtures/golden.json
@@ -935,7 +935,7 @@
"polling.next": "050000000000000000",
"polling.offset.100": "016400000000000000",
"polling.timestamp.1700000000000": "020068e5cf8b010000",
- "request.bind_session":
"023000000973776966742d73646b05302e312e3901000000000000000000000000000000640000000000000078000000000000005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
+ "request.bind_session":
"023000000973776966742d73646b0d302e312e31302d656467652e3101000000000000000000000000000000640000000000000078000000000000005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
"request.change_password":
"010401000000086f6c642d706173730c6e65772d706173732d313233",
"request.create_consumer_group": "01040100000001040200000003677270",
"request.create_partitions": "01040100000001040200000005000000",
@@ -976,8 +976,8 @@
"request.get_users": "",
"request.join_consumer_group": "010401000000010402000000010403000000",
"request.leave_consumer_group": "010401000000010402000000020167",
- "request.login_register":
"023000000973776966742d73646b05302e312e395a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a0469676779046967677900000000",
- "request.login_register_with_pat":
"023000000973776966742d73646b05302e312e395a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a107061742d61626331323364656634353600000000",
+ "request.login_register":
"023000000973776966742d73646b0d302e312e31302d656467652e315a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a0469676779046967677900000000",
+ "request.login_register_with_pat":
"023000000973776966742d73646b0d302e312e31302d656467652e315a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a107061742d61626331323364656634353600000000",
"request.logout_user": "",
"request.ping": "",
"request.poll_messages":
"0101040100000001040a00000001041400000001050000000164000000000000003200000001",
@@ -1032,7 +1032,7 @@
"response.user_details.no_permissions":
"0100000000e090663c130600010561646d696e000000000000000000000000",
"stream_key.fingerprint": "8441e38538cbfa57",
"user_headers.sample":
"0207000000617474656d70740b0400000003000000020800000074726163652d69640203000000616263",
- "version_info": "023000000973776966742d73646b05302e312e39",
+ "version_info": "023000000973776966742d73646b0d302e312e31302d656467652e31",
"vsr.eviction.incompatible_protocol":
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001032547698badcfeefcdab896745230102300000023000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
[...]
"vsr.eviction.stale_client":
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001032547698badcfeefcdab896745230100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
[...]
"vsr.reply.committed":
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0100000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001032547698badcfeefcdab896745230100000000000000000c0000000000000000000000000000000700000000000000800000000000000000000000010000000000000000000000
[...]
diff --git a/scripts/bump-version.sh b/scripts/bump-version.sh
index 0ef47a5d9..08dee36ef 100755
--- a/scripts/bump-version.sh
+++ b/scripts/bump-version.sh
@@ -26,6 +26,10 @@ YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
+SWIFT_VERSION_FILE="foreign/swift/Sources/Iggy/Iggy.swift"
+SWIFT_GOLDEN_GENERATOR="foreign/swift/Tools/golden-vectors/Cargo.toml"
+SWIFT_GOLDEN_FIXTURE="foreign/swift/Tests/IggyTests/Fixtures/golden.json"
+
usage() {
cat <<'EOF'
Usage:
@@ -58,6 +62,7 @@ Components:
sdk-go foreign/go/contracts/version.go
sdk-csharp foreign/csharp/Iggy_SDK/Iggy_SDK.csproj
sdk-java foreign/java/gradle.properties
+ sdk-swift foreign/swift/Sources/Iggy/Iggy.swift + regenerated
golden vectors
web-ui web/package.json
Version flags (exactly one required):
@@ -91,11 +96,11 @@ RUST_COMPONENTS="rust-sdk rust-common rust-binary-protocol
rust-server rust-cli
CONNECTOR_SINK_COMPONENTS="rust-connector-clickhouse-sink
rust-connector-delta-sink rust-connector-doris-sink
rust-connector-elasticsearch-sink rust-connector-http-sink
rust-connector-iceberg-sink rust-connector-influxdb-sink
rust-connector-meilisearch-sink rust-connector-mongodb-sink
rust-connector-postgres-sink rust-connector-quickwit-sink
rust-connector-rabbitmq-sink rust-connector-redshift-sink
rust-connector-s3-sink rust-connector-stdout-sink rust-connector-surrealdb-sink"
CONNECTOR_SOURCE_COMPONENTS="rust-connector-elasticsearch-source
rust-connector-http-source rust-connector-influxdb-source
rust-connector-postgres-source rust-connector-random-source"
CONNECTOR_COMPONENTS="rust-connector-runtime ${CONNECTOR_SINK_COMPONENTS}
${CONNECTOR_SOURCE_COMPONENTS}"
-SDK_COMPONENTS="sdk-python sdk-node sdk-go sdk-csharp sdk-java"
+SDK_COMPONENTS="sdk-python sdk-node sdk-go sdk-csharp sdk-java sdk-swift"
ALL_COMPONENTS="${RUST_COMPONENTS} ${CONNECTOR_COMPONENTS} ${SDK_COMPONENTS}
web-ui"
# Returns "file:format" lines per component.
-# Format keys: cargo, cargo-ws-dep:PKG, cargo-dep:PKG, python-cargo,
pyproject, json, csproj, gradle, go
+# Format keys: cargo, cargo-ws-dep:PKG, cargo-dep:PKG, python-cargo,
pyproject, json, csproj, gradle, go, swift
get_version_files() {
local component="$1"
case "$component" in
@@ -168,6 +173,9 @@ get_version_files() {
sdk-java)
echo "foreign/java/gradle.properties:gradle"
;;
+ sdk-swift)
+ echo "${SWIFT_VERSION_FILE}:swift"
+ ;;
web-ui)
echo "web/package.json:json"
;;
@@ -249,7 +257,7 @@ translate_version() {
read -r base pre_type pre_num <<< "$parsed"
case "$format" in
- cargo|cargo-ws-dep:*|cargo-dep:*|json|csproj|go)
+ cargo|cargo-ws-dep:*|cargo-dep:*|json|csproj|go|swift)
echo "$canonical" ;;
python-cargo)
case "$pre_type" in
@@ -276,7 +284,7 @@ translate_version() {
canonicalize_version() {
local raw="$1" format="$2"
case "$format" in
- cargo|cargo-ws-dep:*|cargo-dep:*|json|csproj|go)
+ cargo|cargo-ws-dep:*|cargo-dep:*|json|csproj|go|swift)
echo "$raw" ;;
python-cargo)
# Handle both old (0.7.2-dev.1) and new (0.7.2-dev1) formats
@@ -331,6 +339,8 @@ read_current_version() {
grep '^version=' "$abs_file" | head -1 | sed 's/version=//' ;;
go)
grep 'const Version' "$abs_file" | head -1 | sed 's/.*const
Version = "\([^"]*\)".*/\1/' ;;
+ swift)
+ grep 'static let sdkVersion = ' "$abs_file" | head -1 | sed
's/.*sdkVersion = "\([^"]*\)".*/\1/' ;;
*)
echo -e "${RED}Unknown format: ${format}${NC}" >&2
return 1 ;;
@@ -375,6 +385,8 @@ write_version() {
sedi "s/^version=.*/version=${translated}/" "$abs_file" ;;
go)
sedi "1,/const Version = \"[^\"]*\"/{s/const Version =
\"[^\"]*\"/const Version = \"${translated}\"/;}" "$abs_file" ;;
+ swift)
+ sedi "s/\(static let sdkVersion = \"\)[^\"]*\"/\1${translated}\"/"
"$abs_file" ;;
*)
echo -e "${RED}Unknown format: ${format}${NC}" >&2
return 1 ;;
@@ -563,6 +575,11 @@ cmd_bump() {
echo -e "${CYAN}New:${NC} ${new_ver}"
echo ""
+ if [[ "$component" == "sdk-swift" ]] && ! command -v cargo > /dev/null
2>&1; then
+ echo -e "${RED}sdk-swift needs cargo to regenerate
${SWIFT_GOLDEN_FIXTURE}${NC}" >&2
+ return 1
+ fi
+
local changed_files=()
while IFS= read -r entry; do
local file="${entry%%:*}"
@@ -589,6 +606,22 @@ cmd_bump() {
fi
done < <(get_version_files "$component")
+ # Swift golden vectors embed the SDK version. A failed generator run
leaves the fixture untouched.
+ if [[ "$component" == "sdk-swift" && ${#changed_files[@]} -gt 0 ]]; then
+ printf " %-55s %s\n" "$SWIFT_GOLDEN_FIXTURE" "regenerated"
+ changed_files+=("$SWIFT_GOLDEN_FIXTURE")
+ if [[ $apply -eq 1 ]]; then
+ trap 'write_version "$SWIFT_VERSION_FILE" swift "$current"; exit
130' INT TERM
+ if ! cargo run --quiet --manifest-path
"${REPO_ROOT}/${SWIFT_GOLDEN_GENERATOR}" --
"${REPO_ROOT}/${SWIFT_GOLDEN_FIXTURE}"; then
+ trap - INT TERM
+ write_version "$SWIFT_VERSION_FILE" swift "$current"
+ echo -e "${RED}Golden vector generation failed, restored
${SWIFT_VERSION_FILE}${NC}" >&2
+ return 1
+ fi
+ trap - INT TERM
+ fi
+ fi
+
echo ""
if [[ $apply -eq 1 ]]; then
echo -e "${GREEN}Applied.${NC}"
diff --git a/scripts/prepare-release.sh b/scripts/prepare-release.sh
index 835aaf962..7fd5f915f 100755
--- a/scripts/prepare-release.sh
+++ b/scripts/prepare-release.sh
@@ -52,6 +52,7 @@ RELEASE_PATHS=(
"Dockerfile"
"LICENSE"
"NOTICE"
+ "Package.swift"
"about.hbs"
"about.toml"
"docker-compose.yml"