Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
178 changes: 73 additions & 105 deletions .github/workflows/bottle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ name: Bottle

on:
push:
branches:
- main
paths:
- "Formula/**"
- ".github/workflows/bottle.yml"
workflow_dispatch:

permissions:
Expand All @@ -30,31 +25,28 @@ jobs:
- name: Set up Homebrew
uses: Homebrew/actions/setup-homebrew@master

# IMPORTANT: do NOT untap mflowcode/mfc in this job. setup-homebrew manages it and
# its post-cleanup expects the tap directory to exist.
- name: Ensure no leftover local tap
shell: bash
run: |
set -euo pipefail
brew untap mflowcode/local >/dev/null 2>&1 || true

- name: Determine version and root URL (from checked-out formula)
id: meta
shell: bash
run: |
set -euo pipefail

FORMULA="Formula/mfc.rb"

# Take the *last* matching URL (guards against accidental duplicates)
URL="$(
grep -E '^\s*url\s+"https://github.com/.*/archive/refs/tags/v[0-9]+\.[0-9]+\.[0-9]+\.tar\.gz"' "${FORMULA}" \
| sed -E 's/^\s*url\s+"([^"]+)".*/\1/' \
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
echo "Expected a line like: url \"https://github.com/<org>/<repo>/archive/refs/tags/vX.Y.Z.tar.gz\"" >&2
exit 1
fi
[[ -n "${URL}" ]]

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
[[ -n "${VERSION}" ]]

TAG="mfc-${VERSION}"
ROOT_URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}"
Expand All @@ -68,74 +60,68 @@ jobs:
echo "Release tag: ${TAG}"
echo "Bottle root_url: ${ROOT_URL}"

- name: Install formula with --build-bottle (from checked-out formula via temp tap)
- name: Install formula with --build-bottle (tap formula)
shell: bash
run: |
set -euo pipefail

echo "Installing mfc with --build-bottle from checked-out Formula/mfc.rb via a temporary local tap..."

# 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"

# Homebrew may exit non-zero due to dylib fixup issues in Python wheels,
# even though the formula actually installed correctly. Treat that as
# non-fatal as long as the formula is present.
echo "Installing mfc with --build-bottle (allowing non-fatal dylib fixup failures)..."

set +e
brew install --build-bottle mflowcode/local/mfc 2>&1 | tee /tmp/brew-install.log
brew install --build-bottle mflowcode/mfc/mfc 2>&1 | tee /tmp/brew-install.log
brew_exit_code=$?
set -e
if brew list mfc &>/dev/null; then

if brew list mflowcode/mfc/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
shell: bash
run: |
set -euo pipefail
echo "Running a simple test case (1D Sod shock tube) on ${{ matrix.os }}..."

TESTDIR=$(mktemp -d)
cp "$(brew --prefix mfc)/examples/1D_sodshocktube/case.py" "$TESTDIR/"

TESTDIR="$(mktemp -d)"
cp "$(brew --prefix mflowcode/mfc/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
shell: bash
run: |
set -euo pipefail
PREFIX="$(brew --prefix mflowcode/mfc/mfc)"

echo "1. Checking binaries..."
test -x "$(brew --prefix mfc)/bin/pre_process"
test -x "$(brew --prefix mfc)/bin/simulation"
test -x "$(brew --prefix mfc)/bin/post_process"
test -x "$(brew --prefix mfc)/bin/mfc"
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 "$(brew --prefix mfc)/toolchain"
test -d "${PREFIX}/toolchain"

echo "3. Checking Python venv..."
test -d "$(brew --prefix mfc)/libexec/venv"
test -x "$(brew --prefix mfc)/libexec/venv/bin/python"
test -d "${PREFIX}/libexec/venv"
test -x "${PREFIX}/libexec/venv/bin/python"

echo "4. Checking examples..."
test -d "$(brew --prefix mfc)/examples"
test -d "${PREFIX}/examples"

echo "5. Testing mfc command..."
mfc --help

echo "✅ All verification checks passed on ${{ matrix.os }}"

- name: Build bottle
- name: Build bottle (tap formula)
shell: bash
run: |
set -euo pipefail
brew bottle --root-url="${{ steps.meta.outputs.root_url }}" --json mfc
brew bottle --root-url="${{ steps.meta.outputs.root_url }}" --json mflowcode/mfc/mfc
ls -1 *.bottle.*

- name: Upload bottle artifacts
Expand All @@ -161,125 +147,107 @@ jobs:

- name: Determine version and root URL (from checked-out formula)
id: meta
shell: bash
run: |
set -euo pipefail

FORMULA="Formula/mfc.rb"

URL="$(
grep -E '^\s*url\s+"https://github.com/.*/archive/refs/tags/v[0-9]+\.[0-9]+\.[0-9]+\.tar\.gz"' "${FORMULA}" \
| sed -E 's/^\s*url\s+"([^"]+)".*/\1/' \
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

[[ -n "${URL}" ]]

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

[[ -n "${VERSION}" ]]

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
shell: bash
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
shell: bash
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
shell: bash
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: Merge bottle metadata into formula
id: merge
shell: bash
run: |
set -euo pipefail

shopt -s nullglob
JSON_BOTTLES=(bottles/*.bottle*.json)
if [[ ! -e "${JSON_BOTTLES[0]}" ]]; then
if (( ${#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 "${JSON_BOTTLES[@]}"

AFTER_SHA=$(git rev-parse HEAD)
echo "HEAD after merge: ${AFTER_SHA}"


BEFORE_SHA="$(git rev-parse HEAD)"
brew bottle --merge --write --root-url="${{ steps.meta.outputs.root_url }}" "${JSON_BOTTLES[@]}"
AFTER_SHA="$(git rev-parse HEAD)"

if [[ "${BEFORE_SHA}" != "${AFTER_SHA}" ]]; then
echo "✅ New bottle commit created by brew bottle --write"
echo "✅ brew bottle --write created commit ${AFTER_SHA}"
echo "bottle_updated=true" >> "$GITHUB_OUTPUT"
else
echo "ℹ️ No new commit (bottles unchanged or already present)"
echo "ℹ️ No commit created (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'
shell: bash
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 remote set-url origin "https://x-access-token:${{ github.token }}@github.com/${{ github.repository }}.git"
git push origin HEAD:main

- name: Create or update GitHub Release
- name: Create/update release and upload bottles
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.*
fail_on_unmatched_files: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ github.token }}
16 changes: 10 additions & 6 deletions Formula/mfc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,11 @@ def install
# Create Python virtual environment inside libexec (inside Cellar for proper bottling)
venv = libexec/"venv"
system Formula["python@3.12"].opt_bin/"python3.12", "-m", "venv", venv

system venv/"bin/pip", "install", "--upgrade",
"pip", "setuptools", "wheel",
"setuptools-scm",
"hatchling", "hatch-vcs",
"editables"

# Install Cantera from PyPI using pre-built wheel (complex package, doesn't need custom flags)
# Cantera has CMake compatibility issues when building from source with newer CMake versions
# Match the version constraint from toolchain/pyproject.toml
Expand All @@ -47,8 +45,9 @@ def install
# Keep toolchain in buildpath for now - mfc.sh needs it there
#
# MFC's toolchain uses VCS-derived versioning (via Hatch/hatch-vcs) and Homebrew builds from
# GitHub release tarballs without a .git directory. Scope pretend-version env vars tightly
# to avoid polluting subsequent pip installs.
# GitHub release tarballs without a .git directory. Use --no-build-isolation so the build
# backend can see our environment variables, and set SETUPTOOLS_SCM_PRETEND_VERSION which
# hatch-vcs respects when VCS metadata is unavailable.
pretend_env = {
"SETUPTOOLS_SCM_PRETEND_VERSION_FOR_MFC" => version.to_s,
"SETUPTOOLS_SCM_PRETEND_VERSION_FOR_mfc" => version.to_s,
Expand All @@ -61,7 +60,7 @@ def install
end

begin
system venv/"bin/pip", "install", "-e", buildpath/"toolchain"
system venv/"bin/pip", "install", "--no-build-isolation", "-e", buildpath/"toolchain"
ensure
pretend_env.each_key do |k|
if saved_env[k].nil?
Expand All @@ -79,7 +78,12 @@ def install
# Now build MFC with pre-configured venv
# Set VIRTUAL_ENV so mfc.sh uses existing venv instead of creating new one
ENV["VIRTUAL_ENV"] = venv


# Also set pretend-version env vars for mfc.sh in case it tries to reinstall toolchain
ENV["SETUPTOOLS_SCM_PRETEND_VERSION_FOR_MFC"] = version.to_s
ENV["SETUPTOOLS_SCM_PRETEND_VERSION_FOR_mfc"] = version.to_s
ENV["SETUPTOOLS_SCM_PRETEND_VERSION"] = version.to_s

# Build MFC using pre-configured venv
# Must run from buildpath (MFC root directory) where toolchain/ exists
Dir.chdir(buildpath) do
Expand Down
Loading