diff --git a/.github/workflows/deploy-docs-draft.yml b/.github/workflows/deploy-docs-draft.yml new file mode 100644 index 00000000..84482525 --- /dev/null +++ b/.github/workflows/deploy-docs-draft.yml @@ -0,0 +1,266 @@ +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@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: yarn + cache-dependency-path: ./docs/yarn.lock + + - name: Validate Branch Names + 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 "${{ github.event.pull_request.head.ref }}" + + - name: Extract Branch Names + id: extract_branch + 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 "${{ github.event.pull_request.head.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 && yarn install + + - name: Build website + if: success() + run: | + set -o pipefail + cd docs + yarn 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_SEGMENT_PUBLIC_WRITE_KEY }} + + - name: Check Build Result + id: buildLogFail + if: failure() + run: | + MULTILINE_LOG=$(cat $GITHUB_WORKSPACE/build.log) + echo "BUILD_FAILURE<> $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<> $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@v4 + if: always() + with: + name: deploy.log + path: ${{ github.workspace }}/deploy.log diff --git a/.github/workflows/deploy-gh-pages.yml b/.github/workflows/deploy-gh-pages.yml new file mode 100644 index 00000000..a665710d --- /dev/null +++ b/.github/workflows/deploy-gh-pages.yml @@ -0,0 +1,43 @@ +name: Deploy to GitHub Pages + +on: + push: + branches: + - main + paths: + - 'docs/**' + # Review gh actions docs if you want to further define triggers, paths, etc + # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on + +jobs: + deploy: + name: Deploy to GitHub Pages + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: yarn + cache-dependency-path: ./docs/yarn.lock + + - name: Install dependencies + run: cd docs && yarn install + - name: Build website + run: cd docs && yarn build + # env: + # SEGMENT_PUBLIC_WRITE_KEY: ${{ vars.DOCS_PROD_SEGMENT_PUBLIC_WRITE_KEY }} + + # Popular action to deploy to GitHub Pages: + # Docs: https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + # Build output to publish to the `gh-pages` branch: + publish_dir: ./docs/build + # The following lines assign commit authorship to the official + # GH-Actions bot for deploys to `gh-pages` branch: + # https://github.com/actions/checkout/issues/13#issuecomment-724415212 + # The GH actions bot is used by default if you didn't specify the two fields. + # You can swap them out with your own user credentials. diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 3c99f2a2..5684a13f 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -20,10 +20,10 @@ const config = { }, // Set the production url of your site here - // url: 'https://openrag.io', + url: 'https://langflow-ai.github.io', // Set the // pathname under which your site is served // For GitHub pages deployment, it is often '//' - baseUrl: '/', + baseUrl: '/openrag/', // GitHub pages deployment config. // If you aren't using GitHub pages, you don't need these.