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
23 changes: 23 additions & 0 deletions .github/actions/cache-build-key/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Generate cache-build key
description: "Generate the cache key for production build caching"
inputs:
branch_key:
required: true
description: "Branch key for cache scoping (e.g., github.head_ref for PRs)"
outputs:
key:
description: "The generated cache key"
value: ${{ steps.generate-key.outputs.key }}
runs:
using: "composite"
steps:
- name: Generate cache key
id: generate-key
shell: bash
env:
CACHE_NAME: prod-build
BRANCH_KEY: ${{ inputs.branch_key }}
LOCKFILE_HASH: ${{ hashFiles('yarn.lock') }}
SOURCE_HASH: ${{ hashFiles('apps/**/**.[jt]s', 'apps/**/**.[jt]sx', 'apps/**/*.json', 'apps/**/*.css', 'packages/**/**.[jt]s', 'packages/**/**.[jt]sx', 'packages/prisma/schema.prisma', 'packages/prisma/migrations/**/*.sql', '!**/node_modules/**', '!packages/prisma/generated/**', '!packages/prisma/client/**', '!packages/prisma/zod/**', '!packages/kysely/**') }}
run: |
echo "key=${CACHE_NAME}-${BRANCH_KEY}-${LOCKFILE_HASH}-${SOURCE_HASH}" >> $GITHUB_OUTPUT
20 changes: 6 additions & 14 deletions .github/actions/cache-build/action.yml
Original file line number Diff line number Diff line change
@@ -1,31 +1,23 @@
name: Cache production build binaries
description: "Cache or restore if necessary"
inputs:
node_version:
required: false
default: v18.x
runs:
using: "composite"
steps:
- name: Generate cache key
id: cache-key
uses: ./.github/actions/cache-build-key
with:
branch_key: ${{ github.head_ref || github.ref_name }}
- name: Cache production build
uses: actions/cache@v4
id: cache-build
env:
cache-name: prod-build
# Use branch name for cache scoping (head_ref for PRs, ref_name for pushes)
branch-key: ${{ github.head_ref || github.ref_name }}
# Hash yarn.lock for dependency changes
lockfile-hash: ${{ hashFiles('yarn.lock') }}
# Hash source files that affect the web build, excluding generated directories
# Excludes packages/prisma/{generated,client,zod}/* and packages/kysely/* which are generated by prisma generate
source-hash: ${{ hashFiles('apps/**/**.[jt]s', 'apps/**/**.[jt]sx', 'apps/**/*.json', 'apps/**/*.css', 'packages/**/**.[jt]s', 'packages/**/**.[jt]sx', 'packages/prisma/schema.prisma', 'packages/prisma/migrations/**/*.sql', '!**/node_modules/**', '!packages/prisma/generated/**', '!packages/prisma/client/**', '!packages/prisma/zod/**', '!packages/kysely/**') }}
with:
path: |
${{ github.workspace }}/apps/web/.next
${{ github.workspace }}/apps/web/public/embed
**/.turbo/**
**/dist/**
key: ${{ runner.os }}-${{ env.cache-name }}-${{ inputs.node_version }}-${{ env.branch-key }}-${{ env.lockfile-hash }}-${{ env.source-hash }}
key: ${{ steps.cache-key.outputs.key }}
- run: |
export NODE_OPTIONS="--max_old_space_size=8192"
yarn build
Expand Down
54 changes: 54 additions & 0 deletions .github/actions/yarn-install/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ inputs:
node_version:
required: false
default: v20.x
skip-install-if-cache-hit:
description: "Skip yarn install if node_modules cache is hit. Use this for jobs that only need to check that cache exists."
required: false
default: "false"

runs:
using: "composite"
Expand All @@ -29,9 +33,56 @@ runs:
run: |
echo "CACHE_FOLDER=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT

# When skip-install-if-cache-hit is true, first check if all caches exist without downloading (lookup-only mode)
# This avoids downloading ~1.2GB of cache data when we just want to verify caches exist
- name: Check yarn cache (lookup-only)
if: ${{ inputs.skip-install-if-cache-hit == 'true' }}
uses: actions/cache/restore@v4
id: yarn-download-cache-check
with:
path: ${{ steps.yarn-config.outputs.CACHE_FOLDER }}
key: yarn-download-cache-${{ hashFiles('yarn.lock') }}
lookup-only: true

- name: Check node_modules cache (lookup-only)
if: ${{ inputs.skip-install-if-cache-hit == 'true' }}
uses: actions/cache/restore@v4
id: yarn-nm-cache-check
with:
path: |
**/node_modules/
!**/.next/node_modules/
key: ${{ runner.os }}-yarn-nm-cache-${{ hashFiles('yarn.lock', '.yarnrc.yml') }}
lookup-only: true

