diff --git a/.github/workflows/fix-completions-on-comment.yml b/.github/workflows/fix-completions-on-comment.yml new file mode 100644 index 000000000000..c326d59a50bb --- /dev/null +++ b/.github/workflows/fix-completions-on-comment.yml @@ -0,0 +1,167 @@ +name: Fix completion snapshots on command + +on: + issue_comment: + types: [created] + +permissions: + contents: write + pull-requests: write + issues: write + +jobs: + fix-completions: + # Only run on PR comments that start with /fixcompletions + if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/fixcompletions') + runs-on: ubuntu-latest + + steps: + - name: React to comment + uses: actions/github-script@v7 + with: + script: | + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: 'eyes' + }); + + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + + - name: Checkout PR branch + run: | + gh pr checkout ${{ github.event.issue.number }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Build repository + id: build + run: | + chmod +x ./build.sh + ./build.sh + continue-on-error: true + timeout-minutes: 15 + + - name: Run completion tests + id: test + if: steps.build.outcome == 'success' + run: | + # Use the repo-local dotnet that was built + ./.dotnet/dotnet test test/dotnet.Tests/dotnet.Tests.csproj --filter "Name~VerifyCompletions" + continue-on-error: true + timeout-minutes: 10 + + - name: Compare snapshots + id: compare + if: steps.test.outcome != 'skipped' + run: | + dotnet restore test/dotnet.Tests/ /t:CompareCliSnapshots + continue-on-error: true + + - name: Check for snapshot changes + id: check-changes + if: steps.compare.outcome == 'success' + run: | + # Check if there are changes to snapshot files + if git diff --name-only test/dotnet.Tests/CompletionTests/snapshots/ | grep -E '\.received\.'; then + echo "changes=true" >> $GITHUB_OUTPUT + echo "Changed snapshot files:" + git diff --name-only test/dotnet.Tests/CompletionTests/snapshots/ + else + echo "changes=false" >> $GITHUB_OUTPUT + fi + + - name: Update verified snapshots + id: update + if: steps.check-changes.outputs.changes == 'true' + run: | + # This renames .received.* files to .verified.* + dotnet restore test/dotnet.Tests/ /t:UpdateCliSnapshots + continue-on-error: true + + - name: Commit and push snapshot changes + id: commit + if: steps.update.outcome == 'success' + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + + # Add snapshot files + git add test/dotnet.Tests/CompletionTests/snapshots/ + + # Create commit + COMMIT_DATE=$(date -u +"%Y-%m-%d") + git commit -m "Update CLI completion snapshots - $COMMIT_DATE" + + # Push to the PR branch + git push + continue-on-error: true + + - name: Comment on PR - Success + if: steps.commit.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.issue.number, + body: '✅ CLI completion snapshots have been updated and committed to this PR.' + }); + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: '+1' + }); + + - name: Comment on PR - No changes + if: steps.compare.outcome == 'success' && steps.check-changes.outputs.changes == 'false' + uses: actions/github-script@v7 + with: + script: | + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.issue.number, + body: 'ℹ️ No completion snapshot files needed to be updated.' + }); + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: '+1' + }); + + - name: Comment on PR - Failure + if: steps.build.outcome == 'failure' || (steps.test.outcome == 'failure' && steps.compare.outcome != 'success') || (steps.check-changes.outputs.changes == 'true' && (steps.update.outcome == 'failure' || steps.commit.outcome == 'failure')) + uses: actions/github-script@v7 + with: + script: | + let errorMsg = '❌ Failed to update completion snapshots.'; + + if ('${{ steps.build.outcome }}' === 'failure') { + errorMsg += ' The build failed.'; + } else if ('${{ steps.update.outcome }}' === 'failure') { + errorMsg += ' Could not update snapshot files.'; + } else if ('${{ steps.commit.outcome }}' === 'failure') { + errorMsg += ' Could not commit changes.'; + } + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.issue.number, + body: errorMsg + ' Please check the workflow logs for details.' + }); + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: 'confused' + }); diff --git a/.github/workflows/update-xlf-on-comment.yml b/.github/workflows/update-xlf-on-comment.yml new file mode 100644 index 000000000000..5d5035bedd5c --- /dev/null +++ b/.github/workflows/update-xlf-on-comment.yml @@ -0,0 +1,152 @@ +name: Update XLF files on command + +on: + issue_comment: + types: [created] + +permissions: + contents: write + pull-requests: write + issues: write + +jobs: + update-xlf: + # Only run on PR comments that start with /updatexlf + if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/updatexlf') + runs-on: ubuntu-latest + + steps: + - name: React to comment + uses: actions/github-script@v7 + with: + script: | + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: 'eyes' + }); + + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + + - name: Checkout PR branch + run: | + gh pr checkout ${{ github.event.issue.number }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Run UpdateXlf target + id: update + run: | + chmod +x ./build.sh + # Try the fast UpdateXlf target first + if ./build.sh /t:UpdateXlf; then + echo "result=success" >> $GITHUB_OUTPUT + else + # If that fails, try a full build + if ./build.sh; then + echo "result=success" >> $GITHUB_OUTPUT + else + echo "result=failure" >> $GITHUB_OUTPUT + exit 1 + fi + fi + continue-on-error: true + timeout-minutes: 15 + + - name: Check for XLF changes + id: check-changes + if: steps.update.outcome == 'success' + run: | + # Check if there are changes to .xlf files + if git diff --name-only | grep -E '\.xlf$'; then + echo "changes=true" >> $GITHUB_OUTPUT + echo "Changed XLF files:" + git diff --name-only | grep -E '\.xlf$' + else + echo "changes=false" >> $GITHUB_OUTPUT + fi + + - name: Commit and push XLF changes + id: commit + if: steps.update.outcome == 'success' && steps.check-changes.outputs.changes == 'true' + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + + # Add all .xlf files + git add **/*.xlf + + # Create commit + COMMIT_DATE=$(date -u +"%Y-%m-%d") + git commit -m "Update XLF translation files - $COMMIT_DATE" + + # Push to the PR branch + git push + continue-on-error: true + + - name: Comment on PR - Success + if: steps.commit.outcome == 'success' + uses: actions/github-script@v7 + with: + script: | + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.issue.number, + body: '✅ XLF translation files have been updated and committed to this PR.' + }); + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: '+1' + }); + + - name: Comment on PR - No changes + if: steps.update.outcome == 'success' && steps.check-changes.outputs.changes == 'false' + uses: actions/github-script@v7 + with: + script: | + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.issue.number, + body: 'ℹ️ No XLF files needed to be updated.' + }); + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: '+1' + }); + + - name: Comment on PR - Failure + if: steps.update.outcome == 'failure' || (steps.check-changes.outputs.changes == 'true' && steps.commit.outcome == 'failure') + uses: actions/github-script@v7 + with: + script: | + let errorMsg = '❌ Failed to update XLF files.'; + + if ('${{ steps.update.outcome }}' === 'failure') { + errorMsg += ' The build failed.'; + } else if ('${{ steps.commit.outcome }}' === 'failure') { + errorMsg += ' Could not commit changes.'; + } + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.issue.number, + body: errorMsg + ' Please check the workflow logs for details.' + }); + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: 'confused' + }); diff --git a/documentation/project-docs/Localization.md b/documentation/project-docs/Localization.md index 884c9fa075a3..51fde9703637 100644 --- a/documentation/project-docs/Localization.md +++ b/documentation/project-docs/Localization.md @@ -7,6 +7,17 @@ The local dev build automatically generates updates to the xlf files that contai When making string changes, update the resx, build, and check in all xlf file changes. Developers should never need to update the xlf files directly and should always rely on the local build for updates to those files. This will leave the files in english initially and they will get translated eventually. +#### Automated XLF Updates via GitHub Actions + +If you've modified `.resx` files in a pull request and need to update the corresponding `.xlf` files but don't want to clone the branch locally, you can use the automated GitHub Action: + +**Comment `/updatexlf` on your pull request** and the workflow will: +1. Check out your PR branch +2. Run the UpdateXlf build target +3. Commit any updated `.xlf` files directly to your PR branch + +This is particularly useful when CI is failing due to outdated XLF files. + For internal folks, see https://aka.ms/allaboutloc ### Loc issues diff --git a/documentation/project-docs/developer-guide.md b/documentation/project-docs/developer-guide.md index 61fb41ea8a37..4cfd06528085 100644 --- a/documentation/project-docs/developer-guide.md +++ b/documentation/project-docs/developer-guide.md @@ -176,6 +176,35 @@ taskkill /F /IM VSTest.Console.exe /T || taskkill /F /IM msbuild.exe /T ``` +## Automated PR Maintenance Commands + +The SDK repository includes GitHub Actions workflows that automate common maintenance tasks directly from pull requests. + +### `/updatexlf` - Update Translation Files + +When you modify `.resx` resource files, the corresponding `.xlf` translation files need to be updated. Instead of manually running the build locally, comment `/updatexlf` on the PR and the GitHub Action will: + +1. Check out the PR branch +2. Run `./build.sh /t:UpdateXlf` (or full build if needed) +3. Commit any updated `.xlf` files directly to the PR branch + +This is useful when you've changed localized strings and the CI build is failing due to outdated XLF files. + +See also: [Localization documentation](Localization.md) + +### `/fixcompletions` - Update CLI Completion Snapshots + +The CLI includes snapshot-based tests for shell completions (bash, zsh, pwsh, etc.). When you add or modify CLI commands, these snapshots need to be updated. Comment `/fixcompletions` on the PR and the GitHub Action will: + +1. Build the repository +2. Run the completion tests +3. Update the verified snapshot files +4. Commit the changes directly to the PR branch + +This is useful when you've added new commands or options and the completion snapshot tests are failing. + +See also: [Snapshot-based testing documentation](snapshot-based-testing.md) + ## Adding a Command The dotnet CLI supports several models for adding new commands: diff --git a/documentation/project-docs/snapshot-based-testing.md b/documentation/project-docs/snapshot-based-testing.md index eb282a9cb174..c423941cca30 100644 --- a/documentation/project-docs/snapshot-based-testing.md +++ b/documentation/project-docs/snapshot-based-testing.md @@ -13,6 +13,18 @@ To fix these tests, you need to diff the two files and visually inspect the chan * [CompareCliSnapshots][compare] - this Target copies the .received. files from the artifacts directory, where they are created due to the way we run tests, to the [snapshots][snapshots] directory in the dotnet.Tests project. This makes it much easier to diff the two. * [UpdateCliSnapshots][update] - this Target renames the .received. files to .verified. in the local [snapshots][snapshots] directory, and so acts as a giant 'I accept these changes' button. Only use this if you've diffed the snapshots and are sure they match your expectations. +### Automated Snapshot Updates via GitHub Actions + +If you've modified CLI commands in a pull request and the completion snapshot tests are failing, but you don't want to clone the branch locally to update the snapshots, you can use the automated GitHub Action: + +**Comment `/fixcompletions` on your pull request** and the workflow will: +1. Build the repository +2. Run the completion tests to generate new snapshots +3. Run the `CompareCliSnapshots` and `UpdateCliSnapshots` targets +4. Commit the updated verified snapshot files directly to your PR branch + +This is particularly useful when you've added new commands, options, or modified existing CLI functionality. + [dotnet.Tests]: ../../test/dotnet.Tests/ [snapshot-tests]: ../../test/dotnet.Tests/CompletionTests/DotnetCliSnapshotTests.cs [snapshots]: ../../test/dotnet.Tests/CompletionTests/snapshots/