feat(lfx): Bundle Separation and LFX Extension Framework #4597
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: Pull Request Docs Draft | |
| on: | |
| pull_request: | |
| branches: | |
| - '**' | |
| paths: | |
| - 'docs/**' | |
| - '.github/workflows/deploy-docs-draft.yml' | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| jobs: | |
| build-and-deploy: | |
| runs-on: ubuntu-latest | |
| if: "! github.event.pull_request.head.repo.fork" | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| cache-dependency-path: ./docs/package-lock.json | |
| - name: Validate Branch Names | |
| env: | |
| BRANCH_REF: ${{ github.event.pull_request.head.ref }} | |
| run: | | |
| # Check if branch names contain invalid characters. Only alphanumeric, _, -, ., and / are allowed. | |
| validate_branch_name() { | |
| local branch_name="$1" | |
| if [[ ! "$branch_name" =~ ^[a-zA-Z0-9/_\.-]+$ ]]; then | |
| echo "Error: Branch name contains invalid characters. Only alphanumeric, _, -, ., and / are allowed." | |
| exit 1 | |
| fi | |
| } | |
| validate_branch_name "$BRANCH_REF" | |
| - name: Extract Branch Names | |
| id: extract_branch | |
| env: | |
| BRANCH_REF: ${{ github.event.pull_request.head.ref }} | |
| run: | | |
| # Extract and transform branch names | |
| extract_branch() { | |
| local input_branch="$1" | |
| # Check if input_branch starts with "refs/heads/" | |
| if [[ "$input_branch" == refs/heads/* ]]; then | |
| # Remove "refs/heads/" prefix safely using parameter expansion | |
| branch_name="${input_branch#refs/heads/}" | |
| echo "$branch_name" | |
| else | |
| echo "$input_branch" | |
| fi | |
| } | |
| # Transform branch names in form of `refs/heads/main` to `main` | |
| draft_branch=$(extract_branch "$BRANCH_REF") | |
| # Replace / with - in the draft branch name to use as a directory name | |
| draft_directory=$(echo "$draft_branch" | tr / -) | |
| # Safe echo to $GITHUB_OUTPUT | |
| { | |
| echo "draft_branch=$draft_branch" | |
| echo "draft_directory=$draft_directory" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Set Draft URL | |
| id: draft_url | |
| if: success() | |
| run: | | |
| echo "url=${{ vars.DOCS_DRAFT_BASE_URL }}/langflow-drafts/${{ steps.extract_branch.outputs.draft_directory }}/index.html" >> $GITHUB_OUTPUT | |
| - name: Install dependencies | |
| run: cd docs && npm install | |
| - name: Build website | |
| if: success() | |
| run: | | |
| set -o pipefail | |
| cd docs | |
| npm run build |& tee $GITHUB_WORKSPACE/build.log | |
| env: | |
| BASE_URL: /langflow-drafts/${{ steps.extract_branch.outputs.draft_directory }} | |
| FORCE_COLOR: 0 # Disable color output | |
| SEGMENT_PUBLIC_WRITE_KEY: ${{ vars.DOCS_DRAFT_IBM_SEGMENT_PUBLIC_WRITE_KEY }} | |
| - name: Check Build Result | |
| id: buildLogFail | |
| if: failure() | |
| run: | | |
| MULTILINE_LOG=$(cat $GITHUB_WORKSPACE/build.log) | |
| echo "BUILD_FAILURE<<EOF" >> $GITHUB_ENV | |
| echo $MULTILINE_LOG >> $GITHUB_ENV | |
| echo "EOF" >> $GITHUB_ENV | |
| - name: Hide Previous Build Comments | |
| if: ${{ github.event.pull_request.number && (success() || failure()) }} | |
| run: | | |
| set -e | |
| # Get all comments on the PR that match our build comments | |
| comments=$(gh api repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments \ | |
| --jq '.[] | select(.body | test("Build failure! :x:|Build successful! :white_check_mark:")) | .node_id') | |
| # Minimize each matching comment using GraphQL API | |
| if [[ -n "$comments" ]]; then | |
| echo "Found previous build comments to hide" | |
| while IFS= read -r comment_id; do | |
| if [[ -n "$comment_id" ]]; then | |
| echo "Minimizing comment: $comment_id" | |
| gh api graphql \ | |
| --field id="$comment_id" \ | |
| --field classifier="OUTDATED" \ | |
| --raw-field query=' | |
| mutation($id: ID!, $classifier: ReportedContentClassifiers!) { | |
| minimizeComment(input: { subjectId: $id, classifier: $classifier }) { | |
| minimizedComment { | |
| isMinimized | |
| } | |
| } | |
| }' || echo "Failed to minimize comment $comment_id, continuing..." | |
| echo | |
| fi | |
| done <<< "$comments" | |
| else | |
| echo "No previous build comments found to hide" | |
| fi | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Assemble Build Success Comment | |
| if: success() | |
| run: | | |
| build_success_comment="Build successful! :white_check_mark:" | |
| build_success_comment+="\nDeploying docs draft." | |
| echo "BUILD_SUCCESS_COMMENT<<EOF" >> $GITHUB_ENV | |
| echo -e "$build_success_comment" >> $GITHUB_ENV | |
| echo "EOF" >> $GITHUB_ENV | |
| - name: Create Build Success Comment | |
| if: success() | |
| uses: peter-evans/create-or-update-comment@v4 | |
| with: | |
| issue-number: ${{ github.event.pull_request.number }} | |
| body: "${{ env.BUILD_SUCCESS_COMMENT }}" | |
| reactions: rocket | |
| - name: Create Build Failure Comment | |
| if: failure() | |
| uses: peter-evans/create-or-update-comment@v4 | |
| with: | |
| issue-number: ${{ github.event.pull_request.number }} | |
| body: | | |
| Build failure! :x: | |
| > ${{ env.BUILD_FAILURE }} | |
| reactions: confused | |
| - name: Find Comment | |
| id: fc | |
| if: success() | |
| uses: peter-evans/find-comment@v3 | |
| with: | |
| issue-number: ${{ github.event.pull_request.number }} | |
| body-includes: Build successful! | |
| direction: last | |
| - name: Configure AWS CLI | |
| if: success() | |
| run: | | |
| aws configure set aws_access_key_id ${{ secrets.DOCS_AWS_ACCESS_KEY_ID }} | |
| aws configure set aws_secret_access_key ${{ secrets.DOCS_AWS_SECRET_ACCESS_KEY }} | |
| aws configure set region us-west-2 | |
| - name: Check for New Assets | |
| run: | | |
| set -o pipefail | |
| echo "Checking for new assets." |& tee -a $GITHUB_WORKSPACE/deploy.log | |
| echo "aws s3 sync docs/build/assets/ s3://${{ vars.DOCS_DRAFT_S3_BUCKET_NAME }}/langflow-drafts/${{ steps.extract_branch.outputs.draft_directory }}/assets/ --size-only --dryrun --no-progress" | tee -a $GITHUB_WORKSPACE/deploy.log | |
| aws s3 sync docs/build/assets/ "s3://${{ vars.DOCS_DRAFT_S3_BUCKET_NAME }}/langflow-drafts/${{ steps.extract_branch.outputs.draft_directory }}/assets/" --size-only --dryrun --no-progress | tee $GITHUB_WORKSPACE/assets.log | |
| - name: Determine Standard or Full Publish | |
| id: check_full_publish | |
| run: | | |
| # Determine if a full publish is required because of new assets. | |
| if grep -qE '(upload:|delete:)' "$GITHUB_WORKSPACE/assets.log"; then | |
| echo "New assets. Perform full publish: true" | tee -a "$GITHUB_WORKSPACE/deploy.log" | |
| echo "perform_full_publish=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "No new assets. Perform full publish: false" | tee -a "$GITHUB_WORKSPACE/deploy.log" | |
| echo "perform_full_publish=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Deploy to S3 | |
| if: success() | |
| run: | | |
| set -o pipefail | |
| cd docs | |
| mkdir langflow-drafts | |
| mv build langflow-drafts/${{ steps.extract_branch.outputs.draft_directory }} | |
| cd langflow-drafts | |
| # Records the repository that originally triggered the build so we can post back | |
| # comments upon clean up of a stale draft if it still has an open pull request. | |
| echo "${{ github.event.repository.full_name }}" > ${{ steps.extract_branch.outputs.draft_directory }}/.github_source_repository | |
| s3_params=( | |
| # Hide upload progress for a cleaner sync log | |
| --no-progress | |
| --delete | |
| --exclude "*" | |
| --include "${{ steps.extract_branch.outputs.draft_directory }}/*" | |
| ) | |
| if [[ "${{ steps.check_full_publish.outputs.perform_full_publish }}" == "false" ]]; then | |
| s3_params+=(--size-only) | |
| fi | |
| echo "Deploying draft to S3." |& tee -a $GITHUB_WORKSPACE/deploy.log | |
| echo "aws s3 sync . s3://${{ vars.DOCS_DRAFT_S3_BUCKET_NAME }}/langflow-drafts ${s3_params[@]}" |& tee -a $GITHUB_WORKSPACE/deploy.log | |
| aws s3 sync . "s3://${{ vars.DOCS_DRAFT_S3_BUCKET_NAME }}/langflow-drafts" "${s3_params[@]}" |& tee -a $GITHUB_WORKSPACE/deploy.log | |
| # Update .github_source_repository file metadata to mark last modified time of the draft. | |
| # This will allow us to later determine if a draft is stale and needs to be cleaned up. | |
| echo "Marking last modified time of the draft." |& tee -a $GITHUB_WORKSPACE/deploy.log | |
| echo "aws s3 cp --metadata '{\"touched\": \"now\"}' \ | |
| s3://${{ vars.DOCS_DRAFT_S3_BUCKET_NAME }}/langflow-drafts/${{ steps.extract_branch.outputs.draft_directory }}/.github_source_repository \ | |
| s3://${{ vars.DOCS_DRAFT_S3_BUCKET_NAME }}/langflow-drafts/${{ steps.extract_branch.outputs.draft_directory }}/.github_source_repository" \ | |
| |& tee -a $GITHUB_WORKSPACE/deploy.log | |
| aws s3 cp --metadata '{ "touched": "now" }' \ | |
| s3://${{ vars.DOCS_DRAFT_S3_BUCKET_NAME }}/langflow-drafts/${{ steps.extract_branch.outputs.draft_directory }}/.github_source_repository \ | |
| s3://${{ vars.DOCS_DRAFT_S3_BUCKET_NAME }}/langflow-drafts/${{ steps.extract_branch.outputs.draft_directory }}/.github_source_repository \ | |
| |& tee -a $GITHUB_WORKSPACE/deploy.log | |
| - name: Invalidate CloudFront Cache | |
| if: success() | |
| run: | | |
| invalidation_batch="{ \"Paths\": { \"Quantity\": 1, \"Items\": [\"/langflow-drafts/${{ steps.extract_branch.outputs.draft_directory }}/*\"] }, \"CallerReference\": \"langflow-docs-draft-files-$(date +%s)\" }" | |
| echo $invalidation_batch | jq . |& tee -a "$GITHUB_WORKSPACE/deploy.log" | |
| echo "Creating invalidation." |& tee -a "$GITHUB_WORKSPACE/deploy.log" | |
| invalidation_id=$(aws cloudfront create-invalidation --distribution-id "${{ vars.DOCS_DRAFT_CLOUD_FRONT_DISTRIBUTION_ID }}" --invalidation-batch "$invalidation_batch" --query 'Invalidation.Id' --output text |& tee -a "$GITHUB_WORKSPACE/deploy.log") | |
| echo "Awaiting invalidation." |& tee -a "$GITHUB_WORKSPACE/deploy.log" | |
| aws cloudfront wait invalidation-completed --distribution-id "${{ vars.DOCS_DRAFT_CLOUD_FRONT_DISTRIBUTION_ID }}" --id "$invalidation_id" |& tee -a "$GITHUB_WORKSPACE/deploy.log" | |
| echo "Invalidation complete." |& tee -a "$GITHUB_WORKSPACE/deploy.log" | |
| - name: Update Comment | |
| if: ${{ steps.fc.outputs.comment-id != '' }} | |
| uses: peter-evans/create-or-update-comment@v4 | |
| with: | |
| comment-id: ${{ steps.fc.outputs.comment-id }} | |
| body: | | |
| Deploy successful! [View draft](${{ steps.draft_url.outputs.url }}) | |
| reactions: hooray | |
| - name: Upload Deploy Log | |
| uses: actions/upload-artifact@v6 | |
| if: always() | |
| with: | |
| name: deploy.log | |
| path: ${{ github.workspace }}/deploy.log |