chore(deps): update actions/upload-artifact action to v6 #66
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI Build | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| packages: write | |
| # Only allow one workflow run per branch at a time | |
| # New commits cancel in-progress runs for the same branch | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| build: | |
| name: Build ${{ matrix.os }}-${{ matrix.arch }} | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # Linux x64 | |
| - os: linux | |
| arch: x64 | |
| runner: ubuntu-latest | |
| artifact_name: IntuneManager.bin | |
| output_path: IntuneManager | |
| asset_name: IntuneManager-linux-x64.bin | |
| content_type: application/octet-stream | |
| # Linux ARM64 | |
| - os: linux | |
| arch: arm64 | |
| runner: ubuntu-24.04-arm | |
| artifact_name: IntuneManager.bin | |
| output_path: IntuneManager | |
| asset_name: IntuneManager-linux-arm64.bin | |
| content_type: application/octet-stream | |
| # Windows x64 | |
| # - os: windows | |
| # arch: x64 | |
| # runner: windows-latest | |
| # artifact_name: IntuneManager.exe | |
| # output_path: IntuneManager.exe | |
| # asset_name: IntuneManager-windows-x64.exe | |
| # content_type: application/vnd.microsoft.portable-executable | |
| # # Windows ARM64 | |
| # - os: windows | |
| # arch: arm64 | |
| # runner: windows-11-arm | |
| # artifact_name: IntuneManager.exe | |
| # output_path: IntuneManager.exe | |
| # asset_name: IntuneManager-windows-arm64.exe | |
| # content_type: application/vnd.microsoft.portable-executable | |
| # macOS Intel | |
| - os: macos | |
| arch: x64 | |
| runner: macos-13 | |
| artifact_name: IntuneManager | |
| output_path: intune_manager.app | |
| asset_name: IntuneManager-macos-x64.app.zip | |
| content_type: application/zip | |
| # macOS ARM64 | |
| - os: macos | |
| arch: arm64 | |
| runner: macos-latest | |
| artifact_name: IntuneManager | |
| output_path: intune_manager.app | |
| asset_name: IntuneManager-macos-arm64.app.zip | |
| content_type: application/zip | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Set up Python 3.13 | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.13' | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v7 | |
| with: | |
| version: "latest" | |
| enable-cache: true | |
| # Setup compiler caching (ccache for Linux/macOS, clcache for Windows) | |
| - name: Setup ccache (Linux/macOS) | |
| if: runner.os != 'Windows' | |
| uses: hendrikmuhs/ccache-action@v1.2 | |
| with: | |
| key: ${{ matrix.os }}-${{ matrix.arch }} | |
| restore-keys: | | |
| ${{ matrix.os }}-${{ matrix.arch }}- | |
| ${{ matrix.os }}- | |
| max-size: 2G | |
| verbose: 2 | |
| create-symlink: true # Create compiler wrapper symlinks so gcc/clang use ccache | |
| - name: Configure ccache (Linux/macOS) | |
| if: runner.os != 'Windows' | |
| run: | | |
| echo "NUITKA_CCACHE_BINARY=$(which ccache)" >> $GITHUB_ENV | |
| echo "::group::ccache configuration" | |
| ccache --version | |
| ccache --show-config | |
| ccache --zero-stats | |
| ccache --max-size=2G | |
| echo "::endgroup::" | |
| echo "::group::Compiler symlinks verification" | |
| which gcc || true | |
| which g++ || true | |
| which clang || true | |
| which clang++ || true | |
| ls -la "$(which gcc)" || true | |
| ls -la "$(which g++)" || true | |
| echo "::endgroup::" | |
| # Windows compiler caching with clcache (built into Nuitka) | |
| # - name: Configure clcache (Windows) | |
| # if: runner.os == 'Windows' | |
| # shell: bash | |
| # run: | | |
| # echo "NUITKA_CLCACHE_BINARY=clcache" >> $GITHUB_ENV | |
| # echo "CLCACHE_DIR=${{ runner.temp }}/clcache" >> $GITHUB_ENV | |
| # echo "CLCACHE_COMPRESS=1" >> $GITHUB_ENV | |
| # echo "CLCACHE_COMPRESSLEVEL=6" >> $GITHUB_ENV | |
| # # Increase cache size for better hit rate | |
| # echo "CLCACHE_HARDLINK=1" >> $GITHUB_ENV # Use hardlinks to save space | |
| # mkdir -p "${{ runner.temp }}/clcache" | |
| # Cache clcache for Windows (stable key per platform, clcache handles source changes internally) | |
| # - name: Cache clcache (Windows) | |
| # if: runner.os == 'Windows' | |
| # uses: actions/cache@v4 | |
| # with: | |
| # path: ${{ runner.temp }}/clcache | |
| # key: clcache-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles('pyproject.toml') }} | |
| # restore-keys: | | |
| # clcache-${{ matrix.os }}-${{ matrix.arch }}- | |
| # Configure Nuitka cache directories | |
| - name: Configure Nuitka environment | |
| shell: bash | |
| run: | | |
| echo "NUITKA_CACHE_DIR=${{ runner.temp }}/nuitka-cache" >> $GITHUB_ENV | |
| echo "NUITKA_CACHE_DIR_DOWNLOADS=${{ runner.temp }}/nuitka-cache/downloads" >> $GITHUB_ENV | |
| echo "NUITKA_CACHE_DIR_CCACHE=${{ runner.temp }}/nuitka-cache/ccache" >> $GITHUB_ENV | |
| echo "NUITKA_CACHE_DIR_CLCACHE=${{ runner.temp }}/nuitka-cache/clcache" >> $GITHUB_ENV | |
| echo "NUITKA_CACHE_DIR_BYTECODE=${{ runner.temp }}/nuitka-cache/bytecode" >> $GITHUB_ENV | |
| echo "NUITKA_CACHE_DIR_DLL_DEPENDENCIES=${{ runner.temp }}/nuitka-cache/dll-dependencies" >> $GITHUB_ENV | |
| mkdir -p "${{ runner.temp }}/nuitka-cache" | |
| # Cache Nuitka build directory (stable key per platform, invalidates only when dependencies/config change) | |
| - name: Cache Nuitka build | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| intune_manager.build | |
| intune_manager.dist | |
| intune_manager.onefile-build | |
| ${{ runner.temp }}/nuitka-cache | |
| key: nuitka-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles('pyproject.toml', 'src/intune_manager/__main__.py') }} | |
| restore-keys: | | |
| nuitka-${{ matrix.os }}-${{ matrix.arch }}- | |
| - name: Install dependencies | |
| run: uv sync --all-groups | |
| - name: Get version from pyproject.toml | |
| id: version | |
| shell: bash | |
| run: | | |
| # For main branch CI builds, use a fixed development version | |
| VERSION="0.0.0.1" | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "Version: $VERSION (CI build)" | |
| - name: Build with Nuitka | |
| shell: bash | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| # Show compiler cache status before build | |
| if [ "${{ runner.os }}" != "Windows" ]; then | |
| echo "::group::Pre-build ccache statistics" | |
| ccache --show-stats | |
| echo "::endgroup::" | |
| fi | |
| # Run Nuitka build with caching enabled | |
| uv run nuitka \ | |
| --product-version="$VERSION" \ | |
| --assume-yes-for-downloads \ | |
| src/intune_manager | |
| - name: Verify build output | |
| shell: bash | |
| run: | | |
| echo "Checking for build output at: ${{ matrix.output_path }}" | |
| if [ -e "${{ matrix.output_path }}" ]; then | |
| echo "✓ Build artifact found" | |
| ls -lh "${{ matrix.output_path }}" | |
| else | |
| echo "✗ Build artifact not found!" | |
| echo "Contents of / directory:" | |
| ls -larth | |
| echo "Contents of src directory:" | |
| ls -larth src/ | |
| exit 1 | |
| fi | |
| # ============================================ | |
| # Code Sign macOS App Bundle | |
| # ============================================ | |
| - name: Code sign macOS app bundle | |
| if: matrix.os == 'macos' | |
| env: | |
| MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }} | |
| MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }} | |
| MACOS_CERTIFICATE_NAME: ${{ secrets.MACOS_CERTIFICATE_NAME }} | |
| MACOS_CI_KEYCHAIN_PWD: ${{ secrets.MACOS_CI_KEYCHAIN_PWD }} | |
| run: | | |
| echo "::group::Decode and verify certificate" | |
| # Decode certificate from base64 | |
| echo "$MACOS_CERTIFICATE" | base64 --decode > certificate.p12 | |
| # Verify the .p12 file is valid | |
| if ! openssl pkcs12 -info -in certificate.p12 -passin pass:"$MACOS_CERTIFICATE_PWD" -noout 2>/dev/null; then | |
| echo "❌ Error: Invalid .p12 certificate file or incorrect password" | |
| exit 1 | |
| fi | |
| echo "✓ Certificate file decoded and validated successfully" | |
| echo "::endgroup::" | |
| echo "::group::Create and configure keychain" | |
| # Create temporary keychain | |
| security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain | |
| security default-keychain -s build.keychain | |
| security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain | |
| echo "✓ Keychain created and unlocked" | |
| echo "::endgroup::" | |
| echo "::group::Import certificate" | |
| # Import certificate to keychain | |
| security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign -T /usr/bin/security | |
| security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain | |
| echo "✓ Certificate imported to keychain" | |
| echo "::endgroup::" | |
| echo "::group::Verify signing identity" | |
| # List available identities to verify import succeeded | |
| echo "Available code signing identities:" | |
| security find-identity -v -p codesigning build.keychain | |
| # Verify the specific identity we need exists | |
| if ! security find-identity -v -p codesigning build.keychain | grep -q "$MACOS_CERTIFICATE_NAME"; then | |
| echo "❌ Error: Required signing identity not found: $MACOS_CERTIFICATE_NAME" | |
| echo "Available identities:" | |
| security find-identity -v -p codesigning build.keychain | |
| exit 1 | |
| fi | |
| echo "✓ Signing identity verified: $MACOS_CERTIFICATE_NAME" | |
| echo "::endgroup::" | |
| echo "::group::Sign app bundle" | |
| # Sign the app bundle (deep sign all nested code) | |
| /usr/bin/codesign --force --deep --sign "$MACOS_CERTIFICATE_NAME" \ | |
| --options runtime \ | |
| --timestamp \ | |
| "${{ matrix.output_path }}" -v | |
| echo "✓ App bundle signed successfully" | |
| echo "::endgroup::" | |
| echo "::group::Verify signature" | |
| # Verify the signature | |
| /usr/bin/codesign --verify --deep --strict --verbose=2 "${{ matrix.output_path }}" | |
| echo "✓ Signature verification passed" | |
| echo "::endgroup::" | |
| # Clean up certificate file | |
| rm certificate.p12 | |
| # ============================================ | |
| # Notarize macOS App Bundle | |
| # ============================================ | |
| - name: Notarize macOS app bundle | |
| if: matrix.os == 'macos' | |
| env: | |
| MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }} | |
| MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }} | |
| MACOS_NOTARIZATION_PWD: ${{ secrets.MACOS_NOTARIZATION_PWD }} | |
| run: | | |
| # Store notarization credentials | |
| xcrun notarytool store-credentials "notarytool-profile" \ | |
| --apple-id "$MACOS_NOTARIZATION_APPLE_ID" \ | |
| --team-id "$MACOS_NOTARIZATION_TEAM_ID" \ | |
| --password "$MACOS_NOTARIZATION_PWD" | |
| # Create a zip for notarization (required format) | |
| ditto -c -k --keepParent "${{ matrix.output_path }}" notarization.zip | |
| # Submit for notarization and wait for result | |
| echo "Submitting app for notarization..." | |
| xcrun notarytool submit notarization.zip \ | |
| --keychain-profile "notarytool-profile" \ | |
| --wait | |
| # Check if notarization succeeded | |
| NOTARIZATION_STATUS=$? | |
| if [ $NOTARIZATION_STATUS -ne 0 ]; then | |
| echo "❌ Notarization failed! Fetching logs..." | |
| # Get the latest submission ID for logs | |
| SUBMISSION_ID=$(xcrun notarytool history --keychain-profile "notarytool-profile" | head -2 | tail -1 | awk '{print $5}') | |
| xcrun notarytool log "$SUBMISSION_ID" --keychain-profile "notarytool-profile" | |
| exit 1 | |
| fi | |
| # Staple the notarization ticket to the app | |
| echo "Stapling notarization ticket..." | |
| xcrun stapler staple "${{ matrix.output_path }}" | |
| # Verify stapling | |
| xcrun stapler validate "${{ matrix.output_path }}" | |
| # Clean up | |
| rm notarization.zip | |
| echo "✅ Code signing and notarization complete!" | |
| # Upload compilation report for debugging (short retention since it's for troubleshooting) | |
| - name: Upload compilation report | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: compilation-report-${{ matrix.os }}-${{ matrix.arch }} | |
| path: compilation-report.xml | |
| retention-days: 5 | |
| if-no-files-found: warn | |
| # Package macOS .app bundles using ditto (preserves app bundle structure) | |
| - name: Package macOS app bundle | |
| if: matrix.os == 'macos' | |
| shell: bash | |
| run: | | |
| # Use ditto with --keepParent to ensure .app bundle structure is preserved on extraction | |
| ditto -c -k --sequesterRsrc --keepParent "${{ matrix.output_path }}" "${{ matrix.asset_name }}" | |
| echo "Created: ${{ matrix.asset_name }}" | |
| ls -lh "${{ matrix.asset_name }}" | |
| # For non-macOS, just copy the binary | |
| - name: Prepare package asset | |
| if: matrix.os != 'macos' | |
| shell: bash | |
| run: | | |
| cp "${{ matrix.output_path }}" "${{ matrix.asset_name }}" | |
| echo "Prepared: ${{ matrix.asset_name }}" | |
| ls -lh "${{ matrix.asset_name }}" | |
| # Upload as workflow artifact (7 day retention since we're publishing to packages) | |
| - name: Upload workflow artifact | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: IntuneManager-${{ steps.version.outputs.version }}-${{ matrix.os }}-${{ matrix.arch }} | |
| path: ${{ matrix.asset_name }} | |
| retention-days: 7 | |
| compression-level: 9 | |
| # Publish to GitHub Packages as rolling pre-release | |
| - name: Determine release tag | |
| id: release_tag | |
| shell: bash | |
| run: | | |
| if [[ "${{ github.event_name }}" == "pull_request" ]]; then | |
| # For PRs, use pr-{number}-{os}-{arch} format | |
| TAG="ci-pr-${{ github.event.pull_request.number }}-${{ matrix.os }}-${{ matrix.arch }}" | |
| else | |
| # For main branch, use main-{os}-{arch} format | |
| TAG="ci-main-${{ matrix.os }}-${{ matrix.arch }}" | |
| fi | |
| echo "tag=$TAG" >> $GITHUB_OUTPUT | |
| echo "Release tag: $TAG" | |
| - name: Publish to GitHub Packages | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ steps.release_tag.outputs.tag }} | |
| name: "CI Build: ${{ matrix.os }}-${{ matrix.arch }} (${{ github.ref_name }})" | |
| files: ${{ matrix.asset_name }} | |
| prerelease: true | |
| draft: false | |
| make_latest: false | |
| body: | | |
| **Automated CI Build** | |
| - **Platform**: ${{ matrix.os }}-${{ matrix.arch }} | |
| - **Branch**: ${{ github.ref_name }} | |
| - **Commit**: ${{ github.sha }} | |
| - **Build Date**: ${{ github.event.head_commit.timestamp }} | |
| This is an automated build from the CI pipeline. | |
| For stable releases, see the [Releases page](https://github.com/${{ github.repository }}/releases). | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # ============================================ | |
| # Cleanup Keychain (always runs, even on failure) | |
| # ============================================ | |
| - name: Clean up keychain | |
| if: always() && matrix.os == 'macos' | |
| run: | | |
| security delete-keychain build.keychain || true | |
| - name: Save compiler cache statistics | |
| if: always() | |
| shell: bash | |
| run: | | |
| if [ "${{ runner.os }}" != "Windows" ]; then | |
| echo "::group::ccache statistics" | |
| ccache --show-stats || true | |
| ccache --show-config || true | |
| echo "Cache directory: $(ccache --get-config cache_dir)" || true | |
| echo "Cache size: $(du -sh $(ccache --get-config cache_dir) 2>/dev/null || echo 'N/A')" | |
| echo "::endgroup::" | |
| # Add to job summary | |
| echo "## 🔧 Compiler Cache Statistics (${{ matrix.os }}-${{ matrix.arch }})" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### ccache (Linux/macOS)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Extract key metrics from ccache stats | |
| STATS=$(ccache --show-stats 2>/dev/null || echo "") | |
| CACHE_DIR=$(ccache --get-config cache_dir 2>/dev/null || echo "N/A") | |
| CACHE_SIZE=$(du -sh "$CACHE_DIR" 2>/dev/null | cut -f1 || echo "N/A") | |
| echo "| Metric | Value |" >> $GITHUB_STEP_SUMMARY | |
| echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY | |
| # Parse ccache stats for key metrics | |
| if [ -n "$STATS" ]; then | |
| CACHE_HIT=$(echo "$STATS" | grep -i "cache hit" | head -1 | awk '{print $NF}' || echo "N/A") | |
| CACHE_MISS=$(echo "$STATS" | grep -i "cache miss" | head -1 | awk '{print $NF}' || echo "N/A") | |
| HIT_RATE=$(echo "$STATS" | grep -i "hit rate" | head -1 | awk '{print $NF}' || echo "N/A") | |
| echo "| Cache hits | ${CACHE_HIT} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Cache misses | ${CACHE_MISS} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Hit rate | ${HIT_RATE} |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "| Cache directory | \`${CACHE_DIR}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Cache size | ${CACHE_SIZE} |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "::group::clcache statistics" | |
| echo "CLCACHE_DIR: $CLCACHE_DIR" | |
| if [ -d "$CLCACHE_DIR" ]; then | |
| echo "Cache size: $(du -sh $CLCACHE_DIR 2>/dev/null || echo 'N/A')" | |
| echo "Number of cached files: $(find $CLCACHE_DIR -type f | wc -l)" | |
| else | |
| echo "No clcache directory found" | |
| fi | |
| echo "::endgroup::" | |
| # Add to job summary | |
| echo "## 🔧 Compiler Cache Statistics (${{ matrix.os }}-${{ matrix.arch }})" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### clcache (Windows)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Metric | Value |" >> $GITHUB_STEP_SUMMARY | |
| echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY | |
| if [ -d "$CLCACHE_DIR" ]; then | |
| CACHE_SIZE=$(du -sh "$CLCACHE_DIR" 2>/dev/null | cut -f1 || echo "N/A") | |
| NUM_FILES=$(find "$CLCACHE_DIR" -type f | wc -l) | |
| echo "| Cache directory | \`${CLCACHE_DIR}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Cache size | ${CACHE_SIZE} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Cached files | ${NUM_FILES} |" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "| Status | ❌ No cache directory found |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi |