Skip to content

Commit 3764620

Browse files
Merge branch 'PowerShell:master' into master
2 parents f436f36 + 000c116 commit 3764620

File tree

358 files changed

+10947
-3384
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

358 files changed

+10947
-3384
lines changed

.config/suppress.json

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
{
2-
"tool": "Credential Scanner",
3-
"suppressions": [
4-
{
5-
"file": "\\test\\tools\\Modules\\WebListener\\ClientCert.pfx",
6-
"_justification": "Test certificate with private key"
7-
},
8-
{
9-
"file": "\\test\\tools\\Modules\\WebListener\\ServerCert.pfx",
10-
"_justification": "Test certificate with private key"
11-
},
12-
{
13-
"file": "\\test\\powershell\\Modules\\Microsoft.PowerShell.Security\\certificateCommon.psm1",
14-
"_justification": "Test certificate with private key and inline suppression isn't working"
15-
}
16-
]
2+
"tool": "Credential Scanner",
3+
"suppressions": [
4+
{
5+
"file": "\\test\\tools\\Modules\\WebListener\\ClientCert.pfx",
6+
"_justification": "Test certificate with private key"
7+
},
8+
{
9+
"file": "\\test\\tools\\Modules\\WebListener\\ServerCert.pfx",
10+
"_justification": "Test certificate with private key"
11+
},
12+
{
13+
"file": "\\test\\powershell\\Modules\\Microsoft.PowerShell.Security\\certificateCommon.psm1",
14+
"_justification": "Test certificate with private key and inline suppression isn't working"
15+
}
16+
]
1717
}

