name: Build Pacparser

on:
  push:
    branches: [main]
    paths:
      - "src/**"
      - ".github/**"
      - Makefile
      - Dockerfile
  pull_request:
  release:
    types: [published]
  workflow_dispatch:
    inputs:
      tag:
        description: "Tag to run workflow for"
        required: true

permissions:
  contents: read
  packages: write

jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        include:
          - os: ubuntu-latest
            os_suffix: "ubuntu-x86_64"
          - os: macos-latest
            os_suffix: "macos-arm64"
          - os: windows-latest
            os_suffix: "windows-x86_64"
    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        if: ${{ !contains(github.event_name, 'workflow_dispatch') }}
        with:
          fetch-depth: 0

      - name: Check out code for workflow_dispatch
        if: ${{ contains(github.event_name, 'workflow_dispatch') }}
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          ref: ${{ github.event.inputs.tag }}

      - name: make non-windows
        if: ${{ matrix.os != 'windows-latest' }}
        run: make -C src

      - name: Run resolve_host tests (non-windows)
        if: ${{ matrix.os != 'windows-latest' }}
        run: |
          cd tests
          for test in test_resolve_host test_multi_ip test_buffer_safety test_edge_cases; do
            echo "=== Building $test ==="
            gcc -g -Wall -o $test ${test}.c || exit 1
            echo "=== Running $test ==="
            ./$test || exit 1
            echo
          done

      - name: make windows
        if: ${{ matrix.os == 'windows-latest' }}
        run: make -C src -f Makefile.win32

      - name: Run resolve_host tests (windows)
        if: ${{ matrix.os == 'windows-latest' }}
        run: |
          cd tests
          foreach ($test in @('test_resolve_host', 'test_multi_ip', 'test_buffer_safety', 'test_edge_cases')) {
            Write-Host "=== Building $test ==="
            gcc -g -Wall -o "$test.exe" "$test.c" -lws2_32
            if ($LASTEXITCODE -ne 0) { exit 1 }
            Write-Host "=== Running $test ==="
            & ".\$test.exe"
            if ($LASTEXITCODE -ne 0) { exit 1 }
            Write-Host ""
          }

      - name: Get ref_name
        id: get_ref_name
        if: ${{ matrix.os != 'windows-latest' }}
        run: |
          if [ "${{ github.event_name }}" == "pull_request" ]; then
            echo "ref_name=${{ github.base_ref }}"
            echo "ref_name=${{ github.base_ref }}" >> $GITHUB_OUTPUT || exit 1
          else
            echo "ref_name=${{ github.ref_name }}"
            echo "ref_name=${{ github.ref_name }}" >> $GITHUB_OUTPUT || exit 1
          fi

      - name: make non-windows dist
        if: ${{ matrix.os != 'windows-latest' }}
        run: |
          DIST_OS_SUFFIX=${{ matrix.os_suffix }} make -C src dist
          ls -R src/*.zip

      - name: make windows dist
        if: ${{ matrix.os == 'windows-latest' }}
        run: |
          make -C src -f Makefile.win32 dist

      - name: Upload dist (non-windows)
        if: ${{ matrix.os != 'windows-latest' }}
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: pacparser-dist-${{ matrix.os }}
          path: src/pacparser*.zip

      - name: Upload dist (windows)
        if: ${{ matrix.os == 'windows-latest' }}
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: pacparser-dist-${{ matrix.os }}
          path: src/dist

  build-release-binaries:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Download ubuntu cloudprober binaries
        uses: actions/download-artifact@v8
        with:
          pattern: pacparser-dist-*

      - name: Fix files
        run: |
          ls -R .
          mv pacparser-dist-*/*.zip .
          file=$(ls pacparser-*-ubuntu*.zip)
          name=${file/ubuntu/windows}
          name=${name/.zip/}
          mv pacparser-dist-windows-latest $name
          zip -r $name.zip $name

      - name: Upload pacparser release
        uses: actions/upload-artifact@v7
        with:
          name: pacparser-release-binaries
          path: pacparser-*.zip

  python-module-build:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        if: ${{ !contains(github.event_name, 'workflow_dispatch') }}
        with:
          fetch-depth: 0

      - name: Check out code for workflow_dispatch
        if: ${{ contains(github.event_name, 'workflow_dispatch') }}
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          ref: ${{ github.event.inputs.tag }}

      - name: Set up Python
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: ${{ matrix.python-version }}

      - name: Set up setuptools
        run: |
          python --version
          python -mpip install setuptools

      - name: make non-windows
        if: ${{ matrix.os != 'windows-latest' }}
        run: make -C src pymod-dist

      - name: make windows
        if: ${{ matrix.os == 'windows-latest' }}
        run: make -C src -f Makefile.win32 pymod-dist

      - name: Upload dist
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name:
            pacparser-python-${{ matrix.python-version }}-${{ matrix.os }}-dist
          path: src/pymod/pacparser-python*

      - name: Build wheel non-linux
        if: ${{ matrix.os != 'ubuntu-latest' }}
        run: |
          python -m pip install wheel
          cd src/pymod && python setup.py bdist_wheel

      - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
        id: src_changes
        with:
          filters: |
            changed:
              - 'src/**'

      - name: Publish package to PyPI (non-linux)
        if: |
          (matrix.os != 'ubuntu-latest') &&
          (steps.src_changes.outputs.changed == 'true' ||
          startsWith(github.event.inputs.tag, 'v') || startsWith(github.ref,
          'refs/tags/v')) && (github.event_name != 'pull_request')
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.PYPI }}
        run: |
          python -m pip install twine
          ls -R .
          twine upload src/pymod/dist/*

  build-linux-wheels:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        if: ${{ !contains(github.event_name, 'workflow_dispatch') }}
        with:
          fetch-depth: 0

      - name: Check out code for workflow_dispatch
        if: ${{ contains(github.event_name, 'workflow_dispatch') }}
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          ref: ${{ github.event.inputs.tag }}

      - name: Set env
        run: |
          echo "PACPARSER_VERSION=$(git describe --always --tags \
          --candidate=100)" >> $GITHUB_ENV

      - name: Set up Python
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0

      - name: Set up setuptools
        run: |
          python --version
          python -mpip install setuptools

      - name: make
        run: make -C src pymod

      - name: Build sdist
        run: cd src/pymod && python setup.py sdist

      - name: Install cibuildwheel and twine
        run: python -m pip install cibuildwheel twine

      - name: Build wheel using cibuildwheel
        run: |
          cd src/pymod && python setup.py clean --all
          cp -r ../quickjs ../pacparser.c ../pac_utils.h ../pacparser.h .
          python -m cibuildwheel --output-dir dist
        env:
          CIBW_BUILD: "cp{37,38,39,310,311,312}-manylinux*64"
          CIBW_ENVIRONMENT: "PACPARSER_VERSION=${{ env.PACPARSER_VERSION }}"
          CIBW_BEFORE_BUILD: >-
            cd {project} &&
            make -C quickjs clean &&
            make -C quickjs CFLAGS='-fPIC' &&
            cc -g -Wall -DVERSION=$PACPARSER_VERSION -Iquickjs -fPIC -c pacparser.c -o pacparser.o &&
            cp quickjs/libquickjs.a .

      - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
        id: src_changes
        with:
          filters: |
            changed:
              - 'src/**'

      - name: Publish package to PyPI
        if: |
          (startsWith(github.event.inputs.tag, 'v') ||
          startsWith(github.ref,'refs/tags/v') ||
          steps.src_changes.outputs.changed == 'true') &&
          (github.event_name != 'pull_request')
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.PYPI }}
        run: |
          twine upload src/pymod/dist/*

  build_and_push_docker_multiarch:
    name: Build and push multiarch docker image
    if: |
      github.repository == 'manugarg/pacparser' &&
      (github.ref == 'refs/heads/main' || startswith(github.ref, 'refs/heads/docker') ||
      startsWith(github.ref, 'refs/tags/v'))
    runs-on: ubuntu-latest
    steps:
      - name: Check out code into the Go module directory
        if: ${{ !contains(github.event_name, 'workflow_dispatch') }}
        uses: actions/checkout@v6.0.2
        with:
          fetch-depth: 0

      - name: Check out code into the Go module directory
        if: ${{ contains(github.event_name, 'workflow_dispatch') }}
        uses: actions/checkout@v6.0.2
        with:
          fetch-depth: 0
          ref: ${{ github.event.inputs.tag }}

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v4

      - name: Login to GHCR
        uses: docker/login-action@v4
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push release Docker Image (main-ghcr)
        run: make docker_multiarch DOCKER_IMAGE=ghcr.io/manugarg/pactester
