diff --git a/.github/CICD.md b/.github/CICD.md new file mode 100644 index 0000000..3214453 --- /dev/null +++ b/.github/CICD.md @@ -0,0 +1,74 @@ +# CI/CD Workflows + +This repository includes GitHub Actions workflows for automated building and releasing of the QR Code Scanner app. + +## Workflows + +### 1. Build Test (`build.yml`) +- **Triggers**: Pull requests and pushes to main/master branches +- **Purpose**: Validates that the code builds successfully and runs tests +- **Outputs**: Debug APK as an artifact + +### 2. Build and Release APK (`release.yml`) +- **Triggers**: + - When a release is published on GitHub + - When a tag starting with 'v' is pushed (e.g., `v1.0.0`) +- **Purpose**: Builds the release APK and attaches it to the GitHub release +- **Outputs**: + - Release APK attached to the GitHub release + - Release APK as an artifact for tag pushes + +## Creating a Release + +### Option 1: Using the Release Script (Recommended) + +The repository includes a helper script to streamline the release process: + +```bash +./scripts/release.sh v1.0.0-alpha-06 +``` + +This script will: +- Validate the version format +- Check if you're on the main/master branch +- Optionally update the version in `app/build.gradle.kts` +- Create and push the git tag +- Provide links to track the build progress + +### Option 2: Manual Process + +To create a new release manually: + +1. **Create and push a tag**: + ```bash + git tag v1.0.0-alpha-06 + git push origin v1.0.0-alpha-06 + ``` + +2. **Create a GitHub release**: + - Go to the repository's Releases page + - Click "Create a new release" + - Select the tag you just created + - Fill in the release title and description + - Click "Publish release" + +3. **Automatic APK build**: + - The workflow will automatically trigger + - Build the release APK + - Attach the APK to the release + +## APK Naming + +The generated APK will be named: `qr-code-scanner-{version}.apk` + +Where `{version}` is extracted from either: +- The git tag (if triggered by tag push) +- The `versionName` from `app/build.gradle.kts` + +## Build Environment + +The workflows use: +- Ubuntu latest +- Java 17 (Temurin distribution) +- Android SDK (via android-actions/setup-android) +- Gradle caching for faster builds \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..d59da41 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,52 @@ +name: Build Test + +on: + pull_request: + branches: [ main, master ] + push: + branches: [ main, master ] + +env: + GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4096m -Dorg.gradle.daemon=false" + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + + - name: Cache Gradle dependencies + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Make gradlew executable + run: chmod +x ./gradlew + + - name: Run tests + run: ./gradlew test + + - name: Build debug APK + run: ./gradlew assembleDebug + + - name: Upload debug APK + uses: actions/upload-artifact@v4 + with: + name: debug-apk + path: app/build/outputs/apk/debug/*.apk \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..149f160 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,86 @@ +name: Build and Release APK + +on: + release: + types: [published] + push: + tags: + - 'v*' + +env: + GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4096m -Dorg.gradle.daemon=false" + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + + - name: Cache Gradle dependencies + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Make gradlew executable + run: chmod +x ./gradlew + + - name: Clean and Build Release APK + run: | + ./gradlew clean + ./gradlew assembleRelease + + - name: Get APK info + id: apk-info + run: | + APK_PATH=$(find app/build/outputs/apk/release -name "*.apk" | head -1) + if [ -z "$APK_PATH" ]; then + echo "APK not found!" + exit 1 + fi + echo "apk_path=$APK_PATH" >> $GITHUB_OUTPUT + + # Extract version info from the APK path or use git tag + if [[ "${{ github.ref }}" == refs/tags/* ]]; then + VERSION=${GITHUB_REF#refs/tags/} + else + VERSION=$(grep 'versionName' app/build.gradle.kts | sed 's/.*"\(.*\)".*/\1/') + fi + + APK_NAME="qr-code-scanner-${VERSION}.apk" + echo "apk_name=$APK_NAME" >> $GITHUB_OUTPUT + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Rename APK + run: | + mv "${{ steps.apk-info.outputs.apk_path }}" "app/build/outputs/apk/release/${{ steps.apk-info.outputs.apk_name }}" + + - name: Upload APK to Release + if: github.event_name == 'release' + uses: softprops/action-gh-release@v1 + with: + files: app/build/outputs/apk/release/${{ steps.apk-info.outputs.apk_name }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload APK as Artifact (for tag pushes) + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') + uses: actions/upload-artifact@v4 + with: + name: qr-code-scanner-${{ steps.apk-info.outputs.version }} + path: app/build/outputs/apk/release/${{ steps.apk-info.outputs.apk_name }} \ No newline at end of file diff --git a/README.md b/README.md index 0ca5b76..2c20f56 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Android QR Code Scanner by Dan Tech [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/dantech0xff/compose-barcode-scanner) +[![Build Test](https://github.com/dantech0xff/compose-qr-code-scanner/actions/workflows/build.yml/badge.svg)](https://github.com/dantech0xff/compose-qr-code-scanner/actions/workflows/build.yml) ![](https://img.shields.io/badge/Kotlin-Compose-green) ![](https://img.shields.io/badge/Google--Billing-blue) ![](https://img.shields.io/badge/Camera--X-red) @@ -7,6 +8,7 @@ ![](https://img.shields.io/badge/Hilt-gray) ![](https://img.shields.io/badge/Flow-yellow) ![](https://img.shields.io/badge/Room--Database-black) +![](https://img.shields.io/badge/CI%2FCD-GitHub%20Actions-blue) This project is built with Jetpack Compose, CameraX, ML Kit and my handsome attitude. This is for my learning purpose in order to get familiar with Jetpack Compose, MLKit and CameraX. @@ -49,6 +51,10 @@ Feel free to use it in your project. ### Download, Clone, or do anything for your own purpose, I don't care! +## Download APK + +Get the latest APK from the [Releases](../../releases) page. Each release automatically includes a built APK file ready for installation. + ### Love it? Give me a star, I will be happy! ### Happy Coding! diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..2f49246 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# Release script for QR Code Scanner +# Usage: ./scripts/release.sh +# Example: ./scripts/release.sh v1.0.0-alpha-06 + +set -e + +if [ $# -eq 0 ]; then + echo "Usage: $0 " + echo "Example: $0 v1.0.0-alpha-06" + exit 1 +fi + +VERSION=$1 + +# Validate version format +if [[ ! $VERSION =~ ^v[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then + echo "Error: Version must start with 'v' and follow semantic versioning (e.g., v1.0.0-alpha-06)" + exit 1 +fi + +echo "Creating release for version: $VERSION" + +# Check if tag already exists +if git rev-parse "$VERSION" >/dev/null 2>&1; then + echo "Error: Tag $VERSION already exists" + exit 1 +fi + +# Get current branch +CURRENT_BRANCH=$(git branch --show-current) +echo "Current branch: $CURRENT_BRANCH" + +# Ensure we're on main/master branch +if [[ "$CURRENT_BRANCH" != "main" && "$CURRENT_BRANCH" != "master" ]]; then + echo "Warning: You're not on main/master branch. Continue? (y/N)" + read -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Aborted" + exit 1 + fi +fi + +# Ensure working directory is clean +if ! git diff-index --quiet HEAD --; then + echo "Error: Working directory is not clean. Please commit your changes first." + exit 1 +fi + +# Update version in build.gradle.kts if needed +echo "Current version in build.gradle.kts:" +grep 'versionName' app/build.gradle.kts || echo "Version not found" + +echo "Do you want to update the version in build.gradle.kts? (y/N)" +read -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + # Extract version without 'v' prefix + VERSION_NUMBER=${VERSION#v} + sed -i.bak "s/versionName = \".*\"/versionName = \"$VERSION_NUMBER\"/" app/build.gradle.kts + echo "Updated versionName to $VERSION_NUMBER" + + # Commit version update + git add app/build.gradle.kts + git commit -m "Bump version to $VERSION_NUMBER" +fi + +# Create and push tag +echo "Creating tag: $VERSION" +git tag -a "$VERSION" -m "Release $VERSION" + +echo "Pushing tag to origin..." +git push origin "$VERSION" + +echo "✅ Tag $VERSION has been pushed!" +echo "🚀 GitHub Actions will now build the APK automatically." +echo "📱 You can find the APK in the releases page once the build completes." +echo "🔗 https://github.com/$(git config --get remote.origin.url | sed 's/.*github.com[:/]\(.*\)\.git/\1/')/releases" \ No newline at end of file