diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f2c641ed1..421cbeac9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,3 +4,4 @@ .github/workflows/nightly-build.yml @unschooledgamer .github/workflows/on-demand-preview-releases-PR.yml @unschooledgamer .github/workflows/community-release-notifier.yml @unschooledgamer +.github/workflows/nightly-release.yml @unschooledgamer diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 3f537dcfb..f259abfda 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -1,311 +1,207 @@ # Implement to use environment secrets someday, At the moment GH Actions doesn't support those in reusable workflows (ref: https://github.com/actions/runner/issues/1490) # at least that's what I found. - name: Nightly Build - on: - schedule: - # Fire every day at 7:00am UTC (Roughly before EU workday and after US workday) - - cron: "0 7 * * *" - push: - tags: - - nightly - workflow_call: - inputs: - is_PR: - default: false - type: boolean - description: If a Pull Request has triggered it. - is_trusted_pr: - default: false - type: boolean - description: If a Pull Request is from the same repository, and not a forked one. - PR_NUMBER: - required: true - type: number - description: The Pull Request that triggered this workflow - skip_tagging_and_releases: - required: false - default: true - type: boolean - description: Skips Tagging & releases, since workflow_call isn't available for github.event_name, default is true - secrets: - KEYSTORE_CONTENT: - required: true - description: Base64 encoded keystore file content - BUILD_JSON_CONTENT: - required: true - description: Base64 encoded build.json file content - outputs: - job_result: - description: "Build job result" - value: ${{ jobs.build.result }} - - workflow_dispatch: - inputs: - skip_tagging_and_releases: - required: false - default: true - type: boolean - description: Skips Tagging & releases, since workflow_call isn't available for github.event_name, default is true - - concurrency: - # Allow only one workflow per any non-`main` branch. - group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}-${{ inputs.is_PR && 'is_PR' || 'not_PR'}} - cancel-in-progress: true - - permissions: - contents: read - - env: - STORE_FILE_PATH: /tmp/app-debug.keystore - BUILD_JSON_PATH: build.json - VERSION_LABEL: ${{ (inputs.is_PR && 'pr') || 'nightly' }} - DISCORD_RELEASE_NOTIFIER_ENABLED: "true" - jobs: - build: - timeout-minutes: 60 - runs-on: ubuntu-latest - if: github.repository_owner == 'Acode-Foundation' - - permissions: - pull-requests: read - - outputs: - updated_version: ${{ steps.update-version.outputs.UPDATED_VERSION}} - NORMAL_APK_PATH: ${{ steps.update-version.outputs.NORMAL_APK_PATH }} - FDROID_APK_PATH: ${{ steps.update-version.outputs.FDROID_APK_PATH }} - tag_commit: ${{ steps.check-nightly-tag-force-update.outputs.tag_commit }} - release_required: ${{ steps.check-nightly-tag-force-update.outputs.release_required }} - steps: - - name: Fast Fail if secrets are missing - if: ${{ env.KEYSTORE_CONTENT == '' || env.BUILD_JSON_CONTENT == '' }} - env: - KEYSTORE_CONTENT: ${{ secrets.KEYSTORE_CONTENT }} - BUILD_JSON_CONTENT: ${{ secrets.BUILD_JSON_CONTENT }} - run: | - echo "::error title=Missing Secrets::KEYSTORE_CONTENT or BUILD_JSON_CONTENT secrets are missing! Aborting workflow." - exit 1 - - - name: Logging & summaries - run: | - echo "::group::Logging" - echo "🎯 github trigger event name: ${{ github.event_name }}" - echo "is_PR: ${{ inputs.is_PR }} " - echo "PR_NUMBER: ${{ inputs.PR_NUMBER }}" - echo "env: STORE_FILE_PATH: ${{ env.STORE_FILE_PATH }}" - echo "env: BUILD_JSON_PATH: ${{ env.BUILD_JSON_PATH }}" - echo "env: VERSION_LABEL: ${{ env. VERSION_LABEL }}" - echo "github sha: ${{ github.sha }}" - echo "should not skip tags, releases: ${{ ! inputs.skip_tagging_and_releases }} " - echo "🤐 env: NORMAL_APK_PATH: ${{ env.NORMAL_APK_PATH }}" - echo "🤐 env: FDROID_APK_PATH: ${{ env.FDROID_APK_PATH }}" - echo "::endgroup::" - - echo "## 🚀 Build Type: ${{ env.VERSION_LABEL }}" >> $GITHUB_STEP_SUMMARY - echo "is_PR: ${{ inputs.is_PR || 'NOT a PR' }}" >> $GITHUB_STEP_SUMMARY - echo "PR_NUMBER: ${{ inputs.PR_NUMBER || 'not a PR' }}" >> $GITHUB_STEP_SUMMARY - echo "should not skip tags, releases: ${{ ! inputs.skip_tagging_and_releases }}" >> $GITHUB_STEP_SUMMARY - - - name: Checkout Repository - uses: actions/checkout@v7 - with: - fetch-depth: 0 # Required for tags - # persists credentials locally if tagging and releases are not skipped. - persist-credentials: ${{ ! inputs.skip_tagging_and_releases }} - ref: ${{ (inputs.is_PR && inputs.PR_NUMBER) && github.event.pull_request.head.sha || '' }} - - - name: Set up Java 21 - uses: actions/setup-java@v5 - with: - distribution: 'temurin' - java-version: '21' - cache: ${{ (!(inputs.is_PR && inputs.PR_NUMBER) && github.ref == 'refs/heads/main' && 'gradle') || '' }} - - - name: Set up Node.js - uses: actions/setup-node@v6 - with: - node-version: 'lts/*' # or '18.x' for latest stable - cache: ${{ (!(inputs.is_PR && inputs.PR_NUMBER) && github.ref == 'refs/heads/main' && 'npm') || '' }} - - - name: Add keystore and build.json from secrets - if: ${{ !inputs.is_PR || inputs.is_trusted_pr }} - run: | - echo "${{ secrets.KEYSTORE_CONTENT }}" | base64 -d > ${{ env.STORE_FILE_PATH }} - echo "${{ secrets.BUILD_JSON_CONTENT }}" | base64 -d > ${{ env.BUILD_JSON_PATH }} - echo "Keystore and build.json added successfully." - - - name: Export Commit Hash & prev tag - run: | - echo "GIT_COMMIT=$(git rev-parse HEAD)" >> $GITHUB_ENV - echo "PREV_TAG=$(git describe --tags --abbrev=0 || git rev-list --max-parents=0 HEAD)" >> $GITHUB_ENV - - - name: Extract versionCode and version from config.xml - id: extract_version - run: | - if [ ! -f config.xml ]; then - echo "config.xml not found!" - exit 1 - fi - VERSION_CODE=$(grep 'versionCode=' config.xml | sed -E 's/.*versionCode="([0-9]+)".*/\1/') - VERSION=$(grep -oP 'version="\K[0-9.]+' config.xml) - echo "VERSION_CODE=$VERSION_CODE" >> $GITHUB_OUTPUT - echo "VERSION=$VERSION" >> $GITHUB_OUTPUT - echo "Extracted Version Code: $VERSION_CODE" - echo "Extracted Version: $VERSION" - - - name: Append Commit Hash to Version and Update config.xml - id: update-version - run: | - SHORT_COMMIT_HASH=$(echo "${{ env.GIT_COMMIT }}" | cut -c 1-7) - if ${{ inputs.is_PR || false }}; then - PR_NUMBER=${{ inputs.PR_NUMBER }} - else - PR_NUMBER= - fi - UPDATED_VERSION="${{ steps.extract_version.outputs.VERSION }}-${{ env.VERSION_LABEL }}.${PR_NUMBER:-$SHORT_COMMIT_HASH}" - echo "Updated Version: $UPDATED_VERSION" - - # Update config.xml with the new version - sed -i "s/version=\"${{ steps.extract_version.outputs.VERSION }}\"/version=\"$UPDATED_VERSION\"/g" config.xml - echo "Updated version in config.xml" - # Output the updated version - echo "UPDATED_VERSION=$UPDATED_VERSION" >> $GITHUB_ENV - echo "UPDATED_VERSION=$UPDATED_VERSION" >> $GITHUB_OUTPUT - echo "NORMAL_APK_PATH=/tmp/acode-debug-normal-${UPDATED_VERSION}.apk" >> $GITHUB_ENV - echo "FDROID_APK_PATH=/tmp/acode-debug-fdroid-${UPDATED_VERSION}.apk" >> $GITHUB_ENV - - - name: Install Node.js Packages - run: npm install - - - name: Install Cordova CLI - run: npm install -g cordova - - - name: Run npm setup - run: npm run setup - - - name: Run npm build paid dev apk - run: | - node utils/storage_manager.mjs y - npm run build paid dev apk - mv platforms/android/app/build/outputs/apk/debug/app-debug.apk ${{ env.NORMAL_APK_PATH }} - echo "VERSION: $UPDATED_VERSION" >> $GITHUB_STEP_SUMMARY - - - name: Upload APK Artifact - uses: actions/upload-artifact@v7 - with: - name: app-debug-${{ env.GIT_COMMIT }} - path: ${{ env.NORMAL_APK_PATH }} - - - name: Run npm build paid dev apk fdroid (for F-Droid) - if: ${{ !inputs.is_PR }} - run: | - node utils/storage_manager.mjs y - npm run build paid dev apk fdroid - mv platforms/android/app/build/outputs/apk/debug/app-debug.apk ${{ env.FDROID_APK_PATH }} - - - name: Upload APK Artifact - uses: actions/upload-artifact@v7 - if: ${{ !inputs.is_PR }} - with: - name: app-debug-fdroid-${{ env.GIT_COMMIT }} - path: ${{ env.FDROID_APK_PATH }} - - - name: remove keystore and build.json - if: ${{ !inputs.is_PR || inputs.is_trusted_pr }} - run: | - rm $STORE_FILE_PATH $BUILD_JSON_PATH - echo "Keystore and build.json removed successfully." - - - name: Check Nightly Tag - if: ${{ !inputs.skip_tagging_and_releases && !inputs.is_PR }} - id: check-nightly-tag-force-update - run: | - if git show-ref --quiet refs/tags/nightly; then - TAG_COMMIT=$(git rev-parse nightly) - else - TAG_COMMIT="" - fi - - if [ "$TAG_COMMIT" != "${{ env.GIT_COMMIT }}" ]; then - echo "release_required=true" >> "$GITHUB_OUTPUT" - else - echo "release_required=false" >> "$GITHUB_OUTPUT" - fi - echo "tag_commit=$TAG_COMMIT" >> "$GITHUB_OUTPUT" - - release: - needs: build - runs-on: ubuntu-latest - if: ${{ github.repository_owner == 'Acode-Foundation' && !inputs.is_PR && !inputs.skip_tagging_and_releases && needs.build.outputs.release_required == 'true' }} - permissions: - contents: write +name: Nightly Build + +on: + workflow_call: + inputs: + is_PR: + default: false + type: boolean + description: If a Pull Request has triggered it. + is_trusted_pr: + default: false + type: boolean + description: If a Pull Request is from the same repository (not a fork). + PR_NUMBER: + required: false + type: number + description: Pull Request number (for PR builds) + secrets: + KEYSTORE_CONTENT: + required: true + BUILD_JSON_CONTENT: + required: true + outputs: + job_result: + description: Build job result + value: ${{ jobs.build.result }} + updated_version: + description: Computed build version + value: ${{ jobs.build.outputs.updated_version }} + git_commit: + description: Commit SHA used for artifact naming + value: ${{ jobs.build.outputs.git_commit }} + normal_artifact_name: + description: Artifact name for normal APK + value: ${{ jobs.build.outputs.normal_artifact_name }} + fdroid_artifact_name: + description: Artifact name for fdroid APK + value: ${{ jobs.build.outputs.fdroid_artifact_name }} + + workflow_dispatch: + +permissions: + contents: read + +concurrency: + # Allow only one workflow per branch. + group: ${{ github.workflow }}-${{ github.ref_name }}-${{ inputs.is_PR && 'is_PR' || 'not_PR' }} + cancel-in-progress: true + +env: + STORE_FILE_PATH: /tmp/app-debug.keystore + BUILD_JSON_PATH: build.json + VERSION_LABEL: ${{ inputs.is_PR && 'pr' || 'nightly' }} + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 60 + if: github.repository_owner == 'Acode-Foundation' + permissions: + contents: read + pull-requests: read + + outputs: + updated_version: ${{ steps.version.outputs.updated_version }} + git_commit: ${{ steps.meta.outputs.git_commit }} + normal_artifact_name: ${{ steps.meta.outputs.normal_artifact_name }} + fdroid_artifact_name: ${{ steps.meta.outputs.fdroid_artifact_name }} + + steps: + - name: Validate required secrets (Fast Fail If missing on nightly builds) + if: ${{ !inputs.is_PR || inputs.is_trusted_pr }} + env: + KEYSTORE_CONTENT: ${{ secrets.KEYSTORE_CONTENT }} + BUILD_JSON_CONTENT: ${{ secrets.BUILD_JSON_CONTENT }} + run: | + [ -n "$KEYSTORE_CONTENT" ] && [ -n "$BUILD_JSON_CONTENT" ] || { echo "::error title=Missing secrets::KEYSTORE_CONTENT or BUILD_JSON_CONTENT is missing"; exit 1; } - outputs: - release_output_url: ${{ steps.release.outputs.url }} - RELEASE_NOTES: ${{ env.RELEASE_NOTES }} - steps: - - name: Checkout Repository - uses: actions/checkout@v7 - with: - fetch-depth: 0 - persist-credentials: true - - - name: Force-update nightly tag - run: | - git config --global user.name 'GitHub Actions' - git config --global user.email 'github-actions@github.com' - git tag -f nightly "${{ github.sha }}" - git push origin nightly --force - - - name: Download APK artifacts - uses: actions/download-artifact@v7 - with: - path: /tmp/artifacts - - # 🚨⚠️ WARNING: the GITHUB_TOKEN under this step, has access to write & read access to Contents, Pull Requests - # Which is why, it uses a fine-granted token with Read-Only Access to Public Repos Only. - - name: Generate Release Notes (Experimental) - id: gen-release-notes - continue-on-error: true - run: | - RELEASE_NOTES=$(node utils/scripts/generate-release-notes.js ${{ github.repository_owner }} Acode ${{ github.sha }} --format md --from-tag ${{ needs.build.outputs.tag_commit }} --important-only --quiet --changelog-only || true) - { - echo "RELEASE_NOTES<> $GITHUB_ENV - env: - GITHUB_TOKEN: ${{ secrets.NIGHTLY_RELEASE_NOTES_GH_TOKEN }} - - - name: Release Nightly Version - uses: softprops/action-gh-release@v3 - env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - with: - prerelease: true - name: ${{ needs.build.outputs.updated_version }} - tag_name: ${{ needs.build.outputs.updated_version }} - files: | - /tmp/artifacts/**/acode-debug-normal-*.apk - /tmp/artifacts/**/acode-debug-fdroid-*.apk - body: | - Automated Nightly (pre-release) Releases for Today - - [Compare Changes](https://github.com/${{ github.repository }}/compare/${{ needs.build.outputs.tag_commit }}...${{ github.sha }}) - - ${{ env.RELEASE_NOTES }} - - community-release-notifier: - needs: [build, release] -# Run only if push tags, or triggered by a schedule - if: ${{ github.repository_owner == 'Acode-Foundation' && (contains(fromJSON('["push", "schedule"]'), github.event_name) || ! inputs.skip_tagging_and_releases) && needs.release.outputs.release_output_url }} - uses: Acode-Foundation/acode/.github/workflows/community-release-notifier.yml@main - with: - tag_name: ${{ needs.build.outputs.updated_version }} - url: ${{ needs.release.outputs.release_output_url }} - body: ${{ needs.release.outputs.RELEASE_NOTES }} - secrets: - DISCORD_WEBHOOK_RELEASE_NOTES: ${{ secrets.DISCORD_WEBHOOK_RELEASE_NOTES }} - TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} - TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} - TELEGRAM_MESSAGE_THREAD_ID: ${{ secrets.TELEGRAM_MESSAGE_THREAD_ID }} + - name: Logging & summaries + run: | + echo "::group::Logging" + echo "🎯 github trigger event name: ${{ github.event_name }}" + echo "is_PR: ${{ inputs.is_PR }} " + echo "PR_NUMBER: ${{ inputs.PR_NUMBER }}" + echo "env: STORE_FILE_PATH: ${{ env.STORE_FILE_PATH }}" + echo "env: BUILD_JSON_PATH: ${{ env.BUILD_JSON_PATH }}" + echo "env: VERSION_LABEL: ${{ env.VERSION_LABEL }}" + echo "github sha: ${{ github.sha }}" + echo "should skip signing with our keys: ${{ inputs.is_PR && !inputs.is_trusted_pr && 'true' || 'false' }}" + echo "::endgroup::" + echo "## 🚀 Build Type: ${{ env.VERSION_LABEL }}" >> $GITHUB_STEP_SUMMARY + echo "is_PR: ${{ inputs.is_PR || 'NOT a PR' }}" >> $GITHUB_STEP_SUMMARY + echo "PR_NUMBER: ${{ inputs.PR_NUMBER || 'not a PR' }}" >> $GITHUB_STEP_SUMMARY + + - name: Checkout repository + uses: actions/checkout@v7 + with: + fetch-depth: 0 # Required for tags + persist-credentials: false + ref: ${{ (inputs.is_PR && inputs.PR_NUMBER) && github.event.pull_request.head.sha || '' }} + + - name: Setup Java + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: "21" + cache: ${{ (!(inputs.is_PR && inputs.PR_NUMBER) && github.ref == 'refs/heads/main' && 'gradle') || '' }} + + - name: Setup Node + uses: actions/setup-node@v6 + with: + node-version: "lts/*" # or '18.x' for latest stable + cache: ${{ (!(inputs.is_PR && inputs.PR_NUMBER) && github.ref == 'refs/heads/main' && 'npm') || '' }} + + - name: Compute metadata + id: meta + run: | + GIT_COMMIT="$(git rev-parse HEAD)" + echo "git_commit=$GIT_COMMIT" >> "$GITHUB_OUTPUT" + echo "normal_artifact_name=app-debug-$GIT_COMMIT" >> "$GITHUB_OUTPUT" + echo "fdroid_artifact_name=app-debug-fdroid-$GIT_COMMIT" >> "$GITHUB_OUTPUT" + + - name: Inject signing files + if: ${{ !inputs.is_PR || inputs.is_trusted_pr }} + run: | + echo "${{ secrets.KEYSTORE_CONTENT }}" | base64 -d > "${{ env.STORE_FILE_PATH }}" + echo "${{ secrets.BUILD_JSON_CONTENT }}" | base64 -d > "${{ env.BUILD_JSON_PATH }}" + + - name: Extract base version + id: base + run: | + VERSION=$(grep -oP 'version="\K[0-9.]+' config.xml) + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Compose and set build version in config.xml + id: version + run: | + SHORT="$(echo "${{ steps.meta.outputs.git_commit }}" | cut -c1-7)" + if ${{ inputs.is_PR || false }}; then + SUFFIX="${{ inputs.PR_NUMBER }}" + else + SUFFIX="$SHORT" + fi + UPDATED="${{ steps.base.outputs.version }}-${{ env.VERSION_LABEL }}.$SUFFIX" + sed -i "s/version=\"${{ steps.base.outputs.version }}\"/version=\"$UPDATED\"/g" config.xml + echo "updated_version=$UPDATED" >> "$GITHUB_OUTPUT" + echo "NORMAL_APK_PATH=/tmp/acode-debug-normal-$UPDATED.apk" >> "$GITHUB_ENV" + echo "FDROID_APK_PATH=/tmp/acode-debug-fdroid-$UPDATED.apk" >> "$GITHUB_ENV" + + - name: Install deps + run: | + npm install + npm install -g cordova + npm run setup + + - name: Build normal APK + run: | + node utils/storage_manager.mjs y + npm run build paid dev apk + mv platforms/android/app/build/outputs/apk/debug/app-debug.apk ${{ env.NORMAL_APK_PATH }} + + - name: Upload normal artifact + uses: actions/upload-artifact@v7 + with: + name: ${{ steps.meta.outputs.normal_artifact_name }} + path: ${{ env.NORMAL_APK_PATH }} + + - name: Build fdroid APK + if: ${{ !inputs.is_PR }} + run: | + node utils/storage_manager.mjs y + npm run build paid dev apk fdroid + mv platforms/android/app/build/outputs/apk/debug/app-debug.apk ${{ env.FDROID_APK_PATH }} + + - name: Upload fdroid artifact + if: ${{ !inputs.is_PR }} + uses: actions/upload-artifact@v7 + with: + name: ${{ steps.meta.outputs.fdroid_artifact_name }} + path: ${{ env.FDROID_APK_PATH }} + + - name: Cleanup signing files + if: ${{ !inputs.is_PR || inputs.is_trusted_pr }} + run: rm -f ${{ env.STORE_FILE_PATH }} ${{ env.BUILD_JSON_PATH }} + + - name: Post Build - SHA256 signature computation (output) + id: sha256 + run: | + echo "normal_apk_sha256=$(sha256sum ${{ env.NORMAL_APK_PATH }} | awk '{print $1}')" >> "$GITHUB_OUTPUT" + if [ -f "${{ env.FDROID_APK_PATH }}" ]; then + echo "fdroid_apk_sha256=$(sha256sum ${{ env.FDROID_APK_PATH }} | awk '{print $1}')" >> "$GITHUB_OUTPUT" + fi + + - name: Post build summary (Table) + run: | + echo "## 🏗️ Build Summary" >> $GITHUB_STEP_SUMMARY + echo "| Key | Value |" >> $GITHUB_STEP_SUMMARY + echo "| --- | ----- |" >> $GITHUB_STEP_SUMMARY + echo "| Build Version | ${{ steps.version.outputs.updated_version }} |" >> $GITHUB_STEP_SUMMARY + echo "| Git Commit | ${{ steps.meta.outputs.git_commit }} |" >> $GITHUB_STEP_SUMMARY + echo "| Normal APK Artifact Name | ${{ steps.meta.outputs.normal_artifact_name }} |" >> $GITHUB_STEP_SUMMARY + echo "| F-Droid APK Artifact Name | ${{ steps.meta.outputs.fdroid_artifact_name || 'N/A' }} |" >> $GITHUB_STEP_SUMMARY + echo "| Normal APK Artifact SHA256 | ${{ steps.sha256.outputs.normal_apk_sha256 }} |" >> $GITHUB_STEP_SUMMARY + echo "| F-Droid APK Artifact SHA256 | ${{ steps.sha256.outputs.fdroid_apk_sha256 || 'N/A' }} |" >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/workflows/nightly-release.yml b/.github/workflows/nightly-release.yml new file mode 100644 index 000000000..1f17fc49d --- /dev/null +++ b/.github/workflows/nightly-release.yml @@ -0,0 +1,228 @@ +name: Nightly Release + +on: + schedule: + # Fire every day at 7:00am UTC (Roughly before EU workday and after US workday) + - cron: "0 7 * * *" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true + +jobs: + build: + if: ${{ github.repository_owner == 'Acode-Foundation' }} + uses: Acode-Foundation/acode/.github/workflows/nightly-build.yml@main + permissions: + contents: read + pull-requests: read + secrets: + KEYSTORE_CONTENT: ${{ secrets.KEYSTORE_CONTENT }} + BUILD_JSON_CONTENT: ${{ secrets.BUILD_JSON_CONTENT }} + with: + is_PR: false + is_trusted_pr: false + + release: + needs: build + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'Acode-Foundation' && needs.build.result == 'success' }} + permissions: + contents: write + id-token: write + + outputs: + should_publish: ${{ steps.publish_gate.outputs.should_publish }} + release_output_url: ${{ steps.gh_release.outputs.url }} + release_notes: ${{ env.RELEASE_NOTES }} + previous_tag_commit: ${{ steps.prev.outputs.tag_commit }} + nightly_tag_name: ${{ needs.build.outputs.updated_version }} + subjects_file: ${{ steps.subjects_file.outputs.handle }} + + steps: + - name: Checkout repository + uses: actions/checkout@v7 + with: + fetch-depth: 0 # Required for tags + persist-credentials: true + + - name: Install cosign + uses: sigstore/cosign-installer@v4.1.0 + + - name: Set Publish Metadata + shell: bash + run: | + set -euo pipefail + echo "ARTIFACTS_DIR=${{ env.ARTIFACTS_DIR}}" >> $GITHUB_ENV + echo "NORMAL_ARTIFACT_NAME=app-debug-${{ github.sha }}" >> $GITHUB_ENV + echo "FDROID_ARTIFACT_NAME=app-debug-fdroid-${{ github.sha }}" >> $GITHUB_ENV + echo "SUBJECTS_FILE=${{ env.SUBJECTS_FILE }}" >> $GITHUB_ENV + env: + ARTIFACTS_DIR: ${{ runner.temp }}/artifacts + SUBJECTS_FILE: ${{ runner.temp }}/slsa-subjects.txt + + + - name: Download normal APK artifact + uses: actions/download-artifact@v8 + with: + name: ${{ needs.build.outputs.normal_artifact_name }} + path: ${{ env.ARTIFACTS_DIR }}/normal + + - name: Download fdroid APK artifact + uses: actions/download-artifact@v8 + with: + name: ${{ needs.build.outputs.fdroid_artifact_name }} + path: ${{ env.ARTIFACTS_DIR }}/fdroid + + - name: Resolve previous nightly tag commit + id: prev + shell: bash + run: | + set -euo pipefail + VERSION_TAG="${{ needs.build.outputs.updated_version }}" + FALLBACK_TAG="nightly" + git fetch --tags --force + + if git show-ref --quiet "refs/tags/${VERSION_TAG}"; then + echo "tag_exists=true" >> "$GITHUB_OUTPUT" + echo "tag_commit=$(git rev-parse "${VERSION_TAG}")" >> "$GITHUB_OUTPUT" + elif git show-ref --quiet "refs/tags/${FALLBACK_TAG}"; then + echo "tag_exists=true" >> "$GITHUB_OUTPUT" + echo "tag_commit=$(git rev-parse "${FALLBACK_TAG}")" >> "$GITHUB_OUTPUT" + else + echo "tag_exists=false" >> "$GITHUB_OUTPUT" + echo "tag_commit=" >> "$GITHUB_OUTPUT" + fi + + - name: Decide whether to publish + id: publish_gate + shell: bash + run: | + set -euo pipefail + if [[ "${{ steps.prev.outputs.tag_exists }}" != "true" || "${{ steps.prev.outputs.tag_commit }}" != "${GITHUB_SHA}" ]]; then + echo "should_publish=true" >> "$GITHUB_OUTPUT" + else + echo "should_publish=false" >> "$GITHUB_OUTPUT" + fi + + - name: Build SLSA subjects file + if: steps.publish_gate.outputs.should_publish == 'true' + shell: bash + run: | + set -euo pipefail + find ${{ env.ARTIFACTS_DIR }} -type f -name '*.apk' -print0 | sort -z | while IFS= read -r -d '' f; do + sha256sum "$f" + done | base64 -w0 > "${SUBJECTS_FILE}" + + test -s "${SUBJECTS_FILE}" || { echo "No subjects found"; exit 1; } + env: + ARTIFACTS_DIR: ${{ env.ARTIFACTS_DIR }} + SUBJECTS_FILE: ${{ env.SUBJECTS_FILE }} + + + - name: Generate per-APK sha256 files + shell: bash + run: | + set -euo pipefail + find ${{ env.ARTIFACTS_DIR }} -type f -name '*.apk' -print0 | while IFS= read -r -d '' f; do + sha256sum "$f" > "${f}.sha256" + done + + - name: Create subjects file handle + id: subjects_file + if: steps.publish_gate.outputs.should_publish == 'true' + uses: slsa-framework/slsa-github-generator/actions/generator/generic/create-base64-subjects-from-file@v2.1.0 + with: + path: ${{ env.SUBJECTS_FILE }} + + - name: Move nightly tag + if: steps.publish_gate.outputs.should_publish == 'true' + run: | + git config --global user.name 'GitHub Actions' + git config --global user.email 'github-actions@github.com' + git tag -f nightly "${GITHUB_SHA}" + git push origin refs/tags/nightly --force + + - name: Generate release notes + if: steps.publish_gate.outputs.should_publish == 'true' + id: notes + continue-on-error: true + run: | + RELEASE_NOTES="$(node utils/scripts/generate-release-notes.js \ + ${{ github.repository_owner }} Acode ${{ github.sha }} \ + --format md --from-tag ${{ steps.prev.outputs.tag_commit }} --important-only --quiet || true)" + { + echo "RELEASE_NOTES<> "$GITHUB_ENV" + env: + GITHUB_TOKEN: ${{ secrets.NIGHTLY_RELEASE_NOTES_GH_TOKEN }} + + - name: Create nightly prerelease + if: steps.publish_gate.outputs.should_publish == 'true' + id: gh_release + uses: softprops/action-gh-release@v3 + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + with: + prerelease: true + tag_name: ${{ needs.build.outputs.updated_version }} + name: ${{ needs.build.outputs.updated_version }} + files: | + ${{ env.ARTIFACTS_DIR }}/normal/*.apk + ${{ env.ARTIFACTS_DIR }}/normal/*.apk.sha256 + ${{ env.ARTIFACTS_DIR }}/fdroid/*.apk + ${{ env.ARTIFACTS_DIR }}/fdroid/*.apk.sha256 + body: | + Automated Nightly pre-release + + [Compare Changes](https://github.com/${{ github.repository }}/compare/${{ steps.prev.outputs.tag_commit }}...${{ github.sha }}) + + ${{ env.RELEASE_NOTES }} + + - name: Post Job summary + if: always() + run: | + echo "| Variable | Value |" >> "$GITHUB_STEP_SUMMARY" + echo "| --- | --- |" >> "$GITHUB_STEP_SUMMARY" + echo "| should_publish | ${{ steps.publish_gate.outputs.should_publish == 'true' && 'true' || 'false' }} |" >> "$GITHUB_STEP_SUMMARY" + echo "| (pre-release versioned/nightly tag) tag_exists | ${{ steps.prev.outputs.tag_exists }} |" >> "$GITHUB_STEP_SUMMARY" + echo "| (pre-release versioned/nightly tag) tag_commit | ${{ steps.prev.outputs.tag_commit }} |" >> "$GITHUB_STEP_SUMMARY" + echo "| release_output_url | ${{ steps.gh_release.outputs.url || 'N/A' }} |" >> "$GITHUB_STEP_SUMMARY" + + provenance: + name: Generate SLSA provenance + needs: [release] + if: needs.release.outputs.should_publish == 'true' + permissions: + actions: read + id-token: write + contents: write + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 + with: + base64-subjects-as-file: "${{ needs.release.outputs.subjects_file }}" + upload-assets: true + upload-tag-name: "${{ needs.release.outputs.nightly_tag_name }}" + draft-release: false + + community-release-notifier: + name: Notify community of nightly release + needs: [release] + if: ${{ needs.release.outputs.should_publish == 'true' && needs.release.outputs.release_output_url }} + permissions: + contents: read + uses: Acode-Foundation/acode/.github/workflows/community-release-notifier.yml@main + with: + url: ${{ needs.release.outputs.release_output_url }} + tag_name: ${{ needs.release.outputs.nightly_tag_name }} + body: ${{ needs.release.outputs.release_notes }} + secrets: + DISCORD_WEBHOOK_RELEASE_NOTES: ${{ secrets.DISCORD_WEBHOOK_RELEASE_NOTES }} + TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} + TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} + TELEGRAM_MESSAGE_THREAD_ID: ${{ secrets.TELEGRAM_MESSAGE_THREAD_ID }} \ No newline at end of file diff --git a/.github/workflows/on-demand-preview-releases-PR.yml b/.github/workflows/on-demand-preview-releases-PR.yml index fb1010918..5a584d40d 100644 --- a/.github/workflows/on-demand-preview-releases-PR.yml +++ b/.github/workflows/on-demand-preview-releases-PR.yml @@ -8,11 +8,8 @@ on: types: [labeled, synchronize] # defined at workflow-level as the workflow, Requires these permissions to function. -permissions: - contents: write # due to nightly workflow complaining, even tho release step isn't executed. - pull-requests: write - # All Pull Requests are issues, but not all issues are Pull Requests (like GitHub says 🙃) - issues: write +permissions: + contents: read concurrency: # Allow only one workflow per any non-`main` branch. @@ -24,14 +21,15 @@ jobs: name: Trigger Preview Release (if conditions met) permissions: contents: read - pull_requests: write + pull-requests: write issues: write if: | github.event.pull_request.draft == false && (github.repository_owner == 'Acode-Foundation' && (!contains(github.event.pull_request.labels.*.name, 'DO NOT PREVIEW RELEASE') && (contains(github.event.pull_request.labels.*.name, 'Bypass check - PREVIEW RELEASE') - || contains(github.event.pull_request.labels.*.name, 'CI: RUN ON-DEMAND PREVIEW RELEASES'))) + || contains(github.event.pull_request.labels.*.name, 'CI: RUN ON-DEMAND PREVIEW RELEASES') + )) ) runs-on: ubuntu-latest @@ -63,11 +61,11 @@ jobs: message: | ⚒️ Starting a workflow to build, Your On-Demand Preview Release/build for ${{ github.event.pull_request.html_url || github.event.pull_request.url }}. - status: **\`🟡 in_progress\`** + status: **`🟡 in_progress`** Kindly wait, this message may be updated with success or failure status. - For Owners: Please [Click here to view that github actions](https://github.com/${{ github.repository}}/actions/runs/${{ github.run_id }})/ + Please [Click here to view that github actions](https://github.com/${{ github.repository}}/actions/runs/${{ github.run_id }})/ env: PR: ${{ github.event.pull_request.number }} @@ -77,7 +75,7 @@ jobs: needs: job_trigger permissions: contents: read - pull_requests: read + pull-requests: read issues: read secrets: KEYSTORE_CONTENT: ${{ secrets.KEYSTORE_CONTENT }} @@ -87,13 +85,12 @@ jobs: is_PR: true PR_NUMBER: ${{ github.event.pull_request.number }} is_trusted_pr: ${{ github.event.pull_request.head.repo.full_name == github.repository }} - skip_tagging_and_releases: true update_Last_Comment: needs: [job_trigger,trigger_builder] permissions: contents: read - + pull-requests: write runs-on: ubuntu-latest if: ${{ github.repository_owner == 'Acode-Foundation' && always() && contains(fromJSON('["failure","cancelled", "success"]'), needs.trigger_builder.result) }} steps: