diff --git a/.github/workflows/docker_images_common.yaml b/.github/workflows/docker_images_common.yaml new file mode 100644 index 000000000..488fbfff9 --- /dev/null +++ b/.github/workflows/docker_images_common.yaml @@ -0,0 +1,152 @@ +name: Build and publish docker images + +on: + workflow_call: + inputs: + version: + required: true + type: string + alias_tag: + required: true + type: string + +jobs: + docker-build: + permissions: + contents: read + packages: write + strategy: + max-parallel: 5 + matrix: + include: + - platform: linux/amd64 + tag: linux-amd64 + os: ubuntu-latest + - platform: linux/arm64 + tag: linux-arm64 + os: ubuntu-24.04-arm + runs-on: ${{ matrix.os }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.SUPERPOSITION_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push production image + uses: docker/build-push-action@v6 + with: + push: true + context: . + tags: ghcr.io/${{ github.repository }}:${{ inputs.version }}-${{ matrix.tag }} + + create-manifest: + needs: docker-build + permissions: + contents: read + packages: write + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.SUPERPOSITION_TOKEN }} + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create manifest for multi-arch image + run: | + docker buildx imagetools create --tag ghcr.io/${{ github.repository }}:${{ inputs.version }} \ + ghcr.io/${{ github.repository }}:${{ inputs.version }}-linux-amd64 \ + ghcr.io/${{ github.repository }}:${{ inputs.version }}-linux-arm64 + + docker buildx imagetools create --tag ghcr.io/${{ github.repository }}:${{ inputs.alias_tag }} \ + ghcr.io/${{ github.repository }}:${{ inputs.version }}-linux-amd64 \ + ghcr.io/${{ github.repository }}:${{ inputs.version }}-linux-arm64 + + docker-demo-build: + needs: create-manifest + permissions: + contents: read + packages: write + strategy: + max-parallel: 5 + matrix: + include: + - platform: linux/amd64 + tag: linux-amd64 + os: ubuntu-latest + - platform: linux/arm64 + tag: linux-arm64 + os: ubuntu-24.04-arm + runs-on: ${{ matrix.os }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.SUPERPOSITION_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push demo image + uses: docker/build-push-action@v6 + with: + push: true + context: . + file: example.Dockerfile + tags: ghcr.io/${{ github.repository }}-demo:${{ inputs.version }}-${{ matrix.tag }} + + create-demo-manifest: + needs: docker-demo-build + permissions: + contents: read + packages: write + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.SUPERPOSITION_TOKEN }} + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create manifest for multi-arch demo image + run: | + docker buildx imagetools create --tag ghcr.io/${{ github.repository }}-demo:${{ inputs.version }} \ + ghcr.io/${{ github.repository }}-demo:${{ inputs.version }}-linux-amd64 \ + ghcr.io/${{ github.repository }}-demo:${{ inputs.version }}-linux-arm64 + + docker buildx imagetools create --tag ghcr.io/${{ github.repository }}-demo:${{ inputs.alias_tag }} \ + ghcr.io/${{ github.repository }}-demo:${{ inputs.version }}-linux-amd64 \ + ghcr.io/${{ github.repository }}-demo:${{ inputs.version }}-linux-arm64 diff --git a/.github/workflows/language_artifacts_common.yaml b/.github/workflows/language_artifacts_common.yaml new file mode 100644 index 000000000..abbcd4885 --- /dev/null +++ b/.github/workflows/language_artifacts_common.yaml @@ -0,0 +1,418 @@ +name: Build and publish language artifacts + +on: + workflow_call: + inputs: + version: + required: true + type: string + ref: + required: true + type: string + release_tag: + required: true + type: string + python_version: + required: false + default: "" + type: string + run_java: + required: false + default: true + type: boolean + run_python: + required: false + default: true + type: boolean + run_js: + required: false + default: true + type: boolean + run_rust_crates: + required: false + default: true + type: boolean + publish_java: + required: false + default: true + type: boolean + publish_python: + required: false + default: true + type: boolean + publish_js: + required: false + default: true + type: boolean + publish_rust_crates: + required: false + default: true + type: boolean + npm_dist_tag: + required: false + default: latest + type: string + create_github_release: + required: false + default: true + type: boolean + release_draft: + required: false + default: true + type: boolean + +jobs: + generate-rust-binary: + permissions: + contents: read + strategy: + max-parallel: 5 + matrix: + platform: + - os: ubuntu-22.04 + target: x86_64-unknown-linux-gnu + superposition_core_bin_name: libsuperposition_core.so + superposition_core_zip_name: superposition_core-x86_64-unknown-linux-gnu.zip + - os: macos-15-intel + target: x86_64-apple-darwin + superposition_core_bin_name: libsuperposition_core.dylib + superposition_core_zip_name: superposition_core-x86_64-apple-darwin.zip + - os: macos-latest + target: aarch64-apple-darwin + superposition_core_bin_name: libsuperposition_core.dylib + superposition_core_zip_name: superposition_core-aarch64-apple-darwin.zip + - os: windows-latest + target: x86_64-pc-windows-msvc + superposition_core_bin_name: superposition_core.dll + superposition_core_zip_name: superposition_core-x86_64-pc-windows-msvc.zip + + runs-on: ${{ matrix.platform.os }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.ref }} + + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.90.0 + targets: ${{ matrix.platform.target }} + components: rustfmt, clippy + + - name: Build and package for ${{ matrix.platform.os }} + if: matrix.platform.os != 'windows-latest' + run: | + cargo build --package superposition_core --release --target ${{ matrix.platform.target }} + mv target/${{ matrix.platform.target }}/release/${{ matrix.platform.superposition_core_bin_name }} ${{ matrix.platform.superposition_core_bin_name }} + mv target/include/superposition_core.h core.h + zip -r ${{ matrix.platform.superposition_core_zip_name }} ${{ matrix.platform.superposition_core_bin_name }} core.h + + - name: Build and package for ${{ matrix.platform.os }} on windows + if: matrix.platform.os == 'windows-latest' + run: | + cargo build --package superposition_core --release --target ${{ matrix.platform.target }} + Move-Item -Path "target\${{ matrix.platform.target }}\release\${{ matrix.platform.superposition_core_bin_name }}" -Destination "lib${{ matrix.platform.superposition_core_bin_name }}" + Move-Item -Path "target\include\superposition_core.h" -Destination "core.h" + Compress-Archive -Path "lib${{ matrix.platform.superposition_core_bin_name }}","core.h" -DestinationPath ${{ matrix.platform.superposition_core_zip_name }} + + - name: Upload superposition_core artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.platform.superposition_core_zip_name }} + path: ${{ matrix.platform.superposition_core_zip_name }} + + generate-java-packages: + needs: generate-rust-binary + permissions: + contents: read + id-token: write + if: ${{ inputs.run_java }} + runs-on: ubuntu-latest + env: + JRELEASER_GPG_SECRET_KEY: ${{ secrets.SONATYPE_MAVEN_SIGNING_KEY }} + JRELEASER_GPG_PUBLIC_KEY: ${{ vars.SONATYPE_MAVEN_SIGNING_PUB_KEY }} + JRELEASER_GPG_PASSPHRASE: ${{ secrets.SONATYPE_MAVEN_SIGNING_KEY_PASSWORD }} + JRELEASER_MAVENCENTRAL_USERNAME: ${{ secrets.SONATYPE_MAVEN_USERNAME }} + JRELEASER_MAVENCENTRAL_TOKEN: ${{ secrets.SONATYPE_MAVEN_PASSWORD }} + VERSION: ${{ inputs.version }} + defaults: + run: + working-directory: clients/java + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref }} + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: "17" + distribution: "temurin" + + - name: Download all rust binary artifacts + uses: actions/download-artifact@v4 + with: + path: clients/java/rust-binaries + pattern: superposition_core-* + merge-multiple: true + + - name: Extract and organize native libraries + run: | + mkdir -p bindings/src/main/resources + mkdir temp_extract + for zip_file in rust-binaries/*.zip; do + unzip -o "$zip_file" -d temp_extract/ + find temp_extract -name "libsuperposition_core.*" -o -name "superposition_core.*" | while read lib; do + filename=$(basename "$lib") + extension="${filename##*.}" + zip_basename=$(basename "$zip_file" .zip) + target_triple="${zip_basename#superposition_core-}" + case "$target_triple" in + "x86_64-apple-darwin") platform="darwin-x86-64" ;; + "aarch64-apple-darwin") platform="darwin-aarch64" ;; + "x86_64-unknown-linux-gnu") platform="linux-x86-64" ;; + "x86_64-pc-windows-msvc") platform="win32-x86-64" ;; + *) continue ;; + esac + target_dir="bindings/src/main/resources/$platform" + mkdir -p "$target_dir" + cp "$lib" "$target_dir/libsuperposition_core.$extension" + done + rm -rf temp_extract + done + + - name: Make gradlew executable + run: chmod +x gradlew + + - name: Build and package + run: ./gradlew clean publish + + - name: Deploy to Sonatype + if: ${{ inputs.publish_java }} + run: ./gradlew jreleaserDeploy --info --stacktrace + + generate-python-packages: + needs: generate-rust-binary + if: ${{ inputs.run_python }} + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + env: + VERSION: ${{ inputs.python_version != '' && inputs.python_version || inputs.version }} + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.ref }} + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + version: "latest" + + - name: install twine + run: | + python -m pip install --upgrade pip + pip install twine + + - name: Download all rust binary artifacts + uses: actions/download-artifact@v4 + with: + path: rust-binaries + pattern: superposition_core-* + merge-multiple: true + + - name: Extract and organize native libraries + run: | + for zip_file in rust-binaries/*.zip; do + zip_basename=$(basename "$zip_file" .zip) + target_triple="${zip_basename#superposition_core-}" + mkdir -p temp_extract/$target_triple + unzip -o "$zip_file" -d temp_extract/$target_triple/ + find temp_extract/$target_triple -name "libsuperposition_core.*" -o -name "superposition_core.*" | while read lib; do + extension="${lib##*.}" + file="libsuperposition_core-$target_triple.$extension" + cp "$lib" clients/python/bindings/superposition_bindings/$file + done + rm -rf temp_extract + done + + - name: Build bindings package + run: | + cd clients/python/bindings + uv build --wheel + + - name: Build SDK package + run: | + cd clients/python/sdk + uv build --wheel + env: + VERSION: ${{ inputs.python_version != '' && inputs.python_version || inputs.version }} + + - name: Build provider package + run: | + cd clients/python/provider + uv build --wheel + env: + VERSION: ${{ inputs.python_version != '' && inputs.python_version || inputs.version }} + + - name: Collect all wheels + run: | + mkdir -p python-wheels + cp clients/python/bindings/dist/*.whl python-wheels/ + cp clients/python/sdk/dist/*.whl python-wheels/ + cp clients/python/provider/dist/*.whl python-wheels/ + + - name: Publish Python packages to PyPI + if: ${{ inputs.publish_python }} + run: | + twine upload python-wheels/*.whl + + generate-js-packages: + needs: generate-rust-binary + if: ${{ inputs.run_js }} + permissions: + contents: read + id-token: write + runs-on: ubuntu-latest + env: + VERSION: ${{ inputs.version }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.ref }} + + - uses: actions/setup-node@v4 + with: + node-version: "24.12.0" + registry-url: "https://registry.npmjs.org" + + - name: Download all rust binary artifacts + uses: actions/download-artifact@v4 + with: + path: rust-binaries + pattern: superposition_core-* + merge-multiple: true + + - name: Extract and organize native libraries + run: | + mkdir -p clients/javascript/bindings/native-lib + for zip_file in rust-binaries/*.zip; do + zip_basename=$(basename "$zip_file" .zip) + target_triple="${zip_basename#superposition_core-}" + mkdir -p temp_extract/$target_triple + unzip -o "$zip_file" -d temp_extract/$target_triple/ + find temp_extract/$target_triple -name "libsuperposition_core.*" -o -name "superposition_core.*" | while read lib; do + extension="${lib##*.}" + file="libsuperposition_core-$target_triple.$extension" + cp "$lib" clients/javascript/bindings/native-lib/$file + done + rm -rf temp_extract + done + + - name: Publish JS packages to npm + if: ${{ inputs.publish_js }} + run: | + cd clients/javascript + npm ci + npm publish --access public --tag ${{ inputs.npm_dist_tag }} --workspace bindings --workspace sdk --workspace open-feature-provider + + - name: Build JS packages + if: ${{ !inputs.publish_js }} + run: | + cd clients/javascript + npm ci + npm run build + + generate-rust-packages: + if: ${{ inputs.run_rust_crates }} + permissions: + contents: read + id-token: write + runs-on: ubuntu-latest + env: + VERSION: ${{ inputs.version }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.ref }} + + - name: Install Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.90.0 + targets: wasm32-unknown-unknown + components: rustfmt, clippy, cargo + + - name: install cargo-edit to perform set-version + uses: baptiste0928/cargo-install@v2.2.0 + with: + crate: cargo-edit + version: "0.13.6" + + - name: Set crate versions + run: | + cargo set-version --workspace $VERSION + cargo upgrade --package superposition_types@$VERSION + cargo upgrade --package superposition_derives@$VERSION + cargo upgrade --package superposition_macros@$VERSION + cargo upgrade --package superposition_core@$VERSION + cargo upgrade --package superposition_provider@$VERSION + cargo upgrade --package superposition_sdk@$VERSION + cargo upgrade --package service_utils@$VERSION + + - name: Publish crates + if: ${{ inputs.publish_rust_crates }} + run: | + cargo login ${{ secrets.CRATES_IO_TOKEN }} + cargo publish --package superposition_derives + cargo publish --package superposition_sdk + cargo publish --package superposition_types + cargo publish --package superposition_core + cargo publish --package superposition_provider + + release: + needs: generate-rust-binary + runs-on: ubuntu-latest + permissions: + contents: write + if: ${{ inputs.create_github_release }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.ref }} + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts_dir + + - uses: softprops/action-gh-release@v2 + name: Create GitHub Release + with: + draft: ${{ inputs.release_draft }} + tag_name: ${{ inputs.release_tag }} + name: ${{ inputs.release_tag }} + files: | + artifacts_dir/superposition_core-x86_64-unknown-linux-gnu.zip/superposition_core-x86_64-unknown-linux-gnu.zip + artifacts_dir/superposition_core-x86_64-apple-darwin.zip/superposition_core-x86_64-apple-darwin.zip + artifacts_dir/superposition_core-aarch64-apple-darwin.zip/superposition_core-aarch64-apple-darwin.zip + artifacts_dir/superposition_core-x86_64-pc-windows-msvc.zip/superposition_core-x86_64-pc-windows-msvc.zip diff --git a/.github/workflows/nightly_release.yaml b/.github/workflows/nightly_release.yaml new file mode 100644 index 000000000..b5b661c2b --- /dev/null +++ b/.github/workflows/nightly_release.yaml @@ -0,0 +1,172 @@ +name: Create a superposition nightly release + +on: + schedule: + # 11:30 UTC == 17:00 Asia/Kolkata + - cron: "30 11 * * *" + workflow_dispatch: + +concurrency: + group: nightly-release-${{ github.ref }} + cancel-in-progress: true + +jobs: + prepare-nightly: + runs-on: ubuntu-latest + permissions: + contents: write + if: github.ref == 'refs/heads/main' + outputs: + should_build: ${{ steps.detect.outputs.should_build }} + merged_pr_count: ${{ steps.detect.outputs.merged_pr_count }} + version: ${{ steps.version.outputs.version }} + python_version: ${{ steps.version.outputs.python_version }} + nightly_tag: ${{ steps.version.outputs.nightly_tag }} + marker_tag: ${{ steps.version.outputs.marker_tag }} + marker_date: ${{ steps.version.outputs.marker_date }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.SUPERPOSITION_TOKEN }} + + - name: Compute nightly version and marker + id: version + shell: bash + run: | + set -euo pipefail + + git fetch --tags --force + + latest_release_tag="$(git tag -l --sort=-creatordate | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-nightly-[0-9]+)?$' | head -n 1 || true)" + latest_stable_tag="$(git tag -l --sort=-creatordate | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1 || true)" + latest_nightly_tag="$(git tag -l --sort=-creatordate | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+-nightly-[0-9]+$' | head -n 1 || true)" + + if [[ -z "$latest_release_tag" ]]; then + if [[ -n "$latest_stable_tag" ]]; then + latest_release_tag="$latest_stable_tag" + else + latest_release_tag="v0.1.0" + fi + fi + + latest_release_version="${latest_release_tag#v}" + + if [[ "$latest_release_version" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)-nightly-[0-9]+$ ]]; then + base_version="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.${BASH_REMATCH[3]}" + elif [[ "$latest_release_version" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then + next_patch=$((BASH_REMATCH[3] + 1)) + base_version="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.${next_patch}" + else + echo "Unexpected release tag format: $latest_release_tag" + exit 1 + fi + + timestamp="$(date -u +%Y%m%d%H%M%S)" + version="${base_version}-nightly-${timestamp}" + python_version="${base_version}.dev${timestamp}" + nightly_tag="v${version}" + + marker_tag="$latest_nightly_tag" + if [[ -z "$marker_tag" ]]; then + marker_tag="$latest_stable_tag" + fi + + marker_date="1970-01-01T00:00:00Z" + if [[ -n "$marker_tag" ]]; then + marker_date="$(git log -1 --format=%cI "$marker_tag")" + fi + + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "python_version=$python_version" >> "$GITHUB_OUTPUT" + echo "nightly_tag=$nightly_tag" >> "$GITHUB_OUTPUT" + echo "marker_tag=$marker_tag" >> "$GITHUB_OUTPUT" + echo "marker_date=$marker_date" >> "$GITHUB_OUTPUT" + + - name: Detect merges on main since marker + id: detect + shell: bash + run: | + set -euo pipefail + + marker_tag="${{ steps.version.outputs.marker_tag }}" + if [[ -n "$marker_tag" ]]; then + range="$marker_tag..HEAD" + else + range="HEAD" + fi + + merged_pr_count="$(git rev-list --count --first-parent "$range")" + should_build="false" + if [[ "$merged_pr_count" -gt 0 ]]; then + should_build="true" + fi + + echo "merged_pr_count=$merged_pr_count" >> "$GITHUB_OUTPUT" + echo "should_build=$should_build" >> "$GITHUB_OUTPUT" + + - name: Create and push nightly tag + if: steps.detect.outputs.should_build == 'true' + shell: bash + run: | + set -euo pipefail + + git config user.email "super_bot@juspay.in" + git config user.name "Superposition Bot" + + nightly_tag="${{ steps.version.outputs.nightly_tag }}" + if git rev-parse -q --verify "refs/tags/$nightly_tag" >/dev/null; then + echo "Tag $nightly_tag already exists" + exit 1 + fi + + git tag "$nightly_tag" + git push origin "$nightly_tag" + + - name: Nightly summary + shell: bash + run: | + echo "marker_tag=${{ steps.version.outputs.marker_tag }}" + echo "marker_date=${{ steps.version.outputs.marker_date }}" + echo "merged_pr_count=${{ steps.detect.outputs.merged_pr_count }}" + echo "nightly_version=${{ steps.version.outputs.version }}" + echo "nightly_python_version=${{ steps.version.outputs.python_version }}" + echo "should_build=${{ steps.detect.outputs.should_build }}" + + build-docker-images-nightly: + needs: prepare-nightly + if: needs.prepare-nightly.outputs.should_build == 'true' + permissions: + contents: read + packages: write + uses: ./.github/workflows/docker_images_common.yaml + with: + version: ${{ needs.prepare-nightly.outputs.version }} + alias_tag: nightly + secrets: inherit + + build-language-artifacts-nightly: + needs: prepare-nightly + if: needs.prepare-nightly.outputs.should_build == 'true' + permissions: + contents: write + id-token: write + uses: ./.github/workflows/language_artifacts_common.yaml + with: + version: ${{ needs.prepare-nightly.outputs.version }} + python_version: ${{ needs.prepare-nightly.outputs.python_version }} + ref: ${{ github.sha }} + release_tag: ${{ needs.prepare-nightly.outputs.nightly_tag }} + run_java: true + run_python: true + run_js: true + run_rust_crates: true + publish_java: true + publish_python: true + publish_js: true + publish_rust_crates: true + npm_dist_tag: nightly + create_github_release: true + release_draft: true + secrets: inherit diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e17d51e89..f4a031d55 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,4 +1,4 @@ -name: Create a superposition release +name: Create a superposition stable release on: workflow_dispatch @@ -53,7 +53,7 @@ jobs: id: git_tag shell: bash run: | - version=`git tag -l --sort=-creatordate | grep "^v" | head -n 1 | sed 's/^v//'` + version=`git tag -l --sort=-creatordate | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1 | sed 's/^v//'` echo "version=$version" >> $GITHUB_OUTPUT - name: Set the tag version to all crates @@ -92,561 +92,39 @@ jobs: git push origin --tags echo "updated_ref=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - docker-build: + build-docker-images: needs: tag-release - permissions: - contents: read - packages: write - if: github.ref == 'refs/heads/main' - strategy: - max-parallel: 5 - matrix: - include: - - platform: linux/amd64 - tag: linux-amd64 - os: ubuntu-latest - - platform: linux/arm64 - tag: linux-arm64 - os: ubuntu-24.04-arm - runs-on: ${{ matrix.os }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.SUPERPOSITION_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push production image - uses: docker/build-push-action@v6 - with: - push: true - context: . - tags: ghcr.io/${{ github.repository }}:${{ needs.tag-release.outputs.version }}-${{ matrix.tag }} - - create-manifest: - needs: [tag-release, docker-build] - permissions: - contents: read - packages: write if: github.ref == 'refs/heads/main' - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.SUPERPOSITION_TOKEN }} - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Create manifest for multi-arch image - run: | - docker buildx imagetools create --tag ghcr.io/${{ github.repository }}:${{ needs.tag-release.outputs.version }} \ - ghcr.io/${{ github.repository }}:${{ needs.tag-release.outputs.version }}-linux-amd64 \ - ghcr.io/${{ github.repository }}:${{ needs.tag-release.outputs.version }}-linux-arm64 - - docker buildx imagetools create --tag ghcr.io/${{ github.repository }}:latest \ - ghcr.io/${{ github.repository }}:${{ needs.tag-release.outputs.version }}-linux-amd64 \ - ghcr.io/${{ github.repository }}:${{ needs.tag-release.outputs.version }}-linux-arm64 - - docker-demo-build: - needs: [tag-release, create-manifest] permissions: contents: read packages: write - if: github.ref == 'refs/heads/main' - strategy: - max-parallel: 5 - matrix: - include: - - platform: linux/amd64 - tag: linux-amd64 - os: ubuntu-latest - - platform: linux/arm64 - tag: linux-arm64 - os: ubuntu-24.04-arm - runs-on: ${{ matrix.os }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.SUPERPOSITION_TOKEN }} + uses: ./.github/workflows/docker_images_common.yaml + with: + version: ${{ needs.tag-release.outputs.version }} + alias_tag: latest + secrets: inherit - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push production image - uses: docker/build-push-action@v6 - with: - push: true - context: . - file: example.Dockerfile - tags: ghcr.io/${{ github.repository }}-demo:${{ needs.tag-release.outputs.version }}-${{ matrix.tag }} - - create-demo-manifest: - needs: [tag-release, docker-demo-build] - permissions: - contents: read - packages: write - if: github.ref == 'refs/heads/main' - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.SUPERPOSITION_TOKEN }} - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Create manifest for multi-arch image - run: | - docker buildx imagetools create --tag ghcr.io/${{ github.repository }}-demo:${{ needs.tag-release.outputs.version }} \ - ghcr.io/${{ github.repository }}-demo:${{ needs.tag-release.outputs.version }}-linux-amd64 \ - ghcr.io/${{ github.repository }}-demo:${{ needs.tag-release.outputs.version }}-linux-arm64 - - docker buildx imagetools create --tag ghcr.io/${{ github.repository }}-demo:latest \ - ghcr.io/${{ github.repository }}-demo:${{ needs.tag-release.outputs.version }}-linux-amd64 \ - ghcr.io/${{ github.repository }}-demo:${{ needs.tag-release.outputs.version }}-linux-arm64 - - generate-rust-binary: + build-language-artifacts: needs: tag-release if: github.ref == 'refs/heads/main' - strategy: - max-parallel: 5 - matrix: - platform: - - os: ubuntu-22.04 - target: x86_64-unknown-linux-gnu - superposition_core_bin_name: libsuperposition_core.so - superposition_core_zip_name: superposition_core-x86_64-unknown-linux-gnu.zip - - os: macos-15-intel - target: x86_64-apple-darwin - superposition_core_bin_name: libsuperposition_core.dylib - superposition_core_zip_name: superposition_core-x86_64-apple-darwin.zip - - os: macos-latest - target: aarch64-apple-darwin - superposition_core_bin_name: libsuperposition_core.dylib - superposition_core_zip_name: superposition_core-aarch64-apple-darwin.zip - - os: windows-latest - target: x86_64-pc-windows-msvc - superposition_core_bin_name: superposition_core.dll - superposition_core_zip_name: superposition_core-x86_64-pc-windows-msvc.zip - - runs-on: ${{ matrix.platform.os }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Install Rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.90.0 - targets: ${{ matrix.platform.target }} - components: rustfmt, clippy - - - name: Build and package for ${{ matrix.platform.os }} - if: matrix.platform.os != 'windows-latest' - run: | - cargo build --package superposition_core --release --target ${{ matrix.platform.target }} - mv target/${{ matrix.platform.target }}/release/${{ matrix.platform.superposition_core_bin_name }} ${{ matrix.platform.superposition_core_bin_name }} - mv target/include/superposition_core.h core.h - zip -r ${{ matrix.platform.superposition_core_zip_name }} ${{ matrix.platform.superposition_core_bin_name }} core.h - - - name: Build and package for ${{ matrix.platform.os }} on windows - if: matrix.platform.os == 'windows-latest' - run: | - cargo build --package superposition_core --release --target ${{ matrix.platform.target }} - Move-Item -Path "target\${{ matrix.platform.target }}\release\${{ matrix.platform.superposition_core_bin_name }}" -Destination "lib${{ matrix.platform.superposition_core_bin_name }}" - Move-Item -Path "target\include\superposition_core.h" -Destination "core.h" - Compress-Archive -Path "lib${{ matrix.platform.superposition_core_bin_name }}","core.h" -DestinationPath ${{ matrix.platform.superposition_core_zip_name }} - - # Why multiple upload artifact jobs? Read: https://github.com/actions/upload-artifact/issues/331 - - - name: Upload superposition_core artifacts - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.platform.superposition_core_zip_name }} - path: ${{ matrix.platform.superposition_core_zip_name }} - - generate-java-packages: - needs: [tag-release, generate-rust-binary] - permissions: - contents: read - id-token: write - if: github.ref == 'refs/heads/main' - runs-on: ubuntu-latest - env: - JRELEASER_GPG_SECRET_KEY: ${{ secrets.SONATYPE_MAVEN_SIGNING_KEY }} - JRELEASER_GPG_PUBLIC_KEY: ${{ vars.SONATYPE_MAVEN_SIGNING_PUB_KEY }} - JRELEASER_GPG_PASSPHRASE: ${{ secrets.SONATYPE_MAVEN_SIGNING_KEY_PASSWORD }} - JRELEASER_MAVENCENTRAL_USERNAME: ${{ secrets.SONATYPE_MAVEN_USERNAME }} - JRELEASER_MAVENCENTRAL_TOKEN: ${{ secrets.SONATYPE_MAVEN_PASSWORD }} - VERSION: ${{ needs.tag-release.outputs.version }} - defaults: - run: - working-directory: clients/java - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: "17" - distribution: "temurin" - - - name: Download all rust binary artifacts - uses: actions/download-artifact@v4 - with: - path: clients/java/rust-binaries - pattern: superposition_core-* - merge-multiple: true - - - name: List downloaded artifacts - run: | - echo "Downloaded artifacts:" - ls -lar rust-binaries - - - name: Extract and organize native libraries - run: | - mkdir -p bindings/src/main/resources - mkdir temp_extract - # Extract all native libraries - for zip_file in rust-binaries/*.zip; do - echo "Extracting $zip_file" - unzip -o "$zip_file" -d temp_extract/ - - # Find and copy the native library - find temp_extract -name "libsuperposition_core.*" -o -name "superposition_core.*" | while read lib; do - filename=$(basename "$lib") - extension="${filename##*.}" - - # Determine platform from the zip file name or path - zip_basename=$(basename "$zip_file" .zip) - target_triple="${zip_basename#superposition_core-}" - - # Map target triple to JNA platform name - case "$target_triple" in - "x86_64-apple-darwin") - platform="darwin-x86-64" - ;; - "aarch64-apple-darwin") - platform="darwin-aarch64" - ;; - "x86_64-unknown-linux-gnu") - platform="linux-x86-64" - ;; - "x86_64-pc-windows-msvc") - platform="win32-x86-64" - ;; - *) - echo "Unknown target triple: $target_triple" - continue - ;; - esac - - # Create the platform-specific directory - target_dir="bindings/src/main/resources/$platform" - mkdir -p "$target_dir" - - # Copy the file - cp "$lib" "$target_dir/libsuperposition_core.$extension" - echo "Copied $filename (.$extension) to $target_dir/" - done - - # Clean up temp directory - rm -rf temp_extract - done - - # List what we have - echo "Native libraries in bindings package:" - ls -lar bindings/src/main/resources/ - - - name: Make gradlew executable - run: chmod +x gradlew - - - name: Publish - run: ./gradlew clean publish - - - name: Deploy to Sonatype - run: ./gradlew jreleaserDeploy --info --stacktrace - - generate-python-packages: - needs: [tag-release, generate-rust-binary] - if: github.ref == 'refs/heads/main' - runs-on: ubuntu-latest - permissions: - contents: read - id-token: write - env: - VERSION: ${{ needs.tag-release.outputs.version }} - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install uv - uses: astral-sh/setup-uv@v3 - with: - version: "latest" - - - name: install twine - run: | - python -m pip install --upgrade pip - pip install twine - - - name: Download all rust binary artifacts - uses: actions/download-artifact@v4 - with: - path: rust-binaries - pattern: superposition_core-* - merge-multiple: true - - - name: List downloaded artifacts - run: | - echo "Downloaded artifacts:" - find rust-binaries -type f -name "*.zip" | sort - - - name: Extract and organize native libraries - run: | - # Extract all native libraries - for zip_file in rust-binaries/*.zip; do - echo "Extracting $zip_file" - zip_basename=$(basename "$zip_file" .zip) - target_triple="${zip_basename#superposition_core-}" - mkdir -p temp_extract/$target_triple - unzip -o "$zip_file" -d temp_extract/$target_triple/ - - - # Find and copy the native library - find temp_extract/$target_triple -name "libsuperposition_core.*" -o -name "superposition_core.*" | while read lib; do - filename=$(basename "$lib") - extension="${filename##*.}" - file="libsuperposition_core-$target_triple.$extension" - cp "$lib" clients/python/bindings/superposition_bindings/$file - echo "Copied $file" - done - - # Clean up temp directory - rm -rf temp_extract - done - - # List what we have - echo "Native libraries in bindings package:" - ls -la clients/python/bindings/superposition_bindings/ - - - name: Build bindings package - run: | - cd clients/python/bindings - uv build --wheel - echo "Built bindings wheel:" - ls -la dist/ - - - name: Build SDK package - run: | - cd clients/python/sdk - uv build --wheel - echo "Built SDK wheel:" - ls -la dist/ - env: - VERSION: ${{ needs.tag-release.outputs.version }} - - - name: Build provider package - run: | - cd clients/python/provider - # Install dependencies first since provider depends on sdk and bindings - # uv pip install --system ../bindings/dist/*.whl ../sdk/dist/*.whl - uv build --wheel - echo "Built provider wheel:" - ls -la dist/ - env: - VERSION: ${{ needs.tag-release.outputs.version }} - - - name: Collect all wheels - run: | - mkdir -p python-wheels - cp clients/python/bindings/dist/*.whl python-wheels/ - cp clients/python/sdk/dist/*.whl python-wheels/ - cp clients/python/provider/dist/*.whl python-wheels/ - echo "All Python wheels:" - ls -la python-wheels/ - - - name: Publish Python packages to PyPI - run: | - twine upload python-wheels/*.whl - - generate-js-packages: - needs: [tag-release, generate-rust-binary] - if: github.ref == 'refs/heads/main' permissions: - contents: read - id-token: write - runs-on: ubuntu-latest - env: - VERSION: ${{ needs.tag-release.outputs.version }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: actions/setup-node@v4 - with: - node-version: "24.12.0" - registry-url: "https://registry.npmjs.org" - - - name: Download all rust binary artifacts - uses: actions/download-artifact@v4 - with: - path: rust-binaries - pattern: superposition_core-* - merge-multiple: true - - - name: List downloaded artifacts - run: | - echo "Downloaded artifacts:" - find rust-binaries -type f -name "*.zip" | sort - - - name: Extract and organize native libraries - run: | - # Extract all native libraries - mkdir -p clients/javascript/bindings/native-lib - for zip_file in rust-binaries/*.zip; do - echo "Extracting $zip_file" - zip_basename=$(basename "$zip_file" .zip) - target_triple="${zip_basename#superposition_core-}" - mkdir -p temp_extract/$target_triple - unzip -o "$zip_file" -d temp_extract/$target_triple/ - - - # Find and copy the native library - find temp_extract/$target_triple -name "libsuperposition_core.*" -o -name "superposition_core.*" | while read lib; do - filename=$(basename "$lib") - extension="${filename##*.}" - file="libsuperposition_core-$target_triple.$extension" - cp "$lib" clients/javascript/bindings/native-lib/$file - echo "Copied $file" - done - - # Clean up temp directory - rm -rf temp_extract - done - - # List what we have - echo "Native libraries in bindings package:" - ls -la clients/javascript/bindings/native-lib - - - run: | - cd clients/javascript - npm ci - npm publish --access public --workspace bindings --workspace sdk --workspace open-feature-provider - - generate-rust-packages: - needs: tag-release - permissions: - contents: read + contents: write id-token: write - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' - env: - VERSION: ${{ needs.tag-release.outputs.version }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ needs.tag-release.outputs.updated_ref }} - - - name: Install Rust - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.90.0 - targets: wasm32-unknown-unknown - components: rustfmt, clippy, cargo - - - name: install cargo-edit to perform set-version - uses: baptiste0928/cargo-install@v2.2.0 - with: - crate: cargo-edit - version: "0.13.6" - - - name: Build and package superposition - run: | - cargo login ${{ secrets.CRATES_IO_TOKEN }} - cargo publish --package superposition_derives - cargo publish --package superposition_sdk - cargo publish --package superposition_types - cargo publish --package superposition_core - cargo publish --package superposition_provider - - release: - needs: [tag-release, generate-rust-binary] - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: - Download all artifacts - # This step downloads all artifacts uploaded by the 'generate-builds' job. - # They will be placed into the 'artifacts_dir' directory. - uses: actions/download-artifact@v4 - with: - path: artifacts_dir - - - name: List downloaded files - run: ls -R artifacts_dir - - - uses: softprops/action-gh-release@v2 - name: Create GitHub Release - # if: startsWith(github.ref, 'refs/tags/') - with: - draft: true - files: | - artifacts_dir/superposition_core-x86_64-unknown-linux-gnu.zip/superposition_core-x86_64-unknown-linux-gnu.zip - artifacts_dir/superposition_core-x86_64-apple-darwin.zip/superposition_core-x86_64-apple-darwin.zip - artifacts_dir/superposition_core-aarch64-apple-darwin.zip/superposition_core-aarch64-apple-darwin.zip - artifacts_dir/superposition_core-x86_64-pc-windows-msvc.zip/superposition_core-x86_64-pc-windows-msvc.zip + uses: ./.github/workflows/language_artifacts_common.yaml + with: + version: ${{ needs.tag-release.outputs.version }} + ref: ${{ needs.tag-release.outputs.updated_ref }} + release_tag: v${{ needs.tag-release.outputs.version }} + python_version: ${{ needs.tag-release.outputs.version }} + run_java: true + run_python: true + run_js: true + run_rust_crates: true + publish_java: true + publish_python: true + publish_js: true + publish_rust_crates: true + npm_dist_tag: latest + create_github_release: true + release_draft: true + secrets: inherit