diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6483b1d..eff3e75 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,11 @@ updates: directory: "/" schedule: interval: "weekly" + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: gitsubmodule + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml deleted file mode 100644 index 0ee7a9b..0000000 --- a/.github/workflows/build_and_test.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: Build and Test - -on: - push: - branches: - - main - pull_request: - branches: - - main - -jobs: - build-and-test: - permissions: - contents: read - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Initialize submodules - env: - GIT_ASKPASS: /bin/echo - GH_PAT: ${{ secrets.GH_PAT }} - run: | - git config --global url."https://${GH_PAT}@github.com/".insteadOf "https://github.com/" - git submodule update --init --recursive - - - name: Install uv - uses: astral-sh/setup-uv@v6 - with: - enable-cache: true - - - name: Set up Python - run: uv python install - - - name: Install the project - run: uv sync --locked --all-extras --dev - - - name: Ensure no differences in generated code - run: | - source .venv/bin/activate - GENERATED_DIR="src/athena_client/generated" - BACKUP_DIR="src/athena_client/generated_backup" - - cp -r $GENERATED_DIR $BACKUP_DIR - - ./scripts/compile_proto.sh || (echo "Protobuf compilation failed. Ensure submodules are initialized and the proto file exists." && exit 1) - - # Fix imports in generated files - if [[ -f "$GENERATED_DIR/athena/athena_pb2_grpc.py" && -f "$GENERATED_DIR/athena/athena_pb2.py" ]]; then - sed -i "$GENERATED_DIR/athena/athena_pb2_grpc.py" -e 's/^from athena /from athena_client.generated.athena /' - sed -i "$GENERATED_DIR/athena/athena_pb2.py" -e 's/^from athena /from athena_client.generated.athena /' - else - echo "Error: Expected files not found in $GENERATED_DIR/athena" - exit 1 - fi - - diff -r $GENERATED_DIR $BACKUP_DIR || (echo "Generated code differs. Please commit the changes after running compile_proto.sh." && exit 1) - - rm -rf $BACKUP_DIR - - - name: Run linter - run: | - uv run ruff check - - - name: Run code formatting check - run: | - uv run ruff format --check - - - name: Run type checking - run: | - uv run pyright - - - name: Run tests - run: | - uv run pytest --junitxml=pytest.xml --tb=auto --cov-report=term-missing:skip-covered --cov=src tests/ | tee pytest-coverage.txt - - - name: Upload test results - uses: actions/upload-artifact@v4 # upload test results - if: ${{ !cancelled() }} # run this step even if previous step failed - with: - name: test-results - path: pytest.xml - - - name: Build package - run: | - uv build diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4c63ca3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,245 @@ +name: Build and Test + +on: + push: + branches: + - main + pull_request: + branches: + - main + release: + types: [published] + +jobs: + test: + permissions: + contents: read + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + # Test all Python versions on Ubuntu (primary platform) + - os: ubuntu-latest + python-version: "3.10" + - os: ubuntu-latest + python-version: "3.11" + - os: ubuntu-latest + python-version: "3.12" + - os: ubuntu-latest + python-version: "3.13" + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Initialize submodules + env: + GIT_ASKPASS: /bin/echo + GH_PAT: ${{ secrets.GH_PAT }} + run: | + git config --global url."https://${GH_PAT}@github.com/".insteadOf "https://github.com/" + git submodule update --init --recursive + + - name: Install uv + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + + - name: Set up Python ${{ matrix.python-version }} + run: uv python install ${{ matrix.python-version }} + + - name: Install the project + run: uv sync --locked --all-extras --dev + + - name: Ensure no differences in generated code (Unix) + if: runner.os != 'Windows' + shell: bash + run: | + source .venv/bin/activate + GENERATED_DIR="src/athena_client/generated" + BACKUP_DIR="src/athena_client/generated_backup" + + cp -r $GENERATED_DIR $BACKUP_DIR + + ./scripts/compile_proto.sh || (echo "Protobuf compilation failed. Ensure submodules are initialized and the proto file exists." && exit 1) + + # Fix imports in generated files + if [[ -f "$GENERATED_DIR/athena/athena_pb2_grpc.py" && -f "$GENERATED_DIR/athena/athena_pb2.py" ]]; then + sed -i.bak 's/^from athena /from athena_client.generated.athena /' "$GENERATED_DIR/athena/athena_pb2_grpc.py" + sed -i.bak 's/^from athena /from athena_client.generated.athena /' "$GENERATED_DIR/athena/athena_pb2.py" + rm -f "$GENERATED_DIR/athena/athena_pb2_grpc.py.bak" "$GENERATED_DIR/athena/athena_pb2.py.bak" + else + echo "Error: Expected files not found in $GENERATED_DIR/athena" + exit 1 + fi + + diff -r $GENERATED_DIR $BACKUP_DIR || (echo "Generated code differs. Please commit the changes after running compile_proto.sh." && exit 1) + + rm -rf $BACKUP_DIR + + - name: Ensure no differences in generated code (Windows) + if: runner.os == 'Windows' + shell: powershell + run: | + & .venv\Scripts\Activate.ps1 + $GENERATED_DIR = "src\athena_client\generated" + $BACKUP_DIR = "src\athena_client\generated_backup" + + Copy-Item -Recurse -Path $GENERATED_DIR -Destination $BACKUP_DIR + + # Compile protobuf files directly since compile_proto.sh won't work on Windows + if (!(Test-Path "athena-protobufs\athena\athena.proto")) { + Write-Host "Error: Protobuf file not found at athena-protobufs\athena\athena.proto. Ensure the submodule is initialized and updated." + exit 1 + } + + New-Item -ItemType Directory -Force -Path $GENERATED_DIR | Out-Null + + python -m grpc_tools.protoc --proto_path="athena-protobufs" --python_out=$GENERATED_DIR --grpc_python_out=$GENERATED_DIR --mypy_out=$GENERATED_DIR "athena-protobufs\athena\athena.proto" + + # Fix imports in generated files + if ((Test-Path "$GENERATED_DIR\athena\athena_pb2_grpc.py") -and (Test-Path "$GENERATED_DIR\athena\athena_pb2.py")) { + (Get-Content "$GENERATED_DIR\athena\athena_pb2_grpc.py") -replace '^from athena ', 'from athena_client.generated.athena ' | Set-Content "$GENERATED_DIR\athena\athena_pb2_grpc.py" + (Get-Content "$GENERATED_DIR\athena\athena_pb2.py") -replace '^from athena ', 'from athena_client.generated.athena ' | Set-Content "$GENERATED_DIR\athena\athena_pb2.py" + } else { + Write-Host "Error: Expected files not found in $GENERATED_DIR\athena" + exit 1 + } + + # Simple directory comparison for Windows + $differences = Compare-Object -ReferenceObject (Get-ChildItem -Recurse $GENERATED_DIR) -DifferenceObject (Get-ChildItem -Recurse $BACKUP_DIR) -Property Name, Length + if ($differences) { + Write-Host "Generated code differs. Please commit the changes after running compile_proto.sh." + exit 1 + } + + Remove-Item -Recurse -Force $BACKUP_DIR + + - name: Run linter + run: | + uv run ruff check + + - name: Run code formatting check + run: | + uv run ruff format --check + + - name: Run type checking + run: | + uv run pyright + + - name: Run tests + run: | + uv run pytest --junitxml=pytest.xml --tb=auto --cov-report=term-missing:skip-covered --cov=src tests/ | tee pytest-coverage.txt + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: test-results-${{ matrix.os }}-py${{ matrix.python-version }} + path: pytest.xml + + build: + needs: test + permissions: + contents: read + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Initialize submodules + env: + GIT_ASKPASS: /bin/echo + GH_PAT: ${{ secrets.GH_PAT }} + run: | + git config --global url."https://${GH_PAT}@github.com/".insteadOf "https://github.com/" + git submodule update --init --recursive + + - name: Install uv + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + + - name: Set version from tag + run: | + if [[ "$GITHUB_REF" == refs/tags/* ]]; then + TAG_VERSION=${GITHUB_REF#refs/tags/} + # Remove 'v' prefix if present + VERSION=${TAG_VERSION#v} + echo "Setting version to: $VERSION" + uv version "$VERSION" + else + echo "Not building from a tag, using default version" + fi + + - name: Build package + run: uv build + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: dist + path: dist/ + + publish: + needs: [test, build] + runs-on: ubuntu-latest + if: github.event_name == 'release' && github.event.action == 'published' + environment: + name: pypi + url: https://pypi.org/p/athena-client + permissions: + id-token: write + + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: dist + path: dist/ + + - name: Verify package version matches release tag + run: | + # Extract version from release tag (remove leading 'v' if present) + TAG_VERSION="${GITHUB_REF#refs/tags/}" + TAG_VERSION="${TAG_VERSION#v}" + echo "Release tag version: $TAG_VERSION" + # Find the first wheel or sdist in dist/ + FILE=$(ls dist/*.whl 2>/dev/null || ls dist/*.tar.gz 2>/dev/null) + if [[ -z "$FILE" ]]; then + echo "No distribution file found in dist/" + exit 1 + fi + echo "Checking version in: $FILE" + if [[ "$FILE" == *.whl ]]; then + # Extract version from wheel filename: name-version-*.whl + FILENAME=$(basename "$FILE") + # Remove extension + FILENAME="${FILENAME%.whl}" + # Extract version (assumes format: name-version-...) + PKG_VERSION=$(echo "$FILENAME" | awk -F'-' '{print $(NF-2)}') + elif [[ "$FILE" == *.tar.gz ]]; then + # Extract version from sdist filename: name-version.tar.gz + FILENAME=$(basename "$FILE") + FILENAME="${FILENAME%.tar.gz}" + PKG_VERSION=$(echo "$FILENAME" | awk -F'-' '{print $NF}') + else + echo "Unknown distribution file type: $FILE" + exit 1 + fi + echo "Package version: $PKG_VERSION" + if [[ "$PKG_VERSION" != "$TAG_VERSION" ]]; then + echo "Version mismatch: tag version is $TAG_VERSION, package version is $PKG_VERSION" + exit 1 + fi + echo "Version check passed." + - name: Publish package distributions to PyPI + run: uv publish --trusted-publishing always diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml deleted file mode 100644 index f8cc242..0000000 --- a/.github/workflows/package.yml +++ /dev/null @@ -1,130 +0,0 @@ -name: Package and Publish - -on: - push: - tags: - - "v*.*.*" - release: - types: [published] - workflow_dispatch: - inputs: - version: - description: "Version to publish (e.g., 1.0.0)" - required: true - type: string - -jobs: - build: - runs-on: ubuntu-latest - permissions: - contents: read - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install uv - uses: astral-sh/setup-uv@v6 - with: - enable-cache: true - cache-dependency-glob: "uv.lock" - - - name: Install dependencies - run: uv sync --locked --dev - - - name: Determine version - id: version - run: | - if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then - VERSION="${{ github.event.inputs.version }}" - elif [[ "${{ github.event_name }}" == "release" ]]; then - VERSION="${{ github.event.release.tag_name }}" - VERSION="${VERSION#v}" - else - # Extract version from tag (remove 'v' prefix if present) - VERSION="${GITHUB_REF#refs/tags/}" - VERSION="${VERSION#v}" - fi - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "Publishing version: $VERSION" - - - name: Set package version - run: | - uv version ${{ steps.version.outputs.version }} - - - name: Build package - run: uv build - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: dist-${{ steps.version.outputs.version }} - path: dist/ - - publish-github: - needs: build - runs-on: ubuntu-latest - if: github.event_name == 'release' || github.event_name == 'push' || github.event_name == 'workflow_dispatch' - permissions: - contents: read - packages: write - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install uv - uses: astral-sh/setup-uv@v6 - with: - enable-cache: true - - - name: Determine version - id: version - run: | - if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then - VERSION="${{ github.event.inputs.version }}" - elif [[ "${{ github.event_name }}" == "release" ]]; then - VERSION="${{ github.event.release.tag_name }}" - VERSION="${VERSION#v}" - else - # Extract version from tag (remove 'v' prefix if present) - VERSION="${GITHUB_REF#refs/tags/}" - VERSION="${VERSION#v}" - fi - echo "version=$VERSION" >> $GITHUB_OUTPUT - - - name: Download build artifacts - uses: actions/download-artifact@v4 - with: - name: dist-${{ steps.version.outputs.version }} - path: dist/ - - - name: Publish to GitHub Package Registry - env: - UV_PUBLISH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - uv publish --publish-url https://pypi.pkg.github.com/${{ github.repository_owner }}/ - - release: - needs: [build, publish-github] - runs-on: ubuntu-latest - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') - permissions: - contents: write - - steps: - - name: Download build artifacts - uses: actions/download-artifact@v4 - with: - name: dist-${{ steps.version.outputs.version }} - path: dist/ - - - name: Create GitHub Release - uses: softprops/action-gh-release@v1 - with: - files: | - dist/*.whl - dist/*.tar.gz - generate_release_notes: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}