Skip to content

Commit 3906eb8

Browse files
lyzno1Copilot
andauthored
feat(ci): optimize CI workflows and i18n translation system (#204)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 69a81c8 commit 3906eb8

File tree

15 files changed

+1029
-145
lines changed

15 files changed

+1029
-145
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: Auto I18n Translation
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- "messages/en-US.json"
8+
9+
permissions:
10+
contents: write
11+
pull-requests: write
12+
13+
jobs:
14+
translate:
15+
name: Auto Translation
16+
runs-on: ubuntu-latest
17+
if: ${{ github.repository == 'ifLabX/AgentifUI' }}
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v4
21+
with:
22+
token: ${{ secrets.GITHUB_TOKEN }}
23+
24+
- name: Setup pnpm
25+
uses: pnpm/action-setup@v4
26+
with:
27+
version: "9"
28+
29+
- name: Setup Node.js
30+
uses: actions/setup-node@v4
31+
with:
32+
node-version: "18"
33+
cache: "pnpm"
34+
35+
- name: Setup Python
36+
uses: actions/setup-python@v5
37+
with:
38+
python-version: "3.x"
39+
40+
- name: Install dependencies
41+
run: pnpm install --frozen-lockfile
42+
43+
- name: Clean extra keys and run full translation
44+
run: |
45+
echo "🧹 Cleaning extra keys..."
46+
pnpm i18n:remove-extra || true
47+
48+
echo "🤖 Starting translation..."
49+
pnpm i18n:translate
50+
51+
echo "✅ Validating translations..."
52+
pnpm i18n:check || true
53+
54+
- name: Create Pull Request
55+
uses: peter-evans/create-pull-request@v6
56+
with:
57+
token: ${{ secrets.GITHUB_TOKEN }}
58+
branch: auto-i18n-updates
59+
delete-branch: true
60+
title: "chore(i18n): auto-update translations"
61+
body: |
62+
## 🤖 Automated Translation Update
63+
64+
Auto-generated translations based on changes to `messages/en-US.json`.
65+
66+
**Triggered by**: Push to main branch
67+
**Languages**: All supported languages
68+
**Mode**: Full translation update
69+
70+
Please review the translations and merge when ready.
71+
labels: |
72+
i18n
73+
automated
74+
commit-message: |
75+
chore(i18n): auto-update translations
76+
77+
Auto-generated by GitHub Actions

.github/workflows/ci.yml

Lines changed: 8 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ jobs:
109109
format-and-lint:
110110
name: Format and Lint Check
111111
runs-on: ubuntu-latest
112+
timeout-minutes: 5
112113
needs: [changes, install]
113114
if: needs.changes.outputs.source == 'true' || needs.changes.outputs.config == 'true' || needs.changes.outputs.styles == 'true'
114115
permissions:
@@ -150,6 +151,7 @@ jobs:
150151
type-check:
151152
name: TypeScript Type Check
152153
runs-on: ubuntu-latest
154+
timeout-minutes: 5
153155
needs: [changes, install]
154156
if: needs.changes.outputs.source == 'true' || needs.changes.outputs.config == 'true'
155157
permissions:
@@ -175,47 +177,12 @@ jobs:
175177
- name: Run TypeScript type check
176178
run: pnpm run type-check
177179

178-
# Internationalization validation
179-
i18n-validation:
180-
name: I18n Validation
181-
runs-on: ubuntu-latest
182-
needs: [changes, install]
183-
if: needs.changes.outputs.i18n == 'true' || needs.changes.outputs.source == 'true'
184-
permissions:
185-
contents: read
186-
steps:
187-
- name: Checkout repository
188-
uses: actions/checkout@v4
189-
190-
- name: Setup Python
191-
uses: actions/setup-python@v5
192-
with:
193-
python-version: '3.x'
194-
195-
- name: Setup pnpm
196-
uses: pnpm/action-setup@v4
197-
with:
198-
version: ${{ env.PNPM_VERSION }}
199-
200-
- name: Setup Node.js
201-
uses: actions/setup-node@v4
202-
with:
203-
node-version: ${{ env.NODE_VERSION }}
204-
cache: 'pnpm'
205-
206-
- name: Install dependencies
207-
run: pnpm install --frozen-lockfile
208-
209-
- name: Quick i18n check
210-
run: pnpm run i18n:check
211-
212-
- name: Validate i18n consistency
213-
run: pnpm run i18n:validate
214180

215181
# Build the application
216182
build:
217183
name: Build Application
218184
runs-on: ubuntu-latest
185+
timeout-minutes: 10
219186
needs: [changes, install]
220187
if: needs.changes.outputs.source == 'true' || needs.changes.outputs.config == 'true'
221188
permissions:
@@ -252,19 +219,18 @@ jobs:
252219
path: .next/
253220
retention-days: 7
254221

255-
# Run tests for changed test files
222+
# Run tests
256223
test:
257224
name: Run Tests
258225
runs-on: ubuntu-latest
226+
timeout-minutes: 10
259227
needs: [changes, install]
260228
if: needs.changes.outputs.tests == 'true' || needs.changes.outputs.source == 'true'
261229
permissions:
262230
contents: read
263231
steps:
264232
- name: Checkout repository
265233
uses: actions/checkout@v4
266-
with:
267-
fetch-depth: 0
268234

269235
- name: Setup pnpm
270236
uses: pnpm/action-setup@v4
@@ -280,45 +246,14 @@ jobs:
280246
- name: Install dependencies
281247
run: pnpm install --frozen-lockfile
282248

283-
- name: Run tests for changed files
284-
run: |
285-
# Get list of changed test files and source files that might have related tests
286-
CHANGED_TEST_FILES=$(git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} | grep -E '\.(test|spec)\.(js|jsx|ts|tsx)$' | tr '\n' ' ' || true)
287-
CHANGED_SOURCE_FILES=$(git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} | grep -E '\.(js|jsx|ts|tsx)$' | grep -v -E '\.(test|spec)\.' | tr '\n' ' ' || true)
288-
289-
# Find related test files for changed source files
290-
RELATED_TEST_FILES=""
291-
if [ -n "$CHANGED_SOURCE_FILES" ]; then
292-
for file in $CHANGED_SOURCE_FILES; do
293-
# Look for corresponding test files
294-
base_name=$(basename "$file" | sed 's/\.[^.]*$//')
295-
dir_name=$(dirname "$file")
296-
297-
# Check multiple test file patterns
298-
for pattern in "${dir_name}/${base_name}.test.*" "${dir_name}/${base_name}.spec.*" "__tests__/**/${base_name}.*" "**/${base_name}.test.*" "**/${base_name}.spec.*"; do
299-
if ls $pattern 1> /dev/null 2>&1; then
300-
RELATED_TEST_FILES="$RELATED_TEST_FILES $(ls $pattern)"
301-
fi
302-
done
303-
done
304-
fi
305-
306-
# Combine all test files to run
307-
ALL_TEST_FILES="$CHANGED_TEST_FILES $RELATED_TEST_FILES"
308-
ALL_TEST_FILES=$(echo $ALL_TEST_FILES | tr ' ' '\n' | sort -u | tr '\n' ' ')
309-
310-
if [ -n "$ALL_TEST_FILES" ] && [ "$ALL_TEST_FILES" != " " ]; then
311-
echo "Running tests for files: $ALL_TEST_FILES"
312-
pnpm exec jest $ALL_TEST_FILES --passWithNoTests
313-
else
314-
echo "No test files found for changed source files, running all tests"
315-
pnpm test
316-
fi
249+
- name: Run tests
250+
run: pnpm test
317251

