Build Plugin #294
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build Plugin | |
| on: | |
| push: | |
| tags: ["plugin-*-v*"] | |
| workflow_dispatch: | |
| inputs: | |
| tags: | |
| description: > | |
| Comma-separated plugin tag:pluginKitVersion pairs. | |
| pluginKitVersion defaults to currentPluginKitVersion from PluginManager.swift. | |
| Examples: plugin-mongodb-v1.0.25, plugin-mongodb-v1.0.25:13 | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| env: | |
| XCODE_PROJECT: TablePro.xcodeproj | |
| jobs: | |
| resolve-tags: | |
| name: Resolve Plugin Tags | |
| runs-on: ubuntu-latest | |
| outputs: | |
| matrix: ${{ steps.tags.outputs.matrix }} | |
| currentPluginKitVersion: ${{ steps.pkv.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Validate registry update script | |
| run: python3 .github/scripts/test_update_registry.py | |
| - name: Read currentPluginKitVersion | |
| id: pkv | |
| run: | | |
| VERSION=$(grep -E 'static let currentPluginKitVersion\s*=\s*[0-9]+' \ | |
| TablePro/Core/Plugins/PluginManager.swift \ | |
| | grep -oE '[0-9]+$' | head -1) | |
| if [ -z "$VERSION" ]; then | |
| echo "::error::Could not parse currentPluginKitVersion from PluginManager.swift" | |
| exit 1 | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "currentPluginKitVersion=$VERSION" | |
| - name: Build matrix | |
| id: tags | |
| env: | |
| DEFAULT_PKV: ${{ steps.pkv.outputs.version }} | |
| INPUT_TAGS: ${{ inputs.tags }} | |
| REF_NAME: ${{ github.ref_name }} | |
| run: | | |
| if [ -n "$INPUT_TAGS" ]; then | |
| IFS=',' read -ra RAW_TAGS <<< "$INPUT_TAGS" | |
| else | |
| RAW_TAGS=("$REF_NAME") | |
| fi | |
| JSON='{"include":[' | |
| FIRST=true | |
| for ITEM in "${RAW_TAGS[@]}"; do | |
| ITEM=$(echo "$ITEM" | xargs) | |
| TAG=$(echo "$ITEM" | cut -d: -f1) | |
| PKV=$(echo "$ITEM" | cut -d: -f2 -s) | |
| if [ -z "$PKV" ]; then | |
| PKV="$DEFAULT_PKV" | |
| fi | |
| if [ "$FIRST" = true ]; then FIRST=false; else JSON+=','; fi | |
| JSON+="{\"tag\":\"$TAG\",\"pluginKitVersion\":$PKV}" | |
| done | |
| JSON+=']}' | |
| echo "matrix=$JSON" >> "$GITHUB_OUTPUT" | |
| echo "Matrix: $JSON" | |
| build-plugin: | |
| name: "Build ${{ matrix.tag }} (PluginKit ${{ matrix.pluginKitVersion }})" | |
| needs: resolve-tags | |
| runs-on: macos-26 | |
| timeout-minutes: 30 | |
| strategy: | |
| matrix: ${{ fromJson(needs.resolve-tags.outputs.matrix) }} | |
| fail-fast: false | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| lfs: true | |
| - name: Pull LFS files | |
| run: git lfs pull | |
| - name: Select Xcode | |
| uses: maxim-lobanov/setup-xcode@v1 | |
| with: | |
| xcode-version: "26.4.1" | |
| - name: Download static libraries | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: scripts/download-libs.sh | |
| - name: Import signing certificate | |
| env: | |
| CERTIFICATES_P12: ${{ secrets.CERTIFICATES_P12 }} | |
| CERTIFICATES_PASSWORD: ${{ secrets.CERTIFICATES_PASSWORD }} | |
| run: | | |
| KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db | |
| security create-keychain -p "" "$KEYCHAIN_PATH" | |
| security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" | |
| security unlock-keychain -p "" "$KEYCHAIN_PATH" | |
| echo "$CERTIFICATES_P12" | base64 --decode > $RUNNER_TEMP/certificate.p12 | |
| security import $RUNNER_TEMP/certificate.p12 -P "$CERTIFICATES_PASSWORD" \ | |
| -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH" | |
| security set-key-partition-list -S apple-tool:,apple: -k "" "$KEYCHAIN_PATH" | |
| security list-keychain -d user -s "$KEYCHAIN_PATH" login.keychain | |
| - name: Configure notarization | |
| env: | |
| APPLE_ID: ${{ secrets.APPLE_ID }} | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| NOTARY_PASSWORD: ${{ secrets.NOTARY_PASSWORD }} | |
| run: | | |
| xcrun notarytool store-credentials "TablePro" \ | |
| --apple-id "$APPLE_ID" \ | |
| --team-id "$APPLE_TEAM_ID" \ | |
| --password "$NOTARY_PASSWORD" | |
| - name: Resolve plugin info | |
| id: plugin | |
| env: | |
| MATRIX_TAG: ${{ matrix.tag }} | |
| run: | | |
| TAG="$MATRIX_TAG" | |
| PLUGIN_NAME=$(echo "$TAG" | sed -E 's/^plugin-([a-z0-9-]+)-v([0-9].*)$/\1/') | |
| VERSION=$(echo "$TAG" | sed -E 's/^plugin-([a-z0-9-]+)-v([0-9].*)$/\2/') | |
| case "$PLUGIN_NAME" in | |
| oracle) | |
| TARGET="OracleDriver"; BUNDLE_ID="com.TablePro.OracleDriver" | |
| DISPLAY_NAME="Oracle Driver"; SUMMARY="Oracle Database 12c+ driver via OracleNIO" | |
| DB_TYPE_IDS='["Oracle"]'; ICON="server.rack"; BUNDLE_NAME="OracleDriver" | |
| CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/oracle" ;; | |
| clickhouse) | |
| TARGET="ClickHouseDriver"; BUNDLE_ID="com.TablePro.ClickHouseDriver" | |
| DISPLAY_NAME="ClickHouse Driver"; SUMMARY="ClickHouse OLAP database driver via HTTP interface" | |
| DB_TYPE_IDS='["ClickHouse"]'; ICON="chart.bar.xaxis"; BUNDLE_NAME="ClickHouseDriver" | |
| CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/clickhouse" ;; | |
| sqlite) | |
| TARGET="SQLiteDriver"; BUNDLE_ID="com.TablePro.SQLiteDriver" | |
| DISPLAY_NAME="SQLite Driver"; SUMMARY="SQLite embedded database driver" | |
| DB_TYPE_IDS='["SQLite"]'; ICON="internaldrive"; BUNDLE_NAME="SQLiteDriver" | |
| CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/sqlite" ;; | |
| duckdb) | |
| TARGET="DuckDBDriver"; BUNDLE_ID="com.TablePro.DuckDBDriver" | |
| DISPLAY_NAME="DuckDB Driver"; SUMMARY="DuckDB analytical database driver" | |
| DB_TYPE_IDS='["DuckDB"]'; ICON="bird"; BUNDLE_NAME="DuckDBDriver" | |
| CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/duckdb" ;; | |
| cassandra) | |
| TARGET="CassandraDriver"; BUNDLE_ID="com.TablePro.CassandraDriver" | |
| DISPLAY_NAME="Cassandra Driver"; SUMMARY="Apache Cassandra and ScyllaDB driver via DataStax C driver" | |
| DB_TYPE_IDS='["Cassandra","ScyllaDB"]'; ICON="cassandra-icon"; BUNDLE_NAME="CassandraDriver" | |
| CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/cassandra" ;; | |
| etcd) | |
| TARGET="EtcdDriverPlugin"; BUNDLE_ID="com.TablePro.EtcdDriverPlugin" | |
| DISPLAY_NAME="etcd Driver"; SUMMARY="etcd v3 key-value store driver with prefix-tree browsing and lease management" | |
| DB_TYPE_IDS='["etcd"]'; ICON="etcd-icon"; BUNDLE_NAME="EtcdDriverPlugin" | |
| CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/etcd" ;; | |
| mssql) | |
| TARGET="MSSQLDriver"; BUNDLE_ID="com.TablePro.MSSQLDriver" | |
| DISPLAY_NAME="MSSQL Driver"; SUMMARY="Microsoft SQL Server driver via FreeTDS" | |
| DB_TYPE_IDS='["SQL Server"]'; ICON="mssql-icon"; BUNDLE_NAME="MSSQLDriver" | |
| CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/mssql" ;; | |
| mongodb) | |
| TARGET="MongoDBDriver"; BUNDLE_ID="com.TablePro.MongoDBDriver" | |
| DISPLAY_NAME="MongoDB Driver"; SUMMARY="MongoDB document database driver via libmongoc" | |
| DB_TYPE_IDS='["MongoDB"]'; ICON="mongodb-icon"; BUNDLE_NAME="MongoDBDriver" | |
| CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/mongodb" ;; | |
| redis) | |
| TARGET="RedisDriver"; BUNDLE_ID="com.TablePro.RedisDriver" | |
| DISPLAY_NAME="Redis Driver"; SUMMARY="Redis in-memory data store driver via hiredis" | |
| DB_TYPE_IDS='["Redis"]'; ICON="redis-icon"; BUNDLE_NAME="RedisDriver" | |
| CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/redis" ;; | |
| cloudflare-d1) | |
| TARGET="CloudflareD1DriverPlugin"; BUNDLE_ID="com.TablePro.CloudflareD1DriverPlugin" | |
| DISPLAY_NAME="Cloudflare D1 Driver"; SUMMARY="Cloudflare D1 serverless SQLite-compatible database driver via REST API" | |
| DB_TYPE_IDS='["Cloudflare D1"]'; ICON="cloudflare-d1-icon"; BUNDLE_NAME="CloudflareD1DriverPlugin" | |
| CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/cloudflare-d1" ;; | |
| libsql) | |
| TARGET="LibSQLDriverPlugin"; BUNDLE_ID="com.TablePro.LibSQLDriverPlugin" | |
| DISPLAY_NAME="libSQL / Turso Driver"; SUMMARY="libSQL and Turso database support via Hrana HTTP protocol" | |
| DB_TYPE_IDS='["libSQL","Turso"]'; ICON="libsql-icon"; BUNDLE_NAME="LibSQLDriverPlugin" | |
| CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/libsql" ;; | |
| dynamodb) | |
| TARGET="DynamoDBDriverPlugin"; BUNDLE_ID="com.TablePro.DynamoDBDriverPlugin" | |
| DISPLAY_NAME="DynamoDB Driver"; SUMMARY="Amazon DynamoDB driver with PartiQL queries and AWS IAM/Profile/SSO authentication" | |
| DB_TYPE_IDS='["DynamoDB"]'; ICON="dynamodb-icon"; BUNDLE_NAME="DynamoDBDriverPlugin" | |
| CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/dynamodb" ;; | |
| bigquery) | |
| TARGET="BigQueryDriverPlugin"; BUNDLE_ID="com.TablePro.BigQueryDriverPlugin" | |
| DISPLAY_NAME="BigQuery Driver"; SUMMARY="Google BigQuery analytics database driver via REST API" | |
| DB_TYPE_IDS='["BigQuery"]'; ICON="bigquery-icon"; BUNDLE_NAME="BigQueryDriverPlugin" | |
| CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/bigquery" ;; | |
| snowflake) | |
| TARGET="SnowflakeDriverPlugin"; BUNDLE_ID="com.TablePro.SnowflakeDriverPlugin" | |
| DISPLAY_NAME="Snowflake Driver"; SUMMARY="Snowflake cloud data warehouse driver via the connector REST protocol" | |
| DB_TYPE_IDS='["Snowflake"]'; ICON="snowflake-icon"; BUNDLE_NAME="SnowflakeDriverPlugin" | |
| CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/snowflake" ;; | |
| xlsx) | |
| TARGET="XLSXExport"; BUNDLE_ID="com.TablePro.XLSXExportPlugin" | |
| DISPLAY_NAME="XLSX Export"; SUMMARY="Export data to Microsoft Excel XLSX format" | |
| DB_TYPE_IDS='null'; ICON="doc.richtext"; BUNDLE_NAME="XLSXExport" | |
| CATEGORY="export-format"; HOMEPAGE="https://docs.tablepro.app/features/export" ;; | |
| mql) | |
| TARGET="MQLExport"; BUNDLE_ID="com.TablePro.MQLExportPlugin" | |
| DISPLAY_NAME="MQL Export"; SUMMARY="Export MongoDB data as MQL statements" | |
| DB_TYPE_IDS='null'; ICON="doc.text"; BUNDLE_NAME="MQLExport" | |
| CATEGORY="export-format"; HOMEPAGE="https://docs.tablepro.app/features/export" ;; | |
| sqlimport) | |
| TARGET="SQLImport"; BUNDLE_ID="com.TablePro.SQLImportPlugin" | |
| DISPLAY_NAME="SQL Import"; SUMMARY="Import data from SQL dump files" | |
| DB_TYPE_IDS='null'; ICON="square.and.arrow.down"; BUNDLE_NAME="SQLImport" | |
| CATEGORY="import-format"; HOMEPAGE="https://docs.tablepro.app/features/import" ;; | |
| *) | |
| echo "::error::Unknown plugin name: $PLUGIN_NAME" | |
| exit 1 ;; | |
| esac | |
| # The app target is the only one with a real version; every plugin, | |
| # test, and framework target is pinned to MARKETING_VERSION = 1.0, | |
| # and target order in the pbxproj is not stable. | |
| MIN_APP_VERSION=$(grep -E 'MARKETING_VERSION\s*=\s*[0-9]' \ | |
| TablePro.xcodeproj/project.pbxproj \ | |
| | sed 's/.*MARKETING_VERSION = \(.*\);/\1/' | tr -d ' ' \ | |
| | grep -v '^1\.0$' | sort -u | head -1) | |
| if [ -z "$MIN_APP_VERSION" ] || [ "$MIN_APP_VERSION" = "1.0" ]; then | |
| echo "::error::Could not resolve the app MARKETING_VERSION (got '$MIN_APP_VERSION')" | |
| exit 1 | |
| fi | |
| DISTINCT_COUNT=$(grep -E 'MARKETING_VERSION\s*=\s*[0-9]' \ | |
| TablePro.xcodeproj/project.pbxproj \ | |
| | sed 's/.*MARKETING_VERSION = \(.*\);/\1/' | tr -d ' ' \ | |
| | grep -v '^1\.0$' | sort -u | wc -l | tr -d ' ') | |
| if [ "$DISTINCT_COUNT" != "1" ]; then | |
| echo "::error::Expected exactly one non-1.0 MARKETING_VERSION, found $DISTINCT_COUNT" | |
| exit 1 | |
| fi | |
| { | |
| echo "target=$TARGET" | |
| echo "bundleId=$BUNDLE_ID" | |
| echo "displayName=$DISPLAY_NAME" | |
| echo "summary=$SUMMARY" | |
| echo "dbTypeIds=$DB_TYPE_IDS" | |
| echo "icon=$ICON" | |
| echo "bundleName=$BUNDLE_NAME" | |
| echo "category=$CATEGORY" | |
| echo "homepage=$HOMEPAGE" | |
| echo "version=$VERSION" | |
| echo "minAppVersion=$MIN_APP_VERSION" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Build Cassandra dependencies | |
| if: ${{ contains(matrix.tag, 'plugin-cassandra-') }} | |
| run: ./scripts/build-cassandra.sh both | |
| - name: Build plugin binaries | |
| env: | |
| TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| run: | | |
| ./scripts/build-plugin.sh "${{ steps.plugin.outputs.target }}" arm64 "${{ steps.plugin.outputs.version }}" | |
| ./scripts/build-plugin.sh "${{ steps.plugin.outputs.target }}" x86_64 "${{ steps.plugin.outputs.version }}" | |
| - name: Verify built PluginKit version matches the release label | |
| env: | |
| MATRIX_PKV: ${{ matrix.pluginKitVersion }} | |
| run: | | |
| BUNDLE_NAME="${{ steps.plugin.outputs.bundleName }}" | |
| EXPECTED="$MATRIX_PKV" | |
| for ARCH in arm64 x86_64; do | |
| WORK=$(mktemp -d) | |
| unzip -oq "build/Plugins/${BUNDLE_NAME}-${ARCH}.zip" -d "$WORK" | |
| PLIST=$(find "$WORK" -path '*.tableplugin/Contents/Info.plist' | head -1) | |
| if [ -z "$PLIST" ]; then | |
| echo "::error::Could not find Info.plist in the built ${BUNDLE_NAME}-${ARCH} bundle." | |
| exit 1 | |
| fi | |
| ACTUAL=$(plutil -extract TableProPluginKitVersion raw "$PLIST") | |
| if [ "$ACTUAL" != "$EXPECTED" ]; then | |
| echo "::error::${BUNDLE_NAME}-${ARCH} was built for PluginKit $ACTUAL but this release is labeled PluginKit $EXPECTED. Refusing to publish a mislabeled binary. Re-release from a commit whose plugin Info.plist matches the target PluginKit version." | |
| exit 1 | |
| fi | |
| echo "Verified ${BUNDLE_NAME}-${ARCH}: built PluginKit $ACTUAL matches the release label." | |
| done | |
| - name: Read checksums | |
| id: sha | |
| run: | | |
| BUNDLE_NAME="${{ steps.plugin.outputs.bundleName }}" | |
| ARM64_SHA=$(cat "build/Plugins/${BUNDLE_NAME}-arm64.zip.sha256") | |
| X86_SHA=$(cat "build/Plugins/${BUNDLE_NAME}-x86_64.zip.sha256") | |
| { | |
| echo "arm64=$ARM64_SHA" | |
| echo "x86_64=$X86_SHA" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Notarize | |
| if: ${{ env.NOTARIZE_PLUGINS == 'true' }} | |
| run: | | |
| BUNDLE_NAME="${{ steps.plugin.outputs.bundleName }}" | |
| for zip in build/Plugins/${BUNDLE_NAME}-*.zip; do | |
| xcrun notarytool submit "$zip" --keychain-profile "TablePro" --wait | |
| done | |
| - name: Create GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| MATRIX_TAG: ${{ matrix.tag }} | |
| MATRIX_PKV: ${{ matrix.pluginKitVersion }} | |
| run: | | |
| TAG="$MATRIX_TAG" | |
| DISPLAY_NAME="${{ steps.plugin.outputs.displayName }}" | |
| VERSION="${{ steps.plugin.outputs.version }}" | |
| BUNDLE_NAME="${{ steps.plugin.outputs.bundleName }}" | |
| ARM64_SHA="${{ steps.sha.outputs.arm64 }}" | |
| X86_SHA="${{ steps.sha.outputs.x86_64 }}" | |
| PKV="$MATRIX_PKV" | |
| RELEASE_BODY="## $DISPLAY_NAME v$VERSION | |
| Plugin release for TablePro (PluginKit $PKV). | |
| ### Installation | |
| TablePro will prompt you to install this plugin automatically when you select the database type. You can also install manually via Settings > Plugins > Browse. | |
| ### SHA-256 | |
| - ARM64: \`$ARM64_SHA\` | |
| - x86_64: \`$X86_SHA\`" | |
| EXISTING_PKV=$(gh release view "$TAG" --json body --jq .body 2>/dev/null \ | |
| | grep -oiE 'PluginKit [0-9]+' | grep -oE '[0-9]+' | head -1 || true) | |
| if [ -n "$EXISTING_PKV" ] && [ "$EXISTING_PKV" != "$PKV" ]; then | |
| echo "::error::Release $TAG already exists for PluginKit $EXISTING_PKV. Refusing to overwrite it with PluginKit $PKV; publish the new ABI under a new plugin version." | |
| exit 1 | |
| fi | |
| gh release delete "$TAG" --yes 2>/dev/null || true | |
| gh release create "$TAG" \ | |
| --title "$DISPLAY_NAME v$VERSION" \ | |
| --notes "$RELEASE_BODY" \ | |
| build/Plugins/${BUNDLE_NAME}-arm64.zip \ | |
| build/Plugins/${BUNDLE_NAME}-x86_64.zip | |
| - name: Verify published assets match the PluginKit label | |
| env: | |
| MATRIX_TAG: ${{ matrix.tag }} | |
| MATRIX_PKV: ${{ matrix.pluginKitVersion }} | |
| run: | | |
| BUNDLE_NAME="${{ steps.plugin.outputs.bundleName }}" | |
| TAG="$MATRIX_TAG" | |
| PKV="$MATRIX_PKV" | |
| REPO="${{ github.repository }}" | |
| for ARCH in arm64 x86_64; do | |
| URL="https://github.com/${REPO}/releases/download/${TAG}/${BUNDLE_NAME}-${ARCH}.zip" | |
| WORK=$(mktemp -d) | |
| DOWNLOADED="" | |
| for attempt in 1 2 3 4 5; do | |
| if curl -fsSL "$URL" -o "$WORK/asset.zip"; then | |
| DOWNLOADED="yes" | |
| break | |
| fi | |
| echo "Published asset not downloadable yet (attempt $attempt/5), retrying in 5s..." | |
| sleep 5 | |
| done | |
| if [ -z "$DOWNLOADED" ]; then | |
| echo "::error::Could not download the published asset at $URL to verify its PluginKit version." | |
| exit 1 | |
| fi | |
| unzip -oq "$WORK/asset.zip" -d "$WORK" | |
| PLIST=$(find "$WORK" -path '*.tableplugin/Contents/Info.plist' | head -1) | |
| if [ -z "$PLIST" ]; then | |
| echo "::error::No .tableplugin Info.plist in the published ${BUNDLE_NAME}-${ARCH} asset." | |
| exit 1 | |
| fi | |
| ACTUAL=$(plutil -extract TableProPluginKitVersion raw "$PLIST") | |
| if [ "$ACTUAL" != "$PKV" ]; then | |
| echo "::error::Published ${BUNDLE_NAME}-${ARCH} at $URL is PluginKit $ACTUAL but the registry will record $PKV. Refusing to record a mismatched binary." | |
| exit 1 | |
| fi | |
| echo "Verified published ${BUNDLE_NAME}-${ARCH}: served PluginKit $ACTUAL matches the registry label." | |
| done | |
| - name: Update plugin registry | |
| if: ${{ env.REGISTRY_DEPLOY_KEY != '' }} | |
| env: | |
| REGISTRY_DEPLOY_KEY: ${{ secrets.REGISTRY_DEPLOY_KEY }} | |
| GH_TOKEN: ${{ github.token }} | |
| MATRIX_TAG: ${{ matrix.tag }} | |
| MATRIX_PKV: ${{ matrix.pluginKitVersion }} | |
| run: | | |
| TAG="$MATRIX_TAG" | |
| BUNDLE_NAME="${{ steps.plugin.outputs.bundleName }}" | |
| BUNDLE_ID="${{ steps.plugin.outputs.bundleId }}" | |
| DISPLAY_NAME="${{ steps.plugin.outputs.displayName }}" | |
| VERSION="${{ steps.plugin.outputs.version }}" | |
| SUMMARY="${{ steps.plugin.outputs.summary }}" | |
| DB_TYPE_IDS='${{ steps.plugin.outputs.dbTypeIds }}' | |
| MIN_APP_VERSION="${{ steps.plugin.outputs.minAppVersion }}" | |
| ICON="${{ steps.plugin.outputs.icon }}" | |
| HOMEPAGE="${{ steps.plugin.outputs.homepage }}" | |
| CATEGORY="${{ steps.plugin.outputs.category }}" | |
| ARM64_SHA="${{ steps.sha.outputs.arm64 }}" | |
| X86_SHA="${{ steps.sha.outputs.x86_64 }}" | |
| PKV="$MATRIX_PKV" | |
| REPO="${{ github.repository }}" | |
| ARM64_URL="https://github.com/${REPO}/releases/download/${TAG}/${BUNDLE_NAME}-arm64.zip" | |
| X86_64_URL="https://github.com/${REPO}/releases/download/${TAG}/${BUNDLE_NAME}-x86_64.zip" | |
| SCRIPT_PATH="$(pwd)/.github/scripts/update-registry.py" | |
| WORK=$(mktemp -d) | |
| eval "$(ssh-agent -s)" | |
| trap 'ssh-add -D 2>/dev/null || true; eval "$(ssh-agent -k)" 2>/dev/null || true' EXIT | |
| echo "$REGISTRY_DEPLOY_KEY" | ssh-add - | |
| git clone git@github.com:TableProApp/plugins.git "$WORK/registry" | |
| cd "$WORK/registry" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| MAX_RETRIES=10 | |
| for attempt in $(seq 1 $MAX_RETRIES); do | |
| echo "Registry update attempt $attempt/$MAX_RETRIES" | |
| git reset --hard origin/main | |
| git pull --rebase origin main | |
| python3 "$SCRIPT_PATH" \ | |
| --manifest plugins.json \ | |
| --id "$BUNDLE_ID" \ | |
| --name "$DISPLAY_NAME" \ | |
| --version "$VERSION" \ | |
| --summary "$SUMMARY" \ | |
| --db-type-ids "$DB_TYPE_IDS" \ | |
| --arm64-url "$ARM64_URL" \ | |
| --arm64-sha "$ARM64_SHA" \ | |
| --x86_64-url "$X86_64_URL" \ | |
| --x86_64-sha "$X86_SHA" \ | |
| --min-app-version "$MIN_APP_VERSION" \ | |
| --icon "$ICON" \ | |
| --homepage "$HOMEPAGE" \ | |
| --category "$CATEGORY" \ | |
| --plugin-kit-version "$PKV" \ | |
| --keep-kit-versions 2 | |
| git add plugins.json | |
| git commit -m "Update $DISPLAY_NAME to v$VERSION (PluginKit $PKV)" | |
| if git push; then | |
| echo "Registry updated on attempt $attempt" | |
| curl -sf "https://purge.jsdelivr.net/gh/TableProApp/plugins@main/plugins.json" >/dev/null \ | |
| && echo "Purged jsDelivr cache for plugins.json" \ | |
| || echo "::warning::jsDelivr purge request failed; the cache will refresh on its own shortly" | |
| break | |
| fi | |
| if [ "$attempt" -eq "$MAX_RETRIES" ]; then | |
| echo "::error::Failed to push registry update after $MAX_RETRIES attempts" | |
| exit 1 | |
| fi | |
| DELAY=$((2 + RANDOM % 4)) | |
| echo "Push rejected (concurrent update), retrying in ${DELAY}s..." | |
| sleep "$DELAY" | |
| done | |
| echo "$DISPLAY_NAME v$VERSION released (PluginKit $PKV)" |