name: "CD: Lume (macOS)" on: push: tags: - "lume-v*" workflow_dispatch: inputs: version: description: "Version to notarize (without v prefix)" required: true default: "0.1.0" workflow_call: inputs: version: description: "Version to notarize" required: true type: string secrets: APPLICATION_CERT_BASE64: required: true INSTALLER_CERT_BASE64: required: true CERT_PASSWORD: required: true APPLE_ID: required: true TEAM_ID: required: true APP_SPECIFIC_PASSWORD: required: true DEVELOPER_NAME: required: true PROVISIONING_PROFILE_BASE64: required: true RELEASE_APP_ID: required: false RELEASE_APP_PRIVATE_KEY: required: false permissions: contents: write env: APPLICATION_CERT_BASE64: ${{ secrets.APPLICATION_CERT_BASE64 }} INSTALLER_CERT_BASE64: ${{ secrets.INSTALLER_CERT_BASE64 }} CERT_PASSWORD: ${{ secrets.CERT_PASSWORD }} APPLE_ID: ${{ secrets.APPLE_ID }} TEAM_ID: ${{ secrets.TEAM_ID }} APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }} DEVELOPER_NAME: ${{ secrets.DEVELOPER_NAME }} PROVISIONING_PROFILE_BASE64: ${{ secrets.PROVISIONING_PROFILE_BASE64 }} RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }} RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} jobs: notarize: runs-on: macos-15 outputs: sha256_checksums: ${{ steps.generate_checksums.outputs.checksums }} version: ${{ steps.set_version.outputs.version }} steps: - uses: actions/checkout@v4 - name: Select Xcode 16.3 run: | sudo xcode-select -s /Applications/Xcode_16.3.app xcodebuild -version - name: Install dependencies run: | brew install cpio - name: Create .release directory run: mkdir -p .release - name: Set version id: set_version run: | # Determine version from tag or input if [[ "$GITHUB_REF" == refs/tags/lume-v* ]]; then VERSION="${GITHUB_REF#refs/tags/lume-v}" echo "Using version from tag: $VERSION" elif [[ -n "${{ inputs.version }}" ]]; then VERSION="${{ inputs.version }}" echo "Using version from input: $VERSION" elif [[ -n "${{ inputs.version }}" ]]; then VERSION="${{ inputs.version }}" echo "Using version from workflow_call input: $VERSION" else echo "Error: No version found in tag or input" exit 1 fi # Update version in Main.swift echo "Updating version in Main.swift to $VERSION" sed -i '' "s/static let current: String = \".*\"/static let current: String = \"$VERSION\"/" libs/lume/src/Main.swift # Set output for later steps echo "version=$VERSION" >> $GITHUB_OUTPUT - name: Import Certificates env: APPLICATION_CERT_BASE64: ${{ secrets.APPLICATION_CERT_BASE64 }} INSTALLER_CERT_BASE64: ${{ secrets.INSTALLER_CERT_BASE64 }} CERT_PASSWORD: ${{ secrets.CERT_PASSWORD }} KEYCHAIN_PASSWORD: "temp_password" run: | # Create a temporary keychain security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain security default-keychain -s build.keychain security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain security set-keychain-settings -t 3600 -l build.keychain # Import certificates echo $APPLICATION_CERT_BASE64 | base64 --decode > application.p12 echo $INSTALLER_CERT_BASE64 | base64 --decode > installer.p12 # Import certificates silently (minimize output) security import application.p12 -k build.keychain -P "$CERT_PASSWORD" -T /usr/bin/codesign -T /usr/bin/pkgbuild > /dev/null 2>&1 security import installer.p12 -k build.keychain -P "$CERT_PASSWORD" -T /usr/bin/codesign -T /usr/bin/pkgbuild > /dev/null 2>&1 # Allow codesign to access the certificates (minimal output) security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain > /dev/null 2>&1 # Verify certificates were imported echo "Verifying signing identities..." CERT_COUNT=$(security find-identity -v -p codesigning build.keychain | grep -c "Developer ID Application" || echo "0") INSTALLER_COUNT=$(security find-identity -v build.keychain | grep -c "Developer ID Installer" || echo "0") if [ "$CERT_COUNT" -eq 0 ]; then echo "Error: No Developer ID Application certificate found" security find-identity -v -p codesigning build.keychain exit 1 fi if [ "$INSTALLER_COUNT" -eq 0 ]; then echo "Error: No Developer ID Installer certificate found" security find-identity -v build.keychain exit 1 fi echo "Found $CERT_COUNT Developer ID Application certificate(s) and $INSTALLER_COUNT Developer ID Installer certificate(s)" echo "All required certificates verified successfully" # Clean up certificate files rm application.p12 installer.p12 - name: Install Provisioning Profile env: PROVISIONING_PROFILE_BASE64: ${{ secrets.PROVISIONING_PROFILE_BASE64 }} run: | echo "Installing provisioning profile..." echo "$PROVISIONING_PROFILE_BASE64" | base64 --decode > libs/lume/resources/embedded.provisionprofile echo "Provisioning profile installed successfully" - name: Build and Notarize id: build_notarize env: APPLE_ID: ${{ secrets.APPLE_ID }} TEAM_ID: ${{ secrets.TEAM_ID }} APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }} # These will now reference the imported certificates CERT_APPLICATION_NAME: "Developer ID Application: ${{ secrets.DEVELOPER_NAME }} (${{ secrets.TEAM_ID }})" CERT_INSTALLER_NAME: "Developer ID Installer: ${{ secrets.DEVELOPER_NAME }} (${{ secrets.TEAM_ID }})" VERSION: ${{ steps.set_version.outputs.version }} working-directory: ./libs/lume run: | # Minimal debug information echo "Starting build process..." echo "Swift version: $(swift --version | head -n 1)" echo "Building version: $VERSION" # Ensure .release directory exists mkdir -p .release chmod 755 .release # Build the project first (redirect verbose output) echo "Building project..." swift build --configuration release > build.log 2>&1 echo "Build completed." # Run the notarization script with LOG_LEVEL env var chmod +x scripts/build/build-release-notarized.sh cd scripts/build LOG_LEVEL=minimal ./build-release-notarized.sh # Return to the lume directory cd ../.. # Debug: List what files were actually created echo "Files in .release directory:" find .release -type f -name "*.tar.gz" -o -name "*.pkg.tar.gz" # Get architecture for output filename ARCH=$(uname -m) OS_IDENTIFIER="darwin-${ARCH}" # Output paths for later use echo "tarball_path=.release/lume-${VERSION}-${OS_IDENTIFIER}.tar.gz" >> $GITHUB_OUTPUT echo "pkg_path=.release/lume-${VERSION}-${OS_IDENTIFIER}.pkg.tar.gz" >> $GITHUB_OUTPUT - name: Upload build log on failure if: failure() && steps.build_notarize.outcome == 'failure' uses: actions/upload-artifact@v4 with: name: swift-build-log path: ./libs/lume/build.log retention-days: 7 - name: Generate SHA256 Checksums id: generate_checksums working-directory: ./libs/lume/.release run: | # Use existing checksums file if it exists, otherwise generate one if [ -f "checksums.txt" ]; then echo "Using existing checksums file" cat checksums.txt else echo "## SHA256 Checksums" > checksums.txt echo '```' >> checksums.txt shasum -a 256 lume-*.tar.gz >> checksums.txt echo '```' >> checksums.txt fi checksums=$(cat checksums.txt) echo "checksums<> $GITHUB_OUTPUT echo "$checksums" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT # Debug: Show all files in the release directory echo "All files in release directory:" ls -la - name: Create Standard Version Releases working-directory: ./libs/lume/.release run: | VERSION=${{ steps.set_version.outputs.version }} ARCH=$(uname -m) OS_IDENTIFIER="darwin-${ARCH}" # Create OS-tagged symlinks ln -sf "lume-${VERSION}-${OS_IDENTIFIER}.tar.gz" "lume-darwin.tar.gz" ln -sf "lume-${VERSION}-${OS_IDENTIFIER}.pkg.tar.gz" "lume-darwin.pkg.tar.gz" # Create simple symlinks ln -sf "lume-${VERSION}-${OS_IDENTIFIER}.tar.gz" "lume.tar.gz" ln -sf "lume-${VERSION}-${OS_IDENTIFIER}.pkg.tar.gz" "lume.pkg.tar.gz" # List all files (including symlinks) echo "Files with symlinks in release directory:" ls -la - name: Upload Notarized Package (Tarball) uses: actions/upload-artifact@v4 with: name: lume-notarized-tarball path: ./libs/lume/${{ steps.build_notarize.outputs.tarball_path }} if-no-files-found: error - name: Upload Notarized Package (Installer) uses: actions/upload-artifact@v4 with: name: lume-notarized-installer path: ./libs/lume/${{ steps.build_notarize.outputs.pkg_path }} if-no-files-found: error - name: Generate path-filtered release notes if: startsWith(github.ref, 'refs/tags/lume-v') id: release-notes run: | # Find previous lume tag PREV_TAG=$(git tag -l "lume-v*" --sort=-v:refname | grep -v "^${{ github.ref_name }}$" | head -n 1 || echo "") echo "Current tag: ${{ github.ref_name }}" echo "Previous tag: $PREV_TAG" # Generate release notes filtered by libs/lume path if [ -n "$PREV_TAG" ]; then echo "Generating notes for commits between $PREV_TAG and HEAD in libs/lume" NOTES=$(git log ${PREV_TAG}..HEAD --pretty=format:"* %s (%h) by @%an" -- "libs/lume" | head -50) else echo "No previous tag found, generating notes for recent commits in libs/lume" NOTES=$(git log --pretty=format:"* %s (%h) by @%an" -- "libs/lume" | head -50) fi if [ -z "$NOTES" ]; then NOTES="* Initial release or no path-specific changes found" fi # Store notes in output echo "RELEASE_NOTES<> $GITHUB_OUTPUT echo "## What's Changed" >> $GITHUB_OUTPUT echo "" >> $GITHUB_OUTPUT echo "$NOTES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - name: Create Release if: startsWith(github.ref, 'refs/tags/lume-v') uses: softprops/action-gh-release@v1 with: files: | ./libs/lume/${{ steps.build_notarize.outputs.tarball_path }} ./libs/lume/${{ steps.build_notarize.outputs.pkg_path }} ./libs/lume/.release/lume-darwin.tar.gz ./libs/lume/.release/lume-darwin.pkg.tar.gz ./libs/lume/.release/lume.tar.gz ./libs/lume/.release/lume.pkg.tar.gz body: | ${{ steps.release-notes.outputs.RELEASE_NOTES }} ${{ steps.generate_checksums.outputs.checksums }} ### Installation with script ```bash /bin/bash -c "$(curl -fsSL https://cua.ai/lume/install.sh)" ``` generate_release_notes: false make_latest: true - name: Generate GitHub App token (for bake-version push) id: app-token if: startsWith(github.ref, 'refs/tags/lume-v') uses: actions/create-github-app-token@v1 with: app-id: ${{ secrets.RELEASE_APP_ID }} private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} owner: ${{ github.repository_owner }} repositories: ${{ github.event.repository.name }} - name: Bake version into install script (commit + push) if: startsWith(github.ref, 'refs/tags/lume-v') env: VERSION: ${{ steps.set_version.outputs.version }} GH_TOKEN: ${{ steps.app-token.outputs.token }} run: | git config --unset-all "http.https://github.com/.extraheader" || true git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git" git fetch origin main git checkout -B bake-lume-version origin/main sed -i '' \ "s/^LUME_BAKED_VERSION=.*/LUME_BAKED_VERSION=\"${VERSION}\"/" \ libs/lume/scripts/install.sh if git diff --quiet -- libs/lume/scripts/install.sh; then echo "LUME_BAKED_VERSION already ${VERSION}; nothing to commit." exit 0 fi git config user.name "trycua-release[bot]" git config user.email "trycua-release[bot]@users.noreply.github.com" git add libs/lume/scripts/install.sh git commit -m "chore(lume): bake version ${VERSION} into install script [skip ci]" git push origin bake-lume-version:main