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
133 changes: 87 additions & 46 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,85 @@ jobs:
!contains(github.event.head_commit.message, 'ci skip')
runs-on: ${{ matrix.os }}
outputs:
result: ${{ steps.test.outcome }}
result: ${{ startsWith(matrix.os, 'ubuntu') && steps.ubuntu-test.outcome || steps.test.outcome }}

strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
os: [ubuntu-24.04, windows-2022, macos-14]
python-version: ['3.8', '3.12']

steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event_name == 'workflow_call' && inputs.ref || github.ref }}

- name: Enable Developer Command Prompt
uses: ilammy/msvc-dev-cmd@v1.12.1
- name: Get runner UID/GID
if: startsWith(matrix.os, 'ubuntu')
id: runner-info
run: echo "uid=$(id -u)" >> $GITHUB_OUTPUT && echo "gid=$(id -g)" >> $GITHUB_OUTPUT

- name: Build in container for Ubuntu 20.04 (for broader compatibility)
if: startsWith(matrix.os, 'ubuntu')
uses: addnab/docker-run-action@v3
with:
image: ubuntu:20.04
options: -v ${{ github.workspace }}:/home/runner/funnel
shell: bash
run: |
export DEBIAN_FRONTEND=noninteractive
export TZ=UTC
apt update
apt install -y software-properties-common build-essential wget
# Python install and venv setup
if [ "${{ matrix.python-version }}" == "3.8" ]; then
apt install -y python3 python3-pip python3-venv
PYTHON_CMD=python3
else
add-apt-repository ppa:deadsnakes/ppa
apt update && apt install -y \
python${{ matrix.python-version }} \
python${{ matrix.python-version }}-venv \
python${{ matrix.python-version }}-dev \
curl
PYTHON_CMD=python${{ matrix.python-version }}
# Install pip for the specific Python version
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
$PYTHON_CMD get-pip.py
rm get-pip.py
fi
# Create group and user matching the runner
groupadd -g ${{ steps.runner-info.outputs.gid }} runnergroup
useradd -u ${{ steps.runner-info.outputs.uid }} -g runnergroup -m runner
chown -R runner:runnergroup /home/runner
# Build and test funnel as runner
su runner << 'EOF'
# CMake install
cd /home/runner
CMAKE_VERSION=3.22.6
wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.tar.gz
tar -xzf cmake-${CMAKE_VERSION}-linux-x86_64.tar.gz
export PATH=$PWD/cmake-${CMAKE_VERSION}-linux-x86_64/bin:$PATH
echo "CMake version: " && cmake --version
# Create and activate virtual environment
$PYTHON_CMD -m venv /venv
source /venv/bin/activate
echo "Python version: " && python --version
cd /home/runner/funnel
pip install --upgrade pip
pip install ".[dev]" # In Python 3.8 editable mode still requires setup.py
mkdir -p build && cd build && cmake ..
cmake --build . --target install --config ${{ env.BUILD_TYPE }}
ctest -C ${{ env.BUILD_TYPE }} --verbose
cd /home/runner/funnel
rm -rf pyfunnel.egg-info
EOF

- name: Enable Developer Command Prompt on Windows
if: startsWith(matrix.os, 'windows')
uses: ilammy/msvc-dev-cmd@v1.12.1

- name: Setup cmake
if: ${{ !startsWith(matrix.os, 'ubuntu') }}
uses: jwlawson/actions-setup-cmake@v1.13
with:
cmake-version: '3.22.x'
Expand All @@ -58,68 +120,47 @@ jobs:
python -m pip install --user -e ".[dev]"

- name: Create Build Environment
if: ${{ !startsWith(matrix.os, 'ubuntu') }}
# Some projects don't allow in-source building, so create a separate build directory
# We'll use this as our working directory for all subsequent commands.
run: |
cmake --version
cmake -E make_directory ${{ github.workspace }}/build

