Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
76dfa3c
chore: initialize issue #172 draft PR
sergio-sisternes-epam Mar 10, 2026
8748663
feat(windows): add phase 1 native support
sergio-sisternes-epam Mar 10, 2026
dc6c09c
feat(windows): add Phase 2 runtime management and CI support (#88)
sergio-sisternes-epam Mar 10, 2026
6f28eb3
feat: add Windows package manager CI dispatch jobs (Scoop, Chocolatey…
sergio-sisternes-epam Mar 10, 2026
26040c7
fix: replace emojis with plain text markers in PowerShell helpers
sergio-sisternes-epam Mar 10, 2026
988e6ce
chore: revert Windows additions from build-binary.sh
sergio-sisternes-epam Mar 10, 2026
d5e27c4
fix: Windows native support - cross-platform fixes and test hardening
sergio-sisternes-epam Mar 11, 2026
5a8f67a
ci: add Windows test job to PR CI workflow
sergio-sisternes-epam Mar 11, 2026
3c1f4da
fix: address Copilot review feedback on PR #227
sergio-sisternes-epam Mar 11, 2026
ccd2684
ci: add full Windows CI parity across all pipelines
sergio-sisternes-epam Mar 11, 2026
6731f7f
ci: replace bash with PowerShell for Windows binary builds
sergio-sisternes-epam Mar 11, 2026
10c6580
fix: Rename-Item path arg and test-windows uv PATH
sergio-sisternes-epam Mar 11, 2026
eab28b6
fix: address Copilot review - Windows command parsing and args
sergio-sisternes-epam Mar 11, 2026
612a49b
fix: disable strip on Windows to prevent DLL corruption
sergio-sisternes-epam Mar 11, 2026
92cc4b1
fix: address review feedback from @danielmeppiel on PR #227
sergio-sisternes-epam Mar 12, 2026
5016794
Address review feedback: docs platform labels, script reorganization,…
sergio-sisternes-epam Mar 13, 2026
51c24db
fix: bundle .ps1 token helper only on Windows builds
sergio-sisternes-epam Mar 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/instructions/cicd.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ description: "CI/CD Pipeline configuration for PyInstaller binary packaging and
Three workflows split by trigger and secret requirements:

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

## Platform Testing Strategy
- **PR time**: Linux-only for speed. Catches logic bugs, dependency issues, and binary packaging problems.
- **Post-merge**: Full 4-platform matrix catches platform-specific issues immediately on main.
- **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.
- **PR time**: Linux + Windows in parallel. Catches logic bugs, dependency issues, path separators, encoding, and Windows-specific problems before merge.
- **Post-merge**: Full 5-platform matrix (linux x86_64/arm64, darwin x86_64/arm64, windows x86_64) catches remaining platform-specific issues on main.
- **Rationale**: Linux + Windows PR coverage catches the two fundamentally different platform families (Unix vs Windows). macOS-specific issues are rare and caught post-merge.

## PyInstaller Binary Packaging
- **CRITICAL**: Uses `--onedir` mode (NOT `--onefile`) for faster CLI startup performance
Expand Down
186 changes: 174 additions & 12 deletions .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ jobs:
- os: macos-latest
arch: arm64
platform: darwin
- os: windows-latest
arch: x86_64
platform: windows

steps:
- uses: actions/checkout@v4
Expand All @@ -70,17 +73,26 @@ jobs:
# Wait for installation to complete
until xcode-select -p >/dev/null 2>&1; do sleep 5; done

- name: Install uv
- name: Install uv (Unix)
if: matrix.platform != 'windows'
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH

- name: Install uv (Windows)
if: matrix.platform == 'windows'
shell: pwsh
run: |
irm https://astral.sh/uv/install.ps1 | iex
echo "$env:USERPROFILE\.local\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

- name: Cache uv environments
uses: actions/cache@v3
with:
path: |
~/.cache/uv
~/.local/share/uv
~\AppData\Local\uv\cache
key: ${{ runner.os }}-uv-${{ hashFiles('**/pyproject.toml') }}
restore-keys: |
${{ runner.os }}-uv-
Expand Down Expand Up @@ -120,6 +132,10 @@ jobs:
platform: darwin
arch: arm64
binary_name: apm-darwin-arm64
- os: windows-latest
platform: windows
arch: x86_64
binary_name: apm-windows-x86_64

runs-on: ${{ matrix.os }}
permissions:
Expand Down Expand Up @@ -149,19 +165,34 @@ jobs:
until xcode-select -p >/dev/null 2>&1; do sleep 5; done
brew install upx

- name: Install uv
- name: Install uv (Unix)
if: matrix.platform != 'windows'
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH

- name: Install uv (Windows)
if: matrix.platform == 'windows'
shell: pwsh
run: |
irm https://astral.sh/uv/install.ps1 | iex
echo "$env:USERPROFILE\.local\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

Comment thread
sergio-sisternes-epam marked this conversation as resolved.
- name: Install Python dependencies
run: |
uv sync --extra dev --extra build

- name: Build binary
- name: Build binary (Unix)
if: matrix.platform != 'windows'
run: |
chmod +x scripts/build-binary.sh
uv run ./scripts/build-binary.sh

- name: Build binary (Windows)
if: matrix.platform == 'windows'
shell: pwsh
run: |
uv run pwsh scripts/windows/build-binary.ps1

- name: Upload binary as workflow artifact
uses: actions/upload-artifact@v4
Expand All @@ -171,7 +202,11 @@ jobs:
./dist/${{ matrix.binary_name }}
./dist/${{ matrix.binary_name }}.sha256
./scripts/test-release-validation.sh
./scripts/windows/test-release-validation.ps1
./scripts/test-dependency-integration.sh
./scripts/windows/test-dependency-integration.ps1
./scripts/github-token-helper.sh
./scripts/windows/github-token-helper.ps1
include-hidden-files: true # Required to include .apm directories
retention-days: 30
if-no-files-found: error
Expand Down Expand Up @@ -202,6 +237,10 @@ jobs:
arch: arm64
platform: darwin
binary_name: apm-darwin-arm64
- os: windows-latest
arch: x86_64
platform: windows
binary_name: apm-windows-x86_64

runs-on: ${{ matrix.os }}
permissions:
Expand Down Expand Up @@ -236,15 +275,24 @@ jobs:
# Wait for installation to complete
until xcode-select -p >/dev/null 2>&1; do sleep 5; done

- name: Install uv
- name: Install uv (Unix)
if: matrix.platform != 'windows'
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH

- name: Install uv (Windows)
if: matrix.platform == 'windows'
shell: pwsh
run: |
irm https://astral.sh/uv/install.ps1 | iex
echo "$env:USERPROFILE\.local\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

- name: Install test dependencies
run: uv sync --extra dev

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

- name: Run integration tests (Windows)
if: matrix.platform == 'windows'
shell: pwsh
env:
APM_E2E_TESTS: "1"
GITHUB_TOKEN: ${{ secrets.GH_MODELS_PAT }}
GITHUB_APM_PAT: ${{ secrets.GH_CLI_PAT }}
ADO_APM_PAT: ${{ secrets.ADO_APM_PAT }}
run: |
uv run pwsh scripts/windows/test-integration.ps1 -SkipBuild
timeout-minutes: 20

# Release validation tests - Final pre-release validation of shipped binary
release-validation:
name: Release Validation
Expand All @@ -279,6 +339,10 @@ jobs:
arch: arm64
platform: darwin
binary_name: apm-darwin-arm64
- os: windows-latest
arch: x86_64
platform: windows
binary_name: apm-windows-x86_64

runs-on: ${{ matrix.os }}
permissions:
Expand Down Expand Up @@ -308,9 +372,10 @@ jobs:
uses: actions/download-artifact@v4
with:
name: ${{ matrix.binary_name }}
path: /tmp/apm-isolated-test/
path: ${{ matrix.platform == 'windows' && 'D:\apm-isolated-test' || '/tmp/apm-isolated-test/' }}

- name: Make binary executable and verify isolation
- name: Make binary executable and verify isolation (Unix)
if: matrix.platform != 'windows'
run: |
cd /tmp/apm-isolated-test

Expand All @@ -322,24 +387,54 @@ jobs:
# Make the binary executable
chmod +x ./dist/${{ matrix.binary_name }}/apm

- name: Create APM symlink for testing
- name: Create APM symlink for testing (Unix)
if: matrix.platform != 'windows'
run: |
cd /tmp/apm-isolated-test
ln -s "$(pwd)/dist/${{ matrix.binary_name }}/apm" "$(pwd)/apm"
echo "/tmp/apm-isolated-test" >> $GITHUB_PATH

- name: Verify binary and add to PATH (Windows)
if: matrix.platform == 'windows'
shell: pwsh
run: |
cd D:\apm-isolated-test

# Debug: List the downloaded structure
Write-Host "Downloaded structure:"
Get-ChildItem -Recurse -Filter "apm.exe"
Get-ChildItem .\dist\

# Add binary directory to PATH
$binDir = "D:\apm-isolated-test\dist\${{ matrix.binary_name }}"
echo $binDir | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

- name: Run release validation tests
- name: Run release validation tests (Unix)
if: matrix.platform != 'windows'
env:
APM_E2E_TESTS: "1" # Avoids interactive prompts for MCP env values with apm install
APM_E2E_TESTS: "1"
GITHUB_TOKEN: ${{ secrets.GH_MODELS_PAT }}
GITHUB_APM_PAT: ${{ secrets.GH_CLI_PAT }} # Primary: APM module access
ADO_APM_PAT: ${{ secrets.ADO_APM_PAT }} # Azure DevOps module access
GITHUB_APM_PAT: ${{ secrets.GH_CLI_PAT }}
ADO_APM_PAT: ${{ secrets.ADO_APM_PAT }}
run: |
cd /tmp/apm-isolated-test
chmod +x scripts/test-release-validation.sh
./scripts/test-release-validation.sh
timeout-minutes: 20

- name: Run release validation tests (Windows)
if: matrix.platform == 'windows'
shell: pwsh
env:
APM_E2E_TESTS: "1"
GITHUB_TOKEN: ${{ secrets.GH_MODELS_PAT }}
GITHUB_APM_PAT: ${{ secrets.GH_CLI_PAT }}
ADO_APM_PAT: ${{ secrets.ADO_APM_PAT }}
run: |
cd D:\apm-isolated-test
.\scripts\windows\test-release-validation.ps1
timeout-minutes: 20


create-release:
name: Create GitHub Release
Expand Down Expand Up @@ -395,6 +490,32 @@ jobs:
exit 1
fi
done

binary="apm-windows-x86_64"
artifact_dir="${binary}"
binary_dir="${artifact_dir}/dist/${binary}"
if [ -d "$binary_dir" ] && [ -f "$binary_dir/apm.exe" ]; then
echo "Processing $binary_dir directory..."
(
cd "${artifact_dir}/dist"
zip -qr "../../${binary}.zip" "${binary}"
)
if command -v sha256sum &> /dev/null; then
sha256sum "${binary}.zip" > "${binary}.zip.sha256"
elif command -v shasum &> /dev/null; then
shasum -a 256 "${binary}.zip" > "${binary}.zip.sha256"
fi
echo "Created ${binary}.zip"
else
echo "ERROR: Binary directory $binary_dir not found or $binary_dir/apm.exe missing"
echo "Artifact directory contents:"
ls -la "$artifact_dir/" || echo "Directory $artifact_dir does not exist"
if [ -d "$artifact_dir/dist" ]; then
echo "Dist directory contents:"
ls -la "$artifact_dir/dist/"
fi
exit 1
fi

- name: Determine release type
id: release_type
Expand Down Expand Up @@ -430,6 +551,8 @@ jobs:
./dist/apm-darwin-x86_64.tar.gz.sha256
./dist/apm-darwin-arm64.tar.gz
./dist/apm-darwin-arm64.tar.gz.sha256
./dist/apm-windows-x86_64.zip
./dist/apm-windows-x86_64.zip.sha256

# Publish to PyPI (only stable releases from public repo)
publish-pypi:
Expand Down Expand Up @@ -533,3 +656,42 @@ jobs:
"linux_arm64": "${{ steps.checksums.outputs.linux-arm64-sha }}"
}
}

