-
Notifications
You must be signed in to change notification settings - Fork 156
ci: add PyPI publish workflow and standardize README #279
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
sudsali
wants to merge
1
commit into
master
Choose a base branch
from
release-1.6.0
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+214
−17
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,202 @@ | ||
| name: Publish to PyPI | ||
|
|
||
| # Manually triggered release, mirroring awslabs/deequ's publish flow: the | ||
| # operator supplies the release version, the workflow sets it, builds, and | ||
| # publishes to PyPI, then tags the release and opens a follow-up PR that bumps | ||
| # the version in the repo. The PyPI token is never stored in GitHub — it is | ||
| # fetched at run time from AWS Secrets Manager via OIDC and passed to Poetry | ||
| # through an env var. | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| release_version: | ||
| description: "Release version, e.g. 1.6.0 (must be a new version, not already on PyPI)" | ||
| required: true | ||
| dry_run: | ||
| description: "Dry run (build only, skip the PyPI upload, tag, and version-bump PR)" | ||
| type: boolean | ||
| default: true | ||
|
|
||
| permissions: | ||
| id-token: write # assume the AWS role via OIDC | ||
| contents: read | ||
|
|
||
| jobs: | ||
| publish: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 15 | ||
| # Real uploads run in the protected environment (required reviewer); a dry | ||
| # run resolves to '' (no environment, no approval, no credentials). | ||
| environment: ${{ !inputs.dry_run && 'pypi-release' || '' }} | ||
| outputs: | ||
| version: ${{ steps.version.outputs.version }} | ||
| sha: ${{ steps.sha.outputs.sha }} | ||
| steps: | ||
| - name: Require dispatch from master for a real release | ||
| # The pypi-release environment only permits deployments from master, and | ||
| # GitHub matches that against the dispatch ref (not the checkout). Fail | ||
| # early with a clear message instead of an opaque environment rejection. | ||
| if: ${{ !inputs.dry_run && github.ref != 'refs/heads/master' }} | ||
| run: | | ||
| echo "::error::Real releases must be dispatched from the master branch (got ${GITHUB_REF})." >&2 | ||
| exit 1 | ||
|
|
||
| - name: Checkout master | ||
| # Always release from master regardless of the branch the dispatch UI | ||
| # selected, so the build/tag/bump reflect the mainline. | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
| with: | ||
| ref: master | ||
| persist-credentials: false | ||
|
|
||
| - name: Record release SHA | ||
| # Pin the exact commit built/published so the tag job tags THIS commit, | ||
| # even if master advances during the approval wait. | ||
| id: sha | ||
| run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 | ||
| with: | ||
| python-version: "3.9" | ||
|
|
||
| - name: Install Poetry | ||
| run: pip install poetry==1.7.1 | ||
|
|
||
| - name: Normalize and validate version | ||
| # Strip an accidental leading 'v', reject anything that isn't a plain | ||
| # release version, and expose the canonical value for every downstream | ||
| # step (build, tag, bump, URL) so tag/__version__/wheel cannot diverge. | ||
| id: version | ||
| run: | | ||
| V="${RAW_VERSION#v}" | ||
| if ! printf '%s' "$V" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+([abc]|rc|\.post|\.dev)?[0-9]*$'; then | ||
| echo "::error::Invalid release version '${RAW_VERSION}' (expected e.g. 1.6.0)" >&2 | ||
| exit 1 | ||
| fi | ||
| echo "version=$V" >> "$GITHUB_OUTPUT" | ||
| echo "Release version: $V" | ||
| env: | ||
| RAW_VERSION: ${{ inputs.release_version }} | ||
|
|
||
| - name: Set release version | ||
| # Set the canonical version in both places pydeequ keeps it, then assert | ||
| # they agree with the wheel version poetry will build. | ||
| run: | | ||
| poetry version "${VERSION}" | ||
| sed -i "s/^__version__ = .*/__version__ = \"${VERSION}\"/" pydeequ/__init__.py | ||
| BUILT=$(poetry version --short) | ||
| if [ "$BUILT" != "${VERSION}" ]; then | ||
| echo "::error::pyproject version $BUILT != requested ${VERSION}" >&2 | ||
| exit 1 | ||
| fi | ||
| grep -q "__version__ = \"${VERSION}\"" pydeequ/__init__.py || { | ||
| echo "::error::__init__ __version__ was not updated to ${VERSION}" >&2; exit 1; } | ||
| env: | ||
| VERSION: ${{ steps.version.outputs.version }} | ||
|
|
||
| - name: Build sdist + wheel | ||
| run: poetry build | ||
|
|
||
| - name: Configure AWS credentials (OIDC) | ||
| if: ${{ !inputs.dry_run }} | ||
| uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2 | ||
| with: | ||
| role-to-assume: arn:aws:iam::059379938386:role/GitHubActionsPyDeequPublish | ||
| aws-region: us-east-1 | ||
|
|
||
| - name: Publish to PyPI | ||
| if: ${{ !inputs.dry_run }} | ||
| # No --skip-existing: a version already on PyPI must fail loudly here so | ||
| # the tag/bump never run for an artifact this release did not upload. | ||
| # Token → POETRY_PYPI_TOKEN_PYPI env (read natively by poetry), never on | ||
| # argv, never in $GITHUB_OUTPUT, never written to a file. Masked first. | ||
| run: | | ||
| PYPI_TOKEN=$(aws secretsmanager get-secret-value --secret-id pypi_token --query SecretString --output text) | ||
| echo "::add-mask::$PYPI_TOKEN" | ||
| export POETRY_PYPI_TOKEN_PYPI="$PYPI_TOKEN" | ||
| poetry publish --no-interaction | ||
|
|
||
| - name: Summary | ||
| run: | | ||
| if [ "${DRY_RUN}" = "true" ]; then | ||
| echo "### Dry run completed for ${VERSION} (not published)" >> "$GITHUB_STEP_SUMMARY" | ||
| else | ||
| echo "### Published ${VERSION} to PyPI" >> "$GITHUB_STEP_SUMMARY" | ||
| echo "View at: https://pypi.org/project/pydeequ/${VERSION}/" >> "$GITHUB_STEP_SUMMARY" | ||
| fi | ||
| env: | ||
| DRY_RUN: ${{ inputs.dry_run }} | ||
| VERSION: ${{ steps.version.outputs.version }} | ||
|
|
||
| tag-and-bump: | ||
| # Runs ONLY after a successful real publish (needs: publish gates on | ||
| # success). Creates the release tag as a record of what is now on PyPI (tag | ||
| # after publish, never before), then opens a PR recording the version. | ||
| needs: publish | ||
| if: ${{ !inputs.dry_run }} | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 5 | ||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
| env: | ||
| VERSION: ${{ needs.publish.outputs.version }} | ||
| RELEASE_SHA: ${{ needs.publish.outputs.sha }} | ||
| steps: | ||
| - name: Checkout the released commit | ||
| # The exact SHA the publish job built/uploaded — not a fresh master tip, | ||
| # so a merge during the approval wait can't move the tag off the release. | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
| with: | ||
| ref: ${{ needs.publish.outputs.sha }} | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 | ||
| with: | ||
| python-version: "3.9" | ||
|
|
||
| - name: Install Poetry | ||
| run: pip install poetry==1.7.1 | ||
|
|
||
| - name: Tag the published release | ||
| # Tag the exact published commit, now that PyPI has the artifact. | ||
| # Idempotent: skip if the tag already exists remotely. | ||
| run: | | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "github-actions[bot]@users.noreply.github.com" | ||
| if git ls-remote --exit-code --tags origin "v${VERSION}" >/dev/null 2>&1; then | ||
| echo "Tag v${VERSION} already exists — skipping." | ||
| else | ||
| git tag -a "v${VERSION}" "${RELEASE_SHA}" -m "Release ${VERSION}" | ||
| git push origin "v${VERSION}" | ||
| fi | ||
|
|
||
| - name: Bump version and open PR | ||
| # Idempotent: skip if the bump branch/PR already exist or master is | ||
| # already at this version (e.g. a re-run after a partial failure). | ||
| run: | | ||
| BRANCH_NAME="bump-version-${VERSION}" | ||
| if git ls-remote --exit-code --heads origin "${BRANCH_NAME}" >/dev/null 2>&1; then | ||
| echo "Branch ${BRANCH_NAME} already exists — skipping bump PR." | ||
| exit 0 | ||
| fi | ||
| git fetch origin master | ||
| git checkout -b "${BRANCH_NAME}" origin/master | ||
| poetry version "${VERSION}" | ||
| sed -i "s/^__version__ = .*/__version__ = \"${VERSION}\"/" pydeequ/__init__.py | ||
| if git diff --quiet -- pyproject.toml pydeequ/__init__.py; then | ||
| echo "master already at ${VERSION} — nothing to bump." | ||
| exit 0 | ||
| fi | ||
| git add pyproject.toml pydeequ/__init__.py | ||
| git commit -m "Update version to ${VERSION}" | ||
| git push origin "${BRANCH_NAME}" | ||
| gh pr create \ | ||
| --base master \ | ||
| --head "${BRANCH_NAME}" \ | ||
| --title "Update version to ${VERSION}" \ | ||
| --body "Automated version bump after publishing ${VERSION} to PyPI." | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.