chore: move update component index job to autofix workflow (#10342)

* chore: Update changes-filter.yaml to include component_index.json in various sections

* chore: remove update-component-index workflow file

* chore: add update component index job to autofix workflow
This commit is contained in:
Gabriel Luiz Freitas Almeida
2025-10-20 18:14:08 -03:00
committed by GitHub
parent aa3d8e621e
commit ac6e2d2eab
3 changed files with 30 additions and 314 deletions

View File

@ -8,8 +8,10 @@ python:
- "src/backend/base/uv.lock"
- "**/python_test.yml"
- ".github/workflows/ci.yml"
- "src/lfx/src/lfx/_assets/component_index.json"
components-changes:
- "src/backend/base/langflow/components/**"
- "src/lfx/src/lfx/_assets/component_index.json"
starter-projects-changes:
- "src/backend/base/langflow/initial_setup/**"
frontend-tests:
@ -19,6 +21,7 @@ frontend:
- "**/typescript_test.yml"
- "**/jest_test.yml"
- ".github/workflows/ci.yml"
- "src/lfx/src/lfx/_assets/component_index.json"
docs:
- "docs/**"
docker:
@ -66,6 +69,7 @@ components:
- "src/backend/base/langflow/components/**"
- "src/backend/base/langflow/initial_setup/**"
- "src/backend/base/langflow/serialization/**"
- "src/lfx/src/lfx/_assets/component_index.json"
workspace:
- "src/backend/base/langflow/inputs/**"

View File

@ -3,6 +3,8 @@ on:
pull_request:
paths:
- "**/*.py"
- "src/lfx/src/lfx/components/**"
- "scripts/build_component_index.py"
env:
PYTHON_VERSION: "3.13"
@ -49,4 +51,28 @@ jobs:
- uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27
update-component-index:
name: Update Component Index
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: "Setup Environment"
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
python-version: ${{ env.PYTHON_VERSION }}
prune-cache: false
- name: "Install dependencies"
run: uv sync --dev --project .
- name: Build component index
env:
LFX_DEV: "1"
run: make build_component_index
- uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27

View File

@ -1,314 +0,0 @@
# SECURITY WARNING: This workflow uses pull_request_target to enable commenting on fork PRs.
# DO NOT checkout or execute code from fork PRs - this could allow malicious code execution with write permissions.
# Fork PRs are detected and handled safely by skipping checkout/build steps.
name: Update Component Index
on:
pull_request_target:
paths:
- "src/lfx/src/lfx/components/**"
- "scripts/build_component_index.py"
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
update-index:
runs-on: ubuntu-latest
steps:
- name: Checkout repository (PR from same repo only)
if: github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name == github.repository
uses: actions/checkout@v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout repository (workflow_dispatch)
if: github.event_name == 'workflow_dispatch'
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install uv
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name == github.repository)
uses: astral-sh/setup-uv@v6
with:
version: "latest"
- name: Install dependencies
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name == github.repository)
run: uv sync --dev --project .
- name: Check if script exists
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name == github.repository)
id: check_script
run: |
echo "Current directory: $(pwd)"
echo "Checking for script at: scripts/build_component_index.py"
ls -la scripts/ || echo "scripts/ directory not found"
if [ ! -f scripts/build_component_index.py ]; then
echo "exists=false" >> $GITHUB_OUTPUT
echo "⚠️ Script not found - PR needs to be rebased with main"
echo "Repository contents:"
ls -la
echo "Checking git history for the script:"
git log --oneline --all -- scripts/build_component_index.py | head -5 || echo "No history found"
else
echo "exists=true" >> $GITHUB_OUTPUT
echo "✅ Script found"
fi
- name: Build component index
if: steps.check_script.outputs.exists == 'true'
env:
LFX_DEV: "1"
run: make build_component_index
- name: Check if index changed
if: steps.check_script.outputs.exists == 'true'
id: check_changes
run: |
INDEX_FILE="src/lfx/src/lfx/_assets/component_index.json"
git add -N "$INDEX_FILE"
if git diff --quiet --exit-code -- "$INDEX_FILE"; then
echo "changed=false" >> $GITHUB_OUTPUT
echo "✅ No changes to component index"
else
echo "changed=true" >> $GITHUB_OUTPUT
echo "📝 Component index was updated"
echo ""
# Show diff statistics
echo "Diff statistics:"
git diff --stat -- "$INDEX_FILE"
echo ""
# Compare SHA256 hashes
if command -v jq >/dev/null 2>&1; then
OLD_SHA=$(git show HEAD:"$INDEX_FILE" 2>/dev/null | jq -r '.sha256 // "N/A"')
NEW_SHA=$(jq -r '.sha256 // "N/A"' "$INDEX_FILE")
echo "SHA256 hash:"
echo " Old: $OLD_SHA"
echo " New: $NEW_SHA"
echo ""
# Count components (not just categories)
OLD_COMPONENTS=$(git show HEAD:"$INDEX_FILE" 2>/dev/null | jq '[.entries[] | .[1] | keys | length] | add' 2>/dev/null || echo "N/A")
NEW_COMPONENTS=$(jq '[.entries[] | .[1] | keys | length] | add' "$INDEX_FILE" 2>/dev/null || echo "N/A")
OLD_CATEGORIES=$(git show HEAD:"$INDEX_FILE" 2>/dev/null | jq '.entries | length' 2>/dev/null || echo "N/A")
NEW_CATEGORIES=$(jq '.entries | length' "$INDEX_FILE" 2>/dev/null || echo "N/A")
echo "Components: $OLD_COMPONENTS → $NEW_COMPONENTS"
echo "Categories: $OLD_CATEGORIES → $NEW_CATEGORIES"
fi
fi
- name: Commit and push if changed
if: steps.check_changes.outputs.changed == 'true' && github.event.pull_request.head.repo.full_name == github.repository
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add src/lfx/src/lfx/_assets/component_index.json
git commit -m "chore: update component index"
# Retry push with rebase if needed (handles concurrent PR updates)
for i in {1..3}; do
if git push; then
echo "Successfully pushed changes"
exit 0
fi
echo "Push failed, attempt $i/3. Rebasing and retrying..."
git pull --rebase
sleep 2
done
echo "Failed to push after 3 attempts"
exit 1
- name: Comment on PR (community fork - cannot auto-update)
if: github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository
uses: actions/github-script@v7
with:
script: |
const marker = '<!-- component-index-update-needed -->';
const commentBody = `${marker}\n⚠ **Component index needs to be updated**\n\nPlease run the following command locally and commit the changes:\n\n\`\`\`bash\nmake build_component_index\n\`\`\`\n\nOr alternatively:\n\n\`\`\`bash\nLFX_DEV=1 uv run python scripts/build_component_index.py\n\`\`\`\n\nThen commit and push the updated \`src/lfx/src/lfx/_assets/component_index.json\` file.`;
// Find existing comments from github-actions bot with the marker
const { data: comments } = await github.rest.issues.listComments({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
});
const botComments = comments.filter(
comment => comment.user.login === 'github-actions[bot]' && comment.body.includes(marker)
);
// Delete old comments to avoid duplicates
for (const comment of botComments) {
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: comment.id,
});
}
// Post new comment
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: commentBody
})
- name: Comment on PR (same repo - auto-updated)
if: steps.check_changes.outputs.changed == 'true' && github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name == github.repository
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const { execSync } = require('child_process');
const indexPath = 'src/lfx/src/lfx/_assets/component_index.json';
let changeDetails = '';
try {
// Get the old and new JSON data
const newData = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
let oldData = null;
try {
const oldContent = execSync(`git show HEAD:${indexPath}`, { encoding: 'utf8' });
oldData = JSON.parse(oldContent);
} catch (e) {
console.log('Could not get old version:', e.message);
}
if (oldData) {
// Compare versions
const oldVersion = oldData.version || 'unknown';
const newVersion = newData.version || 'unknown';
// Compare component counts
const oldComponents = oldData.metadata?.num_components || 0;
const newComponents = newData.metadata?.num_components || 0;
const componentDiff = newComponents - oldComponents;
// Compare category counts
const oldCategories = oldData.metadata?.num_modules || 0;
const newCategories = newData.metadata?.num_modules || 0;
const categoryDiff = newCategories - oldCategories;
// Find added/removed categories
const oldCategoryNames = new Set(oldData.entries?.map(entry => entry[0]) || []);
const newCategoryNames = new Set(newData.entries?.map(entry => entry[0]) || []);
const addedCategories = [...newCategoryNames].filter(name => !oldCategoryNames.has(name));
const removedCategories = [...oldCategoryNames].filter(name => !newCategoryNames.has(name));
// Find added/removed components
const oldComponentsFlat = new Set();
const newComponentsFlat = new Set();
oldData.entries?.forEach(([category, components]) => {
Object.keys(components || {}).forEach(comp => oldComponentsFlat.add(`${category}.${comp}`));
});
newData.entries?.forEach(([category, components]) => {
Object.keys(components || {}).forEach(comp => newComponentsFlat.add(`${category}.${comp}`));
});
const addedComponents = [...newComponentsFlat].filter(comp => !oldComponentsFlat.has(comp));
const removedComponents = [...oldComponentsFlat].filter(comp => !newComponentsFlat.has(comp));
// Build change summary
let changes = [];
if (oldVersion !== newVersion) {
changes.push(`📦 **Version:** ${oldVersion} → ${newVersion}`);
}
if (componentDiff !== 0) {
const sign = componentDiff > 0 ? '+' : '';
changes.push(`🧩 **Components:** ${oldComponents} → ${newComponents} (${sign}${componentDiff})`);
}
if (categoryDiff !== 0) {
const sign = categoryDiff > 0 ? '+' : '';
changes.push(`📁 **Categories:** ${oldCategories} → ${newCategories} (${sign}${categoryDiff})`);
}
if (addedCategories.length > 0) {
changes.push(` **Added Categories:** ${addedCategories.join(', ')}`);
}
if (removedCategories.length > 0) {
changes.push(` **Removed Categories:** ${removedCategories.join(', ')}`);
}
if (addedComponents.length > 0) {
const displayComponents = addedComponents.slice(0, 10);
const moreCount = addedComponents.length - displayComponents.length;
changes.push(` **Added Components:** ${displayComponents.join(', ')}${moreCount > 0 ? ` (+${moreCount} more)` : ''}`);
}
if (removedComponents.length > 0) {
const displayComponents = removedComponents.slice(0, 10);
const moreCount = removedComponents.length - displayComponents.length;
changes.push(` **Removed Components:** ${displayComponents.join(', ')}${moreCount > 0 ? ` (+${moreCount} more)` : ''}`);
}
if (changes.length > 0) {
changeDetails = '\n\n**Changes:**\n' + changes.join('\n');
} else {
changeDetails = '\n\n**Changes:** Internal structure or metadata updates';
}
// Add SHA256 info
const oldSha = oldData.sha256 || 'unknown';
const newSha = newData.sha256 || 'unknown';
changeDetails += `\n\n🔐 **SHA256:** \`${oldSha.substring(0, 12)}...\` → \`${newSha.substring(0, 12)}...\``;
} else {
changeDetails = `\n\n**New Index:**\n🧩 **Components:** ${newData.metadata?.num_components || 0}\n📁 **Categories:** ${newData.metadata?.num_modules || 0}`;
}
} catch (error) {
console.log('Error analyzing changes:', error.message);
changeDetails = '\n\n*Could not analyze specific changes.*';
}
const marker = '<!-- component-index-auto-updated -->';
const commentBody = `${marker}\n✅ **Component index has been automatically updated** due to changes in \`src/lfx/src/lfx/components/\`${changeDetails}`;
// Find existing comments from github-actions bot with the marker
const { data: comments } = await github.rest.issues.listComments({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
});
const botComments = comments.filter(
comment => comment.user.login === 'github-actions[bot]' && comment.body.includes(marker)
);
// Delete old comments to avoid duplicates
for (const comment of botComments) {
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: comment.id,
});
}
// Post new comment
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: commentBody
})