mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
feat: Add Cloudflare R2 setup documentation and update CI/CD workflow for artifact uploads
This commit is contained in:
236
.github/R2_SETUP.md
vendored
Normal file
236
.github/R2_SETUP.md
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
# Cloudflare R2 Setup for CI/CD
|
||||
|
||||
This document describes how to configure Cloudflare R2 for automatic artifact uploads from GitHub Actions.
|
||||
|
||||
## Overview
|
||||
|
||||
The CI/CD pipeline uploads build artifacts (APK, AAB, DMG, Windows ZIP) to a Cloudflare R2 bucket in the `unstable/` directory for every successful build. This provides:
|
||||
|
||||
- **Automatic artifact hosting** for all branches and commits
|
||||
- **Version tracking** with timestamps and commit hashes
|
||||
- **Public download URLs** (optional) for testers and developers
|
||||
- **Build manifests** with metadata for each build
|
||||
|
||||
## Required GitHub Secrets
|
||||
|
||||
Configure these secrets in your GitHub repository settings (`Settings` → `Secrets and variables` → `Actions`):
|
||||
|
||||
### Required Secrets
|
||||
|
||||
| Secret Name | Description | How to Obtain |
|
||||
|-------------|-------------|---------------|
|
||||
| `R2_ACCOUNT_ID` | Your Cloudflare account ID | Cloudflare Dashboard → R2 → Overview (right sidebar) |
|
||||
| `R2_ACCESS_KEY_ID` | R2 API token access key ID | Cloudflare Dashboard → R2 → Manage R2 API Tokens → Create API Token |
|
||||
| `R2_SECRET_ACCESS_KEY` | R2 API token secret key | Generated when creating the API token (save immediately!) |
|
||||
| `R2_BUCKET_NAME` | Name of your R2 bucket | Example: `meshcore-sar-builds` |
|
||||
|
||||
### Optional Secrets
|
||||
|
||||
| Secret Name | Description | Example |
|
||||
|-------------|-------------|---------|
|
||||
| `R2_PUBLIC_URL` | Public URL for R2 bucket (if public access configured) | `https://builds.example.com` |
|
||||
|
||||
## Step-by-Step Setup
|
||||
|
||||
### 1. Create Cloudflare R2 Bucket
|
||||
|
||||
1. Log in to [Cloudflare Dashboard](https://dash.cloudflare.com/)
|
||||
2. Navigate to **R2** in the left sidebar
|
||||
3. Click **Create Bucket**
|
||||
4. Enter bucket name (e.g., `meshcore-sar-builds`)
|
||||
5. Choose a location (e.g., `Automatic` or `ENAM` for Europe/North America)
|
||||
6. Click **Create Bucket**
|
||||
|
||||
### 2. Create R2 API Token
|
||||
|
||||
1. In Cloudflare Dashboard, go to **R2** → **Manage R2 API Tokens**
|
||||
2. Click **Create API Token**
|
||||
3. Configure the token:
|
||||
- **Token name**: `github-actions-upload`
|
||||
- **Permissions**: Select **Object Read & Write**
|
||||
- **Bucket scope**:
|
||||
- Choose **Apply to specific buckets only**
|
||||
- Select your bucket (e.g., `meshcore-sar-builds`)
|
||||
4. Click **Create API Token**
|
||||
5. **IMPORTANT**: Copy both the **Access Key ID** and **Secret Access Key** immediately
|
||||
- The secret key will only be shown once!
|
||||
- Save them securely (e.g., password manager)
|
||||
|
||||
### 3. Get Your Account ID
|
||||
|
||||
1. In Cloudflare Dashboard, go to **R2** → **Overview**
|
||||
2. Your Account ID is displayed in the right sidebar
|
||||
3. Copy the Account ID
|
||||
|
||||
### 4. Configure GitHub Secrets
|
||||
|
||||
1. Go to your GitHub repository
|
||||
2. Navigate to **Settings** → **Secrets and variables** → **Actions**
|
||||
3. Click **New repository secret** for each of the following:
|
||||
|
||||
**R2_ACCOUNT_ID**:
|
||||
```
|
||||
Paste your Cloudflare Account ID
|
||||
```
|
||||
|
||||
**R2_ACCESS_KEY_ID**:
|
||||
```
|
||||
Paste the Access Key ID from step 2
|
||||
```
|
||||
|
||||
**R2_SECRET_ACCESS_KEY**:
|
||||
```
|
||||
Paste the Secret Access Key from step 2
|
||||
```
|
||||
|
||||
**R2_BUCKET_NAME**:
|
||||
```
|
||||
meshcore-sar-builds
|
||||
```
|
||||
|
||||
### 5. (Optional) Configure Public Access
|
||||
|
||||
To enable public downloads from your R2 bucket:
|
||||
|
||||
#### Option A: Custom Domain (Recommended)
|
||||
|
||||
1. In Cloudflare Dashboard, go to **R2** → **Settings** for your bucket
|
||||
2. Under **Public Access**, click **Connect Domain**
|
||||
3. Enter your custom domain (e.g., `builds.meshcore.example.com`)
|
||||
4. Follow Cloudflare's instructions to configure DNS
|
||||
5. Add GitHub secret:
|
||||
```
|
||||
R2_PUBLIC_URL = https://builds.meshcore.example.com
|
||||
```
|
||||
|
||||
#### Option B: R2.dev Subdomain (Quick Setup)
|
||||
|
||||
1. In Cloudflare Dashboard, go to **R2** → **Settings** for your bucket
|
||||
2. Under **Public Access**, click **Allow Access**
|
||||
3. Enable **R2.dev subdomain**
|
||||
4. Copy the generated URL (e.g., `https://pub-xxxxx.r2.dev`)
|
||||
5. Add GitHub secret:
|
||||
```
|
||||
R2_PUBLIC_URL = https://pub-xxxxx.r2.dev
|
||||
```
|
||||
|
||||
**Note**: Without `R2_PUBLIC_URL` configured, artifacts will still upload successfully but download URLs won't be generated.
|
||||
|
||||
## Artifact Structure
|
||||
|
||||
Artifacts are uploaded with the following structure:
|
||||
|
||||
```
|
||||
unstable/
|
||||
├── main-abc1234/ # Branch + commit hash
|
||||
│ ├── meshcore-sar-main-abc1234-20241022-143022.apk
|
||||
│ ├── meshcore-sar-main-abc1234-20241022-143022.aab
|
||||
│ ├── meshcore-sar-main-abc1234-20241022-143022.dmg
|
||||
│ ├── meshcore-sar-main-abc1234-20241022-143022-windows.zip
|
||||
│ └── manifest.json
|
||||
├── v1.2.3/ # Release tag
|
||||
│ ├── meshcore-sar-v1.2.3-20241022-150045.apk
|
||||
│ ├── meshcore-sar-v1.2.3-20241022-150045.aab
|
||||
│ ├── meshcore-sar-v1.2.3-20241022-150045.dmg
|
||||
│ ├── meshcore-sar-v1.2.3-20241022-150045-windows.zip
|
||||
│ └── manifest.json
|
||||
└── develop-def5678/
|
||||
└── ...
|
||||
```
|
||||
|
||||
### Manifest Example
|
||||
|
||||
Each build includes a `manifest.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"build_id": "main-abc1234-20241022-143022",
|
||||
"commit": "abc1234567890abcdef1234567890abcdef12345",
|
||||
"commit_short": "abc1234",
|
||||
"branch": "main",
|
||||
"tag": "",
|
||||
"timestamp": "20241022-143022",
|
||||
"workflow_run": "123",
|
||||
"artifacts": [
|
||||
"meshcore-sar-main-abc1234-20241022-143022.apk",
|
||||
"meshcore-sar-main-abc1234-20241022-143022.aab",
|
||||
"meshcore-sar-main-abc1234-20241022-143022.dmg",
|
||||
"meshcore-sar-main-abc1234-20241022-143022-windows.zip"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Testing the Setup
|
||||
|
||||
1. Push a commit to your repository (or re-run an existing workflow)
|
||||
2. Navigate to **Actions** tab in GitHub
|
||||
3. Click on the running workflow
|
||||
4. Wait for all build jobs to complete
|
||||
5. Check the **Upload to Cloudflare R2** job:
|
||||
- Should show "✅ Artifacts uploaded to R2 bucket"
|
||||
- If `R2_PUBLIC_URL` is configured, download links will appear in the job summary
|
||||
6. Verify in Cloudflare Dashboard:
|
||||
- Go to **R2** → Your bucket
|
||||
- Navigate to `unstable/` directory
|
||||
- You should see your build artifacts
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Invalid credentials" error
|
||||
|
||||
- **Solution**: Verify `R2_ACCESS_KEY_ID` and `R2_SECRET_ACCESS_KEY` are correct
|
||||
- Regenerate API token if needed (remember to update secrets)
|
||||
|
||||
### "Bucket not found" error
|
||||
|
||||
- **Solution**: Check `R2_BUCKET_NAME` matches exactly (case-sensitive)
|
||||
- Verify the bucket exists in your Cloudflare R2 dashboard
|
||||
|
||||
### "Permission denied" error
|
||||
|
||||
- **Solution**: Ensure API token has **Object Read & Write** permissions
|
||||
- Verify token scope includes the specific bucket
|
||||
|
||||
### Artifacts not visible in R2
|
||||
|
||||
- **Solution**: Check workflow logs for upload errors
|
||||
- Verify at least one build job completed successfully
|
||||
- Check bucket permissions and CORS settings if accessing via browser
|
||||
|
||||
### Download URLs not showing
|
||||
|
||||
- **Solution**: This is normal if `R2_PUBLIC_URL` secret is not configured
|
||||
- Artifacts are uploaded successfully even without public URLs
|
||||
- Configure public access (see step 5) to enable download links
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Never commit secrets** to your repository
|
||||
2. Use **specific bucket scopes** for API tokens (not account-wide)
|
||||
3. **Rotate API tokens** periodically (e.g., every 90 days)
|
||||
4. **Limit public access** if artifacts contain sensitive data
|
||||
5. Set up **bucket lifecycle rules** to auto-delete old unstable builds
|
||||
6. Use **custom domains** instead of R2.dev subdomains for production
|
||||
|
||||
## Cost Considerations
|
||||
|
||||
Cloudflare R2 pricing (as of 2024):
|
||||
|
||||
- **Storage**: $0.015/GB per month
|
||||
- **Operations**:
|
||||
- Class A (write): $4.50 per million requests
|
||||
- Class B (read): $0.36 per million requests
|
||||
- **Egress**: **Free** (no bandwidth charges)
|
||||
|
||||
For typical usage:
|
||||
- ~100 builds/month × 4 platforms × ~100MB = ~40GB storage
|
||||
- Monthly cost: ~$0.60 + minimal operation costs
|
||||
|
||||
**Tip**: Set up lifecycle rules to automatically delete builds older than 30 days to minimize storage costs.
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Cloudflare R2 Documentation](https://developers.cloudflare.com/r2/)
|
||||
- [R2 API Documentation](https://developers.cloudflare.com/r2/api/s3/)
|
||||
- [GitHub Actions Secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets)
|
||||
- [ryand56/r2-upload-action](https://github.com/ryand56/r2-upload-action)
|
||||
262
.github/workflows/build-multiplatform.yml
vendored
262
.github/workflows/build-multiplatform.yml
vendored
@@ -1,5 +1,23 @@
|
||||
name: Build Multi-Platform
|
||||
|
||||
# This workflow builds the MeshCore SAR app for multiple platforms and uploads artifacts
|
||||
# to Cloudflare R2 for distribution.
|
||||
#
|
||||
# Required GitHub Secrets for R2 Upload (see .github/R2_SETUP.md):
|
||||
# - R2_ACCOUNT_ID: Cloudflare account ID
|
||||
# - R2_ACCESS_KEY_ID: R2 API token access key
|
||||
# - R2_SECRET_ACCESS_KEY: R2 API token secret
|
||||
# - R2_BUCKET_NAME: Name of the R2 bucket
|
||||
# - R2_PUBLIC_URL: (Optional) Public URL for download links
|
||||
#
|
||||
# Required GitHub Secrets for iOS TestFlight Beta Deployment:
|
||||
# - IOS_P12_BASE64: Base64-encoded .p12 certificate
|
||||
# - IOS_P12_PASSWORD: Password for the .p12 certificate
|
||||
# - IOS_PROVISION_PROFILE_BASE64: Base64-encoded provisioning profile
|
||||
# - FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: App-specific password from Apple
|
||||
# - FASTLANE_USER: Apple ID email (e.g., hey@dz0ny.dev)
|
||||
# - FASTLANE_SESSION: (Optional) Fastlane session token for 2FA
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
@@ -96,12 +114,6 @@ jobs:
|
||||
- name: Generate localizations
|
||||
run: flutter gen-l10n
|
||||
|
||||
- name: Run analyzer
|
||||
run: flutter analyze
|
||||
|
||||
- name: Run tests
|
||||
run: flutter test
|
||||
|
||||
- name: Build APK
|
||||
run: flutter build apk --release
|
||||
|
||||
@@ -192,6 +204,120 @@ jobs:
|
||||
# path: build/ios/ipa/*.ipa
|
||||
# retention-days: 30
|
||||
|
||||
# iOS Beta Build (Fastlane TestFlight)
|
||||
ios-beta-testflight:
|
||||
name: Deploy iOS Beta to TestFlight
|
||||
runs-on: macos-latest
|
||||
needs: [update-version]
|
||||
# Only run on main/develop branches or tags, and only if secrets are configured
|
||||
if: |
|
||||
(github.ref == 'refs/heads/main' ||
|
||||
github.ref == 'refs/heads/develop' ||
|
||||
startsWith(github.ref, 'refs/tags/v')) &&
|
||||
(needs.update-version.result == 'success' || needs.update-version.result == 'skipped')
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download versioned pubspec
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: versioned-pubspec
|
||||
path: .
|
||||
|
||||
- name: Setup Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
channel: 'stable'
|
||||
cache: true
|
||||
|
||||
- name: Install dependencies
|
||||
run: flutter pub get
|
||||
|
||||
- name: Generate localizations
|
||||
run: flutter gen-l10n
|
||||
|
||||
- name: Install CocoaPods dependencies
|
||||
run: |
|
||||
cd ios
|
||||
pod install
|
||||
cd ..
|
||||
|
||||
- name: Setup Ruby for Fastlane
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: '3.2'
|
||||
bundler-cache: false
|
||||
|
||||
- name: Install Fastlane
|
||||
run: |
|
||||
cd ios
|
||||
gem install bundler
|
||||
bundle config set --local path 'vendor/bundle'
|
||||
bundle install
|
||||
cd ..
|
||||
|
||||
- name: Import Code Signing Certificate
|
||||
env:
|
||||
P12_BASE64: ${{ secrets.IOS_P12_BASE64 }}
|
||||
P12_PASSWORD: ${{ secrets.IOS_P12_PASSWORD }}
|
||||
run: |
|
||||
# Create temporary keychain
|
||||
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
||||
KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
|
||||
|
||||
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
|
||||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||
|
||||
# Import certificate
|
||||
echo "$P12_BASE64" | base64 --decode > $RUNNER_TEMP/certificate.p12
|
||||
security import $RUNNER_TEMP/certificate.p12 \
|
||||
-k "$KEYCHAIN_PATH" \
|
||||
-P "$P12_PASSWORD" \
|
||||
-T /usr/bin/codesign
|
||||
|
||||
security list-keychain -d user -s "$KEYCHAIN_PATH"
|
||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||
|
||||
- name: Import Provisioning Profile
|
||||
env:
|
||||
PROVISION_PROFILE_BASE64: ${{ secrets.IOS_PROVISION_PROFILE_BASE64 }}
|
||||
run: |
|
||||
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
||||
echo "$PROVISION_PROFILE_BASE64" | base64 --decode > ~/Library/MobileDevice/Provisioning\ Profiles/profile.mobileprovision
|
||||
|
||||
- name: Build and Upload to TestFlight
|
||||
env:
|
||||
FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ secrets.FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }}
|
||||
FASTLANE_USER: ${{ secrets.FASTLANE_USER }}
|
||||
FASTLANE_SESSION: ${{ secrets.FASTLANE_SESSION }}
|
||||
run: |
|
||||
cd ios
|
||||
bundle exec fastlane beta
|
||||
cd ..
|
||||
|
||||
- name: Cleanup Keychain
|
||||
if: always()
|
||||
run: |
|
||||
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
||||
if [ -f "$KEYCHAIN_PATH" ]; then
|
||||
security delete-keychain "$KEYCHAIN_PATH"
|
||||
fi
|
||||
|
||||
- name: Upload Build Artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ios-beta-logs
|
||||
path: |
|
||||
ios/fastlane/report.xml
|
||||
~/Library/Logs/gym/
|
||||
retention-days: 7
|
||||
|
||||
# macOS DMG Build
|
||||
build-macos:
|
||||
name: Build macOS DMG
|
||||
@@ -322,6 +448,130 @@ jobs:
|
||||
path: MeshCore-SAR-Windows.zip
|
||||
retention-days: 30
|
||||
|
||||
# Upload to Cloudflare R2 (Unstable Bucket)
|
||||
upload-to-r2:
|
||||
name: Upload to Cloudflare R2
|
||||
needs: [build-android, build-ios, build-macos, build-windows]
|
||||
runs-on: ubuntu-latest
|
||||
if: always() && (needs.build-android.result == 'success' || needs.build-ios.result == 'success' || needs.build-macos.result == 'success' || needs.build-windows.result == 'success')
|
||||
|
||||
steps:
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Generate build metadata
|
||||
id: metadata
|
||||
run: |
|
||||
# Generate timestamp and commit info
|
||||
TIMESTAMP=$(date -u +"%Y%m%d-%H%M%S")
|
||||
COMMIT_SHORT=${GITHUB_SHA::7}
|
||||
BRANCH_NAME=${GITHUB_REF#refs/heads/}
|
||||
|
||||
# For tags, use tag name; otherwise use branch-commit
|
||||
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
VERSION=${GITHUB_REF#refs/tags/}
|
||||
BUILD_PREFIX="${VERSION}"
|
||||
else
|
||||
BUILD_PREFIX="${BRANCH_NAME}-${COMMIT_SHORT}"
|
||||
fi
|
||||
|
||||
echo "timestamp=$TIMESTAMP" >> $GITHUB_OUTPUT
|
||||
echo "commit_short=$COMMIT_SHORT" >> $GITHUB_OUTPUT
|
||||
echo "build_prefix=$BUILD_PREFIX" >> $GITHUB_OUTPUT
|
||||
echo "Build prefix: $BUILD_PREFIX"
|
||||
|
||||
- name: Prepare artifacts for upload
|
||||
run: |
|
||||
# Create a structured directory for R2
|
||||
mkdir -p r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}
|
||||
|
||||
# Copy and rename artifacts with metadata
|
||||
if [ -f artifacts/android-apk/app-release.apk ]; then
|
||||
cp artifacts/android-apk/app-release.apk \
|
||||
"r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}/meshcore-sar-${{ steps.metadata.outputs.build_prefix }}-${{ steps.metadata.outputs.timestamp }}.apk"
|
||||
fi
|
||||
|
||||
if [ -f artifacts/android-appbundle/app-release.aab ]; then
|
||||
cp artifacts/android-appbundle/app-release.aab \
|
||||
"r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}/meshcore-sar-${{ steps.metadata.outputs.build_prefix }}-${{ steps.metadata.outputs.timestamp }}.aab"
|
||||
fi
|
||||
|
||||
if [ -f artifacts/macos-dmg/MeshCore-SAR.dmg ]; then
|
||||
cp artifacts/macos-dmg/MeshCore-SAR.dmg \
|
||||
"r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}/meshcore-sar-${{ steps.metadata.outputs.build_prefix }}-${{ steps.metadata.outputs.timestamp }}.dmg"
|
||||
fi
|
||||
|
||||
if [ -f artifacts/windows-executable/MeshCore-SAR-Windows.zip ]; then
|
||||
cp artifacts/windows-executable/MeshCore-SAR-Windows.zip \
|
||||
"r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}/meshcore-sar-${{ steps.metadata.outputs.build_prefix }}-${{ steps.metadata.outputs.timestamp }}-windows.zip"
|
||||
fi
|
||||
|
||||
# Create a build manifest
|
||||
cat > "r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}/manifest.json" <<EOF
|
||||
{
|
||||
"build_id": "${{ steps.metadata.outputs.build_prefix }}-${{ steps.metadata.outputs.timestamp }}",
|
||||
"commit": "${{ github.sha }}",
|
||||
"commit_short": "${{ steps.metadata.outputs.commit_short }}",
|
||||
"branch": "${GITHUB_REF#refs/heads/}",
|
||||
"tag": "${GITHUB_REF#refs/tags/}",
|
||||
"timestamp": "${{ steps.metadata.outputs.timestamp }}",
|
||||
"workflow_run": "${{ github.run_number }}",
|
||||
"artifacts": $(ls -1 r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}/ | grep -v manifest.json | jq -R . | jq -s .)
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "Prepared artifacts:"
|
||||
ls -lah r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}/
|
||||
|
||||
- name: Upload to Cloudflare R2
|
||||
uses: ryand56/r2-upload-action@latest
|
||||
with:
|
||||
r2-account-id: ${{ secrets.R2_ACCOUNT_ID }}
|
||||
r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }}
|
||||
r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }}
|
||||
r2-bucket: ${{ secrets.R2_BUCKET_NAME }}
|
||||
source-dir: r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}
|
||||
destination-dir: unstable/${{ steps.metadata.outputs.build_prefix }}
|
||||
|
||||
- name: Generate download URLs
|
||||
run: |
|
||||
# If R2_PUBLIC_URL is set, generate public download links
|
||||
if [ -n "${{ secrets.R2_PUBLIC_URL }}" ]; then
|
||||
echo "## 📦 Unstable Build Artifacts" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Build ID: \`${{ steps.metadata.outputs.build_prefix }}-${{ steps.metadata.outputs.timestamp }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
BASE_URL="${{ secrets.R2_PUBLIC_URL }}/unstable/${{ steps.metadata.outputs.build_prefix }}"
|
||||
|
||||
if [ -f "r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}"/*.apk ]; then
|
||||
APK_FILE=$(basename r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}/*.apk)
|
||||
echo "- 🤖 **Android APK**: [$APK_FILE]($BASE_URL/$APK_FILE)" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ -f "r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}"/*.aab ]; then
|
||||
AAB_FILE=$(basename r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}/*.aab)
|
||||
echo "- 📦 **Android Bundle**: [$AAB_FILE]($BASE_URL/$AAB_FILE)" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ -f "r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}"/*.dmg ]; then
|
||||
DMG_FILE=$(basename r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}/*.dmg)
|
||||
echo "- 🍎 **macOS DMG**: [$DMG_FILE]($BASE_URL/$DMG_FILE)" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ -f "r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}"/*-windows.zip ]; then
|
||||
WIN_FILE=$(basename r2-upload/unstable/${{ steps.metadata.outputs.build_prefix }}/*-windows.zip)
|
||||
echo "- 🪟 **Windows ZIP**: [$WIN_FILE]($BASE_URL/$WIN_FILE)" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- 📋 **Manifest**: [manifest.json]($BASE_URL/manifest.json)" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "✅ Artifacts uploaded to R2 bucket (no public URL configured)" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# Create Release on Tag
|
||||
create-release:
|
||||
name: Create Release
|
||||
|
||||
Reference in New Issue
Block a user