Skip to content

feat(security): W1.1 — modern password hashing via PBKDF2-SHA256 #260

feat(security): W1.1 — modern password hashing via PBKDF2-SHA256

feat(security): W1.1 — modern password hashing via PBKDF2-SHA256 #260

Workflow file for this run

name: Build flAPI
permissions:
checks: write
contents: write
packages: write
issues: write
pull-requests: write
id-token: write
on:
push:
branches: [ main ]
tags: [ 'v*' ]
pull_request:
branches: [ main ]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
# -------------------------------------------------------------------------------------------------
# Windows Build (AMD64)
# -------------------------------------------------------------------------------------------------
windows-build:
runs-on: windows-latest
env:
VCPKG_DEFAULT_TRIPLET: x64-windows-static
VCPKG_ROOT: ${{ github.workspace }}\\vcpkg
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Setup MSVC
uses: microsoft/setup-msbuild@v2
- name: Install vcpkg
shell: pwsh
run: |
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat -disableMetrics
- name: Setup Ccache
uses: hendrikmuhs/ccache-action@main
with:
key: ${{ github.job }}-windows
- name: Configure
shell: pwsh
run: |
mkdir build\release
cd build\release
cmake -G "Visual Studio 17 2022" -A x64 `
-DCMAKE_BUILD_TYPE=Release `
-DBUILD_TESTING=OFF `
-DVCPKG_TARGET_TRIPLET=x64-windows-static `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT\scripts\buildsystems\vcpkg.cmake" `
../..
- name: Build
shell: pwsh
run: |
cd build\release
cmake --build . --config Release
- uses: actions/upload-artifact@v4
with:
name: flapi-windows-amd64
path: build/release/Release/flapi.exe
if-no-files-found: error
# -------------------------------------------------------------------------------------------------
# Linux Matrix Build (AMD64 + ARM64)
# -------------------------------------------------------------------------------------------------
linux-build:
runs-on: ubuntu-24.04
strategy:
matrix:
arch: [amd64, arm64]
env:
BUILD_ARCH: ${{ matrix.arch }}
BUILD_IMAGE: flapi_build_${{ matrix.arch }}
FLAPI_CROSS_COMPILE: ${{ matrix.arch == 'arm64' && 'arm64' || '' }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Setup Ccache
uses: hendrikmuhs/ccache-action@main
with:
key: ${{ github.job }}-${{ matrix.arch }}
- name: Build Docker image
shell: bash
run: |
docker build \
-t ${BUILD_IMAGE} \
-f .github/docker/linux_${BUILD_ARCH}/Dockerfile .
- name: Run build in docker
shell: bash
run: |
docker run \
${{ matrix.arch == 'arm64' && format('-e FLAPI_CROSS_COMPILE="{0}"', matrix.arch) || '' }} \
-v `pwd`:/build_dir \
-v ~/.ccache:/ccache_dir \
${BUILD_IMAGE} \
bash -c 'cd /build_dir && make clean && make release ${{ matrix.arch != 'arm64' && '&& make test' || '' }}'
- uses: actions/upload-artifact@v4
with:
name: flapi-linux-${{ matrix.arch }}
path: build/release/flapi
if-no-files-found: error
# -------------------------------------------------------------------------------------------------
# Docker Build and Push Multi-Arch
# -------------------------------------------------------------------------------------------------
docker-build:
needs: [linux-build]
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Prepare
run: echo "REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ env.REPO_OWNER }}/flapi
tags: |
type=raw,value=latest
type=sha,format=long
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Prepare artifacts
run: |
mkdir -p binaries/amd64 binaries/arm64
cp artifacts/flapi-linux-amd64/flapi binaries/amd64/
cp artifacts/flapi-linux-arm64/flapi binaries/arm64/
chmod +x binaries/amd64/flapi binaries/arm64/flapi
- name: Build and push multi-arch image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-contexts: |
build=binaries
# -------------------------------------------------------------------------------------------------
# macOS Universal
# -------------------------------------------------------------------------------------------------
osx-universal-build:
runs-on: macos-latest
env:
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Ccache
uses: hendrikmuhs/ccache-action@main
with:
key: ${{ github.job }}
- name: Install dependencies
shell: bash
run: brew install ninja autoconf make libtool pkg-config automake autoconf-archive
- name: Install vcpkg
shell: bash
run: |
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh -disableMetrics
- name: Build
run: make release-arm64 && make test
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: flapi-macos-arm64
path: build/release-arm64/flapi
if-no-files-found: error
retention-days: 90
# -------------------------------------------------------------------------------------------------
# flapii CLI Build (Bun standalone binaries)
# -------------------------------------------------------------------------------------------------
flapii-build:
runs-on: ubuntu-24.04
strategy:
matrix:
include:
- target: bun-linux-x64
artifact: flapii-linux-amd64
binary: flapii
- target: bun-linux-arm64
artifact: flapii-linux-arm64
binary: flapii
- target: bun-darwin-arm64
artifact: flapii-macos-arm64
binary: flapii
- target: bun-windows-x64
artifact: flapii-windows-amd64
binary: flapii.exe
steps:
- uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
- name: Install dependencies
working-directory: cli
run: bun install
- name: Rewrite shared import to relative path
run: |
sed -i "s|from '@flapi/shared'|from '../../shared/src/index'|" cli/src/lib/validation.ts
- name: Build standalone binary
working-directory: cli
run: bun build --compile --minify --target=${{ matrix.target }} src/index.ts --outfile ${{ matrix.binary }}
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: cli/${{ matrix.binary }}
if-no-files-found: error
# -------------------------------------------------------------------------------------------------
# Integration Tests (Linux AMD64)
# -------------------------------------------------------------------------------------------------
integration-tests:
needs: [linux-build]
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Download flapi binary
uses: actions/download-artifact@v4
with:
name: flapi-linux-amd64
path: build/release
- name: Make binary executable
run: chmod +x build/release/flapi
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "latest"
- name: Install test dependencies
working-directory: test/integration
run: |
uv venv
uv pip install -e .
- name: Run integration tests
working-directory: test/integration
timeout-minutes: 20
run: |
source .venv/bin/activate
pytest --tb=short -v --timeout=180 \
--ignore=test_load_testing.py \
--junitxml=../../integration_test_results.xml \
2>&1 | tee ../../integration_test_output.log
continue-on-error: true
- name: Capture server logs on failure
if: failure()
run: |
echo "=== Server log files ==="
find /tmp/flapi_* -name "*.log" -exec echo "--- {} ---" \; -exec cat {} \; 2>/dev/null || echo "No server logs found"
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: integration-test-results
path: |
integration_test_results.xml
integration_test_output.log
retention-days: 30
- name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: integration_test_results.xml
check_name: Integration Tests
comment_mode: off
# -------------------------------------------------------------------------------------------------
# Smoke Tests — start each platform binary and verify HTTP health before any release
# -------------------------------------------------------------------------------------------------
smoke-test-linux-amd64:
needs: [linux-build]
runs-on: ubuntu-24.04
steps:
- name: Download binary
uses: actions/download-artifact@v4
with:
name: flapi-linux-amd64
path: smoke
- name: Start server and health-check
env:
SMOKE_CONFIG: |
project-name: smoke-test
project-description: CI smoke test
template:
path: ./sqls
duckdb:
db_path: ./smoke-test.duckdb
access_mode: READ_WRITE
threads: 1
max_memory: 256MB
mcp:
enabled: false
run: |
cd smoke
chmod +x flapi
mkdir -p sqls
printf '%s' "$SMOKE_CONFIG" > smoke-config.yaml
DATAZOO_DISABLE_TELEMETRY=1 ./flapi -c smoke-config.yaml -p 18080 \
--config-service --config-service-token smoke-test \
> server.log 2>&1 &
FLAPI_PID=$!
healthy=false
for i in $(seq 1 30); do
sleep 1
if curl -sf --max-time 3 http://localhost:18080/api/v1/_config/health > /dev/null; then
echo "Healthy after ${i}s"
healthy=true
break
fi
done
kill $FLAPI_PID 2>/dev/null; wait $FLAPI_PID 2>/dev/null || true
if [ "$healthy" != "true" ]; then
echo "=== server logs ===" && cat server.log
exit 1
fi
smoke-test-linux-arm64:
needs: [linux-build]
runs-on: ubuntu-24.04-arm
steps:
- name: Download binary
uses: actions/download-artifact@v4
with:
name: flapi-linux-arm64
path: smoke
- name: Start server and health-check
env:
SMOKE_CONFIG: |
project-name: smoke-test
project-description: CI smoke test
template:
path: ./sqls
duckdb:
db_path: ./smoke-test.duckdb
access_mode: READ_WRITE
threads: 1
max_memory: 256MB
mcp:
enabled: false
run: |
cd smoke
chmod +x flapi
mkdir -p sqls
printf '%s' "$SMOKE_CONFIG" > smoke-config.yaml
DATAZOO_DISABLE_TELEMETRY=1 ./flapi -c smoke-config.yaml -p 18080 \
--config-service --config-service-token smoke-test \
> server.log 2>&1 &
FLAPI_PID=$!
healthy=false
for i in $(seq 1 30); do
sleep 1
if curl -sf --max-time 3 http://localhost:18080/api/v1/_config/health > /dev/null; then
echo "Healthy after ${i}s"
healthy=true
break
fi
done
kill $FLAPI_PID 2>/dev/null; wait $FLAPI_PID 2>/dev/null || true
if [ "$healthy" != "true" ]; then
echo "=== server logs ===" && cat server.log
exit 1
fi
smoke-test-macos:
needs: [osx-universal-build]
runs-on: macos-latest
steps:
- name: Download binary
uses: actions/download-artifact@v4
with:
name: flapi-macos-arm64
path: smoke
- name: Start server and health-check
env:
SMOKE_CONFIG: |
project-name: smoke-test
project-description: CI smoke test
template:
path: ./sqls
duckdb:
db_path: ./smoke-test.duckdb
access_mode: READ_WRITE
threads: 1
max_memory: 256MB
mcp:
enabled: false
run: |
cd smoke
chmod +x flapi
mkdir -p sqls
printf '%s' "$SMOKE_CONFIG" > smoke-config.yaml
DATAZOO_DISABLE_TELEMETRY=1 ./flapi -c smoke-config.yaml -p 18080 \
--config-service --config-service-token smoke-test \
> server.log 2>&1 &
FLAPI_PID=$!
healthy=false
for i in $(seq 1 30); do
sleep 1
if curl -sf --max-time 3 http://localhost:18080/api/v1/_config/health > /dev/null; then
echo "Healthy after ${i}s"
healthy=true
break
fi
done
kill $FLAPI_PID 2>/dev/null; wait $FLAPI_PID 2>/dev/null || true
if [ "$healthy" != "true" ]; then
echo "=== server logs ===" && cat server.log
exit 1
fi
smoke-test-windows:
needs: [windows-build]
runs-on: windows-latest
steps:
- name: Download binary
uses: actions/download-artifact@v4
with:
name: flapi-windows-amd64
path: smoke
- name: Start server and health-check
env:
SMOKE_CONFIG: |
project-name: smoke-test
project-description: CI smoke test
template:
path: ./sqls
duckdb:
db_path: ./smoke-test.duckdb
access_mode: READ_WRITE
threads: 1
max_memory: 256MB
mcp:
enabled: false
shell: pwsh
run: |
New-Item -ItemType Directory -Force smoke\sqls | Out-Null
$env:SMOKE_CONFIG | Out-File -FilePath smoke\smoke-config.yaml -Encoding UTF8
$env:DATAZOO_DISABLE_TELEMETRY = "1"
$p = Start-Process `
-FilePath "smoke\flapi.exe" `
-ArgumentList @("-c", "smoke-config.yaml", "-p", "18080", "--config-service", "--config-service-token", "smoke-test") `
-WorkingDirectory smoke `
-RedirectStandardOutput smoke\stdout.log `
-RedirectStandardError smoke\stderr.log `
-PassThru
Write-Host "flapi.exe PID: $($p.Id)"
$healthy = $false
for ($i = 1; $i -le 30; $i++) {
Start-Sleep -Seconds 1
try {
$r = Invoke-WebRequest -Uri "http://localhost:18080/api/v1/_config/health" `
-UseBasicParsing -TimeoutSec 3 -ErrorAction Stop
if ($r.StatusCode -eq 200) {
Write-Host "Healthy after ${i}s"
$healthy = $true
break
}
} catch {}
}
Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue
if (-not $healthy) {
Write-Host "=== stdout ===" ; Get-Content smoke\stdout.log -ErrorAction SilentlyContinue
Write-Host "=== stderr ===" ; Get-Content smoke\stderr.log -ErrorAction SilentlyContinue
exit 1
}
# -------------------------------------------------------------------------------------------------
# Create GitHub Release + PyPI Wheels (tag pushes only)
# -------------------------------------------------------------------------------------------------
create-release:
if: startsWith(github.ref, 'refs/tags/v')
needs: [windows-build, linux-build, osx-universal-build, flapii-build, smoke-test-windows, smoke-test-linux-amd64, smoke-test-linux-arm64, smoke-test-macos]
runs-on: ubuntu-24.04
permissions:
contents: write
packages: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get tag version
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Download all build artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
pattern: flapi-*
merge-multiple: false
- name: Download flapii artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
pattern: flapii-*
merge-multiple: false
- name: Strip binaries
run: |
# Only strip flapi (C++ binary). Do NOT strip flapii — Bun standalone
# binaries embed JavaScript at the end of the ELF, and strip corrupts it.
# Skip Windows (MSVC Release is already stripped).
for bin in \
./artifacts/flapi-linux-amd64/flapi \
./artifacts/flapi-linux-arm64/flapi \
./artifacts/flapi-macos-arm64/flapi; do
echo "Stripping $bin ($(du -h "$bin" | cut -f1) before)"
llvm-strip "$bin" 2>/dev/null || strip "$bin" 2>/dev/null || echo " skipped (unsupported format)"
echo " -> $(du -h "$bin" | cut -f1) after"
done
- name: Prepare release assets
run: |
chmod +x ./artifacts/flapi-linux-amd64/flapi
chmod +x ./artifacts/flapi-linux-arm64/flapi
chmod +x ./artifacts/flapi-macos-arm64/flapi
cd artifacts/flapi-windows-amd64 && zip ../flapi-windows-amd64.zip flapi.exe && cd ../..
cd artifacts/flapi-linux-amd64 && tar czf ../flapi-linux-amd64.tar.gz flapi && cd ../..
cd artifacts/flapi-linux-arm64 && tar czf ../flapi-linux-arm64.tar.gz flapi && cd ../..
cd artifacts/flapi-macos-arm64 && tar czf ../flapi-macos-arm64.tar.gz flapi && cd ../..
- name: Install bin-to-wheel
run: pip install "bin-to-wheel @ git+https://github.com/DataZooDE/bin-to-wheel.git@main"
- name: Build flapi-io wheels (flapi + flapii)
run: |
WHEEL_VERSION="${{ steps.get_version.outputs.VERSION }}"
WHEEL_VERSION="${WHEEL_VERSION#v}"
mkdir -p dist/flapi-io
chmod +x ./artifacts/flapii-linux-amd64/flapii
chmod +x ./artifacts/flapii-linux-arm64/flapii
chmod +x ./artifacts/flapii-macos-arm64/flapii
bin-to-wheel --name flapi-io --version "$WHEEL_VERSION" \
--binary ./artifacts/flapi-linux-amd64/flapi --entry-point flapi \
--binary ./artifacts/flapii-linux-amd64/flapii --entry-point flapii \
--platform linux-x86_64 --output-dir dist/flapi-io \
--description "SQL-to-API server" --author "DataZoo GmbH" \
--license Apache-2.0 --url https://github.com/DataZooDE/flapi \
--readme ./Readme.md
bin-to-wheel --name flapi-io --version "$WHEEL_VERSION" \
--binary ./artifacts/flapi-linux-arm64/flapi --entry-point flapi \
--binary ./artifacts/flapii-linux-arm64/flapii --entry-point flapii \
--platform linux-arm64 --output-dir dist/flapi-io \
--description "SQL-to-API server" --author "DataZoo GmbH" \
--license Apache-2.0 --url https://github.com/DataZooDE/flapi \
--readme ./Readme.md
bin-to-wheel --name flapi-io --version "$WHEEL_VERSION" \
--binary ./artifacts/flapi-macos-arm64/flapi --entry-point flapi \
--binary ./artifacts/flapii-macos-arm64/flapii --entry-point flapii \
--platform macos-arm64 --output-dir dist/flapi-io \
--description "SQL-to-API server" --author "DataZoo GmbH" \
--license Apache-2.0 --url https://github.com/DataZooDE/flapi \
--readme ./Readme.md
bin-to-wheel --name flapi-io --version "$WHEEL_VERSION" \
--binary ./artifacts/flapi-windows-amd64/flapi.exe --entry-point flapi \
--binary ./artifacts/flapii-windows-amd64/flapii.exe --entry-point flapii \
--platform windows-x64 --output-dir dist/flapi-io \
--description "SQL-to-API server" --author "DataZoo GmbH" \
--license Apache-2.0 --url https://github.com/DataZooDE/flapi \
--readme ./Readme.md
- name: Upload flapi-io wheels
uses: actions/upload-artifact@v4
with:
name: pypi-wheels-flapi-io
path: dist/flapi-io/*.whl
- name: Create Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG_MSG=$(git tag -l --format='%(contents)' ${{ steps.get_version.outputs.VERSION }})
if [ -z "$TAG_MSG" ]; then
TAG_MSG="Release ${{ steps.get_version.outputs.VERSION }}"
fi
gh release create ${{ steps.get_version.outputs.VERSION }} \
./artifacts/flapi-windows-amd64.zip \
./artifacts/flapi-linux-amd64.tar.gz \
./artifacts/flapi-linux-arm64.tar.gz \
./artifacts/flapi-macos-arm64.tar.gz \
./dist/flapi-io/*.whl \
--title "${{ steps.get_version.outputs.VERSION }}" \
--notes "$TAG_MSG" \
--draft=false \
--prerelease=false
pypi-publish-flapi-io:
if: startsWith(github.ref, 'refs/tags/v')
needs: create-release
runs-on: ubuntu-latest
name: Publish flapi-io to PyPI
environment:
name: pypi-flapi-io
url: https://pypi.org/p/flapi-io
permissions:
id-token: write
steps:
- name: Download wheels
uses: actions/download-artifact@v4
with:
name: pypi-wheels-flapi-io
path: dist
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1