- name: Configure CMake
if: ${{ !startsWith(matrix.os, 'ubuntu') }}
# Use a bash shell so we can use the same syntax for environment variable
# access regardless of the host operating system
shell: bash
working-directory: ${{ github.workspace }}/build
# Note the current convention is to use the -S and -B options here to specify source
# and build directories, but this is only available with CMake 3.13 and higher.
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
run: cmake ..

- name: Build
id: build
if: ${{ !startsWith(matrix.os, 'ubuntu') }}
working-directory: ${{ github.workspace }}/build
shell: bash
run: cmake --build . --target install --config $BUILD_TYPE
run: cmake --build . --target install --config ${{ env.BUILD_TYPE }}

- name: Test
id: test
if: ${{ !startsWith(matrix.os, 'ubuntu') }}
working-directory: ${{ github.workspace }}/build
shell: bash
run: ctest -C $BUILD_TYPE --verbose

- name: Stage binaries
if: |
contains(matrix.python-version, '3.12') &&
(
(github.event_name == 'push' && github.ref == 'refs/heads/master') ||
(github.event_name == 'workflow_call')
)
run: |
git config --global pull.rebase true
git add ${{ github.workspace }}/pyfunnel/lib

- name: Push Linux & macOS binaries
if: |
(contains(matrix.os, 'ubuntu') || matrix.os == 'macos-latest') &&
contains(matrix.python-version, '3.12') &&
(
(github.event_name == 'push' && github.ref == 'refs/heads/master') ||
(github.event_name == 'workflow_call')
)
run: |
git config --global user.name "${GH_USERNAME}"
git config --global user.email "${GH_USERNAME}@users.noreply.github.com"
git diff-index --quiet HEAD || { git commit -m "Add ${{ matrix.os }} binaries" && git pull && git push; }

# We use cmd /c because powershell <7 does not support chain operators.
- name: Push Windows binaries
if: |
matrix.os == 'windows-latest' &&
contains(matrix.python-version, '3.12') &&
(
(github.event_name == 'push' && github.ref == 'refs/heads/master') ||
(github.event_name == 'workflow_call')
)
run: ctest -C ${{ env.BUILD_TYPE }} --verbose

- name: Test simple test in latest Ubuntu
id: ubuntu-test
if: startsWith(matrix.os, 'ubuntu')
run: |
git config --global user.name "${{ env.GH_USERNAME }}"
git config --global user.email "${{ env.GH_USERNAME }}@users.noreply.github.com"
cmd /c 'git diff-index --quiet HEAD || ( git commit -m "Add ${{ matrix.os }} binaries" && git pull && git push )'
# Run the test from the directory with test data
cd tests/test_bin && funnel --reference trended.csv --test simulated.csv --atolx 0.002 --atoly 0.002

- name: Upload platform-specific binaries as artifacts
if: contains(matrix.python-version, '3.12')
uses: actions/upload-artifact@v4
with:
name: binaries-${{ matrix.os }}
path: ${{ github.workspace }}/pyfunnel/lib/
retention-days: 30
if-no-files-found: error
10 changes: 0 additions & 10 deletions .github/workflows/master-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,6 @@ jobs:
exit 1
fi

- name: Check for binaries
run: |
if [ -d "pyfunnel/lib" ] && [ "$(ls -A pyfunnel/lib)" ]; then
echo "✅ Binaries found in pyfunnel/lib"
ls -la pyfunnel/lib/
else
echo "❌ No binaries found in pyfunnel/lib"
exit 1
fi

merge-to-master:
needs: validate-staging
runs-on: ubuntu-latest
Expand Down
31 changes: 26 additions & 5 deletions .github/workflows/staging-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ jobs:
with:
ref: staging

- name: Download all binary artifacts
uses: actions/download-artifact@v4
with:
pattern: binaries-*
path: pyfunnel/lib/
merge-multiple: true

- name: Debug - List downloaded artifacts
run: |
echo "Contents of pyfunnel/lib/ after artifact download:"
find pyfunnel/lib/ -type f -ls || echo "No files found"

- name: Setup Python
uses: actions/setup-python@v4
with:
Expand Down Expand Up @@ -112,6 +124,14 @@ jobs:
uses: actions/checkout@v4
with:
ref: staging
token: ${{ secrets.GITHUB_TOKEN }}

