Refactor bottle metadata merge step in workflow #93
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: Bottle | |
| on: | |
| push: | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| jobs: | |
| test-and-bottle: | |
| name: Test and build bottles per macOS | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [macos-14, macos-15, macos-26] # Sonoma, Sequoia, Tahoe (public preview) | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout tap | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Homebrew | |
| uses: Homebrew/actions/setup-homebrew@master | |
| - name: Test tarball download | |
| run: | | |
| curl -I "https://github.com/MFlowCode/MFC/archive/refs/tags/v5.1.5.tar.gz" | |
| curl -L "https://github.com/MFlowCode/MFC/archive/refs/tags/v5.1.5.tar.gz" | shasum -a 256 | |
| - name: Determine version and root URL (from checked-out formula) | |
| id: meta | |
| run: | | |
| set -euo pipefail | |
| FORMULA="Formula/mfc.rb" | |
| URL="$( | |
| grep -Eo 'https://github.com/[^"]+/archive/refs/tags/v[0-9]+\.[0-9]+\.[0-9]+\.tar\.gz' "${FORMULA}" \ | |
| | tail -n 1 | |
| )" | |
| if [[ -z "${URL}" ]]; then | |
| echo "Could not extract release tarball URL from ${FORMULA}" >&2 | |
| exit 1 | |
| fi | |
| VERSION="$(echo "${URL}" | sed -E 's/.*v([0-9]+\.[0-9]+\.[0-9]+)\.tar\.gz/\1/')" | |
| if [[ -z "${VERSION}" ]]; then | |
| echo "Could not parse version from URL: ${URL}" >&2 | |
| exit 1 | |
| fi | |
| TAG="mfc-${VERSION}" | |
| ROOT_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}" | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "tag=${TAG}" >> "$GITHUB_OUTPUT" | |
| echo "root_url=${ROOT_URL}" >> "$GITHUB_OUTPUT" | |
| echo "Formula URL: ${URL}" | |
| echo "Formula version: ${VERSION}" | |
| echo "Release tag: ${TAG}" | |
| echo "Bottle root_url: ${ROOT_URL}" | |
| - name: Create temp tap with checked-out formula | |
| run: | | |
| set -euo pipefail | |
| # brew tap-new needs git identity sometimes | |
| git config --global user.email "github-actions[bot]@users.noreply.github.com" | |
| git config --global user.name "github-actions[bot]" | |
| # Create a local tap and copy the formula into Homebrew's tap location | |
| brew tap-new mflowcode/local | |
| TAP_DIR="$(brew --repository)/Library/Taps/mflowcode/homebrew-local" | |
| mkdir -p "${TAP_DIR}/Formula" | |
| cp Formula/mfc.rb "${TAP_DIR}/Formula/mfc.rb" | |
| echo "Temp tap formula:" | |
| ls -la "${TAP_DIR}/Formula/mfc.rb" | |
| - name: Install formula with --build-bottle (temp tap) | |
| run: | | |
| set -euo pipefail | |
| echo "Installing mfc with --build-bottle (allowing non-fatal dylib fixup failures)..." | |
| # Homebrew may exit non-zero due to dylib ID fixup issues in Python wheels, | |
| # even though the formula actually installed correctly. Treat that as | |
| # non-fatal as long as the formula is present. | |
| set +e | |
| brew install --build-bottle mflowcode/local/mfc 2>&1 | tee /tmp/brew-install.log | |
| brew_exit_code=$? | |
| set -e | |
| if brew list mflowcode/local/mfc &>/dev/null; then | |
| echo "✅ mfc installed successfully (ignoring dylib fixup warnings)" | |
| else | |
| echo "❌ mfc installation failed; propagating brew exit code" | |
| exit $brew_exit_code | |
| fi | |
| - name: Run Sod shock tube test case | |
| run: | | |
| set -euo pipefail | |
| echo "Running a simple test case (1D Sod shock tube) on ${{ matrix.os }}..." | |
| TESTDIR=$(mktemp -d) | |
| cp "$(brew --prefix mflowcode/local/mfc)/examples/1D_sodshocktube/case.py" "$TESTDIR/" | |
| cd "$TESTDIR" | |
| mfc case.py -j 1 | |
| test -d "$TESTDIR/silo_hdf5" | |
| echo "✅ Test case ran successfully and produced output" | |
| - name: Basic installation verification | |
| run: | | |
| set -euo pipefail | |
| PREFIX="$(brew --prefix mflowcode/local/mfc)" | |
| echo "1. Checking binaries..." | |
| test -x "${PREFIX}/bin/pre_process" | |
| test -x "${PREFIX}/bin/simulation" | |
| test -x "${PREFIX}/bin/post_process" | |
| test -x "${PREFIX}/bin/mfc" | |
| echo "2. Checking toolchain..." | |
| test -d "${PREFIX}/toolchain" | |
| echo "3. Checking Python venv..." | |
| test -d "${PREFIX}/libexec/venv" | |
| test -x "${PREFIX}/libexec/venv/bin/python" | |
| echo "4. Checking examples..." | |
| test -d "${PREFIX}/examples" | |
| echo "5. Testing mfc command..." | |
| mfc --help | |
| echo "✅ All verification checks passed on ${{ matrix.os }}" | |
| - name: Build bottle (temp tap formula) | |
| run: | | |
| set -euo pipefail | |
| brew bottle --root-url="${{ steps.meta.outputs.root_url }}" --json mflowcode/local/mfc | |
| ls -1 *.bottle.* | |
| - name: Upload bottle artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: bottles-${{ matrix.os }} | |
| path: | | |
| *.bottle.* | |
| merge-bottles: | |
| name: Merge bottles and update formula | |
| needs: test-and-bottle | |
| runs-on: macos-14 | |
| steps: | |
| - name: Checkout tap | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Homebrew | |
| uses: Homebrew/actions/setup-homebrew@master | |
| - name: Determine version and root URL (from checked-out formula) | |
| id: meta | |
| run: | | |
| set -euo pipefail | |
| FORMULA="Formula/mfc.rb" | |
| URL="$( | |
| grep -Eo 'https://github.com/[^"]+/archive/refs/tags/v[0-9]+\.[0-9]+\.[0-9]+\.tar\.gz' "${FORMULA}" \ | |
| | tail -n 1 | |
| )" | |
| if [[ -z "${URL}" ]]; then | |
| echo "Could not extract release tarball URL from ${FORMULA}" >&2 | |
| exit 1 | |
| fi | |
| VERSION="$(echo "${URL}" | sed -E 's/.*v([0-9]+\.[0-9]+\.[0-9]+)\.tar\.gz/\1/')" | |
| if [[ -z "${VERSION}" ]]; then | |
| echo "Could not parse version from URL: ${URL}" >&2 | |
| exit 1 | |
| fi | |
| TAG="mfc-${VERSION}" | |
| ROOT_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}" | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "tag=${TAG}" >> "$GITHUB_OUTPUT" | |
| echo "root_url=${ROOT_URL}" >> "$GITHUB_OUTPUT" | |
| - name: Configure git for bottle merge | |
| run: | | |
| set -euo pipefail | |
| git config --global user.email "github-actions[bot]@users.noreply.github.com" | |
| git config --global user.name "github-actions[bot]" | |
| - name: Download all bottle artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: bottles | |
| - name: Flatten bottle directory | |
| run: | | |
| set -euo pipefail | |
| echo "Before flattening:" | |
| find bottles -name '*.bottle.*' -type f | |
| find bottles -mindepth 2 -name '*.bottle.*' -type f -exec mv {} bottles/ \; | |
| echo "" | |
| echo "After flattening:" | |
| ls -1 bottles/*.bottle.* | |
| - name: Rename bottles for GitHub release | |
| run: | | |
| set -euo pipefail | |
| echo "Renaming bottles (mfc-- → mfc-)..." | |
| for file in bottles/mfc--*.bottle.*; do | |
| if [[ -f "$file" ]]; then | |
| newname=$(echo "$file" | sed 's/mfc--/mfc-/') | |
| mv "$file" "$newname" | |
| echo " $file → $newname" | |
| fi | |
| done | |
| echo "" | |
| echo "Bottles ready for upload:" | |
| ls -1 bottles/*.bottle.* | |
| - name: Tap this checkout as mflowcode/local (so brew can find mflowcode/local/mfc) | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| # Ensure a clean state if a previous run left the tap installed | |
| brew untap mflowcode/local >/dev/null 2>&1 || true | |
| # Tap the checked-out repository as a local tap | |
| brew tap mflowcode/local "${GITHUB_WORKSPACE}" | |
| # Verify the tap exists (brew tap with no args lists taps) | |
| brew tap | grep -q '^mflowcode/local$' | |
| # Verify the formula resolves | |
| brew info mflowcode/local/mfc | |
| - name: Merge bottle metadata into formula | |
| id: merge | |
| run: | | |
| set -euo pipefail | |
| JSON_BOTTLES=(bottles/*.bottle*.json) | |
| if [[ ! -e "${JSON_BOTTLES[0]}" ]]; then | |
| echo "No bottle metadata (*.bottle*.json) found in bottles/" | |
| ls -la bottles/ | |
| exit 1 | |
| fi | |
| echo "Merging bottle metadata from:" | |
| printf '%s\n' "${JSON_BOTTLES[@]}" | |
| BEFORE_SHA=$(git rev-parse HEAD) | |
| echo "HEAD before merge: ${BEFORE_SHA}" | |
| brew bottle --merge --write --root-url="${{ steps.meta.outputs.root_url }}" "${JSON_BOTTLES[@]}" | |
| AFTER_SHA=$(git rev-parse HEAD) | |
| echo "HEAD after merge: ${AFTER_SHA}" | |
| if [[ "${BEFORE_SHA}" != "${AFTER_SHA}" ]]; then | |
| echo "✅ New bottle commit created by brew bottle --write" | |
| echo "bottle_updated=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "ℹ️ No new commit (bottles unchanged or already present)" | |
| echo "bottle_updated=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Push bottle updates if changed | |
| if: steps.merge.outputs.bottle_updated == 'true' | |
| run: | | |
| set -euo pipefail | |
| echo "Pushing bottle update commit..." | |
| git log -1 --stat | |
| git config --local --unset-all http.https://github.com/.extraheader || true | |
| git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git | |
| git push origin HEAD:main | |
| - name: Create or update GitHub Release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| tag_name: ${{ steps.meta.outputs.tag }} | |
| name: MFC bottles ${{ steps.meta.outputs.version }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Upload bottles to Release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| tag_name: ${{ steps.meta.outputs.tag }} | |
| files: bottles/*.bottle.* | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |