├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── close-bom-if-passing.yml │ ├── auto-merge-safe-deps.yml │ ├── self-update-major-tag.yml │ ├── close-bom-if-passing.sh │ └── maven-cd.yml ├── .editorconfig ├── README.md └── LICENSE /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf working-tree-encoding=UTF-8 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 2 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # github-reusable-workflows 2 | 3 | Repository for reusable workflows 4 | 5 | ## Other workflows 6 | 7 | We do host some non reusable workflows that is meant to be consumed by repository as in any workflows files starting with `self-` 8 | -------------------------------------------------------------------------------- /.github/workflows/close-bom-if-passing.yml: -------------------------------------------------------------------------------- 1 | name: Close BOM update PR if Passing 2 | 3 | # To be used with: 4 | # on: 5 | # check_run: 6 | # types: 7 | # - completed 8 | # permissions: 9 | # contents: read 10 | # pull-requests: write 11 | 12 | on: 13 | - workflow_call 14 | 15 | jobs: 16 | handle-ci-status: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Download and run script 21 | run: | 22 | gh api repos/jenkins-infra/github-reusable-workflows/contents/.github/workflows/close-bom-if-passing.sh --jq .content | base64 -d > close-bom-if-passing.sh 23 | chmod +x close-bom-if-passing.sh 24 | ./close-bom-if-passing.sh 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | GITHUB_EVENT_PATH: ${{ github.event_path }} 28 | GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }} 29 | GITHUB_REPOSITORY: ${{ github.repository }} 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Jenkins Infra 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge-safe-deps.yml: -------------------------------------------------------------------------------- 1 | name: Automatically approve and merge safe dependency updates 2 | 3 | # To be used with: 4 | # on: 5 | # - pull_request_target 6 | # permissions: 7 | # contents: write 8 | # pull-requests: write 9 | 10 | on: 11 | - workflow_call 12 | 13 | jobs: 14 | auto-merge: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: act 18 | if: ${{ (github.event.pull_request.user.login == 'renovate[bot]' && ( 19 | startsWith(github.head_ref, 'renovate/org.jenkins-ci.plugins-plugin-')) || 20 | startsWith(github.head_ref, 'renovate/io.jenkins.tools.incrementals-git-changelist-maven-extension-')) || 21 | (github.event.pull_request.user.login == 'dependabot[bot]' && ( 22 | startsWith(github.head_ref, 'dependabot/maven/org.jenkins-ci.plugins-plugin-')) || 23 | startsWith(github.head_ref, 'dependabot/maven/io.jenkins.tools.incrementals-git-changelist-maven-extension-')) }} 24 | run: | 25 | gh pr review --approve "$PR_URL" 26 | gh pr merge --squash --auto "$PR_URL" 27 | env: 28 | PR_URL: ${{github.event.pull_request.html_url}} 29 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 30 | -------------------------------------------------------------------------------- /.github/workflows/self-update-major-tag.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | release: 4 | types: [published] 5 | workflow_dispatch: 6 | inputs: 7 | tag: 8 | required: false 9 | description: The tag to move major version tag to 10 | default: "" 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v5 17 | with: 18 | fetch-depth: 0 19 | - name: version 20 | id: version 21 | env: 22 | INPUT_TAG: ${{ inputs.tag }} 23 | run: | 24 | if [[ "${INPUT_TAG}" =~ ^v[0-9]+.[0-9]+.[0-9]+$ ]]; then 25 | tag="${INPUT_TAG}" 26 | fi 27 | if [[ ! -z "${GITHUB_REF}" ]] && [[ "${GITHUB_REF}" =~ ^refs/tags/v[0-9]+.[0-9]+.[0-9]+$ ]]; then 28 | tag=${GITHUB_REF/refs\/tags\//} 29 | fi 30 | if [[ -z "${tag}" ]]; then 31 | echo "No tag found" 32 | exit 1 33 | fi 34 | version=${tag#v} 35 | major=${version%%.*} 36 | echo "tag=${tag}" >> $GITHUB_OUTPUT 37 | echo "version=${version}" >> $GITHUB_OUTPUT 38 | echo "major=${major}" >> $GITHUB_OUTPUT 39 | - name: force update major tag 40 | run: | 41 | git tag v${{ steps.version.outputs.major }} ${{ steps.version.outputs.tag }} -f 42 | git push origin refs/tags/v${{ steps.version.outputs.major }} -f 43 | -------------------------------------------------------------------------------- /.github/workflows/close-bom-if-passing.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | # Exit early if this is not a Jenkins check that completed 5 | if [[ "$(jq -r .check_run.name < "$GITHUB_EVENT_PATH")" != "Jenkins" ]]; then 6 | echo "Not a Jenkins check, exiting" 7 | exit 0 8 | fi 9 | 10 | if [[ "$(jq -r .check_run.status < "$GITHUB_EVENT_PATH")" != "completed" ]]; then 11 | echo "Check not completed, exiting" 12 | exit 0 13 | fi 14 | 15 | # Get commit SHA from the check run 16 | check_run_sha="$(jq -r .check_run.head_sha < "$GITHUB_EVENT_PATH")" 17 | check_run_conclusion="$(jq -r .check_run.conclusion < "$GITHUB_EVENT_PATH")" 18 | 19 | echo "Processing Jenkins check for commit: $check_run_sha" 20 | echo "Check conclusion: $check_run_conclusion" 21 | 22 | # Get the PR associated with this commit 23 | pr_data=$(gh api graphql -f query=' 24 | query($owner: String!, $repo: String!, $sha: GitObjectID!) { 25 | repository(owner: $owner, name: $repo) { 26 | object(oid: $sha) { 27 | ... on Commit { 28 | associatedPullRequests(first: 1) { 29 | nodes { 30 | number 31 | headRefName 32 | isDraft 33 | headRefOid 34 | } 35 | } 36 | } 37 | } 38 | } 39 | }' -f owner="$GITHUB_REPOSITORY_OWNER" -f repo="${GITHUB_REPOSITORY#*/}" -f sha="$check_run_sha") 40 | 41 | # Extract PR data 42 | pr_number=$(echo "$pr_data" | jq -r '.data.repository.object.associatedPullRequests.nodes[0].number // empty') 43 | pr_head_sha=$(echo "$pr_data" | jq -r '.data.repository.object.associatedPullRequests.nodes[0].headRefOid // empty') 44 | pr_head_ref=$(echo "$pr_data" | jq -r '.data.repository.object.associatedPullRequests.nodes[0].headRefName // empty') 45 | 46 | # Exit early if no associated PR found 47 | if [[ -z "$pr_number" ]]; then 48 | echo "No associated pull request found for commit $check_run_sha, exiting" 49 | exit 0 50 | fi 51 | 52 | pr_draft=$(echo "$pr_data" | jq -r '.data.repository.object.associatedPullRequests.nodes[0].isDraft // empty') 53 | 54 | echo "Head ref: $pr_head_ref" 55 | echo "Draft: $pr_draft" 56 | 57 | # Check if this is a Dependabot or Renovate PR for io.jenkins.tools.bom-bom-* dependencies 58 | if [[ ! ("$pr_head_ref" =~ ^renovate/io\.jenkins\.tools\.bom-bom- || "$pr_head_ref" =~ ^dependabot/maven/io\.jenkins\.tools\.bom-bom-) ]]; then 59 | echo "Not a BOM dependency PR (head ref: $pr_head_ref), exiting" 60 | exit 0 61 | fi 62 | 63 | echo "Processing BOM dependency PR with head ref: $pr_head_ref" 64 | 65 | # Verify commit matches the PR head 66 | if [[ "$check_run_sha" != "$pr_head_sha" ]]; then 67 | echo "Head SHA mismatch (check: $check_run_sha, PR head: $pr_head_sha), exiting" 68 | exit 0 69 | fi 70 | 71 | # TODO verify that there is only commit in the PR (presumably from the bot) so we do not close PRs with manual changes 72 | 73 | # Handle based on CI conclusion 74 | if [[ "$check_run_conclusion" == "success" ]]; then 75 | echo "CI passed, closing PR #$pr_number" 76 | 77 | # Leave a comment and close the PR 78 | gh pr comment "$pr_number" --body "✅ CI passed successfully. Closing this PR." --repo "$GITHUB_REPOSITORY" 79 | gh pr close "$pr_number" --repo "$GITHUB_REPOSITORY" 80 | 81 | echo "PR #$pr_number closed due to successful CI" 82 | else 83 | echo "CI failed with conclusion: $check_run_conclusion" 84 | 85 | # Leave a comment 86 | gh pr comment "$pr_number" --body "❌ CI failed with conclusion: \`$check_run_conclusion\`. Please investigate the failure." --repo "$GITHUB_REPOSITORY" 87 | 88 | # Convert from draft to ready for review if it's currently a draft 89 | if [[ "$pr_draft" == "true" ]]; then 90 | gh pr ready "$pr_number" --repo "$GITHUB_REPOSITORY" 91 | echo "PR #$pr_number converted from draft to ready for review" 92 | else 93 | echo "PR #$pr_number is already ready for review" 94 | fi 95 | fi 96 | -------------------------------------------------------------------------------- /.github/workflows/maven-cd.yml: -------------------------------------------------------------------------------- 1 | # Note: additional setup is required, see https://www.jenkins.io/redirect/continuous-delivery-of-plugins 2 | 3 | name: maven-cd 4 | on: 5 | workflow_call: 6 | inputs: 7 | validate_only: 8 | type: string 9 | required: false 10 | description: | 11 | Validate with release drafter only 12 | => Skip release job 13 | default: false 14 | secrets: 15 | MAVEN_USERNAME: 16 | required: true 17 | description: Maven username used for deploying the plugin jar to Jenkins Artifactory Repository 18 | MAVEN_TOKEN: 19 | required: true 20 | description: Maven token used for deploying the plugin jar to Jenkins Artifactory Repository 21 | jobs: 22 | validate: 23 | runs-on: ubuntu-latest 24 | outputs: 25 | # If this is being changed, then align step log-should_release-details below! 26 | should_release: ${{ inputs.validate_only == 'false' && steps.verify-ci-status.outputs.result == 'success' && steps.interesting-categories.outputs.interesting == 'true' }} 27 | steps: 28 | - name: Verify CI status 29 | uses: jenkins-infra/verify-ci-status-action@v1.2.2 30 | id: verify-ci-status 31 | with: 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | output_result: true 34 | - name: Release Drafter 35 | uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6 36 | id: draft 37 | if: steps.verify-ci-status.outputs.result == 'success' 38 | with: 39 | name: next 40 | tag: next 41 | version: next 42 | env: 43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 44 | - name: Check interesting categories 45 | uses: jenkins-infra/interesting-category-action@v1.2.1 46 | id: interesting-categories 47 | if: steps.verify-ci-status.outputs.result == 'success' 48 | with: 49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 50 | RELEASE_DRAFT_BODY: ${{ steps.draft.outputs.body }} 51 | - name: Log should_release details 52 | id: log-should_release-details 53 | run: | 54 | MD_CODE='```' 55 | cat << END_OF_SUMMARY | tee -a $GITHUB_STEP_SUMMARY 56 | ## Job Filter Summary 57 | ${MD_CODE} 58 | ================================ 59 | Release job filter details: 60 | VALIDATE_ONLY: ${VALIDATE_ONLY} 61 | CI_STATUS: ${CI_STATUS} 62 | INTERESTING_CATEGORY: ${INTERESTING_CATEGORY} 63 | -------------------------------- 64 | => SHOULD_RELEASE: ${SHOULD_RELEASE} 65 | ================================ 66 | ${MD_CODE} 67 | END_OF_SUMMARY 68 | env: 69 | VALIDATE_ONLY: ${{ inputs.validate_only }} 70 | CI_STATUS: ${{ steps.verify-ci-status.outputs.result }} 71 | INTERESTING_CATEGORY: ${{ steps.interesting-categories.outputs.interesting }} 72 | # This must be equal to output should_release of job validate above! 73 | SHOULD_RELEASE: ${{ inputs.validate_only == 'false' && steps.verify-ci-status.outputs.result == 'success' && steps.interesting-categories.outputs.interesting == 'true' }} 74 | release: 75 | runs-on: ubuntu-latest 76 | needs: [validate] 77 | if: needs.validate.outputs.should_release == 'true' 78 | steps: 79 | - name: Check out 80 | uses: actions/checkout@v5 81 | with: 82 | fetch-depth: 0 83 | - name: Set up JDK 84 | uses: actions/setup-java@v5 85 | with: 86 | distribution: temurin 87 | java-version: 21 88 | - name: Release 89 | uses: jenkins-infra/jenkins-maven-cd-action@v1.4.1 90 | with: 91 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 92 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 93 | MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }} 94 | --------------------------------------------------------------------------------