fix(ci): fix pipeline #366 — web @mosaic/ui build, Dockerfile find bug, event handler types
All checks were successful
ci/woodpecker/push/orchestrator Pipeline was successful
ci/woodpecker/push/web Pipeline was successful

Three root causes resolved:

1. .woodpecker/web.yml: build-shared step was missing @mosaic/ui build,
   causing 10 test suite failures + 20 typecheck errors (TS2307)

2. apps/orchestrator/Dockerfile: find -o without parentheses only deleted
   last pattern's matches, leaving spec files with test fixture secrets
   that triggered 5 Trivy false positives (3 CRITICAL, 2 HIGH)

3. 9 web files had untyped event handler parameters (e) causing 49 lint
   errors and 19 typecheck errors — added React.ChangeEvent<T> types

Verification: lint 0 errors, typecheck 0 errors, tests 73/73 suites pass

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-12 17:50:41 -06:00
parent 3b12adf8f7
commit e8a9a3087a
12 changed files with 37 additions and 23 deletions

View File

@@ -84,7 +84,7 @@ COPY --from=builder --chown=nestjs:nodejs /app/packages ./packages
# Copy built orchestrator application
COPY --from=builder --chown=nestjs:nodejs /app/apps/orchestrator/dist ./apps/orchestrator/dist
# Remove compiled test files from production (contain test fixtures that trigger Trivy secret scanning)
RUN find ./apps/orchestrator/dist -name '*.spec.js' -o -name '*.spec.js.map' -o -name '*.test.js' -o -name '*.test.js.map' | xargs rm -f 2>/dev/null || true
RUN find ./apps/orchestrator/dist \( -name '*.spec.js' -o -name '*.spec.js.map' -o -name '*.test.js' -o -name '*.test.js.map' \) -print | xargs rm -f 2>/dev/null || true
COPY --from=builder --chown=nestjs:nodejs /app/apps/orchestrator/package.json ./apps/orchestrator/
# Copy app's node_modules which contains symlinks to root node_modules

View File

@@ -180,7 +180,7 @@ export default function CredentialAuditPage(): React.ReactElement {
<Input
type="date"
value={filters.startDate ?? ""}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
handleFilterChange("startDate", e.target.value);
}}
/>
@@ -192,7 +192,7 @@ export default function CredentialAuditPage(): React.ReactElement {
<Input
type="date"
value={filters.endDate ?? ""}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
handleFilterChange("endDate", e.target.value);
}}
/>

View File

@@ -125,7 +125,7 @@ function TeamsPageContent(): ReactElement {
<Input
label="Team Name"
value={newTeamName}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setNewTeamName(e.target.value);
}}
placeholder="Enter team name"
@@ -136,7 +136,7 @@ function TeamsPageContent(): ReactElement {
<Input
label="Description (optional)"
value={newTeamDescription}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setNewTeamDescription(e.target.value);
}}
placeholder="Enter team description"

View File

@@ -27,7 +27,7 @@ export function LogoutButton({
};
return (
<Button variant={variant} onClick={handleSignOut} className={className}>
<Button variant={variant} onClick={() => void handleSignOut()} className={className}>
Sign Out
</Button>
);

View File

@@ -116,7 +116,7 @@ export function CreateCredentialDialog({
<Input
id="name"
value={formData.name}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setFormData({ ...formData, name: e.target.value });
}}
placeholder="e.g., GitHub Personal Token"
@@ -178,7 +178,7 @@ export function CreateCredentialDialog({
id="value"
type="password"
value={formData.value}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setFormData({ ...formData, value: e.target.value });
}}
placeholder="Enter credential value"
@@ -195,7 +195,7 @@ export function CreateCredentialDialog({
<Textarea
id="description"
value={formData.description}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
setFormData({ ...formData, description: e.target.value });
}}
placeholder="Optional description"
@@ -211,7 +211,7 @@ export function CreateCredentialDialog({
id="expiresAt"
type="date"
value={formData.expiresAt}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setFormData({ ...formData, expiresAt: e.target.value });
}}
disabled={isSubmitting}

View File

@@ -99,7 +99,7 @@ export function EditCredentialDialog({
<Input
id="edit-name"
value={formData.name}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setFormData({ ...formData, name: e.target.value });
}}
placeholder="e.g., GitHub Personal Token"
@@ -113,7 +113,7 @@ export function EditCredentialDialog({
<Textarea
id="edit-description"
value={formData.description}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
setFormData({ ...formData, description: e.target.value });
}}
placeholder="Optional description"
@@ -129,7 +129,7 @@ export function EditCredentialDialog({
id="edit-expiresAt"
type="date"
value={formData.expiresAt}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setFormData({ ...formData, expiresAt: e.target.value });
}}
disabled={isSubmitting}

View File

@@ -101,7 +101,7 @@ export function RotateCredentialDialog({
id="rotate-new-value"
type="password"
value={newValue}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setNewValue(e.target.value);
}}
placeholder="Enter new credential value"
@@ -116,7 +116,7 @@ export function RotateCredentialDialog({
id="rotate-confirm-value"
type="password"
value={confirmValue}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setConfirmValue(e.target.value);
}}
placeholder="Re-enter new credential value"

View File

@@ -82,7 +82,7 @@ export function PersonalityForm({
<Input
id="name"
value={formData.name}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setFormData({ ...formData, name: e.target.value });
}}
placeholder="e.g., Professional, Casual, Friendly"
@@ -96,7 +96,7 @@ export function PersonalityForm({
<Textarea
id="description"
value={formData.description}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
setFormData({ ...formData, description: e.target.value });
}}
placeholder="Brief description of this personality style"
@@ -110,7 +110,7 @@ export function PersonalityForm({
<Input
id="tone"
value={formData.tone}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setFormData({ ...formData, tone: e.target.value });
}}
placeholder="e.g., professional, friendly, enthusiastic"
@@ -146,7 +146,7 @@ export function PersonalityForm({
<Textarea
id="systemPrompt"
value={formData.systemPromptTemplate}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
setFormData({ ...formData, systemPromptTemplate: e.target.value });
}}
placeholder="You are a helpful AI assistant..."

View File

@@ -136,7 +136,7 @@ export function TeamMemberList({
label: `${user.name} (${user.email})`,
}))}
value={selectedUserId}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
setSelectedUserId(e.target.value);
}}
placeholder="Select a user..."
@@ -148,7 +148,7 @@ export function TeamMemberList({
<Select
options={roleOptions}
value={selectedRole}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
setSelectedRole(e.target.value as TeamMemberRole);
}}
fullWidth

View File

@@ -69,7 +69,7 @@ export function TeamSettings({ team, onUpdate, onDelete }: TeamSettingsProps): R
<Input
label="Team Name"
value={name}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setName(e.target.value);
setIsEditing(true);
}}
@@ -81,7 +81,7 @@ export function TeamSettings({ team, onUpdate, onDelete }: TeamSettingsProps): R
<Textarea
label="Description"
value={description}
onChange={(e) => {
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
setDescription(e.target.value);
setIsEditing(true);
}}