name: Build All Packages
on:
push:
tags:
- '*'
workflow_dispatch:
inputs:
version:
description: 'Version to build (leave empty for auto)'
required: false
type: string
jobs:
prepare:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
is_release: ${{ steps.version.outputs.is_release }}
steps:
- uses: actions/checkout@v4
- name: Determine version
id: version
run: |
if [[ "${GIT_REF}" =~ ^refs/tags/ ]]; then
G4F_VERSION="${REF_NAME}"
IS_RELEASE="true"
elif [[ -n "${{ inputs.version }}" ]]; then
G4F_VERSION="${{ inputs.version }}"
IS_RELEASE="false"
else
G4F_VERSION="0.0.0-dev"
IS_RELEASE="false"
fi
echo "version=${G4F_VERSION}" >> $GITHUB_OUTPUT
echo "is_release=${IS_RELEASE}" >> $GITHUB_OUTPUT
echo "Building version: ${G4F_VERSION}"
env:
GIT_REF: ${{ github.ref }}
REF_NAME: ${{ github.ref_name }}
# PyPI Package
build-pypi:
runs-on: ubuntu-latest
needs: prepare
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install build tools
run: |
python -m pip install --upgrade pip
python -m pip install build twine
- name: Build package
env:
G4F_VERSION: ${{ needs.prepare.outputs.version }}
run: python -m build
- name: Verify package
run: |
python -m twine check dist/*
ls -la dist/
- name: Upload PyPI package artifact (combined)
uses: actions/upload-artifact@v4
with:
name: pypi-package
path: dist/
- name: Upload PyPI build artifacts (individual)
run: |
for f in dist/*; do
echo "Uploading $f"
done
shell: bash
- name: Upload wheel artifact
uses: actions/upload-artifact@v4
with:
name: pypi-wheel
path: dist/*.whl
- name: Upload sdist artifact
uses: actions/upload-artifact@v4
with:
name: pypi-sdist
path: dist/*.tar.gz
# Executables (built with g4f-go, no Nuitka)
# Cross-compiles the Go launcher on ubuntu-latest for every OS/arch.
# The embedded CPython runtime zips are generated by fetch-python.sh
# (linux) / fetched & merged per-platform (windows, darwin, freebsd).
build-g4f-go:
runs-on: ubuntu-latest
needs: prepare
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.22"
cache-dependency-path: g4f-go/go.mod
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install tooling
run: |
python -m pip install --upgrade pip
pip install requests aiohttp brotli pycryptodome nest-asyncio2
sudo apt-get update
sudo apt-get install -y rsync zip
- name: Fetch & merge Python runtimes (all platforms)
working-directory: g4f-go
env:
G4F_VERSION: ${{ needs.prepare.outputs.version }}
run: ./fetch-python.sh
- name: Cross-compile g4f-go launchers (all platforms)
working-directory: g4f-go
env:
G4F_VERSION: ${{ needs.prepare.outputs.version }}
run: ./build-all.sh
- name: Upload g4f-go executables (combined)
uses: actions/upload-artifact@v4
with:
name: g4f-go
path: g4f-go/dist/*
- name: Upload g4f-go executables (individual per platform)
run: |
for zip in g4f-go/dist/*.zip; do
base="$(basename "$zip" .zip)"
echo "Uploading $zip as artifact g4f-go-${base}"
done
shell: bash
- name: Upload g4f-go windows-amd64
uses: actions/upload-artifact@v4
with:
name: g4f-go-windows-amd64
path: g4f-go/dist/g4f-go-*-windows-amd64.zip
if-no-files-found: error
- name: Upload g4f-go linux-amd64
uses: actions/upload-artifact@v4
with:
name: g4f-go-linux-amd64
path: g4f-go/dist/g4f-go-*-linux-amd64.zip
if-no-files-found: error
- name: Upload g4f-go linux-arm64
uses: actions/upload-artifact@v4
with:
name: g4f-go-linux-arm64
path: g4f-go/dist/g4f-go-*-linux-arm64.zip
if-no-files-found: error
- name: Upload g4f-go darwin-arm64
uses: actions/upload-artifact@v4
with:
name: g4f-go-darwin-arm64
path: g4f-go/dist/g4f-go-*-darwin-arm64.zip
if-no-files-found: error
- name: Upload g4f-go darwin-amd64
uses: actions/upload-artifact@v4
with:
name: g4f-go-darwin-amd64
path: g4f-go/dist/g4f-go-*-darwin-amd64.zip
if-no-files-found: error
# Docker Images
build-docker:
runs-on: ubuntu-latest
needs: prepare
if: needs.prepare.outputs.is_release == 'true'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: Get metadata for Docker
id: metadata
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5
with:
images: |
hlohaus789/g4f
- name: Log in to Docker Hub
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push armv7 image
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
with:
context: .
file: docker/Dockerfile-armv7
platforms: linux/arm/v7
push: true
provenance: mode=max
sbom: true
tags: |
hlohaus789/g4f:latest-armv7
hlohaus789/g4f:${{ needs.prepare.outputs.version }}-armv7
labels: ${{ steps.metadata.outputs.labels }}
build-args: |
G4F_VERSION=${{ needs.prepare.outputs.version }}
- name: Build and push slim images
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
with:
context: .
file: docker/Dockerfile-slim
platforms: linux/amd64,linux/arm64
push: true
provenance: mode=max
sbom: true
tags: |
hlohaus789/g4f:latest-slim
hlohaus789/g4f:${{ needs.prepare.outputs.version }}-slim
labels: ${{ steps.metadata.outputs.labels }}
build-args: |
G4F_VERSION=${{ needs.prepare.outputs.version }}
- name: Build and push image
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64
push: true
provenance: mode=max
sbom: true
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
build-args: |
G4F_VERSION=${{ needs.prepare.outputs.version }}
# Release Creation
create-release:
runs-on: ubuntu-latest
needs: [prepare, build-pypi, build-g4f-go]
if: needs.prepare.outputs.is_release == 'true'
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Download PyPI artifacts
uses: actions/download-artifact@v4
with:
name: pypi-package
path: ./release/pypi
- name: Download g4f-go artifacts
uses: actions/download-artifact@v4
with:
name: g4f-go
path: ./release/g4f-go
- name: Download individual g4f-go platform artifacts
uses: actions/download-artifact@v4
with:
path: ./release/g4f-go/individual
pattern: g4f-go-*
merge-multiple: true
- name: Flatten all release assets
run: |
mkdir -p ./release/flat
find ./release -type f | sort
cp -v ./release/pypi/* ./release/flat/ 2>/dev/null || true
cp -v ./release/g4f-go/dist/*.zip ./release/flat/ 2>/dev/null || true
cp -v ./release/g4f-go/individual/* ./release/flat/ 2>/dev/null || true
echo "--- Final release assets ---"
find ./release/flat -type f | sort
- name: Create Release with Assets
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2
with:
tag_name: ${{ needs.prepare.outputs.version }}
name: Release ${{ needs.prepare.outputs.version }}
append_body: true
body: |
### Download Options
**Python Package:**
- PyPI: `pip install g4f==${{ needs.prepare.outputs.version }}`
**Executables (g4f-go, self-contained with embedded Python):**
- Windows amd64: `g4f-go-${{ needs.prepare.outputs.version }}-windows-amd64.zip`
- Linux x64: `g4f-go-${{ needs.prepare.outputs.version }}-linux-amd64.zip`
- Linux ARM64: `g4f-go-${{ needs.prepare.outputs.version }}-linux-arm64.zip`
- macOS x64: `g4f-go-${{ needs.prepare.outputs.version }}-darwin-amd64.zip`
- macOS ARM64: `g4f-go-${{ needs.prepare.outputs.version }}-darwin-arm64.zip`
- FreeBSD amd64: `g4f-go-${{ needs.prepare.outputs.version }}-freebsd-amd64.zip`
**System Packages:**
- WinGet: `winget install gpt4free`
**Docker:**
- `docker pull hlohaus789/g4f:${{ needs.prepare.outputs.version }}`
- `docker pull hlohaus789/g4f:${{ needs.prepare.outputs.version }}-slim`
💻 **Having trouble with the .exe from this release?**
👉 Check out the Windows launcher for GPT4Free instead:
🔗 [https://github.com/gpt4free/g4f.exe](https://github.com/gpt4free/g4f.exe) 🚀
draft: false
prerelease: false
files: ./release/flat/*
token: ${{ secrets.GITHUB_TOKEN }}
# Publish to PyPI (only for releases)
# publish-pypi:
# runs-on: ubuntu-latest
# needs: [prepare, build-pypi, create-release]
# if: needs.prepare.outputs.is_release == 'true'
# environment:
# name: pypi
# url: https://pypi.org/p/g4f
# permissions:
# id-token: write
# steps:
# - name: Download PyPI artifacts
# uses: actions/download-artifact@v4
# with:
# name: pypi-package
# path: dist/
# - name: Publish to PyPI
# uses: pypa/gh-action-pypi-publish@release/v1
name: Build All Packages
on:
push:
tags:
- '*'
workflow_dispatch:
inputs:
version:
description: 'Version to build (leave empty for auto)'
required: false
type: string
jobs:
prepare:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
is_release: ${{ steps.version.outputs.is_release }}
steps:
- uses: actions/checkout@v4
- name: Determine version
id: version
run: |
if [[ "${GIT_REF}" =~ ^refs/tags/ ]]; then
G4F_VERSION="${REF_NAME}"
IS_RELEASE="true"
elif [[ -n "${{ inputs.version }}" ]]; then
G4F_VERSION="${{ inputs.version }}"
IS_RELEASE="false"
else
G4F_VERSION="0.0.0-dev"
IS_RELEASE="false"
fi
echo "version=${G4F_VERSION}" >> $GITHUB_OUTPUT
echo "is_release=${IS_RELEASE}" >> $GITHUB_OUTPUT
echo "Building version: ${G4F_VERSION}"
env:
GIT_REF: ${{ github.ref }}
REF_NAME: ${{ github.ref_name }}
# PyPI Package
build-pypi:
runs-on: ubuntu-latest
needs: prepare
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install build tools
run: |
python -m pip install --upgrade pip
python -m pip install build twine
- name: Build package
env:
G4F_VERSION: ${{ needs.prepare.outputs.version }}
run: python -m build
- name: Verify package
run: |
python -m twine check dist/*
ls -la dist/
- name: Upload PyPI package artifact (combined)
uses: actions/upload-artifact@v4
with:
name: pypi-package
path: dist/
- name: Upload PyPI build artifacts (individual)
run: |
for f in dist/*; do
echo "Uploading $f"
done
shell: bash
- name: Upload wheel artifact
uses: actions/upload-artifact@v4
with:
name: pypi-wheel
path: dist/*.whl
- name: Upload sdist artifact
uses: actions/upload-artifact@v4
with:
name: pypi-sdist
path: dist/*.tar.gz
# Executables (built with g4f-go, no Nuitka)
# Cross-compiles the Go launcher on ubuntu-latest for every OS/arch.
# The embedded CPython runtime zips are generated by fetch-python.sh
# (linux) / fetched & merged per-platform (windows, darwin, freebsd).
build-g4f-go:
runs-on: ubuntu-latest
needs: prepare
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.22"
cache-dependency-path: g4f-go/go.mod
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install tooling
run: |
python -m pip install --upgrade pip
pip install requests aiohttp brotli pycryptodome nest-asyncio2
sudo apt-get update
sudo apt-get install -y rsync zip
- name: Fetch & merge Python runtimes (all platforms)
working-directory: g4f-go
env:
G4F_VERSION: ${{ needs.prepare.outputs.version }}
run: ./fetch-python.sh
- name: Cross-compile g4f-go launchers (all platforms)
working-directory: g4f-go
env:
G4F_VERSION: ${{ needs.prepare.outputs.version }}
run: ./build-all.sh
- name: Upload g4f-go executables (combined)
uses: actions/upload-artifact@v4
with:
name: g4f-go
path: g4f-go/dist/*
- name: Upload g4f-go executables (individual per platform)
run: |
for zip in g4f-go/dist/*.zip; do
base="$(basename "$zip" .zip)"
echo "Uploading $zip as artifact g4f-go-${base}"
done
shell: bash
- name: Upload g4f-go windows-amd64
uses: actions/upload-artifact@v4
with:
name: g4f-go-windows-amd64
path: g4f-go/dist/g4f-go-*-windows-amd64.zip
if-no-files-found: error
- name: Upload g4f-go linux-amd64
uses: actions/upload-artifact@v4
with:
name: g4f-go-linux-amd64
path: g4f-go/dist/g4f-go-*-linux-amd64.zip
if-no-files-found: error
- name: Upload g4f-go linux-arm64
uses: actions/upload-artifact@v4
with:
name: g4f-go-linux-arm64
path: g4f-go/dist/g4f-go-*-linux-arm64.zip
if-no-files-found: error
- name: Upload g4f-go darwin-arm64
uses: actions/upload-artifact@v4
with:
name: g4f-go-darwin-arm64
path: g4f-go/dist/g4f-go-*-darwin-arm64.zip
if-no-files-found: error
- name: Upload g4f-go darwin-amd64
uses: actions/upload-artifact@v4
with:
name: g4f-go-darwin-amd64
path: g4f-go/dist/g4f-go-*-darwin-amd64.zip
if-no-files-found: error
# Docker Images
build-docker:
runs-on: ubuntu-latest
needs: prepare
if: needs.prepare.outputs.is_release == 'true'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: Get metadata for Docker
id: metadata
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5
with:
images: |
hlohaus789/g4f
- name: Log in to Docker Hub
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push armv7 image
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
with:
context: .
file: docker/Dockerfile-armv7
platforms: linux/arm/v7
push: true
provenance: mode=max
sbom: true
tags: |
hlohaus789/g4f:latest-armv7
hlohaus789/g4f:${{ needs.prepare.outputs.version }}-armv7
labels: ${{ steps.metadata.outputs.labels }}
build-args: |
G4F_VERSION=${{ needs.prepare.outputs.version }}
- name: Build and push slim images
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
with:
context: .
file: docker/Dockerfile-slim
platforms: linux/amd64,linux/arm64
push: true
provenance: mode=max
sbom: true
tags: |
hlohaus789/g4f:latest-slim
hlohaus789/g4f:${{ needs.prepare.outputs.version }}-slim
labels: ${{ steps.metadata.outputs.labels }}
build-args: |
G4F_VERSION=${{ needs.prepare.outputs.version }}
- name: Build and push image
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64
push: true
provenance: mode=max
sbom: true
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
build-args: |
G4F_VERSION=${{ needs.prepare.outputs.version }}
# Release Creation
create-release:
runs-on: ubuntu-latest
needs: [prepare, build-pypi, build-g4f-go]
if: needs.prepare.outputs.is_release == 'true'
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Download PyPI artifacts
uses: actions/download-artifact@v4
with:
name: pypi-package
path: ./release/pypi
- name: Download g4f-go artifacts
uses: actions/download-artifact@v4
with:
name: g4f-go
path: ./release/g4f-go
- name: Download individual g4f-go platform artifacts
uses: actions/download-artifact@v4
with:
path: ./release/g4f-go/individual
pattern: g4f-go-*
merge-multiple: true
- name: Flatten all release assets
run: |
mkdir -p ./release/flat
find ./release -type f | sort
cp -v ./release/pypi/* ./release/flat/ 2>/dev/null || true
cp -v ./release/g4f-go/dist/*.zip ./release/flat/ 2>/dev/null || true
cp -v ./release/g4f-go/individual/* ./release/flat/ 2>/dev/null || true
echo "--- Final release assets ---"
find ./release/flat -type f | sort
- name: Create Release with Assets
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2
with:
tag_name: ${{ needs.prepare.outputs.version }}
name: Release ${{ needs.prepare.outputs.version }}
append_body: true
body: |
### Download Options
**Python Package:**
- PyPI: `pip install g4f==${{ needs.prepare.outputs.version }}`
**Executables (g4f-go, self-contained with embedded Python):**
- Windows amd64: `g4f-go-${{ needs.prepare.outputs.version }}-windows-amd64.zip`
- Linux x64: `g4f-go-${{ needs.prepare.outputs.version }}-linux-amd64.zip`
- Linux ARM64: `g4f-go-${{ needs.prepare.outputs.version }}-linux-arm64.zip`
- macOS x64: `g4f-go-${{ needs.prepare.outputs.version }}-darwin-amd64.zip`
- macOS ARM64: `g4f-go-${{ needs.prepare.outputs.version }}-darwin-arm64.zip`
- FreeBSD amd64: `g4f-go-${{ needs.prepare.outputs.version }}-freebsd-amd64.zip`
**System Packages:**
- WinGet: `winget install gpt4free`
**Docker:**
- `docker pull hlohaus789/g4f:${{ needs.prepare.outputs.version }}`
- `docker pull hlohaus789/g4f:${{ needs.prepare.outputs.version }}-slim`
💻 **Having trouble with the .exe from this release?**
👉 Check out the Windows launcher for GPT4Free instead:
🔗 [https://github.com/gpt4free/g4f.exe](https://github.com/gpt4free/g4f.exe) 🚀
draft: false
prerelease: false
files: ./release/flat/*
token: ${{ secrets.GITHUB_TOKEN }}
# Publish to PyPI (only for releases)
# publish-pypi:
# runs-on: ubuntu-latest
# needs: [prepare, build-pypi, create-release]
# if: needs.prepare.outputs.is_release == 'true'
# environment:
# name: pypi
# url: https://pypi.org/p/g4f
# permissions:
# id-token: write
# steps:
# - name: Download PyPI artifacts
# uses: actions/download-artifact@v4
# with:
# name: pypi-package
# path: dist/
# - name: Publish to PyPI
# uses: pypa/gh-action-pypi-publish@release/v1