Skip to content

Commit 6113794

Browse files
CopilotpwshBot
authored andcommitted
Cherry-pick PR PowerShell#26268 with conflicts for manual resolution
1 parent fa7640b commit 6113794

File tree

6 files changed

+1022
-84
lines changed

6 files changed

+1022
-84
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
---
2+
applyTo:
3+
- "build.psm1"
4+
- "tools/ci.psm1"
5+
- ".github/**/*.yml"
6+
- ".github/**/*.yaml"
7+
---
8+
9+
# Build Configuration Guide
10+
11+
## Choosing the Right Configuration
12+
13+
### For Testing
14+
15+
**Use: Default (Debug)**
16+
17+
```yaml
18+
- name: Build for Testing
19+
shell: pwsh
20+
run: |
21+
Import-Module ./tools/ci.psm1
22+
Start-PSBuild
23+
```
24+
25+
**Why Debug:**
26+
- Includes debugging symbols
27+
- Better error messages
28+
- Faster build times
29+
- Suitable for xUnit and Pester tests
30+
31+
**Do NOT use:**
32+
- `-Configuration 'Release'` (unnecessary for tests)
33+
- `-ReleaseTag` (not needed for tests)
34+
- `-CI` (unless you specifically need Pester module)
35+
36+
### For Release/Packaging
37+
38+
**Use: Release with version tag and public NuGet feeds**
39+
40+
```yaml
41+
- name: Build for Release
42+
shell: pwsh
43+
run: |
44+
Import-Module ./build.psm1
45+
Import-Module ./tools/ci.psm1
46+
Switch-PSNugetConfig -Source Public
47+
$releaseTag = Get-ReleaseTag
48+
Start-PSBuild -Configuration 'Release' -ReleaseTag $releaseTag
49+
```
50+
51+
**Why Release:**
52+
- Optimized binaries
53+
- No debug symbols (smaller size)
54+
- Production-ready
55+
56+
**Why Switch-PSNugetConfig -Source Public:**
57+
- Switches NuGet package sources to public feeds (nuget.org and public Azure DevOps feeds)
58+
- Required for CI/CD environments that don't have access to private feeds
59+
- Uses publicly available packages instead of Microsoft internal feeds
60+
61+
### For Code Coverage
62+
63+
**Use: CodeCoverage configuration**
64+
65+
```yaml
66+
- name: Build with Coverage
67+
shell: pwsh
68+
run: |
69+
Import-Module ./tools/ci.psm1
70+
Start-PSBuild -Configuration 'CodeCoverage'
71+
```
72+
73+
## Platform Considerations
74+
75+
### All Platforms
76+
77+
Same commands work across Linux, Windows, and macOS:
78+
79+
```yaml
80+
strategy:
81+
matrix:
82+
os: [ubuntu-latest, windows-latest, macos-latest]
83+
runs-on: ${{ matrix.os }}
84+
steps:
85+
- name: Build PowerShell
86+
shell: pwsh
87+
run: |
88+
Import-Module ./tools/ci.psm1
89+
Start-PSBuild
90+
```
91+
92+
### Output Locations
93+
94+
**Linux/macOS:**
95+
```
96+
src/powershell-unix/bin/Debug/<netversion>/<runtime>/publish/
97+
```
98+
99+
**Windows:**
100+
```
101+
src/powershell-win-core/bin/Debug/<netversion>/<runtime>/publish/
102+
```
103+
104+
## Best Practices
105+
106+
1. Use default configuration for testing
107+
2. Avoid redundant parameters
108+
3. Match configuration to purpose
109+
4. Use `-CI` only when needed
110+
5. Always specify `-ReleaseTag` for release or packaging builds
111+
6. Use `Switch-PSNugetConfig -Source Public` in CI/CD for release builds
112+
113+
## NuGet Feed Configuration
114+
115+
### Switch-PSNugetConfig
116+
117+
The `Switch-PSNugetConfig` function in `build.psm1` manages NuGet package source configuration.
118+
119+
**Available Sources:**
120+
121+
- **Public**: Uses public feeds (nuget.org and public Azure DevOps feeds)
122+
- Required for: CI/CD environments, public builds, packaging
123+
- Does not require authentication
124+
125+
- **Private**: Uses internal PowerShell team feeds
126+
- Required for: Internal development with preview packages
127+
- Requires authentication credentials
128+
129+
- **NuGetOnly**: Uses only nuget.org
130+
- Required for: Minimal dependency scenarios
131+
132+
**Usage:**
133+
134+
```powershell
135+
# Switch to public feeds (most common for CI/CD)
136+
Switch-PSNugetConfig -Source Public
137+
138+
# Switch to private feeds with authentication
139+
Switch-PSNugetConfig -Source Private -UserName $userName -ClearTextPAT $pat
140+
141+
# Switch to nuget.org only
142+
Switch-PSNugetConfig -Source NuGetOnly
143+
```
144+
145+
**When to Use:**
146+
147+
- **Always use `-Source Public`** before building in CI/CD workflows
148+
- Use before any build that will create packages for distribution
149+
- Use in forks or environments without access to Microsoft internal feeds
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
---
2+
applyTo:
3+
- "**/*.ps1"
4+
- "**/*.psm1"
5+
---
6+
7+
# Using Start-NativeExecution for Native Command Execution
8+
9+
## Purpose
10+
11+
`Start-NativeExecution` is the standard function for executing native commands (external executables) in PowerShell scripts within this repository. It provides consistent error handling and better diagnostics when native commands fail.
12+
13+
## When to Use
14+
15+
Use `Start-NativeExecution` whenever you need to:
16+
- Execute external commands (e.g., `git`, `dotnet`, `pkgbuild`, `productbuild`, `fpm`, `rpmbuild`)
17+
- Ensure proper exit code checking
18+
- Get better error messages with caller information
19+
- Handle verbose output on error
20+
21+
## Basic Usage
22+
23+
```powershell
24+
Start-NativeExecution {
25+
git clone https://github.com/PowerShell/PowerShell.git
26+
}
27+
```
28+
29+
## With Parameters
30+
31+
Use backticks for line continuation within the script block:
32+
33+
```powershell
34+
Start-NativeExecution {
35+
pkgbuild --root $pkgRoot `
36+
--identifier $pkgIdentifier `
37+
--version $Version `
38+
--scripts $scriptsDir `
39+
$outputPath
40+
}
41+
```
42+
43+
## Common Parameters
44+
45+
### -VerboseOutputOnError
46+
47+
Captures command output and displays it only if the command fails:
48+
49+
```powershell
50+
Start-NativeExecution -VerboseOutputOnError {
51+
dotnet build --configuration Release
52+
}
53+
```
54+
55+
### -IgnoreExitcode
56+
57+
Allows the command to fail without throwing an exception:
58+
59+
```powershell
60+
Start-NativeExecution -IgnoreExitcode {
61+
git diff --exit-code # Returns 1 if differences exist
62+
}
63+
```
64+
65+
## Availability
66+
67+
The function is defined in `tools/buildCommon/startNativeExecution.ps1` and is available in:
68+
- `build.psm1` (dot-sourced automatically)
69+
- `tools/packaging/packaging.psm1` (dot-sourced automatically)
70+
- Test modules that include `HelpersCommon.psm1`
71+
72+
To use in other scripts, dot-source the function:
73+
74+
```powershell
75+
. "$PSScriptRoot/../buildCommon/startNativeExecution.ps1"
76+
```
77+
78+
## Error Handling
79+
80+
When a native command fails (non-zero exit code), `Start-NativeExecution`:
81+
1. Captures the exit code
82+
2. Identifies the calling location (file and line number)
83+
3. Throws a descriptive error with full context
84+
85+
Example error message:
86+
```
87+
Execution of {git clone ...} by /path/to/script.ps1: line 42 failed with exit code 1
88+
```
89+
90+
## Examples from the Codebase
91+
92+
### Git Operations
93+
```powershell
94+
Start-NativeExecution {
95+
git fetch --tags --quiet upstream
96+
}
97+
```
98+
99+
### Build Operations
100+
```powershell
101+
Start-NativeExecution -VerboseOutputOnError {
102+
dotnet publish --configuration Release
103+
}
104+
```
105+
106+
### Packaging Operations
107+
```powershell
108+
Start-NativeExecution -VerboseOutputOnError {
109+
pkgbuild --root $pkgRoot --identifier $pkgId --version $version $outputPath
110+
}
111+
```
112+
113+
### Permission Changes
114+
```powershell
115+
Start-NativeExecution {
116+
find $staging -type d | xargs chmod 755
117+
find $staging -type f | xargs chmod 644
118+
}
119+
```
120+
121+
## Anti-Patterns
122+
123+
**Don't do this:**
124+
```powershell
125+
& somecommand $args
126+
if ($LASTEXITCODE -ne 0) {
127+
throw "Command failed"
128+
}
129+
```
130+
131+
**Do this instead:**
132+
```powershell
133+
Start-NativeExecution {
134+
somecommand $args
135+
}
136+
```
137+
138+
## Best Practices
139+
140+
1. **Always use Start-NativeExecution** for native commands to ensure consistent error handling
141+
2. **Use -VerboseOutputOnError** for commands with useful diagnostic output
142+
3. **Use backticks for readability** when commands have multiple arguments
143+
4. **Don't capture output unnecessarily** - let the function handle it
144+
5. **Use -IgnoreExitcode sparingly** - only when non-zero exit codes are expected and acceptable
145+
146+
## Related Documentation
147+
148+
- Source: `tools/buildCommon/startNativeExecution.ps1`
149+
- Blog post: https://mnaoumov.wordpress.com/2015/01/11/execution-of-external-commands-in-powershell-done-right/

