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
35 changes: 35 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
# ============================================================================
# Cloudflare API Configuration
# ============================================================================
# For managing Cloudflare IP lists, firewall rules, and Access policies.
# Documentation: https://developers.cloudflare.com/api/

# Cloudflare API Token (required for cloudflare-api package)
# Create a token at: https://dash.cloudflare.com/profile/api-tokens
# Required permissions:
# - Account > Account Filter Lists > Edit (for IP lists)
# - Account > Account Firewall Access Rules > Edit (for firewall rules)
CLOUDFLARE_API_TOKEN=your-cloudflare-api-token-here

# Cloudflare Account ID (required)
# Find this at: https://dash.cloudflare.com/ (right sidebar)
CLOUDFLARE_ACCOUNT_ID=your-cloudflare-account-id-here

# Cloudflare Zone ID (optional, for zone-scoped operations)
# Find this on your domain's overview page in the dashboard
# CLOUDFLARE_ZONE_ID=your-zone-id-here

# Cloudflare API Settings (optional)
# CF_DEFAULT_LIST_KIND=ip
# CF_REQUEST_TIMEOUT=30
# CF_MAX_RETRIES=3
# CF_BULK_POLL_INTERVAL=1.0
# CF_BULK_TIMEOUT=300

# ============================================================================
# Cloudflare Access Configuration (for cloudflare-auth package)
# ============================================================================
# For JWT validation with Cloudflare Access.
# CLOUDFLARE_TEAM_DOMAIN=your-team.cloudflareaccess.com
# CLOUDFLARE_AUDIENCE_TAG=your-application-audience-tag

# ============================================================================
# Google Cloud - Assured OSS Configuration
# ============================================================================
Expand Down
104 changes: 104 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# .github/workflows/publish.yml
# Publishes packages to GCP Artifact Registry when tags are pushed
# Secrets are fetched from Infisical
# Uses Assured OSS dependencies for supply chain security

name: Publish Package

on:
push:
tags:
- 'cloudflare-auth-v*'
- 'gcs-utilities-v*'

permissions:
contents: read

env:
INFISICAL_DOMAIN: https://secrets.byronwilliamscpa.com
INFISICAL_PROJECT: python-libs
INFISICAL_ENV: prod
ARTIFACT_REGISTRY_URL: https://us-central1-python.pkg.dev/assured-oss-457903/python-libs/

jobs:
determine-package:
runs-on: ubuntu-latest
outputs:
package_dir: ${{ steps.parse.outputs.package_dir }}
package_name: ${{ steps.parse.outputs.package_name }}
version: ${{ steps.parse.outputs.version }}
steps:
- name: Parse tag
id: parse
run: |
TAG="${{ github.ref_name }}"
echo "Processing tag: $TAG"

if [[ "$TAG" == cloudflare-auth-v* ]]; then
echo "package_dir=packages/cloudflare-auth" >> $GITHUB_OUTPUT
echo "package_name=byronwilliamscpa-cloudflare-auth" >> $GITHUB_OUTPUT
echo "version=${TAG#cloudflare-auth-v}" >> $GITHUB_OUTPUT
elif [[ "$TAG" == gcs-utilities-v* ]]; then
echo "package_dir=packages/gcs-utilities" >> $GITHUB_OUTPUT
echo "package_name=byronwilliamscpa-gcs-utilities" >> $GITHUB_OUTPUT
echo "version=${TAG#gcs-utilities-v}" >> $GITHUB_OUTPUT
else
echo "::error::Unknown tag format: $TAG"
exit 1
fi
Comment on lines +24 to +48
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add timeout to prevent indefinite hangs.

The determine-package job lacks a timeout-minutes setting. While this job is quick, adding a timeout prevents resource waste if the job hangs unexpectedly.

Apply this diff:

 jobs:
   determine-package:
     runs-on: ubuntu-latest
+    timeout-minutes: 5
     outputs:
🤖 Prompt for AI Agents
In .github/workflows/publish.yml around lines 24 to 48 the determine-package job
has no timeout-minutes set, which can let it hang indefinitely; add a
timeout-minutes entry (e.g., timeout-minutes: 5) directly under the
determine-package: or runs-on: ubuntu-latest line (with the same indentation
level as runs-on) to enforce a short max runtime and prevent resource waste if
the job hangs.


