diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a3137d1..849b090 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -141,6 +141,61 @@ jobs: issue-number: ${{ github.event.pull_request.number }} token: ${{ secrets.GITHUB_TOKEN }} + test-job-with-custom-base-ref: + runs-on: ubuntu-latest + name: Test custom base ref + if: github.event_name == 'pull_request' + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version-file: .java-version + + - name: Downgrade dependency version to see the diff + run: sed -i -E 's/androidx.paging:paging-common-ktx:[[:digit:]]+.[[:digit:]]+.[[:digit:]]+/androidx.paging:paging-common-ktx:3.2.0/g' testproject/build.gradle + + - uses: gradle/actions/setup-gradle@v6 + + # Ref resolved in the local repository only + - id: dependency-diff-local-ref + uses: ./ + with: + configuration: runtimeClasspath + build-root-directory: testproject + project: "" + base-ref: HEAD^1 + debug: true + + - uses: actions/upload-artifact@v7 + with: + name: file-diff-test-base-ref-local + path: ${{ steps.dependency-diff-local-ref.outputs.file-diff }} + if-no-files-found: error + + # The action restores the checked out commit, which reverts the downgrade above + - name: Downgrade dependency version again to see the diff + run: sed -i -E 's/androidx.paging:paging-common-ktx:[[:digit:]]+.[[:digit:]]+.[[:digit:]]+/androidx.paging:paging-common-ktx:3.2.0/g' testproject/build.gradle + + # Sha fetched from origin + - id: dependency-diff-sha + uses: ./ + with: + configuration: runtimeClasspath + build-root-directory: testproject + project: "" + base-ref: ${{ github.event.pull_request.base.sha }} + debug: true + + - uses: actions/upload-artifact@v7 + with: + name: file-diff-test-base-ref-sha + path: ${{ steps.dependency-diff-sha.outputs.file-diff }} + if-no-files-found: error + test-on-different-os: strategy: fail-fast: false diff --git a/Readme.md b/Readme.md index 63460c4..87cdcaa 100644 --- a/Readme.md +++ b/Readme.md @@ -64,6 +64,7 @@ All inputs with their default values: configuration: 'releaseRuntimeClasspath' project: 'app' build-root-directory: . + base-ref: '' additional-gradle-arguments: '' lib-version: 'latest' ``` @@ -75,9 +76,33 @@ Dependency diff for root projects can be configured using `project: ''`. For Android projects use the one that has `com.android.application` plugin applied. - **`build-root-directory`** - Relative path to folder containing gradle wrapper. Example usage: `build-root-directory: library` +- **`base-ref`** - The ref currently checked out revision is compared against. +Accepts anything git can resolve to a commit - a branch name, a tag, a commit sha or an expression like `HEAD^1`. +Refs available in the local repository are used as is, everything else is fetched from `origin` first. +When left empty (the default) the action falls back to `github.base_ref`, i.e. the branch the pull request targets. +See [Stacked pull requests](#stacked-pull-requests). - **`additional-gradle-arguments`** - Additional arguments passed to internal Gradle invocation. Example: `"--no-configuration-cache"` or `"--stacktrace"` - **`lib-version`** - Overrides [dependency-tree-diff](https://github.com/JakeWharton/dependency-tree-diff) dependency version. Example: `"1.2.1"`, `"1.1.0"`, `"latest"` +### Stacked pull requests + +For [stacked pull requests](https://docs.github.com/en/pull-requests/reference/stacked-pull-requests) `github.base_ref` points at the base of the whole stack (e.g. `main`), +while the merge commit CI checks out is built on top of the parent pull request. +Both sides of the comparison then contain a different version of `main`, and dependency changes that landed there in the meantime get reported as if they were introduced by the pull request. + +Comparing against the first parent of the checked out merge commit puts the same `main` on both sides: + +```yml + - uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - id: dependency-diff + uses: usefulness/dependency-tree-diff-action@v2 + with: + base-ref: HEAD^1 +``` +