# Update Scoop bucket (only stable releases from public repo)
update-scoop:
name: Update Scoop Bucket
runs-on: ubuntu-latest
needs: [test, build, integration-tests, release-validation, create-release, publish-pypi]
# TODO: Enable once downstream repository and secrets are configured (see #88)
if: false && github.ref_type == 'tag' && needs.create-release.outputs.is_private_repo != 'true' && needs.create-release.outputs.is_prerelease != 'true'
permissions:
contents: read

steps:
- name: Extract Windows checksum from GitHub release
id: checksums
run: |
RELEASE_TAG="${{ github.ref_name }}"
curl -L -o apm-windows-x86_64.zip.sha256 \
"https://github.com/${{ github.repository }}/releases/download/$RELEASE_TAG/apm-windows-x86_64.zip.sha256"
WINDOWS_X86_64_SHA=$(cat apm-windows-x86_64.zip.sha256 | cut -d' ' -f1)
echo "windows-x86_64-sha=$WINDOWS_X86_64_SHA" >> $GITHUB_OUTPUT
echo "Windows x86_64 SHA: $WINDOWS_X86_64_SHA"

- name: Trigger Scoop bucket repository update
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.GH_PKG_PAT }}
repository: microsoft/scoop-apm
event-type: bucket-update
client-payload: |
{
"release": {
"version": "${{ github.ref_name }}",
"tag": "${{ github.ref_name }}",
"repository": "${{ github.repository }}"
},
"checksums": {
"windows_x86_64": "${{ steps.checksums.outputs.windows-x86_64-sha }}"
}
}
10 changes: 5 additions & 5 deletions .github/workflows/ci-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
steps:
- run: echo "Internal PR auto-approved for ${{ github.event.workflow_run.head_branch }}"

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

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

# Linux-only — validates the Linux binary in isolation. Full platform validation runs post-merge.
# Linux release validation — validates the Linux binary in isolation.
release-validation:
name: Release Validation
name: Release Validation (Linux)
needs: [integration-tests]
if: always() && needs.integration-tests.result == 'success'
runs-on: ubuntu-24.04
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ permissions:
contents: read

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

# Linux-only binary build for PR validation. Full platform builds run post-merge.
build:
name: Build APM Binary
name: Build APM Binary (Linux)
needs: [test]
runs-on: ubuntu-24.04
permissions:
Expand Down
Loading
Loading