- name: Check yarn install state cache (lookup-only)
if: ${{ inputs.skip-install-if-cache-hit == 'true' }}
uses: actions/cache/restore@v4
id: yarn-install-state-cache-check
with:
path: .yarn/ci-cache/
key: ${{ runner.os }}-yarn-install-state-cache-${{ hashFiles('yarn.lock', '.yarnrc.yml') }}
lookup-only: true

# Compute whether all caches hit (only in skip mode)
- name: Check if all caches hit
if: ${{ inputs.skip-install-if-cache-hit == 'true' }}
id: all-caches-check
shell: bash
run: |
if [[ "${{ steps.yarn-download-cache-check.outputs.cache-hit }}" == "true" && \
"${{ steps.yarn-nm-cache-check.outputs.cache-hit }}" == "true" && \
"${{ steps.yarn-install-state-cache-check.outputs.cache-hit }}" == "true" ]]; then
echo "all-hit=true" >> $GITHUB_OUTPUT
else
echo "all-hit=false" >> $GITHUB_OUTPUT
fi

# Yarn rotates the downloaded cache archives, @see https://github.com/actions/setup-node/issues/325
# Yarn cache is also reusable between arch and os.
# Only restore if not in skip mode, or if skip mode but any cache check missed
- name: Restore yarn cache
if: ${{ inputs.skip-install-if-cache-hit != 'true' || steps.all-caches-check.outputs.all-hit != 'true' }}
uses: actions/cache@v4
id: yarn-download-cache
with:
Expand All @@ -40,6 +91,7 @@ runs:

# Invalidated on yarn.lock changes
- name: Restore node_modules
if: ${{ inputs.skip-install-if-cache-hit != 'true' || steps.all-caches-check.outputs.all-hit != 'true' }}
id: yarn-nm-cache
uses: actions/cache@v4
with:
Expand All @@ -50,13 +102,15 @@ runs:

# Invalidated on yarn.lock changes
- name: Restore yarn install state
if: ${{ inputs.skip-install-if-cache-hit != 'true' || steps.all-caches-check.outputs.all-hit != 'true' }}
id: yarn-install-state-cache
uses: actions/cache@v4
with:
path: .yarn/ci-cache/
key: ${{ runner.os }}-yarn-install-state-cache-${{ hashFiles('yarn.lock', '.yarnrc.yml') }}

- name: Install dependencies
if: ${{ inputs.skip-install-if-cache-hit != 'true' || steps.all-caches-check.outputs.all-hit != 'true' }}
shell: bash
run: |
yarn install --inline-builds
Expand Down
26 changes: 25 additions & 1 deletion .github/actions/yarn-playwright-install/action.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
name: Install playwright binaries
description: "Install playwright, cache and restore if necessary"
inputs:
skip-install-if-cache-hit:
description: "Skip playwright install if cache is hit. Use this for jobs that only need to check that cache exists."
required: false
default: "false"
runs:
using: "composite"
steps:
- name: Get installed Playwright version
shell: bash
id: playwright-version
run: echo "PLAYWRIGHT_VERSION=$(node -e "console.log(require('./package.json').devDependencies['@playwright/test'])")" >> $GITHUB_ENV

