docs: clarify Sourcegraph endpoint/token are required, recommend env … #25
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build release | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Package version to publish, for example 0.1.0 or v0.1.0" | |
| required: true | |
| type: string | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| concurrency: | |
| group: release-${{ github.event_name == 'workflow_dispatch' && inputs.version || github.ref_name }} | |
| cancel-in-progress: false | |
| defaults: | |
| run: | |
| shell: bash | |
| jobs: | |
| release_ref: | |
| name: Resolve release tag | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| tag: ${{ steps.release.outputs.tag }} | |
| version: ${{ steps.release.outputs.version }} | |
| steps: | |
| - name: Resolve release tag | |
| id: release | |
| env: | |
| RELEASE_INPUT: ${{ github.event_name == 'workflow_dispatch' && inputs.version || github.ref_name }} | |
| run: | | |
| release_input="${RELEASE_INPUT}" | |
| if [[ ! "${release_input}" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "::error title=Invalid release version::Use MAJOR.MINOR.PATCH or vMAJOR.MINOR.PATCH, got '${release_input}'." | |
| exit 1 | |
| fi | |
| release_version="${release_input#v}" | |
| release_tag="v${release_version}" | |
| echo "tag=${release_tag}" >> "${GITHUB_OUTPUT}" | |
| echo "version=${release_version}" >> "${GITHUB_OUTPUT}" | |
| validate: | |
| name: Validate | |
| needs: release_ref | |
| uses: ./.github/workflows/validate.yml | |
| with: | |
| ref: ${{ needs.release_ref.outputs.tag }} | |
| build-package: false | |
| wheel: | |
| name: Build wheel | |
| needs: release_ref | |
| runs-on: ubuntu-24.04 | |
| env: | |
| IMPORT_NAME: src_py_lib | |
| PYTHON_VERSION: "3.11" | |
| UV_VERSION: "0.11.7" | |
| steps: | |
| - name: Check out release ref | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| ref: ${{ needs.release_ref.outputs.tag }} | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Cache uv | |
| uses: actions/cache@v6 | |
| 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 "uv==${UV_VERSION}" | |
| - name: Validate release inputs | |
| id: release | |
| env: | |
| RELEASE_TAG: ${{ needs.release_ref.outputs.tag }} | |
| run: | | |
| release_tag="${RELEASE_TAG}" | |
| if [[ ! "${release_tag}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "::error title=Invalid release tag::Use a vMAJOR.MINOR.PATCH tag, got '${release_tag}'." | |
| exit 1 | |
| fi | |
| if ! git rev-parse --verify --quiet "refs/tags/${release_tag}" >/dev/null; then | |
| 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::Tag a commit from main, then rerun the release." | |
| exit 1 | |
| fi | |
| echo "tag=${release_tag}" >> "${GITHUB_OUTPUT}" | |
| echo "version=${release_tag#v}" >> "${GITHUB_OUTPUT}" | |
| - name: Validate runtime dependencies | |
| run: | | |
| runtime_requirements_file="build/release/runtime-requirements.txt" | |
| mkdir -p "$(dirname "${runtime_requirements_file}")" | |
| uv export \ | |
| --no-dev \ | |
| --no-emit-project \ | |
| --no-hashes \ | |
| --no-header \ | |
| --no-annotate \ | |
| --frozen \ | |
| --output-file "${runtime_requirements_file}" | |
| if grep -Eq '(^-e[[:space:]]|^(\.\.?/)|(^|[[:space:]])file:| @ (\.\.?/|file:))' "${runtime_requirements_file}"; then | |
| echo "::error title=Unexpected local dependency::Runtime requirements must resolve from PyPI." | |
| cat "${runtime_requirements_file}" | |
| exit 1 | |
| fi | |
| - name: Build distributions | |
| id: build | |
| run: | | |
| release_version="${{ steps.release.outputs.version }}" | |
| dist_dir="build/release/dist" | |
| rm -rf build/release | |
| mkdir -p "${dist_dir}" | |
| shopt -s nullglob | |
| uv build --wheel --sdist --out-dir "${dist_dir}" --no-create-gitignore | |
| project_wheels=("${dist_dir}"/*.whl) | |
| if [[ "${#project_wheels[@]}" -ne 1 ]]; then | |
| echo "::error title=Unexpected wheel count::Expected one project wheel, found ${#project_wheels[@]}." | |
| exit 1 | |
| fi | |
| source_distributions=("${dist_dir}"/*.tar.gz) | |
| if [[ "${#source_distributions[@]}" -ne 1 ]]; then | |
| echo "::error title=Unexpected source distribution count::Expected one source distribution, found ${#source_distributions[@]}." | |
| exit 1 | |
| fi | |
| wheel_path="${project_wheels[0]}" | |
| wheel_name="$(basename "${wheel_path}")" | |
| source_distribution_path="${source_distributions[0]}" | |
| source_distribution_name="$(basename "${source_distribution_path}")" | |
| case "${wheel_name}" in | |
| src_py_lib-"${release_version}"-*.whl) | |
| ;; | |
| *) | |
| echo "::error title=Wheel version mismatch::Expected wheel version ${release_version}, got '${wheel_name}'." | |
| exit 1 | |
| ;; | |
| esac | |
| case "${source_distribution_name}" in | |
| src_py_lib-"${release_version}".tar.gz) | |
| ;; | |
| *) | |
| echo "::error title=Source distribution version mismatch::Expected source distribution version ${release_version}, got '${source_distribution_name}'." | |
| exit 1 | |
| ;; | |
| esac | |
| wheel_checksum_path="${wheel_path}.sha256" | |
| source_distribution_checksum_path="${source_distribution_path}.sha256" | |
| ( | |
| cd "$(dirname "${wheel_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}" | |
| 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 | |
| env: | |
| WHEEL_PATH: ${{ steps.build.outputs.wheel_path }} | |
| run: | | |
| validation_dir="$(mktemp -d)" | |
| python -m venv "${validation_dir}/venv" | |
| . "${validation_dir}/venv/bin/activate" | |
| python -m pip install "${GITHUB_WORKSPACE}/${WHEEL_PATH}" | |
| ( | |
| cd "${validation_dir}" | |
| 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: Write release notes | |
| id: notes | |
| 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 | |
| Install from PyPI: | |
| \`\`\`sh | |
| pip install src-py-lib | |
| \`\`\` | |
| Install from the release wheel: | |
| \`\`\`sh | |
| 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 assets with the matching \`.sha256\` files. | |
| EOF | |
| echo "path=${notes_path}" >> "${GITHUB_OUTPUT}" | |
| - name: Upload workflow artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: src-py-lib-release | |
| path: | | |
| ${{ steps.build.outputs.wheel_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 | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: pypi-distributions | |
| path: | | |
| ${{ steps.build.outputs.wheel_path }} | |
| ${{ steps.build.outputs.source_distribution_path }} | |
| github-release: | |
| name: Publish GitHub release assets | |
| needs: [release_ref, validate, wheel] | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download release assets | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: src-py-lib-release | |
| path: release-assets | |
| - name: Publish GitHub release assets | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| RELEASE_TAG: ${{ needs.release_ref.outputs.tag }} | |
| run: | | |
| release_tag="${RELEASE_TAG}" | |
| notes_path="$(find release-assets -name release-notes.md -print -quit)" | |
| mapfile -t release_assets < <(find release-assets -type f ! -name release-notes.md | sort) | |
| if [[ -z "${notes_path}" ]]; then | |
| echo "::error title=Missing release notes::release-notes.md was not found in release artifact." | |
| exit 1 | |
| fi | |
| if [[ "${#release_assets[@]}" -eq 0 ]]; then | |
| echo "::error title=Missing release assets::No release assets were downloaded." | |
| exit 1 | |
| fi | |
| 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}" "${release_assets[@]}" --clobber | |
| else | |
| gh release create "${release_tag}" \ | |
| "${release_assets[@]}" \ | |
| --title "${release_tag}" \ | |
| --notes-file "${notes_path}" \ | |
| --verify-tag | |
| fi | |
| pypi: | |
| name: Publish PyPI package | |
| needs: [validate, wheel] | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: read | |
| id-token: write | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/project/src-py-lib/ | |
| steps: | |
| - name: Download built distribution | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: pypi-distributions | |
| path: dist | |
| - name: Publish PyPI package | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: dist | |
| skip-existing: true |