workflows/pr: refactor base/head branch decision making
Some jobs purposefully only run on certain base or head branches. By centralizing the logic, parts of it can easily be re-used later. Also, this gives them an explicit name and thus makes them easier to understand.
This commit is contained in:
		
							parent
							
								
									caf4ced100
								
							
						
					
					
						commit
						7763be5a80
					
				
							
								
								
									
										7
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @ -3,6 +3,9 @@ name: Build | ||||
| on: | ||||
|   workflow_call: | ||||
|     inputs: | ||||
|       baseBranch: | ||||
|         required: true | ||||
|         type: string | ||||
|       mergedSha: | ||||
|         required: true | ||||
|         type: string | ||||
| @ -63,7 +66,7 @@ jobs: | ||||
|       - name: Build NixOS manual | ||||
|         if: | | ||||
|           contains(matrix.builds, 'manual-nixos') && !cancelled() && | ||||
|           (github.base_ref == 'master' || startsWith(github.base_ref, 'release-')) | ||||
|           contains(fromJSON(inputs.baseBranch).type, 'primary') | ||||
|         run: nix-build untrusted/ci -A manual-nixos --argstr system ${{ matrix.system }} --out-link nixos-manual | ||||
| 
 | ||||
|       - name: Build Nixpkgs manual | ||||
| @ -81,7 +84,7 @@ jobs: | ||||
|       - name: Upload NixOS manual | ||||
|         if: | | ||||
|           contains(matrix.builds, 'manual-nixos') && !cancelled() && | ||||
|           (github.base_ref == 'master' || startsWith(github.base_ref, 'release-')) | ||||
|           contains(fromJSON(inputs.baseBranch).type, 'primary') | ||||
|         uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||||
|         with: | ||||
|           name: nixos-manual-${{ matrix.system }} | ||||
|  | ||||
							
								
								
									
										11
									
								
								.github/workflows/check.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/check.yml
									
									
									
									
										vendored
									
									
								
							| @ -2,6 +2,10 @@ name: Check | ||||
| 
 | ||||
| on: | ||||
|   workflow_call: | ||||
|     inputs: | ||||
|       baseBranch: | ||||
|         required: true | ||||
|         type: string | ||||
| 
 | ||||
| permissions: {} | ||||
| 
 | ||||
| @ -12,9 +16,7 @@ defaults: | ||||
| jobs: | ||||
|   no-channel-base: | ||||
|     name: no channel base | ||||
|     if: | | ||||
|       startsWith(github.base_ref, 'nixos-') || | ||||
|       startsWith(github.base_ref, 'nixpkgs-') | ||||
|     if: contains(fromJSON(inputs.baseBranch).type, 'channel') | ||||
|     runs-on: ubuntu-24.04-arm | ||||
|     steps: | ||||
|       - run: | | ||||
| @ -29,8 +31,7 @@ jobs: | ||||
|   cherry-pick: | ||||
|     if: | | ||||
|       github.event_name == 'pull_request' || | ||||
|       startsWith(github.base_ref, 'release-') || | ||||
|       (startsWith(github.base_ref, 'staging-') && github.base_ref != 'staging-next') | ||||
|       fromJSON(inputs.baseBranch).stable | ||||
|     permissions: | ||||
|       pull-requests: write | ||||
|     runs-on: ubuntu-24.04-arm | ||||
|  | ||||
							
								
								
									
										25
									
								
								.github/workflows/labels.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								.github/workflows/labels.yml
									
									
									
									
										vendored
									
									
								
							| @ -9,6 +9,10 @@ on: | ||||