# When skip-install-if-cache-hit is true, first check if cache exists without downloading (lookup-only mode)
- name: Check playwright cache (lookup-only)
if: ${{ inputs.skip-install-if-cache-hit == 'true' }}
id: playwright-cache-check
uses: actions/cache/restore@v4
with:
path: |
~/Library/Caches/ms-playwright
~/.cache/ms-playwright
${{ github.workspace }}/node_modules/playwright
key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
lookup-only: true

# Only restore cache if not in skip mode, or if skip mode but cache check missed
- name: Cache playwright binaries
if: ${{ inputs.skip-install-if-cache-hit != 'true' || steps.playwright-cache-check.outputs.cache-hit != 'true' }}
id: playwright-cache
uses: actions/cache@v4
with:
Expand All @@ -16,7 +37,10 @@ runs:
~/.cache/ms-playwright
${{ github.workspace }}/node_modules/playwright
key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}

# In default mode: run if playwright-cache missed
# In skip mode: run if playwright-cache-check missed (but cache restore step was also skipped)
- name: Yarn playwright install
if: steps.playwright-cache.outputs.cache-hit != 'true'
if: ${{ (inputs.skip-install-if-cache-hit != 'true' && steps.playwright-cache.outputs.cache-hit != 'true') || (inputs.skip-install-if-cache-hit == 'true' && steps.playwright-cache-check.outputs.cache-hit != 'true') }}
shell: bash
run: yarn playwright install --with-deps
20 changes: 18 additions & 2 deletions .github/workflows/delete-blacksmith-cache.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
name: Manually Delete Blacksmith Cache
name: Delete Blacksmith Cache
on:
workflow_dispatch:
inputs:
cache_key:
description: "Blacksmith Cache Key to Delete"
required: true
type: string
pull_request:
types: [closed]

jobs:
manually-delete-blacksmith-cache:
if: github.event_name == 'workflow_dispatch'
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: useblacksmith/cache-delete@v1
with:
cache_key: ${{ inputs.cache_key }}
key: ${{ inputs.cache_key }}

delete-cache-build-on-pr-close:
if: github.event_name == 'pull_request' && github.event.action == 'closed'
runs-on: blacksmith-2vcpu-ubuntu-2404
env:
CACHE_NAME: prod-build
steps:
- name: Delete cache-build cache
uses: useblacksmith/cache-delete@v1
with:
key: ${{ env.CACHE_NAME }}-${{ github.event.pull_request.head.ref }}
prefix: "true"
4 changes: 4 additions & 0 deletions .github/workflows/yarn-install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ jobs:
- uses: actions/checkout@v4
- uses: ./.github/actions/dangerous-git-checkout
- uses: ./.github/actions/yarn-install
with:
skip-install-if-cache-hit: "true"
- uses: ./.github/actions/yarn-playwright-install
with:
skip-install-if-cache-hit: "true"
4 changes: 3 additions & 1 deletion apps/api/v1/test/lib/bookings/_post.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,9 @@ describe("POST /api/bookings", () => {

const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
method: "POST",
body: {},
body: {
eventTypeId: 2,
},
});

await handler(req, res);
Expand Down
29 changes: 18 additions & 11 deletions apps/web/app/api/defaultResponderForAppDir.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ import { TRPCError } from "@trpc/server";

import { defaultResponderForAppDir } from "./defaultResponderForAppDir";

vi.mock("next/server", () => ({
NextRequest: class MockNextRequest {},
NextResponse: {
json: vi.fn((body, init) => ({
json: vi.fn().mockResolvedValue(body),
status: init?.status || 200,
})),
},
}));
vi.mock("next/server", () => {
class MockNextRequest extends Request {}
return {
NextRequest: MockNextRequest,
NextResponse: {
json: vi.fn((body, init) => ({
json: vi.fn().mockResolvedValue(body),
status: init?.status || 200,
})),
},
};
});

