diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..825064e0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,432 @@ +name: Build and Release + +on: + push: + tags: + - 'v*.*.*' # Official release tags (e.g., v3.1.1) + workflow_dispatch: + inputs: + version: + description: 'Version number (e.g., 3.1.1) - Manual trigger will NOT publish to Release page' + required: true + type: string + +jobs: + validate-and-release: + name: Validate Version and Build Release + runs-on: ubuntu-latest + + permissions: + contents: write # Required for creating releases and uploading artifacts + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup NDK + uses: nttld/setup-ndk@v1.4.2 + id: setup-ndk + with: + ndk-version: r26d + link-to-sdk: true + add-to-path: true + + - name: Install Ninja + run: sudo apt-get update && sudo apt-get install -y ninja-build + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: gradle + + - name: Grant execute permission for scripts + run: chmod +x gradlew tasks.sh publish.sh + + - name: Extract tag version + id: tag_version + run: | + # Determine if this is a manual trigger or tag push + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + # Manual trigger - use input version + VERSION="${{ github.event.inputs.version }}" + TAG_NAME="v$VERSION" + IS_MANUAL="true" + echo "🔧 Manual trigger detected" + else + # Tag push - extract from ref + TAG_NAME="${GITHUB_REF#refs/tags/}" + IS_MANUAL="false" + + # Extract version numbers (e.g., v3.1.1 -> 3.1.1) + if [[ $TAG_NAME =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then + VERSION="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.${BASH_REMATCH[3]}" + else + echo "❌ Invalid tag format: $TAG_NAME" + echo "Tag must be in format: v\${major}.\${minor}.\${patch} (e.g., v3.1.1)" + exit 1 + fi + fi + + # Validate version format + if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "❌ Invalid version format: $VERSION" + echo "Version must be in format: \${major}.\${minor}.\${patch} (e.g., 3.1.1)" + exit 1 + fi + + echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "is_manual=$IS_MANUAL" >> $GITHUB_OUTPUT + echo "✅ Valid version: $VERSION (tag: $TAG_NAME, manual: $IS_MANUAL)" + + - name: Validate version matches project version + run: | + PROJECT_VERSION=$(grep -E 'versionName\s*:' build.gradle | sed -E 's/.*versionName\s*:\s*"([^"]+)".*/\1/') + TAG_VERSION="${{ steps.tag_version.outputs.version }}" + + echo "Project version: $PROJECT_VERSION" + echo "Tag version: $TAG_VERSION" + + if [ "$PROJECT_VERSION" != "$TAG_VERSION" ]; then + echo "❌ Version mismatch!" + echo "Tag version ($TAG_VERSION) does not match project version ($PROJECT_VERSION)" + echo "Please update the versionName in build.gradle to match the tag, or use the correct tag." + exit 1 + fi + + echo "✅ Version validation passed: $TAG_VERSION" + + - name: Setup local.properties for publishing + run: | + # Create local.properties with necessary configurations + mkdir -p /tmp/maven-repo + cat > local.properties << EOF + usingCMakeCompile=true + usingCMakeCompileDebug=false + deployArtifacts=true + localRepoDir=/tmp/maven-repo + EOF + + echo "✅ local.properties configured for publishing" + cat local.properties + + - name: Build all artifact variants + run: | + echo "🔨 Building all 4 artifact variants..." + bash -e ./publish.sh + + echo "✅ All artifacts built successfully" + echo "đŸ“Ļ Generated artifacts:" + find /tmp/maven-repo -type f \( -name "*.aar" -o -name "*.pom" \) | sort + + - name: Build demo APK with video module + run: | + echo "🔨 Building demo APK with video module (default configuration)..." + ./tasks.sh --enable-cmake --release --enable-video-module --disable-16kb-page-size --build + + # Find and copy the APK (look specifically for release build output) + APK_PATH=$(find "cgeDemo/build/outputs/apk/release" \( -name "*-release.apk" -o -name "*-release-unsigned.apk" \) | head -1) + if [ -z "$APK_PATH" ]; then + # Fallback: search in build directory + APK_PATH=$(find "cgeDemo/build" -path "*/release/*" -name "*.apk" | head -1) + fi + if [ -z "$APK_PATH" ]; then + echo "❌ APK not found!" + echo "Searched locations:" + find "cgeDemo/build" -name "*.apk" || echo "No APK files found" + exit 1 + fi + + mkdir -p /tmp/release-artifacts + cp "$APK_PATH" /tmp/release-artifacts/cgeDemo-${{ steps.tag_version.outputs.version }}.apk + + echo "✅ Demo APK built successfully" + ls -lh /tmp/release-artifacts/ + + - name: Package AAR artifacts + run: | + echo "đŸ“Ļ Packaging AAR artifacts..." + + VERSION="${{ steps.tag_version.outputs.version }}" + MAVEN_REPO="/tmp/maven-repo" + ARTIFACTS_DIR="/tmp/release-artifacts" + + # Find and copy all AAR files from the maven repository + # Expected structure: /tmp/maven-repo/org/wysaid/gpuimage-plus/{version}/gpuimage-plus-{version}.aar + while IFS= read -r aar_file; do + if [ -n "$aar_file" ]; then + filename=$(basename "$aar_file") + cp "$aar_file" "$ARTIFACTS_DIR/$filename" + echo " ✓ Packaged: $filename" + fi + done < <(find "$MAVEN_REPO/org/wysaid/gpuimage-plus" -name "*.aar") + + # Validate that we have exactly 4 AAR files + AAR_COUNT=$(find "$ARTIFACTS_DIR" -name "*.aar" | wc -l) + if [ "$AAR_COUNT" -ne 4 ]; then + echo "❌ Expected 4 AAR files but found $AAR_COUNT" + exit 1 + fi + + echo "✅ All AAR artifacts packaged ($AAR_COUNT files)" + echo "đŸ“Ļ Final artifacts:" + ls -lh "$ARTIFACTS_DIR/" + + - name: Sync artifacts to maven repo and open PR + if: steps.tag_version.outputs.is_manual != 'true' + id: sync_maven_repo + run: | + set -euo pipefail + + VERSION="${{ steps.tag_version.outputs.version }}" + TAG_NAME="${{ steps.tag_version.outputs.tag_name }}" + ARTIFACT_REPO_TOKEN="${{ secrets.ARTIFACT_REPO_TOKEN }}" + + if [[ -z "${ARTIFACT_REPO_TOKEN:-}" ]]; then + echo "❌ Missing secret: ARTIFACT_REPO_TOKEN (repo:write to android-gpuimage-plus-maven)" + exit 1 + fi + + ARTIFACT_REPO="wysaid/android-gpuimage-plus-maven" + WORKDIR="/tmp/maven-repo-target" + SOURCE_REPO="/tmp/maven-repo" + BRANCH="sync/v${VERSION}" + + echo "🔄 Cloning artifact repo..." + git clone --depth 1 "https://${ARTIFACT_REPO_TOKEN}@github.com/${ARTIFACT_REPO}.git" "$WORKDIR" + + echo "🚚 Syncing generated Maven artifacts..." + mkdir -p "$WORKDIR/org/wysaid/gpuimage-plus" + rsync -av --delete "${SOURCE_REPO}/org/wysaid/gpuimage-plus/" "$WORKDIR/org/wysaid/gpuimage-plus/" + + echo "🧭 Regenerating index pages" + pushd "$WORKDIR" + chmod +x genSites.sh + ./genSites.sh + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + git checkout -b "$BRANCH" + git add org/wysaid/gpuimage-plus || true + + echo "artifact_pr_url=" >> $GITHUB_OUTPUT + + if git diff --cached --quiet; then + echo "â„šī¸ No changes to publish; skipping PR." + exit 0 + fi + + git commit -m "Publish artifacts ${TAG_NAME}" + git push origin "$BRANCH" + + echo "📝 Creating pull request..." + PR_BODY="Automated artifact sync for ${TAG_NAME}.\n\nGenerated by main repo release workflow." + API_JSON=$(jq -n --arg title "Publish ${TAG_NAME} artifacts" \ + --arg head "$BRANCH" \ + --arg base "master" \ + --arg body "$PR_BODY" \ + '{title:$title, head:$head, base:$base, body:$body}') + + PR_RESPONSE=$(curl -sS -X POST \ + -H "Authorization: token ${ARTIFACT_REPO_TOKEN}" \ + -H "Accept: application/vnd.github+json" \ + -d "$API_JSON" \ + "https://api.github.com/repos/${ARTIFACT_REPO}/pulls") + + echo "PR response: $PR_RESPONSE" + PR_URL=$(echo "$PR_RESPONSE" | jq -r '.html_url') + if [[ "$PR_URL" == "null" || -z "$PR_URL" ]]; then + echo "❌ Failed to create PR" + exit 1 + fi + + echo "✅ PR created: $PR_URL" + echo "artifact_pr_url=$PR_URL" >> $GITHUB_OUTPUT + popd + + - name: Generate release notes + id: release_notes + run: | + VERSION="${{ steps.tag_version.outputs.version }}" + TAG_NAME="${{ steps.tag_version.outputs.tag_name }}" + cat > /tmp/release_notes.md << EOF + ## 🚀 Android GPUImage Plus $TAG_NAME + + ### đŸ“Ļ Downloads + + **Demo APK:** + - \`cgeDemo-$VERSION.apk\` - Demo application with full video features + + **AAR Library Artifacts:** + + Four variants are available for different use cases: + + 1. **gpuimage-plus-$VERSION.aar** + - Page size: 4KB (default) + - Full-featured with FFmpeg bundled + - Architectures: armeabi-v7a, arm64-v8a, x86, x86_64 + + 2. **gpuimage-plus-$VERSION-16k.aar** + - Page size: 16KB + - Full-featured with FFmpeg bundled + - Architectures: armeabi-v7a, arm64-v8a, x86, x86_64 + + 3. **gpuimage-plus-$VERSION-min.aar** + - Page size: 4KB (default) + - Image-only version (no video features or FFmpeg) + - Architectures: armeabi-v7a, arm64-v8a, x86, x86_64 + + 4. **gpuimage-plus-$VERSION-16k-min.aar** + - Page size: 16KB + - Image-only version (no video features or FFmpeg) + - Architectures: armeabi-v7a, arm64-v8a, x86, x86_64 + + ### 🔧 Gradle Dependency + + \`\`\`gradle + allprojects { + repositories { + maven { + url 'https://maven.wysaid.org/' + } + } + } + + dependencies { + // Choose one of the following based on your needs: + + // Full-featured with FFmpeg (4KB page size) + implementation 'org.wysaid:gpuimage-plus:$VERSION' + + // Full-featured with FFmpeg (16KB page size) + implementation 'org.wysaid:gpuimage-plus:$VERSION-16k' + + // Image-only, no video (4KB page size) + implementation 'org.wysaid:gpuimage-plus:$VERSION-min' + + // Image-only, no video (16KB page size) + implementation 'org.wysaid:gpuimage-plus:$VERSION-16k-min' + } + \`\`\` + + ### 📋 System Requirements + + - **Minimum Android SDK**: API 21 (Android 5.0) + - **Target Android SDK**: API 25 + - **NDK Version**: r26d + - **Supported Architectures**: armeabi-v7a, arm64-v8a, x86, x86_64 + + ### 📚 Documentation + + - **GitHub Repository**: https://github.com/wysaid/android-gpuimage-plus + - **Wiki**: https://github.com/wysaid/android-gpuimage-plus/wiki + - **Filter Rule Documentation**: https://github.com/wysaid/android-gpuimage-plus/wiki/Parsing-String-Rule-(EN) + + --- + + For complete changelog, see the auto-generated content below. + EOF + + echo "✅ Release notes generated" + + - name: Create GitHub Release + if: steps.tag_version.outputs.is_manual != 'true' + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.tag_version.outputs.tag_name }} + name: Release ${{ steps.tag_version.outputs.tag_name }} + body_path: /tmp/release_notes.md + files: | + /tmp/release-artifacts/*.apk + /tmp/release-artifacts/*.aar + draft: false + prerelease: false + generate_release_notes: true + make_latest: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload artifacts (Manual trigger) + if: steps.tag_version.outputs.is_manual == 'true' + uses: actions/upload-artifact@v4 + with: + name: release-artifacts-${{ steps.tag_version.outputs.version }} + path: /tmp/release-artifacts/ + retention-days: 7 + + - name: Manual trigger success message + if: steps.tag_version.outputs.is_manual == 'true' + run: | + echo "🎉 ============================================" + echo "🎉 Manual build completed successfully!" + echo "🎉 ============================================" + echo "" + echo "đŸ“Ļ Version: ${{ steps.tag_version.outputs.version }}" + echo "đŸ“Ļ Artifacts are available for download from the workflow run." + echo "" + echo "âš ī¸ Note: This is a manual trigger build." + echo "âš ī¸ Artifacts are NOT published to the Release page." + echo "âš ī¸ To create an official release, push a tag (e.g., git tag v${{ steps.tag_version.outputs.version }} && git push origin v${{ steps.tag_version.outputs.version }})" + echo "" + echo "đŸ“Ļ Built artifacts:" + ls -lh /tmp/release-artifacts/ + + - name: Summary + run: | + IS_MANUAL="${{ steps.tag_version.outputs.is_manual }}" + VERSION="${{ steps.tag_version.outputs.version }}" + TAG_NAME="${{ steps.tag_version.outputs.tag_name }}" + + if [ "$IS_MANUAL" = "true" ]; then + echo "## 🔧 Manual Build Completed Successfully!" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Version**: $VERSION" >> $GITHUB_STEP_SUMMARY + echo "**Trigger**: Manual (workflow_dispatch)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### âš ī¸ Note" >> $GITHUB_STEP_SUMMARY + echo "This is a **manual trigger build**. Artifacts are **NOT published** to the Release page." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "To create an official release, push a tag:" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY + echo "git tag v$VERSION && git push origin v$VERSION" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### đŸ“Ļ Built Artifacts (available for download):" >> $GITHUB_STEP_SUMMARY + else + echo "## 🎉 Release Created Successfully!" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Release**: $TAG_NAME" >> $GITHUB_STEP_SUMMARY + echo "**Version**: $VERSION" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ✅ Version Validation" >> $GITHUB_STEP_SUMMARY + echo "- ✅ Tag format validated: v\${major}.\${minor}.\${patch}" >> $GITHUB_STEP_SUMMARY + echo "- ✅ Version matches project version in build.gradle" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### đŸ“Ļ Released Artifacts:" >> $GITHUB_STEP_SUMMARY + fi + + echo "**Demo APK:**" >> $GITHUB_STEP_SUMMARY + echo "- cgeDemo-$VERSION.apk" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**AAR Libraries:**" >> $GITHUB_STEP_SUMMARY + echo "- gpuimage-plus-$VERSION.aar (4KB, with video)" >> $GITHUB_STEP_SUMMARY + echo "- gpuimage-plus-$VERSION-16k.aar (16KB, with video)" >> $GITHUB_STEP_SUMMARY + echo "- gpuimage-plus-$VERSION-min.aar (4KB, image-only)" >> $GITHUB_STEP_SUMMARY + echo "- gpuimage-plus-$VERSION-16k-min.aar (16KB, image-only)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "$IS_MANUAL" != "true" ]; then + echo "Release page: ${{ github.server_url }}/${{ github.repository }}/releases/tag/$TAG_NAME" >> $GITHUB_STEP_SUMMARY + fi + + ARTIFACT_PR_URL="${{ steps.sync_maven_repo.outputs.artifact_pr_url }}" + if [ -n "$ARTIFACT_PR_URL" ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 📮 Artifact Repository PR" >> $GITHUB_STEP_SUMMARY + echo "- $ARTIFACT_PR_URL" >> $GITHUB_STEP_SUMMARY + fi diff --git a/README.md b/README.md index 4ed9db89..915f5570 100644 --- a/README.md +++ b/README.md @@ -239,6 +239,57 @@ Some utils are available for creating filters: [https://github.com/wysaid/cge-to [![Tool](https://raw.githubusercontent.com/wysaid/cge-tools/master/screenshots/0.jpg "cge-tool")](https://github.com/wysaid/cge-tools) +## Release Process + +This project uses an automated release workflow that is triggered by version tags. + +### Creating a Release + +1. **Update the version** in `build.gradle`: + ```gradle + ext { + android = [ + ... + versionName: "3.1.1", // Update this version + ... + ] + } + ``` + +2. **Commit and push** the version change: + ```bash + git add build.gradle + git commit -m "Bump version to 3.1.1" + git push + ``` + +3. **Create and push a tag** matching the version: + ```bash + git tag v3.1.1 + git push origin v3.1.1 + ``` + +### What Happens Automatically + +When you push a tag in the format `v*.*.*` (e.g., `v3.1.1`), the release workflow will: + +1. **Validate** that the tag version matches the `versionName` in `build.gradle` +2. **Build** all four AAR variants: + - `gpuimage-plus-{version}.aar` - Full-featured with FFmpeg (4KB page size) + - `gpuimage-plus-{version}-16k.aar` - Full-featured with FFmpeg (16KB page size) + - `gpuimage-plus-{version}-min.aar` - Image-only, no video (4KB page size) + - `gpuimage-plus-{version}-16k-min.aar` - Image-only, no video (16KB page size) +3. **Build** the demo APK with video module +4. **Create** a GitHub release with all artifacts and release notes + +### Requirements + +- Tag must be in the format: `v{major}.{minor}.{patch}` (e.g., `v3.1.1`) +- Tag version must exactly match the `versionName` in `build.gradle` +- All version parts must be pure numbers + +If the validation fails, the workflow will report an error and stop. + ## License [MIT License](https://github.com/wysaid/android-gpuimage-plus/blob/master/LICENSE)