build-and-publish:
needs: determine-package
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Fetch secrets from Infisical
uses: Infisical/secrets-action@03d3fa38607956c493f53c6633f94006a13c47ae # v1.0.7
with:
client-id: ${{ secrets.INFISICAL_CLIENT_ID }}
client-secret: ${{ secrets.INFISICAL_CLIENT_SECRET }}
env-slug: ${{ env.INFISICAL_ENV }}
project-slug: ${{ env.INFISICAL_PROJECT }}
domain: ${{ env.INFISICAL_DOMAIN }}

Comment thread
coderabbitai[bot] marked this conversation as resolved.
- name: Install uv
uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca # v6.0.1
with:
enable-cache: true

- name: Set up Python
run: uv python install 3.12
Comment on lines +50 to +72
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add timeout to prevent indefinite publishing jobs.

The build-and-publish job lacks a timeout-minutes setting. Publishing workflows should have timeouts to prevent indefinite hangs, especially when interacting with external registries.

Apply this diff:

   build-and-publish:
     needs: determine-package
     runs-on: ubuntu-latest
+    timeout-minutes: 15
     steps:
🤖 Prompt for AI Agents
.github/workflows/publish.yml lines 50-72: the build-and-publish job is missing
a timeout-minutes setting which can allow the workflow to hang indefinitely; add
a timeout-minutes: 60 (or your preferred minute value) at the same indentation
level as runs-on (under the job name) to ensure the job is automatically
cancelled after the specified time, keeping the value as an integer and
preserving YAML indentation.


- name: Verify version matches tag
working-directory: ${{ needs.determine-package.outputs.package_dir }}
run: |
TOML_VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
TAG_VERSION="${{ needs.determine-package.outputs.version }}"
if [[ "$TOML_VERSION" != "$TAG_VERSION" ]]; then
echo "::error::Version mismatch! pyproject.toml=$TOML_VERSION, tag=$TAG_VERSION"
exit 1
fi
Comment on lines +74 to +82
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider using a TOML parser for more robust version extraction.

The sed command assumes specific formatting and could break with comments, different quote styles, or whitespace variations. Using a proper TOML parser would be more reliable.

Consider this alternative using Python's built-in TOML support:

       - name: Verify version matches tag
         working-directory: ${{ needs.determine-package.outputs.package_dir }}
         run: |
