From d3047f2163d4ebb4e0945fdf6fbe1e897f829c26 Mon Sep 17 00:00:00 2001 From: Art Shendrik Date: Fri, 28 Jun 2024 17:52:55 +0100 Subject: [PATCH] ci(GitHub): init workflows Signed-off-by: Art Shendrik --- .github/CODEOWNERS | 1 + .github/actions/actionlint/action.yml | 49 +++++ .github/dependabot.yml | 30 +++ .github/workflows/actionlint.yml | 36 ++++ .github/workflows/build.yml | 184 ++++++++++++++++++ .github/workflows/clear_cache.yml | 33 ++++ .../workflows/gradle-wrapper-validation.yml | 40 ++++ .github/workflows/pr-baseline.yml | 137 +++++++++++++ .github/workflows/pr-check.yml | 38 ++++ .github/workflows/pr-clean-cache.yml | 40 ++++ .github/workflows/pr-fast-forward.yml | 52 +++++ 11 files changed, 640 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/actions/actionlint/action.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/actionlint.yml create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/clear_cache.yml create mode 100644 .github/workflows/gradle-wrapper-validation.yml create mode 100644 .github/workflows/pr-baseline.yml create mode 100644 .github/workflows/pr-check.yml create mode 100644 .github/workflows/pr-clean-cache.yml create mode 100644 .github/workflows/pr-fast-forward.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..6c54c4b --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* artyom.shendrik@gmail.com diff --git a/.github/actions/actionlint/action.yml b/.github/actions/actionlint/action.yml new file mode 100644 index 0000000..491c3a5 --- /dev/null +++ b/.github/actions/actionlint/action.yml @@ -0,0 +1,49 @@ +# https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions +name: actionlint +description: ✅ Run actionlint for validating GitHub Actions workflow files. +author: Art Shendrik +branding: + icon: check-circle + color: gray-dark + +inputs: + files: + description: Comma-separated glob pattern list of files to check + required: false + default: '.github/workflows/*.yml, .github/actions/**.yaml' + flags: + description: Extra flags for the actionlint + required: false + default: ${{ null }} + fail-on-error: + description: Fail on error + required: false + default: ${{ true }} + +runs: + using: "composite" + steps: + # https://github.com/marketplace/actions/actionlint + # https://github.com/rhysd/actionlint/releases + # https://github.com/gmazzo/gradle-codeowners-plugin/pull/66/files + - name: actionlint + uses: raven-actions/actionlint@v1 + id: al + with: + fail-on-error: ${{ inputs.fail-on-error }} + files: ${{ inputs.files }} + flags: ${{ inputs.flags }} + + - name: actionlint Summary + continue-on-error: true + shell: bash + env: + AL_VERSION: ${{ steps.al.outputs.version-semver }} + AL_CACHE_HIT: ${{ steps.al.outputs.cache-hit }} + run: | + echo "Used actionlint version $AL_VERSION" + echo "actionlint cache used: $AL_CACHE_HIT" + +# Ref: +# https://github.com/gmazzo/gradle-codeowners-plugin/blob/346ed70/.github/actions/setup-host/action.yml (composite action example) +# https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..3177348 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,30 @@ +# To get started with Dependabot version updates, you'll need to specify, which +# package ecosystems to update and where the package manifests are located. +# Documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "gradle" + directory: "/" + commit-message: + prefix: "build(deps)" + schedule: + interval: "monthly" + reviewers: + - "amal" + labels: + - "gradle" + - "dependencies" + + - package-ecosystem: "github-actions" + directory: "/" + commit-message: + prefix: "ci(GitHub)" + schedule: + interval: "monthly" + reviewers: + - "amal" + labels: + - "gh-action" + - "dependencies" diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml new file mode 100644 index 0000000..d15a449 --- /dev/null +++ b/.github/workflows/actionlint.yml @@ -0,0 +1,36 @@ +name: Validate Workflows + +on: + push: + paths: + - '.github/actions/**.yml' + - '.github/workflows/*.yml' + pull_request: + paths: + - '.github/actions/**.yml' + - '.github/workflows/*.yml' + +permissions: + contents: read + +jobs: + actionlint: + runs-on: ubuntu-latest + steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + disable-sudo: true + egress-policy: block + allowed-endpoints: > + api.github.com:443 + files.pythonhosted.org:443 + github.com:443 + objects.githubusercontent.com:443 + pypi.org:443 + raw.githubusercontent.com:443 + registry.npmjs.org:443 + + - uses: actions/checkout@v4 + + - uses: ./.github/actions/actionlint diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..abdf5bd --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,184 @@ +name: Build + +on: + pull_request: + paths-ignore: + - '**-validation.yml' + - '**.*ignore' + - '**.md' + - '**.txt' + - '**/actionlint**' + - '**/pr-**.yml' + - '**/release.yml' + - '**dependabot.yml' + # Avoid useless and/or duplicate runs. + # Also, we merge with --ff-only, + # so we don't need to run on the merge commit. + branches-ignore: + # Dependabot creates both branch and PR. Avoid running twice. + - 'dependabot/**' + - 'dev' + - 'feat*/**' + - 'fix/**' + - 'mr/**' + - 'pr/**' + - 'pull/**' + - 'wip/**' + push: + paths-ignore: + - '**-validation.yml' + - '**.*ignore' + - '**.md' + - '**.txt' + - '**/actionlint**' + - '**/pr-**.yml' + - '**/release.yml' + - '**dependabot.yml' + +permissions: + contents: write + # required for all workflows (CodeQL) + security-events: write + # required for workflows in private repositories (CodeQL) + actions: read + # We appear to need write permission for both pull-requests and + # issues to post a comment to a pull request. + pull-requests: write + issues: write + +env: + CI: true + BUILD_NUMBER: ${{ github.run_number }} + SCM_TAG: ${{ github.sha }} + #GRADLE_OPTS: "-Dorg.gradle.daemon=false" + DEPENDENCY_GRAPH_INCLUDE_CONFIGURATIONS: "^(?!(classpath)).*" + DEPENDENCY_GRAPH_INCLUDE_PROJECTS: "^:(?!(buildSrc|test|check)).*" + IS_DEFAULT_BRANCH: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + +jobs: + buildAndCheck: + strategy: + fail-fast: false + matrix: + java: [ '22' ] + os: [ 'macos', 'windows', 'ubuntu' ] + # CodeQL supports ['c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support. + language: [ 'java-kotlin' ] + + name: 'Build and check on ${{ matrix.os }}' + timeout-minutes: 30 + runs-on: '${{ matrix.os }}-latest' + if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} + env: + GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: false + + steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + disable-sudo: true + egress-policy: audit + + - name: Checkout + uses: actions/checkout@v4 + + - name: 'Set up JDK ${{ matrix.java }}' + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '${{ matrix.java }}' + + - name: 'Cached KMP things (Konan, Node, Yarn, Binaryen)' + if: false # Seems slower than without it. + uses: actions/cache@v4 + with: + path: | + ~/.konan + ~/.gradle/yarn + ~/.gradle/nodejs + ~/.gradle/binaryen + key: "${{ runner.os }}-kmp-2.0.20" + restore-keys: | + ${{ runner.os }}-kmp- + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 + with: + gradle-home-cache-cleanup: true + cache-disabled: ${{ matrix.os == 'windows' }} # super slow on Windows. + cache-encryption-key: "${{ secrets.GRADLE_ENCRYPTION_KEY }}" + cache-read-only: ${{ !env.IS_DEFAULT_BRANCH }} + dependency-graph: ${{ env.IS_DEFAULT_BRANCH && 'generate-and-submit' || 'disabled'}} + add-job-summary-as-pr-comment: on-failure + artifact-retention-days: 1 + + # TODO: If it's a "build(deps): ..." commit, then run the dependency review actions, + # amend the `dependencyGuardBaseline` and `kotlinUpgradeYarnLock` task results + + - name: Initialize CodeQL + if: matrix.os == 'ubuntu' + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list with "+" to use these queries, and those in the config file. + # + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended, security-and-quality + + - name: 'Build and check' + timeout-minutes: 18 + run: ./gradlew build assemble check --continue --stacktrace --scan + + - name: Upload sarif report (Detekt) + if: always() && (github.event_name == 'pull_request' || env.IS_DEFAULT_BRANCH) + uses: github/codeql-action/upload-sarif@v3 + continue-on-error: true + with: + sarif_file: build/detekt-merged.sarif + category: detekt + + - name: Upload sarif report (Lint) + if: always() && (github.event_name == 'pull_request' || env.IS_DEFAULT_BRANCH) + uses: github/codeql-action/upload-sarif@v3 + continue-on-error: true + with: + sarif_file: build/lint-merged.sarif + category: lint + + - name: Upload the build report + if: always() + uses: actions/upload-artifact@v4 + with: + name: '${{ matrix.os }}-build-report' + path: | + **/build/logs/ + **/build/reports/ + **/build/output/ + build/*-merged.* + compression-level: 9 + + - name: Perform CodeQL Analysis + if: matrix.os == 'ubuntu' + timeout-minutes: 6 + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" + + - name: "Post result in PR comment" + uses: actions/github-script@v7 + if: github.event_name == 'pull_request' && failure() + env: + OS: ${{ matrix.os }} + GH_WORKFLOW: ${{ github.workflow }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { OS, GH_WORKFLOW, RUN_URL } = process.env + github.rest.issues.createComment({ + issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, + body: `❌ ${GH_WORKFLOW} [failed](${RUN_URL}) on ${OS}.`, + }) diff --git a/.github/workflows/clear_cache.yml b/.github/workflows/clear_cache.yml new file mode 100644 index 0000000..d686212 --- /dev/null +++ b/.github/workflows/clear_cache.yml @@ -0,0 +1,33 @@ +name: Clear cache + +on: + workflow_dispatch: + +permissions: + actions: write + +jobs: + clear-cache: + runs-on: ubuntu-latest + steps: + - name: Clear cache + uses: actions/github-script@v7 + continue-on-error: true + with: + script: | + console.log("About to clear all GitHub Actions caches") + const owner = context.repo.owner + const repo = context.repo.repo + const caches = await github.rest.actions.getActionsCacheList({ + owner: owner, + repo: repo, + }) + for (const cache of caches.data.actions_caches) { + await github.rest.actions.deleteActionsCacheById({ + owner: owner, + repo: repo, + cache_id: cache.id, + }) + console.log(`cleaned cache "${cache.key}"`) + } + console.log("Clear completed") diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml new file mode 100644 index 0000000..ea2f2c6 --- /dev/null +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -0,0 +1,40 @@ +name: Validate Gradle Wrapper + +on: + pull_request: + paths: + - '**/gradle-wrapper-validation.yml' + - '**/gradle/wrapper/' + - '**/gradle-wrapper.jar' + - '**/gradle*.properties' + - '**/gradlew*' + push: + paths: + - '**/gradle-wrapper-validation.yml' + - '**/gradle/wrapper/' + - '**/gradle-wrapper.jar' + - '**/gradle*.properties' + - '**/gradlew*' + +permissions: + contents: read + +jobs: + validation: + name: "Validate Gradle Wrapper" + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + disable-sudo: true + egress-policy: block + allowed-endpoints: > + github.com:443 + downloads.gradle-dn.com:443 + downloads.gradle.org:443 + services.gradle.org:443 + + - uses: actions/checkout@v4 + - uses: gradle/wrapper-validation-action@v3 diff --git a/.github/workflows/pr-baseline.yml b/.github/workflows/pr-baseline.yml new file mode 100644 index 0000000..a1189ab --- /dev/null +++ b/.github/workflows/pr-baseline.yml @@ -0,0 +1,137 @@ +name: PR Deps Baseline + +# Add the necessary changes to the dependency update PRs. + +on: + pull_request: + types: [ opened, reopened, synchronize ] + +permissions: + contents: write + # We appear to need write permission for both pull-requests and + # issues to post a comment to a pull request. + pull-requests: write + issues: write + +jobs: + pr-deps: + runs-on: ubuntu-latest + if: github.actor == 'dependabot[bot]' + concurrency: + group: '${{ github.workflow }}-${{ github.ref }}' + cancel-in-progress: true + env: + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + disable-sudo: true + egress-policy: audit + + # Verify that the PR is from Dependabot + - uses: dependabot/fetch-metadata@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Confirm start in the comment + uses: actions/github-script@v7 + continue-on-error: true + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { RUN_URL } = process.env + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `🔄 Baseline update [started](${RUN_URL})...` + }) + + - uses: actions/checkout@v4 + with: + # Needed for correct git commit --amend. + fetch-depth: 3 + # Checkout pull request HEAD commit instead of merge commit. + ref: ${{ github.event.pull_request.head.sha }} + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 22 + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 + with: + cache-disabled: true + cache-read-only: true + + - name: Update baseline + run: ./updateBaseline + + - name: Setup GIT + run: | + # Set GIT user email and name to match author of the last commit. + #git config --local user.name "$(git log --pretty=format:'%an' -1)" + #git config --local user.email "$(git log --pretty='%ae' -1)" + git config --local user.name "Baseline Action" + git config --local user.email "baseline.action@fluxo.local" + + # Create SSH key + #mkdir -p ~/.ssh/ + #echo "${{ secrets.BOT_GIT_SSH_KEY }}" > ~/.ssh/id_rsa_bot + #chmod 666 ~/.ssh/id_rsa_bot + + # Configure GH commit signing key. + # TODO: Fix it, doesn't work atm. Fails for SSH key saved and used this way. + #git config --local commit.gpgsign true + #git config --local gpg.format ssh + #git config --local user.signingkey ~/.ssh/id_rsa + + - name: GIT add + run: | + git add -v . + + - name: Commit amend and push + env: + GITHUB_HEAD_REF: ${{ github.head_ref }} + run: | + if [ -n "$(git diff --name-only --cached)" ]; then + # Show what's available. + git log -n 3 --pretty=format:"%h - %an, %ar : %s" + + # Amend the baseline changes to the last commit. + git commit --amend --no-edit -vv + + # Show what we are about to push. + git log -n 3 --pretty=format:"%h - %an, %ar : %s" + + # Push changes back to branch + git push --force -v origin "HEAD:refs/heads/${GITHUB_HEAD_REF}" + else + echo "No changes needed." + fi + + # Track result in the comment + - uses: actions/github-script@v7 + continue-on-error: true + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { RUN_URL } = process.env + github.rest.issues.createComment({ + issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, + body: `✅ Baseline [updated](${RUN_URL}).` + }) + - uses: actions/github-script@v7 + if: failure() + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { RUN_URL } = process.env + github.rest.issues.createComment({ + issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, + body: `❌ Baseline update [failed](${RUN_URL})!` + }) diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml new file mode 100644 index 0000000..80cf110 --- /dev/null +++ b/.github/workflows/pr-check.yml @@ -0,0 +1,38 @@ +name: PR Check + +on: + pull_request: + types: [ opened, reopened, synchronize ] + +permissions: + contents: read + # We appear to need write permission for both pull-requests and + # issues to post a comment to a pull request. + pull-requests: write + issues: write + +jobs: + pr-check: + runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + + steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + disable-sudo: true + egress-policy: block + allowed-endpoints: > + github.com:443 + + - name: Check if fast forwarding is possible + uses: sequoia-pgp/fast-forward@v1 + with: + merge: false + # To reduce the workflow's verbosity, use 'on-error' + # to only post a comment when an error occurs, or 'never' to + # never post a comment. + # (Information is always available in the step's summary.) + comment: on-error diff --git a/.github/workflows/pr-clean-cache.yml b/.github/workflows/pr-clean-cache.yml new file mode 100644 index 0000000..5dcb37f --- /dev/null +++ b/.github/workflows/pr-clean-cache.yml @@ -0,0 +1,40 @@ +name: PR Cache Cleanup + +on: + pull_request: + types: [ closed ] + +jobs: + pr-clean-cache: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + disable-sudo: true + egress-policy: block + allowed-endpoints: > + api.github.com:443 + objects.githubusercontent.com:443 + + - name: Clean up + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge + run: | + gh extension install actions/gh-actions-cache + + echo "Fetching list of cache key" + cacheKeysForPR=$(gh actions-cache list -R "$REPO" -B "$BRANCH" -L 100 | cut -f 1 ) + + ## Setting this to not fail the workflow while deleting cache keys. + set +e + echo "Deleting caches..." + for cacheKey in $cacheKeysForPR + do + gh actions-cache delete "$cacheKey" -R "$REPO" -B "$BRANCH" --confirm + echo " - Deleted cache key: $cacheKey" + done + echo "Done" diff --git a/.github/workflows/pr-fast-forward.yml b/.github/workflows/pr-fast-forward.yml new file mode 100644 index 0000000..6a87d36 --- /dev/null +++ b/.github/workflows/pr-fast-forward.yml @@ -0,0 +1,52 @@ +name: PR Fast-Forward Merge + +on: + issue_comment: + types: [ created ] + +permissions: + contents: write + # We appear to need write permission for both pull-requests and + # issues to post a comment to a pull request. + pull-requests: write + issues: write + +jobs: + pr-fast-forward: + # Only run if the PR comment contains the '/fast-forward' command or equals a '/ff' command. + # Only run if a user with write permissions made the comment. + if: | + github.event.issue.pull_request && + (github.event.comment.body == '/ff' || contains(github.event.comment.body, '/fast-forward')) && + (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER') + runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + + steps: + - name: Harden Runner + uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0 + with: + disable-sudo: true + egress-policy: block + allowed-endpoints: > + api.github.com:443 + github.com:443 + + - name: React to the comment right away + uses: dkershner6/reaction-action@v2 + continue-on-error: true + with: + token: ${{ secrets.GITHUB_TOKEN }} + reaction: "rocket" + + - name: Fast-forward and push + uses: sequoia-pgp/fast-forward@v1 + with: + merge: true + # To reduce the workflow's verbosity, use 'on-error' + # to only post a comment when an error occurs, or 'never' to + # never post a comment. + # (Information is always available in the step's summary.) + comment: always