.github/actions/build/ci/action.yml

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ runs:
55
steps:
66
- name: Capture Environment
77
if: success() || failure()
8-
run: 'Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose'
8+
run: |-
9+
Import-Module .\tools\ci.psm1
10+
Show-Environment
911
shell: pwsh
1012
- name: Set Build Name for Non-PR
1113
if: github.event_name != 'PullRequest'
@@ -31,22 +33,8 @@ runs:
3133
Import-Module .\tools\ci.psm1
3234
Invoke-CIBuild
3335
shell: pwsh
34-
- name: xUnit Tests
35-
if: success()
36-
continue-on-error: true
37-
run: |-
38-
Write-Verbose -Verbose "Running xUnit tests..."
39-
Import-Module .\tools\ci.psm1
40-
Restore-PSOptions
41-
Invoke-CIxUnit -SkipFailing
42-
shell: pwsh
4336
- name: Upload build artifact
4437
uses: actions/upload-artifact@v4
4538
with:
4639
name: build
4740
path: ${{ runner.workspace }}/build
48-
- name: Upload xunit artifact
49-
uses: actions/upload-artifact@v4
50-
with:
51-
name: testResults-xunit
52-
path: ${{ runner.workspace }}/xunit
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Get Changed Files Action
2+
3+
A reusable composite action that retrieves the list of files changed in a pull request or push event.
4+
5+
## Features
6+
7+
- Supports both `pull_request` and `push` events
8+
- Optional filtering by file pattern
9+
- Returns files as JSON array for easy consumption
10+
- Filters out deleted files (only returns added, modified, or renamed files)
11+
- Handles up to 100 changed files per request
12+
13+
## Usage
14+
15+
### Basic Usage (Pull Requests Only)
16+
17+
```yaml
18+
- name: Get changed files
19+
id: changed-files
20+
uses: "./.github/actions/infrastructure/get-changed-files"
21+
22+
- name: Process files
23+
run: |
24+
echo "Changed files: ${{ steps.changed-files.outputs.files }}"
25+
echo "Count: ${{ steps.changed-files.outputs.count }}"
26+
```
27+
28+
### With Filtering
29+
30+
```yaml
31+
# Get only markdown files
32+
- name: Get changed markdown files
33+
id: changed-md
34+
uses: "./.github/actions/infrastructure/get-changed-files"
35+
with:
36+
filter: '*.md'
37+
38+
# Get only GitHub workflow/action files
39+
- name: Get changed GitHub files
40+
id: changed-github
41+
uses: "./.github/actions/infrastructure/get-changed-files"
42+
with:
43+
filter: '.github/'
44+
```
45+
46+
### Support Both PR and Push Events
47+
48+
```yaml
49+
- name: Get changed files
50+
id: changed-files
51+
uses: "./.github/actions/infrastructure/get-changed-files"
52+
with:
53+
event-types: 'pull_request,push'
54+
```
55+
56+
## Inputs
57+
58+
| Name | Description | Required | Default |
59+
|------|-------------|----------|---------|
60+
| `filter` | Optional filter pattern (e.g., `*.md` for markdown files, `.github/` for GitHub files) | No | `''` |
61+
| `event-types` | Comma-separated list of event types to support (`pull_request`, `push`) | No | `pull_request` |
62+
63+
## Outputs
64+
65+
| Name | Description |
66+
|------|-------------|
67+
| `files` | JSON array of changed file paths |
68+
| `count` | Number of changed files |
69+
70+
## Filter Patterns
71+
72+
The action supports simple filter patterns:
73+
74+
- **Extension matching**: Use `*.ext` to match files with a specific extension
75+
- Example: `*.md` matches all markdown files
76+
- Example: `*.yml` matches all YAML files
77+
78+
- **Path prefix matching**: Use a path prefix to match files in a directory
79+
- Example: `.github/` matches all files in the `.github` directory
80+
- Example: `tools/` matches all files in the `tools` directory
81+
82+
## Example: Processing Changed Files
83+
84+
```yaml
85+
- name: Get changed files
86+
id: changed-files
87+
uses: "./.github/actions/infrastructure/get-changed-files"
88+
89+
- name: Process each file
90+
shell: pwsh
91+
env:
92+
CHANGED_FILES: ${{ steps.changed-files.outputs.files }}
93+
run: |
94+
$changedFilesJson = $env:CHANGED_FILES
95+
$changedFiles = $changedFilesJson | ConvertFrom-Json
96+
97+
foreach ($file in $changedFiles) {
98+
Write-Host "Processing: $file"
99+
# Your processing logic here
100+
}
101+
```
102+
103+
## Limitations
104+
105+
- Simple filter patterns only (no complex glob or regex patterns)
106+
107+
## Pagination
108+
109+
The action automatically handles pagination to fetch **all** changed files in a PR, regardless of how many files were changed:
110+
111+
- Fetches files in batches of 100 per page
112+
- Continues fetching until all files are retrieved
113+
- Logs a note when pagination occurs, showing the total file count
114+
- **No file limit** - all changed files will be processed, even in very large PRs
115+
116+
This ensures that critical workflows (such as merge conflict checking, link validation, etc.) don't miss files due to pagination limits.
117+
118+
## Related Actions
119+
120+
- **markdownlinks**: Uses this pattern to get changed markdown files
121+
- **merge-conflict-checker**: Uses this pattern to get changed files for conflict detection
122+
- **path-filters**: Similar functionality but with more complex filtering logic
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: 'Get Changed Files'
2+
description: 'Gets the list of files changed in a pull request or push event'
3+
inputs:
4+
filter:
5+
description: 'Optional filter pattern (e.g., "*.md" for markdown files, ".github/" for GitHub files)'
6+
required: false
7+
default: ''
8+
event-types:
9+
description: 'Comma-separated list of event types to support (pull_request, push)'
10+
required: false
11+
default: 'pull_request'
12+
outputs:
13+
files:
14+
description: 'JSON array of changed file paths'
15+
value: ${{ steps.get-files.outputs.files }}
16+
count:
17+
description: 'Number of changed files'
18+
value: ${{ steps.get-files.outputs.count }}
19+
runs:
20+
using: 'composite'
21+
steps:
22+
- name: Get changed files
23+
id: get-files
24+
uses: actions/github-script@v7
25+
with:
26+
script: |
27+
const eventTypes = '${{ inputs.event-types }}'.split(',').map(t => t.trim());
28+
const filter = '${{ inputs.filter }}';
29+
let changedFiles = [];
30+
31+
if (eventTypes.includes('pull_request') && context.eventName === 'pull_request') {
32+
console.log(`Getting files changed in PR #${context.payload.pull_request.number}`);
33+
34+
// Fetch all files changed in the PR with pagination
35+
let allFiles = [];
36+
let page = 1;
37+
let fetchedCount;
38+
39+
do {
40+
const { data: files } = await github.rest.pulls.listFiles({
41+
owner: context.repo.owner,
42+
repo: context.repo.repo,
43+
pull_number: context.payload.pull_request.number,
44+
per_page: 100,
45+
page: page
46+
});
47+
48+
allFiles = allFiles.concat(files);
49+
fetchedCount = files.length;
50+
page++;
51+
} while (fetchedCount === 100);
52+
53+
if (allFiles.length >= 100) {
54+
console.log(`Note: This PR has ${allFiles.length} changed files. All files fetched using pagination.`);
55+
}
56+
57+
changedFiles = allFiles
58+
.filter(file => file.status === 'added' || file.status === 'modified' || file.status === 'renamed')
59+
.map(file => file.filename);
60+
61+
} else if (eventTypes.includes('push') && context.eventName === 'push') {
62+
console.log(`Getting files changed in push to ${context.ref}`);
63+
64+
const { data: comparison } = await github.rest.repos.compareCommits({
65+
owner: context.repo.owner,
66+
repo: context.repo.repo,
67+
base: context.payload.before,
68+
head: context.payload.after,
69+
});
70+
71+
changedFiles = comparison.files
72+
.filter(file => file.status === 'added' || file.status === 'modified' || file.status === 'renamed')
73+
.map(file => file.filename);
74+
75+
} else {
76+
core.setFailed(`Unsupported event type: ${context.eventName}. Supported types: ${eventTypes.join(', ')}`);
77+
return;
78+
}
79+
80+
// Apply filter if provided
81+
if (filter) {
82+
const filterLower = filter.toLowerCase();
83+
const beforeFilter = changedFiles.length;
84+
changedFiles = changedFiles.filter(file => {
85+
const fileLower = file.toLowerCase();
86+
// Support simple patterns like "*.md" or ".github/"
87+
if (filterLower.startsWith('*.')) {
88+
const ext = filterLower.substring(1);
89+
return fileLower.endsWith(ext);
90+
} else {
91+
return fileLower.startsWith(filterLower);
92+
}
93+
});
94+
console.log(`Filter '${filter}' applied: ${beforeFilter} → ${changedFiles.length} files`);
95+
}
96+
97+
// Calculate simple hash for verification
98+
const crypto = require('crypto');
99+
const filesJson = JSON.stringify(changedFiles.sort());
100+
const hash = crypto.createHash('sha256').update(filesJson).digest('hex').substring(0, 8);
101+
102+
// Log changed files in a collapsible group
103+
core.startGroup(`Changed Files (${changedFiles.length} total, hash: ${hash})`);
104+
if (changedFiles.length > 0) {
105+
changedFiles.forEach(file => console.log(` - ${file}`));
106+
} else {
107+
console.log(' (no files changed)');
108+
}
109+
core.endGroup();
110+
111+
console.log(`Found ${changedFiles.length} changed files`);
112+
core.setOutput('files', JSON.stringify(changedFiles));
113+
core.setOutput('count', changedFiles.length);
114+
115+
branding:
116+
icon: 'file-text'
117+
color: 'blue'

0 commit comments

Comments
 (0)