318252
# Security checks
319253
security-check:
320254
name: Security Check
321255
runs-on: ubuntu-latest
256+
timeout-minutes: 5
322257
needs: [changes, install]
323258
if: needs.changes.outputs.source == 'true' || needs.changes.outputs.config == 'true'
324259
permissions:
@@ -364,7 +299,6 @@ jobs:
364299
- changes
365300
- format-and-lint
366301
- type-check
367-
- i18n-validation
368302
- build
369303
- test
370304
- security-check
@@ -379,7 +313,6 @@ jobs:
379313
# Check if any job failed
380314
if [[ "${{ needs.format-and-lint.result }}" == "failure" ]] || \
381315
[[ "${{ needs.type-check.result }}" == "failure" ]] || \
382-
[[ "${{ needs.i18n-validation.result }}" == "failure" ]] || \
383316
[[ "${{ needs.build.result }}" == "failure" ]] || \
384317
[[ "${{ needs.test.result }}" == "failure" ]] || \
385318
[[ "${{ needs.security-check.result }}" == "failure" ]]; then

.gitignore

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ next-env.d.ts
3939

4040
# test files
4141
/files
42-
scripts/*
43-
!scripts/validate-i18n-consistency.py
44-
!scripts/i18n-refactor-helper.py
42+
43+
# scripts - allow all development scripts
44+
# scripts/
4545

4646
# venv
4747
.venv

app/admin/api-config/layout.tsx

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import { InstanceFilterSelector } from '@components/admin/api-config/instance-filter-selector';
44
import { SearchInput } from '@components/ui';
5+
import { ConfirmDialog } from '@components/ui/confirm-dialog';
56
import { useTheme } from '@lib/hooks/use-theme';
67
import { useApiConfigStore } from '@lib/stores/api-config-store';
8+
import { ServiceInstance } from '@lib/types/database';
79
import { cn } from '@lib/utils';
810
import {
911
Bot,
@@ -76,6 +78,10 @@ export default function ApiConfigLayout({ children }: ApiConfigLayoutProps) {
7678
const [selectedInstanceId, setSelectedInstanceId] = useState<string | null>(
7779
null
7880
);
81+
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
82+
const [instanceToDelete, setInstanceToDelete] =
83+
useState<ServiceInstance | null>(null);
84+
const [isDeleting, setIsDeleting] = useState(false);
7985

8086
// get filter state from URL query params
8187
const [filterProviderId, setFilterProviderId] = useState<string | null>(
@@ -223,6 +229,26 @@ export default function ApiConfigLayout({ children }: ApiConfigLayoutProps) {
223229
[instances, t, tDebug, setDefaultInstance]
224230
);
225231

232+
const handleDeleteInstance = (instance: ServiceInstance) => {
233+
setInstanceToDelete(instance);
234+
setShowDeleteDialog(true);
235+
};
236+
237+
const handleConfirmDelete = async () => {
238+
if (!instanceToDelete) return;
239+
240+
setIsDeleting(true);
241+
try {
242+
await deleteInstance(instanceToDelete.id);
243+
setShowDeleteDialog(false);
244+
setInstanceToDelete(null);
245+
} catch (error) {
246+
console.error('Failed to delete instance:', error);
247+
} finally {
248+
setIsDeleting(false);
249+
}
250+
};
251+
226252
// listen to page component's state change, fully sync page's form state
227253
useEffect(() => {
228254
const handleAddFormToggled = (event: CustomEvent) => {
@@ -533,7 +559,7 @@ export default function ApiConfigLayout({ children }: ApiConfigLayoutProps) {
533559
<button
534560
onClick={e => {
535561
e.stopPropagation();
536-
deleteInstance(instance.instance_id);
562+
handleDeleteInstance(instance);
537563
}}
538564
disabled={isProcessing || instance.is_default}
539565
className={cn(
@@ -582,6 +608,18 @@ export default function ApiConfigLayout({ children }: ApiConfigLayoutProps) {
582608
<div className="ml-80 h-full flex-1 overflow-hidden pl-px">
583609
{children}
584610
</div>
611+
612+
<ConfirmDialog
613+
isOpen={showDeleteDialog}
614+
onClose={() => !isDeleting && setShowDeleteDialog(false)}
615+
onConfirm={handleConfirmDelete}
616+
title={t('delete')}
617+
message={t('deleteConfirm')}
618+
confirmText={t('delete')}
619+
variant="danger"
620+
icon="delete"
621+
isLoading={isDeleting}
622+
/>
585623
</div>
586624
);
587625
}

0 commit comments

Comments
 (0)