Skip to content
Open
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
13 changes: 13 additions & 0 deletions .github/actions/setup-hatch/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: "Set up Hatch build tooling"
description: >-
Install the pinned hatch / hatchling / virtualenv toolchain used to build
and publish the package. Assumes Python is already set up by the caller.

runs:
using: "composite"
steps:
- shell: bash
run: |
python -m pip install --upgrade pip
pip install "virtualenv<20.36"
pip install hatchling==1.27.0 hatch==1.14.0
41 changes: 41 additions & 0 deletions .github/actions/setup-sfw/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: "Set up Socket Firewall"
description: >-
Set up the requested Python/uv toolchain and install Socket Firewall so
subsequent steps can run package-manager commands wrapped with `sfw`.
Defaults to free/anonymous mode (no API token -- safe on untrusted /
Dependabot / fork PRs). Pass mode: firewall-enterprise + socket-token for
full org-policy enforcement on trusted maintainer PRs.

inputs:
python:
description: "Set up Python 3.12"
default: "false"
uv:
description: "Install uv (implies Python)"
default: "false"
mode:
description: "socketdev/action mode: firewall-free or firewall-enterprise"
default: "firewall-free"
socket-token:
description: "Socket API token (only used/required for firewall-enterprise)"
default: ""

runs:
using: "composite"
steps:
- if: ${{ inputs.python == 'true' || inputs.uv == 'true' }}
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"

# Official Socket setup action. Wires up sfw routing correctly.
# socket-token is ignored in firewall-free mode and empty when absent.
- uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2
with:
mode: ${{ inputs.mode }}
socket-token: ${{ inputs.socket-token }}

- if: ${{ inputs.uv == 'true' }}
name: Install uv
shell: bash
run: python -m pip install --upgrade pip uv
63 changes: 63 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Dependabot configuration for socket-sdk-python.
#
# Design notes:
# - Python deps are grouped into a weekly PR (minor/patch), with a
# separate group for majors so breaking bumps stay reviewable.
# - GitHub Actions are grouped similarly into one weekly PR, and Dependabot
# scans both the workflows and the local composite actions.
# - 7-day cooldown enforced across all ecosystems.
# - This repo ships no Dockerfile, so there is no docker ecosystem entry.

version: 2
updates:

# Python deps (uv-tracked via uv.lock)
- package-ecosystem: "uv"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 2
groups:
python-minor-patch:
patterns:
- "*"
update-types:
- "minor"
- "patch"
python-major:
patterns:
- "*"
update-types:
- "major"
labels:
- "dependencies"
- "python:uv"
commit-message:
prefix: "chore"
include: "scope"
cooldown:
default-days: 7

# GitHub Actions used in workflows and local composite actions.
- package-ecosystem: "github-actions"
directories:
- "/"
- "/.github/actions/*"
schedule:
interval: "weekly"
open-pull-requests-limit: 2
groups:
github-actions-minor-patch:
patterns:
- "*"
update-types:
- "minor"
- "patch"
labels:
- "dependencies"
- "github-actions"
commit-message:
prefix: "ci"
include: "scope"
cooldown:
default-days: 7
204 changes: 204 additions & 0 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
name: dependency-review

# Supply-chain guardrails for dependency-update PRs -- for BOTH Dependabot
# and maintainers. `inspect` classifies the PR, then exactly one Socket
# Firewall (sfw) install smoke job runs when Python deps change:
#
# - python-sfw-smoke-enterprise -- trusted authors: any in-repo (non-fork)
# PR other than Dependabot's (i.e. someone with write access). Runs the
# authenticated enterprise edition for full org-policy enforcement. The
# SOCKET_SFW_API_TOKEN is scoped to the `socket-firewall` environment, so
# it is the ONLY job that can read the token.
# - python-sfw-smoke-free -- everyone else (Dependabot + all fork PRs from
# external contributors). Anonymous free edition, no token. This job never
# references the secret.
#
# Splitting the jobs (rather than picking a mode in one job) keeps the token
# out of scope on every untrusted run and satisfies zizmor's
# `secrets-outside-env` audit without suppressing it. The free path runs in
# the unprivileged `pull_request` context with no secret-leak surface.
#
# Pattern adapted from SocketDev/socket-python-cli.

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]

permissions:
contents: read

concurrency:
group: dependency-review-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
inspect:
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
python_deps_changed: ${{ steps.diff.outputs.python_deps_changed }}
workflow_or_action_changed: ${{ steps.diff.outputs.workflow_or_action_changed }}
is_trusted: ${{ steps.trust.outputs.is_trusted }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false

- name: Inspect changed files
id: diff
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
CHANGED_FILES="$(git diff --name-only "$BASE_SHA" "$HEAD_SHA")"

{
echo "## Changed files"
echo '```'
printf '%s\n' "$CHANGED_FILES"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"

has_file() {
local pattern="$1"
if printf '%s\n' "$CHANGED_FILES" | grep -Eq "$pattern"; then
echo "true"
else
echo "false"
fi
}

{
echo "python_deps_changed=$(has_file '^(pyproject\.toml|uv\.lock)$')"
echo "workflow_or_action_changed=$(has_file '^\.github/workflows/|^\.github/actions/|^\.github/dependabot\.yml$')"
} >> "$GITHUB_OUTPUT"

- name: Classify PR trust
id: trust
# Trusted == any in-repo (non-fork) PR that isn't Dependabot's. Only
# accounts with write access can push a branch to this repo, so a
# non-fork PR already implies a trusted author -- the same boundary
# GitHub uses to decide whether secrets are exposed at all.
#
# NB: author_association is deliberately NOT used to require strict org
# membership. It only reflects PUBLIC org membership, so private members
# (the common case) show up as CONTRIBUTOR and would be misclassified.
# Reliable strict-membership detection would need a read:org token or
# public membership. This step references NO secret regardless -- it
# only decides which smoke job runs.
env:
IS_DEPENDABOT: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }}
IS_FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
AUTHOR_ASSOC: ${{ github.event.pull_request.author_association }}
run: |
is_trusted=false
if [ "$IS_DEPENDABOT" != "true" ] && [ "$IS_FORK" != "true" ]; then
is_trusted=true
fi

