Implementation:Langchain ai Langchain PyPI Trusted Publishing Production
Overview
Concrete tool for publishing validated LangChain packages to production PyPI and creating a GitHub Release, provided by the publish and mark-release jobs in _release.yml.
Description
The production publishing stage consists of two sequential jobs:
Job 1: publish (lines 534-578)
Uploads the distribution artifacts to production PyPI using the pypa/gh-action-pypi-publish action with OIDC trusted publishing. Key characteristics:
- Requires
id-token: writepermission for OIDC authentication. - Uses the default PyPI repository URL (production
https://upload.pypi.org/legacy/). - Does not set
skip-existing(unlike Test PyPI), so re-uploading the same version will fail -- this is intentional to prevent accidental overwrites. - Attestations are disabled as a temporary workaround.
- Only runs if all preceding jobs succeeded or were skipped (
!cancelled() && !failure()).
Job 2: mark-release (lines 580-620)
Creates a GitHub Release using the ncipollo/release-action:
- Tags the release as
<pkg-name>==<version>(e.g.,langchain-core==1.2.11). - Attaches the distribution artifacts (
dist/*) to the release. - Sets the release body to the auto-generated release notes from the
release-notesjob. - Pins the release to the exact commit SHA that triggered the workflow.
- Marks the release as "latest" only if the package is
langchain-core.
Usage
These jobs run automatically at the end of the release pipeline. The publish job depends on all validation jobs passing. The mark-release job depends on publish succeeding.
Code Reference
Source Location: .github/workflows/_release.yml (lines 534-620)
Publish Job:
publish:
needs:
- build
- release-notes
- test-pypi-publish
- pre-release-checks
- test-dependents
- test-prior-published-packages-against-new-core
if: ${{ !cancelled() && !failure() }}
runs-on: ubuntu-latest
permissions:
id-token: write
defaults:
run:
working-directory: ${{ inputs.working-directory }}
steps:
- uses: actions/checkout@v6
- name: Set up Python + uv
uses: "./.github/actions/uv_setup"
with:
python-version: ${{ env.PYTHON_VERSION }}
- uses: actions/download-artifact@v7
with:
name: dist
path: ${{ inputs.working-directory }}/dist/
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: ${{ inputs.working-directory }}/dist/
verbose: true
print-hash: true
attestations: false
Mark Release Job:
mark-release:
needs:
- build
- release-notes
- test-pypi-publish
- pre-release-checks
- publish
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v6
- name: Set up Python + uv
uses: "./.github/actions/uv_setup"
with:
python-version: ${{ env.PYTHON_VERSION }}
- uses: actions/download-artifact@v7
with:
name: dist
path: ${{ inputs.working-directory }}/dist/
- name: Create Tag
uses: ncipollo/release-action@v1
with:
artifacts: "dist/*"
token: ${{ secrets.GITHUB_TOKEN }}
generateReleaseNotes: false
tag: ${{ needs.build.outputs.pkg-name }}==${{ needs.build.outputs.version }}
body: ${{ needs.release-notes.outputs.release-body }}
commit: ${{ github.sha }}
makeLatest: ${{ needs.build.outputs.pkg-name == 'langchain-core' }}
Invocation: Automatic, as the final jobs in the release pipeline.
I/O Contract
| Direction | Name | Type | Description |
|---|---|---|---|
| Input | dist/*.whl | File | Built wheel artifact |
| Input | dist/*.tar.gz | File | Built source distribution |
| Input | OIDC token | Token | Short-lived identity token for PyPI authentication |
| Input | pkg-name | string | Package name from build job (e.g., langchain-core)
|
| Input | version | string | Version from build job (e.g., 1.2.11)
|
| Input | release-body | string | Auto-generated release notes |
| Input | github.sha | string | Commit SHA that triggered the workflow |
| Input | GITHUB_TOKEN | Secret | Token for creating the GitHub Release |
| Output | PyPI package | Published package | Available at https://pypi.org/project/<pkg-name>/<version>/
|
| Output | GitHub Release | Release | Tagged as <pkg-name>==<version> with artifacts and release notes
|
Usage Examples
Example 1: Verifying the published package
# After publish completes:
pip install langchain-core==1.2.11
python -c "import langchain_core; print(langchain_core.__version__)"
Example 2: Viewing the GitHub Release
# The release is available at:
# https://github.com/langchain-ai/langchain/releases/tag/langchain-core==1.2.11
# Or via gh CLI:
gh release view "langchain-core==1.2.11" --repo langchain-ai/langchain
Example 3: Tag format examples
langchain-core==1.2.11
langchain-openai==1.1.9
langchain-anthropic==0.3.12
langchain==0.3.25
langchain-text-splitters==0.3.8
Example 4: The makeLatest flag behavior
# Only langchain-core releases are marked as "Latest" on GitHub Releases:
makeLatest: ${{ needs.build.outputs.pkg-name == 'langchain-core' }}
# For langchain-core==1.2.11 -> makeLatest = true
# For langchain-openai==1.1.9 -> makeLatest = false