|   schedule: | ||||
|     - cron: '07,17,27,37,47,57 * * * *' | ||||
|   workflow_call: | ||||
|     inputs: | ||||
|       headBranch: | ||||
|         required: true | ||||
|         type: string | ||||
|   workflow_dispatch: | ||||
|     inputs: | ||||
|       updatedWithin: | ||||
| @ -252,12 +256,7 @@ jobs: | ||||
|         name: Labels from touched files | ||||
|         if: | | ||||
|           github.event_name == 'pull_request_target' && | ||||
|           (github.event.pull_request.head.repo.owner.login != 'NixOS' || !( | ||||
|             github.head_ref == 'haskell-updates' || | ||||
|             github.head_ref == 'python-updates' || | ||||
|             github.head_ref == 'staging-next' || | ||||
|             startsWith(github.head_ref, 'staging-next-') | ||||
|           )) | ||||
|           !contains(fromJSON(inputs.headBranch).type, 'development') | ||||
|         with: | ||||
|           repo-token: ${{ secrets.GITHUB_TOKEN }} | ||||
|           configuration-path: .github/labeler.yml # default | ||||
| @ -267,12 +266,7 @@ jobs: | ||||
|         name: Labels from touched files (no sync) | ||||
|         if: | | ||||
|           github.event_name == 'pull_request_target' && | ||||
|           (github.event.pull_request.head.repo.owner.login != 'NixOS' || !( | ||||
|             github.head_ref == 'haskell-updates' || | ||||
|             github.head_ref == 'python-updates' || | ||||
|             github.head_ref == 'staging-next' || | ||||
|             startsWith(github.head_ref, 'staging-next-') | ||||
|           )) | ||||
|           !contains(fromJSON(inputs.headBranch).type, 'development') | ||||
|         with: | ||||
|           repo-token: ${{ secrets.GITHUB_TOKEN }} | ||||
|           configuration-path: .github/labeler-no-sync.yml | ||||
| @ -285,12 +279,7 @@ jobs: | ||||
|         # the backport labels. | ||||
|         if: | | ||||
|           github.event_name == 'pull_request_target' && | ||||
|           (github.event.pull_request.head.repo.owner.login == 'NixOS' && ( | ||||
|             github.head_ref == 'haskell-updates' || | ||||
|             github.head_ref == 'python-updates' || | ||||
|             github.head_ref == 'staging-next' || | ||||
|             startsWith(github.head_ref, 'staging-next-') | ||||
|           )) | ||||
|           contains(fromJSON(inputs.headBranch).type, 'development') | ||||
|         with: | ||||
|           repo-token: ${{ secrets.GITHUB_TOKEN }} | ||||
|           configuration-path: .github/labeler-development-branches.yml | ||||
|  | ||||
							
								
								
									
										31
									
								
								.github/workflows/pr.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								.github/workflows/pr.yml
									
									
									
									
										vendored
									
									
								
							| @ -21,6 +21,8 @@ jobs: | ||||
|   prepare: | ||||
|     runs-on: ubuntu-24.04-arm | ||||
|     outputs: | ||||
|       baseBranch: ${{ steps.branches.outputs.base }} | ||||
|       headBranch: ${{ steps.branches.outputs.head }} | ||||
|       mergedSha: ${{ steps.get-merge-commit.outputs.mergedSha }} | ||||
|       targetSha: ${{ steps.get-merge-commit.outputs.targetSha }} | ||||
|       systems: ${{ steps.systems.outputs.systems }} | ||||
| @ -29,6 +31,7 @@ jobs: | ||||
|         with: | ||||
|           sparse-checkout: | | ||||
|             .github/actions | ||||
|             ci/supportedBranches.js | ||||
|             ci/supportedSystems.json | ||||
|       - name: Check if the PR can be merged and get the test merge commit | ||||
|         uses: ./.github/actions/get-merge-commit | ||||
| @ -39,12 +42,35 @@ jobs: | ||||
|         run: | | ||||
|           echo "systems=$(jq -c <ci/supportedSystems.json)" >> "$GITHUB_OUTPUT" | ||||
| 
 | ||||
|       - name: Determine branch type | ||||
|         id: branches | ||||
|         uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 | ||||
|         with: | ||||
|           script: | | ||||
|             const { classify } = require('./ci/supportedBranches.js') | ||||
|             const { base, head } = context.payload.pull_request | ||||
| 
 | ||||
|             const baseClassification = classify(base.ref) | ||||
|             core.setOutput('base', baseClassification) | ||||
|             core.info('base classification:', baseClassification) | ||||
| 
 | ||||
|             const headClassification = | ||||
|               (base.repo.full_name == head.repo.full_name) ? | ||||
|               classify(head.ref) : | ||||
|               // PRs from forks are always considered WIP. | ||||
|               { type: ['wip'] } | ||||
|             core.setOutput('head', headClassification) | ||||
|             core.info('head classification:', headClassification) | ||||
| 
 | ||||
|   check: | ||||
|     name: Check | ||||
|     needs: [prepare] | ||||
|     uses: ./.github/workflows/check.yml | ||||
|     permissions: | ||||
|       # cherry-picks | ||||
|       pull-requests: write | ||||
|     with: | ||||
|       baseBranch: ${{ needs.prepare.outputs.baseBranch }} | ||||
| 
 | ||||
|   lint: | ||||
|     name: Lint | ||||
| @ -70,11 +96,13 @@ jobs: | ||||
| 
 | ||||
|   labels: | ||||
|     name: Labels | ||||
|     needs: [eval] | ||||
|     needs: [prepare, eval] | ||||
|     uses: ./.github/workflows/labels.yml | ||||
|     permissions: | ||||
|       issues: write | ||||
|       pull-requests: write | ||||
|     with: | ||||
|       headBranch: ${{ needs.prepare.outputs.headBranch }} | ||||
| 
 | ||||
|   reviewers: | ||||
|     name: Reviewers | ||||
| @ -91,6 +119,7 @@ jobs: | ||||
|     secrets: | ||||
|       CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} | ||||
|     with: | ||||
|       baseBranch: ${{ needs.prepare.outputs.baseBranch }} | ||||
|       mergedSha: ${{ needs.prepare.outputs.mergedSha }} | ||||
| 
 | ||||
|   # This job's only purpose is to serve as a target for the "Required Status Checks" branch ruleset. | ||||
|  | ||||
							
								
								
									
										4
									
								
								ci/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								ci/.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| # TODO: Move to top-level via staging PR | ||||