.github/workflows/macos-ci.yml

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,21 +156,76 @@ jobs:
156156
- name: Verify xUnit test results
157157
uses: "./.github/actions/test/verify_xunit"
158158
PackageMac-macos_packaging:
159-
name: macOS packaging (bootstrap only)
159+
name: macOS packaging and testing
160160
needs:
161161
- changes
162162
if: ${{ needs.changes.outputs.source == 'true' }}
163163
runs-on:
164164
- macos-latest
165165
steps:
166166
- name: checkout
167+
<<<<<<< HEAD
167168
uses: actions/[email protected]
169+
=======
170+
uses: actions/checkout@v5
171+
with:
172+
fetch-depth: 1000
173+
- uses: actions/setup-dotnet@v4
174+
with:
175+
global-json-file: ./global.json
176+
>>>>>>> 47e8e900a (Replace fpm with native macOS packaging tools (pkgbuild/productbuild) (#26268))
168177
- name: Bootstrap packaging
169-
if: success() || failure()
178+
if: success()
170179
run: |-
171180
import-module ./build.psm1
172181
start-psbootstrap -Scenario package
173182
shell: pwsh
183+
- name: Build PowerShell and Create macOS package
184+
if: success()
185+
run: |-
186+
import-module ./build.psm1
187+
import-module ./tools/ci.psm1
188+
import-module ./tools/packaging/packaging.psm1
189+
Switch-PSNugetConfig -Source Public
190+
Sync-PSTags -AddRemoteIfMissing
191+
$releaseTag = Get-ReleaseTag
192+
Start-PSBuild -Configuration Release -PSModuleRestore -ReleaseTag $releaseTag
193+
$macOSRuntime = if ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq 'Arm64') { 'osx-arm64' } else { 'osx-x64' }
194+
Start-PSPackage -Type osxpkg -ReleaseTag $releaseTag -MacOSRuntime $macOSRuntime -SkipReleaseChecks
195+
shell: pwsh
196+
- name: Test package contents
197+
if: success()
198+
run: |-
199+
$env:PACKAGE_FOLDER = Get-Location
200+
$testResultsPath = Join-Path $env:RUNNER_WORKSPACE "testResults"
201+
if (-not (Test-Path $testResultsPath)) {
202+
New-Item -ItemType Directory -Path $testResultsPath -Force | Out-Null
203+
}
204+
Import-Module Pester
205+
$pesterConfig = New-PesterConfiguration
206+
$pesterConfig.Run.Path = './tools/packaging/releaseTests/macOSPackage.tests.ps1'
207+
$pesterConfig.Run.PassThru = $true
208+
$pesterConfig.Output.Verbosity = 'Detailed'
209+
$pesterConfig.TestResult.Enabled = $true
210+
$pesterConfig.TestResult.OutputFormat = 'NUnitXml'
211+
$pesterConfig.TestResult.OutputPath = Join-Path $testResultsPath "macOSPackage.xml"
212+
$result = Invoke-Pester -Configuration $pesterConfig
213+
if ($result.FailedCount -gt 0) {
214+
throw "Package validation failed with $($result.FailedCount) failed test(s)"
215+
}
216+
shell: pwsh
217+
- name: Publish and Upload Pester Test Results
218+
if: always()
219+
uses: "./.github/actions/test/process-pester-results"
220+
with:
221+
name: "macOSPackage"
222+
testResultsFolder: "${{ runner.workspace }}/testResults"
223+
- name: Upload package artifact
224+
if: always()
225+
uses: actions/upload-artifact@v4
226+
with:
227+
name: macos-package
228+
path: "*.pkg"
174229
ready_to_merge:
175230
name: macos ready to merge
176231
needs:

0 commit comments

Comments
 (0)