diff --git a/.github/actions/android/action.yml b/.github/actions/android/action.yml new file mode 100644 index 000000000..0ed50b47b --- /dev/null +++ b/.github/actions/android/action.yml @@ -0,0 +1,57 @@ +name: "Android Workflow" + +inputs: + STORE_PASS: + description: 'Store Password' + required: false + default: '' + ALIAS: + description: 'Certificate Alias' + required: false + default: '' + KEY_PASS: + description: 'Key Password' + required: false + default: '' + VERSION_NAME: + description: 'Version Name to be used for build' + required: false + default: '1.0.0' + VERSION_CODE: + description: 'Version Code to be used for build' + required: true + default: '1' + +runs: + using: "composite" + steps: + - name: Set up Java + uses: actions/setup-java@v2 + with: + java-version: 17 + distribution: 'adopt' + cache: 'gradle' + + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + cache: true + + - name: Build Android APK/AAB + shell: bash + env: + STORE_PASS: ${{ inputs.STORE_PASS }} + ALIAS: ${{ inputs.ALIAS }} + KEY_PASS: ${{ inputs.KEY_PASS }} + VERSION_NAME: ${{inputs.VERSION_NAME}} + VERSION_CODE: ${{inputs.VERSION_CODE}} + run: | + flutter build apk --build-name $VERSION_NAME --build-number $VERSION_CODE + flutter build appbundle --build-name $VERSION_NAME --build-number $VERSION_CODE + + - name: Store APK file + uses: actions/upload-artifact@v4 + with: + name: apk-files + path: | + build/app/outputs/flutter-apk \ No newline at end of file diff --git a/.github/actions/common/action.yml b/.github/actions/common/action.yml new file mode 100644 index 000000000..2ad59b786 --- /dev/null +++ b/.github/actions/common/action.yml @@ -0,0 +1,25 @@ +name: "Common Workflow" + +runs: + using: "composite" + steps: + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + cache: true + + - name: Fetch Flutter Dependencies + shell: bash + run: flutter pub get + + - name: Validate Code Format + shell: bash + run: dart format --output=none --set-exit-if-changed . + + - name: Analyze Code + shell: bash + run: flutter analyze + + - name: Run tests + shell: bash + run: flutter test \ No newline at end of file diff --git a/.github/actions/ios/action.yml b/.github/actions/ios/action.yml new file mode 100644 index 000000000..99d7110ad --- /dev/null +++ b/.github/actions/ios/action.yml @@ -0,0 +1,27 @@ +name: "iOS Workflow" + +inputs: + VERSION_NAME: + description: 'Version Name to be used for build' + required: false + default: '1.0.0' + VERSION_CODE: + description: 'Version Code to be used for build' + required: true + default: '1' + +runs: + using: "composite" + steps: + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + cache: true + + - name: Build iOS IPA + shell: bash + env: + VERSION_NAME: ${{inputs.VERSION_NAME}} + VERSION_CODE: ${{inputs.VERSION_CODE}} + run: | + flutter build ipa --no-codesign --build-name $VERSION_NAME --build-number $VERSION_CODE \ No newline at end of file diff --git a/.github/workflows/pull-request-comment.yml b/.github/workflows/pull-request-comment.yml new file mode 100644 index 000000000..e22813ceb --- /dev/null +++ b/.github/workflows/pull-request-comment.yml @@ -0,0 +1,128 @@ +name: Comment + +on: + workflow_run: + workflows: [ Build ] + types: + - completed + +jobs: + comment: + runs-on: ubuntu-latest + if: > + github.event.workflow_run.event == 'pull_request' + steps: + - name: Download artifact + uses: actions/github-script@v7 + with: + script: | + var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{github.event.workflow_run.id }}, + }); + var matchArtifact = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "pr" + })[0]; + var download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + var fs = require('fs'); + fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); + - run: unzip pr.zip + + - name: Build success + if: ${{ github.event.workflow_run.conclusion == 'success' }} + uses: actions/github-script@v7 + with: + script: | + var fs = require('fs') + var issue_number = Number(fs.readFileSync('./NR')); + const owner = context.repo.owner; + const repo = context.repo.repo; + var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner, + repo, + run_id: ${{github.event.workflow_run.id }}, + }); + var matchArtifact = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "apk-files" + })[0]; + const artifact_url = `https://github.com/${owner}/${repo}/actions/runs/${{ github.event.workflow_run.id }}/artifacts/${matchArtifact.id}`; + + const comments = await github.rest.issues.listComments({ + owner, + repo, + issue_number + }); + + let comment_id; + for (const comment of comments.data) { + if (comment.user.login === 'github-actions[bot]') { + comment_id = comment.id; + break; + } + } + + const body = `Build successful. APKs to test: ${artifact_url}`; + + if (comment_id) { + await github.rest.issues.updateComment({ + owner, + repo, + comment_id, + body + }); + } else { + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body + }); + } + + - name: Build failed + if: ${{ github.event.workflow_run.conclusion == 'failure' }} + uses: actions/github-script@v7 + with: + script: | + var fs = require('fs') + var issue_number = Number(fs.readFileSync('./NR')); + const owner = context.repo.owner; + const repo = context.repo.repo; + + const comments = await github.rest.issues.listComments({ + owner, + repo, + issue_number + }); + + let comment_id; + for (const comment of comments.data) { + if (comment.user.login === 'github-actions[bot]') { + comment_id = comment.id; + break; + } + } + + const body = `Build failed`; + + if (comment_id) { + await github.rest.issues.updateComment({ + owner, + repo, + comment_id, + body + }); + } else { + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body + }); + } \ No newline at end of file diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 000000000..b22c53086 --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,45 @@ +name: Build + +on: + pull_request: + branches: ["flutter"] + +jobs: + common: + name: Common Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Common Workflow + uses: ./.github/actions/common + + - name: Save PR number + run: | + mkdir -p ./pr + echo ${{ github.event.number }} > ./pr/NR + + - uses: actions/upload-artifact@v3 + with: + name: pr + path: pr/ + + android: + name: Android Flutter Build + needs: common + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Android Workflow + uses: ./.github/actions/android + + ios: + name: iOS Flutter Build + needs: common + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + + - name: iOS Workflow + uses: ./.github/actions/ios \ No newline at end of file diff --git a/.github/workflows/push-event.yml b/.github/workflows/push-event.yml new file mode 100644 index 000000000..df724f793 --- /dev/null +++ b/.github/workflows/push-event.yml @@ -0,0 +1,175 @@ +name: Push + +on: + push: + branches: ["flutter"] + +jobs: + common: + name: Common Build + runs-on: ubuntu-latest + outputs: + VERSION_NAME: ${{ steps.flutter-version.outputs.VERSION_NAME }} + VERSION_CODE: ${{ steps.flutter-version.outputs.VERSION_CODE }} + steps: + - uses: actions/checkout@v4 + + - name: Common Workflow + uses: ./.github/actions/common + + - name: Hydrate and Update Version + id: flutter-version + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + + git clone --branch=version https://${{ github.repository_owner }}:${{ github.token }}@github.com/${{ github.repository }} version + cd version + + # Read and increment version name + IFS='.' read -r major minor patch < versionName.txt + current_patch_version_name="$major.$minor.$patch" + echo "VERSION_NAME=$current_patch_version_name" >> $GITHUB_OUTPUT + + next_patch=$((patch + 1)) + next_patch_version_name="$major.$minor.$next_patch" + echo "$next_patch_version_name" > versionName.txt + + # Read and increment version code + read -r version_code < versionCode.txt + echo "VERSION_CODE=$version_code" >> $GITHUB_OUTPUT + + new_version_code=$((version_code + 1)) + echo "$new_version_code" > versionCode.txt + + # Force push to version branch + git checkout --orphan temporary + git add --all . + git commit -am "[Auto] Update versionName: $next_patch_version_name & versionCode: $new_version_code ($(date +%Y-%m-%d.%H:%M:%S))" + git branch -D version + git branch -m version + git push --force origin version + + android: + name: Android Flutter Build + needs: common + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Prepare Build Keys + if: ${{ github.repository == 'fossasia/pslab-android' }} + env: + ENCRYPTED_F10B5E0E5262_IV: ${{ secrets.ENCRYPTED_F10B5E0E5262_IV }} + ENCRYPTED_F10B5E0E5262_KEY: ${{ secrets.ENCRYPTED_F10B5E0E5262_KEY }} + run: | + bash scripts/prep-key.sh + + - name: Android Workflow + uses: ./.github/actions/android + with: + STORE_PASS: ${{ secrets.STORE_PASS }} + ALIAS: ${{ secrets.ALIAS }} + KEY_PASS: ${{ secrets.KEY_PASS }} + VERSION_NAME: ${{needs.common.outputs.VERSION_NAME}} + VERSION_CODE: ${{needs.common.outputs.VERSION_CODE}} + + - name: Upload APK + uses: actions/upload-artifact@v4 + with: + name: APK Generated + path: build/app/outputs/flutter-apk + + - name: Upload AAB Release + uses: actions/upload-artifact@v4 + with: + name: AAB Generated + path: build/app/outputs/bundle + + - name: Upload APK/AAB to apk branch + if: ${{ github.repository == 'fossasia/pslab-android' }} + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + + git clone --branch=apk https://${{ github.repository_owner }}:${{ github.token }}@github.com/${{ github.repository }} apk + cd apk + + branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}} + + echo "Removing previous files from branch" + + rm -rf pslab-$branch* + + ls + + echo "Copying new build files" + + find ../build/app/outputs/flutter-apk -type f \( -name '*.apk' -o -name '*.aab' \) -exec cp -v {} . \; + find ../build/app/outputs/bundle -type f \( -name '*.apk' -o -name '*.aab' \) -exec cp -v {} . \; + + ls + + echo "Renaming new build files" + + for file in app*; do + mv $file pslab-$branch-${file#*-} + done + + ls + + echo "Pushing to apk branch" + + git checkout --orphan temporary + git add --all . + git commit -am "[Auto] Update APK/AAB's from $branch ($(date +%Y-%m-%d.%H:%M:%S))" + git branch -D apk + git branch -m apk + git push --force origin apk + + - name: Setup Ruby + if: ${{ github.repository == 'fossasia/pslab-android' }} + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: true + + - name: Prepare Bundler + if: ${{ github.repository == 'fossasia/pslab-android' }} + run: | + bundle config path vendor/bundle + bundle install --jobs 4 --retry 3 + + - name: Update app in Closed Testing track + if: ${{ github.repository == 'fossasia/pslab-android' }} + run: | + git clone --branch=fastlane --depth=1 https://${{ github.repository_owner }}:${{ github.token }}@github.com/${{ github.repository }} fastlane + bundle exec fastlane uploadToClosedTesting + if [[ $? -ne 0 ]]; then + exit 1 + fi + + ios: + name: iOS Flutter Build + needs: common + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + + - name: iOS Workflow + uses: ./.github/actions/ios + with: + VERSION_NAME: ${{needs.common.outputs.VERSION_NAME}} + VERSION_CODE: ${{needs.common.outputs.VERSION_CODE}} + + update-release: + name: Update Draft Release + needs: [common, android, ios] + runs-on: ubuntu-latest + steps: + - name: Run Release Drafter + uses: release-drafter/release-drafter@v6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + version: ${{ needs.common.outputs.VERSION_NAME }} \ No newline at end of file