refactor: reorganize repository structure #177
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| # SPDX-FileCopyrightText: NONE | |
| # SPDX-License-Identifier: CC0-1.0 | |
| name: "Auto-nightly" | |
| permissions: {} | |
| on: | |
| push: | |
| paths: | |
| - "LICENSES/*" | |
| - "docs/*.rst" | |
| - "includes/*" | |
| - "tools/*.jar" | |
| - "zip-content/**" | |
| - "CHANGELOG.rst" | |
| - "LICENSE*.rst" | |
| - "build.sh" | |
| - "conf-*.sh" | |
| branches: | |
| - "**" | |
| schedule: | |
| # At 02:00 AM, every 6 days (UTC) | |
| - cron: "0 2 */6 * *" | |
| jobs: | |
| nightly: | |
| name: "Nightly" | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| if: "${{ github.event_name == 'push' }}" | |
| concurrency: | |
| group: "${{ github.repository_id }}-${{ github.workflow }}-nightly" | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write # Needed to delete a release and to modify a tag | |
| id-token: write # Needed to attest build provenance | |
| attestations: write # Needed to attest build provenance | |
| env: | |
| GITHUB_REPOSITORY_DEFAULT_BRANCH: "${{ github.event.repository.default_branch }}" | |
| steps: | |
| - name: "Checkout sources" | |
| uses: actions/checkout@v6 | |
| with: | |
| lfs: false | |
| - name: "Setup Java" | |
| uses: actions/setup-java@v5 | |
| with: | |
| distribution: "temurin" | |
| java-version-file: ".tool-versions" | |
| - name: "Use build cache" | |
| uses: actions/cache@v5 | |
| timeout-minutes: 5 | |
| with: | |
| key: "build-${{ hashFiles('conf/full.inc.sh') }}" | |
| restore-keys: "build-" | |
| path: "cache/build" | |
| enableCrossOsArchive: true | |
| - name: "Use LFS cache" | |
| uses: actions/cache@v5 | |
| timeout-minutes: 5 | |
| with: | |
| key: "lfs-${{ hashFiles('conf/lfs.inc.sh') }}" | |
| restore-keys: "lfs-" | |
| path: "cache/lfs" | |
| enableCrossOsArchive: true | |
| - name: "Build the flashable OTA zip" | |
| id: "build" | |
| shell: bash | |
| timeout-minutes: 10 | |
| run: | | |
| # Building... | |
| BUILD_TYPE='oss' '${{ github.workspace }}/build.sh' | |
| - name: "Attest build provenance" | |
| id: "attest" | |
| uses: actions/attest-build-provenance@v4 | |
| timeout-minutes: 10 | |
| if: "${{ vars.NIGHTLY_ATTESTATION == 'true' && github.run_attempt == '1' && steps.build.outputs.ZIP_IS_ALPHA == 'true' && steps.build.outputs.ZIP_BUILD_TYPE_SUPPORTED == 'true' }}" | |
| with: | |
| subject-path: "${{ steps.build.outputs.ZIP_FOLDER }}/*.zip" | |
| show-summary: false | |
| - name: "ZIP info" | |
| id: "info" | |
| shell: bash | |
| run: | | |
| # Retrieving informations... | |
| test -n '${{ steps.build.outputs.ZIP_FOLDER }}' || exit 3 | |
| ZIP_FILENAME='${{ steps.build.outputs.ZIP_FILENAME }}' | |
| ZIP_VERSION='${{ steps.build.outputs.ZIP_VERSION }}' | |
| ZIP_SHORT_COMMIT_ID='${{ steps.build.outputs.ZIP_SHORT_COMMIT_ID }}' | |
| ZIP_IS_ALPHA='${{ steps.build.outputs.ZIP_IS_ALPHA }}' | |
| ZIP_BUILD_TYPE='${{ steps.build.outputs.ZIP_BUILD_TYPE }}' | |
| ZIP_BUILD_TYPE_SUPPORTED='${{ steps.build.outputs.ZIP_BUILD_TYPE_SUPPORTED }}' | |
| ZIP_BRANCH_NAME='${{ steps.build.outputs.ZIP_BRANCH_NAME }}' | |
| ZIP_MD5='${{ steps.build.outputs.ZIP_MD5 }}' | |
| ZIP_SHA256='${{ steps.build.outputs.ZIP_SHA256 }}' | |
| ZIP_ATTESTATION_URL='${{ steps.attest.outputs.attestation-url }}' | |
| # Displaying informations... | |
| printf '%s\n' "::notice::Filename: ${ZIP_FILENAME:-Missing}" | |
| printf '%s\n' "::notice::Version: ${ZIP_VERSION:?} - Short commit ID: ${ZIP_SHORT_COMMIT_ID:?} - Is alpha: ${ZIP_IS_ALPHA:?} - Build type: ${ZIP_BUILD_TYPE:?}" | |
| printf '%s\n' "::notice::Build type supported: ${ZIP_BUILD_TYPE_SUPPORTED:?}" | |
| if '${{ github.ref_name && github.ref_name != github.event.repository.default_branch }}'; then | |
| ZIP_RETENTION_DAYS=30 | |
| printf '%s\n' '::notice::Branch: ${{ github.ref_name }}' | |
| else | |
| ZIP_RETENTION_DAYS=10 | |
| fi | |
| printf '%s\n' "::notice::MD5: ${ZIP_MD5:-Missing}" | |
| printf '%s\n' "::notice::SHA-256: ${ZIP_SHA256:-Missing}" | |
| printf '%s\n' "::notice::Logs retention days: ${{ github.retention_days }}" | |
| printf '%s\n' "::notice::Artifacts retention days: ${ZIP_RETENTION_DAYS:?}" | |
| printf '%s\n' "::notice::Attestation: ${ZIP_ATTESTATION_URL:-Missing}" | |
| # Outputs | |
| printf 'ZIP_RETENTION_DAYS=%s\n' "${ZIP_RETENTION_DAYS:?}" 1>> "${GITHUB_OUTPUT?}" | |
| # Preparing temp folder... | |
| export TMPDIR="${TMPDIR:-${RUNNER_TEMP:-${TMP:-${TEMP:-/tmp}}}}" || exit "${?}" | |
| our_tmp_dir="$(mktemp -d -t -- "RELEASE-${ZIP_IS_ALPHA:?}-XXXXXX")" || exit "${?}" | |
| test -n "${our_tmp_dir?}" || exit "${?}" | |
| rm -r -f -- "${our_tmp_dir:?}"/* || exit "${?}" # Empty our temp dir (should be already empty, but we must be sure) | |
| # Preparing release notes... | |
| release_type='nightly' | |
| repo_url='${{ github.server_url }}/${{ github.repository }}' | |
| { | |
| printf '%s' "**If you'd like to support my work, you can find donation details on " | |
| printf '%s\n' "[this page](${repo_url:?}/blob/main/docs/DONATE.rst)." | |
| printf '%s\n' 'Contributions are greatly appreciated but always optional.**' | |
| printf '\n' | |
| if test "${release_type:?}" != 'release'; then | |
| printf '%s\n\n' "Latest automatically built ZIP ($(date -u -- '+%Y/%m/%d' || :))." | |
| fi | |
| printf '%s\n\n' '## Verification' | |
| test -z "${ZIP_SHA256?}" || printf '%s\n' "SHA-256: ${ZIP_SHA256:?}" | |
| test -z "${ZIP_ATTESTATION_URL?}" || printf '\n%s\n' "Attestation: ${ZIP_ATTESTATION_URL:?}" | |
| if test "${release_type:?}" = 'release'; then | |
| printf '\n' | |
| printf '%s\n\n' '## Changelog' | |
| printf '\n%s\n' '[**Changelog**](./CHANGELOG.rst).' | |
| fi | |
| } 1> "${our_tmp_dir:?}/release-notes.md" || exit "${?}" | |
| printf 'ZIP_RELEASE_NOTES=%s\n' "${our_tmp_dir:?}/release-notes.md" 1>> "${GITHUB_OUTPUT?}" | |
| # Preparing attestation file... | |
| old_attest_file='${{ steps.attest.outputs.bundle-path }}' | |
| if test -n "${old_attest_file?}"; then | |
| new_attest_file="${our_tmp_dir:?}/${release_type:?}-$(basename -- "${old_attest_file:?}")" || exit "${?}" | |
| cp -f -T -- "${old_attest_file:?}" "${new_attest_file:?}" || exit "${?}" | |
| printf 'ZIP_ATTESTATION_FILE=%s\n' "${new_attest_file:?}" 1>> "${GITHUB_OUTPUT?}" | |
| fi | |
| - name: "Nightly logic" | |
| id: "nightly-logic" | |
| shell: bash | |
| run: | | |
| # Nightly logic... | |
| UPDATE_TAG='false' | |
| UPDATE_RELEASE='false' | |
| if '${{ github.ref_name == github.event.repository.default_branch && github.run_attempt == '1' && steps.build.outputs.ZIP_IS_ALPHA == 'true' }}'; then | |
| UPDATE_TAG='true' | |
| fi | |
| if test "${UPDATE_TAG:?}" = 'true' && '${{ steps.build.outputs.ZIP_BUILD_TYPE_SUPPORTED == 'true' }}'; then | |
| UPDATE_RELEASE='true' | |
| fi | |
| printf 'UPDATE_TAG=%s\n' "${UPDATE_TAG:?}" 1>> "${GITHUB_OUTPUT?}" | |
| printf 'UPDATE_RELEASE=%s\n' "${UPDATE_RELEASE:?}" 1>> "${GITHUB_OUTPUT?}" | |
| printf 'Update tag? %s\n' "${UPDATE_TAG:?}" | |
| printf 'Update release? %s\n' "${UPDATE_RELEASE:?}" | |
| - name: "Delete previous nightly release" | |
| uses: actions/github-script@v8 | |
| if: "${{ steps.nightly-logic.outputs.UPDATE_RELEASE == 'true' }}" | |
| with: | |
| retries: 3 | |
| script: | | |
| /* jshint esversion: 11 */ | |
| const response = await github.rest.repos.getReleaseByTag({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| tag: 'nightly' | |
| }).catch(response => response); | |
| if(response && response.status === 404) { | |
| // There is no previous nightly release, nothing to do | |
| } else if(response && response.status >= 200 && response.status < 300 && response.data && response.data.id && response.data.tag_name === 'nightly') { | |
| await github.rest.repos.deleteRelease({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| release_id: response.data.id | |
| }); | |
| console.log('Previous nightly release deleted: ' + response.data.name + ' (' + response.data.id + ')'); | |
| } else { | |
| if(response && response.message) console.error('::error::' + response.message); | |
| throw new Error('getReleaseByTag failed: response is undefined or missing data.'); | |
| } | |
| - name: "Update nightly tag" | |
| uses: actions/github-script@v8 | |
| if: "${{ steps.nightly-logic.outputs.UPDATE_TAG == 'true' }}" | |
| with: | |
| retries: 3 | |
| script: | | |
| /* jshint esversion: 11 */ | |
| const response = await github.rest.git.updateRef({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: 'tags/nightly', | |
| sha: context.sha, | |
| force: true | |
| }).catch(response => response); | |
| if(response && response.status === 200) { | |
| console.log('Nightly tag updated.'); | |
| } else if(response && response.status === 422 && response.message === 'Reference does not exist') { | |
| const responseCreate = await github.rest.git.createRef({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: 'refs/tags/nightly', | |
| sha: context.sha | |
| }).catch(responseCreate => responseCreate); | |
| if(responseCreate && responseCreate.status === 201) { | |
| console.log('Nightly tag created.'); | |
| } else { | |
| let errorMsg = 'createRef failed'; | |
| if(responseCreate && responseCreate.status && responseCreate.message) errorMsg += ' with error ' + responseCreate.status + ' (' + responseCreate.message + ')'; | |
| throw new Error(errorMsg); | |
| } | |
| } else { | |
| let errorMsg = 'updateRef failed'; | |
| if(response && response.status && response.message) errorMsg += ' with error ' + response.status + ' (' + response.message + ')'; | |
| throw new Error(errorMsg); | |
| } | |
| - name: "Create nightly release" | |
| uses: softprops/action-gh-release@v2 | |
| if: "${{ steps.nightly-logic.outputs.UPDATE_RELEASE == 'true' }}" | |
| with: | |
| name: "${{ steps.build.outputs.ZIP_VERSION }} nightly" | |
| tag_name: "nightly" | |
| target_commitish: "${{ github.sha }}" | |
| body_path: "${{ steps.info.outputs.ZIP_RELEASE_NOTES }}" | |
| append_body: false | |
| generate_release_notes: false | |
| draft: false | |
| prerelease: true | |
| make_latest: false | |
| overwrite_files: false | |
| files: | | |
| ${{ steps.build.outputs.ZIP_FOLDER }}/*.zip* | |
| ${{ steps.info.outputs.ZIP_ATTESTATION_FILE }} | |
| fail_on_unmatched_files: true | |
| - name: "Upload artifacts" | |
| uses: actions/upload-artifact@v7 | |
| if: "${{ steps.build.outputs.ZIP_BUILD_TYPE_SUPPORTED == 'true' }}" | |
| with: | |
| name: "${{ github.event.repository.name }} ${{ steps.build.outputs.ZIP_BRANCH_NAME || github.ref_name }} g${{ steps.build.outputs.ZIP_SHORT_COMMIT_ID }} ${{ steps.build.outputs.ZIP_BUILD_TYPE }}" | |
| path: "${{ steps.build.outputs.ZIP_FOLDER }}/*.zip" | |
| retention-days: "${{ steps.info.outputs.ZIP_RETENTION_DAYS }}" | |
| overwrite: false | |
| archive: false | |
| compression-level: 0 | |
| if-no-files-found: "error" | |
| keep-alive: | |
| name: "Keep alive" | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| if: "${{ github.event_name == 'schedule' }}" | |
| permissions: | |
| contents: read # Needed to checkout the repository (only required for private repositories) | |
| actions: write # Needed to keep alive the workflow | |
| steps: | |
| - name: "Checkout needed files" | |
| uses: actions/checkout@v6 | |
| with: | |
| lfs: false | |
| sparse-checkout: | | |
| conf/full.inc.sh | |
| conf/lfs.inc.sh | |
| sparse-checkout-cone-mode: false | |
| - name: "Ping build cache" # Cache expiration: 7 days | |
| uses: actions/cache/restore@v5 | |
| timeout-minutes: 5 | |
| with: | |
| key: "build-${{ hashFiles('conf/full.inc.sh') }}" | |
| restore-keys: "build-" | |
| path: "cache/build" | |
| enableCrossOsArchive: true | |
| lookup-only: true | |
| - name: "Ping LFS cache" # Cache expiration: 7 days | |
| uses: actions/cache/restore@v5 | |
| timeout-minutes: 5 | |
| with: | |
| key: "lfs-${{ hashFiles('conf/lfs.inc.sh') }}" | |
| restore-keys: "lfs-" | |
| path: "cache/lfs" | |
| enableCrossOsArchive: true | |
| lookup-only: true | |
| - name: "Keep workflow alive" | |
| uses: actions/github-script@v8 | |
| timeout-minutes: 5 | |
| env: | |
| WORKFLOW_REF: "${{ github.workflow_ref }}" | |
| with: | |
| retries: 3 | |
| script: | | |
| /* jshint esversion: 11 */ | |
| const workflow_filename = process.env.WORKFLOW_REF.split('@', 1).at(0).split('/').slice(2).join('/'); | |
| const response = await github.rest.actions.enableWorkflow({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| workflow_id: workflow_filename | |
| }).catch(response => response); | |
| if(response && response.status === 204) { | |
| console.log('Workflow enabled.'); | |
| } else { | |
| let errorMsg = 'enableWorkflow failed'; | |
| if(response && response.status && response.message) errorMsg += ' with error ' + response.status + ' (' + response.message + ')'; | |
| throw new Error(errorMsg); | |
| } | |
| clean-caches: | |
| name: "Clean old caches" | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| if: "${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}" | |
| permissions: | |
| actions: write # Needed to delete caches | |
| steps: | |
| - name: "Delete old caches" | |
| uses: actions/github-script@v8 | |
| with: | |
| retries: 3 | |
| script: | | |
| /* jshint esversion: 11 */ | |
| const CACHE_PREFIXES = Object.freeze(['build-', 'lfs-', 'gradle-wrapper-']); | |
| for (const prefix of CACHE_PREFIXES) { | |
| let failed = false; | |
| const caches = await github.paginate( | |
| github.rest.actions.getActionsCacheList, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| key: prefix, | |
| ref: 'refs/heads/main', | |
| sort: 'created_at', | |
| direction: 'desc', | |
| per_page: 100, | |
| }, (response, done) => { | |
| if (response.status !== 200) { | |
| core.error(`Failed to list caches for prefix "${prefix}": [HTTP ${response.status}] ${JSON.stringify(response.data)}`); | |
| failed = true; | |
| done(); | |
| return []; | |
| } | |
| return response.data.actions_caches; | |
| } | |
| ).catch(e => { | |
| core.error(`Failed to paginate caches for prefix "${prefix}": [HTTP ${e.status ?? 'unknown'}] ${e.message}`); | |
| failed = true; | |
| return []; | |
| }); | |
| if (failed || caches.length <= 2) continue; | |
| // Keep the 2 most recent caches per prefix; delete the rest | |
| for (const cache of caches.slice(2)) { | |
| await github.rest.actions.deleteActionsCacheById({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| cache_id: cache.id, | |
| }); | |
| core.info(`Deleted cache "${cache.key}" (id=${cache.id})`); | |
| await new Promise(r => setTimeout(r, 200)); | |
| } | |
| } |