From 7c1a3b0c15c4ac3d356b8a3987a9e3baca74c826 Mon Sep 17 00:00:00 2001 From: Manas Kenge Date: Tue, 30 Dec 2025 13:19:46 +0530 Subject: [PATCH 1/3] fix dialog (#26298) --- .../components/VoiceSelectionDialog.tsx | 14 +- packages/app-store/apps.browser.generated.tsx | 160 +++++------------- 2 files changed, 46 insertions(+), 128 deletions(-) diff --git a/apps/web/modules/ee/workflows/components/VoiceSelectionDialog.tsx b/apps/web/modules/ee/workflows/components/VoiceSelectionDialog.tsx index 129a99e4a47836..0017c01250f4c7 100644 --- a/apps/web/modules/ee/workflows/components/VoiceSelectionDialog.tsx +++ b/apps/web/modules/ee/workflows/components/VoiceSelectionDialog.tsx @@ -1,6 +1,6 @@ import { getCoreRowModel, getSortedRowModel, useReactTable, type ColumnDef } from "@tanstack/react-table"; import { usePathname } from "next/navigation"; -import { useMemo, useState } from "react"; +import { useMemo, useState, useCallback } from "react"; import { DataTableProvider, DataTableWrapper } from "@calcom/features/data-table"; import { useSegments } from "@calcom/features/data-table/hooks/useSegments"; @@ -58,10 +58,10 @@ function VoiceSelectionContent({ const { data: voices, isLoading } = trpc.viewer.aiVoiceAgent.listVoices.useQuery(); - const handleUseVoice = (voiceId: string) => { + const handleUseVoice = useCallback((voiceId: string) => { onVoiceSelect(voiceId); showToast("Voice selected successfully", "success"); - }; + }, [onVoiceSelect]); const voiceData: Voice[] = useMemo(() => { if (!voices) return []; @@ -105,7 +105,7 @@ function VoiceSelectionContent({ header: t("trait"), size: 200, cell: ({ row }) => ( -
+
{row.original.accent && ( {row.original.accent} @@ -145,12 +145,12 @@ function VoiceSelectionContent({ color={selectedVoiceId === row.original.voice_id ? "primary" : "secondary"} onClick={() => handleUseVoice(row.original.voice_id)} className="whitespace-nowrap"> - {selectedVoiceId === row.original.voice_id ? <>{t("current_voice")} : t("use_voice")} + {selectedVoiceId === row.original.voice_id ? t("current_voice") : t("use_voice")} ), }, ], - [t, playingVoiceId, selectedVoiceId] + [t, playingVoiceId, selectedVoiceId, handlePlayVoice, handleUseVoice] ); const table = useReactTable({ @@ -196,7 +196,7 @@ export function VoiceSelectionDialog({ return ( - +
diff --git a/packages/app-store/apps.browser.generated.tsx b/packages/app-store/apps.browser.generated.tsx index 1bbd92de0c3ba1..cf6fef19fff46f 100644 --- a/packages/app-store/apps.browser.generated.tsx +++ b/packages/app-store/apps.browser.generated.tsx @@ -3,150 +3,68 @@ Don't modify this file manually. **/ import dynamic from "next/dynamic"; - export const InstallAppButtonMap = { - exchange2013calendar: dynamic( - () => import("./exchange2013calendar/components/InstallAppButton") - ), - exchange2016calendar: dynamic( - () => import("./exchange2016calendar/components/InstallAppButton") - ), - office365video: dynamic( - () => import("./office365video/components/InstallAppButton") - ), + exchange2013calendar: dynamic(() => import("./exchange2013calendar/components/InstallAppButton")), + exchange2016calendar: dynamic(() => import("./exchange2016calendar/components/InstallAppButton")), + office365video: dynamic(() => import("./office365video/components/InstallAppButton")), vital: dynamic(() => import("./vital/components/InstallAppButton")), }; - export const AppSettingsComponentsMap = { "general-app-settings": dynamic( - () => - import("./templates/general-app-settings/components/AppSettingsInterface") + () => import("./templates/general-app-settings/components/AppSettingsInterface") ), weather_in_your_calendar: dynamic( () => import("./weather_in_your_calendar/components/AppSettingsInterface") ), zapier: dynamic(() => import("./zapier/components/AppSettingsInterface")), }; - export const EventTypeAddonMap = { alby: dynamic(() => import("./alby/components/EventTypeAppCardInterface")), - basecamp3: dynamic( - () => import("./basecamp3/components/EventTypeAppCardInterface") - ), - btcpayserver: dynamic( - () => import("./btcpayserver/components/EventTypeAppCardInterface") - ), - closecom: dynamic( - () => import("./closecom/components/EventTypeAppCardInterface") - ), - databuddy: dynamic( - () => import("./databuddy/components/EventTypeAppCardInterface") - ), - fathom: dynamic( - () => import("./fathom/components/EventTypeAppCardInterface") - ), + basecamp3: dynamic(() => import("./basecamp3/components/EventTypeAppCardInterface")), + btcpayserver: dynamic(() => import("./btcpayserver/components/EventTypeAppCardInterface")), + closecom: dynamic(() => import("./closecom/components/EventTypeAppCardInterface")), + databuddy: dynamic(() => import("./databuddy/components/EventTypeAppCardInterface")), + fathom: dynamic(() => import("./fathom/components/EventTypeAppCardInterface")), ga4: dynamic(() => import("./ga4/components/EventTypeAppCardInterface")), giphy: dynamic(() => import("./giphy/components/EventTypeAppCardInterface")), gtm: dynamic(() => import("./gtm/components/EventTypeAppCardInterface")), - hitpay: dynamic( - () => import("./hitpay/components/EventTypeAppCardInterface") - ), - hubspot: dynamic( - () => import("./hubspot/components/EventTypeAppCardInterface") - ), - insihts: dynamic( - () => import("./insihts/components/EventTypeAppCardInterface") - ), - matomo: dynamic( - () => import("./matomo/components/EventTypeAppCardInterface") - ), - metapixel: dynamic( - () => import("./metapixel/components/EventTypeAppCardInterface") - ), - "mock-payment-app": dynamic( - () => import("./mock-payment-app/components/EventTypeAppCardInterface") - ), - paypal: dynamic( - () => import("./paypal/components/EventTypeAppCardInterface") - ), - "pipedrive-crm": dynamic( - () => import("./pipedrive-crm/components/EventTypeAppCardInterface") - ), - plausible: dynamic( - () => import("./plausible/components/EventTypeAppCardInterface") - ), - posthog: dynamic( - () => import("./posthog/components/EventTypeAppCardInterface") - ), - qr_code: dynamic( - () => import("./qr_code/components/EventTypeAppCardInterface") - ), - salesforce: dynamic( - () => import("./salesforce/components/EventTypeAppCardInterface") - ), - stripepayment: dynamic( - () => import("./stripepayment/components/EventTypeAppCardInterface") - ), + hitpay: dynamic(() => import("./hitpay/components/EventTypeAppCardInterface")), + hubspot: dynamic(() => import("./hubspot/components/EventTypeAppCardInterface")), + insihts: dynamic(() => import("./insihts/components/EventTypeAppCardInterface")), + matomo: dynamic(() => import("./matomo/components/EventTypeAppCardInterface")), + metapixel: dynamic(() => import("./metapixel/components/EventTypeAppCardInterface")), + "mock-payment-app": dynamic(() => import("./mock-payment-app/components/EventTypeAppCardInterface")), + paypal: dynamic(() => import("./paypal/components/EventTypeAppCardInterface")), + "pipedrive-crm": dynamic(() => import("./pipedrive-crm/components/EventTypeAppCardInterface")), + plausible: dynamic(() => import("./plausible/components/EventTypeAppCardInterface")), + posthog: dynamic(() => import("./posthog/components/EventTypeAppCardInterface")), + qr_code: dynamic(() => import("./qr_code/components/EventTypeAppCardInterface")), + salesforce: dynamic(() => import("./salesforce/components/EventTypeAppCardInterface")), + stripepayment: dynamic(() => import("./stripepayment/components/EventTypeAppCardInterface")), "booking-pages-tag": dynamic( - () => - import( - "./templates/booking-pages-tag/components/EventTypeAppCardInterface" - ) + () => import("./templates/booking-pages-tag/components/EventTypeAppCardInterface") ), "event-type-app-card": dynamic( - () => - import( - "./templates/event-type-app-card/components/EventTypeAppCardInterface" - ) - ), - twipla: dynamic( - () => import("./twipla/components/EventTypeAppCardInterface") + () => import("./templates/event-type-app-card/components/EventTypeAppCardInterface") ), + twipla: dynamic(() => import("./twipla/components/EventTypeAppCardInterface")), umami: dynamic(() => import("./umami/components/EventTypeAppCardInterface")), - "zoho-bigin": dynamic( - () => import("./zoho-bigin/components/EventTypeAppCardInterface") - ), - zohocrm: dynamic( - () => import("./zohocrm/components/EventTypeAppCardInterface") - ), + "zoho-bigin": dynamic(() => import("./zoho-bigin/components/EventTypeAppCardInterface")), + zohocrm: dynamic(() => import("./zohocrm/components/EventTypeAppCardInterface")), }; export const EventTypeSettingsMap = { - alby: dynamic( - () => import("./alby/components/EventTypeAppSettingsInterface") - ), - basecamp3: dynamic( - () => import("./basecamp3/components/EventTypeAppSettingsInterface") - ), - btcpayserver: dynamic( - () => import("./btcpayserver/components/EventTypeAppSettingsInterface") - ), - databuddy: dynamic( - () => import("./databuddy/components/EventTypeAppSettingsInterface") - ), - fathom: dynamic( - () => import("./fathom/components/EventTypeAppSettingsInterface") - ), + alby: dynamic(() => import("./alby/components/EventTypeAppSettingsInterface")), + basecamp3: dynamic(() => import("./basecamp3/components/EventTypeAppSettingsInterface")), + btcpayserver: dynamic(() => import("./btcpayserver/components/EventTypeAppSettingsInterface")), + databuddy: dynamic(() => import("./databuddy/components/EventTypeAppSettingsInterface")), + fathom: dynamic(() => import("./fathom/components/EventTypeAppSettingsInterface")), ga4: dynamic(() => import("./ga4/components/EventTypeAppSettingsInterface")), - giphy: dynamic( - () => import("./giphy/components/EventTypeAppSettingsInterface") - ), + giphy: dynamic(() => import("./giphy/components/EventTypeAppSettingsInterface")), gtm: dynamic(() => import("./gtm/components/EventTypeAppSettingsInterface")), - hitpay: dynamic( - () => import("./hitpay/components/EventTypeAppSettingsInterface") - ), - metapixel: dynamic( - () => import("./metapixel/components/EventTypeAppSettingsInterface") - ), - paypal: dynamic( - () => import("./paypal/components/EventTypeAppSettingsInterface") - ), - plausible: dynamic( - () => import("./plausible/components/EventTypeAppSettingsInterface") - ), - qr_code: dynamic( - () => import("./qr_code/components/EventTypeAppSettingsInterface") - ), - stripepayment: dynamic( - () => import("./stripepayment/components/EventTypeAppSettingsInterface") - ), + hitpay: dynamic(() => import("./hitpay/components/EventTypeAppSettingsInterface")), + metapixel: dynamic(() => import("./metapixel/components/EventTypeAppSettingsInterface")), + paypal: dynamic(() => import("./paypal/components/EventTypeAppSettingsInterface")), + plausible: dynamic(() => import("./plausible/components/EventTypeAppSettingsInterface")), + qr_code: dynamic(() => import("./qr_code/components/EventTypeAppSettingsInterface")), + stripepayment: dynamic(() => import("./stripepayment/components/EventTypeAppSettingsInterface")), }; From ae14e10b245cb4a917f8b94651b02c5aa63296dc Mon Sep 17 00:00:00 2001 From: Pedro Castro Date: Tue, 30 Dec 2025 07:23:02 -0300 Subject: [PATCH 2/3] fix(ci): verify org membership when author_association fails (#26296) * fix(ci): verify org membership via API when author_association fails * refactor: use core team membership check instead of org membership --- .github/workflows/pr.yml | 51 ++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index ec4acafa7ac61e..6cf9c12afa2c07 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -57,6 +57,22 @@ jobs: const prNumber = pr.number; const headSha = pr.head.sha; + async function isCoreTeamMember(username) { + try { + await github.rest.teams.getMembershipForUserInOrg({ + org: owner, + team_slug: 'core', + username, + }); + return true; + } catch (e) { + if (e.status !== 404) { + console.log(`Could not verify core team membership for ${username}: ${e.message}`); + } + return false; + } + } + console.log(`PR #${prNumber} by ${pr.user.login}`); console.log(`Author association: ${pr.author_association}`); console.log(`Head SHA: ${headSha}`); @@ -69,9 +85,16 @@ jobs: return; } - console.log(`Author ${pr.user.login} is external (${pr.author_association}), checking for core team approval...`); + // Check 2: Verify core team membership via API (author_association can be unreliable) + if (await isCoreTeamMember(pr.user.login)) { + console.log(`Author ${pr.user.login} verified as core team member`); + core.setOutput('is-trusted', true); + return; + } + + console.log(`Author ${pr.user.login} is not a core team member, checking for approval...`); - // Check 2: Has a core team member approved the current commit? + // Check 3: Has a core team member approved the current commit? const reviews = await github.paginate(github.rest.pulls.listReviews, { owner, repo, @@ -105,30 +128,18 @@ jobs: } } - // Check if any approver is a core team member (has write access or higher) + // Check if any approver is a core team member for (const [reviewer, reviewData] of reviewerStates) { if (reviewData.state !== 'APPROVED') { continue; } - try { - const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({ - owner, - repo, - username: reviewer, - }); - - const isCoreTeam = ['admin', 'maintain', 'write'].includes(permission.permission); - console.log(`Reviewer ${reviewer}: permission=${permission.permission}, isCoreTeam=${isCoreTeam}`); - - if (isCoreTeam) { - console.log(`PR approved by core team member ${reviewer} for commit ${headSha}`); - core.setOutput('is-trusted', true); - return; - } - } catch (e) { - console.log(`Could not check permission for ${reviewer}: ${e.message}`); + if (await isCoreTeamMember(reviewer)) { + console.log(`PR approved by core team member ${reviewer} for commit ${headSha}`); + core.setOutput('is-trusted', true); + return; } + console.log(`Reviewer ${reviewer} is not a core team member`); } console.log('PR requires approval from a core team member before CI can run'); From e4753778fe569c2918006ca6a79162d884eba6e3 Mon Sep 17 00:00:00 2001 From: Keith Williams Date: Tue, 30 Dec 2025 08:04:09 -0300 Subject: [PATCH 3/3] fix(ci): use getCollaboratorPermissionLevel for trust check (#26306) * fix(ci): use GitHub App token for team membership check The default GITHUB_TOKEN doesn't have org-level permissions to query team membership via the teams.getMembershipForUserInOrg API. This causes the API to return 404 even for valid team members, making the trust check incorrectly fail for core team members. This change generates a short-lived GitHub App token with org-level access to properly verify team membership. Co-Authored-By: keith@cal.com * fix(ci): use getCollaboratorPermissionLevel for trust check Replace the team membership check (teams.getMembershipForUserInOrg) with getCollaboratorPermissionLevel which works with the default GITHUB_TOKEN. This approach: - Checks if users have write/maintain/admin access to the repo - Works without requiring a GitHub App token with org-level permissions - Is semantically correct: if someone can push to the repo, they're trusted The previous approach using teams.getMembershipForUserInOrg required org-level permissions that the default GITHUB_TOKEN doesn't have, causing 404 errors even for valid team members. Co-Authored-By: keith@cal.com * Apply suggestion from @keithwillcode --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .github/workflows/pr.yml | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 6cf9c12afa2c07..825d7cd286e2b4 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -57,18 +57,19 @@ jobs: const prNumber = pr.number; const headSha = pr.head.sha; - async function isCoreTeamMember(username) { + // Check if user has write access or higher (admin, maintain, write) + async function hasWriteAccess(username) { try { - await github.rest.teams.getMembershipForUserInOrg({ - org: owner, - team_slug: 'core', + const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner, + repo, username, }); - return true; + const hasAccess = ['admin', 'maintain', 'write'].includes(permission.permission); + console.log(`User ${username}: hasWriteAccess=${hasAccess}`); + return hasAccess; } catch (e) { - if (e.status !== 404) { - console.log(`Could not verify core team membership for ${username}: ${e.message}`); - } + console.log(`Could not check permission for ${username}: ${e.message}`); return false; } } @@ -85,16 +86,16 @@ jobs: return; } - // Check 2: Verify core team membership via API (author_association can be unreliable) - if (await isCoreTeamMember(pr.user.login)) { - console.log(`Author ${pr.user.login} verified as core team member`); + // Check 2: Verify write access via API (author_association can be unreliable) + if (await hasWriteAccess(pr.user.login)) { + console.log(`Author ${pr.user.login} verified as having write access`); core.setOutput('is-trusted', true); return; } - console.log(`Author ${pr.user.login} is not a core team member, checking for approval...`); + console.log(`Author ${pr.user.login} does not have write access, checking for approval...`); - // Check 3: Has a core team member approved the current commit? + // Check 3: Has someone with write access approved the current commit? const reviews = await github.paginate(github.rest.pulls.listReviews, { owner, repo, @@ -128,21 +129,21 @@ jobs: } } - // Check if any approver is a core team member + // Check if any approver has write access for (const [reviewer, reviewData] of reviewerStates) { if (reviewData.state !== 'APPROVED') { continue; } - if (await isCoreTeamMember(reviewer)) { - console.log(`PR approved by core team member ${reviewer} for commit ${headSha}`); + if (await hasWriteAccess(reviewer)) { + console.log(`PR approved by ${reviewer} (has write access) for commit ${headSha}`); core.setOutput('is-trusted', true); return; } - console.log(`Reviewer ${reviewer} is not a core team member`); + console.log(`Reviewer ${reviewer} does not have write access`); } - console.log('PR requires approval from a core team member before CI can run'); + console.log('PR requires approval from someone with write access before CI can run'); core.setOutput('is-trusted', false); prepare: