Skip to content

Commit 10975b6

Browse files
merge: resolve conflict with main (arrow style in dependency.py docstring)
Main branch (PR #227, Windows native support) standardized docstring arrows from '→' to '->' across the codebase. Our branch had added a local path line using '→'. Resolution: adopt main's '->' style for all arrows and keep our local path documentation line. The only conflict was in to_canonical() docstring in dependency.py. All other files auto-merged cleanly. Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
1 parent 8a269eb commit 10975b6

85 files changed

Lines changed: 3959 additions & 926 deletions

File tree

Some content is hidden

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

.github/instructions/cicd.instructions.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ description: "CI/CD Pipeline configuration for PyInstaller binary packaging and
99
Three workflows split by trigger and secret requirements:
1010

1111
1. **`ci.yml`**`pull_request` trigger (all PRs, including forks)
12-
- **Linux-only** (ubuntu-24.04). Unit tests + single binary build. No secrets needed. Fast PR feedback (~3 min).
12+
- **Linux + Windows** (ubuntu-24.04, windows-latest). Unit tests in parallel on both platforms + single Linux binary build. No secrets needed.
13+
- Windows job catches path separator, encoding, and platform-specific issues before merge.
1314
- Uploads Linux x86_64 binary artifact for downstream integration testing.
1415
2. **`ci-integration.yml`**`workflow_run` trigger (after CI completes, environment-gated)
1516
- **Linux-only**. Smoke tests, integration tests, release validation. Requires `integration-tests` environment approval.
@@ -21,9 +22,9 @@ Three workflows split by trigger and secret requirements:
2122
- macOS builds and cross-platform validation happen here, where queue time doesn't block PRs.
2223

2324
## Platform Testing Strategy
24-
- **PR time**: Linux-only for speed. Catches logic bugs, dependency issues, and binary packaging problems.
25-
- **Post-merge**: Full 4-platform matrix catches platform-specific issues immediately on main.
26-
- **Rationale**: PR-time Linux coverage gives fast feedback on logic, dependency, and packaging changes, while the post-merge full-matrix workflows quickly catch any remaining platform-specific issues.
25+
- **PR time**: Linux + Windows in parallel. Catches logic bugs, dependency issues, path separators, encoding, and Windows-specific problems before merge.
26+
- **Post-merge**: Full 5-platform matrix (linux x86_64/arm64, darwin x86_64/arm64, windows x86_64) catches remaining platform-specific issues on main.
27+
- **Rationale**: Linux + Windows PR coverage catches the two fundamentally different platform families (Unix vs Windows). macOS-specific issues are rare and caught post-merge.
2728

2829
## PyInstaller Binary Packaging
2930
- **CRITICAL**: Uses `--onedir` mode (NOT `--onefile`) for faster CLI startup performance

.github/workflows/build-release.yml

Lines changed: 174 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ jobs:
4848
- os: macos-latest
4949
arch: arm64
5050
platform: darwin
51+
- os: windows-latest
52+
arch: x86_64
53+
platform: windows
5154

5255
steps:
5356
- uses: actions/checkout@v4
@@ -70,17 +73,26 @@ jobs:
7073
# Wait for installation to complete
7174
until xcode-select -p >/dev/null 2>&1; do sleep 5; done
7275
73-
- name: Install uv
76+
- name: Install uv (Unix)
77+
if: matrix.platform != 'windows'
7478
run: |
7579
curl -LsSf https://astral.sh/uv/install.sh | sh
7680
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
81+
82+
- name: Install uv (Windows)
83+
if: matrix.platform == 'windows'
84+
shell: pwsh
85+
run: |
86+
irm https://astral.sh/uv/install.ps1 | iex
87+
echo "$env:USERPROFILE\.local\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
7788
7889
- name: Cache uv environments
7990
uses: actions/cache@v3
8091
with:
8192
path: |
8293
~/.cache/uv
8394
~/.local/share/uv
95+
~\AppData\Local\uv\cache
8496
key: ${{ runner.os }}-uv-${{ hashFiles('**/pyproject.toml') }}
8597
restore-keys: |
8698
${{ runner.os }}-uv-
@@ -120,6 +132,10 @@ jobs:
120132
platform: darwin
121133
arch: arm64
122134
binary_name: apm-darwin-arm64
135+
- os: windows-latest
136+
platform: windows
137+
arch: x86_64
138+
binary_name: apm-windows-x86_64
123139

124140
runs-on: ${{ matrix.os }}
125141
permissions:
@@ -149,19 +165,34 @@ jobs:
149165
until xcode-select -p >/dev/null 2>&1; do sleep 5; done
150166
brew install upx
151167
152-
- name: Install uv
168+
- name: Install uv (Unix)
169+
if: matrix.platform != 'windows'
153170
run: |
154171
curl -LsSf https://astral.sh/uv/install.sh | sh
155172
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
173+
174+
- name: Install uv (Windows)
175+
if: matrix.platform == 'windows'
176+
shell: pwsh
177+
run: |
178+
irm https://astral.sh/uv/install.ps1 | iex
179+
echo "$env:USERPROFILE\.local\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
156180
157181
- name: Install Python dependencies
158182
run: |
159183
uv sync --extra dev --extra build
160184
161-
- name: Build binary
185+
- name: Build binary (Unix)
186+
if: matrix.platform != 'windows'
162187
run: |
163188
chmod +x scripts/build-binary.sh
164189
uv run ./scripts/build-binary.sh
190+
191+
- name: Build binary (Windows)
192+
if: matrix.platform == 'windows'
193+
shell: pwsh
194+
run: |
195+
uv run pwsh scripts/windows/build-binary.ps1
165196
166197
- name: Upload binary as workflow artifact
167198
uses: actions/upload-artifact@v4
@@ -171,7 +202,11 @@ jobs:
171202
./dist/${{ matrix.binary_name }}
172203
./dist/${{ matrix.binary_name }}.sha256
173204
./scripts/test-release-validation.sh
205+
./scripts/windows/test-release-validation.ps1
206+
./scripts/test-dependency-integration.sh
207+
./scripts/windows/test-dependency-integration.ps1
174208
./scripts/github-token-helper.sh
209+
./scripts/windows/github-token-helper.ps1
175210
include-hidden-files: true # Required to include .apm directories
176211
retention-days: 30
177212
if-no-files-found: error
@@ -202,6 +237,10 @@ jobs:
202237
arch: arm64
203238
platform: darwin
204239
binary_name: apm-darwin-arm64
240+
- os: windows-latest
241+
arch: x86_64
242+
platform: windows
243+
binary_name: apm-windows-x86_64
205244

206245
runs-on: ${{ matrix.os }}
207246
permissions:
@@ -236,15 +275,24 @@ jobs:
236275
# Wait for installation to complete
237276
until xcode-select -p >/dev/null 2>&1; do sleep 5; done
238277
239-
- name: Install uv
278+
- name: Install uv (Unix)
279+
if: matrix.platform != 'windows'
240280
run: |
241281
curl -LsSf https://astral.sh/uv/install.sh | sh
242282
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
283+
284+
- name: Install uv (Windows)
285+
if: matrix.platform == 'windows'
286+
shell: pwsh
287+
run: |
288+
irm https://astral.sh/uv/install.ps1 | iex
289+
echo "$env:USERPROFILE\.local\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
243290
244291
- name: Install test dependencies
245292
run: uv sync --extra dev
246293

247-
- name: Run integration tests
294+
- name: Run integration tests (Unix)
295+
if: matrix.platform != 'windows'
248296
env:
249297
APM_E2E_TESTS: "1"
250298
GITHUB_TOKEN: ${{ secrets.GH_MODELS_PAT }} # Models access
@@ -255,6 +303,18 @@ jobs:
255303
uv run ./scripts/test-integration.sh
256304
timeout-minutes: 20
257305

306+
- name: Run integration tests (Windows)
307+
if: matrix.platform == 'windows'
308+
shell: pwsh
309+
env:
310+
APM_E2E_TESTS: "1"
311+
GITHUB_TOKEN: ${{ secrets.GH_MODELS_PAT }}
312+
GITHUB_APM_PAT: ${{ secrets.GH_CLI_PAT }}
313+
ADO_APM_PAT: ${{ secrets.ADO_APM_PAT }}
314+
run: |
315+
uv run pwsh scripts/windows/test-integration.ps1 -SkipBuild
316+
timeout-minutes: 20
317+
258318
# Release validation tests - Final pre-release validation of shipped binary
259319
release-validation:
260320
name: Release Validation
@@ -279,6 +339,10 @@ jobs:
279339
arch: arm64
280340
platform: darwin
281341
binary_name: apm-darwin-arm64
342+
- os: windows-latest
343+
arch: x86_64
344+
platform: windows
345+
binary_name: apm-windows-x86_64
282346

283347
runs-on: ${{ matrix.os }}
284348
permissions:
@@ -308,9 +372,10 @@ jobs:
308372
uses: actions/download-artifact@v4
309373
with:
310374
name: ${{ matrix.binary_name }}
311-
path: /tmp/apm-isolated-test/
375+
path: ${{ matrix.platform == 'windows' && 'D:\apm-isolated-test' || '/tmp/apm-isolated-test/' }}
312376

313-
- name: Make binary executable and verify isolation
377+
- name: Make binary executable and verify isolation (Unix)
378+
if: matrix.platform != 'windows'
314379
run: |
315380
cd /tmp/apm-isolated-test
316381
@@ -322,24 +387,54 @@ jobs:
322387
# Make the binary executable
323388
chmod +x ./dist/${{ matrix.binary_name }}/apm
324389
325-
- name: Create APM symlink for testing
390+
- name: Create APM symlink for testing (Unix)
391+
if: matrix.platform != 'windows'
326392
run: |
327393
cd /tmp/apm-isolated-test
328394
ln -s "$(pwd)/dist/${{ matrix.binary_name }}/apm" "$(pwd)/apm"
329395
echo "/tmp/apm-isolated-test" >> $GITHUB_PATH
396+
397+
- name: Verify binary and add to PATH (Windows)
398+
if: matrix.platform == 'windows'
399+
shell: pwsh
400+
run: |
401+
cd D:\apm-isolated-test
402+
403+
# Debug: List the downloaded structure
404+
Write-Host "Downloaded structure:"
405+
Get-ChildItem -Recurse -Filter "apm.exe"
406+
Get-ChildItem .\dist\
407+
408+
# Add binary directory to PATH
409+
$binDir = "D:\apm-isolated-test\dist\${{ matrix.binary_name }}"
410+
echo $binDir | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
330411
331-
- name: Run release validation tests
412+
- name: Run release validation tests (Unix)
413+
if: matrix.platform != 'windows'
332414
env:
333-
APM_E2E_TESTS: "1" # Avoids interactive prompts for MCP env values with apm install
415+
APM_E2E_TESTS: "1"
334416
GITHUB_TOKEN: ${{ secrets.GH_MODELS_PAT }}
335-
GITHUB_APM_PAT: ${{ secrets.GH_CLI_PAT }} # Primary: APM module access
336-
ADO_APM_PAT: ${{ secrets.ADO_APM_PAT }} # Azure DevOps module access
417+
GITHUB_APM_PAT: ${{ secrets.GH_CLI_PAT }}
418+
ADO_APM_PAT: ${{ secrets.ADO_APM_PAT }}
337419
run: |
338420
cd /tmp/apm-isolated-test
339421
chmod +x scripts/test-release-validation.sh
340422
./scripts/test-release-validation.sh
341423
timeout-minutes: 20
342424

425+
- name: Run release validation tests (Windows)
426+
if: matrix.platform == 'windows'
427+
shell: pwsh
428+
env:
429+
APM_E2E_TESTS: "1"
430+
GITHUB_TOKEN: ${{ secrets.GH_MODELS_PAT }}
431+
GITHUB_APM_PAT: ${{ secrets.GH_CLI_PAT }}
432+
ADO_APM_PAT: ${{ secrets.ADO_APM_PAT }}
433+
run: |
434+
cd D:\apm-isolated-test
435+
.\scripts\windows\test-release-validation.ps1
436+
timeout-minutes: 20
437+
343438

344439
create-release:
345440
name: Create GitHub Release
@@ -395,6 +490,32 @@ jobs:
395490
exit 1
396491
fi
397492
done
493+
494+
binary="apm-windows-x86_64"
495+
artifact_dir="${binary}"
496+
binary_dir="${artifact_dir}/dist/${binary}"
497+
if [ -d "$binary_dir" ] && [ -f "$binary_dir/apm.exe" ]; then
498+
echo "Processing $binary_dir directory..."
499+
(
500+
cd "${artifact_dir}/dist"
501+
zip -qr "../../${binary}.zip" "${binary}"
502+
)
503+
if command -v sha256sum &> /dev/null; then
504+
sha256sum "${binary}.zip" > "${binary}.zip.sha256"
505+
elif command -v shasum &> /dev/null; then
506+
shasum -a 256 "${binary}.zip" > "${binary}.zip.sha256"
507+
fi
508+
echo "Created ${binary}.zip"
509+
else
510+
echo "ERROR: Binary directory $binary_dir not found or $binary_dir/apm.exe missing"
511+
echo "Artifact directory contents:"
512+
ls -la "$artifact_dir/" || echo "Directory $artifact_dir does not exist"
513+
if [ -d "$artifact_dir/dist" ]; then
514+
echo "Dist directory contents:"
515+
ls -la "$artifact_dir/dist/"
516+
fi
517+
exit 1
518+
fi
398519
399520
- name: Determine release type
400521
id: release_type
@@ -430,6 +551,8 @@ jobs:
430551
./dist/apm-darwin-x86_64.tar.gz.sha256
431552
./dist/apm-darwin-arm64.tar.gz
432553
./dist/apm-darwin-arm64.tar.gz.sha256
554+
./dist/apm-windows-x86_64.zip
555+
./dist/apm-windows-x86_64.zip.sha256
433556
434557
# Publish to PyPI (only stable releases from public repo)
435558
publish-pypi:
@@ -533,3 +656,42 @@ jobs:
533656
"linux_arm64": "${{ steps.checksums.outputs.linux-arm64-sha }}"
534657
}
535658
}
659+
660+
# Update Scoop bucket (only stable releases from public repo)
661+
update-scoop:
662+
name: Update Scoop Bucket
663+
runs-on: ubuntu-latest
664+
needs: [test, build, integration-tests, release-validation, create-release, publish-pypi]
665+
# TODO: Enable once downstream repository and secrets are configured (see #88)
666+
if: false && github.ref_type == 'tag' && needs.create-release.outputs.is_private_repo != 'true' && needs.create-release.outputs.is_prerelease != 'true'
667+
permissions:
668+
contents: read
669+
670+
steps:
671+
- name: Extract Windows checksum from GitHub release
672+
id: checksums
673+
run: |
674+
RELEASE_TAG="${{ github.ref_name }}"
675+
curl -L -o apm-windows-x86_64.zip.sha256 \
676+
"https://github.com/${{ github.repository }}/releases/download/$RELEASE_TAG/apm-windows-x86_64.zip.sha256"
677+
WINDOWS_X86_64_SHA=$(cat apm-windows-x86_64.zip.sha256 | cut -d' ' -f1)
678+
echo "windows-x86_64-sha=$WINDOWS_X86_64_SHA" >> $GITHUB_OUTPUT
679+
echo "Windows x86_64 SHA: $WINDOWS_X86_64_SHA"
680+
681+
- name: Trigger Scoop bucket repository update
682+
uses: peter-evans/repository-dispatch@v3
683+
with:
684+
token: ${{ secrets.GH_PKG_PAT }}
685+
repository: microsoft/scoop-apm
686+
event-type: bucket-update
687+
client-payload: |
688+
{
689+
"release": {
690+
"version": "${{ github.ref_name }}",
691+
"tag": "${{ github.ref_name }}",
692+
"repository": "${{ github.repository }}"
693+
},
694+
"checksums": {
695+
"windows_x86_64": "${{ steps.checksums.outputs.windows-x86_64-sha }}"
696+
}
697+
}

