Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions .github/workflows/fix-completions-on-comment.yml
Original file line number Diff line number Diff line change
@@ -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'
});
152 changes: 152 additions & 0 deletions .github/workflows/update-xlf-on-comment.yml
Original file line number Diff line number Diff line change
@@ -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'
});
11 changes: 11 additions & 0 deletions documentation/project-docs/Localization.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 29 additions & 0 deletions documentation/project-docs/developer-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
12 changes: 12 additions & 0 deletions documentation/project-docs/snapshot-based-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down
Loading