diff --git a/action.yml b/action.yml index 2f08fbe..e251563 100644 --- a/action.yml +++ b/action.yml @@ -13,6 +13,10 @@ inputs: description: 'Build root directory' required: false default: '.' + base-ref: + description: 'Git ref (branch name, tag or commit sha) the currently checked out revision is compared against. Defaults to `github.base_ref`' + required: false + default: '' lib-version: description: 'Dependency diff library version' required: true @@ -36,7 +40,7 @@ outputs: description: "Path to a file containing dependencies dump from currently checked out ref" value: ${{ steps.diff-generator-unix.outputs.file-dependencies-head || steps.diff-generator-windows.outputs.file-dependencies-head }} file-dependencies-base: - description: "Path to a file containing dependencies dump from `github.base_ref`" + description: "Path to a file containing dependencies dump from the base ref" value: ${{ steps.diff-generator-unix.outputs.file-dependencies-base || steps.diff-generator-windows.outputs.file-dependencies-base }} branding: color: 'red' @@ -49,7 +53,7 @@ runs: env: INPUT_PROJECT: ${{ inputs.project }} INPUT_CONFIGURATION: ${{ inputs.configuration }} - INPUT_BASEREF: ${{ github.base_ref }} + INPUT_BASEREF: ${{ inputs.base-ref || github.base_ref }} INPUT_BUILD_ROOT_DIR: ${{ inputs.build-root-directory }} INPUT_VERSION: ${{ inputs.lib-version }} INPUT_ADDITIONAL_GRADLE_ARGUMENTS: ${{ inputs.additional-gradle-arguments }} @@ -63,7 +67,7 @@ runs: env: INPUT_PROJECT: ${{ inputs.project }} INPUT_CONFIGURATION: ${{ inputs.configuration }} - INPUT_BASEREF: ${{ github.base_ref }} + INPUT_BASEREF: ${{ inputs.base-ref || github.base_ref }} INPUT_BUILD_ROOT_DIR: ${{ inputs.build-root-directory }} INPUT_VERSION: ${{ inputs.lib-version }} INPUT_ADDITIONAL_GRADLE_ARGUMENTS: ${{ inputs.additional-gradle-arguments }} diff --git a/entrypoint.ps1 b/entrypoint.ps1 index c85bb15..478f263 100644 --- a/entrypoint.ps1 +++ b/entrypoint.ps1 @@ -11,6 +11,11 @@ param( $ErrorActionPreference = "Stop" +if ([string]::IsNullOrWhiteSpace($InputBaseRef)) { + Write-Host "::error::There is no ref to compare against. The action falls back to ``github.base_ref``, which is only set for pull request events - pass the ``base-ref`` input explicitly for other triggers." + exit 1 +} + Set-Location $InputBuildRootDir $headers = @{ @@ -41,8 +46,23 @@ $currentHead = git rev-parse HEAD $cmd = "./gradlew.bat $InputAdditionalGradleArguments ${InputProject}:dependencies --configuration $InputConfiguration" Invoke-Expression $cmd | Out-File -FilePath "dependency-tree-diff_dependencies-head.txt" -Encoding UTF8 -git fetch --force origin "${InputBaseRef}:${InputBaseRef}" --no-tags -git switch --force $InputBaseRef +# Prefer the ref as published by `origin`, but fall back to the local repository, +# so refs that only exist locally (`HEAD^1`, a not yet pushed commit) keep working. +try { + git fetch --force origin $InputBaseRef --no-tags + $fetched = $LASTEXITCODE -eq 0 +} catch { + $fetched = $false +} + +if ($fetched) { + $baseRef = "FETCH_HEAD" +} else { + Write-Host "Could not fetch '$InputBaseRef' from origin, resolving it in the local repository instead" + $baseRef = $InputBaseRef +} + +git switch --force --detach $baseRef Invoke-Expression $cmd | Out-File -FilePath "dependency-tree-diff_dependencies-base.txt" -Encoding UTF8 java -jar dependency-tree-diff.jar dependency-tree-diff_dependencies-base.txt dependency-tree-diff_dependencies-head.txt | Out-File -FilePath "dependency-tree-diff_output.txt" -Encoding UTF8 diff --git a/entrypoint.sh b/entrypoint.sh index 578c222..be06c08 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,5 +1,10 @@ #!/bin/bash -el +if [ -z "$INPUT_BASEREF" ]; then + echo "::error::There is no ref to compare against. The action falls back to \`github.base_ref\`, which is only set for pull request events - pass the \`base-ref\` input explicitly for other triggers." + exit 1 +fi + cd "$INPUT_BUILD_ROOT_DIR" if [ "$INPUT_VERSION" == "latest" ]; then @@ -28,8 +33,17 @@ chmod +x dependency-tree-diff.jar current_head=$(git rev-parse HEAD) ./gradlew $INPUT_ADDITIONAL_GRADLE_ARGUMENTS "$INPUT_PROJECT":dependencies --configuration "$INPUT_CONFIGURATION" > dependency-tree-diff_dependencies-head.txt -git fetch --force origin "$INPUT_BASEREF":"$INPUT_BASEREF" --no-tags -git switch --force "$INPUT_BASEREF" + +# Prefer the ref as published by `origin`, but fall back to the local repository, +# so refs that only exist locally (`HEAD^1`, a not yet pushed commit) keep working. +if git fetch --force origin "$INPUT_BASEREF" --no-tags; then + base_ref="FETCH_HEAD" +else + echo "Could not fetch '$INPUT_BASEREF' from origin, resolving it in the local repository instead" + base_ref="$INPUT_BASEREF" +fi + +git switch --force --detach "$base_ref" ./gradlew $INPUT_ADDITIONAL_GRADLE_ARGUMENTS "$INPUT_PROJECT":dependencies --configuration "$INPUT_CONFIGURATION" > dependency-tree-diff_dependencies-base.txt java -jar dependency-tree-diff.jar dependency-tree-diff_dependencies-base.txt dependency-tree-diff_dependencies-head.txt > dependency-tree-diff_output.txt