diff --git a/.github/dependency_filters.yml b/.github/dependency_filters.yml new file mode 100644 index 00000000000..b3c3aa1b287 --- /dev/null +++ b/.github/dependency_filters.yml @@ -0,0 +1,9 @@ +dependencies: + - Gemfile + - Appraisals + - datadog.gemspec + - tasks/appraisal.rake + - .github/workflows/lock-dependency.yml + - lib/datadog/version.rb + - appraisal/** + - gemfiles/** diff --git a/.github/workflows/lock-dependency.yml b/.github/workflows/lock-dependency.yml index 1b977e1f241..b226b135bc2 100644 --- a/.github/workflows/lock-dependency.yml +++ b/.github/workflows/lock-dependency.yml @@ -1,26 +1,65 @@ name: Lock Dependency +# TODO: Make this job mandatory +# TODO: Make this on workflow_dispatch + on: - workflow_dispatch: - inputs: - branch: - description: 'Branch to be lock dependency' - required: true - # Testing purpose, to be removed before merge. + # Limitation about `paths` types: + # > https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#git-diff-comparisons push: - branches: - - tonycthsu/automate-update-gemfiles + branches-ignore: + - master + - release + - '*-stable' + pull_request: + # Run this job when a PR is opened, covering the scenario where branch is ready and pushed before PR is opened. + types: + - opened + -# Ensure obsolete job is cancelled if another commit is pushed to the same branch. +# TODO: Improve concurrency between push event and pull_request event concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: - # TODO: In order to fully automate this workflow for each PR, - # have a reliable way to precheck job to understand whether it need to be updated + pr: + name: Pull Request attached + runs-on: ubuntu-latest + outputs: + pr_found: ${{ steps.pr.outputs.pr_found }} + pr_base_ref: ${{ steps.pr.outputs.pr.base.ref }} + steps: + # Limitation with pull_request trigger: https://github.com/8BitJonny/gh-get-current-pr/tree/3.0.0/?tab=readme-ov-file#limitations + - uses: 8BitJonny/gh-get-current-pr@3.0.0 + id: pr + with: + filterOutClosed: true # Don't trigger on commits with closed PRs, including merges into `master`. + + dependency: + name: Depenedency changes + needs: pr + if: ${{ needs.pr.outputs.pr_found == 'true' }} + runs-on: ubuntu-latest + outputs: + changes: ${{ steps.changes.outputs.dependencies }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: changes + with: + # This is the best effort to get the diff comparison. + # The result remains time-sensitive since the `base` is constantly changing and + # the PR cannot be guaranteed to be up-to-date. + # + # Unless enable `Require branches to be up to date before merging` in the repository rule settings + base: ${{ needs.pr.outputs.pr_base_ref }} + filters: .github/dependency_filters.yml + lock: runs-on: ubuntu-latest + needs: dependency + if: ${{ needs.dependency.outputs.changes == 'true' }} strategy: fail-fast: false matrix: @@ -54,39 +93,35 @@ jobs: BUNDLE_WITHOUT: check steps: - uses: actions/checkout@v4 - - run: ls -al - run: | ruby -v gem -v bundler -v - - run: bundle install # TODO: Migrate away from `appraisal` - run: bundle exec appraisal generate - run: bundle exec rake dependency:lock - - uses: actions/upload-artifact@v4 with: name: lock-dependency-${{ github.run_id }}-${{ matrix.engine.name }}-${{ matrix.engine.version }} path: gemfiles/${{ matrix.engine.name }}_${{ matrix.engine.version }}* + # TODO: Change token to trigger workflow automation + # > New commit by GITHUB_TOKEN does not trigger workflow automation to prevent infinite loop commit: + name: Commit changes needs: lock runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 - - uses: actions/download-artifact@v4 with: path: gemfiles pattern: lock-dependency-${{ github.run_id }}-* merge-multiple: true - - - run: git diff --color - - uses: stefanzweifel/git-auto-commit-action@v5 with: file_pattern: 'gemfiles/*' diff --git a/.github/workflows/update-gemfiles.yml b/.github/workflows/update-gemfiles.yml new file mode 100644 index 00000000000..078a15ccd3d --- /dev/null +++ b/.github/workflows/update-gemfiles.yml @@ -0,0 +1,83 @@ +name: Update Gemfiles + +# This action cannot be skipped altogether because it is a mandatory status check. +# Instead we conditionally skip it at job level, instead of workflow level. +on: + # Execute on `push` and not `pull_request` because `pull_request` + # always compares if the `paths` have changed compared to the PR base. + # This is an issue because it means that all commits to the branch + # will trigger the gemfile update process, which is unnecessary and expensive. + # + # By executing on `push`, GitHub compares `paths` with the parent commit, + # meaning the gemfile update process will only execute on the exact commit + # that changes any of the `paths`. + # + # Because this process is slow and expensive, and we commit the gemfile changes back + # to the branch, we have an additional filter to only execute this action on branches + # attached to a PR. + # + # We could do the inverse: execute this action on `pull_request`, and additionally check + # if `paths` was changed compared to the parent commit, but this proved more complicated. + push + +# Ensure obsolete job is cancelled if another commit is pushed to the same branch. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + check: + name: Update Gemfiles + runs-on: ubuntu-22.04 + permissions: + contents: write + pull-requests: write + steps: + # Only execute if there's a PR attached to this branch. + # Because we execute on `push`, we have to double check here if this is part of a PR. + - name: Check if this branch is attached to a Pull Request + uses: 8bitjonny/gh-get-current-pr@2215326c76d51bfa3f2af0a470f32677f6c0cae9 # v2.2.0 + id: pr + with: + filterOutClosed: true # Don't trigger on commits with closed PRs, including merges into `master`. + - if: steps.pr.outputs.pr_found == 'true' + uses: actions/checkout@v4 + # And also, only execute if files that can affect gemfiles are modified. + - if: steps.pr.outputs.pr_found == 'true' + uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 + id: filter + with: + base: ${{ github.ref_name }} + filters: | + gemfile: + # Files that declare the dependency tree + - Gemfile + - Appraisals + - datadog.gemspec + # Files that control gemfile generation + - tasks/appraisal.rake + - .github/workflows/update-gemfiles.yml + # The gem version is present in all lock files + - lib/datadog/version.rb + # In case the generated files were updated manually or in a merge commit + - appraisal/** + - gemfiles/** + - if: steps.pr.outputs.pr_found == 'true' && steps.filter.outputs.gemfile == 'true' + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2' + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - if: steps.pr.outputs.pr_found == 'true' && steps.filter.outputs.gemfile == 'true' + name: Ensure gemfiles/*.gemfile.lock match gem definition + run: bundle exec rake appraisal:lock + - if: steps.pr.outputs.pr_found == 'true' && steps.filter.outputs.gemfile == 'true' + name: Add all supported platforms to gemfiles/*.gemfile.lock + run: bundle exec rake appraisal:platform + - if: steps.pr.outputs.pr_found == 'true' && steps.filter.outputs.gemfile == 'true' + name: Remove obsolete gemfiles/* + run: bundle exec rake appraisal:clean + - if: steps.pr.outputs.pr_found == 'true' && steps.filter.outputs.gemfile == 'true' + name: Commit gemfiles changes, if any, back to the branch + uses: stefanzweifel/git-auto-commit-action@3ea6ae190baf489ba007f7c92608f33ce20ef04a # v4.16.0 + with: + commit_message: Update gemfiles/*