diff --git a/.craft.yml b/.craft.yml index 60981d6..1d7cfec 100644 --- a/.craft.yml +++ b/.craft.yml @@ -1,6 +1,6 @@ minVersion: 0.23.1 changelogPolicy: auto -preReleaseCommand: pwsh scripts/update-version.ps1 +preReleaseCommand: pwsh -cwa '' artifactProvider: name: none targets: diff --git a/.github/workflows/danger-workflow-tests.yml b/.github/workflows/danger-workflow-tests.yml index 0b27a3a..04880eb 100644 --- a/.github/workflows/danger-workflow-tests.yml +++ b/.github/workflows/danger-workflow-tests.yml @@ -5,14 +5,38 @@ on: pull_request: types: [opened, synchronize, reopened, edited, ready_for_review] -jobs: - danger: - uses: ./.github/workflows/danger.yml - with: - _workflow_version: ${{ github.sha }} +permissions: + contents: read + pull-requests: write + statuses: write - test-outputs: +jobs: + # Test Danger action on pull requests - should analyze PR and report findings + pr-analysis: runs-on: ubuntu-latest - needs: danger steps: - - run: "[[ '${{ needs.danger.outputs.outcome }}' == 'success' ]]" + - uses: actions/checkout@v4 + + - name: Run danger action + id: danger + uses: ./danger + + - name: Validate danger outputs + env: + DANGER_OUTCOME: ${{ steps.danger.outputs.outcome }} + run: | + echo "🔍 Validating Danger action outputs..." + echo "Danger Outcome: '$DANGER_OUTCOME'" + + # Validate that Danger ran successfully + if [[ "$DANGER_OUTCOME" != "success" ]]; then + echo "❌ Expected Danger outcome 'success', got '$DANGER_OUTCOME'" + echo "This could indicate:" + echo " - Danger found issues that caused it to fail" + echo " - The action itself encountered an error" + echo " - Docker container issues" + exit 1 + fi + + echo "✅ Danger PR analysis completed successfully!" + echo "â„šī¸ Check the PR comments for any Danger findings" diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml deleted file mode 100644 index ac426f1..0000000 --- a/.github/workflows/danger.yml +++ /dev/null @@ -1,43 +0,0 @@ -# Runs DangerJS with a pre-configured set of rules on a Pull Request. -on: - workflow_call: - inputs: - _workflow_version: - description: 'Internal: specify github-workflows (this repo) revision to use when checking out scripts.' - type: string - required: false - default: '2.14.1' # Note: this is updated during release process - outputs: - outcome: - description: Whether the Danger run finished successfully. Possible values are success, failure, cancelled, or skipped. - value: ${{ jobs.danger.outputs.outcome }} - -jobs: - danger: - runs-on: ubuntu-latest - outputs: - outcome: ${{ steps.danger.outcome }} - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Download dangerfile.js and utilities - run: | - wget https://raw.githubusercontent.com/getsentry/github-workflows/${{ inputs._workflow_version }}/danger/dangerfile.js -P ${{ runner.temp }} - wget https://raw.githubusercontent.com/getsentry/github-workflows/${{ inputs._workflow_version }}/danger/dangerfile-utils.js -P ${{ runner.temp }} - - # Using a pre-built docker image in GitHub container registry instaed of NPM to reduce possible attack vectors. - - name: Run DangerJS - id: danger - run: | - docker run \ - --volume ${{ github.workspace }}:/github/workspace \ - --volume ${{ runner.temp }}:${{ runner.temp }} \ - --workdir /github/workspace \ - --user $UID \ - -e "INPUT_ARGS" -e "GITHUB_JOB" -e "GITHUB_REF" -e "GITHUB_SHA" -e "GITHUB_REPOSITORY" -e "GITHUB_REPOSITORY_OWNER" -e "GITHUB_RUN_ID" -e "GITHUB_RUN_NUMBER" -e "GITHUB_RETENTION_DAYS" -e "GITHUB_RUN_ATTEMPT" -e "GITHUB_ACTOR" -e "GITHUB_TRIGGERING_ACTOR" -e "GITHUB_WORKFLOW" -e "GITHUB_HEAD_REF" -e "GITHUB_BASE_REF" -e "GITHUB_EVENT_NAME" -e "GITHUB_SERVER_URL" -e "GITHUB_API_URL" -e "GITHUB_GRAPHQL_URL" -e "GITHUB_REF_NAME" -e "GITHUB_REF_PROTECTED" -e "GITHUB_REF_TYPE" -e "GITHUB_WORKSPACE" -e "GITHUB_ACTION" -e "GITHUB_EVENT_PATH" -e "GITHUB_ACTION_REPOSITORY" -e "GITHUB_ACTION_REF" -e "GITHUB_PATH" -e "GITHUB_ENV" -e "GITHUB_STEP_SUMMARY" -e "RUNNER_OS" -e "RUNNER_ARCH" -e "RUNNER_NAME" -e "RUNNER_TOOL_CACHE" -e "RUNNER_TEMP" -e "RUNNER_WORKSPACE" -e "ACTIONS_RUNTIME_URL" -e "ACTIONS_RUNTIME_TOKEN" -e "ACTIONS_CACHE_URL" -e GITHUB_ACTIONS=true -e CI=true \ - -e GITHUB_TOKEN="${{ github.token }}" \ - -e DANGER_DISABLE_TRANSPILATION="true" \ - ghcr.io/danger/danger-js:11.3.1 \ - --failOnErrors --dangerfile ${{ runner.temp }}/dangerfile.js diff --git a/.github/workflows/updater.yml b/.github/workflows/updater.yml deleted file mode 100644 index 975e8f7..0000000 --- a/.github/workflows/updater.yml +++ /dev/null @@ -1,287 +0,0 @@ -# Allows updating dependencies to the latest published tag -on: - workflow_call: - inputs: - path: - description: Dependency path in the source repository, this can be either a submodule, a .properties file, a shell script, or a CMake file with FetchContent. - type: string - required: true - name: - description: Name used in the PR title and the changelog entry. - type: string - required: true - pattern: - description: RegEx pattern that will be matched against available versions when picking the latest one. - type: string - required: false - default: '' - changelog-entry: - description: Whether to add a changelog entry for the update. - type: boolean - required: false - default: true - changelog-section: - description: Section header to attach the changelog entry to. - type: string - required: false - default: Dependencies - runs-on: - description: GitHub Actions virtual environment name to run the udpater job on. - type: string - required: false - default: 'ubuntu-latest' - pr-strategy: - description: | - How to handle PRs - can be either of the following: - * create - create a new PR for new dependency versions as they are released - maintainers may merge or close older PRs manually - * update - keep a single PR that gets updated with new dependency versions until merged - only the latest version update is available at any time - type: string - required: false - default: create - _workflow_version: - description: 'Internal: specify github-workflows (this repo) revision to use when checking out scripts.' - type: string - required: false - default: '2.14.1' # Note: this is updated during release process - secrets: - api-token: - required: true - outputs: - prUrl: - description: 'The created/updated PRs url.' - value: ${{ jobs.update.outputs.prUrl }} - baseBranch: - description: 'The base branch name.' - value: ${{ jobs.update.outputs.baseBranch }} - prBranch: - description: 'The created/updated pr branch name.' - value: ${{ jobs.update.outputs.prBranch }} - originalTag: - description: 'The original tag from which the dependency was updated from.' - value: ${{ jobs.update.outputs.originalTag }} - latestTag: - description: 'The latest tag to which the dependency was updated to.' - value: ${{ jobs.update.outputs.latestTag }} - -jobs: - cancel-previous-run: - runs-on: ubuntu-latest - steps: - - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@85880fa0301c86cca9da44039ee3bb12d3bedbfa # Tag: 0.12.1 - with: - access_token: ${{ github.token }} - - validate-inputs: - runs-on: ubuntu-latest - steps: - - name: Validate dependency name - shell: pwsh - run: | - # Validate that inputs.name contains only safe characters - if ('${{ inputs.name }}' -notmatch '^[a-zA-Z0-9_\./@\s-]+$') { - Write-Output "::error::Invalid dependency name: '${{ inputs.name }}'. Only alphanumeric characters, spaces, and _-./@ are allowed." - exit 1 - } - Write-Output "✓ Dependency name '${{ inputs.name }}' is valid" - - name: Validate dependency path - shell: pwsh - run: | - # Validate that inputs.path contains only safe characters (including # for CMake dependencies) - if ('${{ inputs.path }}' -notmatch '^[a-zA-Z0-9_\./#-]+$') { - Write-Output "::error::Invalid dependency path: '${{ inputs.path }}'. Only alphanumeric characters and _-./# are allowed." - exit 1 - } - Write-Output "✓ Dependency path '${{ inputs.path }}' is valid" - - # What we need to accomplish: - # * update to the latest tag - # * create a PR - # * update changelog (including the link to the just created PR) - # - # What we actually do is based on whether a PR exists already: - # * YES it does: - # * make the update - # * update changelog (with the ID of an existing PR) - # * push to the PR - # * NO it doesn't: - # * make the update - # * push to a new PR - # * update changelog (with the ID of the just created PR) - # * push to the PR - # We do different approach on subsequent runs because otherwise we would spam users' mailboxes - # with notifications about pushes to existing PRs. This way there is actually no push if not needed. - update: - needs: validate-inputs - runs-on: ${{ inputs.runs-on }} - # Map the job outputs to step outputs - outputs: - prUrl: ${{ steps.pr.outputs.url }} - baseBranch: ${{ steps.root.outputs.baseBranch }} - prBranch: ${{ steps.root.outputs.prBranch }} - originalTag: ${{ steps.target.outputs.originalTag }} - latestTag: ${{ steps.target.outputs.latestTag }} - timeout-minutes: 30 - defaults: - run: - shell: pwsh - env: - DEPENDENCY_NAME: ${{ inputs.name }} - DEPENDENCY_PATH: ${{ inputs.path }} - DEPENDENCY_PATTERN: ${{ inputs.pattern }} - CHANGELOG_SECTION: ${{ inputs.changelog-section }} - PR_STRATEGY: ${{ inputs.pr-strategy }} - steps: - - uses: actions/checkout@v4 - with: - ssh-key: ${{ secrets.api-token }} - - # In order to run scripts from this repo, we need to check it out manually, doesn't seem available locally. - - name: Check out workflow scripts - # Note: cannot use `actions/checkout` at the moment because you can't clone outside of the repo root. - # Follow https://github.com/actions/checkout/issues/197 - run: | - mkdir -p ${{ runner.temp }}/ghwf - cd ${{ runner.temp }}/ghwf - git init - git remote add origin https://github.com/getsentry/github-workflows.git - git fetch --depth 1 origin ${{ inputs._workflow_version }} - git checkout FETCH_HEAD - - - name: Update to the latest version - id: target - run: ${{ runner.temp }}/ghwf/updater/scripts/update-dependency.ps1 -Path $env:DEPENDENCY_PATH -Pattern $env:DEPENDENCY_PATTERN - - - name: Get the base repo info - if: steps.target.outputs.latestTag != steps.target.outputs.originalTag - id: root - run: | - $mainBranch = $(git remote show origin | Select-String "HEAD branch: (.*)").Matches[0].Groups[1].Value - $prBranch = switch ($env:PR_STRATEGY) - { - 'create' { "deps/$env:DEPENDENCY_PATH/${{ steps.target.outputs.latestTag }}" } - 'update' { "deps/$env:DEPENDENCY_PATH" } - default { throw "Unkown PR strategy '$env:PR_STRATEGY'." } - } - "baseBranch=$mainBranch" | Tee-Object $env:GITHUB_OUTPUT -Append - "prBranch=$prBranch" | Tee-Object $env:GITHUB_OUTPUT -Append - $nonBotCommits = ${{ runner.temp }}/ghwf/updater/scripts/nonbot-commits.ps1 ` - -RepoUrl "$(git config --get remote.origin.url)" -PrBranch $prBranch -MainBranch $mainBranch - $changed = $nonBotCommits.Length -gt 0 ? 'true' : 'false' - "changed=$changed" | Tee-Object $env:GITHUB_OUTPUT -Append - if ("$changed" -eq "true") - { - Write-Output "::warning::Target branch '$prBranch' has been changed manually - skipping updater to avoid overwriting these changes." - } - - - name: Parse the existing PR URL - if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.root.outputs.changed == 'false') }} - id: existing-pr - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - $urls = @(gh api 'repos/${{ github.repository }}/pulls?base=${{ steps.root.outputs.baseBranch }}&head=${{ github.repository_owner }}:${{ steps.root.outputs.prBranch }}' --jq '.[].html_url') - if ($urls.Length -eq 0) - { - "url=" | Tee-Object $env:GITHUB_OUTPUT -Append - } - elseif ($urls.Length -eq 1) - { - "url=$($urls[0])" | Tee-Object $env:GITHUB_OUTPUT -Append - } - else - { - throw "Unexpected number of PRs matched ($($urls.Length)): $urls" - } - - - run: git --no-pager diff - if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.existing-pr.outputs.url == '') && ( steps.root.outputs.changed == 'false') }} - - - name: Get target changelog - if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.root.outputs.changed == 'false') }} - run: | - $changelog = ${{ runner.temp }}/ghwf/updater/scripts/get-changelog.ps1 ` - -RepoUrl '${{ steps.target.outputs.url }}' ` - -OldTag '${{ steps.target.outputs.originalTag }}' ` - -NewTag '${{ steps.target.outputs.latestTag }}' - ${{ runner.temp }}/ghwf/updater/scripts/set-github-env.ps1 TARGET_CHANGELOG $changelog - - # First we create a PR only if it doesn't exist. We will later overwrite the content with the same action. - - name: Create a PR - if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.existing-pr.outputs.url == '') && ( steps.root.outputs.changed == 'false') }} - uses: peter-evans/create-pull-request@a4f52f8033a6168103c2538976c07b467e8163bc # pin#v6.0.1 - id: create-pr - with: - base: ${{ steps.root.outputs.baseBranch }} - branch: ${{ steps.root.outputs.prBranch }} - commit-message: 'chore: update ${{ env.DEPENDENCY_PATH }} to ${{ steps.target.outputs.latestTag }}' - author: 'GitHub ' - title: 'chore(deps): update ${{ env.DEPENDENCY_NAME }} to ${{ steps.target.outputs.latestTagNice }}' - body: | - Bumps ${{ env.DEPENDENCY_PATH }} from ${{ steps.target.outputs.originalTag }} to ${{ steps.target.outputs.latestTag }}. - - Auto-generated by a [dependency updater](https://github.com/getsentry/github-workflows/blob/main/.github/workflows/updater.yml). - ${{ env.TARGET_CHANGELOG }} - labels: dependencies - # draft: true - - - name: Verify we have a PR - if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.root.outputs.changed == 'false') }} - id: pr - run: | - if ('${{ steps.create-pr.outputs.pull-request-url }}' -ne '') - { - "url=${{ steps.create-pr.outputs.pull-request-url }}" | Tee-Object $env:GITHUB_OUTPUT -Append - } - elseif ('${{ steps.existing-pr.outputs.url }}' -ne '') - { - "url=${{ steps.existing-pr.outputs.url }}" | Tee-Object $env:GITHUB_OUTPUT -Append - } - else - { - throw "PR hasn't been created" - } - - # If we had to create a new PR, we must do a clean checkout & update the submodule again. - # If we didn't do this, the new PR would only have a changelog... - - name: 'After new PR: restore repo' - if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.existing-pr.outputs.url == '') && ( steps.root.outputs.changed == 'false') }} - uses: actions/checkout@v4 - with: - ssh-key: ${{ secrets.api-token }} - - - name: 'After new PR: redo the update' - if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.existing-pr.outputs.url == '') && ( steps.root.outputs.changed == 'false') }} - run: ${{ runner.temp }}/ghwf/updater/scripts/update-dependency.ps1 -Path $env:DEPENDENCY_PATH -Tag '${{ steps.target.outputs.latestTag }}' - - - name: Update Changelog - if: ${{ inputs.changelog-entry && ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.root.outputs.changed == 'false') }} - run: | - ${{ runner.temp }}/ghwf/updater/scripts/update-changelog.ps1 ` - -Name $env:DEPENDENCY_NAME ` - -PR '${{ steps.pr.outputs.url }}' ` - -RepoUrl '${{ steps.target.outputs.url }}' ` - -MainBranch '${{ steps.target.outputs.mainBranch }}' ` - -OldTag '${{ steps.target.outputs.originalTag }}' ` - -NewTag '${{ steps.target.outputs.latestTag }}' ` - -Section $env:CHANGELOG_SECTION - - - run: git --no-pager diff - if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.root.outputs.changed == 'false') }} - - # Now make the PR in its final state. This way we only have one commit and no updates if there are no changes between runs. - - name: Update the PR - if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.root.outputs.changed == 'false') }} - uses: peter-evans/create-pull-request@a4f52f8033a6168103c2538976c07b467e8163bc # pin#v6.0.1 - with: - base: ${{ steps.root.outputs.baseBranch }} - branch: ${{ steps.root.outputs.prBranch }} - commit-message: 'chore: update ${{ env.DEPENDENCY_PATH }} to ${{ steps.target.outputs.latestTag }}' - author: 'GitHub ' - title: 'chore(deps): update ${{ env.DEPENDENCY_NAME }} to ${{ steps.target.outputs.latestTagNice }}' - body: | - Bumps ${{ env.DEPENDENCY_PATH }} from ${{ steps.target.outputs.originalTag }} to ${{ steps.target.outputs.latestTag }}. - - Auto-generated by a [dependency updater](https://github.com/getsentry/github-workflows/blob/main/.github/workflows/updater.yml). - ${{ env.TARGET_CHANGELOG }} - labels: dependencies diff --git a/.github/workflows/workflow-tests.yml b/.github/workflows/workflow-tests.yml index aeeb351..0688d5f 100644 --- a/.github/workflows/workflow-tests.yml +++ b/.github/workflows/workflow-tests.yml @@ -4,46 +4,134 @@ name: Workflow Tests on: push: +permissions: + contents: write + pull-requests: write + actions: write + jobs: - updater-create-pr: - uses: ./.github/workflows/updater.yml - with: - path: updater/tests/sentry-cli.properties - name: WORKFLOW-TEST-DEPENDENCY-DO-NOT-MERGE - pattern: '^2\.0\.' - pr-strategy: update - _workflow_version: ${{ github.sha }} - secrets: - api-token: ${{ github.token }} - - updater-test-args: - uses: ./.github/workflows/updater.yml - with: - path: updater/tests/workflow-args.sh - name: Workflow args test script - runs-on: macos-latest - pattern: '.*' - _workflow_version: ${{ github.sha }} - secrets: - api-token: ${{ github.token }} - - updater-test-outputs: + # Test PR creation scenario - should create a PR with specific version pattern + updater-pr-creation: runs-on: ubuntu-latest - needs: - - updater-create-pr - - updater-test-args steps: - - run: "[[ '${{ needs.updater-create-pr.outputs.baseBranch }}' == 'main' ]]" - - run: "[[ '${{ needs.updater-create-pr.outputs.originalTag }}' == '2.0.0' ]]" - - run: "[[ '${{ needs.updater-create-pr.outputs.latestTag }}' =~ ^[0-9.]+$ ]]" - - run: "[[ '${{ needs.updater-create-pr.outputs.prUrl }}' =~ ^https://github.com/getsentry/github-workflows/pull/[0-9]+$ ]]" - - run: "[[ '${{ needs.updater-create-pr.outputs.prBranch }}' == 'deps/updater/tests/sentry-cli.properties' ]]" - - - run: "[[ '${{ needs.updater-test-args.outputs.baseBranch }}' == '' ]]" - - run: "[[ '${{ needs.updater-test-args.outputs.originalTag }}' == 'latest' || '${{ needs.updater-test-args.outputs.originalTag }}' =~ ^v?[0-9]+\\.[0-9]+\\.[0-9]+$ ]]" - - run: "[[ '${{ needs.updater-test-args.outputs.originalTag }}' == '${{ needs.updater-test-args.outputs.latestTag }}' ]]" - - run: "[[ '${{ needs.updater-test-args.outputs.prUrl }}' == '' ]]" - - run: "[[ '${{ needs.updater-test-args.outputs.prBranch }}' == '' ]]" + - uses: actions/checkout@v4 + + - name: Run updater action + id: updater + uses: ./updater + with: + path: updater/tests/sentry-cli.properties + name: WORKFLOW-TEST-DEPENDENCY-DO-NOT-MERGE + pattern: '^2\.0\.' + pr-strategy: update + api-token: ${{ github.token }} + + - name: Validate PR creation outputs + env: + BASE_BRANCH: ${{ steps.updater.outputs.baseBranch }} + ORIGINAL_TAG: ${{ steps.updater.outputs.originalTag }} + LATEST_TAG: ${{ steps.updater.outputs.latestTag }} + PR_URL: ${{ steps.updater.outputs.prUrl }} + PR_BRANCH: ${{ steps.updater.outputs.prBranch }} + run: | + echo "🔍 Validating PR creation scenario outputs..." + echo "Base Branch: '$BASE_BRANCH'" + echo "Original Tag: '$ORIGINAL_TAG'" + echo "Latest Tag: '$LATEST_TAG'" + echo "PR URL: '$PR_URL'" + echo "PR Branch: '$PR_BRANCH'" + + # Validate base branch is main + if [[ "$BASE_BRANCH" != "main" ]]; then + echo "❌ Expected base branch 'main', got '$BASE_BRANCH'" + exit 1 + fi + + # Validate original tag is expected test value + if [[ "$ORIGINAL_TAG" != "2.0.0" ]]; then + echo "❌ Expected original tag '2.0.0', got '$ORIGINAL_TAG'" + exit 1 + fi + + # Validate latest tag is a valid version + if [[ ! "$LATEST_TAG" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "❌ Latest tag '$LATEST_TAG' is not a valid version format" + exit 1 + fi + + # Validate PR URL format + if [[ ! "$PR_URL" =~ ^https://github\.com/getsentry/github-workflows/pull/[0-9]+$ ]]; then + echo "❌ PR URL '$PR_URL' is not a valid GitHub PR URL" + exit 1 + fi + + # Validate PR branch format + if [[ "$PR_BRANCH" != "deps/updater/tests/sentry-cli.properties" ]]; then + echo "❌ Expected PR branch 'deps/updater/tests/sentry-cli.properties', got '$PR_BRANCH'" + exit 1 + fi + + echo "✅ PR creation scenario validation passed!" + + # Test no-change scenario - should detect no updates needed + updater-no-changes: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + + - name: Run updater action + id: updater + uses: ./updater + with: + path: updater/tests/workflow-args.sh + name: Workflow args test script + pattern: '.*' + api-token: ${{ github.token }} + + - name: Validate no-changes outputs + env: + BASE_BRANCH: ${{ steps.updater.outputs.baseBranch }} + ORIGINAL_TAG: ${{ steps.updater.outputs.originalTag }} + LATEST_TAG: ${{ steps.updater.outputs.latestTag }} + PR_URL: ${{ steps.updater.outputs.prUrl }} + PR_BRANCH: ${{ steps.updater.outputs.prBranch }} + run: | + echo "🔍 Validating no-changes scenario outputs..." + echo "Base Branch: '$BASE_BRANCH'" + echo "Original Tag: '$ORIGINAL_TAG'" + echo "Latest Tag: '$LATEST_TAG'" + echo "PR URL: '$PR_URL'" + echo "PR Branch: '$PR_BRANCH'" + + # Validate no PR was created (empty values) + if [[ -n "$BASE_BRANCH" ]]; then + echo "❌ Expected empty base branch for no-changes, got '$BASE_BRANCH'" + exit 1 + fi + + if [[ -n "$PR_URL" ]]; then + echo "❌ Expected no PR URL for no-changes, got '$PR_URL'" + exit 1 + fi + + if [[ -n "$PR_BRANCH" ]]; then + echo "❌ Expected no PR branch for no-changes, got '$PR_BRANCH'" + exit 1 + fi + + # Validate original equals latest (no update) + if [[ "$ORIGINAL_TAG" != "$LATEST_TAG" ]]; then + echo "❌ Expected original tag to equal latest tag, got '$ORIGINAL_TAG' != '$LATEST_TAG'" + exit 1 + fi + + # Validate tag format (should be 'latest' or valid version) + if [[ "$ORIGINAL_TAG" != "latest" && ! "$ORIGINAL_TAG" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "❌ Original tag '$ORIGINAL_TAG' is not 'latest' or valid version format" + exit 1 + fi + + echo "✅ No-changes scenario validation passed!" cli-integration: runs-on: ${{ matrix.host }}-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index 02405c7..96c1610 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,55 @@ ## Unreleased -### Fixes +### Breaking Changes + +Updater and Danger reusable workflows are now composite actions ([#114](https://github.com/getsentry/github-workflows/pull/114)) + +To update your existing Updater workflows: +```yaml +### Before + native: + uses: getsentry/github-workflows/.github/workflows/updater.yml@v2 + with: + path: scripts/update-sentry-native-ndk.sh + name: Native SDK + secrets: + # If a custom token is used instead, a CI would be triggered on a created PR. + api-token: ${{ secrets.CI_DEPLOY_KEY }} + +### After + native: + runs-on: ubuntu-latest + steps: + - uses: getsentry/github-workflows/updater@v3 + with: + path: scripts/update-sentry-native-ndk.sh + name: Native SDK + api-token: ${{ secrets.CI_DEPLOY_KEY }} +``` + +To update your existing Danger workflows: +```yaml +### Before + danger: + uses: getsentry/github-workflows/.github/workflows/danger.yml@v2 + +### After + danger: + runs-on: ubuntu-latest + steps: + - uses: getsentry/github-workflows/danger@v3 +``` + +### Features + +- Danger - Improve conventional commit scope handling, and non-conventional PR title support ([#105](https://github.com/getsentry/github-workflows/pull/105)) +- Add Proguard artifact endpoint for Android builds in sentry-server ([#100](https://github.com/getsentry/github-workflows/pull/100)) +- Updater - Add CMake FetchContent support for automated dependency updates ([#104](https://github.com/getsentry/github-workflows/pull/104)) -- Danger and updater download script URLs cannot use GITHUB_WORKFLOW_REF ([#111](https://github.com/getsentry/github-workflows/pull/111)) +### Security + +- Updater - Prevent script injection vulnerabilities through workflow inputs ([#98](https://github.com/getsentry/github-workflows/pull/98)) ## 2.14.1 diff --git a/README.md b/README.md index 1b5a6c5..9c7cfc3 100644 --- a/README.md +++ b/README.md @@ -1,124 +1,23 @@ -# Workflows +# GitHub Workflows -This repository contains reusable workflows and scripts to be used with GitHub Actions. +This repository contains composite actions and scripts to be used with GitHub Actions. -## Updater +## Composite Actions -Dependency updater - see [updater.yml](.github/workflows/updater.yml) - updates dependencies to the latest published git tag. +### Updater -### Example workflow definition +Dependency updater - updates dependencies to the latest published git tag and creates/updates PRs. -```yaml -name: Update Dependencies -on: - # Run every day. - schedule: - - cron: '0 3 * * *' - # And on on every PR merge so we get the updated dependencies ASAP, and to make sure the changelog doesn't conflict. - push: - branches: - - main -jobs: - # Update a git submodule - cocoa: - uses: getsentry/github-workflows/.github/workflows/updater.yml@v2 - with: - path: modules/sentry-cocoa - name: Cocoa SDK - pattern: '^1\.' # Limit to major version '1' - secrets: - api-token: ${{ secrets.CI_DEPLOY_KEY }} +**[📖 View full documentation →](updater/README.md)** - # Update a properties file - cli: - uses: getsentry/github-workflows/.github/workflows/updater.yml@v2 - with: - path: sentry-cli.properties - name: CLI - secrets: - api-token: ${{ secrets.CI_DEPLOY_KEY }} +### Danger - # Update using a custom shell script, see updater/scripts/update-dependency.ps1 for the required arguments - agp: - uses: getsentry/github-workflows/.github/workflows/updater.yml@v2 - with: - path: script.ps1 - name: Gradle Plugin - secrets: - api-token: ${{ secrets.CI_DEPLOY_KEY }} +Runs DangerJS on Pull Requests with a pre-configured set of rules. - # Update a CMake FetchContent dependency with auto-detection (single dependency only) - sentry-native: - uses: getsentry/github-workflows/.github/workflows/updater.yml@v2 - with: - path: vendor/sentry-native.cmake - name: Sentry Native SDK - secrets: - api-token: ${{ secrets.CI_DEPLOY_KEY }} +**[📖 View full documentation →](danger/README.md)** - # Update a CMake FetchContent dependency with explicit dependency name - deps: - uses: getsentry/github-workflows/.github/workflows/updater.yml@v2 - with: - path: vendor/dependencies.cmake#googletest - name: GoogleTest - secrets: - api-token: ${{ secrets.CI_DEPLOY_KEY }} -``` +## Legacy Reusable Workflows (v2) -### Inputs +> âš ī¸ **Deprecated**: Reusable workflows have been converted to composite actions in v3. Please migrate to the composite actions above. -* `path`: Dependency path in the source repository. Supported formats: - * Submodule path - * Properties file (`.properties`) - * Shell script (`.ps1`, `.sh`) - * CMake file with FetchContent: - * `path/to/file.cmake#DepName` - specify dependency name - * `path/to/file.cmake` - auto-detection (single dependency only) - * type: string - * required: true -* `name`: Name used in the PR title and the changelog entry. - * type: string - * required: true -* `pattern`: RegEx pattern that will be matched against available versions when picking the latest one. - * type: string - * required: false - * default: '' -* `changelog-entry`: Whether to add a changelog entry for the update. - * type: boolean - * required: false - * default: true -* `changelog-section`: Section header to attach the changelog entry to. - * type: string - * required: false - * default: Dependencies -* `runs-on`: GitHub Actions virtual environment name to run the udpater job on. - * type: string - * required: false - * default: ubuntu-latest -* `pr-strategy`: How to handle PRs. - Can be either of the following: - * `create` (default) - create a new PR for new dependency versions as they are released - maintainers may merge or close older PRs manually - * `update` - keep a single PR that gets updated with new dependency versions until merged - only the latest version update is available at any time - -### Secrets - -* `api-token`: GH authentication token to create PRs with & push. - If you provide the usual `${{github.token}}`, no followup CI will run on the created PR. - If you want CI to run on the PRs created by the Updater, you need to provide custom user-specific auth token. - -## Danger - -Runs DangerJS on Pull Reqeusts in your repository. This uses custom set of rules defined in [this dangerfile](danger/dangerfile.js). - -```yaml -name: Danger - -on: - pull_request: - types: [opened, synchronize, reopened, edited, ready_for_review, labeled, unlabeled] - -jobs: - danger: - uses: getsentry/github-workflows/.github/workflows/danger.yml@v2 -``` +For v2 migration guide and breaking changes, see [CHANGELOG.md](CHANGELOG.md#3.0.0). diff --git a/danger/README.md b/danger/README.md new file mode 100644 index 0000000..daaee7d --- /dev/null +++ b/danger/README.md @@ -0,0 +1,55 @@ +# Danger Composite Action + +Runs DangerJS on Pull Requests in your repository. This uses custom set of rules defined in [dangerfile.js](dangerfile.js). + +## Usage + +```yaml +name: Danger + +on: + pull_request: + types: [opened, synchronize, reopened, edited, ready_for_review, labeled, unlabeled] + +permissions: + contents: read # To read repository files + pull-requests: write # To post comments on pull requests + statuses: write # To post commit status checks + +jobs: + danger: + runs-on: ubuntu-latest + steps: + - uses: getsentry/github-workflows/danger@v3 +``` + +## Inputs + +* `api-token`: Token for the repo. Can be passed in using `${{ secrets.GITHUB_TOKEN }}`. + * type: string + * required: false + * default: `${{ github.token }}` + +## Outputs + +* `outcome`: Whether the Danger run finished successfully. Possible values are `success`, `failure`, `cancelled`, or `skipped`. + +## Migration from v2 Reusable Workflow + +If you're migrating from the v2 reusable workflow, see the [changelog migration guide](../CHANGELOG.md#unreleased) for detailed examples. + +Key changes: +- Add `runs-on` to specify the runner +- No need for explicit `actions/checkout` step (handled internally) +- Optional `api-token` input (defaults to `github.token`) + +## Rules + +The Danger action runs the following checks: + +- **Changelog validation**: Ensures PRs include appropriate changelog entries +- **Action pinning**: Verifies GitHub Actions are pinned to specific commits for security +- **Conventional commits**: Validates commit message format and PR title conventions +- **Cross-repo links**: Checks for proper formatting of links in changelog entries + +For detailed rule implementations, see [dangerfile.js](dangerfile.js). \ No newline at end of file diff --git a/danger/action.yml b/danger/action.yml new file mode 100644 index 0000000..dcdc110 --- /dev/null +++ b/danger/action.yml @@ -0,0 +1,40 @@ +name: 'Danger JS' +description: 'Runs DangerJS with a pre-configured set of rules on a Pull Request' +author: 'Sentry' + +inputs: + api-token: + description: 'Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}' + required: false + default: ${{ github.token }} + +outputs: + outcome: + description: 'Whether the Danger run finished successfully. Possible values are success, failure, cancelled, or skipped.' + value: ${{ steps.danger.outcome }} + +runs: + using: 'composite' + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ inputs.api-token }} + fetch-depth: 0 + + # Using a pre-built docker image in GitHub container registry instead of NPM to reduce possible attack vectors. + - name: Run DangerJS + id: danger + shell: bash + run: | + docker run \ + --volume ${{ github.workspace }}:/github/workspace \ + --volume ${{ github.action_path }}:${{ github.action_path }} \ + --volume ${{ github.event_path }}:${{ github.event_path }} \ + --workdir /github/workspace \ + --user $(id -u) \ + -e "INPUT_ARGS" -e "GITHUB_JOB" -e "GITHUB_REF" -e "GITHUB_SHA" -e "GITHUB_REPOSITORY" -e "GITHUB_REPOSITORY_OWNER" -e "GITHUB_RUN_ID" -e "GITHUB_RUN_NUMBER" -e "GITHUB_RETENTION_DAYS" -e "GITHUB_RUN_ATTEMPT" -e "GITHUB_ACTOR" -e "GITHUB_TRIGGERING_ACTOR" -e "GITHUB_WORKFLOW" -e "GITHUB_HEAD_REF" -e "GITHUB_BASE_REF" -e "GITHUB_EVENT_NAME" -e "GITHUB_SERVER_URL" -e "GITHUB_API_URL" -e "GITHUB_GRAPHQL_URL" -e "GITHUB_REF_NAME" -e "GITHUB_REF_PROTECTED" -e "GITHUB_REF_TYPE" -e "GITHUB_WORKSPACE" -e "GITHUB_ACTION" -e "GITHUB_EVENT_PATH" -e "GITHUB_ACTION_REPOSITORY" -e "GITHUB_ACTION_REF" -e "GITHUB_PATH" -e "GITHUB_ENV" -e "GITHUB_STEP_SUMMARY" -e "RUNNER_OS" -e "RUNNER_ARCH" -e "RUNNER_NAME" -e "RUNNER_TOOL_CACHE" -e "RUNNER_TEMP" -e "RUNNER_WORKSPACE" -e "ACTIONS_RUNTIME_URL" -e "ACTIONS_RUNTIME_TOKEN" -e "ACTIONS_CACHE_URL" -e GITHUB_ACTIONS=true -e CI=true \ + -e GITHUB_TOKEN="${{ inputs.api-token }}" \ + -e DANGER_DISABLE_TRANSPILATION="true" \ + ghcr.io/danger/danger-js:11.3.1 \ + --failOnErrors --dangerfile ${{ github.action_path }}/dangerfile.js \ No newline at end of file diff --git a/scripts/update-version.ps1 b/scripts/update-version.ps1 deleted file mode 100644 index 57ca601..0000000 --- a/scripts/update-version.ps1 +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env pwsh - -param( - [Parameter(Mandatory=$true, Position=0)] - [string]$OldVersion, - - [Parameter(Mandatory=$true, Position=1)] - [string]$NewVersion -) - -Set-StrictMode -Version Latest -$ErrorActionPreference = "Stop" -$PSNativeCommandUseErrorActionPreference = $true - -Write-Host "Updating version from $OldVersion to $NewVersion" - -# Update specific workflow files with _workflow_version inputs -Write-Host "Updating workflow files..." -$workflowFiles = @( - ".github/workflows/updater.yml", - ".github/workflows/danger.yml" -) - -foreach ($filePath in $workflowFiles) { - $content = Get-Content -Path $filePath -Raw - - # Check if this file has _workflow_version input with a default value - if ($content -match '(?ms)_workflow_version:.*?default:\s*([^\s#]+)') { - Write-Host "Updating $filePath..." - $oldDefault = $Matches[1] - - # Replace the default value for _workflow_version - $newContent = $content -replace '((?ms)_workflow_version:.*?default:\s*)([^\s#]+)', "`${1}'$NewVersion'" - - # Write the updated content back to the file - $newContent | Out-File -FilePath $filePath -Encoding utf8 -NoNewline - - Write-Host " Updated default from '$oldDefault' to '$NewVersion'" - } else { - Write-Error "No _workflow_version default found in $filePath" - } -} - -Write-Host "Version update completed successfully!" diff --git a/updater/README.md b/updater/README.md new file mode 100644 index 0000000..ca5db31 --- /dev/null +++ b/updater/README.md @@ -0,0 +1,127 @@ +# Updater Composite Action + +Dependency updater - updates dependencies to the latest published git tag and creates/updates PRs. + +## Usage + +```yaml +name: Update Dependencies +on: + # Run every day. + schedule: + - cron: '0 3 * * *' + # And on every PR merge so we get the updated dependencies ASAP, and to make sure the changelog doesn't conflict. + push: + branches: + - main + +permissions: + contents: write # To modify files and create commits + pull-requests: write # To create and update pull requests + actions: write # To cancel previous workflow runs + +jobs: + # Update a git submodule + cocoa: + runs-on: ubuntu-latest + steps: + - uses: getsentry/github-workflows/updater@v3 + with: + path: modules/sentry-cocoa + name: Cocoa SDK + pattern: '^1\.' # Limit to major version '1' + api-token: ${{ secrets.CI_DEPLOY_KEY }} + + # Update a properties file + cli: + runs-on: ubuntu-latest + steps: + - uses: getsentry/github-workflows/updater@v3 + with: + path: sentry-cli.properties + name: CLI + api-token: ${{ secrets.CI_DEPLOY_KEY }} + + # Update using a custom shell script, see updater/scripts/update-dependency.ps1 for the required arguments + agp: + runs-on: ubuntu-latest + steps: + - uses: getsentry/github-workflows/updater@v3 + with: + path: script.ps1 + name: Gradle Plugin + api-token: ${{ secrets.CI_DEPLOY_KEY }} + + # Update a CMake FetchContent dependency with auto-detection (single dependency only) + sentry-native: + runs-on: ubuntu-latest + steps: + - uses: getsentry/github-workflows/updater@v3 + with: + path: vendor/sentry-native.cmake + name: Sentry Native SDK + api-token: ${{ secrets.CI_DEPLOY_KEY }} + + # Update a CMake FetchContent dependency with explicit dependency name + deps: + runs-on: ubuntu-latest + steps: + - uses: getsentry/github-workflows/updater@v3 + with: + path: vendor/dependencies.cmake#googletest + name: GoogleTest + api-token: ${{ secrets.CI_DEPLOY_KEY }} +``` + +## Inputs + +* `path`: Dependency path in the source repository. Supported formats: + * Submodule path + * Properties file (`.properties`) + * Shell script (`.ps1`, `.sh`) + * CMake file with FetchContent: + * `path/to/file.cmake#DepName` - specify dependency name + * `path/to/file.cmake` - auto-detection (single dependency only) + * type: string + * required: true +* `name`: Name used in the PR title and the changelog entry. + * type: string + * required: true +* `pattern`: RegEx pattern that will be matched against available versions when picking the latest one. + * type: string + * required: false + * default: '' +* `changelog-entry`: Whether to add a changelog entry for the update. + * type: boolean + * required: false + * default: true +* `changelog-section`: Section header to attach the changelog entry to. + * type: string + * required: false + * default: Dependencies +* `pr-strategy`: How to handle PRs. + Can be either of the following: + * `create` (default) - create a new PR for new dependency versions as they are released - maintainers may merge or close older PRs manually + * `update` - keep a single PR that gets updated with new dependency versions until merged - only the latest version update is available at any time +* `api-token`: Token for the repo. Can be passed in using `${{ secrets.GITHUB_TOKEN }}`. + If you provide the usual `${{ github.token }}`, no followup CI will run on the created PR. + If you want CI to run on the PRs created by the Updater, you need to provide custom user-specific auth token. + * type: string + * required: true + +## Outputs + +* `prUrl`: The created/updated PR's URL. +* `baseBranch`: The base branch name. +* `prBranch`: The created/updated PR branch name. +* `originalTag`: The original tag from which the dependency was updated from. +* `latestTag`: The latest tag to which the dependency was updated to. + +## Migration from v2 Reusable Workflow + +If you're migrating from the v2 reusable workflow, see the [changelog migration guide](../CHANGELOG.md#unreleased) for detailed examples. + +Key changes: +- Add `runs-on` to specify the runner +- Move `secrets.api-token` to `with.api-token` +- No need for explicit `actions/checkout` step (handled internally) \ No newline at end of file diff --git a/updater/action.yml b/updater/action.yml new file mode 100644 index 0000000..a40a9f9 --- /dev/null +++ b/updater/action.yml @@ -0,0 +1,268 @@ +name: 'Dependency Updater' +description: 'Updates dependencies to the latest published tag and creates/updates PRs' +author: 'Sentry' + +inputs: + path: + description: 'Dependency path in the source repository, this can be either a submodule, a .properties file, a shell script, or a CMake file with FetchContent.' + required: true + name: + description: 'Name used in the PR title and the changelog entry.' + required: true + pattern: + description: 'RegEx pattern that will be matched against available versions when picking the latest one.' + required: false + default: '' + changelog-entry: + description: 'Whether to add a changelog entry for the update.' + required: false + default: 'true' + changelog-section: + description: 'Section header to attach the changelog entry to.' + required: false + default: 'Dependencies' + pr-strategy: + description: 'How to handle PRs - can be either "create" (create new PRs for each version) or "update" (keep single PR updated with latest version)' + required: false + default: 'create' + api-token: + description: 'Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}' + required: true + +outputs: + prUrl: + description: 'The created/updated PRs url.' + value: ${{ steps.pr.outputs.url }} + baseBranch: + description: 'The base branch name.' + value: ${{ steps.root.outputs.baseBranch }} + prBranch: + description: 'The created/updated pr branch name.' + value: ${{ steps.root.outputs.prBranch }} + originalTag: + description: 'The original tag from which the dependency was updated from.' + value: ${{ steps.target.outputs.originalTag }} + latestTag: + description: 'The latest tag to which the dependency was updated to.' + value: ${{ steps.target.outputs.latestTag }} + +runs: + using: 'composite' + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ inputs.api-token }} + + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@85880fa0301c86cca9da44039ee3bb12d3bedbfa # Tag: 0.12.1 + with: + access_token: ${{ github.token }} + + - name: Validate dependency name + shell: pwsh + run: | + # Validate that inputs.name contains only safe characters + if ('${{ inputs.name }}' -notmatch '^[a-zA-Z0-9_\./@\s-]+$') { + Write-Output "::error::Invalid dependency name: '${{ inputs.name }}'. Only alphanumeric characters, spaces, and _-./@ are allowed." + exit 1 + } + Write-Output "✓ Dependency name '${{ inputs.name }}' is valid" + + - name: Validate dependency path + shell: pwsh + run: | + # Validate that inputs.path contains only safe characters (including # for CMake dependencies) + if ('${{ inputs.path }}' -notmatch '^[a-zA-Z0-9_\./#-]+$') { + Write-Output "::error::Invalid dependency path: '${{ inputs.path }}'. Only alphanumeric characters and _-./# are allowed." + exit 1 + } + Write-Output "✓ Dependency path '${{ inputs.path }}' is valid" + + # What we need to accomplish: + # * update to the latest tag + # * create a PR + # * update changelog (including the link to the just created PR) + # + # What we actually do is based on whether a PR exists already: + # * YES it does: + # * make the update + # * update changelog (with the ID of an existing PR) + # * push to the PR + # * NO it doesn't: + # * make the update + # * push to a new PR + # * update changelog (with the ID of the just created PR) + # * push to the PR + # We do different approach on subsequent runs because otherwise we would spam users' mailboxes + # with notifications about pushes to existing PRs. This way there is actually no push if not needed. + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ inputs.api-token }} + + - name: Update to the latest version + id: target + shell: pwsh + env: + DEPENDENCY_PATH: ${{ inputs.path }} + DEPENDENCY_PATTERN: ${{ inputs.pattern }} + run: ${{ github.action_path }}/scripts/update-dependency.ps1 -Path $env:DEPENDENCY_PATH -Pattern $env:DEPENDENCY_PATTERN + + - name: Get the base repo info + if: steps.target.outputs.latestTag != steps.target.outputs.originalTag + id: root + shell: pwsh + env: + PR_STRATEGY: ${{ inputs.pr-strategy }} + DEPENDENCY_PATH: ${{ inputs.path }} + run: | + $mainBranch = $(git remote show origin | Select-String "HEAD branch: (.*)").Matches[0].Groups[1].Value + $prBranch = switch ($env:PR_STRATEGY) + { + 'create' { "deps/$env:DEPENDENCY_PATH/${{ steps.target.outputs.latestTag }}" } + 'update' { "deps/$env:DEPENDENCY_PATH" } + default { throw "Unkown PR strategy '$env:PR_STRATEGY'." } + } + "baseBranch=$mainBranch" | Tee-Object $env:GITHUB_OUTPUT -Append + "prBranch=$prBranch" | Tee-Object $env:GITHUB_OUTPUT -Append + $nonBotCommits = ${{ github.action_path }}/scripts/nonbot-commits.ps1 ` + -RepoUrl "$(git config --get remote.origin.url)" -PrBranch $prBranch -MainBranch $mainBranch + $changed = $nonBotCommits.Length -gt 0 ? 'true' : 'false' + "changed=$changed" | Tee-Object $env:GITHUB_OUTPUT -Append + if ("$changed" -eq "true") + { + Write-Output "::warning::Target branch '$prBranch' has been changed manually - skipping updater to avoid overwriting these changes." + } + + - name: Parse the existing PR URL + if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.root.outputs.changed == 'false') }} + id: existing-pr + env: + GH_TOKEN: ${{ inputs.api-token }} + shell: pwsh + run: | + $urls = @(gh api 'repos/${{ github.repository }}/pulls?base=${{ steps.root.outputs.baseBranch }}&head=${{ github.repository_owner }}:${{ steps.root.outputs.prBranch }}' --jq '.[].html_url') + if ($urls.Length -eq 0) + { + "url=" | Tee-Object $env:GITHUB_OUTPUT -Append + } + elseif ($urls.Length -eq 1) + { + "url=$($urls[0])" | Tee-Object $env:GITHUB_OUTPUT -Append + } + else + { + throw "Unexpected number of PRs matched ($($urls.Length)): $urls" + } + + - name: Show git diff + if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.existing-pr.outputs.url == '') && ( steps.root.outputs.changed == 'false') }} + shell: bash + run: git --no-pager diff + + - name: Get target changelog + if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.root.outputs.changed == 'false') }} + shell: pwsh + run: | + $changelog = ${{ github.action_path }}/scripts/get-changelog.ps1 ` + -RepoUrl '${{ steps.target.outputs.url }}' ` + -OldTag '${{ steps.target.outputs.originalTag }}' ` + -NewTag '${{ steps.target.outputs.latestTag }}' + ${{ github.action_path }}/scripts/set-github-env.ps1 TARGET_CHANGELOG $changelog + + # First we create a PR only if it doesn't exist. We will later overwrite the content with the same action. + - name: Create a PR + if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.existing-pr.outputs.url == '') && ( steps.root.outputs.changed == 'false') }} + uses: peter-evans/create-pull-request@a4f52f8033a6168103c2538976c07b467e8163bc # pin#v6.0.1 + id: create-pr + env: + DEPENDENCY_PATH: ${{ inputs.path }} + DEPENDENCY_NAME: ${{ inputs.name }} + with: + base: ${{ steps.root.outputs.baseBranch }} + branch: ${{ steps.root.outputs.prBranch }} + commit-message: 'chore: update ${{ env.DEPENDENCY_PATH }} to ${{ steps.target.outputs.latestTag }}' + author: 'GitHub ' + title: 'chore(deps): update ${{ env.DEPENDENCY_NAME }} to ${{ steps.target.outputs.latestTagNice }}' + body: | + Bumps ${{ env.DEPENDENCY_PATH }} from ${{ steps.target.outputs.originalTag }} to ${{ steps.target.outputs.latestTag }}. + + Auto-generated by a [dependency updater](https://github.com/getsentry/github-workflows/blob/main/updater/action.yml). + ${{ env.TARGET_CHANGELOG }} + labels: dependencies + + - name: Verify we have a PR + if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.root.outputs.changed == 'false') }} + id: pr + shell: pwsh + run: | + if ('${{ steps.create-pr.outputs.pull-request-url }}' -ne '') + { + "url=${{ steps.create-pr.outputs.pull-request-url }}" | Tee-Object $env:GITHUB_OUTPUT -Append + } + elseif ('${{ steps.existing-pr.outputs.url }}' -ne '') + { + "url=${{ steps.existing-pr.outputs.url }}" | Tee-Object $env:GITHUB_OUTPUT -Append + } + else + { + throw "PR hasn't been created" + } + + # If we had to create a new PR, we must do a clean checkout & update the submodule again. + # If we didn't do this, the new PR would only have a changelog... + - name: 'After new PR: restore repo' + if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.existing-pr.outputs.url == '') && ( steps.root.outputs.changed == 'false') }} + uses: actions/checkout@v4 + with: + token: ${{ inputs.api-token }} + + - name: 'After new PR: redo the update' + if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.existing-pr.outputs.url == '') && ( steps.root.outputs.changed == 'false') }} + shell: pwsh + env: + DEPENDENCY_PATH: ${{ inputs.path }} + run: ${{ github.action_path }}/scripts/update-dependency.ps1 -Path $env:DEPENDENCY_PATH -Tag '${{ steps.target.outputs.latestTag }}' + + - name: Update Changelog + if: ${{ inputs.changelog-entry && ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.root.outputs.changed == 'false') }} + shell: pwsh + env: + DEPENDENCY_NAME: ${{ inputs.name }} + CHANGELOG_SECTION: ${{ inputs.changelog-section }} + run: | + ${{ github.action_path }}/scripts/update-changelog.ps1 ` + -Name $env:DEPENDENCY_NAME ` + -PR '${{ steps.pr.outputs.url }}' ` + -RepoUrl '${{ steps.target.outputs.url }}' ` + -MainBranch '${{ steps.target.outputs.mainBranch }}' ` + -OldTag '${{ steps.target.outputs.originalTag }}' ` + -NewTag '${{ steps.target.outputs.latestTag }}' ` + -Section $env:CHANGELOG_SECTION + + - name: Show final git diff + if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.root.outputs.changed == 'false') }} + shell: bash + run: git --no-pager diff + + # Now make the PR in its final state. This way we only have one commit and no updates if there are no changes between runs. + - name: Update the PR + if: ${{ ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.root.outputs.changed == 'false') }} + uses: peter-evans/create-pull-request@a4f52f8033a6168103c2538976c07b467e8163bc # pin#v6.0.1 + id: update + env: + DEPENDENCY_PATH: ${{ inputs.path }} + DEPENDENCY_NAME: ${{ inputs.name }} + with: + base: ${{ steps.root.outputs.baseBranch }} + branch: ${{ steps.root.outputs.prBranch }} + commit-message: 'chore: update ${{ env.DEPENDENCY_PATH }} to ${{ steps.target.outputs.latestTag }}' + author: 'GitHub ' + title: 'chore(deps): update ${{ env.DEPENDENCY_NAME }} to ${{ steps.target.outputs.latestTagNice }}' + body: | + Bumps ${{ env.DEPENDENCY_PATH }} from ${{ steps.target.outputs.originalTag }} to ${{ steps.target.outputs.latestTag }}. + + Auto-generated by a [dependency updater](https://github.com/getsentry/github-workflows/blob/main/updater/action.yml). + ${{ env.TARGET_CHANGELOG }} + labels: dependencies