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
89 changes: 6 additions & 83 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,92 +2,15 @@ name: CI

on:
pull_request:
push:
branches:
- main

permissions:
contents: read

defaults:
run:
shell: bash
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
name: Build and test
runs-on: ubuntu-24.04
env:
IMPORT_NAME: src_py_lib
PYTHON_VERSION: "3.11"
UV_VERSION: "0.11.7"

steps:
- name: Check out code
uses: actions/checkout@v6
with:
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip

- name: Install uv
run: |
python -m pip install --upgrade pip
python -m pip install "uv==${UV_VERSION}"

- name: Validate lockfile
run: uv lock --check

- name: Lint Markdown
run: npx --yes markdownlint-cli2

- name: Lint Python
run: uv run --frozen ruff check .

- name: Check Python formatting
run: uv run --frozen ruff format --check .

- name: Type check
run: uv run --frozen pyright

- name: Run tests
run: uv run --frozen python -m unittest discover -s tests

- name: Smoke test source checkout import
run: |
uv run --frozen python - <<'PY'
import os

import src_py_lib

if src_py_lib.__name__ != os.environ["IMPORT_NAME"]:
raise SystemExit(f"unexpected import name: {src_py_lib.__name__}")
PY

- name: Build wheel
run: uv build --wheel --out-dir dist --no-create-gitignore