-          TOML_VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
+          TOML_VERSION=$(uv run python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
           TAG_VERSION="${{ needs.determine-package.outputs.version }}"

Note: tomllib is available in Python 3.11+. For Python 3.10, you'd need tomli.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Verify version matches tag
working-directory: ${{ needs.determine-package.outputs.package_dir }}
run: |
TOML_VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
TAG_VERSION="${{ needs.determine-package.outputs.version }}"
if [[ "$TOML_VERSION" != "$TAG_VERSION" ]]; then
echo "::error::Version mismatch! pyproject.toml=$TOML_VERSION, tag=$TAG_VERSION"
exit 1
fi
- name: Verify version matches tag
working-directory: ${{ needs.determine-package.outputs.package_dir }}
run: |
TOML_VERSION=$(uv run python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
TAG_VERSION="${{ needs.determine-package.outputs.version }}"
if [[ "$TOML_VERSION" != "$TAG_VERSION" ]]; then
echo "::error::Version mismatch! pyproject.toml=$TOML_VERSION, tag=$TAG_VERSION"
exit 1
fi
🤖 Prompt for AI Agents
.github/workflows/publish.yml around lines 74 to 82: the current grep/sed
approach brittlely parses pyproject.toml and can fail with different
formatting/comments/quotes; replace the shell text-extraction with a small
Python invocation that reads pyproject.toml and uses a real TOML parser to
extract the [project]/version (use tomllib on Python 3.11+ and fall back to
tomli if not available), print that version to stdout for comparison with the
tag, and keep the existing exit-on-mismatch logic; ensure the action still runs
in the same working-directory and add a short comment noting the parser
fallback.


- name: Authenticate to Google Cloud
uses: google-github-actions/auth@71f986410dfbc7added4569d411d040a91dc6935 # v2.1.8
with:
credentials_json: ${{ env.GCP_SA_KEY_BASE64 }}

- name: Install keyring for Artifact Registry
run: pip install keyrings.google-artifactregistry-auth
Comment on lines +89 to +90
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

git ls-files .github/workflows/publish.yml

Repository: ByronWilliamsCPA/python-libs

Length of output: 101


🏁 Script executed:

cat -n .github/workflows/publish.yml | sed -n '80,105p'

Repository: ByronWilliamsCPA/python-libs

Length of output: 1370


🌐 Web query:

keyrings.google-artifactregistry-auth latest version PyPI

💡 Result:

Latest PyPI version: 1.1.2 — uploaded Mar 1, 2023. [1]

Source: PyPI project page for keyrings.google-artifactregistry-auth. [1]


Pin the keyring package version for supply chain security.

Installing keyrings.google-artifactregistry-auth without a version constraint creates a supply chain risk. The package version could change between workflow runs, potentially introducing vulnerabilities or breaking changes.

Apply this diff to pin to the latest stable version:

       - name: Install keyring for Artifact Registry
-        run: pip install keyrings.google-artifactregistry-auth
+        run: pip install keyrings.google-artifactregistry-auth==1.1.2
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Install keyring for Artifact Registry
run: pip install keyrings.google-artifactregistry-auth
- name: Install keyring for Artifact Registry
run: pip install keyrings.google-artifactregistry-auth==1.1.2
🤖 Prompt for AI Agents
.github/workflows/publish.yml around lines 89-90: the workflow installs
keyrings.google-artifactregistry-auth without a version pin; replace the
unconstrained pip install with a pinned install (pip install
keyrings.google-artifactregistry-auth==<tested-stable-version>) using the
specific version you validated for this repo (or reference a repo
constant/variable), commit that change and update any workflow docs noting the
chosen version.


- name: Build package
working-directory: ${{ needs.determine-package.outputs.package_dir }}
run: uv build

- name: Publish to Artifact Registry
working-directory: ${{ needs.determine-package.outputs.package_dir }}
run: uv publish --publish-url ${{ env.ARTIFACT_REGISTRY_URL }}
Comment on lines +92 to +98
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider adding package verification before publishing.

Publishing immediately after building without verification is risky. Consider adding a validation step to check package metadata and catch issues before they reach the registry.

Add a verification step between build and publish:

       - name: Build package
         working-directory: ${{ needs.determine-package.outputs.package_dir }}
         run: uv build

+      - name: Verify package
+        working-directory: ${{ needs.determine-package.outputs.package_dir }}
+        run: |
+          # Check that dist files were created
+          ls -lh dist/
+          # Verify package metadata (requires twine)
+          uv run pip install twine
+          uv run twine check dist/*

       - name: Publish to Artifact Registry
🤖 Prompt for AI Agents
In .github/workflows/publish.yml around lines 92 to 98, add a verification step
between the Build package and Publish to Artifact Registry steps that runs
package validation in the same working-directory (${{
needs.determine-package.outputs.package_dir }}); the step should validate
package metadata (e.g., version, manifest, checksums), run any lightweight
post-build checks (lint/verify or unit smoke tests), and fail the job on errors
so publishing is blocked when verification fails.

🧹 Nitpick | 🔵 Trivial

Consider uploading build artifacts for audit trail.

Uploading the built distributions as workflow artifacts provides an audit trail and allows manual inspection if issues arise. This is particularly valuable for published packages.

Add after the build step:

       - name: Build package
         working-directory: ${{ needs.determine-package.outputs.package_dir }}
         run: uv build

+      - name: Upload build artifacts
+        uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
+        with:
+          name: dist-${{ needs.determine-package.outputs.package_name }}-${{ needs.determine-package.outputs.version }}
+          path: ${{ needs.determine-package.outputs.package_dir }}/dist/
+          retention-days: 90

       - name: Publish to Artifact Registry
🤖 Prompt for AI Agents
.github/workflows/publish.yml around lines 92 to 98: after the "Build package"
step, add a step to upload the built distribution artifacts (e.g., the dist or
build output directory from ${{ needs.determine-package.outputs.package_dir }})
using actions/upload-artifact so the workflow retains a copy for auditing and
manual inspection; specify a clear artifact name (like package-build) and set
the artifact path to the build output directory (and optionally a retention-days
value) to ensure the built files are stored before publishing.


- name: Job summary
run: |
echo "## 📦 Published: ${{ needs.determine-package.outputs.package_name }} v${{ needs.determine-package.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Registry: \`us-central1-python.pkg.dev/assured-oss-457903/python-libs\`" >> $GITHUB_STEP_SUMMARY
2 changes: 1 addition & 1 deletion .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ jobs:

- name: Check Quality Gate
if: steps.sonar-scan.outcome == 'success'
uses: sonarsource/sonarqube-quality-gate-action@master
uses: sonarsource/sonarqube-quality-gate-action@cf038b0e0cdecfa9e56c198bbb7d21d751d62c3b # v1.2.0
timeout-minutes: 5
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/validate-cruft.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ on:
- cron: '0 9 * * 1'
workflow_dispatch:

permissions:
contents: read

jobs:
validate-cruft:
runs-on: ubuntu-latest
Expand Down
Loading
Loading