Skip to content
Merged
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
193 changes: 193 additions & 0 deletions .github/workflows/fix-completions-on-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
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 or /completions
if: github.event.issue.pull_request &&
(startsWith(github.event.comment.body, '/fixcompletions') || startsWith(github.event.comment.body, '/completions'))
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: Comment on PR - Started
uses: actions/github-script@v7
with:
script: |
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
body: `▢️ CLI completion snapshot update started. Track progress in [this workflow run](${runUrl}).`
});

- 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 "FullyQualifiedName~VerifyCompletions"
continue-on-error: true
timeout-minutes: 10

- name: Compare snapshots
id: compare
if: steps.test.outcome != 'skipped'
run: |
# Use repo-local dotnet so we only restore the project we need on the runner
./.dotnet/dotnet msbuild test/dotnet.Tests/dotnet.Tests.csproj -restore -t:CompareCliSnapshots
continue-on-error: true

- name: Check for snapshot changes
id: check-changes
if: steps.compare.outcome == 'success'
run: |
# Detect new received files (ignored by git) and diff-ed verified files
shopt -s nullglob
received_files=(test/dotnet.Tests/CompletionTests/snapshots/**/*.received.*)
shopt -u nullglob

diff_output=$(git diff --name-only -- test/dotnet.Tests/CompletionTests/snapshots/ | grep -E '\.verified\.' || true)

if [ ${#received_files[@]} -gt 0 ] || [ -n "$diff_output" ]; then
echo "changes=true" >> $GITHUB_OUTPUT
echo "Changed snapshot files:"
printf '%s\n' "${received_files[@]}"
if [ -n "$diff_output" ]; then
echo "$diff_output"
fi
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/dotnet msbuild test/dotnet.Tests/dotnet.Tests.csproj -restore -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: |
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}`;
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. See [workflow details](${runUrl}).`
});
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: |
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}`;
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. Review [the workflow run](${runUrl}).`
});
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.';
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}`;

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 run](${runUrl}) for details.`
});
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: 'confused'
});
168 changes: 168 additions & 0 deletions .github/workflows/update-xlf-on-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
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 or /xlf
if: github.event.issue.pull_request &&
(startsWith(github.event.comment.body, '/updatexlf') || startsWith(github.event.comment.body, '/xlf'))
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: Comment on PR - Started
uses: actions/github-script@v7
with:
script: |
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
body: `▢️ XLF update workflow started. Track progress in [this workflow run](${runUrl}).`
});

- 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: |
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}`;
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. See [workflow details](${runUrl}).`
});
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: |
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}`;
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. Review [the workflow run](${runUrl}).`
});
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.';
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}`;

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 run](${runUrl}) 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
Loading
Loading