From 2ae3d4dfe68bc9c4c9dc78a580c684e9af769f28 Mon Sep 17 00:00:00 2001 From: Nicholas Felt Date: Fri, 30 Aug 2024 11:48:54 -0700 Subject: [PATCH] feat: Add an action that can be used to fetch a PR number (#32) * feat: Add an action that can be used to fetch a PR number * test: Add test for PR from a fork * docs: Add documentation on the new action --- .../_reusable-publish-api-comparison.yml | 19 ++++-- .../_reusable-publish-test-results.yml | 17 ++++-- .github/workflows/test-actions.yml | 50 ++++++++++++++++ CHANGELOG.md | 4 ++ README.md | 6 +- actions/fetch_pr_number/action.yml | 60 +++++++++++++++++++ actions/fetch_pr_number/readme.md | 41 +++++++++++++ doc_config/known_words.txt | 1 + mkdocs.yml | 1 + 9 files changed, 186 insertions(+), 13 deletions(-) create mode 100644 actions/fetch_pr_number/action.yml create mode 100644 actions/fetch_pr_number/readme.md diff --git a/.github/workflows/_reusable-publish-api-comparison.yml b/.github/workflows/_reusable-publish-api-comparison.yml index 5bb2aa6e..d8b4e5b7 100644 --- a/.github/workflows/_reusable-publish-api-comparison.yml +++ b/.github/workflows/_reusable-publish-api-comparison.yml @@ -23,17 +23,24 @@ jobs: else echo "BREAKING_CHANGES=true" >> "$GITHUB_ENV" fi - - name: Fetch PR number - id: pr - uses: 8BitJonny/gh-get-current-pr@08e737c57a3a4eb24cec6487664b243b77eb5e36 + - if: ${{ endsWith(github.repository, '/python-package-ci-cd') }} # Run the local action when this is run in the python-package-ci-cd repository + id: fetch-pr-number-local + uses: ./actions/fetch_pr_number with: sha: ${{ github.event.workflow_run.head_sha }} + github-repository: ${{ github.repository }} + - if: ${{ !endsWith(github.repository, '/python-package-ci-cd') }} # Run the public action when this is run outside the python-package-ci-cd repository + id: fetch-pr-number + uses: tektronix/python-package-ci-cd/actions/fetch_pr_number@v1.1.1 + with: + sha: ${{ github.event.workflow_run.head_sha }} + github-repository: ${{ github.repository }} - name: Publish API Breaking Changes Check Results uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 if: ${{ env.BREAKING_CHANGES == 'true' }} with: header: breaking-api-changes - number: ${{ steps.pr.outputs.number }} + number: ${{ steps.fetch-pr-number.outputs.number || steps.fetch-pr-number-local.outputs.number }} recreate: true path: artifacts/breaking_changes.md - name: Add workflow link to comment @@ -41,7 +48,7 @@ jobs: uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 with: header: breaking-api-changes - number: ${{ steps.pr.outputs.number }} + number: ${{ steps.fetch-pr-number.outputs.number || steps.fetch-pr-number-local.outputs.number }} append: true message: |-

Link to workflow run

@@ -50,5 +57,5 @@ jobs: uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 with: header: breaking-api-changes - number: ${{ steps.pr.outputs.number }} + number: ${{ steps.fetch-pr-number.outputs.number || steps.fetch-pr-number-local.outputs.number }} delete: true diff --git a/.github/workflows/_reusable-publish-test-results.yml b/.github/workflows/_reusable-publish-test-results.yml index 5c6b02b2..7a95e173 100644 --- a/.github/workflows/_reusable-publish-test-results.yml +++ b/.github/workflows/_reusable-publish-test-results.yml @@ -27,23 +27,30 @@ jobs: run_id: ${{ github.event.workflow_run.id }} name: artifact_${{ matrix.os-name }}_tests path: artifacts - - name: Fetch PR number - id: pr - uses: 8BitJonny/gh-get-current-pr@08e737c57a3a4eb24cec6487664b243b77eb5e36 + - if: ${{ endsWith(github.repository, '/python-package-ci-cd') }} # Run the local action when this is run in the python-package-ci-cd repository + id: fetch-pr-number-local + uses: ./actions/fetch_pr_number with: sha: ${{ github.event.workflow_run.head_sha }} + github-repository: ${{ github.repository }} + - if: ${{ !endsWith(github.repository, '/python-package-ci-cd') }} # Run the public action when this is run outside the python-package-ci-cd repository + id: fetch-pr-number + uses: tektronix/python-package-ci-cd/actions/fetch_pr_number@v1.1.1 + with: + sha: ${{ github.event.workflow_run.head_sha }} + github-repository: ${{ github.repository }} - name: Publish Test Results uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 with: header: test-results-${{ matrix.os-name }} - number: ${{ steps.pr.outputs.number }} + number: ${{ steps.fetch-pr-number.outputs.number || steps.fetch-pr-number-local.outputs.number }} recreate: true path: artifacts/.results_tests/github_report.md - name: Add workflow link to comment uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 with: header: test-results-${{ matrix.os-name }} - number: ${{ steps.pr.outputs.number }} + number: ${{ steps.fetch-pr-number.outputs.number || steps.fetch-pr-number-local.outputs.number }} append: true message: |-

Link to workflow run

diff --git a/.github/workflows/test-actions.yml b/.github/workflows/test-actions.yml index 6c56d801..c6462c73 100644 --- a/.github/workflows/test-actions.yml +++ b/.github/workflows/test-actions.yml @@ -126,6 +126,55 @@ jobs: dependency-dict: '{"dev": ["pyright"]}' pre-commit-hook-skip-list: remove-tabs,forbid-tabs,check-readthedocs,check-dependabot,check-github-actions,check-github-workflows,commitizen,blacken-docs,yamlfix,hadolint,mdformat,markdown-link-check,check-poetry,toml-sort-fix,pyright,poetry-audit,ruff,ruff-format,docformatter export-dependency-groups: udd:actions/update_development_dependencies,cutv:actions/create_unique_testpypi_version,fci:actions/find_unreleased_changelog_items,tests + test-fetch_pr_number: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: Fetch known PR number + uses: ./actions/fetch_pr_number + id: fetch-pr-number-found + with: + sha: 7a6fb39bda8ace8217530c3bc79407333462fc30 + github-repository: ${{ github.repository }} + - name: Verify the PR number + run: | + if [ "${{ steps.fetch-pr-number-found.outputs.number }}" != "30" ]; then + echo "The fetched PR number doesn't match the expected PR number." + echo "Expected: 30" + echo "Actual: ${{ steps.fetch-pr-number-found.outputs.number }}" + exit 1 + fi + - name: Fetch known PR number from a fork + uses: ./actions/fetch_pr_number + id: fetch-pr-number-found-fork + with: + sha: 9163270797352721c78d82054f6ead259f2f7366 + github-repository: ${{ github.repository }} + - name: Verify the PR number from a fork + run: | + if [ "${{ steps.fetch-pr-number-found-fork.outputs.number }}" != "31" ]; then + echo "The fetched PR number doesn't match the expected PR number." + echo "Expected: 31" + echo "Actual: ${{ steps.fetch-pr-number-found.outputs.number }}" + exit 1 + fi + - name: Fetch unknown PR number + uses: ./actions/fetch_pr_number + id: fetch-pr-number-not-found + with: + sha: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + github-repository: ${{ github.repository }} + max-attempts: 2 + retry-delay: 2 + continue-on-error: true # This step should fail + - name: Verify no PR number was found and the previous step failed + run: | + if [ "${{ steps.fetch-pr-number-not-found.outcome }}" != "failure" ]; then + echo "Step did not fail as expected." + exit 1 + else + echo "Step failed as expected." + fi # Check that all jobs passed check-action-tests-passed: if: ${{ !cancelled() }} @@ -133,6 +182,7 @@ jobs: - test-create_unique_testpypi_version - test-find_unreleased_changelog_items - test-update_development_dependencies + - test-fetch_pr_number runs-on: ubuntu-latest steps: - name: Decide whether the needed jobs succeeded or failed diff --git a/CHANGELOG.md b/CHANGELOG.md index c572f01f..2de95f11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ Valid subsections within a version are: Things to be included in the next release go here. +### Added + +- Added an action that can be used to fetch a PR number based on the `head_sha`. + ### Changed - Converted all references to third-party Actions used in Reusable Workflows from tags to SHAs to ensure that the workflows are stable and do not change unexpectedly. diff --git a/README.md b/README.md index 1ea279c4..8d1b63d8 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,13 @@ Python Packaging CI/CD. 1. [`create_unique_testpypi_version`](actions/create_unique_testpypi_version/readme.md) - This action creates a unique version number for the provided Python package to enable uploading the package to [TestPyPI](https://test.pypi.org). -2. [`find_unreleased_changelog_items`](./actions/find_unreleased_changelog_items/readme.md) +2. [`fetch_pr_number`](actions/fetch_pr_number/readme.md) + - This action fetches the Pull Request number for the provided SHA in the provided GitHub repository. +3. [`find_unreleased_changelog_items`](./actions/find_unreleased_changelog_items/readme.md) - This action will parse the repository's `CHANGELOG.md` file to determine if there are any unreleased items. It will fail if it cannot find any unreleased items, as this means that the package is not ready for a new release. -3. [`update_development_dependencies`](./actions/update_development_dependencies/readme.md) +4. [`update_development_dependencies`](./actions/update_development_dependencies/readme.md) - This action enables updating Python development dependencies using the [`Poetry`](https://python-poetry.org/) package manager in-sync with [`pre-commit`](https://pre-commit.com/) hooks. diff --git a/actions/fetch_pr_number/action.yml b/actions/fetch_pr_number/action.yml new file mode 100644 index 00000000..3ff555c8 --- /dev/null +++ b/actions/fetch_pr_number/action.yml @@ -0,0 +1,60 @@ +--- +name: Fetch PR Number +description: Find the PR number for the provided SHA. This action also works for PRs + that are from forks. +inputs: + sha: + description: The SHA of the commit to find the PR number for. + required: true + github-repository: + description: The GitHub repository to search for the PR in. + required: true + retry-delay: + description: The delay in seconds between retries. + default: '120' + required: false + max-attempts: + description: The maximum number of attempts to find the PR number. + default: '5' + required: false +outputs: + number: + description: The PR number. + value: ${{ steps.fetch-pr.outputs.result }} +runs: + using: composite + steps: + - name: Fetch PR number + id: fetch-pr + uses: actions/github-script@e69ef5462fd455e02edcaf4dd7708eda96b9eda0 # v7.0.0 + with: + script: |- + const maxAttempts = ${{ inputs.max-attempts }}; + let attempt = 0; + let pullRequestNumber; + while (attempt < maxAttempts) { + try { + const response = await github.rest.search.issuesAndPullRequests({ + q: 'repo:${{ inputs.github-repository }} is:pr sha:${{ inputs.sha }}', + per_page: 1, + }); + const items = response.data.items; + if (items.length < 1) { + throw new Error('No PRs found'); + } + pullRequestNumber = items[0].number; + console.info("Pull request number is", pullRequestNumber); + break; // Exit loop on success + } catch (error) { + console.error(`Attempt ${attempt + 1} failed:`, error.message); + if (attempt < maxAttempts - 1) { // Check if not last attempt + console.log(`Waiting for ${{ inputs.retry-delay }} seconds before retrying...`); + await new Promise(resolve => setTimeout(resolve, ${{ inputs.retry-delay }} * 1000)); // Wait for 2 minutes + } + } + attempt++; + } + if (!pullRequestNumber) { + core.setFailed(`Failed to fetch PR number after ${maxAttempts} attempts`); + } + return pullRequestNumber; diff --git a/actions/fetch_pr_number/readme.md b/actions/fetch_pr_number/readme.md new file mode 100644 index 00000000..13ade655 --- /dev/null +++ b/actions/fetch_pr_number/readme.md @@ -0,0 +1,41 @@ +# fetch_pr_number + +This action fetches the Pull Request number for the provided SHA in the provided GitHub repository. +See the [example](#example) below for usage details. + +## Inputs + +| Input variable | Necessity | Description | Default | +| ------------------- | --------- | ----------------------------------------------------- | ------- | +| `sha` | required | The SHA of the commit to find the PR number for. | | +| `github-repository` | required | The GitHub repository to search for the PR in. | | +| `retry-delay` | optional | The delay in seconds between retries. | 120 | +| `max-attempts` | optional | The maximum number of attempts to find the PR number. | 5 | + +## Outputs + +| Output variable | Description | +| --------------- | -------------- | +| `number` | The PR number. | + +## Example + +```yaml +name: Publish Results +on: + workflow_call: +jobs: + publish-results: + runs-on: ubuntu-latest + steps: + - uses: tektronix/python-package-ci-cd/actions/fetch_pr_number@v1.1.1 + id: fetch-pr-number + with: + sha: ${{ github.event.workflow_run.head_sha }} # required + github-repository: ${{ github.repository }} # required + retry-delay: 120 # optional + max-attempts: 5 # optional + - name: Echo PR Number + run: | + echo "PR Number: ${{ steps.fetch-pr-number.outputs.number }}" +``` diff --git a/doc_config/known_words.txt b/doc_config/known_words.txt index 7839ff73..5154a9e7 100644 --- a/doc_config/known_words.txt +++ b/doc_config/known_words.txt @@ -12,6 +12,7 @@ codecov codeql create_unique_testpypi_version dependabot +fetch_pr_number find_unreleased_changelog_items gh ghaction diff --git a/mkdocs.yml b/mkdocs.yml index b3085b4d..29c2ab2e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -135,4 +135,5 @@ nav: - actions/create_unique_testpypi_version/readme.md - actions/find_unreleased_changelog_items/readme.md - actions/update_development_dependencies/readme.md + - actions/fetch_pr_number/readme.md - Contributing: [CONTRIBUTING.md, CODE_OF_CONDUCT.md, LICENSE.md]