| [*.js] | ||||
| indent_style = space | ||||
| indent_size = 2 | ||||
							
								
								
									
										29
									
								
								ci/README.md
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								ci/README.md
									
									
									
									
									
								
							| @ -20,3 +20,32 @@ Arguments: | ||||
| 
 | ||||
| - `BASE_BRANCH`: The base branch to use, e.g. master or release-24.05 | ||||
| - `REPOSITORY`: The repository from which to fetch the base branch. Defaults to <https://github.com/NixOS/nixpkgs.git>. | ||||
| 
 | ||||
| # Branch classification | ||||
| 
 | ||||
| For the purposes of CI, branches in the NixOS/nixpkgs repository are classified as follows: | ||||
| 
 | ||||
| - **Channel** branches | ||||
|   - `nixos-` or `nixpkgs-` prefix | ||||
|   - Are only updated from `master` or `release-` branches, when hydra passes. | ||||
|   - Otherwise not worked on, Pull Requests are not allowed. | ||||
|   - Long-lived, no deletion, no force push. | ||||
| - **Primary development** branches | ||||
|   - `release-` prefix and `master` | ||||
|   - Pull Requests required. | ||||
|   - Long-lived, no deletion, no force push. | ||||
| - **Secondary development** branches | ||||
|   - `staging-` prefix, `haskell-updates` and `python-updates` | ||||
|   - Pull Requests normally required, except when merging development branches into each other. | ||||
|   - Long-lived, no deletion, no force push. | ||||
| - **Work-In-Progress** branches | ||||
|   - `backport-`, `revert-` and `wip-` prefixes. | ||||
|   - Deprecated: All other branches, not matched by channel/development. | ||||
|   - Pull Requests are optional. | ||||
|   - Short-lived, force push allowed, deleted after merge. | ||||
| 
 | ||||
| Some branches also have a version component, which is either `unstable` or `YY.MM`. | ||||
| 
 | ||||
| `ci/supportedBranches.js` is a script imported by CI to classify the base and head branches of a Pull Request. | ||||
| This classification will then be used to skip certain jobs. | ||||
| This script can also be run locally to print basic test cases. | ||||
|  | ||||
							
								
								
									
										62
									
								
								ci/supportedBranches.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										62
									
								
								ci/supportedBranches.js
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,62 @@ | ||||
| #!/usr/bin/env nix-shell
 | ||||
| /* | ||||
| #!nix-shell -i node -p nodejs | ||||
| */ | ||||
| 
 | ||||
| const typeConfig = { | ||||
|   master: ['development', 'primary'], | ||||
|   release: ['development', 'primary'], | ||||
|   staging: ['development', 'secondary'], | ||||
|   'staging-next': ['development', 'secondary'], | ||||
|   'haskell-updates': ['development', 'secondary'], | ||||
|   'python-updates': ['development', 'secondary'], | ||||
|   nixos: ['channel'], | ||||
|   nixpkgs: ['channel'], | ||||
| } | ||||
| 
 | ||||
| function split(branch) { | ||||
|   return { ...branch.match(/(?<prefix>.+?)(-(?<version>\d{2}\.\d{2}|unstable)(?:-(?<suffix>.*))?)?$/).groups } | ||||
| } | ||||
| 
 | ||||
| function classify(branch) { | ||||
|   const { prefix, version } = split(branch) | ||||
|   return { | ||||
|     stable: (version ?? 'unstable') !== 'unstable', | ||||
|     type: typeConfig[prefix] ?? [ 'wip' ] | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| module.exports = { classify } | ||||
| 
 | ||||
| // If called directly via CLI, runs the following tests:
 | ||||
| if (!module.parent) { | ||||
|   console.log('split(branch)') | ||||
|   function testSplit(branch) { | ||||
|     console.log(branch, split(branch)) | ||||
|   } | ||||
|   testSplit('master') | ||||
|   testSplit('release-25.05') | ||||
|   testSplit('staging-next') | ||||
|   testSplit('staging-25.05') | ||||
|   testSplit('staging-next-25.05') | ||||
|   testSplit('nixpkgs-25.05-darwin') | ||||
|   testSplit('nixpkgs-unstable') | ||||
|   testSplit('haskell-updates') | ||||
|   testSplit('backport-123-to-release-25.05') | ||||
| 
 | ||||
|   console.log('') | ||||
| 
 | ||||
|   console.log('classify(branch)') | ||||
|   function testClassify(branch) { | ||||
|     console.log(branch, classify(branch)) | ||||
|   } | ||||
|   testClassify('master') | ||||
|   testClassify('release-25.05') | ||||
|   testClassify('staging-next') | ||||
|   testClassify('staging-25.05') | ||||
|   testClassify('staging-next-25.05') | ||||
|   testClassify('nixpkgs-25.05-darwin') | ||||
|   testClassify('nixpkgs-unstable') | ||||
|   testClassify('haskell-updates') | ||||
|   testClassify('backport-123-to-release-25.05') | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Wolfgang Walther
						Wolfgang Walther