.github/workflows/ci-integration.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ jobs:
4444
steps:
4545
- run: echo "Internal PR auto-approved for ${{ github.event.workflow_run.head_branch }}"
4646

47-
# Linux-only for fast PR feedback. Full platform smoke tests run post-merge.
47+
# Linux smoke test
4848
smoke-test:
4949
needs: [approve-fork, approve-internal]
5050
# Run if either approval job succeeded (the other will be skipped)
@@ -92,9 +92,9 @@ jobs:
9292
GITHUB_APM_PAT: ${{ secrets.GH_CLI_PAT }}
9393
run: uv run pytest tests/integration/test_runtime_smoke.py -v
9494

95-
# Linux-only — downloads the single Linux binary artifact from ci.yml.
95+
# Linux integration tests — downloads the Linux binary artifact from ci.yml.
9696
integration-tests:
97-
name: Integration Tests
97+
name: Integration Tests (Linux)
9898
needs: [smoke-test]
9999
if: always() && needs.smoke-test.result == 'success'
100100
runs-on: ubuntu-24.04
@@ -145,9 +145,9 @@ jobs:
145145
uv run ./scripts/test-integration.sh
146146
timeout-minutes: 20
147147

148-
# Linux-only — validates the Linux binary in isolation. Full platform validation runs post-merge.
148+
# Linux release validation — validates the Linux binary in isolation.
149149
release-validation:
150-
name: Release Validation
150+
name: Release Validation (Linux)
151151
needs: [integration-tests]
152152
if: always() && needs.integration-tests.result == 'success'
153153
runs-on: ubuntu-24.04

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ permissions:
1515
contents: read
1616

1717
jobs:
18-
# Linux-only for fast PR feedback. Full platform matrix runs post-merge in build-release.yml.
18+
# Linux-only for PR feedback. Full platform matrix (incl. macOS + Windows) runs post-merge in build-release.yml.
1919
test:
2020
runs-on: ubuntu-24.04
2121
permissions:
@@ -57,7 +57,7 @@ jobs:
5757

5858
# Linux-only binary build for PR validation. Full platform builds run post-merge.
5959
build:
60-
name: Build APM Binary
60+
name: Build APM Binary (Linux)
6161
needs: [test]
6262
runs-on: ubuntu-24.04
6363
permissions:

0 commit comments

Comments
 (0)