From b392e77410d56018da203aa685d69fa7c94ccbe0 Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 08:15:14 +0200 Subject: [PATCH 01/29] ci(workflow): add filter input to user-update-branch.yml for partial folder updates --- .github/workflows/user-update-branch.yml | 180 ++++++++++------------- 1 file changed, 79 insertions(+), 101 deletions(-) diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index 62e19c37..415e508d 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -24,6 +24,10 @@ on: description: 'Target branch (to be updated)' required: true type: string + filter: + description: 'Specific folder to update (e.g., .github or src/SmartHopper.Components). Leave blank to update all code.' + required: false + type: string permissions: contents: write @@ -116,130 +120,104 @@ jobs: # Create a unique branch name TEMP_BRANCH="update-branch/${{ github.event.inputs.target_branch }}-from-${{ github.event.inputs.origin_branch }}-$(date +%s)" echo "temp_branch=$TEMP_BRANCH" >> $GITHUB_OUTPUT - # Checkout target branch git checkout ${{ github.event.inputs.target_branch }} - # Create temporary branch from target git checkout -b $TEMP_BRANCH - echo "Created temporary branch: $TEMP_BRANCH" shell: bash - - name: Attempt merge - id: attempt-merge + - name: Perform update + id: perform-update run: | - # Try to merge origin into temp branch - if git merge origin/${{ github.event.inputs.origin_branch }} --no-ff; then - echo "merge_status=success" >> $GITHUB_OUTPUT - echo "Merge successful, no conflicts detected." - - # Check if there are any changes between branches - if git diff --quiet origin/${{ github.event.inputs.target_branch }} origin/${{ github.event.inputs.origin_branch }}; then - echo "merge_status=no_changes" >> $GITHUB_OUTPUT - echo "No changes to merge. Branches are already in sync." + ORIGIN=${{ github.event.inputs.origin_branch }} + TARGET=${{ github.event.inputs.target_branch }} + FILTER=${{ github.event.inputs.filter }} + if [ -z "$FILTER" ]; then + echo "Merging origin/$ORIGIN into $TARGET..." + if git merge origin/$ORIGIN --no-ff; then + echo "Merge successful, no conflicts detected." + echo "Checking for any changes..." + if git diff --quiet origin/$TARGET origin/$ORIGIN; then + echo "No changes to merge. Branches are already in sync." + echo "::set-output name=status::no_changes::true" + exit 0 + fi + echo "::set-output name=status::success::true" + else + echo "Merge conflicts detected. Will create a PR for manual resolution." + git merge --abort + echo "::set-output name=status::conflict::true" + exit 0 fi else - echo "merge_status=conflict" >> $GITHUB_OUTPUT - echo "Merge conflicts detected. Will create a PR for manual resolution." - - # Abort the merge - git merge --abort - - # Create a new branch with changes from origin - git checkout ${{ github.event.inputs.origin_branch }} - git checkout -b ${{ steps.create-temp-branch.outputs.temp_branch }} + echo "Updating specific path: $FILTER" + git checkout origin/$ORIGIN -- "$FILTER" + if git diff --quiet; then + echo "No changes to merge for path $FILTER." + echo "::set-output name=status::no_changes::true" + exit 0 + fi + echo "Committing changes for path $FILTER" + git add "$FILTER" + git commit -m "chore(branch): update $TARGET from $ORIGIN for path $FILTER" + echo "::set-output name=status::success::true" fi shell: bash - - name: Push changes if no conflicts - id: push-changes - if: steps.attempt-merge.outputs.merge_status == 'success' + - name: Publish updates + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - # Try to push changes directly to target branch - if git push origin ${{ steps.create-temp-branch.outputs.temp_branch }}:${{ github.event.inputs.target_branch }}; then - echo "push_status=success" >> $GITHUB_OUTPUT - echo "Successfully updated ${{ github.event.inputs.target_branch }} with changes from ${{ github.event.inputs.origin_branch }}." - else - echo "push_status=failed" >> $GITHUB_OUTPUT - echo "Direct push failed. Branch may be protected. Will create a PR instead." + TMP_BRANCH=${{ steps.create-temp-branch.outputs.temp_branch }} + TARGET=${{ github.event.inputs.target_branch }} + ORIGIN=${{ github.event.inputs.origin_branch }} + FILTER=${{ github.event.inputs.filter }} + STATUS=${{ steps.perform-update.outputs.status }} + if [ "$STATUS" = "no_changes" ]; then + echo "ℹ️ No changes to merge. Nothing to do." + exit 0 fi - shell: bash - continue-on-error: true - - - name: Create PR if conflicts or protected branch - if: steps.attempt-merge.outputs.merge_status == 'conflict' || steps.push-changes.outputs.push_status == 'failed' - id: create-pr - uses: actions/github-script@v7.0.1 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - // Push the temporary branch - const { execSync } = require('child_process'); - execSync('git push origin ${{ steps.create-temp-branch.outputs.temp_branch }}'); - - const prTitle = `chore(branch): update ${{ github.event.inputs.target_branch }} from ${{ github.event.inputs.origin_branch }}`; - - let prBody = `## Branch Update + if [ "$STATUS" = "success" ]; then + if git push origin $TMP_BRANCH:$TARGET; then + echo "✅ Successfully updated $TARGET with changes from $ORIGIN." + exit 0 + else + echo "ℹ️ Direct push failed. Branch may be protected. Creating a PR..." + fi + elif [ "$STATUS" = "conflict" ]; then + echo "⚠️ Merge conflicts detected. Creating a PR for manual resolution..." + fi + # Build PR body + prBody="## Branch Update - This PR updates \`${{ github.event.inputs.target_branch }}\` with changes from \`${{ github.event.inputs.origin_branch }}\`.`; - - if ('${{ steps.attempt-merge.outputs.merge_status }}' === 'conflict') { - prBody += ` + This PR updates \\`$TARGET\\` with changes from \\`$ORIGIN\\`." + if [ "$STATUS" = "conflict" ]; then + prBody+=" - ### ⚠️ Merge Conflicts + ### ⚠️ Merge Conflicts - This PR contains merge conflicts that need to be resolved manually.`; - } else { - prBody += ` + This PR contains merge conflicts that need to be resolved manually." + else + prBody+=" - ### ℹ️ Protected Branch + ### ℹ️ Protected Branch - This PR was created because \`${{ github.event.inputs.target_branch }}\` is a protected branch that requires changes through pull requests.`; - } - - prBody += ` + This PR was created because \\`$TARGET\\` is a protected branch that requires changes through pull requests." + fi + prBody+=" ### Instructions - 1. ${('${{ steps.attempt-merge.outputs.merge_status }}' === 'conflict') ? 'Resolve the conflicts in this PR' : 'Review the changes'} + 1. $( [ "$STATUS" = "conflict" ] && echo 'Resolve the conflicts in this PR' || echo 'Review the changes' ) 2. Approve and merge the changes - - This PR was automatically created by the Branch Update workflow.`; - - try { - const pr = await github.rest.pulls.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: prTitle, - body: prBody, - head: '${{ steps.create-temp-branch.outputs.temp_branch }}', - base: '${{ github.event.inputs.target_branch }}' - }); - - console.log(`PR created: ${pr.data.html_url}`); - - return { - pr_number: pr.data.number, - pr_url: pr.data.html_url - }; - } catch (error) { - console.log('Error creating PR:'); - console.log(error); - return null; - } - - name: Output results - run: | - if [[ "${{ steps.attempt-merge.outputs.merge_status }}" == "success" && "${{ steps.push-changes.outputs.push_status }}" == "success" ]]; then - echo "✅ Successfully updated ${{ github.event.inputs.target_branch }} with changes from ${{ github.event.inputs.origin_branch }}." - elif [[ "${{ steps.attempt-merge.outputs.merge_status }}" == "success" && "${{ steps.push-changes.outputs.push_status }}" == "failed" ]]; then - echo "ℹ️ Branch is protected. Created PR #$(echo '${{ steps.create-pr.outputs.result }}' | jq -r .pr_number) for review." - echo "PR URL: $(echo '${{ steps.create-pr.outputs.result }}' | jq -r .pr_url)" - elif [[ "${{ steps.attempt-merge.outputs.merge_status }}" == "no_changes" ]]; then - echo "ℹ️ No changes to merge. Branches are already in sync." - elif [[ "${{ steps.attempt-merge.outputs.merge_status }}" == "conflict" ]]; then - echo "⚠️ Merge conflicts detected. Created PR #$(echo '${{ steps.create-pr.outputs.result }}' | jq -r .pr_number) for manual resolution." - echo "PR URL: $(echo '${{ steps.create-pr.outputs.result }}' | jq -r .pr_url)" - fi + This PR was automatically created by the Branch Update workflow." + + # Create PR + prUrl=$(gh pr create \ + --title "chore(branch): update $TARGET from $ORIGIN" \ + --body "$prBody" \ + --head $TMP_BRANCH --base $TARGET) + echo "$prUrl" shell: bash From b64283b67a6c0e5670d082a4979a124c0c718561 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 20 Apr 2025 06:16:38 +0000 Subject: [PATCH 02/29] docs: update changelog with 1 closed issues --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cec7c1c9..80e3de01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +### Fixed +- Fixes "Bug: Unmatching paths in list components return duplicated values" ([#32](https://github.com/architects-toolkit/SmartHopper/issues/32)). + ## [0.2.0-alpha] - 2025-04-06 ### Added From ae378e101ea5a756cf194ad490c7fc91ceeff5c7 Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 08:45:41 +0200 Subject: [PATCH 03/29] ci(workflow): fixed run errors in user-update-branch --- .github/workflows/user-update-branch.yml | 40 ++++++++++++------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index 415e508d..74f8d0e5 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -140,14 +140,14 @@ jobs: echo "Checking for any changes..." if git diff --quiet origin/$TARGET origin/$ORIGIN; then echo "No changes to merge. Branches are already in sync." - echo "::set-output name=status::no_changes::true" + echo "status=no_changes" >> $GITHUB_OUTPUT exit 0 fi - echo "::set-output name=status::success::true" + echo "status=success" >> $GITHUB_OUTPUT else echo "Merge conflicts detected. Will create a PR for manual resolution." git merge --abort - echo "::set-output name=status::conflict::true" + echo "status=conflict" >> $GITHUB_OUTPUT exit 0 fi else @@ -155,13 +155,13 @@ jobs: git checkout origin/$ORIGIN -- "$FILTER" if git diff --quiet; then echo "No changes to merge for path $FILTER." - echo "::set-output name=status::no_changes::true" + echo "status=no_changes" >> $GITHUB_OUTPUT exit 0 fi echo "Committing changes for path $FILTER" git add "$FILTER" git commit -m "chore(branch): update $TARGET from $ORIGIN for path $FILTER" - echo "::set-output name=status::success::true" + echo "status=success" >> $GITHUB_OUTPUT fi shell: bash @@ -170,10 +170,10 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | TMP_BRANCH=${{ steps.create-temp-branch.outputs.temp_branch }} - TARGET=${{ github.event.inputs.target_branch }} - ORIGIN=${{ github.event.inputs.origin_branch }} - FILTER=${{ github.event.inputs.filter }} - STATUS=${{ steps.perform-update.outputs.status }} + TARGET="${{ github.event.inputs.target_branch }}" + ORIGIN="${{ github.event.inputs.origin_branch }}" + FILTER="${{ github.event.inputs.filter }}" + STATUS="${{ steps.perform-update.outputs.status }}" if [ "$STATUS" = "no_changes" ]; then echo "ℹ️ No changes to merge. Nothing to do." exit 0 @@ -191,21 +191,21 @@ jobs: # Build PR body prBody="## Branch Update - This PR updates \\`$TARGET\\` with changes from \\`$ORIGIN\\`." - if [ "$STATUS" = "conflict" ]; then - prBody+=" + This PR updates \`$TARGET\` with changes from \`$ORIGIN\`." + if [ "$STATUS" = "conflict" ]; then + prBody+=" - ### ⚠️ Merge Conflicts + ### ⚠️ Merge Conflicts - This PR contains merge conflicts that need to be resolved manually." - else - prBody+=" + This PR contains merge conflicts that need to be resolved manually." + else + prBody+=" - ### ℹ️ Protected Branch + ### ℹ️ Protected Branch - This PR was created because \\`$TARGET\\` is a protected branch that requires changes through pull requests." - fi - prBody+=" + This PR was created because \`$TARGET\` is a protected branch that requires changes through pull requests." + fi + prBody+=" ### Instructions From 17f92f6f33774178a89590f8331f91e1246b882c Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 08:51:35 +0200 Subject: [PATCH 04/29] ci(workflow): fix not detecting new files and deleted files in user-update-branch with filter --- .github/workflows/user-update-branch.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index 74f8d0e5..88f78239 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -152,12 +152,23 @@ jobs: fi else echo "Updating specific path: $FILTER" + # First check if there are any differences in the specified path between branches + if ! git diff --name-only origin/$TARGET..origin/$ORIGIN -- "$FILTER" | grep -q .; then + echo "No changes to merge for path $FILTER between branches." + echo "status=no_changes" >> $GITHUB_OUTPUT + exit 0 + fi + + # Checkout the files from the origin branch git checkout origin/$ORIGIN -- "$FILTER" + + # Check if the checkout resulted in any changes to the working directory if git diff --quiet; then - echo "No changes to merge for path $FILTER." + echo "No changes to merge for path $FILTER after checkout." echo "status=no_changes" >> $GITHUB_OUTPUT exit 0 fi + echo "Committing changes for path $FILTER" git add "$FILTER" git commit -m "chore(branch): update $TARGET from $ORIGIN for path $FILTER" From 6f14a5b5b0fd962badf7233f17db2f0d9421b9c4 Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 08:55:18 +0200 Subject: [PATCH 05/29] ci(workflow): fix not detecting new files and deleted files in user-update-branch with filter --- .github/workflows/user-update-branch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index 88f78239..83f73c77 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -163,7 +163,7 @@ jobs: git checkout origin/$ORIGIN -- "$FILTER" # Check if the checkout resulted in any changes to the working directory - if git diff --quiet; then + if ! git status --porcelain -- "$FILTER" | grep -q .; then echo "No changes to merge for path $FILTER after checkout." echo "status=no_changes" >> $GITHUB_OUTPUT exit 0 From 0c0e3eb14789c54b25cad42793955b3ef8ad5767 Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 08:57:37 +0200 Subject: [PATCH 06/29] ci(workflow): fix not properly creating PR in user-update-branch --- .github/workflows/user-update-branch.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index 83f73c77..4863fda4 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -199,6 +199,9 @@ jobs: elif [ "$STATUS" = "conflict" ]; then echo "⚠️ Merge conflicts detected. Creating a PR for manual resolution..." fi + # Push temporary branch to remote for PR creation + echo "Pushing temporary branch $TMP_BRANCH to remote..." + git push --set-upstream origin $TMP_BRANCH # Build PR body prBody="## Branch Update From d812b6087b072b8ab8f1d6d534f064a037f8963b Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 09:02:43 +0200 Subject: [PATCH 07/29] ci(workflow): updated PR title and description in user-update-branch with filter --- .github/workflows/user-update-branch.yml | 31 +++++++++++++++--------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index 4863fda4..c26dca2d 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -205,21 +205,28 @@ jobs: # Build PR body prBody="## Branch Update - This PR updates \`$TARGET\` with changes from \`$ORIGIN\`." - if [ "$STATUS" = "conflict" ]; then - prBody+=" - - ### ⚠️ Merge Conflicts - - This PR contains merge conflicts that need to be resolved manually." - else - prBody+=" + This PR updates \`$TARGET\` with changes from \`$ORIGIN\`" + if [ -n "$FILTER" ]; then + prBody+=" for path \`$FILTER\`" + fi + # Build PR title + title="chore(branch): update $TARGET from $ORIGIN" + if [ -n "$FILTER" ]; then + title+=" for path $FILTER" + fi + prBody+=". ### ℹ️ Protected Branch This PR was created because \`$TARGET\` is a protected branch that requires changes through pull requests." - fi - prBody+=" + if [ "$STATUS" = "conflict" ]; then + prBody+=" + + ### ⚠️ Merge Conflicts + + This PR contains merge conflicts that need to be resolved manually." + fi + prBody+=" ### Instructions @@ -230,7 +237,7 @@ jobs: # Create PR prUrl=$(gh pr create \ - --title "chore(branch): update $TARGET from $ORIGIN" \ + --title "$title" \ --body "$prBody" \ --head $TMP_BRANCH --base $TARGET) echo "$prUrl" From 3359e7e72e0959e3e36b64428fab501f192bd263 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 20 Apr 2025 07:11:07 +0000 Subject: [PATCH 08/29] chore(branch): update main from marc-romu/chat for path .github --- .github/actions/code-style/action.yml | 40 ++++ .../code-style/header-fixer/action.yml | 58 ++++++ .../code-style/namespace-fixer/action.yml | 47 +++++ .../code-style/trailing-whitespace/action.yml | 42 ++++ .../code-style/using-sorter/action.yml | 44 ++++ .../update-changelog-issues/action.yml | 2 +- .../documentation/update-changelog/action.yml | 4 +- .github/labels.yml | 3 - .github/workflows/pr-block-dev-to-main.yml | 48 +++++ .../workflows/pr-update-changelog-issues.yml | 65 ++++++ .github/workflows/pr-validation.yml | 142 +++++++++++++ .github/workflows/user-code-style.yml | 23 ++ .github/workflows/user-update-branch.yml | 197 +++++++++--------- 13 files changed, 611 insertions(+), 104 deletions(-) create mode 100644 .github/actions/code-style/action.yml create mode 100644 .github/actions/code-style/header-fixer/action.yml create mode 100644 .github/actions/code-style/namespace-fixer/action.yml create mode 100644 .github/actions/code-style/trailing-whitespace/action.yml create mode 100644 .github/actions/code-style/using-sorter/action.yml create mode 100644 .github/workflows/pr-block-dev-to-main.yml create mode 100644 .github/workflows/pr-update-changelog-issues.yml create mode 100644 .github/workflows/pr-validation.yml create mode 100644 .github/workflows/user-code-style.yml diff --git a/.github/actions/code-style/action.yml b/.github/actions/code-style/action.yml new file mode 100644 index 00000000..53af3b25 --- /dev/null +++ b/.github/actions/code-style/action.yml @@ -0,0 +1,40 @@ +name: "Code Style" +description: "Run code-style checks/fixes: formatting, headers, etc." +inputs: + mode: + description: "Operation mode: fix or check" + required: false + default: "check" +runs: + using: "composite" + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Run dotnet-format + uses: xt0rted/dotnet-format@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + action: ${{ inputs.mode }} + only-changed-files: true + - name: Process C# file headers + uses: ./.github/actions/code-style/header-fixer + with: + mode: ${{ inputs.mode }} + - name: Process trailing whitespace + uses: ./.github/actions/code-style/trailing-whitespace + with: + mode: ${{ inputs.mode }} + - name: Process using directives + uses: ./.github/actions/code-style/using-sorter + with: + mode: ${{ inputs.mode }} + - name: Commit & push changes + if: ${{ inputs.mode == 'fix' }} + shell: bash + run: | + git config user.name "github-actions" + git config user.email "actions@github.com" + git add . + git diff --quiet || (git commit -m "chore: automatically fix code style" && git push) diff --git a/.github/actions/code-style/header-fixer/action.yml b/.github/actions/code-style/header-fixer/action.yml new file mode 100644 index 00000000..ada52981 --- /dev/null +++ b/.github/actions/code-style/header-fixer/action.yml @@ -0,0 +1,58 @@ +name: "Header Fixer" +description: "Check or fix C# file headers to match .editorconfig header template" +inputs: + mode: + description: "Operation mode: fix or check" + required: false + default: "check" +runs: + using: "composite" + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Process C# file headers + if: always() + shell: bash + run: | + FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} -- '*.cs') + ERR=0 + for file in $FILES; do + if [ -f "$file" ]; then + year=$(grep -m1 '^ \* Copyright' "$file" | grep -oE '[0-9]{4}') + [ -z "$year" ] && year=$(date +%Y) + EXPECTED_HEADER=$(cat < tmp && mv tmp "$file" + printf "%s\n\n%s" "$EXPECTED_HEADER" "$(cat \"$file\")" > "$file" + else + echo "Header mismatch in $file" + ERR=1 + fi + fi + fi + done + exit $ERR + - name: Commit & push changes + if: ${{ inputs.mode == 'fix' }} + shell: bash + run: | + git config user.name "github-actions" + git config user.email "actions@github.com" + git add . + git diff --quiet || (git commit -m "chore: automatically fixed file headers" && git push) diff --git a/.github/actions/code-style/namespace-fixer/action.yml b/.github/actions/code-style/namespace-fixer/action.yml new file mode 100644 index 00000000..37fc4388 --- /dev/null +++ b/.github/actions/code-style/namespace-fixer/action.yml @@ -0,0 +1,47 @@ +name: "Namespace Fixer" +description: "Check or fix C# file namespaces to match file path" +inputs: + mode: + description: "Operation mode: fix or check" + required: false + default: "check" +runs: + using: "composite" + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Process C# namespaces + if: always() + shell: bash + run: | + FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} -- '*.cs') + ERR=0 + for file in $FILES; do + if [ -f "$file" ]; then + rel=${file#src/} + ns=SmartHopper.${rel%.*} + ns=${ns//\//.} + actual=$(grep -m1 '^namespace ' "$file" | cut -d ' ' -f2) + if [ "$actual" != "$ns" ]; then + if [ "${{ inputs.mode }}" = "fix" ]; then + echo "Updating namespace in $file to $ns" + sed -i "s/^namespace .*/namespace $ns/" "$file" + echo "WARNING: References to this file may need manual updates." + else + echo "Namespace mismatch in $file: expected $ns but found $actual" + ERR=1 + fi + fi + fi + done + exit $ERR + - name: Commit & push changes + if: ${{ inputs.mode == 'fix' }} + shell: bash + run: | + git config user.name "github-actions" + git config user.email "actions@github.com" + git add . + git diff --quiet || (git commit -m "chore: automatically fix namespaces" && git push) diff --git a/.github/actions/code-style/trailing-whitespace/action.yml b/.github/actions/code-style/trailing-whitespace/action.yml new file mode 100644 index 00000000..910bf0da --- /dev/null +++ b/.github/actions/code-style/trailing-whitespace/action.yml @@ -0,0 +1,42 @@ +name: "Trailing Whitespace" +description: "Check or fix trailing whitespace and normalize line endings" +inputs: + mode: + description: "Operation mode: fix or check" + required: false + default: "check" +runs: + using: "composite" + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Process trailing whitespace + shell: bash + run: | + FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}) + ERR=0 + for file in $FILES; do + if [[ -f "$file" ]]; then + if [[ "${{ inputs.mode }}" == "fix" ]]; then + sed -i 's/[ \t]*$//' "$file" + # normalize to unix line endings + sed -i 's/\r$//' "$file" + else + if grep -qE '[ \t]+$' "$file" || file "$file" | grep -q CRLF; then + echo "Trailing whitespace or CRLF found in $file" + ERR=1 + fi + fi + fi + done + exit $ERR + - name: Commit & push changes + if: ${{ inputs.mode == 'fix' }} + shell: bash + run: | + git config user.name "github-actions" + git config user.email "actions@github.com" + git add . + git diff --quiet || (git commit -m "chore: automatically fixed trailing whitespace" && git push) diff --git a/.github/actions/code-style/using-sorter/action.yml b/.github/actions/code-style/using-sorter/action.yml new file mode 100644 index 00000000..361f256c --- /dev/null +++ b/.github/actions/code-style/using-sorter/action.yml @@ -0,0 +1,44 @@ +name: "Using Sorter" +description: "Check or fix C# using directives ordering and remove unused usings" +inputs: + mode: + description: "Operation mode: fix or check" + required: false + default: "check" +runs: + using: "composite" + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Setup .NET SDK + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 7.0.x + - name: Run using sorter + shell: bash + run: | + FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} -- '*.cs') + ERR=0 + dotnet tool install -g dotnet-format || true + export PATH="$HOME/.dotnet/tools:$PATH" + for file in $FILES; do + if [ -f "$file" ]; then + if [ "${{ inputs.mode }}" = "check" ]; then + dotnet format "$file" --verify-no-changes --fix-analyzers + else + dotnet format "$file" --fix-analyzers + fi + fi + done + # fail if any check error + exit $ERR + - name: Commit & push changes + if: ${{ inputs.mode == 'fix' }} + shell: bash + run: | + git config user.name "github-actions" + git config user.email "actions@github.com" + git add . + git diff --quiet || (git commit -m "chore: automatically sort using directives" && git push) diff --git a/.github/actions/documentation/update-changelog-issues/action.yml b/.github/actions/documentation/update-changelog-issues/action.yml index c40a2548..b5d918e6 100644 --- a/.github/actions/documentation/update-changelog-issues/action.yml +++ b/.github/actions/documentation/update-changelog-issues/action.yml @@ -145,7 +145,7 @@ runs: echo "Adding issue #$ISSUE_NUMBER to changelog: $ISSUE_TITLE" # Format the line to add - LINE="- Fixes \"$ISSUE_TITLE\" ([#$ISSUE_NUMBER](https://github.com/$REPO/issues/$ISSUE_NUMBER))." + LINE="- (automatically added) Fixes \"$ISSUE_TITLE\" ([#$ISSUE_NUMBER](https://github.com/$REPO/issues/$ISSUE_NUMBER))." # Add the line to the Fixed section at the determined position sed -i "${INSERT_LINE}i\\$LINE" "$CHANGELOG_PATH" diff --git a/.github/actions/documentation/update-changelog/action.yml b/.github/actions/documentation/update-changelog/action.yml index 4d8850bf..76f1a99b 100644 --- a/.github/actions/documentation/update-changelog/action.yml +++ b/.github/actions/documentation/update-changelog/action.yml @@ -6,8 +6,8 @@ inputs: required: true type: choice options: - - create-release # Create a new release section from Unreleased content - - add-line # Add a new line to a section in the Unreleased area + - create-release # Create a new release section from Unreleased content + - add-line # Add a new line to a section in the Unreleased area version: description: 'Version to add to changelog (required for create-release action)' required: false diff --git a/.github/labels.yml b/.github/labels.yml index ef3ebbe9..cde32c17 100644 --- a/.github/labels.yml +++ b/.github/labels.yml @@ -47,9 +47,6 @@ - name: "component: GhGet" color: "000" description: "Issues related to the Grasshopper Get Components component" -- name: "component: GhGetSelected" - color: "000" - description: "Issues related to the Grasshopper Get Selected Components component" - name: "component: GhPut" color: "000" description: "Issues related to the Grasshopper Put Components component" diff --git a/.github/workflows/pr-block-dev-to-main.yml b/.github/workflows/pr-block-dev-to-main.yml new file mode 100644 index 00000000..ed1210fc --- /dev/null +++ b/.github/workflows/pr-block-dev-to-main.yml @@ -0,0 +1,48 @@ + +name: 📦 Block Dev Release to Main + +# Description: This workflow blocks development release versions from being merged into the main branch. +# It checks for the presence of "-dev" in the version number and prevents the merge if found. +# +# Trigger: Automatically when a pull request is opened or updated targeting the main branch +# +# Permissions: +# - contents:read - Required to read repository content +# - pull-requests:read - Required to read pull request information + +on: + pull_request: + branches: [ main ] + paths: + - 'Solution.props' + +permissions: + contents: read + pull-requests: read + +jobs: + check-dev-release: + name: 🚫 Block Dev Release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Get version from Solution.props + id: get-version + uses: ./.github/actions/versioning/get-version + + - name: Check for Dev Release + id: check-dev + run: | + VERSION="${{ steps.get-version.outputs.version }}" + echo "Version found: $VERSION" + + if [[ $VERSION == *"-dev"* ]]; then + echo "::error::Development release versions (-dev) cannot be merged into main branch" + echo "IS_DEV_RELEASE=true" >> $GITHUB_ENV + exit 1 + else + echo "IS_DEV_RELEASE=false" >> $GITHUB_ENV + fi diff --git a/.github/workflows/pr-update-changelog-issues.yml b/.github/workflows/pr-update-changelog-issues.yml new file mode 100644 index 00000000..b874e552 --- /dev/null +++ b/.github/workflows/pr-update-changelog-issues.yml @@ -0,0 +1,65 @@ +name: 📝 Update Changelog with Closed Issues + +# Description: This workflow updates the CHANGELOG.md with closed-solved issues that are not already mentioned. +# It identifies issues marked as solved in the last month and adds them to the Unreleased/Fixed section. +# +# Triggers: +# - Automatically when a pull request is created or updated targeting main or dev branches +# - Manually via workflow_dispatch on any branch +# +# Permissions: +# - contents:write - Required to commit changes to the repository +# - pull-requests:write - Required to update pull requests +# - issues:read - Required to read issue information + +on: + pull_request: + branches: + - main + - dev + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + issues: read + +jobs: + update-changelog: + name: 📝 Update Changelog with Closed Issues + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.ref }} + + - name: Update Changelog with Closed Issues + id: update-changelog-issues + uses: ./.github/actions/documentation/update-changelog-issues + with: + token: ${{ secrets.GITHUB_TOKEN }} + days-lookback: 30 + + - name: Commit changes if needed + if: steps.update-changelog-issues.outputs.updated == 'true' && github.event_name == 'pull_request' + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add CHANGELOG.md + git commit -m "docs: update changelog with ${{ steps.update-changelog-issues.outputs.issues-added }} closed issues" + git push origin HEAD:${{ github.event.pull_request.head.ref }} + + echo "Added ${{ steps.update-changelog-issues.outputs.issues-added }} closed issues to the changelog" + + - name: Commit changes if workflow_dispatch + if: steps.update-changelog-issues.outputs.updated == 'true' && github.event_name == 'workflow_dispatch' + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add CHANGELOG.md + git commit -m "docs: update changelog with ${{ steps.update-changelog-issues.outputs.issues-added }} closed issues" + git push origin HEAD:${{ github.ref_name }} + + echo "Added ${{ steps.update-changelog-issues.outputs.issues-added }} closed issues to the changelog" diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml new file mode 100644 index 00000000..f61df658 --- /dev/null +++ b/.github/workflows/pr-validation.yml @@ -0,0 +1,142 @@ +name: 📦 Pull Request Validation + +# Description: This workflow validates pull requests to ensure they meet project standards. +# It checks for version conflicts, validates formatting, and performs other quality checks +# to maintain code integrity before merging. +# +# Triggers: +# - Automatically when a pull request is created or updated targeting main or dev branches +# +# Permissions: +# - contents:read - Required to read repository content +# - pull-requests:read - Required to read pull request information + +on: + pull_request: + branches: + - main + - dev + +permissions: + contents: read + pull-requests: read + +jobs: + version-check: + name: 📦 Version Check + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + + - name: Validate Version in Solution.props + id: version-check + uses: ./.github/actions/versioning/get-version + + - name: Verify Version Format + run: | + VERSION="${{ steps.version-check.outputs.version }}" + echo "Checking version: $VERSION" + if [[ -z "$VERSION" ]]; then + echo "Error: Failed to extract version from Solution.props" + exit 1 + fi + + echo "Version components:" + echo "- Major: ${{ steps.version-check.outputs.major }}" + echo "- Minor: ${{ steps.version-check.outputs.minor }}" + echo "- Patch: ${{ steps.version-check.outputs.patch }}" + echo "- Suffix: ${{ steps.version-check.outputs.suffix }}" + + header-check: + name: 🛡️ Header Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + with: + fetch-depth: 0 + - name: Validate File Headers + uses: ./.github/actions/code-style/header-fixer + with: + mode: check + + namespace-check: + name: 📂 Namespace Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + with: + fetch-depth: 0 + - name: Validate Namespaces + uses: ./.github/actions/code-style/namespace-fixer + with: + mode: check + + changelog-check: + name: 📝 Changelog Check + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Check CHANGELOG.md is Updated + uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32 # v46.0.1 + id: changelog-check + with: + files: CHANGELOG.md + + - name: Validate Changelog Update + if: steps.changelog-check.outputs.any_changed != 'true' + env: + CHANGED_FILES: ${{ steps.changelog-check.outputs.all_changed_files }} + run: | + echo "Error: CHANGELOG.md must be updated with pull request changes" + exit 1 + + title-check: + name: 🏷️ PR Title Style Check + runs-on: ubuntu-latest + permissions: + pull-requests: read + steps: + - name: Validate PR Title Style + run: | + PR_TITLE="${{ github.event.pull_request.title }}" + echo "Checking PR title: $PR_TITLE" + + # Valid types based on conventional commits + VALID_TYPES="feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert|security" + + # Check if PR title follows conventional commits format + if [[ ! $PR_TITLE =~ ^($VALID_TYPES)(\([a-z0-9-]+\))?:\ .+ ]]; then + echo "Error: Pull request title must follow conventional commits format" + echo "" + echo "Required format: (): " + echo "" + echo "Where is one of:" + echo " feat: A new feature" + echo " fix: A bug fix" + echo " docs: Documentation changes" + echo " style: Code style changes (formatting, etc)" + echo " refactor: Code refactoring" + echo " perf: Performance improvements" + echo " test: Adding or modifying tests" + echo " build: Changes to build process or tools" + echo " ci: Changes to CI configuration" + echo " chore: Other changes that don't modify src or test files" + echo " revert: Revert a previous commit" + echo " security: Security-related changes" + echo "" + echo "Examples:" + echo " feat: add new grasshopper component" + echo " fix(component): resolve issue with parameter handling" + echo " docs(readme): update installation instructions" + echo " security: update dependencies to address vulnerabilities" + exit 1 + else + echo "PR title format is valid! ✅" + fi diff --git a/.github/workflows/user-code-style.yml b/.github/workflows/user-code-style.yml new file mode 100644 index 00000000..912c64f9 --- /dev/null +++ b/.github/workflows/user-code-style.yml @@ -0,0 +1,23 @@ +name: 👤🖌️ Fix Code Style +permissions: + contents: write +on: + workflow_dispatch: + +jobs: + code-style: + name: 🛠️ Fix Code Style + runs-on: ubuntu-latest + steps: + - name: Run Code Style + uses: ./.github/actions/code-style + with: + mode: "fix" + namespace-check: + name: 📂 Namespaces Check + runs-on: ubuntu-latest + steps: + - name: Run Namespace Fixer + uses: ./.github/actions/code-style/namespace-fixer + with: + mode: "check" diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index c26dca2d..62e19c37 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -24,10 +24,6 @@ on: description: 'Target branch (to be updated)' required: true type: string - filter: - description: 'Specific folder to update (e.g., .github or src/SmartHopper.Components). Leave blank to update all code.' - required: false - type: string permissions: contents: write @@ -120,125 +116,130 @@ jobs: # Create a unique branch name TEMP_BRANCH="update-branch/${{ github.event.inputs.target_branch }}-from-${{ github.event.inputs.origin_branch }}-$(date +%s)" echo "temp_branch=$TEMP_BRANCH" >> $GITHUB_OUTPUT + # Checkout target branch git checkout ${{ github.event.inputs.target_branch }} + # Create temporary branch from target git checkout -b $TEMP_BRANCH + echo "Created temporary branch: $TEMP_BRANCH" shell: bash - - name: Perform update - id: perform-update + - name: Attempt merge + id: attempt-merge run: | - ORIGIN=${{ github.event.inputs.origin_branch }} - TARGET=${{ github.event.inputs.target_branch }} - FILTER=${{ github.event.inputs.filter }} - if [ -z "$FILTER" ]; then - echo "Merging origin/$ORIGIN into $TARGET..." - if git merge origin/$ORIGIN --no-ff; then - echo "Merge successful, no conflicts detected." - echo "Checking for any changes..." - if git diff --quiet origin/$TARGET origin/$ORIGIN; then - echo "No changes to merge. Branches are already in sync." - echo "status=no_changes" >> $GITHUB_OUTPUT - exit 0 - fi - echo "status=success" >> $GITHUB_OUTPUT - else - echo "Merge conflicts detected. Will create a PR for manual resolution." - git merge --abort - echo "status=conflict" >> $GITHUB_OUTPUT - exit 0 + # Try to merge origin into temp branch + if git merge origin/${{ github.event.inputs.origin_branch }} --no-ff; then + echo "merge_status=success" >> $GITHUB_OUTPUT + echo "Merge successful, no conflicts detected." + + # Check if there are any changes between branches + if git diff --quiet origin/${{ github.event.inputs.target_branch }} origin/${{ github.event.inputs.origin_branch }}; then + echo "merge_status=no_changes" >> $GITHUB_OUTPUT + echo "No changes to merge. Branches are already in sync." fi else - echo "Updating specific path: $FILTER" - # First check if there are any differences in the specified path between branches - if ! git diff --name-only origin/$TARGET..origin/$ORIGIN -- "$FILTER" | grep -q .; then - echo "No changes to merge for path $FILTER between branches." - echo "status=no_changes" >> $GITHUB_OUTPUT - exit 0 - fi - - # Checkout the files from the origin branch - git checkout origin/$ORIGIN -- "$FILTER" + echo "merge_status=conflict" >> $GITHUB_OUTPUT + echo "Merge conflicts detected. Will create a PR for manual resolution." - # Check if the checkout resulted in any changes to the working directory - if ! git status --porcelain -- "$FILTER" | grep -q .; then - echo "No changes to merge for path $FILTER after checkout." - echo "status=no_changes" >> $GITHUB_OUTPUT - exit 0 - fi + # Abort the merge + git merge --abort - echo "Committing changes for path $FILTER" - git add "$FILTER" - git commit -m "chore(branch): update $TARGET from $ORIGIN for path $FILTER" - echo "status=success" >> $GITHUB_OUTPUT + # Create a new branch with changes from origin + git checkout ${{ github.event.inputs.origin_branch }} + git checkout -b ${{ steps.create-temp-branch.outputs.temp_branch }} fi shell: bash - - name: Publish updates - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Push changes if no conflicts + id: push-changes + if: steps.attempt-merge.outputs.merge_status == 'success' run: | - TMP_BRANCH=${{ steps.create-temp-branch.outputs.temp_branch }} - TARGET="${{ github.event.inputs.target_branch }}" - ORIGIN="${{ github.event.inputs.origin_branch }}" - FILTER="${{ github.event.inputs.filter }}" - STATUS="${{ steps.perform-update.outputs.status }}" - if [ "$STATUS" = "no_changes" ]; then - echo "ℹ️ No changes to merge. Nothing to do." - exit 0 - fi - if [ "$STATUS" = "success" ]; then - if git push origin $TMP_BRANCH:$TARGET; then - echo "✅ Successfully updated $TARGET with changes from $ORIGIN." - exit 0 - else - echo "ℹ️ Direct push failed. Branch may be protected. Creating a PR..." - fi - elif [ "$STATUS" = "conflict" ]; then - echo "⚠️ Merge conflicts detected. Creating a PR for manual resolution..." - fi - # Push temporary branch to remote for PR creation - echo "Pushing temporary branch $TMP_BRANCH to remote..." - git push --set-upstream origin $TMP_BRANCH - # Build PR body - prBody="## Branch Update - - This PR updates \`$TARGET\` with changes from \`$ORIGIN\`" - if [ -n "$FILTER" ]; then - prBody+=" for path \`$FILTER\`" - fi - # Build PR title - title="chore(branch): update $TARGET from $ORIGIN" - if [ -n "$FILTER" ]; then - title+=" for path $FILTER" + # Try to push changes directly to target branch + if git push origin ${{ steps.create-temp-branch.outputs.temp_branch }}:${{ github.event.inputs.target_branch }}; then + echo "push_status=success" >> $GITHUB_OUTPUT + echo "Successfully updated ${{ github.event.inputs.target_branch }} with changes from ${{ github.event.inputs.origin_branch }}." + else + echo "push_status=failed" >> $GITHUB_OUTPUT + echo "Direct push failed. Branch may be protected. Will create a PR instead." fi - prBody+=". + shell: bash + continue-on-error: true - ### ℹ️ Protected Branch + - name: Create PR if conflicts or protected branch + if: steps.attempt-merge.outputs.merge_status == 'conflict' || steps.push-changes.outputs.push_status == 'failed' + id: create-pr + uses: actions/github-script@v7.0.1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + // Push the temporary branch + const { execSync } = require('child_process'); + execSync('git push origin ${{ steps.create-temp-branch.outputs.temp_branch }}'); + + const prTitle = `chore(branch): update ${{ github.event.inputs.target_branch }} from ${{ github.event.inputs.origin_branch }}`; + + let prBody = `## Branch Update - This PR was created because \`$TARGET\` is a protected branch that requires changes through pull requests." - if [ "$STATUS" = "conflict" ]; then - prBody+=" + This PR updates \`${{ github.event.inputs.target_branch }}\` with changes from \`${{ github.event.inputs.origin_branch }}\`.`; + + if ('${{ steps.attempt-merge.outputs.merge_status }}' === 'conflict') { + prBody += ` ### ⚠️ Merge Conflicts - This PR contains merge conflicts that need to be resolved manually." - fi - prBody+=" + This PR contains merge conflicts that need to be resolved manually.`; + } else { + prBody += ` + + ### ℹ️ Protected Branch + + This PR was created because \`${{ github.event.inputs.target_branch }}\` is a protected branch that requires changes through pull requests.`; + } + + prBody += ` ### Instructions - 1. $( [ "$STATUS" = "conflict" ] && echo 'Resolve the conflicts in this PR' || echo 'Review the changes' ) + 1. ${('${{ steps.attempt-merge.outputs.merge_status }}' === 'conflict') ? 'Resolve the conflicts in this PR' : 'Review the changes'} 2. Approve and merge the changes + + This PR was automatically created by the Branch Update workflow.`; + + try { + const pr = await github.rest.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: prTitle, + body: prBody, + head: '${{ steps.create-temp-branch.outputs.temp_branch }}', + base: '${{ github.event.inputs.target_branch }}' + }); + + console.log(`PR created: ${pr.data.html_url}`); + + return { + pr_number: pr.data.number, + pr_url: pr.data.html_url + }; + } catch (error) { + console.log('Error creating PR:'); + console.log(error); + return null; + } - This PR was automatically created by the Branch Update workflow." - - # Create PR - prUrl=$(gh pr create \ - --title "$title" \ - --body "$prBody" \ - --head $TMP_BRANCH --base $TARGET) - echo "$prUrl" + - name: Output results + run: | + if [[ "${{ steps.attempt-merge.outputs.merge_status }}" == "success" && "${{ steps.push-changes.outputs.push_status }}" == "success" ]]; then + echo "✅ Successfully updated ${{ github.event.inputs.target_branch }} with changes from ${{ github.event.inputs.origin_branch }}." + elif [[ "${{ steps.attempt-merge.outputs.merge_status }}" == "success" && "${{ steps.push-changes.outputs.push_status }}" == "failed" ]]; then + echo "ℹ️ Branch is protected. Created PR #$(echo '${{ steps.create-pr.outputs.result }}' | jq -r .pr_number) for review." + echo "PR URL: $(echo '${{ steps.create-pr.outputs.result }}' | jq -r .pr_url)" + elif [[ "${{ steps.attempt-merge.outputs.merge_status }}" == "no_changes" ]]; then + echo "ℹ️ No changes to merge. Branches are already in sync." + elif [[ "${{ steps.attempt-merge.outputs.merge_status }}" == "conflict" ]]; then + echo "⚠️ Merge conflicts detected. Created PR #$(echo '${{ steps.create-pr.outputs.result }}' | jq -r .pr_number) for manual resolution." + echo "PR URL: $(echo '${{ steps.create-pr.outputs.result }}' | jq -r .pr_url)" + fi shell: bash From 4f0e270b0a89baa2ba609fc266523b4463a5fbe5 Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 09:15:56 +0200 Subject: [PATCH 09/29] ci(workflows): remove old code --- .github/actions/version-tools/README.md | 168 ------------- .github/actions/version-tools/action.yml | 181 -------------- .../changelog-updater/action.yml | 169 ------------- .../version-calculator/action.yml | 229 ------------------ .../workflows/github-pr-block-dev-to-main.yml | 48 ---- .../github-pr-update-changelog-issues.yml | 65 ----- .github/workflows/github-pr-validation.yml | 118 --------- 7 files changed, 978 deletions(-) delete mode 100644 .github/actions/version-tools/README.md delete mode 100644 .github/actions/version-tools/action.yml delete mode 100644 .github/actions/version-tools/changelog-updater/action.yml delete mode 100644 .github/actions/version-tools/version-calculator/action.yml delete mode 100644 .github/workflows/github-pr-block-dev-to-main.yml delete mode 100644 .github/workflows/github-pr-update-changelog-issues.yml delete mode 100644 .github/workflows/github-pr-validation.yml diff --git a/.github/actions/version-tools/README.md b/.github/actions/version-tools/README.md deleted file mode 100644 index 014c07bd..00000000 --- a/.github/actions/version-tools/README.md +++ /dev/null @@ -1,168 +0,0 @@ -# Version Tools - -A set of composite actions for managing versioning in the SmartHopper project. - -## Overview - -This directory contains several composite actions that help with various aspects of version management: - -1. **Version Tools** (`action.yml`) - The main action for core version operations: - - `get-version`: Extract the current version from Solution.props - - `update-version`: Update the version in Solution.props - - `update-badge`: Update the version and status badges in README.md - -2. **Version Calculator** (`version-calculator/action.yml`) - For calculating new versions based on semantic versioning rules: - - Supports multiple increment types: `patch`, `minor`, `major`, `date`, `auto-date` - - Automatically detects and updates dates in development versions when using `auto-date` - - Handles pre-release suffixes: `dev`, `alpha`, `beta`, `rc`, or removal - -3. **Changelog Updater** (`changelog-updater/action.yml`) - For updating the CHANGELOG.md file: - - `create-release`: Creates a new release section from Unreleased content - - `add-line`: Adds a new line to a section in the Unreleased area - - Supports security fixes and issue references - -## Usage Examples - -### Getting the Current Version - -```yaml -- name: Get current version - id: current-version - uses: ./.github/actions/version-tools - with: - task: get-version -``` - -### Calculating a New Version - -```yaml -- name: Calculate new version - id: calculate-version - uses: ./.github/actions/version-tools/version-calculator - with: - version: ${{ steps.current-version.outputs.version }} - increment: patch # Options: none, patch, minor, major, date, auto-date - change-pre-release: none # Options: none, dev, alpha, beta, rc, remove -``` - -### Auto-updating Date in Development Versions - -```yaml -- name: Auto-update version date if needed - id: update-date - uses: ./.github/actions/version-tools/version-calculator - with: - version: ${{ steps.current-version.outputs.version }} - increment: auto-date -``` - -### Changing Pre-release Type - -```yaml -- name: Change to beta release - id: change-to-beta - uses: ./.github/actions/version-tools/version-calculator - with: - version: ${{ steps.current-version.outputs.version }} - increment: none - change-pre-release: beta -``` - -### Updating the Version in Solution.props - -```yaml -- name: Update version - uses: ./.github/actions/version-tools - with: - task: update-version - new-version: ${{ steps.calculate-version.outputs.new-version }} -``` - -### Updating Badges in README.md - -```yaml -- name: Update badges - id: update-badge - uses: ./.github/actions/version-tools - with: - task: update-badge -``` - -### Adding a Line to CHANGELOG.md - -```yaml -- name: Add security fix to CHANGELOG.md - uses: ./.github/actions/version-tools/changelog-updater - with: - action: add-line - section: Security - description: "Fixed critical security vulnerability in XYZ component" - issue-number: 123 # Optional -``` - -### Creating a New Release in CHANGELOG.md - -```yaml -- name: Update CHANGELOG.md with version changes - uses: ./.github/actions/version-tools/changelog-updater - with: - action: create-release - version: ${{ steps.calculate-version.outputs.new-version }} -``` - -## Action Inputs and Outputs - -### Version Tools (Main Action) - -**Inputs:** -- `task`: (required) One of `get-version`, `update-version`, `update-badge` -- `new-version`: (required for update-version) New version to set -- `branch`: (optional) Branch name for context - -**Outputs:** -- `version`: Current version from Solution.props -- `major`, `minor`, `patch`, `suffix`: Version components -- `badges-changed`: Whether badges were changed - -### Version Calculator - -**Inputs:** -- `version`: (required) Input version in format X.X.X or X.X.X-suffix.YYMMDD -- `increment`: (required) Type of increment: - - `none`: No increment, only apply other changes - - `patch`: Increment patch version - - `minor`: Increment minor version - - `major`: Increment major version - - `date`: Explicitly update the date part of a pre-release version - - `auto-date`: Auto-detect if this is a dated version and update it if needed -- `change-pre-release`: (optional) Change or add pre-release suffix: - - `none`: No change to pre-release suffix (default) - - `dev`: Change to development pre-release - - `alpha`: Change to alpha pre-release - - `beta`: Change to beta pre-release - - `rc`: Change to release candidate - - `remove`: Remove pre-release suffix entirely -- `pre-release-date`: (optional) Date to use for pre-release in format YYMMDD - -**Outputs:** -- `new-version`: Calculated new version -- `is-prerelease`: Whether the calculated version is a pre-release -- `was-date-updated`: Whether the date was updated in a development version -- `major`, `minor`, `patch`, `suffix`: Version components - -### Changelog Updater - -**Inputs:** -- `action`: (required) Action to perform on the changelog: - - `create-release`: Create a new release section from Unreleased content - - `add-line`: Add a new line to a section in the Unreleased area -- `version`: (required for create-release) Version to add to changelog -- `date`: (optional) Release date (defaults to today) -- `section`: (required for add-line) Section to add the line to (Added, Changed, Deprecated, Removed, Fixed, Security) -- `description`: (required for add-line) Description to add as a new line -- `issue-number`: (optional for add-line) Issue number to reference in the new line -- `changelog-path`: (optional) Path to CHANGELOG.md file - -**Outputs:** -- `updated`: Whether the changelog was updated -- `unreleased-content`: Content that was in the Unreleased section diff --git a/.github/actions/version-tools/action.yml b/.github/actions/version-tools/action.yml deleted file mode 100644 index 2f19d05e..00000000 --- a/.github/actions/version-tools/action.yml +++ /dev/null @@ -1,181 +0,0 @@ -name: 'Version Tools' -description: 'Common tools for version management in SmartHopper' -inputs: - task: - description: 'Task to perform' - required: true - type: choice - options: - - get-version - - update-version - - update-badge - new-version: - description: 'New version to set (only for update-version task)' - required: false - branch: - description: 'Branch name (for context)' - required: false - default: '' - -outputs: - version: - description: 'Current version from Solution.props' - value: ${{ steps.get-version.outputs.version }} - major: - description: 'Major version component' - value: ${{ steps.parse-version.outputs.major }} - minor: - description: 'Minor version component' - value: ${{ steps.parse-version.outputs.minor }} - patch: - description: 'Patch version component' - value: ${{ steps.parse-version.outputs.patch }} - suffix: - description: 'Version suffix (if any)' - value: ${{ steps.parse-version.outputs.suffix }} - badges-changed: - description: 'Whether badges were changed' - value: ${{ steps.update-badge.outputs.badges-changed || 'false' }} - -runs: - using: "composite" - steps: - - name: Get version from Solution.props - id: get-version - if: inputs.task == 'get-version' || inputs.task == 'update-version' || inputs.task == 'update-badge' - shell: bash - run: | - VERSION=$(grep -oP '(?<=)[^<]+' Solution.props) - echo "Current version: $VERSION" - echo "version=$VERSION" >> $GITHUB_OUTPUT - - - name: Parse version components - id: parse-version - if: inputs.task == 'get-version' || inputs.task == 'update-version' - shell: bash - run: | - VERSION="${{ steps.get-version.outputs.version }}" - if [[ $VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-[a-zA-Z0-9]+(\.[0-9]+)?)?$ ]]; then - MAJOR="${BASH_REMATCH[1]}" - MINOR="${BASH_REMATCH[2]}" - PATCH="${BASH_REMATCH[3]}" - SUFFIX="${BASH_REMATCH[4]}" - - echo "Parsed version: Major=$MAJOR, Minor=$MINOR, Patch=$PATCH, Suffix=$SUFFIX" - echo "major=$MAJOR" >> $GITHUB_OUTPUT - echo "minor=$MINOR" >> $GITHUB_OUTPUT - echo "patch=$PATCH" >> $GITHUB_OUTPUT - echo "suffix=$SUFFIX" >> $GITHUB_OUTPUT - else - echo "Error: Version format does not follow semantic versioning." - exit 1 - fi - - - name: Update version in Solution.props - id: update-version - if: inputs.task == 'update-version' - shell: bash - run: | - if [[ -z "${{ inputs.new-version }}" ]]; then - echo "Error: new-version input is required for update-version task" - exit 1 - fi - - echo "Updating version from ${{ steps.get-version.outputs.version }} to ${{ inputs.new-version }}" - sed -i "s/.*<\/SolutionVersion>/${{ inputs.new-version }}<\/SolutionVersion>/" Solution.props - echo "Updated Solution.props with new version: ${{ inputs.new-version }}" - - - name: Update version badge in README.md - id: update-badge - if: inputs.task == 'update-badge' || inputs.task == 'update-version' - shell: bash - run: | - # Get the current version (either the updated one or the original) - if [[ "${{ inputs.task }}" == "update-version" ]]; then - VERSION="${{ inputs.new-version }}" - else - VERSION="${{ steps.get-version.outputs.version }}" - fi - - echo "Current version from Solution.props: $VERSION" - - # Format version for shields.io by encoding special characters - ENCODED_VERSION=$(echo "$VERSION" | sed 's/-/--/g' | sed 's/\./%2E/g') - - # Determine color and status based on version - if [[ $VERSION == *"-dev"* ]]; then - COLOR="brown" - STATUS="Unstable%20Development" - elif [[ $VERSION == *"-alpha"* ]]; then - COLOR="orange" - STATUS="Alpha" - elif [[ $VERSION == *"-beta"* ]]; then - COLOR="yellow" - STATUS="Beta" - elif [[ $VERSION == *"-rc"* ]]; then - COLOR="lightblue" - STATUS="Release%20Candidate" - else - COLOR="lightgreen" - STATUS="Stable" - fi - - # Create new badge URLs - VERSION_BADGE_URL="https://img.shields.io/badge/version-$ENCODED_VERSION-$COLOR" - STATUS_BADGE_URL="https://img.shields.io/badge/status-$STATUS-$COLOR" - - # Create badge markdown - VERSION_BADGE="[![Version]($VERSION_BADGE_URL)](https://github.com/architects-toolkit/SmartHopper/releases)" - STATUS_BADGE="[![Status]($STATUS_BADGE_URL)](https://github.com/architects-toolkit/SmartHopper/releases)" - - echo "New version badge: $VERSION_BADGE" - echo "New status badge: $STATUS_BADGE" - - BADGES_CHANGED=false - - # Update README.md with new badges - if [[ -f "README.md" ]]; then - # Check if README contains version badge and if it needs updating - if grep -q "\[\!\[Version\]" README.md; then - echo "Found version badge in README" - - # Extract current badge URL to check if color matches - CURRENT_VERSION_BADGE=$(grep -o "\[\!\[Version\](https://img\.shields\.io/badge/version[^)]*)" README.md || echo "") - - # Check if the badge needs updating (different version or color) - if [[ "$CURRENT_VERSION_BADGE" != *"$ENCODED_VERSION"* || "$CURRENT_VERSION_BADGE" != *"-$COLOR"* ]]; then - sed -i "s|\[\!\[Version\](https://img\.shields\.io/badge/version[^)]*)|[![Version]($VERSION_BADGE_URL)|g" README.md - echo "Updated version badge" - BADGES_CHANGED=true - else - echo "Version badge is already up to date" - fi - else - echo "No version badge found in README to replace" - fi - - # Check if README contains status badge and if it needs updating - if grep -q "\[\!\[Status\]" README.md; then - echo "Found status badge in README" - - # Extract current badge URL to check if status or color matches - CURRENT_STATUS_BADGE=$(grep -o "\[\!\[Status\](https://img\.shields\.io/badge/status[^)]*)" README.md || echo "") - - # Check if the badge needs updating (different status or color) - if [[ "$CURRENT_STATUS_BADGE" != *"$STATUS"* || "$CURRENT_STATUS_BADGE" != *"-$COLOR"* ]]; then - sed -i "s|\[\!\[Status\](https://img\.shields\.io/badge/status[^)]*)|[![Status]($STATUS_BADGE_URL)|g" README.md - echo "Updated status badge" - BADGES_CHANGED=true - else - echo "Status badge is already up to date" - fi - else - echo "No status badge found in README to replace" - fi - - # Output whether badges were changed - echo "badges-changed=$BADGES_CHANGED" >> $GITHUB_OUTPUT - else - echo "README.md not found, skipping badge update" - echo "badges-changed=false" >> $GITHUB_OUTPUT - fi diff --git a/.github/actions/version-tools/changelog-updater/action.yml b/.github/actions/version-tools/changelog-updater/action.yml deleted file mode 100644 index 17e9e7bb..00000000 --- a/.github/actions/version-tools/changelog-updater/action.yml +++ /dev/null @@ -1,169 +0,0 @@ -name: 'Changelog Updater' -description: 'Update CHANGELOG.md structure and content with new version information' -inputs: - action: - description: 'Action to perform on the changelog' - required: true - type: choice - options: - - create-release # Create a new release section from Unreleased content - - add-line # Add a new line to a section in the Unreleased area - version: - description: 'Version to add to changelog (required for create-release action)' - required: false - date: - description: 'Release date (defaults to today)' - required: false - section: - description: 'Section to add the line to (required for add-line action)' - required: false - type: choice - options: - - Added - - Changed - - Deprecated - - Removed - - Fixed - - Security - description: - description: 'Description to add as a new line (required for add-line action)' - required: false - issue-number: - description: 'Issue number to reference in the new line (optional for add-line action)' - required: false - changelog-path: - description: 'Path to CHANGELOG.md file' - required: false - default: 'CHANGELOG.md' - -outputs: - updated: - description: 'Whether the changelog was updated' - value: ${{ steps.update-changelog.outputs.updated }} - unreleased-content: - description: 'Content that was in the Unreleased section' - value: ${{ steps.update-changelog.outputs.unreleased-content }} - -runs: - using: "composite" - steps: - - name: Update CHANGELOG.md - id: update-changelog - shell: bash - run: | - # Set today's date if not provided - if [[ -z "${{ inputs.date }}" ]]; then - TODAY=$(date +%Y-%m-%d) - else - TODAY="${{ inputs.date }}" - fi - - CHANGELOG="${{ inputs.changelog-path }}" - ACTION="${{ inputs.action }}" - - echo "Updating CHANGELOG.md with action: $ACTION" - - # Check if CHANGELOG.md exists - if [[ ! -f "$CHANGELOG" ]]; then - echo "Error: CHANGELOG.md not found at '$CHANGELOG'" - exit 1 - fi - - # Handle different actions - if [[ "$ACTION" == "create-release" ]]; then - # Create a new release section from Unreleased content - VERSION="${{ inputs.version }}" - - if [[ -z "$VERSION" ]]; then - echo "Error: version is required for create-release action" - exit 1 - fi - - echo "Creating new release section for version: $VERSION (date: $TODAY)" - - # Extract existing Unreleased content - UNRELEASED_CONTENT=$(awk '/^## \[Unreleased\]/,/^## \[[0-9]+\.[0-9]+\.[0-9]+/ {if (!/^## \[/) print}' "$CHANGELOG" | sed '/./,$!d') - - # Create a multiline output for unreleased content - echo "unreleased-content<> $GITHUB_OUTPUT - echo "$UNRELEASED_CONTENT" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - - # Only proceed if we're creating an actual release (not just updating Unreleased) - if [[ "$VERSION" != "Unreleased" ]]; then - # Create temporary file for new changelog - TMP_CHANGELOG="${CHANGELOG}.tmp" - - # Add header part and Unreleased section - awk '/^# Changelog/,/^## \[Unreleased\]/ {print}' "$CHANGELOG" > "$TMP_CHANGELOG" - echo "" >> "$TMP_CHANGELOG" - - # Add the new version section - echo "## [$VERSION] - $TODAY" >> "$TMP_CHANGELOG" - echo "" >> "$TMP_CHANGELOG" - - # Add unreleased content to the new version section if it exists - if [[ -n "$UNRELEASED_CONTENT" ]]; then - echo "$UNRELEASED_CONTENT" >> "$TMP_CHANGELOG" - fi - - # Add the rest of the file (everything after the Unreleased section) - awk '/^## \[[0-9]+\.[0-9]+\.[0-9]+/ {found=1} found==1 {print}' "$CHANGELOG" >> "$TMP_CHANGELOG" - - # Replace the original changelog with the new one - mv "$TMP_CHANGELOG" "$CHANGELOG" - - echo "Updated CHANGELOG.md with new version: $VERSION" - fi - - echo "updated=true" >> $GITHUB_OUTPUT - - elif [[ "$ACTION" == "add-line" ]]; then - # Add a new line to a section in the Unreleased area - SECTION="${{ inputs.section }}" - DESCRIPTION="${{ inputs.description }}" - ISSUE_NUMBER="${{ inputs.issue-number }}" - - if [[ -z "$SECTION" ]]; then - echo "Error: section is required for add-line action" - exit 1 - fi - - if [[ -z "$DESCRIPTION" ]]; then - echo "Error: description is required for add-line action" - exit 1 - fi - - echo "Adding new line to section: $SECTION" - - # Create the line content - if [[ -n "$ISSUE_NUMBER" ]]; then - LINE_CONTENT="- (automatically added) $DESCRIPTION ([#$ISSUE_NUMBER](https://github.com/architects-toolkit/SmartHopper/issues/$ISSUE_NUMBER))" - else - LINE_CONTENT="- (automatically added) $DESCRIPTION" - fi - - # Check if the Unreleased section exists - if ! grep -q "^## \[Unreleased\]" "$CHANGELOG"; then - echo "Error: Unreleased section not found in CHANGELOG.md" - exit 1 - fi - - # Check if the section exists in the Unreleased area - SECTION_EXISTS=$(awk '/^## \[Unreleased\]/,/^## \[[0-9]+\.[0-9]+\.[0-9]+/ {if ($0 ~ /^### '"$SECTION"'/) print "yes"}' "$CHANGELOG") - - if [[ "$SECTION_EXISTS" == "yes" ]]; then - # Section exists, add the line to it - sed -i "/^### $SECTION/a $LINE_CONTENT" "$CHANGELOG" - else - # Section doesn't exist, create it and add the line - sed -i "/^## \[Unreleased\]/a \n### $SECTION\n$LINE_CONTENT" "$CHANGELOG" - fi - - echo "Added new line to CHANGELOG.md in section: $SECTION" - echo "updated=true" >> $GITHUB_OUTPUT - - else - echo "Error: Unknown action '$ACTION'" - exit 1 - fi diff --git a/.github/actions/version-tools/version-calculator/action.yml b/.github/actions/version-tools/version-calculator/action.yml deleted file mode 100644 index b64e9cdf..00000000 --- a/.github/actions/version-tools/version-calculator/action.yml +++ /dev/null @@ -1,229 +0,0 @@ -name: 'Version Calculator' -description: 'Calculate new version numbers based on semantic versioning rules' -inputs: - version: - description: 'Input version in format X.X.X or X.X.X-suffix.YYMMDD' - required: true - increment: - description: 'Type of increment to perform on version numbers' - required: true - type: choice - options: - - none # No increment, only apply other changes - - patch # Increment patch version - - minor # Increment minor version - - major # Increment major version - - date # Explicitly update the date part of a pre-release version - - auto-date # Auto-detect if this is a dated version and update it if needed - change-pre-release: - description: 'Change or add pre-release suffix' - required: false - type: choice - options: - - none # No change to pre-release suffix - - dev # Change to development pre-release - - alpha # Change to alpha pre-release - - beta # Change to beta pre-release - - rc # Change to release candidate - - remove # Remove pre-release suffix entirely - default: 'none' - pre-release-date: - description: 'Date to use for pre-release in format YYMMDD (defaults to today)' - required: false - -outputs: - new-version: - description: 'Calculated new version' - value: ${{ steps.calculate.outputs.new-version }} - is-prerelease: - description: 'Whether the calculated version is a pre-release' - value: ${{ steps.calculate.outputs.is-prerelease }} - was-date-updated: - description: 'Whether the date was updated in a development version' - value: ${{ steps.calculate.outputs.was-date-updated }} - major: - description: 'Major version component of new version' - value: ${{ steps.calculate.outputs.major }} - minor: - description: 'Minor version component of new version' - value: ${{ steps.calculate.outputs.minor }} - patch: - description: 'Patch version component of new version' - value: ${{ steps.calculate.outputs.patch }} - suffix: - description: 'Suffix of new version (if any)' - value: ${{ steps.calculate.outputs.suffix }} - -runs: - using: "composite" - steps: - - name: Calculate new version - id: calculate - shell: bash - run: | - # Parse input version - INPUT_VERSION="${{ inputs.version }}" - INCREMENT="${{ inputs.increment }}" - CHANGE_PRE_RELEASE="${{ inputs.change-pre-release }}" - - echo "Input version: $INPUT_VERSION" - echo "Increment type: $INCREMENT" - echo "Pre-release change: $CHANGE_PRE_RELEASE" - - # Get today's date in YYMMDD format for version updates - TODAY=$(date +%y%m%d) - echo "Today's date (YYMMDD): $TODAY" - - # Parse version components using regex - if [[ $INPUT_VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-[a-zA-Z0-9]+(\.[0-9]+)?)?$ ]]; then - MAJOR="${BASH_REMATCH[1]}" - MINOR="${BASH_REMATCH[2]}" - PATCH="${BASH_REMATCH[3]}" - SUFFIX="${BASH_REMATCH[4]}" - - echo "Current version components:" - echo " Major: $MAJOR" - echo " Minor: $MINOR" - echo " Patch: $PATCH" - echo " Suffix: $SUFFIX" - - # Initialize variables for tracking changes - WAS_DATE_UPDATED="false" - IS_PRERELEASE="false" - if [[ -n "$SUFFIX" ]]; then - IS_PRERELEASE="true" - fi - - # Step 1: Handle auto-date increment - detect if this is a dated version - if [[ "$INCREMENT" == "auto-date" ]]; then - # Check if this is a version with a date component - if [[ $SUFFIX =~ ^-([a-zA-Z]+)\.([0-9]{6})$ ]]; then - PRE_RELEASE_TYPE="${BASH_REMATCH[1]}" - DATE_PART="${BASH_REMATCH[2]}" - - echo "Detected dated pre-release: $PRE_RELEASE_TYPE.$DATE_PART" - - # Check if date needs updating - if [[ "$DATE_PART" != "$TODAY" ]]; then - echo "Date component needs updating from $DATE_PART to $TODAY" - # Update the suffix with the new date - SUFFIX="-$PRE_RELEASE_TYPE.$TODAY" - WAS_DATE_UPDATED="true" - else - echo "Date component is already current, no update needed" - fi - else - echo "Not a dated pre-release version, no date update needed" - fi - fi - - # Step 2: Apply version number increments - case "$INCREMENT" in - "none") - # No increment to version numbers - ;; - - "patch") - # Increment patch version - PATCH=$((PATCH + 1)) - ;; - - "minor") - # Increment minor, reset patch - MINOR=$((MINOR + 1)) - PATCH=0 - ;; - - "major") - # Increment major, reset minor and patch - MAJOR=$((MAJOR + 1)) - MINOR=0 - PATCH=0 - ;; - - "date") - # Explicitly update date part in pre-release suffix - if [[ $SUFFIX =~ ^-([a-zA-Z]+)(\.[0-9]+)?$ ]]; then - # Extract the pre-release type (dev, alpha, beta, rc) - PRE_RELEASE_TYPE="${BASH_REMATCH[1]}" - - # Calculate new date (today or from input) - if [[ -n "${{ inputs.pre-release-date }}" ]]; then - NEW_DATE="${{ inputs.pre-release-date }}" - else - NEW_DATE="$TODAY" - fi - - # Update the suffix with the new date - SUFFIX="-$PRE_RELEASE_TYPE.$NEW_DATE" - WAS_DATE_UPDATED="true" - else - echo "Error: Input version doesn't contain a valid pre-release suffix for date update." - echo "Current suffix: $SUFFIX" - echo "Expected format: -type or -type.date" - - # If no valid suffix, but one is specified, try to add the date - if [[ -n "$SUFFIX" ]]; then - echo "Attempting to add date to existing suffix..." - # Remove leading dash if present for the comparison - SUFFIX_CLEAN="${SUFFIX#-}" - SUFFIX="-$SUFFIX_CLEAN.$TODAY" - WAS_DATE_UPDATED="true" - echo "Created new suffix: $SUFFIX" - else - echo "No suffix found, cannot update date." - exit 1 - fi - fi - ;; - esac - - # Step 3: Apply pre-release changes - if [[ "$CHANGE_PRE_RELEASE" != "none" ]]; then - if [[ "$CHANGE_PRE_RELEASE" == "remove" ]]; then - # Remove pre-release suffix entirely - SUFFIX="" - IS_PRERELEASE="false" - echo "Removed pre-release suffix" - else - # Add or change pre-release suffix - # Calculate date (today or from input) - if [[ -n "${{ inputs.pre-release-date }}" ]]; then - NEW_DATE="${{ inputs.pre-release-date }}" - else - NEW_DATE="$TODAY" - fi - - SUFFIX="-$CHANGE_PRE_RELEASE.$NEW_DATE" - IS_PRERELEASE="true" - WAS_DATE_UPDATED="true" - echo "Changed pre-release suffix to: $SUFFIX" - fi - fi - - # Build the new version string - NEW_VERSION="$MAJOR.$MINOR.$PATCH$SUFFIX" - - # Parse new version components for output - if [[ $NEW_VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-[a-zA-Z0-9]+(\.[0-9]+)?)?$ ]]; then - NEW_MAJOR="${BASH_REMATCH[1]}" - NEW_MINOR="${BASH_REMATCH[2]}" - NEW_PATCH="${BASH_REMATCH[3]}" - NEW_SUFFIX="${BASH_REMATCH[4]}" - else - echo "Error: Generated version doesn't match expected format: $NEW_VERSION" - exit 1 - fi - - echo "Calculated new version: $NEW_VERSION" - echo "new-version=$NEW_VERSION" >> $GITHUB_OUTPUT - echo "is-prerelease=$IS_PRERELEASE" >> $GITHUB_OUTPUT - echo "was-date-updated=$WAS_DATE_UPDATED" >> $GITHUB_OUTPUT - echo "major=$NEW_MAJOR" >> $GITHUB_OUTPUT - echo "minor=$NEW_MINOR" >> $GITHUB_OUTPUT - echo "patch=$NEW_PATCH" >> $GITHUB_OUTPUT - echo "suffix=$NEW_SUFFIX" >> $GITHUB_OUTPUT - else - echo "Error: Input version format doesn't follow semantic versioning: $INPUT_VERSION" - exit 1 - fi diff --git a/.github/workflows/github-pr-block-dev-to-main.yml b/.github/workflows/github-pr-block-dev-to-main.yml deleted file mode 100644 index ed1210fc..00000000 --- a/.github/workflows/github-pr-block-dev-to-main.yml +++ /dev/null @@ -1,48 +0,0 @@ - -name: 📦 Block Dev Release to Main - -# Description: This workflow blocks development release versions from being merged into the main branch. -# It checks for the presence of "-dev" in the version number and prevents the merge if found. -# -# Trigger: Automatically when a pull request is opened or updated targeting the main branch -# -# Permissions: -# - contents:read - Required to read repository content -# - pull-requests:read - Required to read pull request information - -on: - pull_request: - branches: [ main ] - paths: - - 'Solution.props' - -permissions: - contents: read - pull-requests: read - -jobs: - check-dev-release: - name: 🚫 Block Dev Release - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - - - name: Get version from Solution.props - id: get-version - uses: ./.github/actions/versioning/get-version - - - name: Check for Dev Release - id: check-dev - run: | - VERSION="${{ steps.get-version.outputs.version }}" - echo "Version found: $VERSION" - - if [[ $VERSION == *"-dev"* ]]; then - echo "::error::Development release versions (-dev) cannot be merged into main branch" - echo "IS_DEV_RELEASE=true" >> $GITHUB_ENV - exit 1 - else - echo "IS_DEV_RELEASE=false" >> $GITHUB_ENV - fi diff --git a/.github/workflows/github-pr-update-changelog-issues.yml b/.github/workflows/github-pr-update-changelog-issues.yml deleted file mode 100644 index b874e552..00000000 --- a/.github/workflows/github-pr-update-changelog-issues.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: 📝 Update Changelog with Closed Issues - -# Description: This workflow updates the CHANGELOG.md with closed-solved issues that are not already mentioned. -# It identifies issues marked as solved in the last month and adds them to the Unreleased/Fixed section. -# -# Triggers: -# - Automatically when a pull request is created or updated targeting main or dev branches -# - Manually via workflow_dispatch on any branch -# -# Permissions: -# - contents:write - Required to commit changes to the repository -# - pull-requests:write - Required to update pull requests -# - issues:read - Required to read issue information - -on: - pull_request: - branches: - - main - - dev - workflow_dispatch: - -permissions: - contents: write - pull-requests: write - issues: read - -jobs: - update-changelog: - name: 📝 Update Changelog with Closed Issues - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.ref }} - - - name: Update Changelog with Closed Issues - id: update-changelog-issues - uses: ./.github/actions/documentation/update-changelog-issues - with: - token: ${{ secrets.GITHUB_TOKEN }} - days-lookback: 30 - - - name: Commit changes if needed - if: steps.update-changelog-issues.outputs.updated == 'true' && github.event_name == 'pull_request' - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git add CHANGELOG.md - git commit -m "docs: update changelog with ${{ steps.update-changelog-issues.outputs.issues-added }} closed issues" - git push origin HEAD:${{ github.event.pull_request.head.ref }} - - echo "Added ${{ steps.update-changelog-issues.outputs.issues-added }} closed issues to the changelog" - - - name: Commit changes if workflow_dispatch - if: steps.update-changelog-issues.outputs.updated == 'true' && github.event_name == 'workflow_dispatch' - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git add CHANGELOG.md - git commit -m "docs: update changelog with ${{ steps.update-changelog-issues.outputs.issues-added }} closed issues" - git push origin HEAD:${{ github.ref_name }} - - echo "Added ${{ steps.update-changelog-issues.outputs.issues-added }} closed issues to the changelog" diff --git a/.github/workflows/github-pr-validation.yml b/.github/workflows/github-pr-validation.yml deleted file mode 100644 index 6ffac439..00000000 --- a/.github/workflows/github-pr-validation.yml +++ /dev/null @@ -1,118 +0,0 @@ -name: 📦 Pull Request Validation - -# Description: This workflow validates pull requests to ensure they meet project standards. -# It checks for version conflicts, validates formatting, and performs other quality checks -# to maintain code integrity before merging. -# -# Triggers: -# - Automatically when a pull request is created or updated targeting main or dev branches -# -# Permissions: -# - contents:read - Required to read repository content -# - pull-requests:read - Required to read pull request information - -on: - pull_request: - branches: - - main - - dev - -permissions: - contents: read - pull-requests: read - -jobs: - version-check: - name: 📦 Version Check - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - fetch-depth: 0 - - - name: Validate Version in Solution.props - id: version-check - uses: ./.github/actions/versioning/get-version - - - name: Verify Version Format - run: | - VERSION="${{ steps.version-check.outputs.version }}" - echo "Checking version: $VERSION" - if [[ -z "$VERSION" ]]; then - echo "Error: Failed to extract version from Solution.props" - exit 1 - fi - - echo "Version components:" - echo "- Major: ${{ steps.version-check.outputs.major }}" - echo "- Minor: ${{ steps.version-check.outputs.minor }}" - echo "- Patch: ${{ steps.version-check.outputs.patch }}" - echo "- Suffix: ${{ steps.version-check.outputs.suffix }}" - - changelog-check: - name: 📝 Changelog Check - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: read - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: Check CHANGELOG.md is Updated - uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32 # v46.0.1 - id: changelog-check - with: - files: CHANGELOG.md - - - name: Validate Changelog Update - if: steps.changelog-check.outputs.any_changed != 'true' - env: - CHANGED_FILES: ${{ steps.changelog-check.outputs.all_changed_files }} - run: | - echo "Error: CHANGELOG.md must be updated with pull request changes" - exit 1 - - title-check: - name: 🏷️ PR Title Style Check - runs-on: ubuntu-latest - permissions: - pull-requests: read - steps: - - name: Validate PR Title Style - run: | - PR_TITLE="${{ github.event.pull_request.title }}" - echo "Checking PR title: $PR_TITLE" - - # Valid types based on conventional commits - VALID_TYPES="feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert|security" - - # Check if PR title follows conventional commits format - if [[ ! $PR_TITLE =~ ^($VALID_TYPES)(\([a-z0-9-]+\))?:\ .+ ]]; then - echo "Error: Pull request title must follow conventional commits format" - echo "" - echo "Required format: (): " - echo "" - echo "Where is one of:" - echo " feat: A new feature" - echo " fix: A bug fix" - echo " docs: Documentation changes" - echo " style: Code style changes (formatting, etc)" - echo " refactor: Code refactoring" - echo " perf: Performance improvements" - echo " test: Adding or modifying tests" - echo " build: Changes to build process or tools" - echo " ci: Changes to CI configuration" - echo " chore: Other changes that don't modify src or test files" - echo " revert: Revert a previous commit" - echo " security: Security-related changes" - echo "" - echo "Examples:" - echo " feat: add new grasshopper component" - echo " fix(component): resolve issue with parameter handling" - echo " docs(readme): update installation instructions" - echo " security: update dependencies to address vulnerabilities" - exit 1 - else - echo "PR title format is valid! ✅" - fi From b2a98ec7b203b2dee8e618e25150c951c33f01b2 Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 09:20:52 +0200 Subject: [PATCH 10/29] ci(workflow): add mode selector to user-code-style.yml --- .github/workflows/user-code-style.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/user-code-style.yml b/.github/workflows/user-code-style.yml index 912c64f9..554b20e7 100644 --- a/.github/workflows/user-code-style.yml +++ b/.github/workflows/user-code-style.yml @@ -1,8 +1,15 @@ name: 👤🖌️ Fix Code Style +# Description: Runs code style fixes or checks, and namespace checks for C# code. permissions: contents: write on: workflow_dispatch: + inputs: + mode: + description: 'Mode for code style: fix or check' + required: true + default: 'fix' + type: string jobs: code-style: @@ -12,7 +19,7 @@ jobs: - name: Run Code Style uses: ./.github/actions/code-style with: - mode: "fix" + mode: "${{ github.event.inputs.mode }}" namespace-check: name: 📂 Namespaces Check runs-on: ubuntu-latest From 428f8afb196402775daa7fd63299a50977e169c7 Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 09:21:28 +0200 Subject: [PATCH 11/29] ci(workflow): change default to check in user-code-style --- .github/workflows/user-code-style.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/user-code-style.yml b/.github/workflows/user-code-style.yml index 554b20e7..756ebb28 100644 --- a/.github/workflows/user-code-style.yml +++ b/.github/workflows/user-code-style.yml @@ -8,7 +8,7 @@ on: mode: description: 'Mode for code style: fix or check' required: true - default: 'fix' + default: 'check' type: string jobs: From a874e99a798011acc4103e23c5d93cce94f6fbc7 Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 09:23:45 +0200 Subject: [PATCH 12/29] ci(workflow): fix issue in user-code-style --- .github/workflows/pr-validation.yml | 4 ++-- .github/workflows/user-code-style.yml | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index f61df658..fd7c1f7d 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -54,7 +54,7 @@ jobs: name: 🛡️ Header Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 - name: Validate File Headers @@ -66,7 +66,7 @@ jobs: name: 📂 Namespace Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 - name: Validate Namespaces diff --git a/.github/workflows/user-code-style.yml b/.github/workflows/user-code-style.yml index 756ebb28..f2716ee2 100644 --- a/.github/workflows/user-code-style.yml +++ b/.github/workflows/user-code-style.yml @@ -16,6 +16,8 @@ jobs: name: 🛠️ Fix Code Style runs-on: ubuntu-latest steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Run Code Style uses: ./.github/actions/code-style with: @@ -24,6 +26,8 @@ jobs: name: 📂 Namespaces Check runs-on: ubuntu-latest steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Run Namespace Fixer uses: ./.github/actions/code-style/namespace-fixer with: From cf6b18758e2881a58aea09cafa96d0482066507a Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 09:30:25 +0200 Subject: [PATCH 13/29] ci(workflow): missing github-token for code-style --- .github/actions/code-style/action.yml | 5 ++++- .../code-style/header-fixer/action.yml | 22 +++++++++---------- .github/workflows/pr-validation.yml | 22 +++++-------------- .github/workflows/user-code-style.yml | 1 + 4 files changed, 21 insertions(+), 29 deletions(-) diff --git a/.github/actions/code-style/action.yml b/.github/actions/code-style/action.yml index 53af3b25..86edcb48 100644 --- a/.github/actions/code-style/action.yml +++ b/.github/actions/code-style/action.yml @@ -5,6 +5,9 @@ inputs: description: "Operation mode: fix or check" required: false default: "check" + token: + description: "GitHub token for authentication" + required: true runs: using: "composite" steps: @@ -15,7 +18,7 @@ runs: - name: Run dotnet-format uses: xt0rted/dotnet-format@v1 with: - repo-token: ${{ secrets.GITHUB_TOKEN }} + repo-token: ${{ inputs.token }} action: ${{ inputs.mode }} only-changed-files: true - name: Process C# file headers diff --git a/.github/actions/code-style/header-fixer/action.yml b/.github/actions/code-style/header-fixer/action.yml index ada52981..9c935aaa 100644 --- a/.github/actions/code-style/header-fixer/action.yml +++ b/.github/actions/code-style/header-fixer/action.yml @@ -23,17 +23,17 @@ runs: year=$(grep -m1 '^ \* Copyright' "$file" | grep -oE '[0-9]{4}') [ -z "$year" ] && year=$(date +%Y) EXPECTED_HEADER=$(cat < Date: Sun, 20 Apr 2025 09:32:58 +0200 Subject: [PATCH 14/29] fix(workflow): code-style action --- .github/actions/code-style/action.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/actions/code-style/action.yml b/.github/actions/code-style/action.yml index 86edcb48..70ed3ed8 100644 --- a/.github/actions/code-style/action.yml +++ b/.github/actions/code-style/action.yml @@ -16,11 +16,15 @@ runs: with: fetch-depth: 0 - name: Run dotnet-format - uses: xt0rted/dotnet-format@v1 - with: - repo-token: ${{ inputs.token }} - action: ${{ inputs.mode }} - only-changed-files: true + shell: bash + run: | + dotnet tool install -g dotnet-format || true + export PATH="$HOME/.dotnet/tools:$PATH" + if [ "${{ inputs.mode }}" = "check" ]; then + dotnet format --verify-no-changes + else + dotnet format + fi - name: Process C# file headers uses: ./.github/actions/code-style/header-fixer with: From f14e718824f2ab3e82020e3721ec95fe86f404a7 Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 09:36:56 +0200 Subject: [PATCH 15/29] ci(workflow): user-code-style minor fixes --- .github/actions/code-style/action.yml | 5 +++++ .github/actions/code-style/namespace-fixer/action.yml | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/actions/code-style/action.yml b/.github/actions/code-style/action.yml index 70ed3ed8..fe582f6f 100644 --- a/.github/actions/code-style/action.yml +++ b/.github/actions/code-style/action.yml @@ -15,9 +15,14 @@ runs: uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '7.0.x' - name: Run dotnet-format shell: bash run: | + dotnet restore dotnet tool install -g dotnet-format || true export PATH="$HOME/.dotnet/tools:$PATH" if [ "${{ inputs.mode }}" = "check" ]; then diff --git a/.github/actions/code-style/namespace-fixer/action.yml b/.github/actions/code-style/namespace-fixer/action.yml index 37fc4388..3fe0b4b0 100644 --- a/.github/actions/code-style/namespace-fixer/action.yml +++ b/.github/actions/code-style/namespace-fixer/action.yml @@ -16,7 +16,12 @@ runs: if: always() shell: bash run: | - FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} -- '*.cs') + if [ -n "${{ github.event.pull_request }}" ]; then + FILES=$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" -- '*.cs') + else + # Fallback to all .cs files when not a pull_request event + FILES=$(find . -type f -name '*.cs') + fi ERR=0 for file in $FILES; do if [ -f "$file" ]; then From 0be81c10243a0afe3a4b30d81a98b16183303d18 Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 09:40:04 +0200 Subject: [PATCH 16/29] ci: fix nesting test project in solution --- SmartHopper.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SmartHopper.sln b/SmartHopper.sln index cbd82995..9d248f01 100644 --- a/SmartHopper.sln +++ b/SmartHopper.sln @@ -67,7 +67,7 @@ Global {60732624-037C-4D6D-BC4C-588A0C25C338} = {05A44F09-190D-434E-A91B-0ECA631110FE} {A932CFFA-0C82-4A1F-92F2-003CDE1C94AC} = {05A44F09-190D-434E-A91B-0ECA631110FE} {B932CFFA-0C82-4A1F-92F2-003CDE1C94AD} = {05A44F09-190D-434E-A91B-0ECA631110FE} - {B932CFFA-0C82-4A1F-92F2-003CDE1C94AE} = {00000000-0000-0000-0000-000000000000} + {B932CFFA-0C82-4A1F-92F2-003CDE1C94AE} = {05A44F09-190D-434E-A91B-0ECA631110FE} {F2063A0F-FDF2-4AD2-8C92-1E374B991752} = {05A44F09-190D-434E-A91B-0ECA631110FE} {087FFA5E-1049-459D-9C68-1C0B8E7F9EBC} = {05A44F09-190D-434E-A91B-0ECA631110FE} EndGlobalSection From bbfc792b4ba697beaf6e1839a7cbcba02b309919 Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 09:42:03 +0200 Subject: [PATCH 17/29] ci(workflow): fix namespace check --- .github/actions/code-style/namespace-fixer/action.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/actions/code-style/namespace-fixer/action.yml b/.github/actions/code-style/namespace-fixer/action.yml index 3fe0b4b0..37d373f5 100644 --- a/.github/actions/code-style/namespace-fixer/action.yml +++ b/.github/actions/code-style/namespace-fixer/action.yml @@ -25,9 +25,11 @@ runs: ERR=0 for file in $FILES; do if [ -f "$file" ]; then - rel=${file#src/} - ns=SmartHopper.${rel%.*} - ns=${ns//\//.} + # derive namespace from file path without file name + fp=${file#src/} + fp_no_ext=${fp%.*} + dir=${fp_no_ext%/*} + ns=${dir//\//.} actual=$(grep -m1 '^namespace ' "$file" | cut -d ' ' -f2) if [ "$actual" != "$ns" ]; then if [ "${{ inputs.mode }}" = "fix" ]; then From c92979727fd7db93051c69e44b8567c56f6dbf8e Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 09:43:39 +0200 Subject: [PATCH 18/29] ci(workflow): fix namespace check --- .github/actions/code-style/namespace-fixer/action.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/actions/code-style/namespace-fixer/action.yml b/.github/actions/code-style/namespace-fixer/action.yml index 37d373f5..9dfae059 100644 --- a/.github/actions/code-style/namespace-fixer/action.yml +++ b/.github/actions/code-style/namespace-fixer/action.yml @@ -25,8 +25,9 @@ runs: ERR=0 for file in $FILES; do if [ -f "$file" ]; then - # derive namespace from file path without file name - fp=${file#src/} + # normalize path: strip leading './' and 'src/' + fp=${file#./} + fp=${fp#src/} fp_no_ext=${fp%.*} dir=${fp_no_ext%/*} ns=${dir//\//.} From 6ddb2107a57366f35bbb0fa95d97c327a6f331de Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 09:48:09 +0200 Subject: [PATCH 19/29] ci(workflow): fix indention in header-fixer --- .../code-style/header-fixer/action.yml | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/actions/code-style/header-fixer/action.yml b/.github/actions/code-style/header-fixer/action.yml index 9c935aaa..aaa8bdd0 100644 --- a/.github/actions/code-style/header-fixer/action.yml +++ b/.github/actions/code-style/header-fixer/action.yml @@ -23,16 +23,16 @@ runs: year=$(grep -m1 '^ \* Copyright' "$file" | grep -oE '[0-9]{4}') [ -z "$year" ] && year=$(date +%Y) EXPECTED_HEADER=$(cat < Date: Sun, 20 Apr 2025 10:08:49 +0200 Subject: [PATCH 20/29] ci: minor fixes --- .../code-style/header-fixer/action.yml | 42 +++++++++++-------- .github/workflows/user-update-branch.yml | 2 +- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/.github/actions/code-style/header-fixer/action.yml b/.github/actions/code-style/header-fixer/action.yml index aaa8bdd0..3ebe3f47 100644 --- a/.github/actions/code-style/header-fixer/action.yml +++ b/.github/actions/code-style/header-fixer/action.yml @@ -16,30 +16,38 @@ runs: if: always() shell: bash run: | - FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} -- '*.cs') + if [ -n "${{ github.event.pull_request }}" ]; then + FILES=$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" -- '*.cs') + else + # Fallback to all .cs files when not a pull_request event + FILES=$(find . -type f -name '*.cs') + fi ERR=0 for file in $FILES; do if [ -f "$file" ]; then year=$(grep -m1 '^ \* Copyright' "$file" | grep -oE '[0-9]{4}') [ -z "$year" ] && year=$(date +%Y) - EXPECTED_HEADER=$(cat < tmp && mv tmp "$file" - printf "%s\n\n%s" "$EXPECTED_HEADER" "$(cat \"$file\")" > "$file" + # Find namespace line + NAMESPACE_LINE=$(grep -n '^namespace ' "$file" | head -1 | cut -d: -f1) + if [ -n "$NAMESPACE_LINE" ]; then + # Extract content from namespace line to end + CONTENT=$(tail -n +$NAMESPACE_LINE "$file") + # Write new file with correct header + echo -e "$HEADER\n\n$CONTENT" > "$file" + else + echo "Warning: Could not find namespace line in $file" + fi else echo "Header mismatch in $file" ERR=1 diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index 62e19c37..22b90b5e 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -114,7 +114,7 @@ jobs: id: create-temp-branch run: | # Create a unique branch name - TEMP_BRANCH="update-branch/${{ github.event.inputs.target_branch }}-from-${{ github.event.inputs.origin_branch }}-$(date +%s)" + TEMP_BRANCH="update-branch/${{ github.event.inputs.target_branch }}-from-${{ github.event.inputs.origin_branch }}" echo "temp_branch=$TEMP_BRANCH" >> $GITHUB_OUTPUT # Checkout target branch From c3df8e1296b7a5a17b411216eef73573d68a8e52 Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 10:12:46 +0200 Subject: [PATCH 21/29] ci(workflow): reapply filter to user-update-branc --- .github/workflows/user-update-branch.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index 22b90b5e..e701f918 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -24,6 +24,10 @@ on: description: 'Target branch (to be updated)' required: true type: string + filter: + description: 'Specific folder to update (e.g., .github or src/SmartHopper.Components). Leave blank to update all code.' + required: false + type: string permissions: contents: write @@ -129,6 +133,23 @@ jobs: - name: Attempt merge id: attempt-merge run: | + ORIGIN=${{ github.event.inputs.origin_branch }} + TARGET=${{ github.event.inputs.target_branch }} + FILTER=${{ github.event.inputs.filter }} + if [[ -n "$FILTER" ]]; then + echo "Updating specific path: $FILTER" + git checkout origin/$ORIGIN -- "$FILTER" + if git diff --quiet; then + echo "merge_status=no_changes" >> $GITHUB_OUTPUT + echo "No changes to merge for path $FILTER." + else + git add "$FILTER" + git commit -m "chore(branch): update $TARGET from $ORIGIN for path $FILTER" + echo "merge_status=success" >> $GITHUB_OUTPUT + fi + exit 0 + fi + # Try to merge origin into temp branch if git merge origin/${{ github.event.inputs.origin_branch }} --no-ff; then echo "merge_status=success" >> $GITHUB_OUTPUT From 0b26fd5fcbf859dc5a8f34fe76350dd4d70641ac Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 10:15:52 +0200 Subject: [PATCH 22/29] ci(workflow): minor fixes in update-branch --- .github/workflows/user-update-branch.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index e701f918..91841129 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -139,13 +139,15 @@ jobs: if [[ -n "$FILTER" ]]; then echo "Updating specific path: $FILTER" git checkout origin/$ORIGIN -- "$FILTER" - if git diff --quiet; then - echo "merge_status=no_changes" >> $GITHUB_OUTPUT - echo "No changes to merge for path $FILTER." - else + # Check if there are any changes to stage + if git status --porcelain | grep -q "$FILTER"; then git add "$FILTER" git commit -m "chore(branch): update $TARGET from $ORIGIN for path $FILTER" echo "merge_status=success" >> $GITHUB_OUTPUT + echo "Changes detected and committed for path $FILTER." + else + echo "merge_status=no_changes" >> $GITHUB_OUTPUT + echo "No changes to merge for path $FILTER." fi exit 0 fi From 754cf61fa0be2efe9f0136164a60b4e79bc8272c Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 20 Apr 2025 08:16:36 +0000 Subject: [PATCH 23/29] chore(branch): update main from marc-romu/workflows for path .github --- .github/actions/code-style/action.yml | 22 +++++++--- .../code-style/header-fixer/action.yml | 42 +++++++++++-------- .../code-style/namespace-fixer/action.yml | 16 +++++-- .github/workflows/pr-validation.yml | 24 +++-------- .github/workflows/user-code-style.yml | 14 ++++++- .github/workflows/user-update-branch.yml | 25 ++++++++++- 6 files changed, 97 insertions(+), 46 deletions(-) diff --git a/.github/actions/code-style/action.yml b/.github/actions/code-style/action.yml index 53af3b25..fe582f6f 100644 --- a/.github/actions/code-style/action.yml +++ b/.github/actions/code-style/action.yml @@ -5,6 +5,9 @@ inputs: description: "Operation mode: fix or check" required: false default: "check" + token: + description: "GitHub token for authentication" + required: true runs: using: "composite" steps: @@ -12,12 +15,21 @@ runs: uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Run dotnet-format - uses: xt0rted/dotnet-format@v1 + - name: Setup .NET + uses: actions/setup-dotnet@v3 with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - action: ${{ inputs.mode }} - only-changed-files: true + dotnet-version: '7.0.x' + - name: Run dotnet-format + shell: bash + run: | + dotnet restore + dotnet tool install -g dotnet-format || true + export PATH="$HOME/.dotnet/tools:$PATH" + if [ "${{ inputs.mode }}" = "check" ]; then + dotnet format --verify-no-changes + else + dotnet format + fi - name: Process C# file headers uses: ./.github/actions/code-style/header-fixer with: diff --git a/.github/actions/code-style/header-fixer/action.yml b/.github/actions/code-style/header-fixer/action.yml index ada52981..3ebe3f47 100644 --- a/.github/actions/code-style/header-fixer/action.yml +++ b/.github/actions/code-style/header-fixer/action.yml @@ -16,30 +16,38 @@ runs: if: always() shell: bash run: | - FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} -- '*.cs') + if [ -n "${{ github.event.pull_request }}" ]; then + FILES=$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" -- '*.cs') + else + # Fallback to all .cs files when not a pull_request event + FILES=$(find . -type f -name '*.cs') + fi ERR=0 for file in $FILES; do if [ -f "$file" ]; then year=$(grep -m1 '^ \* Copyright' "$file" | grep -oE '[0-9]{4}') [ -z "$year" ] && year=$(date +%Y) - EXPECTED_HEADER=$(cat < tmp && mv tmp "$file" - printf "%s\n\n%s" "$EXPECTED_HEADER" "$(cat \"$file\")" > "$file" + # Find namespace line + NAMESPACE_LINE=$(grep -n '^namespace ' "$file" | head -1 | cut -d: -f1) + if [ -n "$NAMESPACE_LINE" ]; then + # Extract content from namespace line to end + CONTENT=$(tail -n +$NAMESPACE_LINE "$file") + # Write new file with correct header + echo -e "$HEADER\n\n$CONTENT" > "$file" + else + echo "Warning: Could not find namespace line in $file" + fi else echo "Header mismatch in $file" ERR=1 diff --git a/.github/actions/code-style/namespace-fixer/action.yml b/.github/actions/code-style/namespace-fixer/action.yml index 37fc4388..9dfae059 100644 --- a/.github/actions/code-style/namespace-fixer/action.yml +++ b/.github/actions/code-style/namespace-fixer/action.yml @@ -16,13 +16,21 @@ runs: if: always() shell: bash run: | - FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} -- '*.cs') + if [ -n "${{ github.event.pull_request }}" ]; then + FILES=$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}" -- '*.cs') + else + # Fallback to all .cs files when not a pull_request event + FILES=$(find . -type f -name '*.cs') + fi ERR=0 for file in $FILES; do if [ -f "$file" ]; then - rel=${file#src/} - ns=SmartHopper.${rel%.*} - ns=${ns//\//.} + # normalize path: strip leading './' and 'src/' + fp=${file#./} + fp=${fp#src/} + fp_no_ext=${fp%.*} + dir=${fp_no_ext%/*} + ns=${dir//\//.} actual=$(grep -m1 '^namespace ' "$file" | cut -d ' ' -f2) if [ "$actual" != "$ns" ]; then if [ "${{ inputs.mode }}" = "fix" ]; then diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index f61df658..8cf8d70e 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -50,29 +50,17 @@ jobs: echo "- Patch: ${{ steps.version-check.outputs.patch }}" echo "- Suffix: ${{ steps.version-check.outputs.suffix }}" - header-check: - name: 🛡️ Header Check + code-style-check: + name: 🖌️ Code Style Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - with: - fetch-depth: 0 - - name: Validate File Headers - uses: ./.github/actions/code-style/header-fixer - with: - mode: check + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - namespace-check: - name: 📂 Namespace Check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - with: - fetch-depth: 0 - - name: Validate Namespaces - uses: ./.github/actions/code-style/namespace-fixer + - name: Validate Code Style + uses: ./.github/actions/code-style with: mode: check + token: ${{ github.token }} changelog-check: name: 📝 Changelog Check diff --git a/.github/workflows/user-code-style.yml b/.github/workflows/user-code-style.yml index 912c64f9..b47f5611 100644 --- a/.github/workflows/user-code-style.yml +++ b/.github/workflows/user-code-style.yml @@ -1,22 +1,34 @@ name: 👤🖌️ Fix Code Style +# Description: Runs code style fixes or checks, and namespace checks for C# code. permissions: contents: write on: workflow_dispatch: + inputs: + mode: + description: 'Mode for code style: fix or check' + required: true + default: 'check' + type: string jobs: code-style: name: 🛠️ Fix Code Style runs-on: ubuntu-latest steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Run Code Style uses: ./.github/actions/code-style with: - mode: "fix" + mode: "${{ github.event.inputs.mode }}" + token: ${{ github.token }} namespace-check: name: 📂 Namespaces Check runs-on: ubuntu-latest steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Run Namespace Fixer uses: ./.github/actions/code-style/namespace-fixer with: diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index 62e19c37..91841129 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -24,6 +24,10 @@ on: description: 'Target branch (to be updated)' required: true type: string + filter: + description: 'Specific folder to update (e.g., .github or src/SmartHopper.Components). Leave blank to update all code.' + required: false + type: string permissions: contents: write @@ -114,7 +118,7 @@ jobs: id: create-temp-branch run: | # Create a unique branch name - TEMP_BRANCH="update-branch/${{ github.event.inputs.target_branch }}-from-${{ github.event.inputs.origin_branch }}-$(date +%s)" + TEMP_BRANCH="update-branch/${{ github.event.inputs.target_branch }}-from-${{ github.event.inputs.origin_branch }}" echo "temp_branch=$TEMP_BRANCH" >> $GITHUB_OUTPUT # Checkout target branch @@ -129,6 +133,25 @@ jobs: - name: Attempt merge id: attempt-merge run: | + ORIGIN=${{ github.event.inputs.origin_branch }} + TARGET=${{ github.event.inputs.target_branch }} + FILTER=${{ github.event.inputs.filter }} + if [[ -n "$FILTER" ]]; then + echo "Updating specific path: $FILTER" + git checkout origin/$ORIGIN -- "$FILTER" + # Check if there are any changes to stage + if git status --porcelain | grep -q "$FILTER"; then + git add "$FILTER" + git commit -m "chore(branch): update $TARGET from $ORIGIN for path $FILTER" + echo "merge_status=success" >> $GITHUB_OUTPUT + echo "Changes detected and committed for path $FILTER." + else + echo "merge_status=no_changes" >> $GITHUB_OUTPUT + echo "No changes to merge for path $FILTER." + fi + exit 0 + fi + # Try to merge origin into temp branch if git merge origin/${{ github.event.inputs.origin_branch }} --no-ff; then echo "merge_status=success" >> $GITHUB_OUTPUT From 2c86288942284c331bf8582f467442e8afbd2658 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 20 Apr 2025 08:26:11 +0000 Subject: [PATCH 24/29] chore(branch): update marc-romu/workflows from marc-romu/chat for path .github --- .github/workflows/user-update-branch.yml | 67 +++++++++++++++++++++--- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index 91841129..51ef0114 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -77,7 +77,8 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const prTitle = `chore(branch): update ${{ github.event.inputs.target_branch }} from ${{ github.event.inputs.origin_branch }}`; + const filterSuffix = '${{ github.event.inputs.filter }}' ? ` for path ${{ github.event.inputs.filter }}` : ''; + const prTitle = `chore(branch): update ${{ github.event.inputs.target_branch }} from ${{ github.event.inputs.origin_branch }}${filterSuffix}`; console.log(`Checking for existing PRs with title: ${prTitle}`); @@ -136,29 +137,83 @@ jobs: ORIGIN=${{ github.event.inputs.origin_branch }} TARGET=${{ github.event.inputs.target_branch }} FILTER=${{ github.event.inputs.filter }} + + # Function to check for file suppressions + check_file_suppressions() { + local origin_branch=$1 + local target_branch=$2 + local filter_path=$3 + local filter_suffix="" + + if [[ -n "$filter_path" ]]; then + filter_suffix=" for path $filter_path" + filter_arg="-- $filter_path" + else + filter_arg="" + fi + + # Get list of files from both branches + git ls-tree -r --name-only origin/$target_branch $filter_arg > target_files.txt + git ls-tree -r --name-only origin/$origin_branch $filter_arg > origin_files.txt + + # Check for file suppressions (files in target but not in origin) + if ! diff --new-line-format="" --unchanged-line-format="" target_files.txt origin_files.txt | grep -q .; then + echo "merge_status=no_changes" >> $GITHUB_OUTPUT + echo "No changes to merge$filter_suffix. Branches are already in sync." + return 1 + else + # Handle file suppressions + echo "Detected file suppressions$filter_suffix" + # Get files that exist in target but not in origin + diff --new-line-format="%L" --unchanged-line-format="" target_files.txt origin_files.txt > suppressed_files.txt + + # Remove these files + if [ -s suppressed_files.txt ]; then + while IFS= read -r file; do + echo "Removing file: $file" + git rm -f "$file" || true + done < suppressed_files.txt + + git commit -m "chore(branch): update $target_branch from $origin_branch$filter_suffix (with file suppressions)" + echo "merge_status=success" >> $GITHUB_OUTPUT + echo "Changes with file suppressions detected and committed$filter_suffix." + return 0 + else + echo "merge_status=no_changes" >> $GITHUB_OUTPUT + echo "No changes to merge$filter_suffix. Branches are already in sync." + return 1 + fi + fi + } + + # Handle filtered path update if [[ -n "$FILTER" ]]; then echo "Updating specific path: $FILTER" git checkout origin/$ORIGIN -- "$FILTER" - # Check if there are any changes to stage + + # Check if there are any changes to stage (modified files) if git status --porcelain | grep -q "$FILTER"; then git add "$FILTER" git commit -m "chore(branch): update $TARGET from $ORIGIN for path $FILTER" echo "merge_status=success" >> $GITHUB_OUTPUT echo "Changes detected and committed for path $FILTER." else - echo "merge_status=no_changes" >> $GITHUB_OUTPUT - echo "No changes to merge for path $FILTER." + # Check for file suppressions + check_file_suppressions "$ORIGIN" "$TARGET" "$FILTER" fi + + # Clean up temporary files + rm -f origin_files.txt target_files.txt suppressed_files.txt || true exit 0 fi # Try to merge origin into temp branch - if git merge origin/${{ github.event.inputs.origin_branch }} --no-ff; then + if git merge origin/$ORIGIN --no-ff; then echo "merge_status=success" >> $GITHUB_OUTPUT echo "Merge successful, no conflicts detected." # Check if there are any changes between branches - if git diff --quiet origin/${{ github.event.inputs.target_branch }} origin/${{ github.event.inputs.origin_branch }}; then + if git diff --quiet origin/$TARGET origin/$ORIGIN; then echo "merge_status=no_changes" >> $GITHUB_OUTPUT echo "No changes to merge. Branches are already in sync." fi From ab56e1a28707688d666c666a2d14143facee665d Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 10:33:38 +0200 Subject: [PATCH 25/29] ci(workflows): fixes in update-branch --- .github/workflows/user-update-branch.yml | 38 +++++++++++++++--------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index 51ef0114..42162d42 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -186,24 +186,33 @@ jobs: fi } - # Handle filtered path update + # Handle filtered path update with modified and deleted files if [[ -n "$FILTER" ]]; then echo "Updating specific path: $FILTER" git checkout origin/$ORIGIN -- "$FILTER" - - # Check if there are any changes to stage (modified files) - if git status --porcelain | grep -q "$FILTER"; then - git add "$FILTER" + # Commit modified files + modified=$(git diff origin/$TARGET origin/$ORIGIN --name-only -- "$FILTER") + if [[ -n "$modified" ]]; then + echo "$modified" | xargs -r git add git commit -m "chore(branch): update $TARGET from $ORIGIN for path $FILTER" echo "merge_status=success" >> $GITHUB_OUTPUT - echo "Changes detected and committed for path $FILTER." - else - # Check for file suppressions - check_file_suppressions "$ORIGIN" "$TARGET" "$FILTER" + echo "Modified files detected and committed for path $FILTER." + exit 0 fi - - # Clean up temporary files - rm -f origin_files.txt target_files.txt suppressed_files.txt || true + # Commit deleted files + deleted=$(git diff origin/$TARGET origin/$ORIGIN --name-only --diff-filter=D -- "$FILTER") + if [[ -n "$deleted" ]]; then + echo "$deleted" | while read file; do + echo "Removing file: $file" + git rm "$file" + done + git commit -m "chore(branch): update $TARGET from $ORIGIN for path $FILTER (deleted files)" + echo "merge_status=success" >> $GITHUB_OUTPUT + echo "Deleted files detected and removed for path $FILTER." + exit 0 + fi + echo "merge_status=no_changes" >> $GITHUB_OUTPUT + echo "No changes to merge for path $FILTER. Branches are already in sync." exit 0 fi @@ -256,11 +265,12 @@ jobs: const { execSync } = require('child_process'); execSync('git push origin ${{ steps.create-temp-branch.outputs.temp_branch }}'); - const prTitle = `chore(branch): update ${{ github.event.inputs.target_branch }} from ${{ github.event.inputs.origin_branch }}`; + const filterSuffix = '${{ github.event.inputs.filter }}' ? ` for path ${{ github.event.inputs.filter }}` : ''; + const prTitle = `chore(branch): update ${{ github.event.inputs.target_branch }} from ${{ github.event.inputs.origin_branch }}${filterSuffix}`; let prBody = `## Branch Update - This PR updates \`${{ github.event.inputs.target_branch }}\` with changes from \`${{ github.event.inputs.origin_branch }}\`.`; + This PR updates \`${{ github.event.inputs.target_branch }}\` with changes from \`${{ github.event.inputs.origin_branch }}\`${filterSuffix ? ` for the path \`${filterSuffix.substring(10)}\`` : ''}.`; if ('${{ steps.attempt-merge.outputs.merge_status }}' === 'conflict') { prBody += ` From ef8742571936339856fb7ba4ac270adbf00d6b67 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 20 Apr 2025 08:34:12 +0000 Subject: [PATCH 26/29] chore(branch): update main from marc-romu/workflows for path .github --- .github/workflows/user-update-branch.yml | 89 ++++++++++++++++++++---- 1 file changed, 77 insertions(+), 12 deletions(-) diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index 91841129..42162d42 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -77,7 +77,8 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - const prTitle = `chore(branch): update ${{ github.event.inputs.target_branch }} from ${{ github.event.inputs.origin_branch }}`; + const filterSuffix = '${{ github.event.inputs.filter }}' ? ` for path ${{ github.event.inputs.filter }}` : ''; + const prTitle = `chore(branch): update ${{ github.event.inputs.target_branch }} from ${{ github.event.inputs.origin_branch }}${filterSuffix}`; console.log(`Checking for existing PRs with title: ${prTitle}`); @@ -136,29 +137,92 @@ jobs: ORIGIN=${{ github.event.inputs.origin_branch }} TARGET=${{ github.event.inputs.target_branch }} FILTER=${{ github.event.inputs.filter }} + + # Function to check for file suppressions + check_file_suppressions() { + local origin_branch=$1 + local target_branch=$2 + local filter_path=$3 + local filter_suffix="" + + if [[ -n "$filter_path" ]]; then + filter_suffix=" for path $filter_path" + filter_arg="-- $filter_path" + else + filter_arg="" + fi + + # Get list of files from both branches + git ls-tree -r --name-only origin/$target_branch $filter_arg > target_files.txt + git ls-tree -r --name-only origin/$origin_branch $filter_arg > origin_files.txt + + # Check for file suppressions (files in target but not in origin) + if ! diff --new-line-format="" --unchanged-line-format="" target_files.txt origin_files.txt | grep -q .; then + echo "merge_status=no_changes" >> $GITHUB_OUTPUT + echo "No changes to merge$filter_suffix. Branches are already in sync." + return 1 + else + # Handle file suppressions + echo "Detected file suppressions$filter_suffix" + # Get files that exist in target but not in origin + diff --new-line-format="%L" --unchanged-line-format="" target_files.txt origin_files.txt > suppressed_files.txt + + # Remove these files + if [ -s suppressed_files.txt ]; then + while IFS= read -r file; do + echo "Removing file: $file" + git rm -f "$file" || true + done < suppressed_files.txt + + git commit -m "chore(branch): update $target_branch from $origin_branch$filter_suffix (with file suppressions)" + echo "merge_status=success" >> $GITHUB_OUTPUT + echo "Changes with file suppressions detected and committed$filter_suffix." + return 0 + else + echo "merge_status=no_changes" >> $GITHUB_OUTPUT + echo "No changes to merge$filter_suffix. Branches are already in sync." + return 1 + fi + fi + } + + # Handle filtered path update with modified and deleted files if [[ -n "$FILTER" ]]; then echo "Updating specific path: $FILTER" git checkout origin/$ORIGIN -- "$FILTER" - # Check if there are any changes to stage - if git status --porcelain | grep -q "$FILTER"; then - git add "$FILTER" + # Commit modified files + modified=$(git diff origin/$TARGET origin/$ORIGIN --name-only -- "$FILTER") + if [[ -n "$modified" ]]; then + echo "$modified" | xargs -r git add git commit -m "chore(branch): update $TARGET from $ORIGIN for path $FILTER" echo "merge_status=success" >> $GITHUB_OUTPUT - echo "Changes detected and committed for path $FILTER." - else - echo "merge_status=no_changes" >> $GITHUB_OUTPUT - echo "No changes to merge for path $FILTER." + echo "Modified files detected and committed for path $FILTER." + exit 0 + fi + # Commit deleted files + deleted=$(git diff origin/$TARGET origin/$ORIGIN --name-only --diff-filter=D -- "$FILTER") + if [[ -n "$deleted" ]]; then + echo "$deleted" | while read file; do + echo "Removing file: $file" + git rm "$file" + done + git commit -m "chore(branch): update $TARGET from $ORIGIN for path $FILTER (deleted files)" + echo "merge_status=success" >> $GITHUB_OUTPUT + echo "Deleted files detected and removed for path $FILTER." + exit 0 fi + echo "merge_status=no_changes" >> $GITHUB_OUTPUT + echo "No changes to merge for path $FILTER. Branches are already in sync." exit 0 fi # Try to merge origin into temp branch - if git merge origin/${{ github.event.inputs.origin_branch }} --no-ff; then + if git merge origin/$ORIGIN --no-ff; then echo "merge_status=success" >> $GITHUB_OUTPUT echo "Merge successful, no conflicts detected." # Check if there are any changes between branches - if git diff --quiet origin/${{ github.event.inputs.target_branch }} origin/${{ github.event.inputs.origin_branch }}; then + if git diff --quiet origin/$TARGET origin/$ORIGIN; then echo "merge_status=no_changes" >> $GITHUB_OUTPUT echo "No changes to merge. Branches are already in sync." fi @@ -201,11 +265,12 @@ jobs: const { execSync } = require('child_process'); execSync('git push origin ${{ steps.create-temp-branch.outputs.temp_branch }}'); - const prTitle = `chore(branch): update ${{ github.event.inputs.target_branch }} from ${{ github.event.inputs.origin_branch }}`; + const filterSuffix = '${{ github.event.inputs.filter }}' ? ` for path ${{ github.event.inputs.filter }}` : ''; + const prTitle = `chore(branch): update ${{ github.event.inputs.target_branch }} from ${{ github.event.inputs.origin_branch }}${filterSuffix}`; let prBody = `## Branch Update - This PR updates \`${{ github.event.inputs.target_branch }}\` with changes from \`${{ github.event.inputs.origin_branch }}\`.`; + This PR updates \`${{ github.event.inputs.target_branch }}\` with changes from \`${{ github.event.inputs.origin_branch }}\`${filterSuffix ? ` for the path \`${filterSuffix.substring(10)}\`` : ''}.`; if ('${{ steps.attempt-merge.outputs.merge_status }}' === 'conflict') { prBody += ` From b1c503ee8e0afe5512dd683e571b89de7098479b Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 10:39:47 +0200 Subject: [PATCH 27/29] ci(workflow): fix not detecting deletions when filtering --- .github/workflows/user-update-branch.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index 42162d42..3b837a9f 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -199,16 +199,8 @@ jobs: echo "Modified files detected and committed for path $FILTER." exit 0 fi - # Commit deleted files - deleted=$(git diff origin/$TARGET origin/$ORIGIN --name-only --diff-filter=D -- "$FILTER") - if [[ -n "$deleted" ]]; then - echo "$deleted" | while read file; do - echo "Removing file: $file" - git rm "$file" - done - git commit -m "chore(branch): update $TARGET from $ORIGIN for path $FILTER (deleted files)" - echo "merge_status=success" >> $GITHUB_OUTPUT - echo "Deleted files detected and removed for path $FILTER." + # Handle file suppressions (deleted files) + if check_file_suppressions "$ORIGIN" "$TARGET" "$FILTER"; then exit 0 fi echo "merge_status=no_changes" >> $GITHUB_OUTPUT From be5a0138c5e55cc636708524aec69442f2048206 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 20 Apr 2025 08:42:29 +0000 Subject: [PATCH 28/29] chore(branch): update marc-romu/workflows from main for path .github --- .github/workflows/user-update-branch.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index 42162d42..3b837a9f 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -199,16 +199,8 @@ jobs: echo "Modified files detected and committed for path $FILTER." exit 0 fi - # Commit deleted files - deleted=$(git diff origin/$TARGET origin/$ORIGIN --name-only --diff-filter=D -- "$FILTER") - if [[ -n "$deleted" ]]; then - echo "$deleted" | while read file; do - echo "Removing file: $file" - git rm "$file" - done - git commit -m "chore(branch): update $TARGET from $ORIGIN for path $FILTER (deleted files)" - echo "merge_status=success" >> $GITHUB_OUTPUT - echo "Deleted files detected and removed for path $FILTER." + # Handle file suppressions (deleted files) + if check_file_suppressions "$ORIGIN" "$TARGET" "$FILTER"; then exit 0 fi echo "merge_status=no_changes" >> $GITHUB_OUTPUT From 5c2c0c2ba4ee407f28081d9054b1b33236e5f568 Mon Sep 17 00:00:00 2001 From: marc-romu <49920661+marc-romu@users.noreply.github.com> Date: Sun, 20 Apr 2025 10:48:56 +0200 Subject: [PATCH 29/29] ci(workflow): fix user-update-branch to handle deletion --- .github/workflows/user-update-branch.yml | 31 ++++++++++++++---------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/.github/workflows/user-update-branch.yml b/.github/workflows/user-update-branch.yml index 3b837a9f..ba38cdfd 100644 --- a/.github/workflows/user-update-branch.yml +++ b/.github/workflows/user-update-branch.yml @@ -186,25 +186,30 @@ jobs: fi } - # Handle filtered path update with modified and deleted files + # Handle filtered path update: delete removed files, then update contents if [[ -n "$FILTER" ]]; then echo "Updating specific path: $FILTER" + # Remove files deleted in origin + deleted=$(git diff origin/$TARGET origin/$ORIGIN --diff-filter=D --name-only -- "$FILTER") + if [[ -n "$deleted" ]]; then + echo "$deleted" | while read file; do + echo "Removing file: $file" + git rm -f "$file" + done + fi + # Checkout new and modified files git checkout origin/$ORIGIN -- "$FILTER" - # Commit modified files - modified=$(git diff origin/$TARGET origin/$ORIGIN --name-only -- "$FILTER") - if [[ -n "$modified" ]]; then - echo "$modified" | xargs -r git add + # Stage all changes under filter + git add "$FILTER" + # Commit if there are staged changes + if git diff --cached --quiet; then + echo "merge_status=no_changes" >> $GITHUB_OUTPUT + echo "No changes to merge for path $FILTER. Branches are already in sync." + else git commit -m "chore(branch): update $TARGET from $ORIGIN for path $FILTER" echo "merge_status=success" >> $GITHUB_OUTPUT - echo "Modified files detected and committed for path $FILTER." - exit 0 - fi - # Handle file suppressions (deleted files) - if check_file_suppressions "$ORIGIN" "$TARGET" "$FILTER"; then - exit 0 + echo "Detected and committed changes for path $FILTER." fi - echo "merge_status=no_changes" >> $GITHUB_OUTPUT - echo "No changes to merge for path $FILTER. Branches are already in sync." exit 0 fi