- name: Smoke test installed wheel
run: |
python -m venv build/ci-venv
. build/ci-venv/bin/activate
python -m pip install --upgrade pip
python -m pip install dist/*.whl
python - <<'PY'
import os

import src_py_lib

if src_py_lib.__name__ != os.environ["IMPORT_NAME"]:
raise SystemExit(f"unexpected import name: {src_py_lib.__name__}")
PY

- name: Upload wheel artifact
uses: actions/upload-artifact@v7
with:
name: src-py-lib-wheel
path: dist/*.whl
validate:
name: Validate
uses: ./.github/workflows/validate.yml
86 changes: 58 additions & 28 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,15 @@ defaults:
shell: bash

jobs:
validate:
name: Validate
uses: ./.github/workflows/validate.yml
with:
ref: ${{ github.event.inputs.tag || github.ref }}

wheel:
name: Build wheel
needs: validate
runs-on: ubuntu-24.04
env:
IMPORT_NAME: src_py_lib
Expand All @@ -45,6 +52,14 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip

- name: Cache uv
uses: actions/cache@v4
with:
path: ~/.cache/uv
key: uv-${{ runner.os }}-py${{ env.PYTHON_VERSION }}-${{ hashFiles('uv.lock') }}
restore-keys: |
uv-${{ runner.os }}-py${{ env.PYTHON_VERSION }}-

- name: Install build tools
run: |
python -m pip install --upgrade pip
Expand All @@ -62,6 +77,14 @@ jobs:
echo "::error title=Missing tag::Tag '${release_tag}' was not fetched. Create and push it before running this workflow."
exit 1
fi
tag_revision="$(git rev-list -n 1 "${release_tag}")"
git fetch --no-tags origin main
main_revision="$(git rev-parse origin/main)"
if ! git merge-base --is-ancestor "${tag_revision}" "${main_revision}"; then
echo "::error title=Tag is not on main::Tag '${release_tag}' points at ${tag_revision}, which is not reachable from origin/main."
echo "::error::Merge the release PR first, then tag the main commit."
exit 1
fi

project_version=$(uv run --frozen python - <<'PY'
import tomllib
Expand All @@ -77,22 +100,6 @@ jobs:

echo "tag=${release_tag}" >> "${GITHUB_OUTPUT}"

- name: Validate package
run: |
uv lock --check
uv run --frozen ruff check .
uv run --frozen ruff format --check .
uv run --frozen pyright
uv run --frozen python -m unittest discover -s tests
uv run --frozen python - <<'PY'
import os

import src_py_lib

if src_py_lib.__name__ != os.environ["IMPORT_NAME"]:
raise SystemExit(f"unexpected import name: {src_py_lib.__name__}")
PY

- name: Build distributions
id: build
run: |
Expand All @@ -115,17 +122,24 @@ jobs:
wheel_path="${project_wheels[0]}"
wheel_name="$(basename "${wheel_path}")"
source_distribution_path="${source_distributions[0]}"
checksum_path="${wheel_path}.sha256"
source_distribution_name="$(basename "${source_distribution_path}")"
wheel_checksum_path="${wheel_path}.sha256"
source_distribution_checksum_path="${source_distribution_path}.sha256"

(
cd "$(dirname "${wheel_path}")"
shasum -a 256 "${wheel_name}" > "$(basename "${checksum_path}")"
shasum -a 256 "${wheel_name}" > "$(basename "${wheel_checksum_path}")"
shasum -a 256 "${source_distribution_name}" > "$(basename "${source_distribution_checksum_path}")"
)

echo "wheel_path=${wheel_path}" >> "${GITHUB_OUTPUT}"
echo "wheel_name=${wheel_name}" >> "${GITHUB_OUTPUT}"
echo "source_distribution_path=${source_distribution_path}" >> "${GITHUB_OUTPUT}"
echo "checksum_path=${checksum_path}" >> "${GITHUB_OUTPUT}"
{
echo "wheel_path=${wheel_path}"
echo "wheel_name=${wheel_name}"
echo "source_distribution_path=${source_distribution_path}"
echo "source_distribution_name=${source_distribution_name}"
echo "wheel_checksum_path=${wheel_checksum_path}"
echo "source_distribution_checksum_path=${source_distribution_checksum_path}"
} >> "${GITHUB_OUTPUT}"

- name: Smoke test installed wheel
run: |
Expand All @@ -147,6 +161,7 @@ jobs:
run: |
release_tag="${{ steps.release.outputs.tag }}"
wheel_name="${{ steps.build.outputs.wheel_name }}"
source_distribution_name="${{ steps.build.outputs.source_distribution_name }}"
notes_path="build/release/release-notes.md"
cat > "${notes_path}" <<EOF
## Install
Expand All @@ -163,13 +178,19 @@ jobs:
pip install "https://github.com/sourcegraph/src-py-lib/releases/download/${release_tag}/${wheel_name}"
\`\`\`

Source distribution:

\`\`\`sh
curl -LO "https://github.com/sourcegraph/src-py-lib/releases/download/${release_tag}/${source_distribution_name}"
\`\`\`

Or install this tag with uv:

\`\`\`sh
uv add "git+https://github.com/sourcegraph/src-py-lib.git@${release_tag}"
\`\`\`

Verify downloaded wheel assets with the matching \`.sha256\` file.
Verify downloaded assets with the matching \`.sha256\` files.
EOF
echo "path=${notes_path}" >> "${GITHUB_OUTPUT}"

Expand All @@ -179,7 +200,9 @@ jobs:
name: src-py-lib-release
path: |
${{ steps.build.outputs.wheel_path }}
${{ steps.build.outputs.checksum_path }}
${{ steps.build.outputs.source_distribution_path }}
${{ steps.build.outputs.wheel_checksum_path }}
${{ steps.build.outputs.source_distribution_checksum_path }}
${{ steps.notes.outputs.path }}

- name: Upload PyPI artifact
Expand All @@ -196,16 +219,23 @@ jobs:
run: |
release_tag="${{ steps.release.outputs.tag }}"
wheel_path="${{ steps.build.outputs.wheel_path }}"
checksum_path="${{ steps.build.outputs.checksum_path }}"
source_distribution_path="${{ steps.build.outputs.source_distribution_path }}"
wheel_checksum_path="${{ steps.build.outputs.wheel_checksum_path }}"
source_distribution_checksum_path="${{ steps.build.outputs.source_distribution_checksum_path }}"
notes_path="${{ steps.notes.outputs.path }}"
release_assets=(
"${wheel_path}"
"${source_distribution_path}"
"${wheel_checksum_path}"
"${source_distribution_checksum_path}"
)

if gh release view "${release_tag}" >/dev/null 2>&1; then
gh release edit "${release_tag}" --title "${release_tag}" --notes-file "${notes_path}"
gh release upload "${release_tag}" "${wheel_path}" "${checksum_path}" --clobber
gh release upload "${release_tag}" "${release_assets[@]}" --clobber
else
gh release create "${release_tag}" \
"${wheel_path}" \
"${checksum_path}" \
"${release_assets[@]}" \
--title "${release_tag}" \
--notes-file "${notes_path}" \
--verify-tag
Expand Down
124 changes: 124 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
name: Validate

on:
workflow_call:
inputs:
ref:
description: "Git ref to validate. Defaults to the caller's ref."
required: false
type: string

permissions:
contents: read

defaults:
run:
shell: bash

jobs:
package:
name: Validate package
runs-on: ubuntu-24.04
env:
ACTIONLINT_VERSION: "1.7.12"
IMPORT_NAME: src_py_lib
MARKDOWNLINT_CLI2_VERSION: "0.22.1"
PYTHON_VERSION: "3.11"
UV_VERSION: "0.11.7"

steps:
- name: Check out code
uses: actions/checkout@v6
with:
persist-credentials: false
ref: ${{ inputs.ref || github.ref }}

- name: Cache actionlint
id: cache-actionlint
uses: actions/cache@v4
with:
path: ~/.local/bin/actionlint
key: actionlint-${{ runner.os }}-${{ runner.arch }}-${{ env.ACTIONLINT_VERSION }}

- name: Install actionlint
if: steps.cache-actionlint.outputs.cache-hit != 'true'
run: |
mkdir -p "${HOME}/.local/bin"
go install "github.com/rhysd/actionlint/cmd/actionlint@v${ACTIONLINT_VERSION}"
install -m 0755 "${HOME}/go/bin/actionlint" "${HOME}/.local/bin/actionlint"

- name: Lint GitHub Actions
run: |
"${HOME}/.local/bin/actionlint"

- name: Cache npm
uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-markdownlint-cli2-${{ env.MARKDOWNLINT_CLI2_VERSION }}

- name: Lint Markdown
run: npx --yes "markdownlint-cli2@${MARKDOWNLINT_CLI2_VERSION}"

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip

- name: Cache uv
uses: actions/cache@v4
with:
path: ~/.cache/uv
key: uv-${{ runner.os }}-py${{ env.PYTHON_VERSION }}-${{ hashFiles('uv.lock') }}
restore-keys: |
uv-${{ runner.os }}-py${{ env.PYTHON_VERSION }}-

- name: Install uv
run: |
python -m pip install --upgrade pip
python -m pip install "uv==${UV_VERSION}"

- name: Validate lockfile
run: uv lock --check

- name: Lint Python
run: uv run --frozen ruff check .

- name: Check Python formatting
run: uv run --frozen ruff format --check .

- name: Type check
run: uv run --frozen pyright

- name: Run tests
run: uv run --frozen python -m unittest discover -s tests

- name: Smoke test source checkout import
run: |
uv run --frozen python - <<'PY'
import os

import src_py_lib

if src_py_lib.__name__ != os.environ["IMPORT_NAME"]:
raise SystemExit(f"unexpected import name: {src_py_lib.__name__}")
PY

- name: Build wheel
run: uv build --wheel --out-dir dist --no-create-gitignore

- name: Smoke test installed wheel
run: |
python -m venv build/ci-venv
. build/ci-venv/bin/activate
python -m pip install --upgrade pip
python -m pip install dist/*.whl
python - <<'PY'
import os

import src_py_lib

if src_py_lib.__name__ != os.environ["IMPORT_NAME"]:
raise SystemExit(f"unexpected import name: {src_py_lib.__name__}")
PY
Loading
Loading