echo "is_trusted=$is_trusted" >> "$GITHUB_OUTPUT"
{
echo "## Socket Firewall edition: \`$([ "$is_trusted" = true ] && echo enterprise || echo free)\`"
echo "- author_association: \`$AUTHOR_ASSOC\`"
echo "- dependabot: \`$IS_DEPENDABOT\` | fork: \`$IS_FORK\`"
} >> "$GITHUB_STEP_SUMMARY"

- name: Summarize review expectations
env:
PR_URL: ${{ github.event.pull_request.html_url }}
run: |
{
echo "## Dependency Review Checklist"
echo "- PR: $PR_URL"
echo "- Confirm upstream release notes before merge"
echo "- Do not treat a dependency PR as trusted solely because of the actor"
echo "- This workflow runs in pull_request context only; no publish secrets are exposed"
} >> "$GITHUB_STEP_SUMMARY"

# Untrusted PRs (Dependabot, forks, outside collaborators, externals):
# anonymous free edition. Never references the token.
python-sfw-smoke-free:
needs: inspect
if: needs.inspect.outputs.python_deps_changed == 'true' && needs.inspect.outputs.is_trusted != 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
persist-credentials: false

- uses: ./.github/actions/setup-sfw
with:
uv: "true"
mode: firewall-free

- name: Sync project through Socket Firewall (free)
env:
UV_PYTHON: "3.12"
UV_PYTHON_DOWNLOADS: never
run: sfw uv sync --locked --extra test --extra dev

- name: Import smoke test
run: |
uv run python -c "
import socketdev
from socketdev import socketdev as SocketDevClient
from socketdev.core.api import API
from socketdev.version import __version__
print('import smoke OK', __version__)
"

# Trusted SocketDev members: authenticated enterprise edition. The token is
# scoped to the `socket-firewall` environment, so only this job can read it.
python-sfw-smoke-enterprise:
needs: inspect
if: needs.inspect.outputs.python_deps_changed == 'true' && needs.inspect.outputs.is_trusted == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
environment: socket-firewall
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
persist-credentials: false

- uses: ./.github/actions/setup-sfw
with:
uv: "true"
mode: firewall-enterprise
socket-token: ${{ secrets.SOCKET_SFW_API_TOKEN }}

- name: Sync project through Socket Firewall (enterprise)
# See free job for the UV_PYTHON rationale: .python-version pins an
# exact patch that uv would otherwise fetch from GitHub through the
# firewall (blocked by its TLS interception); use the runner's Python.
env:
UV_PYTHON: "3.12"
UV_PYTHON_DOWNLOADS: never
run: sfw uv sync --locked --extra test --extra dev

- name: Import smoke test
run: |
uv run python -c "
import socketdev
from socketdev import socketdev as SocketDevClient
from socketdev.core.api import API
from socketdev.version import __version__
print('import smoke OK', __version__)
"

workflow-notice:
needs: inspect
if: needs.inspect.outputs.workflow_or_action_changed == 'true'
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: Flag workflow-sensitive updates
run: |
{
echo "## Sensitive File Notice"
echo "This PR changes workflow, composite-action, or dependabot config files."
echo "Require explicit human review before merge."
} >> "$GITHUB_STEP_SUMMARY"
22 changes: 15 additions & 7 deletions .github/workflows/pr-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,21 @@ on:
pull_request:
types: [opened, synchronize, ready_for_review]

# Cancel an in-flight preview when the PR is pushed again -- previews publish
# to Test PyPI, so superseded runs shouldn't keep churning.
concurrency:
group: pr-preview-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
preview:
# Skip on:
# - PRs from forks (no access to publish secrets / OIDC)
# - Dependabot PRs: preview-publishing a dependency bump to Test PyPI is
# pointless (no package version bump) and would fail the version check.
if: >-
github.event.pull_request.head.repo.full_name == github.repository &&
github.event.pull_request.user.login != 'dependabot[bot]'
runs-on: ubuntu-latest
permissions:
id-token: write
Expand All @@ -19,13 +32,8 @@ jobs:
with:
python-version: '3.13'

# Install all dependencies from pyproject.toml
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install "virtualenv<20.36"
pip install hatchling==1.27.0
pip install hatch==1.14.0
- name: Install build tooling
uses: ./.github/actions/setup-hatch

- name: Inject full dynamic version
run: python .hooks/sync_version.py --dev
Expand Down
11 changes: 3 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,9 @@ jobs:
with:
python-version: '3.13'

# Install all dependencies from pyproject.toml
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install "virtualenv<20.36"
pip install hatchling==1.27.0
pip install hatch==1.14.0

- name: Install build tooling
uses: ./.github/actions/setup-hatch

- name: Get Version
id: version
env:
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/version-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ permissions:

jobs:
check_version:
# Skip on Dependabot PRs: they bump dependencies (touching uv.lock /
# pyproject.toml) without bumping the package version, so the increment
# check would always fail. Package-version bumps come from maintainer PRs.
if: github.event.pull_request.user.login != 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand Down
Loading
Loading