فهرست منبع

Add automated protoc artifact bundle generation for releases (#1835)

## Motivation

Currently the build plugin distributed from swift-protobuf and other
repos that offer protoc plugins such as grpc-swift, are hardly usable
since they require the user to install protoc and then point SwiftPM or
the IDE to its location.

## Modifications

This PR adds a new step in our release flow that bundles the upstream
protoc binaries into an artifactbundle and uploads them to the latest
draft release. It also adds to the release steps to update the
`Package.swift`

## Result

We now have an ecosystem wide distributing of protoc that can be used
directly from the package manager without requiring users to install it
manually. This makes build and command plugins that rely on protoc
vastly more useful.
Franz Busch 7 ماه پیش
والد
کامیت
70f9e85cd5
3فایلهای تغییر یافته به همراه161 افزوده شده و 1 حذف شده
  1. 104 0
      .github/scripts/upload_artifactbundle.sh
  2. 16 0
      .github/workflows/prerelease_protoc_artifactbundle.yml
  3. 41 1
      Documentation/RELEASING.md

+ 104 - 0
.github/scripts/upload_artifactbundle.sh

@@ -0,0 +1,104 @@
+#!/bin/bash
+
+# This script generates an artifactbundle for protoc. This artifactbundle
+# is used by the Swift package manger. The script is run by a GitHub action
+# when a new pre-release is created for swift-protobuf.
+
+set -eux
+
+# Fetch the latest stable release from protocolbuffers/protobuf
+AUTH="Authorization: token $GITHUB_TOKEN"
+response=$(curl -sH "$AUTH" "https://api.github.com/repos/protocolbuffers/protobuf/releases/latest")
+TAG=$(echo "$response" | grep -m 1 '"tag_name":' | cut -d '"' -f 4)
+
+# Remove 'v' prefix if present
+TAG="${TAG#v}"
+
+if [[ ! "$TAG" =~ ^[0-9]+\.[0-9]+$ ]]; then
+    echo "Error: $TAG does not match the expected pattern"
+    exit 1
+fi
+
+# Fetch all protoc release assets from protocolbuffers/protobuf
+curl -LJ --output protoc-$TAG-osx-x86_64.zip -H 'Accept: application/octet-stream' https://github.com/protocolbuffers/protobuf/releases/download/v$TAG/protoc-$TAG-osx-x86_64.zip
+curl -LJ --output protoc-$TAG-osx-aarch_64.zip -H 'Accept: application/octet-stream' https://github.com/protocolbuffers/protobuf/releases/download/v$TAG/protoc-$TAG-osx-aarch_64.zip
+curl -LJ --output protoc-$TAG-linux-aarch_64.zip -H 'Accept: application/octet-stream' https://github.com/protocolbuffers/protobuf/releases/download/v$TAG/protoc-$TAG-linux-aarch_64.zip
+curl -LJ --output protoc-$TAG-linux-x86_64.zip -H 'Accept: application/octet-stream' https://github.com/protocolbuffers/protobuf/releases/download/v$TAG/protoc-$TAG-linux-x86_64.zip
+curl -LJ --output protoc-$TAG-win64.zip -H 'Accept: application/octet-stream' https://github.com/protocolbuffers/protobuf/releases/download/v$TAG/protoc-$TAG-win64.zip
+
+# Fetch and validate license from protocolbuffers/protobuf
+curl -LJ --output LICENSE -H 'Accept: application/vnd.github.v3.raw' https://api.github.com/repos/protocolbuffers/protobuf/contents/LICENSE
+LICENSE_HASH=$(sha256sum LICENSE | cut -d ' ' -f 1)
+EXPECTED_HASH="6e5e117324afd944dcf67f36cf329843bc1a92229a8cd9bb573d7a83130fea7d"
+
+if [ "$LICENSE_HASH" != "$EXPECTED_HASH" ]; then
+    echo "Error: License file has changed. Expected hash: $EXPECTED_HASH, Got: $LICENSE_HASH"
+    exit 1
+fi
+
+# Unzip all assets
+mkdir protoc-$TAG.artifactbundle
+unzip -d protoc-$TAG.artifactbundle/protoc-$TAG-osx-x86_64 protoc-$TAG-osx-x86_64.zip
+unzip -d protoc-$TAG.artifactbundle/protoc-$TAG-osx-aarch_64 protoc-$TAG-osx-aarch_64.zip
+unzip -d protoc-$TAG.artifactbundle/protoc-$TAG-linux-aarch_64 protoc-$TAG-linux-aarch_64.zip
+unzip -d protoc-$TAG.artifactbundle/protoc-$TAG-linux-x86_64 protoc-$TAG-linux-x86_64.zip
+unzip -d protoc-$TAG.artifactbundle/protoc-$TAG-win64 protoc-$TAG-win64.zip
+
+# Copy license file into artifactbundle
+cp LICENSE protoc-$TAG.artifactbundle/
+
+# Create info.json for artifactbundle
+cat > protoc-$TAG.artifactbundle/info.json << EOF
+{
+    "schemaVersion": "1.0",
+    "artifacts": {
+        "protoc": {
+            "type": "executable",
+            "version": "$TAG",
+            "variants": [
+                {
+                    "path": "protoc-$TAG-linux-x86_64/bin/protoc",
+                    "supportedTriples": ["x86_64-unknown-linux-gnu"]
+                },
+                {
+                    "path": "protoc-$TAG-linux-aarch_64/bin/protoc",
+                    "supportedTriples": ["aarch64-unknown-linux-gnu", "arm64-unknown-linux-gnu", "aarch64-unknown-linux", "arm64-unknown-linux"]
+                },
+                {
+                    "path": "protoc-$TAG-osx-x86_64/bin/protoc",
+                    "supportedTriples": ["x86_64-apple-macosx"]
+                },
+                {
+                    "path": "protoc-$TAG-osx-aarch_64/bin/protoc",
+                    "supportedTriples": ["arm64-apple-macosx"]
+                },
+                {
+                    "path": "protoc-$TAG-win64/bin/protoc.exe",
+                    "supportedTriples": ["x86_64-unknown-windows"]
+                },
+            ]
+        }
+    }
+}
+EOF
+
+# Zip artifactbundle
+zip -r protoc-$TAG.artifactbundle.zip protoc-$TAG.artifactbundle
+
+# Get asset upload url for the latest swift-protobuf draft release
+response=$(curl -sH "$AUTH" "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/releases")
+upload_url=$(echo "$response" | jq -r '.[] | select(.draft == true) | .upload_url' | head -n 1)
+SWIFT_PROTOBUF_TAG=$(echo "$response" | jq -r '.[] | select(.draft == true) | .tag_name' | head -n 1)
+
+if [ -z "$SWIFT_PROTOBUF_TAG" ] || [ -z "$upload_url" ]; then
+    echo "Error: No draft release found"
+    exit 1
+fi
+
+# Remove the {?name,label} template from upload_url
+upload_url=$(echo "$upload_url" | sed 's/{?name,label}//')
+echo "Found draft release: $SWIFT_PROTOBUF_TAG"
+echo "Upload URL: $upload_url"
+
+# Upload asset
+curl --data-binary @protoc-$TAG.artifactbundle.zip -H "$AUTH" -H "Content-Type: application/octet-stream" "$upload_url?name=protoc-$TAG.artifactbundle.zip"

+ 16 - 0
.github/workflows/prerelease_protoc_artifactbundle.yml

@@ -0,0 +1,16 @@
+name: Upload protoc artifactbundle
+
+on:
+  workflow_dispatch:
+
+jobs:
+  upload-artifactbundle:
+    runs-on: ubuntu-latest
+    steps:
+      - name: "Checkout code"
+        uses: actions/checkout@v4
+      - name: Upload artifactbundle
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        run: cd ${{ github.workspace }} && .github/scripts/upload_artifactbundle.sh
+  

+ 41 - 1
Documentation/RELEASING.md

@@ -48,7 +48,47 @@ When doing a release:
    everything based on the PR descriptions and _semver_ tags in the repo. Just read
    though was was generate to see if any tweaks are needed.
 
-   When everything is good, click on _Publish release_.
+   **Important:** Save this as a **draft** release first (do not publish yet).
+
+1. Generate protoc artifact bundle if needed
+
+   First, check if there have been any new protoc releases since the last
+   time we bundled protoc. To do this check the binary target protoc version number
+   in the Package.swift. If there has been a new release of protoc then once you
+   have created the draft release, trigger the "Upload protoc artifactbundle" 
+   workflow from the [Actions tab](https://github.com/apple/swift-protobuf/actions/workflows/prerelease_protoc_artifactbundle.yml).
+   
+   This workflow will:
+   - Fetch the latest stable protoc release from protocolbuffers/protobuf
+   - Create a Swift Package Manager compatible artifact bundle
+   - Upload it to your draft release
+   
+   Wait for the workflow to complete successfully before proceeding.
+
+1. Update Package.swift with new artifact bundle
+
+   If there was a new protoc release and you uploaded a new artifact bundle in
+   the previous step. Create a pull request that updates the `Package.swift` file
+   to reference the new artifact bundle. You'll need to update two things:
+   
+   - **URL**: Change to point to your new release tag
+   - **Checksum**: Download the artifact bundle and calculate its SHA256 hash
+   
+   Example update:
+   ```swift
+   .binaryTarget(
+       name: "protoc",
+       url: "https://github.com/apple/swift-protobuf/releases/download/[a.b.c]/protoc-X.Y.artifactbundle.zip",
+       checksum: "new-sha256-checksum-here"
+   ),
+   ```
+   
+   To get the checksum copy it from the Github UI when looking at the draft release.bundle.zip
+
+1. Publish the release
+
+   After the Package.swift PR is merged, return to your draft release and click 
+   _Publish release_.
 
 1. Publish the `SwiftProtobuf.podspec`