summaryrefslogtreecommitdiffstats
path: root/.github/workflows/reusable-change-detection.yml
blob: 1a6fd33186840c93e456b70ba270d8e8819cb8ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
name: Reusable change detection

on:  # yamllint disable-line rule:truthy
  workflow_call:
    outputs:
      # Some of the referenced steps set outputs conditionally and there may be
      # cases when referencing them evaluates to empty strings. It is nice to
      # work with proper booleans so they have to be evaluated through JSON
      # conversion in the expressions. However, empty strings used like that
      # may trigger all sorts of undefined and hard-to-debug behaviors in
      # GitHub Actions CI/CD. To help with this, all of the outputs set here
      # that are meant to be used as boolean flags (and not arbitrary strings),
      # MUST have fallbacks with default values set. A common pattern would be
      # to add ` || false` to all such expressions here, in the output
      # definitions. They can then later be safely used through the following
      # idiom in job conditionals and other expressions. Here's some examples:
      #
      #   if: fromJSON(needs.change-detection.outputs.run-docs)
      #
      #   ${{
      #        fromJSON(needs.change-detection.outputs.run-tests)
      #        && 'truthy-branch'
      #        || 'falsy-branch'
      #   }}
      #
      config_hash:
        description: Config hash value for use in cache keys
        value: ${{ jobs.compute-changes.outputs.config-hash }}  # str
      run-docs:
        description: Whether to build the docs
        value: ${{ jobs.compute-changes.outputs.run-docs || false }}  # bool
      run_tests:
        description: Whether to run the regular tests
        value: ${{ jobs.compute-changes.outputs.run-tests || false }}  # bool
      run-win-msi:
        description: Whether to run the MSI installer smoke tests
        value: >-  # bool
          ${{ jobs.compute-changes.outputs.run-win-msi || false }}
      run_hypothesis:
        description: Whether to run the Hypothesis tests
        value: >-  # bool
          ${{ jobs.compute-changes.outputs.run-hypothesis || false }}
      run_cifuzz:
        description: Whether to run the CIFuzz job
        value: >-  # bool
          ${{ jobs.compute-changes.outputs.run-cifuzz || false }}

jobs:
  compute-changes:
    name: Compute changed files
    runs-on: ubuntu-latest
    timeout-minutes: 10
    outputs:
      config-hash: ${{ steps.config-hash.outputs.hash }}
      run-cifuzz: ${{ steps.check.outputs.run-cifuzz }}
      run-docs: ${{ steps.docs-changes.outputs.run-docs }}
      run-hypothesis: ${{ steps.check.outputs.run-hypothesis }}
      run-tests: ${{ steps.check.outputs.run-tests }}
      run-win-msi: ${{ steps.win-msi-changes.outputs.run-win-msi }}
    steps:
    - run: >-
        echo '${{ github.event_name }}'
    - uses: actions/checkout@v4
    - name: Check for source changes
      id: check
      run: |
        if [ -z "$GITHUB_BASE_REF" ]; then
          echo "run-tests=true" >> "$GITHUB_OUTPUT"
        else
          git fetch origin "$GITHUB_BASE_REF" --depth=1
          # git diff "origin/$GITHUB_BASE_REF..." (3 dots) may be more
          # reliable than git diff "origin/$GITHUB_BASE_REF.." (2 dots),
          # but it requires to download more commits (this job uses
          # "git fetch --depth=1").
          #
          # git diff "origin/$GITHUB_BASE_REF..." (3 dots) works with Git
          # 2.26, but Git 2.28 is stricter and fails with "no merge base".
          #
          # git diff "origin/$GITHUB_BASE_REF.." (2 dots) should be enough on
          # GitHub, since GitHub starts by merging origin/$GITHUB_BASE_REF
          # into the PR branch anyway.
          #
          # https://github.com/python/core-workflow/issues/373
          git diff --name-only "origin/$GITHUB_BASE_REF.." | grep -qvE '(\.rst$|^Doc|^Misc|^\.pre-commit-config\.yaml$|\.ruff\.toml$|\.md$|mypy\.ini$)' && echo "run-tests=true" >> "$GITHUB_OUTPUT" || true
        fi

        # Check if we should run hypothesis tests
        GIT_BRANCH=${GITHUB_BASE_REF:-${GITHUB_REF#refs/heads/}}
        echo "$GIT_BRANCH"
        if $(echo "$GIT_BRANCH" | grep -q -w '3\.\(8\|9\|10\|11\)'); then
          echo "Branch too old for hypothesis tests"
          echo "run-hypothesis=false" >> "$GITHUB_OUTPUT"
        else
          echo "Run hypothesis tests"
          echo "run-hypothesis=true" >> "$GITHUB_OUTPUT"
        fi

        # oss-fuzz maintains a configuration for fuzzing the main branch of
        # CPython, so CIFuzz should be run only for code that is likely to be
        # merged into the main branch; compatibility with older branches may
        # be broken.
        FUZZ_RELEVANT_FILES='(\.c$|\.h$|\.cpp$|^configure$|^\.github/workflows/build\.yml$|^Modules/_xxtestfuzz)'
        if [ "$GITHUB_BASE_REF" = "main" ] && [ "$(git diff --name-only "origin/$GITHUB_BASE_REF.." | grep -qE $FUZZ_RELEVANT_FILES; echo $?)" -eq 0 ]; then
          # The tests are pretty slow so they are executed only for PRs
          # changing relevant files.
          echo "Run CIFuzz tests"
          echo "run-cifuzz=true" >> "$GITHUB_OUTPUT"
        else
          echo "Branch too old for CIFuzz tests; or no C files were changed"
          echo "run-cifuzz=false" >> "$GITHUB_OUTPUT"
        fi
    - name: Compute hash for config cache key
      id: config-hash
      run: |
        echo "hash=${{ hashFiles('configure', 'configure.ac', '.github/workflows/build.yml') }}" >> "$GITHUB_OUTPUT"
    - name: Get a list of the changed documentation-related files
      if: github.event_name == 'pull_request'
      id: changed-docs-files
      uses: Ana06/get-changed-files@v2.3.0
      with:
        filter: |
          Doc/**
          Misc/**
          .github/workflows/reusable-docs.yml
        format: csv  # works for paths with spaces
    - name: Check for docs changes
      # We only want to run this on PRs when related files are changed,
      # or when user triggers manual workflow run.
      if: >-
        (
          github.event_name == 'pull_request'
          && steps.changed-docs-files.outputs.added_modified_renamed != ''
        ) || github.event_name == 'workflow_dispatch'
      id: docs-changes
      run: |
        echo "run-docs=true" >> "${GITHUB_OUTPUT}"
    - name: Get a list of the MSI installer-related files
      if: github.event_name == 'pull_request'
      id: changed-win-msi-files
      uses: Ana06/get-changed-files@v2.3.0
      with:
        filter: |
          Tools/msi/**
          .github/workflows/reusable-windows-msi.yml
        format: csv  # works for paths with spaces
    - name: Check for changes in MSI installer-related files
      # We only want to run this on PRs when related files are changed,
      # or when user triggers manual workflow run.
      if: >-
        (
          github.event_name == 'pull_request'
          && steps.changed-win-msi-files.outputs.added_modified_renamed != ''
        ) || github.event_name == 'workflow_dispatch'
      id: win-msi-changes
      run: |
        echo "run-win-msi=true" >> "${GITHUB_OUTPUT}"