diff --git a/.github/workflows/_release.yml b/.github/workflows/_release.yml index 78d1624..5e1c369 100644 --- a/.github/workflows/_release.yml +++ b/.github/workflows/_release.yml @@ -6,44 +6,200 @@ # separate terms of service, privacy policy, and support # documentation. -name: Upload Python Package +name: Build and Publish Python Package on: release: types: [published] workflow_dispatch: + inputs: + working-directory: + required: true + type: string + default: 'libs/oci' -permissions: - contents: read +env: + PYTHON_VERSION: "3.11" + POETRY_VERSION: "1.7.1" jobs: - build-n-publish: - name: Build and publish Python 🐍 distribution 📦 to PyPI + build: + if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest - defaults: - run: - working-directory: libs/oci # Set default for all steps - environment: - name: pypi - url: https://pypi.org/p/langchain-oci + + outputs: + pkg-name: ${{ steps.check-version.outputs.pkg-name }} + version: ${{ steps.check-version.outputs.version }} + steps: - uses: actions/checkout@v4 + + - name: Set up Python + Poetry ${{ env.POETRY_VERSION }} + uses: "./.github/actions/poetry_setup" + with: + python-version: ${{ env.PYTHON_VERSION }} + poetry-version: ${{ env.POETRY_VERSION }} + working-directory: ${{ inputs.working-directory }} + cache-key: release + + - name: Build project for distribution + run: poetry build + working-directory: ${{ inputs.working-directory }} + + - name: Upload build + uses: actions/upload-artifact@v4 + with: + name: dist + path: ${{ inputs.working-directory }}/dist/ + + - name: Check Version + id: check-version + shell: bash + working-directory: ${{ inputs.working-directory }} + run: | + echo pkg-name="$(poetry version | cut -d ' ' -f 1)" >> $GITHUB_OUTPUT + echo version="$(poetry version --short)" >> $GITHUB_OUTPUT + + test-pypi-publish: + needs: build + runs-on: ubuntu-latest + environment: + name: testpypi + url: https://test.pypi.org/project/${{ needs.build.outputs.pkg-name }}/ + steps: + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: dist + path: ${{ inputs.working-directory }}/dist/ + - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.x" - - name: Build distribution 📦 + python-version: '3.x' + + - name: Publish distribution 📦 to TestPyPI + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.GH_LC_OCI_TESTPYPI_TOKEN }} run: | - pip install build - python -m build - - name: Validate + pip install twine + twine upload --repository-url https://test.pypi.org/legacy/ ${{ inputs.working-directory }}/dist/* -u $TWINE_USERNAME -p $TWINE_PASSWORD + + pre-release-checks: + needs: + - build + - test-pypi-publish + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + Poetry ${{ env.POETRY_VERSION }} + uses: "./.github/actions/poetry_setup" + with: + python-version: ${{ env.PYTHON_VERSION }} + poetry-version: ${{ env.POETRY_VERSION }} + working-directory: ${{ inputs.working-directory }} + + - name: Import published package + shell: bash + working-directory: ${{ inputs.working-directory }} + env: + PKG_NAME: ${{ needs.build.outputs.pkg-name }} + VERSION: ${{ needs.build.outputs.version }} + # Here we use: + # - The default regular PyPI index as the *primary* index, meaning + # that it takes priority (https://pypi.org/simple) + # - The test PyPI index as an extra index, so that any dependencies that + # are not found on test PyPI can be resolved and installed anyway. + # (https://test.pypi.org/simple). This will include the PKG_NAME==VERSION + # package because VERSION will not have been uploaded to regular PyPI yet. + # - attempt install again after 5 seconds if it fails because there is + # sometimes a delay in availability on test pypi + run: | + poetry run pip install \ + --extra-index-url https://test.pypi.org/simple/ \ + "$PKG_NAME==$VERSION" || \ + ( \ + sleep 5 && \ + poetry run pip install \ + --extra-index-url https://test.pypi.org/simple/ \ + "$PKG_NAME==$VERSION" \ + ) + + # Replace all dashes in the package name with underscores, + # since that's how Python imports packages with dashes in the name. + IMPORT_NAME="$(echo "$PKG_NAME" | sed s/-/_/g)" + + poetry run python -c "import $IMPORT_NAME; print(dir($IMPORT_NAME))" + + - name: Import test dependencies + run: poetry install --with test,test_integration + working-directory: ${{ inputs.working-directory }} + + # Overwrite the local version of the package with the test PyPI version. + - name: Import published package (again) + working-directory: ${{ inputs.working-directory }} + shell: bash + env: + PKG_NAME: ${{ needs.build.outputs.pkg-name }} + VERSION: ${{ needs.build.outputs.version }} + run: | + poetry run pip install \ + --extra-index-url https://test.pypi.org/simple/ \ + "$PKG_NAME==$VERSION" + + - name: Run unit tests + run: make tests + working-directory: ${{ inputs.working-directory }} + + - name: Run integration tests + run: make integration_tests + working-directory: ${{ inputs.working-directory }} + + - name: Get minimum versions + working-directory: ${{ inputs.working-directory }} + id: min-version + run: | + poetry run pip install packaging + min_versions="$(poetry run python $GITHUB_WORKSPACE/.github/scripts/get_min_versions.py pyproject.toml)" + echo "min-versions=$min_versions" >> "$GITHUB_OUTPUT" + echo "min-versions=$min_versions" + + - name: Run unit tests with minimum dependency versions + if: ${{ steps.min-version.outputs.min-versions != '' }} + env: + MIN_VERSIONS: ${{ steps.min-version.outputs.min-versions }} run: | - pip install dist/*.whl - python -c "import langchain_oci;" + poetry run pip install $MIN_VERSIONS + make tests + working-directory: ${{ inputs.working-directory }} + + publish: + needs: + - build + - test-pypi-publish + - pre-release-checks + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/${{ needs.build.outputs.pkg-name }} + steps: + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: dist + path: ${{ inputs.working-directory }}/dist/ + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Publish distribution 📦 to PyPI env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.GH_LC_OCI_PYPI_TOKEN }} run: | pip install twine - twine upload dist/* -u $TWINE_USERNAME -p $TWINE_PASSWORD \ No newline at end of file + twine upload ${{ inputs.working-directory }}/dist/* -u $TWINE_USERNAME -p $TWINE_PASSWORD