describe("defaultResponderForAppDir", () => {
it("should return a JSON response when handler resolves with a result", async () => {
Expand Down Expand Up @@ -44,7 +47,9 @@ describe("defaultResponderForAppDir", () => {
});

it("should respond with status code 409 for NoAvailableUsersFound", async () => {
const f = vi.fn().mockRejectedValue(new Error(ErrorCode.NoAvailableUsersFound));
const f = vi
.fn()
.mockRejectedValue(new Error(ErrorCode.NoAvailableUsersFound));
const req = { method: "GET", url: "/api/test" } as unknown as NextRequest;
const params = Promise.resolve<Params>({});

Expand All @@ -60,7 +65,9 @@ describe("defaultResponderForAppDir", () => {
});

it("should respond with a 429 status code for rate limit errors", async () => {
const f = vi.fn().mockRejectedValue(new TRPCError({ code: "TOO_MANY_REQUESTS" }));
const f = vi
.fn()
.mockRejectedValue(new TRPCError({ code: "TOO_MANY_REQUESTS" }));
const req = { method: "POST", url: "/api/test" } as unknown as NextRequest;
const params = Promise.resolve<Params>({});

Expand Down
14 changes: 7 additions & 7 deletions apps/web/modules/shell/UpgradeTip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,18 @@ export function UpgradeTip({
return (
<>
<div className="relative flex min-h-[295px] w-full items-center justify-between overflow-hidden rounded-lg pb-10">
<picture className="absolute min-h-[295px] w-full rounded-lg object-cover">
<source srcSet={imageSrc} media="(prefers-color-scheme: dark)" />
<picture className="absolute min-h-full w-full rounded-lg object-cover">
{" "}
<source srcSet={imageSrc} media="(prefers-color-scheme: dark)" />
<img
className="absolute min-h-[295px] w-full select-none rounded-lg object-cover object-left md:object-center"
src={imageSrc}
className="absolute min-h-full w-full select-none rounded-lg object-cover object-left md:object-center" src={imageSrc}
loading="lazy"
alt={title}
/>
</picture>
<div className="relative my-4 px-8 pb-4 sm:px-14">
<h1 className={classNames("font-cal mt-4 text-3xl")}>{title}</h1>
<p className={classNames("mb-8 mt-4 max-w-sm")}>{description}</p>
<div className="relative my-4 w-full px-4 pb-4 sm:px-8 md:px-14">
<h1 className={classNames("font-cal mt-4 text-2xl sm:text-3xl")}>{title}</h1>
<p className={classNames("mb-8 mt-4 max-w-sm text-sm sm:text-base")}>{description}</p>
{buttons}
</div>
</div>
Expand Down
17 changes: 17 additions & 0 deletions apps/web/modules/users/components/UserTable/UserListTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ const initalColumnVisibility = {
teams: true,
createdAt: false,
updatedAt: false,
completedOnboarding: false,
twoFactorEnabled: false,
actions: true,
};
Expand Down Expand Up @@ -418,6 +419,22 @@ function UserListTableContent({
},
cell: ({ row }) => <div>{row.original.updatedAt || ""}</div>,
},
{
id: "completedOnboarding",
accessorKey: "completedOnboarding",
header: t("completed_onboarding"),
enableSorting: false,
enableColumnFilter: false,
size: 80,
cell: ({ row }) => {
const { completedOnboarding } = row.original;
return (
<Badge variant={completedOnboarding ? "green" : "gray"}>
{completedOnboarding ? t("yes") : t("no")}
</Badge>
);
},
},
{
id: "twoFactorEnabled",
accessorKey: "twoFactorEnabled",
Expand Down
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@
},
"devDependencies": {
"@babel/core": "7.26.10",
"@biomejs/biome": "2.3.10",
"@calcom/config": "workspace:*",
"@calcom/types": "workspace:*",
"@microsoft/microsoft-graph-types-beta": "0.15.0-preview",
Expand Down
Loading
Loading