Reputation: 1
** Description:
I'm experiencing an issue with a GitHub Actions workflow that creates Pull Requests while attempting to exclude specific folders.
** Current Behavior:
The workflow creates a PR from main branch to dev branch
Despite using a filtered tree approach, the ".github/workflows" and "tests" folders are still appearing in the PR
These folders have been removed from the target branch (dev)
The filtered tree logs don't show these folders, but they still appear as changes in the PR
The generated PR should not include any changes from ".github/workflows" and "tests" folders
These folders should be completely excluded from the PR, regardless of their presence in the source branch
Using "github-script@v6" action
Implementing folder exclusion through GitHub's Git Data API
The workflow creates a new branch with filtered content before creating the PR
Direct git commands cannot be used in the workflow due to restrictions
// Filtering implementation in the workflow
const excludedPaths = ['.github/workflows', 'tests'];
const filteredTree = baseTree.data.tree.filter(item => {
return !excludedPaths.some(excludedPath =>
item.path === excludedPath ||
item.path.startsWith(excludedPath + '/')
);
});
Here's the complete workflow file:
name: Deploy to Environment
permissions:
contents: write
actions: write
pull-requests: write
on:
workflow_dispatch:
inputs:
base_branch:
description: 'Base Branch to deploy from'
required: true
default: 'Feature/Auto-Unit-Testing-Main'
type: choice
options:
- Feature/Auto-Unit-Testing-Main
- Feature/Auto-Unit-Testing-Dev
- Feature/Auto-Unit-Testing-qa
environment:
description: 'Target Environment'
required: true
default: 'Feature/Auto-Unit-Testing-Dev'
type: choice
options:
- Feature/Auto-Unit-Testing-Dev
- Feature/Auto-Unit-Testing-qa
pr_title:
description: 'Pull Request Title'
required: true
type: string
default: 'Deploy to target environment'
pr_description:
description: 'Initial Pull Request Description'
required: true
type: string
default: 'Deployment changes from source to target environment'
push:
branches:
- Feature/Auto-Unit-Testing-Main
- Feature/Auto-Unit-Testing-Dev
- Feature/Auto-Unit-Testing-qa
jobs:
prepare-deployment:
runs-on: self-hosted
outputs:
target_branch: ${{ steps.set-target.outputs.branch }}
steps:
- name: Set Target Branch
id: set-target
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TARGET_BRANCH="${{ github.event.inputs.environment }}"
else
TARGET_BRANCH="Feature/Auto-Unit-Testing-Dev"
fi
echo "branch=$TARGET_BRANCH" >> $GITHUB_OUTPUT
echo "Deploying to: $TARGET_BRANCH"
create-pr:
needs: prepare-deployment
runs-on: self-hosted
if: github.event_name == 'workflow_dispatch'
outputs:
pr_url: ${{ steps.create-pr.outputs.pr_url }}
pr_number: ${{ steps.create-pr.outputs.pr_number }}
steps:
- name: Create Pull Request with Excluded Paths
id: create-pr
uses: actions/github-script@v6
with:
script: |
try {
// Get the base branch reference
const baseRef = await github.rest.git.getRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `heads/${{ github.event.inputs.base_branch }}`
});
// Get the commit for the base branch
const baseCommit = await github.rest.git.getCommit({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: baseRef.data.object.sha
});
// Get the tree for the base commit
const baseTree = await github.rest.git.getTree({
owner: context.repo.owner,
repo: context.repo.repo,
tree_sha: baseCommit.data.tree.sha,
recursive: true
});
// Define paths to exclude
const excludedPaths = ['.github/workflows', 'tests'];
// Filter the tree to exclude specified paths
const filteredTree = baseTree.data.tree.filter(item => {
return !excludedPaths.some(excludedPath =>
item.path === excludedPath || // Exact match
item.path.startsWith(excludedPath + '/') // Files within folder
);
});
// Create a new tree with the filtered content
const newTree = await github.rest.git.createTree({
owner: context.repo.owner,
repo: context.repo.repo,
base_tree: baseCommit.data.tree.sha,
tree: filteredTree.map(item => ({
path: item.path,
mode: item.mode,
type: item.type,
sha: item.sha
}))
});
// Create a new commit with the new tree
const newCommit = await github.rest.git.createCommit({
owner: context.repo.owner,
repo: context.repo.repo,
message: 'Filtered deployment commit (excluding workflows and tests)',
tree: newTree.data.sha,
parents: [baseRef.data.object.sha]
});
// Create a new branch for the PR
const timestamp = Date.now();
const branchName = `deploy-${timestamp}`;
await github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `refs/heads/${branchName}`,
sha: newCommit.data.sha
});
// Create the PR
const pr = await github.rest.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: '${{ github.event.inputs.pr_title }}',
body: `Deploying changes from ${{ github.event.inputs.base_branch }} to ${{ needs.prepare-deployment.outputs.target_branch }}
${{ github.event.inputs.pr_description }}
Note: The following paths have been excluded from this PR:
- .github/workflows/
- tests/`,
head: branchName,
base: '${{ needs.prepare-deployment.outputs.target_branch }}'
});
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.data.number,
labels: ['deployment', '${{ needs.prepare-deployment.outputs.target_branch }}']
});
console.log('PR created successfully:', pr.data.html_url);
core.setOutput('pr_url', pr.data.html_url);
core.setOutput('pr_number', pr.data.number);
// Log the filtered paths for debugging
console.log('Filtered tree paths:');
filteredTree.forEach(item => console.log(item.path));
} catch (error) {
console.error('Error creating PR:', error);
core.setFailed(error.message);
}
- name: PR Summary
if: success()
run: |
echo "## Pull Request Created" >> $GITHUB_STEP_SUMMARY
echo "- PR URL: ${{ steps.create-pr.outputs.pr_url }}" >> $GITHUB_STEP_SUMMARY
echo "- PR Number: ${{ steps.create-pr.outputs.pr_number }}" >> $GITHUB_STEP_SUMMARY
echo "- Source Branch: ${{ github.event.inputs.base_branch }}" >> $GITHUB_STEP_SUMMARY
echo "- Target Branch: ${{ needs.prepare-deployment.outputs.target_branch }}" >> $GITHUB_STEP_SUMMARY
deploy-after-merge:
needs: [prepare-deployment, create-pr]
runs-on: self-hosted
if: github.event.pull_request.merged == true
steps:
- name: Deploy Merged Changes
run: |
echo "Deployment completed to ${{ needs.prepare-deployment.outputs.target_branch }}"
** Troubleshooting Steps Taken:
Verified that the folders are properly removed from the target branch
Confirmed that the filtered tree logs don't show the excluded paths
Created a new branch from filtered content to avoid history issues
** Additional Context:
The workflow is triggered manually through "workflow_dispatch"
Full workflow file is implemented using GitHub's REST API calls
The console logs under "Filtered tree paths" confirm that these folders are being filtered, but the exclusion isn't reflected in the final PR
** Questions:
How can I achieve my requirement? What am I missing?
Are there alternative approaches to exclude specific folders when creating PRs through the API?
Could this be related to how GitHub handles file history in PRs?
** Repository Information:
Source Branch: Feature/Auto-Unit-Testing-Main
Target Branch: Feature/Auto-Unit-Testing-Dev
Excluded Folders: ".github/workflows", "tests"
Please let me know if you need any additional information or clarification.
Upvotes: -3
Views: 50