Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
name: Release Build

# Build xcframework on a GitHub-hosted macOS runner, resolving Swift package
# dependencies through the curated Artifactory pull-through cache (ADR Rule 3).
# Produces a build attestation binding the artifact digest to the source commit.

on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+'

permissions:
contents: read
id-token: write
attestations: write

jobs:
build_xcframework:
runs-on: macos-26
env:
ARTIFACT_NAME: Segment.xcframework.zip
ARTIFACTORY_URL: https://twilio.jfrog.io
SWIFT_VIRTUAL_REPO: virtual-swift-thirdparty
outputs:
artifact-digest: ${{ steps.digest.outputs.sha256 }}

steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with:
xcode-version: "26"

# Exchange GitHub OIDC token for a short-lived Artifactory read token.
# The segmentio org uses provider_name "github-actions-segmentio".
# Token lands in sdk-build-readers group (read access to virtual repos).
- name: Get Artifactory token via OIDC
run: |
set -euo pipefail
OIDC_JWT=$(curl -sS "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=${ARTIFACTORY_URL}" \
-H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" | jq -r '.value')
ART_TOKEN=$(curl -sS "${ARTIFACTORY_URL}/access/api/v1/oidc/token" \
-H 'Content-Type: application/json' \
-d "{\"grant_type\":\"urn:ietf:params:oauth:grant-type:token-exchange\",
\"subject_token_type\":\"urn:ietf:params:oauth:token-type:id_token\",
\"subject_token\":\"${OIDC_JWT}\",
\"provider_name\":\"github-actions-segmentio\"}" | jq -r '.access_token')
echo "::add-mask::$ART_TOKEN"
echo "ART_TOKEN=$ART_TOKEN" >> "$GITHUB_ENV"

# Configure SPM to resolve dependencies through the Artifactory Swift
# pull-through cache instead of hitting GitHub directly.
- name: Configure SPM to resolve through Artifactory
run: |
set -euo pipefail
MIRROR_BASE="${ARTIFACTORY_URL}/artifactory/api/swift/${SWIFT_VIRTUAL_REPO}"

# Set up netrc for git authentication to Artifactory
echo "machine twilio.jfrog.io login token password ${ART_TOKEN}" > ~/.netrc
chmod 600 ~/.netrc

# Mirror each dependency through Artifactory
swift package config set-mirror \
--original-url https://github.com/segmentio/sovran-swift.git \
--mirror-url "${MIRROR_BASE}/segmentio/sovran-swift.git"
swift package config set-mirror \
--original-url https://github.com/segmentio/jsonsafeencoding-swift.git \
--mirror-url "${MIRROR_BASE}/segmentio/jsonsafeencoding-swift.git"

- name: Resolve dependencies
run: swift package resolve

# Build xcframework for all supported platforms
- name: Build xcframework
run: |
set -euo pipefail

ARCHIVE_DIR="$(pwd)/.build/archives"
XCFRAMEWORK_DIR="$(pwd)/.build/xcframework"
mkdir -p "$ARCHIVE_DIR" "$XCFRAMEWORK_DIR"

declare -a PLATFORMS=(
"iOS:generic/platform=iOS"
"iOS-Simulator:generic/platform=iOS Simulator"
"macOS:generic/platform=macOS"
"tvOS:generic/platform=tvOS"
"tvOS-Simulator:generic/platform=tvOS Simulator"
"watchOS:generic/platform=watchOS"
"watchOS-Simulator:generic/platform=watchOS Simulator"
)

FRAMEWORK_ARGS=()
for entry in "${PLATFORMS[@]}"; do
NAME="${entry%%:*}"
DEST="${entry#*:}"
ARCHIVE_PATH="$ARCHIVE_DIR/$NAME.xcarchive"

xcodebuild archive \
-scheme Segment \
-destination "$DEST" \
-archivePath "$ARCHIVE_PATH" \
-derivedDataPath .build/derived-data \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
SWIFT_SERIALIZE_DEBUGGING_OPTIONS=NO

FRAMEWORK_ARGS+=("-framework" "$ARCHIVE_PATH/Products/Library/Frameworks/Segment.framework")
done

xcodebuild -create-xcframework \
"${FRAMEWORK_ARGS[@]}" \
-output "$XCFRAMEWORK_DIR/Segment.xcframework"

cd "$XCFRAMEWORK_DIR"
ditto -c -k --sequesterRsrc --keepParent Segment.xcframework "${{ env.ARTIFACT_NAME }}"
mv "${{ env.ARTIFACT_NAME }}" "$GITHUB_WORKSPACE/"

- name: Compute artifact digest
id: digest
run: |
SHA256=$(shasum -a 256 "${{ env.ARTIFACT_NAME }}" | awk '{print $1}')
echo "sha256=$SHA256" >> "$GITHUB_OUTPUT"
echo "Artifact digest: $SHA256"

- name: Attest build provenance
uses: actions/attest-build-provenance@1c608d11d69870c2092266b3f9a6f3abbf17002c # v1.4.3
with:
subject-path: ${{ env.ARTIFACT_NAME }}

- name: Upload artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: xcframework-release
path: ${{ env.ARTIFACT_NAME }}
retention-days: 30
39 changes: 0 additions & 39 deletions .github/workflows/create_jira.yml

This file was deleted.

73 changes: 0 additions & 73 deletions .github/workflows/e2e-tests.yml

This file was deleted.

36 changes: 0 additions & 36 deletions .github/workflows/publish-e2e-cli.yml

This file was deleted.

109 changes: 109 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
name: Publish

# Verify clearance and publish the exact cleared bytes to GitHub Release.
#
# This workflow:
# 1. Downloads the built artifact from the release-build run
# 2. Recomputes its sha256 digest
# 3. Verifies the build attestation (signer-workflow pinned)
# 4. Verifies internal clearance (placeholder — not yet built by Platform)
# 5. On all checks pass: uploads to GitHub Release
# 6. The sha256 becomes the checksum in Package.swift's binaryTarget
#
# FAILS CLOSED on any mismatch.

on:
workflow_dispatch:
inputs:
version:
description: 'Version to publish (e.g., 1.9.5)'
required: true
run-id:
description: 'The release-build workflow run ID'
required: true
artifact-digest:
description: 'The sha256 digest from the release build'
required: true

permissions:
contents: write
id-token: write
attestations: write

env:
ARTIFACT_NAME: Segment.xcframework.zip
BUILD_WORKFLOW_REF: ".github/workflows/build.yml"

jobs:
verify_and_publish:
runs-on: macos-26
environment: production

steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Download release artifact
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: xcframework-release
run-id: ${{ inputs.run-id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

# Gate 1: Digest verification — detect substitution between build and publish.
- name: Verify artifact digest
run: |
set -euo pipefail
COMPUTED_DIGEST=$(shasum -a 256 "${{ env.ARTIFACT_NAME }}" | awk '{print $1}')
EXPECTED_DIGEST="${{ inputs.artifact-digest }}"

if [ "$COMPUTED_DIGEST" != "$EXPECTED_DIGEST" ]; then
echo "::error::DIGEST MISMATCH — artifact may have been tampered with"
exit 1
fi

echo "Digest verified: $COMPUTED_DIGEST"
echo "ARTIFACT_DIGEST=$COMPUTED_DIGEST" >> "$GITHUB_ENV"

# Gate 2: Build attestation — verify it was signed by the expected workflow.
- name: Verify build attestation
run: |
set -euo pipefail
gh attestation verify "${{ env.ARTIFACT_NAME }}" \
-R "${{ github.repository }}" \
--signer-workflow "${{ env.BUILD_WORKFLOW_REF }}"

# Gate 3: Internal clearance — placeholder until Platform delivers the gate.
- name: Verify internal clearance
run: |
echo "PLACEHOLDER: Internal clearance gate not yet available (PEA-1323)"
echo "Once delivered, this step will:"
echo " 1. Fetch KMS-signed clearance attestation for digest ${{ env.ARTIFACT_DIGEST }}"
echo " 2. Verify signature against the clearance KMS public key"
echo " 3. Assert clearance subject.digest.sha256 == ${{ env.ARTIFACT_DIGEST }}"
echo " 4. Assert clearance predicate.sourceCommit == ${{ github.sha }}"
echo " 5. Fail closed on any mismatch"

# All gates passed — publish to GitHub Release.
- name: Create GitHub Release
run: |
set -euo pipefail
VERSION="${{ inputs.version }}"

gh release create "$VERSION" \
"${{ env.ARTIFACT_NAME }}" \
--title "Version $VERSION" \
--notes "## Segment.xcframework ${VERSION}

Binary Swift package release.

### Consumer verification

Verify the build attestation:
\`\`\`
gh attestation verify Segment.xcframework.zip -R ${{ github.repository }} --signer-workflow ${{ env.BUILD_WORKFLOW_REF }}
\`\`\`

### Checksum (for Package.swift binaryTarget)
\`\`\`
${{ env.ARTIFACT_DIGEST }}
\`\`\`"
Loading