- name: Download all binary artifacts
uses: actions/download-artifact@v4
with:
pattern: binaries-*
path: pyfunnel/lib/
merge-multiple: true

- name: Install from Test PyPI
if: github.event.inputs.installation_mode == 'test-pypi'
Expand All @@ -121,14 +141,14 @@ jobs:

# Install from Test PyPI
# The `--extra-index-url https://pypi.org/simple/` ensures dependencies are installed from regular PyPI
python -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ pyfunnel==${{ needs.version-bump.outputs.new-release-version }}
python -m pip install --user --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ pyfunnel==${{ needs.version-bump.outputs.new-release-version }}

- name: Install from source
if: github.event.inputs.installation_mode == 'source'
run: |
# Install from source
python -m pip install --upgrade pip
python -m pip install .
python -m pip install --user --upgrade pip
python -m pip install --user .

- name: Run test
run: |
Expand All @@ -140,6 +160,9 @@ jobs:

- name: Create staging validation marker
run: |
git config --global user.name "${GH_USERNAME}"
git config --global user.email "${GH_USERNAME}@users.noreply.github.com"

# Create validation marker indicating successful staging pipeline
echo "Staging pipeline completed successfully on $(date)" > .staging-validated
echo "Version: ${{ needs.version-bump.outputs.new-release-version }}" >> .staging-validated
Expand All @@ -150,8 +173,6 @@ jobs:
fi
echo "Binaries: Built and committed" >> .staging-validated

git config --global user.name "${GH_USERNAME}"
git config --global user.email "${GH_USERNAME}@users.noreply.github.com"
git add .staging-validated
git commit -m "chore: mark staging pipeline as validated"
git push origin staging
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Binaries
*.py[cod]
pyfunnel/lib

# Object files
*.o
Expand Down
14 changes: 5 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ By convention, the error is `max(0, y - y_up) - min(0, y - y_low)` and hence it

The software is tested on the following platforms.

- Linux x64 (Ubuntu 24.04)
- Windows x64 (Windows Server 2022)
- macOS x64 and arm64 (macOS 12)
- Linux x64: Ubuntu 20.04 and 24.04
- Windows x64: Windows Server 2022
- macOS x64 and arm64: macOS 14

A Python binding is available to access the library. It is supported on Python versions 3.8 through 3.12.

Expand All @@ -86,11 +86,7 @@ The package `pyfunnel` provides the following functions.
Displays plot in default browser. See function docstring for further details.

A standalone CLI script `pyfunnel/cli.py` is available, which is also accessible via the
`funnel` entry point when the package is installed.
To access the usage instructions, use one of the following:

- Installed package: `funnel --help`
- Standalone: `python pyfunnel/cli.py --help`
`funnel` entry point when the package is installed. To access the usage instructions, run: `funnel --help`

### Example

Expand All @@ -109,7 +105,7 @@ From a Python shell with `./tests/test_bin` as the current working directory, ru
Or from a terminal with `./tests/test_bin` as the current working directory, run

```bash
python ../../pyfunnel/cli.py --reference trended.csv --test simulated.csv --atolx 0.002 --atoly 0.002
funnel --reference trended.csv --test simulated.csv --atolx 0.002 --atoly 0.002
```

## Build from Source
Expand Down
Binary file removed pyfunnel/lib/darwin64/libfunnel.dylib
Binary file not shown.
Binary file removed pyfunnel/lib/linux64/libfunnel.so
Binary file not shown.
Binary file removed pyfunnel/lib/win64/funnel.dll
Binary file not shown.
Binary file removed pyfunnel/lib/win64/funnel.lib
Binary file not shown.
7 changes: 7 additions & 0 deletions src/mkdir_p.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@
int mkdir_p(const char *path)
{
/* Adapted from http://stackoverflow.com/a/2336245/119527 */

// Validate input
if (strpbrk(path, "*|<>?\"") != NULL) {
errno = EINVAL;
return -1;
}

const size_t len = strlen(path);
char *_path = NULL;
char *p;
Expand Down
Loading