diff --git a/.github/pr-welcome-community.md b/.github/pr-welcome-community.md index c5004aad9..d4bc37935 100644 --- a/.github/pr-welcome-community.md +++ b/.github/pr-welcome-community.md @@ -27,6 +27,7 @@ As needed or by request, Airbyte Maintainers can execute the following slash com - `/fix-pr` - Fixes most formatting and linting issues - `/poetry-lock` - Updates poetry.lock file - `/test-pr` - Runs tests with the updated PyAirbyte +- `/prerelease` - Builds and publishes a prerelease version to PyPI ### Community Support diff --git a/.github/pr-welcome-internal.md b/.github/pr-welcome-internal.md index 7d17cfdb9..b7784e866 100644 --- a/.github/pr-welcome-internal.md +++ b/.github/pr-welcome-internal.md @@ -26,6 +26,7 @@ Airbyte Maintainers can execute the following slash commands on your PR: - `/fix-pr` - Fixes most formatting and linting issues - `/poetry-lock` - Updates poetry.lock file - `/test-pr` - Runs tests with the updated PyAirbyte +- `/prerelease` - Builds and publishes a prerelease version to PyPI ### Community Support diff --git a/.github/workflows/prerelease-command.yml b/.github/workflows/prerelease-command.yml new file mode 100644 index 000000000..789d2c2d9 --- /dev/null +++ b/.github/workflows/prerelease-command.yml @@ -0,0 +1,117 @@ +name: On-Demand Prerelease + +on: + workflow_dispatch: + inputs: + pr: + description: 'PR Number' + type: string + required: true + comment-id: + description: 'Comment ID (Optional)' + type: string + required: false + +env: + AIRBYTE_ANALYTICS_ID: ${{ vars.AIRBYTE_ANALYTICS_ID }} + +permissions: + contents: read + pull-requests: write + issues: write + +jobs: + resolve-pr: + name: Set up Workflow + runs-on: ubuntu-latest + steps: + - name: Resolve workflow variables + id: vars + uses: aaronsteers/resolve-ci-vars-action@2e56afab0344bbe03c047dfa39bae559d0291472 # v0.1.6 + + - name: Append comment with job run link + id: first-comment-action + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 + with: + comment-id: ${{ github.event.inputs.comment-id }} + issue-number: ${{ github.event.inputs.pr }} + body: | + > **Prerelease Build Started** + > + > Building and publishing prerelease package from this PR... + > [Check job output.](${{ steps.vars.outputs.run-url }}) + + - name: Checkout to get latest tag + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 0 + + - name: Compute prerelease version + id: version + run: | + # Get the latest tag version (strip 'v' prefix if present) + LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") + BASE_VERSION=${LATEST_TAG#v} + # Create a unique prerelease version using PR number and run ID + # Format: {base}.dev{pr_number}{run_id} (e.g., 0.34.0.dev825123456789) + PRERELEASE_VERSION="${BASE_VERSION}.dev${{ github.event.inputs.pr }}${{ github.run_id }}" + echo "version=$PRERELEASE_VERSION" >> $GITHUB_OUTPUT + echo "Computed prerelease version: $PRERELEASE_VERSION" + outputs: + source-repo: ${{ steps.vars.outputs.pr-source-repo-name-full }} + source-branch: ${{ steps.vars.outputs.pr-source-git-branch }} + commit-sha: ${{ steps.vars.outputs.pr-source-git-sha }} + pr-number: ${{ steps.vars.outputs.pr-number }} + job-run-url: ${{ steps.vars.outputs.run-url }} + first-comment-id: ${{ steps.first-comment-action.outputs.comment-id }} + prerelease-version: ${{ steps.version.outputs.version }} + + build-and-publish: + name: Call Publish Workflow + needs: [resolve-pr] + uses: ./.github/workflows/pypi_publish.yml + with: + # Use refs/pull//head instead of raw SHA for fork compatibility + git_ref: refs/pull/${{ github.event.inputs.pr }}/head + version_override: ${{ needs.resolve-pr.outputs.prerelease-version }} + publish: true + + post-result-comment: + name: Write Status to PR + needs: [resolve-pr, build-and-publish] + if: always() + runs-on: ubuntu-latest + steps: + - name: Post success comment + if: needs.build-and-publish.result == 'success' + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 + with: + comment-id: ${{ needs.resolve-pr.outputs.first-comment-id }} + issue-number: ${{ github.event.inputs.pr }} + reactions: rocket + body: | + > **Prerelease Published to PyPI** + > + > Version: `${{ needs.resolve-pr.outputs.prerelease-version }}` + > + > Install with: + > ```bash + > pip install airbyte==${{ needs.resolve-pr.outputs.prerelease-version }} + > ``` + - name: Post failure comment + if: needs.build-and-publish.result == 'failure' + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 + with: + comment-id: ${{ needs.resolve-pr.outputs.first-comment-id }} + issue-number: ${{ github.event.inputs.pr }} + reactions: confused + body: | + > **Prerelease Build/Publish Failed** + > + > The prerelease encountered an error. + > [Check job output](${{ needs.resolve-pr.outputs.job-run-url }}) for details. + > + > You can still install directly from this PR branch: + > ```bash + > pip install 'git+https://github.com/${{ needs.resolve-pr.outputs.source-repo }}.git@${{ needs.resolve-pr.outputs.source-branch }}' + > ``` diff --git a/.github/workflows/pypi_publish.yml b/.github/workflows/pypi_publish.yml index 5baaa59e5..c4d04c647 100644 --- a/.github/workflows/pypi_publish.yml +++ b/.github/workflows/pypi_publish.yml @@ -5,6 +5,22 @@ on: workflow_dispatch: + workflow_call: + inputs: + git_ref: + description: 'Git ref (SHA or branch) to checkout and build' + required: true + type: string + version_override: + description: 'Version to use (overrides dynamic versioning)' + required: false + type: string + publish: + description: 'Whether to publish to PyPI' + required: false + type: boolean + default: false + env: AIRBYTE_ANALYTICS_ID: ${{ vars.AIRBYTE_ANALYTICS_ID }} @@ -14,8 +30,21 @@ jobs: steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: + ref: ${{ inputs.git_ref || github.ref }} fetch-depth: 0 - - uses: hynek/build-and-inspect-python-package@efb823f52190ad02594531168b7a2d5790e66516 # v2.14.0 + - name: Prepare version override + id: version + run: | + echo "override=${{ inputs.version_override }}" >> $GITHUB_OUTPUT + echo "has_override=${{ inputs.version_override != '' }}" >> $GITHUB_OUTPUT + - name: Build package (with version override) + if: steps.version.outputs.has_override == 'true' + uses: hynek/build-and-inspect-python-package@efb823f52190ad02594531168b7a2d5790e66516 # v2.14.0 + env: + POETRY_DYNAMIC_VERSIONING_BYPASS: ${{ steps.version.outputs.override }} + - name: Build package (dynamic version) + if: steps.version.outputs.has_override != 'true' + uses: hynek/build-and-inspect-python-package@efb823f52190ad02594531168b7a2d5790e66516 # v2.14.0 publish: name: Publish to PyPI @@ -27,13 +56,16 @@ jobs: environment: name: PyPi url: https://pypi.org/p/airbyte - if: startsWith(github.ref, 'refs/tags/') + # Publish when: (1) triggered by a tag push, OR (2) called with publish=true + if: startsWith(github.ref, 'refs/tags/') || inputs.publish == true steps: - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: name: Packages path: dist - name: Upload wheel to release + # Only upload to GitHub release when triggered by a tag + if: startsWith(github.ref, 'refs/tags/') uses: svenstaro/upload-release-action@81c65b7cd4de9b2570615ce3aad67a41de5b1a13 # latest with: repo_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/slash_command_dispatch.yml b/.github/workflows/slash_command_dispatch.yml index c45a34ebc..402cbff57 100644 --- a/.github/workflows/slash_command_dispatch.yml +++ b/.github/workflows/slash_command_dispatch.yml @@ -34,6 +34,7 @@ jobs: fix-pr test-pr poetry-lock + prerelease static-args: | pr=${{ github.event.issue.number }} comment-id=${{ github.event.comment.id }}