Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e85fc501a6 | |||
| 0bfeb0816f | |||
| b207601732 | |||
| 3913e57ef4 | |||
| 26f310397a | |||
| 0a845767a0 | |||
| ed2a26c865 |
@@ -0,0 +1,9 @@
|
||||
---
|
||||
"trigger.dev": patch
|
||||
"@trigger.dev/core": patch
|
||||
---
|
||||
|
||||
- Fix additionalFiles that aren't decendants
|
||||
- Stop swallowing uncaught exceptions in prod
|
||||
- Improve warnings and errors, fail early on critical warnings
|
||||
- New arg to --save-logs even for successful builds
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"trigger.dev": patch
|
||||
---
|
||||
|
||||
v3 CLI update command and package manager detection fix
|
||||
@@ -44,6 +44,8 @@
|
||||
"@trigger.dev/yalt": "2.3.18"
|
||||
},
|
||||
"changesets": [
|
||||
"angry-eagles-trade",
|
||||
"beige-pens-dance",
|
||||
"breezy-gorillas-mate",
|
||||
"chilled-hornets-move",
|
||||
"clean-pianos-listen",
|
||||
|
||||
@@ -2,8 +2,14 @@ import { Fragment } from "react";
|
||||
import { Modifier, ShortcutDefinition } from "~/hooks/useShortcutKeys";
|
||||
import { cn } from "~/utils/cn";
|
||||
import { useOperatingSystem } from "./OperatingSystemProvider";
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
ChevronUpIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
|
||||
const variants = {
|
||||
export const variants = {
|
||||
small:
|
||||
"text-[0.6rem] font-medium min-w-[17px] rounded-[2px] px-1 ml-1 -mr-0.5 grid place-content-center border border-dimmed/40 text-text-dimmed group-hover:text-text-bright/80 group-hover:border-dimmed/60 transition uppercase",
|
||||
medium:
|
||||
@@ -23,7 +29,7 @@ export function ShortcutKey({ shortcut, variant, className }: ShortcutKeyProps)
|
||||
const isMac = platform === "mac";
|
||||
let relevantShortcut = "mac" in shortcut ? (isMac ? shortcut.mac : shortcut.windows) : shortcut;
|
||||
const modifiers = relevantShortcut.modifiers ?? [];
|
||||
const character = keyString(relevantShortcut.key, isMac);
|
||||
const character = keyString(relevantShortcut.key, isMac, variant);
|
||||
|
||||
return (
|
||||
<span className={cn(variants[variant], className)}>
|
||||
@@ -35,10 +41,22 @@ export function ShortcutKey({ shortcut, variant, className }: ShortcutKeyProps)
|
||||
);
|
||||
}
|
||||
|
||||
function keyString(key: String, isMac: boolean) {
|
||||
function keyString(key: String, isMac: boolean, size: "small" | "medium") {
|
||||
key = key.toLowerCase();
|
||||
|
||||
const className = size === "small" ? "w-2.5 h-4" : "w-3 h-5";
|
||||
|
||||
switch (key) {
|
||||
case "enter":
|
||||
return isMac ? "↵" : key;
|
||||
case "arrowdown":
|
||||
return <ChevronDownIcon className={className} />;
|
||||
case "arrowup":
|
||||
return <ChevronUpIcon className={className} />;
|
||||
case "arrowleft":
|
||||
return <ChevronLeftIcon className={className} />;
|
||||
case "arrowright":
|
||||
return <ChevronRightIcon className={className} />;
|
||||
default:
|
||||
return key;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { VirtualItem, Virtualizer, useVirtualizer } from "@tanstack/react-virtual";
|
||||
import { motion } from "framer-motion";
|
||||
import { MutableRefObject, RefObject, useCallback, useEffect, useReducer, useRef } from "react";
|
||||
import { UnmountClosed } from "react-collapse";
|
||||
import { cn } from "~/utils/cn";
|
||||
import { NodeState, NodesState, reducer } from "./reducer";
|
||||
import { applyFilterToState, concreteStateFromInput, selectedIdFromState } from "./utils";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
export type TreeViewProps<TData> = {
|
||||
tree: FlatTree<TData>;
|
||||
@@ -165,6 +165,11 @@ export type UseTreeStateOutput = {
|
||||
expandNode: (id: string, scrollToNode?: boolean) => void;
|
||||
collapseNode: (id: string) => void;
|
||||
toggleExpandNode: (id: string, scrollToNode?: boolean) => void;
|
||||
expandAllBelowDepth: (depth: number) => void;
|
||||
collapseAllBelowDepth: (depth: number) => void;
|
||||
expandLevel: (level: number) => void;
|
||||
collapseLevel: (level: number) => void;
|
||||
toggleExpandLevel: (level: number) => void;
|
||||
selectFirstVisibleNode: (scrollToNode?: boolean) => void;
|
||||
selectLastVisibleNode: (scrollToNode?: boolean) => void;
|
||||
selectNextVisibleNode: (scrollToNode?: boolean) => void;
|
||||
@@ -333,6 +338,41 @@ export function useTree<TData>({
|
||||
[state]
|
||||
);
|
||||
|
||||
const expandAllBelowDepth = useCallback(
|
||||
(depth: number) => {
|
||||
dispatch({ type: "EXPAND_ALL_BELOW_DEPTH", payload: { tree, depth } });
|
||||
},
|
||||
[state]
|
||||
);
|
||||
|
||||
const collapseAllBelowDepth = useCallback(
|
||||
(depth: number) => {
|
||||
dispatch({ type: "COLLAPSE_ALL_BELOW_DEPTH", payload: { tree, depth } });
|
||||
},
|
||||
[state]
|
||||
);
|
||||
|
||||
const expandLevel = useCallback(
|
||||
(level: number) => {
|
||||
dispatch({ type: "EXPAND_LEVEL", payload: { tree, level } });
|
||||
},
|
||||
[state]
|
||||
);
|
||||
|
||||
const collapseLevel = useCallback(
|
||||
(level: number) => {
|
||||
dispatch({ type: "COLLAPSE_LEVEL", payload: { tree, level } });
|
||||
},
|
||||
[state]
|
||||
);
|
||||
|
||||
const toggleExpandLevel = useCallback(
|
||||
(level: number) => {
|
||||
dispatch({ type: "TOGGLE_EXPAND_LEVEL", payload: { tree, level } });
|
||||
},
|
||||
[state]
|
||||
);
|
||||
|
||||
const getTreeProps = useCallback(() => {
|
||||
return {
|
||||
role: "tree",
|
||||
@@ -368,25 +408,48 @@ export function useTree<TData>({
|
||||
}
|
||||
case "Left":
|
||||
case "ArrowLeft": {
|
||||
e.preventDefault();
|
||||
|
||||
const selected = selectedIdFromState(state.nodes);
|
||||
if (selected) {
|
||||
const treeNode = tree.find((node) => node.id === selected);
|
||||
if (treeNode && treeNode.hasChildren && state.nodes[selected].expanded) {
|
||||
|
||||
if (e.altKey) {
|
||||
if (treeNode && treeNode.hasChildren) {
|
||||
collapseLevel(treeNode.level);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const shouldCollapse =
|
||||
treeNode && treeNode.hasChildren && state.nodes[selected].expanded;
|
||||
if (shouldCollapse) {
|
||||
collapseNode(selected);
|
||||
} else {
|
||||
selectParentNode(true);
|
||||
}
|
||||
}
|
||||
e.preventDefault();
|
||||
|
||||
break;
|
||||
}
|
||||
case "Right":
|
||||
case "ArrowRight": {
|
||||
e.preventDefault();
|
||||
|
||||
const selected = selectedIdFromState(state.nodes);
|
||||
|
||||
if (selected) {
|
||||
const treeNode = tree.find((node) => node.id === selected);
|
||||
|
||||
if (e.altKey) {
|
||||
if (treeNode && treeNode.hasChildren) {
|
||||
expandLevel(treeNode.level);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
expandNode(selected, true);
|
||||
}
|
||||
e.preventDefault();
|
||||
break;
|
||||
}
|
||||
case "Escape": {
|
||||
@@ -427,6 +490,11 @@ export function useTree<TData>({
|
||||
expandNode,
|
||||
collapseNode,
|
||||
toggleExpandNode,
|
||||
expandAllBelowDepth,
|
||||
collapseAllBelowDepth,
|
||||
expandLevel,
|
||||
collapseLevel,
|
||||
toggleExpandLevel,
|
||||
selectFirstVisibleNode,
|
||||
selectLastVisibleNode,
|
||||
selectNextVisibleNode,
|
||||
|
||||
@@ -91,6 +91,46 @@ type ToggleExpandNodeAction = {
|
||||
} & WithScrollToNode;
|
||||
};
|
||||
|
||||
type ExpandAllBelowDepthAction = {
|
||||
type: "EXPAND_ALL_BELOW_DEPTH";
|
||||
payload: {
|
||||
depth: number;
|
||||
tree: FlatTree<any>;
|
||||
};
|
||||
};
|
||||
|
||||
type CollapseAllBelowDepthAction = {
|
||||
type: "COLLAPSE_ALL_BELOW_DEPTH";
|
||||
payload: {
|
||||
depth: number;
|
||||
tree: FlatTree<any>;
|
||||
};
|
||||
};
|
||||
|
||||
type ExpandLevelAction = {
|
||||
type: "EXPAND_LEVEL";
|
||||
payload: {
|
||||
level: number;
|
||||
tree: FlatTree<any>;
|
||||
};
|
||||
};
|
||||
|
||||
type CollapseLevelAction = {
|
||||
type: "COLLAPSE_LEVEL";
|
||||
payload: {
|
||||
level: number;
|
||||
tree: FlatTree<any>;
|
||||
};
|
||||
};
|
||||
|
||||
type ToggleExpandLevelAction = {
|
||||
type: "TOGGLE_EXPAND_LEVEL";
|
||||
payload: {
|
||||
level: number;
|
||||
tree: FlatTree<any>;
|
||||
};
|
||||
};
|
||||
|
||||
type SelectFirstVisibleNodeAction = {
|
||||
type: "SELECT_FIRST_VISIBLE_NODE";
|
||||
payload: {
|
||||
@@ -135,6 +175,11 @@ export type Action =
|
||||
| ExpandNodeAction
|
||||
| CollapseNodeAction
|
||||
| ToggleExpandNodeAction
|
||||
| ExpandAllBelowDepthAction
|
||||
| CollapseAllBelowDepthAction
|
||||
| ExpandLevelAction
|
||||
| CollapseLevelAction
|
||||
| ToggleExpandLevelAction
|
||||
| SelectFirstVisibleNodeAction
|
||||
| SelectLastVisibleNodeAction
|
||||
| SelectNextVisibleNodeAction
|
||||
@@ -229,6 +274,109 @@ export function reducer(state: TreeState, action: Action): TreeState {
|
||||
});
|
||||
}
|
||||
}
|
||||
case "EXPAND_ALL_BELOW_DEPTH": {
|
||||
const nodesToExpand = action.payload.tree.filter(
|
||||
(n) => n.level >= action.payload.depth && n.hasChildren
|
||||
);
|
||||
|
||||
const newNodes = Object.fromEntries(
|
||||
Object.entries(state.nodes).map(([key, value]) => [
|
||||
key,
|
||||
{
|
||||
...value,
|
||||
expanded: nodesToExpand.find((n) => n.id === key) ? true : value.expanded,
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
const visibleNodes = applyVisibility(action.payload.tree, newNodes);
|
||||
return { nodes: visibleNodes, changes: generateChanges(state.nodes, visibleNodes) };
|
||||
}
|
||||
case "COLLAPSE_ALL_BELOW_DEPTH": {
|
||||
const nodesToCollapse = action.payload.tree.filter(
|
||||
(n) => n.level >= action.payload.depth && n.hasChildren
|
||||
);
|
||||
|
||||
const newNodes = Object.fromEntries(
|
||||
Object.entries(state.nodes).map(([key, value]) => [
|
||||
key,
|
||||
{
|
||||
...value,
|
||||
expanded: nodesToCollapse.find((n) => n.id === key) ? false : value.expanded,
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
const visibleNodes = applyVisibility(action.payload.tree, newNodes);
|
||||
return { nodes: visibleNodes, changes: generateChanges(state.nodes, visibleNodes) };
|
||||
}
|
||||
case "EXPAND_LEVEL": {
|
||||
const nodesToExpand = action.payload.tree.filter(
|
||||
(n) => n.level <= action.payload.level && n.hasChildren
|
||||
);
|
||||
|
||||
const newNodes = Object.fromEntries(
|
||||
Object.entries(state.nodes).map(([key, value]) => [
|
||||
key,
|
||||
{
|
||||
...value,
|
||||
expanded: nodesToExpand.find((n) => n.id === key) ? true : value.expanded,
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
const visibleNodes = applyVisibility(action.payload.tree, newNodes);
|
||||
return { nodes: visibleNodes, changes: generateChanges(state.nodes, visibleNodes) };
|
||||
}
|
||||
case "COLLAPSE_LEVEL": {
|
||||
const nodesToCollapse = action.payload.tree.filter(
|
||||
(n) => n.level === action.payload.level && n.hasChildren
|
||||
);
|
||||
|
||||
const newNodes = Object.fromEntries(
|
||||
Object.entries(state.nodes).map(([key, value]) => [
|
||||
key,
|
||||
{
|
||||
...value,
|
||||
expanded: nodesToCollapse.find((n) => n.id === key) ? false : value.expanded,
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
const visibleNodes = applyVisibility(action.payload.tree, newNodes);
|
||||
return { nodes: visibleNodes, changes: generateChanges(state.nodes, visibleNodes) };
|
||||
}
|
||||
case "TOGGLE_EXPAND_LEVEL": {
|
||||
//first get the first item at that level in the tree. If it is expanded, collapse all nodes at that level
|
||||
//if it is collapsed, expand all nodes at that level
|
||||
const nodesAtLevel = action.payload.tree.filter(
|
||||
(n) => n.level === action.payload.level && n.hasChildren
|
||||
);
|
||||
const firstNode = nodesAtLevel[0];
|
||||
if (!firstNode) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const currentlyExpanded = state.nodes[firstNode.id]?.expanded ?? true;
|
||||
const currentVisible = state.nodes[firstNode.id]?.visible ?? true;
|
||||
if (currentlyExpanded && currentVisible) {
|
||||
return reducer(state, {
|
||||
type: "COLLAPSE_LEVEL",
|
||||
payload: {
|
||||
level: action.payload.level,
|
||||
tree: action.payload.tree,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return reducer(state, {
|
||||
type: "EXPAND_LEVEL",
|
||||
payload: {
|
||||
level: action.payload.level,
|
||||
tree: action.payload.tree,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
case "SELECT_FIRST_VISIBLE_NODE": {
|
||||
const node = firstVisibleNode(action.payload.tree, state.nodes);
|
||||
if (node) {
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import { ArrowPathIcon, StopCircleIcon } from "@heroicons/react/20/solid";
|
||||
import { StopIcon } from "@heroicons/react/24/outline";
|
||||
import { BeakerIcon, BookOpenIcon, CheckIcon } from "@heroicons/react/24/solid";
|
||||
import { useLocation } from "@remix-run/react";
|
||||
import { formatDuration } from "@trigger.dev/core/v3";
|
||||
import { User } from "@trigger.dev/database";
|
||||
import { Button, LinkButton } from "~/components/primitives/Buttons";
|
||||
import { Dialog, DialogTrigger } from "~/components/primitives/Dialog";
|
||||
import { useEnvironments } from "~/hooks/useEnvironments";
|
||||
import { useOrganization } from "~/hooks/useOrganizations";
|
||||
import { useProject } from "~/hooks/useProject";
|
||||
import { RunListAppliedFilters, RunListItem } from "~/presenters/v3/RunListPresenter.server";
|
||||
import { docsPath, v3RunPath, v3TestPath } from "~/utils/pathBuilder";
|
||||
import { docsPath, v3RunSpanPath, v3TestPath } from "~/utils/pathBuilder";
|
||||
import { EnvironmentLabel } from "../../environments/EnvironmentLabel";
|
||||
import { DateTime } from "../../primitives/DateTime";
|
||||
import { Paragraph } from "../../primitives/Paragraph";
|
||||
@@ -14,21 +20,14 @@ import {
|
||||
TableBlankRow,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableCellChevron,
|
||||
TableCellMenu,
|
||||
TableHeader,
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
} from "../../primitives/Table";
|
||||
import { formatDuration } from "@trigger.dev/core/v3";
|
||||
import { TaskRunStatusCombo } from "./TaskRunStatus";
|
||||
import { useEnvironments } from "~/hooks/useEnvironments";
|
||||
import { Button, LinkButton } from "~/components/primitives/Buttons";
|
||||
import { ArrowPathIcon, StopCircleIcon } from "@heroicons/react/20/solid";
|
||||
import { Dialog, DialogTrigger } from "~/components/primitives/Dialog";
|
||||
import { CancelRunDialog } from "./CancelRunDialog";
|
||||
import { useLocation } from "@remix-run/react";
|
||||
import { ReplayRunDialog } from "./ReplayRunDialog";
|
||||
import { TaskRunStatusCombo } from "./TaskRunStatus";
|
||||
|
||||
type RunsTableProps = {
|
||||
total: number;
|
||||
@@ -78,7 +77,7 @@ export function TaskRunsTable({
|
||||
<BlankState isLoading={isLoading} filters={filters} />
|
||||
) : (
|
||||
runs.map((run) => {
|
||||
const path = v3RunPath(organization, project, run);
|
||||
const path = v3RunSpanPath(organization, project, run, { spanId: run.spanId });
|
||||
const usernameForEnv =
|
||||
currentUser.id !== run.environment.userId ? run.environment.userName : undefined;
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { useCallback, useState } from "react";
|
||||
import { useOptimisticLocation } from "./useOptimisticLocation";
|
||||
import type { Location } from "@remix-run/react";
|
||||
|
||||
export function useReplaceLocation() {
|
||||
const optimisticLocation = useOptimisticLocation();
|
||||
const [location, setLocation] = useState(optimisticLocation);
|
||||
|
||||
const replaceLocation = useCallback((location: Location<any>) => {
|
||||
const fullPath = location.pathname + location.search + location.hash;
|
||||
//replace the URL in the browser
|
||||
history.replaceState(null, "", fullPath);
|
||||
//update the state (new object in case the same location ref was modified)
|
||||
const newLocation = { ...location };
|
||||
setLocation(newLocation);
|
||||
}, []);
|
||||
|
||||
const replaceSearchParam = useCallback(
|
||||
(key: string, value?: string) => {
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
if (value) {
|
||||
searchParams.set(key, value);
|
||||
} else {
|
||||
searchParams.delete(key);
|
||||
}
|
||||
replaceLocation({ ...optimisticLocation, search: "?" + searchParams.toString() });
|
||||
},
|
||||
[optimisticLocation, replaceLocation]
|
||||
);
|
||||
|
||||
return { location, replaceLocation, replaceSearchParam };
|
||||
}
|
||||
@@ -106,6 +106,7 @@ export class RunListPresenter {
|
||||
lockedAt: Date | null;
|
||||
completedAt: Date | null;
|
||||
isTest: boolean;
|
||||
spanId: string;
|
||||
attempts: BigInt;
|
||||
}[]
|
||||
>`
|
||||
@@ -121,6 +122,7 @@ export class RunListPresenter {
|
||||
tr."lockedAt" AS "lockedAt",
|
||||
tra."completedAt" AS "completedAt",
|
||||
tr."isTest" AS "isTest",
|
||||
tr."spanId" AS "spanId",
|
||||
COUNT(tra.id) AS attempts
|
||||
FROM
|
||||
${sqlDatabaseSchema}."TaskRun" tr
|
||||
@@ -225,6 +227,7 @@ export class RunListPresenter {
|
||||
status: run.status,
|
||||
version: run.version,
|
||||
taskIdentifier: run.taskIdentifier,
|
||||
spanId: run.spanId,
|
||||
attempts: Number(run.attempts),
|
||||
isReplayable: true,
|
||||
isCancellable: CANCELLABLE_STATUSES.includes(run.status),
|
||||
|
||||
+2
-2
@@ -60,7 +60,7 @@ export default function Page() {
|
||||
const user = useUser();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
|
||||
{job.hasIntegrationsRequiringAction && (
|
||||
<Callout variant="error" to={organizationIntegrationsPath(organization)} className="mb-2">
|
||||
{simplur`This Job has ${
|
||||
@@ -96,6 +96,6 @@ export default function Page() {
|
||||
</div>
|
||||
)}
|
||||
</Help>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+209
-89
@@ -1,10 +1,13 @@
|
||||
import {
|
||||
ArrowsPointingInIcon,
|
||||
ArrowsPointingOutIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronRightIcon,
|
||||
MagnifyingGlassMinusIcon,
|
||||
MagnifyingGlassPlusIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
import { Outlet, useNavigate, useParams, useRevalidator } from "@remix-run/react";
|
||||
import type { Location } from "@remix-run/react";
|
||||
import { useParams, useRevalidator } from "@remix-run/react";
|
||||
import { LoaderFunctionArgs } from "@remix-run/server-runtime";
|
||||
import { Virtualizer } from "@tanstack/react-virtual";
|
||||
import {
|
||||
@@ -23,7 +26,7 @@ import { InlineCode } from "~/components/code/InlineCode";
|
||||
import { EnvironmentLabel } from "~/components/environments/EnvironmentLabel";
|
||||
import { MainCenteredContainer, PageBody } from "~/components/layout/AppLayout";
|
||||
import { Badge } from "~/components/primitives/Badge";
|
||||
import { LinkButton } from "~/components/primitives/Buttons";
|
||||
import { Button, LinkButton } from "~/components/primitives/Buttons";
|
||||
import { Callout } from "~/components/primitives/Callout";
|
||||
import { Input } from "~/components/primitives/Input";
|
||||
import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader";
|
||||
@@ -33,6 +36,7 @@ import {
|
||||
ResizablePanel,
|
||||
ResizablePanelGroup,
|
||||
} from "~/components/primitives/Resizable";
|
||||
import { ShortcutKey, variants } from "~/components/primitives/ShortcutKey";
|
||||
import { Slider } from "~/components/primitives/Slider";
|
||||
import { Switch } from "~/components/primitives/Switch";
|
||||
import * as Timeline from "~/components/primitives/Timeline";
|
||||
@@ -45,8 +49,9 @@ import { useDebounce } from "~/hooks/useDebounce";
|
||||
import { useEventSource } from "~/hooks/useEventSource";
|
||||
import { useInitialDimensions } from "~/hooks/useInitialDimensions";
|
||||
import { useOrganization } from "~/hooks/useOrganizations";
|
||||
import { usePathName } from "~/hooks/usePathName";
|
||||
import { useProject } from "~/hooks/useProject";
|
||||
import { useReplaceLocation } from "~/hooks/useReplaceLocation";
|
||||
import { Shortcut, useShortcutKeys } from "~/hooks/useShortcutKeys";
|
||||
import { useUser } from "~/hooks/useUser";
|
||||
import { RunEvent, RunPresenter } from "~/presenters/v3/RunPresenter.server";
|
||||
import { getResizableRunSettings, setResizableRunSettings } from "~/services/resizablePanel";
|
||||
@@ -60,6 +65,11 @@ import {
|
||||
v3RunStreamingPath,
|
||||
v3RunsPath,
|
||||
} from "~/utils/pathBuilder";
|
||||
import { SpanView } from "../resources.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam.spans.$spanParam/route";
|
||||
import { number } from "zod";
|
||||
import { useHotkeys } from "react-hotkeys-hook";
|
||||
import { Popover, PopoverArrowTrigger, PopoverContent } from "~/components/primitives/Popover";
|
||||
import { Header3 } from "~/components/primitives/Headers";
|
||||
|
||||
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
const userId = await requireUserId(request);
|
||||
@@ -82,19 +92,18 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
});
|
||||
};
|
||||
|
||||
function getSpanId(path: string): string | undefined {
|
||||
const regex = /spans\/([^\/]*)/;
|
||||
const match = path.match(regex);
|
||||
return match ? match[1] : undefined;
|
||||
function getSpanId(location: Location<any>): string | undefined {
|
||||
const search = new URLSearchParams(location.search);
|
||||
return search.get("span") ?? undefined;
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
const { run, trace, resizeSettings } = useTypedLoaderData<typeof loader>();
|
||||
const navigate = useNavigate();
|
||||
const organization = useOrganization();
|
||||
const pathName = usePathName();
|
||||
const project = useProject();
|
||||
const user = useUser();
|
||||
const { location, replaceSearchParam } = useReplaceLocation();
|
||||
const selectedSpanId = getSpanId(location);
|
||||
|
||||
const usernameForEnv = user.id !== run.environment.userId ? run.environment.userName : undefined;
|
||||
|
||||
@@ -133,10 +142,8 @@ export default function Page() {
|
||||
|
||||
const { events, parentRunFriendlyId, duration, rootSpanStatus, rootStartedAt } = trace;
|
||||
|
||||
const selectedSpanId = getSpanId(pathName);
|
||||
|
||||
const changeToSpan = useDebounce((selectedSpan: string) => {
|
||||
navigate(v3RunSpanPath(organization, project, run, { spanId: selectedSpan }));
|
||||
replaceSearchParam("span", selectedSpan);
|
||||
}, 250);
|
||||
|
||||
const revalidator = useRevalidator();
|
||||
@@ -166,62 +173,47 @@ export default function Page() {
|
||||
</NavBar>
|
||||
<PageBody scrollable={false}>
|
||||
<div className={cn("grid h-full max-h-full grid-cols-1 overflow-hidden")}>
|
||||
{selectedSpanId === undefined ? (
|
||||
<TasksTreeView
|
||||
selectedId={selectedSpanId}
|
||||
key={events[0]?.id ?? "-"}
|
||||
events={events}
|
||||
parentRunFriendlyId={parentRunFriendlyId}
|
||||
onSelectedIdChanged={(selectedSpan) => {
|
||||
//instantly close the panel if no span is selected
|
||||
if (!selectedSpan) {
|
||||
navigate(v3RunPath(organization, project, run));
|
||||
return;
|
||||
}
|
||||
<ResizablePanelGroup
|
||||
direction="horizontal"
|
||||
className="h-full max-h-full"
|
||||
onLayout={(layout) => {
|
||||
if (layout.length !== 2) return;
|
||||
if (!selectedSpanId) return;
|
||||
setResizableRunSettings(document, layout);
|
||||
}}
|
||||
>
|
||||
<ResizablePanel order={1} minSize={30} defaultSize={resizeSettings.layout?.[0]}>
|
||||
<TasksTreeView
|
||||
selectedId={selectedSpanId}
|
||||
key={events[0]?.id ?? "-"}
|
||||
events={events}
|
||||
parentRunFriendlyId={parentRunFriendlyId}
|
||||
onSelectedIdChanged={(selectedSpan) => {
|
||||
//instantly close the panel if no span is selected
|
||||
if (!selectedSpan) {
|
||||
replaceSearchParam("span");
|
||||
return;
|
||||
}
|
||||
|
||||
changeToSpan(selectedSpan);
|
||||
}}
|
||||
totalDuration={duration}
|
||||
rootSpanStatus={rootSpanStatus}
|
||||
rootStartedAt={rootStartedAt}
|
||||
environmentType={run.environment.type}
|
||||
/>
|
||||
) : (
|
||||
<ResizablePanelGroup
|
||||
direction="horizontal"
|
||||
className="h-full max-h-full"
|
||||
onLayout={(layout) => {
|
||||
if (layout.length !== 2) return;
|
||||
setResizableRunSettings(document, layout);
|
||||
}}
|
||||
>
|
||||
<ResizablePanel order={1} minSize={30} defaultSize={resizeSettings.layout?.[0]}>
|
||||
<TasksTreeView
|
||||
selectedId={selectedSpanId}
|
||||
key={events[0]?.id ?? "-"}
|
||||
events={events}
|
||||
parentRunFriendlyId={parentRunFriendlyId}
|
||||
onSelectedIdChanged={(selectedSpan) => {
|
||||
//instantly close the panel if no span is selected
|
||||
if (!selectedSpan) {
|
||||
navigate(v3RunPath(organization, project, run));
|
||||
return;
|
||||
}
|
||||
|
||||
changeToSpan(selectedSpan);
|
||||
}}
|
||||
totalDuration={duration}
|
||||
rootSpanStatus={rootSpanStatus}
|
||||
rootStartedAt={rootStartedAt}
|
||||
environmentType={run.environment.type}
|
||||
changeToSpan(selectedSpan);
|
||||
}}
|
||||
totalDuration={duration}
|
||||
rootSpanStatus={rootSpanStatus}
|
||||
rootStartedAt={rootStartedAt}
|
||||
environmentType={run.environment.type}
|
||||
/>
|
||||
</ResizablePanel>
|
||||
<ResizableHandle withHandle />
|
||||
{selectedSpanId && (
|
||||
<ResizablePanel order={2} minSize={30} defaultSize={resizeSettings.layout?.[1]}>
|
||||
<SpanView
|
||||
runParam={run.friendlyId}
|
||||
spanId={selectedSpanId}
|
||||
closePanel={() => replaceSearchParam("span")}
|
||||
/>
|
||||
</ResizablePanel>
|
||||
<ResizableHandle withHandle />
|
||||
<ResizablePanel order={2} minSize={30} defaultSize={resizeSettings.layout?.[1]}>
|
||||
<Outlet key={selectedSpanId} />
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
)}
|
||||
)}
|
||||
</ResizablePanelGroup>
|
||||
</div>
|
||||
</PageBody>
|
||||
</>
|
||||
@@ -263,6 +255,9 @@ function TasksTreeView({
|
||||
getNodeProps,
|
||||
toggleNodeSelection,
|
||||
toggleExpandNode,
|
||||
expandAllBelowDepth,
|
||||
toggleExpandLevel,
|
||||
collapseAllBelowDepth,
|
||||
selectNode,
|
||||
scrollToNode,
|
||||
virtualizer,
|
||||
@@ -286,7 +281,7 @@ function TasksTreeView({
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="grid h-full grid-rows-[2.5rem_1fr] overflow-hidden">
|
||||
<div className="grid h-full grid-rows-[2.5rem_1fr_3.25rem] overflow-hidden">
|
||||
<div className="mx-3 flex items-center justify-between gap-2 border-b border-grid-dimmed">
|
||||
<Input
|
||||
placeholder="Search log"
|
||||
@@ -297,30 +292,12 @@ function TasksTreeView({
|
||||
onChange={(e) => setFilterText(e.target.value)}
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<LiveReloadingStatus rootSpanCompleted={rootSpanStatus !== "executing"} />
|
||||
<Switch
|
||||
variant="small"
|
||||
label="Errors only"
|
||||
checked={errorsOnly}
|
||||
onCheckedChange={(e) => setErrorsOnly(e.valueOf())}
|
||||
/>
|
||||
<Switch
|
||||
variant="small"
|
||||
label="Show durations"
|
||||
checked={showDurations}
|
||||
onCheckedChange={(e) => setShowDurations(e.valueOf())}
|
||||
/>
|
||||
<Slider
|
||||
variant={"tertiary"}
|
||||
className="w-20"
|
||||
LeadingIcon={MagnifyingGlassMinusIcon}
|
||||
TrailingIcon={MagnifyingGlassPlusIcon}
|
||||
value={[scale]}
|
||||
onValueChange={(value) => setScale(value[0])}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.05}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ResizablePanelGroup
|
||||
@@ -333,14 +310,15 @@ function TasksTreeView({
|
||||
{/* Tree list */}
|
||||
<ResizablePanel order={1} minSize={20} defaultSize={50} className="pl-3">
|
||||
<div className="grid h-full grid-rows-[2rem_1fr] overflow-hidden">
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center pr-2">
|
||||
{parentRunFriendlyId ? (
|
||||
<ShowParentLink runFriendlyId={parentRunFriendlyId} />
|
||||
) : (
|
||||
<Paragraph variant="small" className="text-charcoal-500">
|
||||
<Paragraph variant="small" className="flex-1 text-charcoal-500">
|
||||
This is the root task
|
||||
</Paragraph>
|
||||
)}
|
||||
<LiveReloadingStatus rootSpanCompleted={rootSpanStatus !== "executing"} />
|
||||
</div>
|
||||
<TreeView
|
||||
parentRef={parentRef}
|
||||
@@ -355,13 +333,13 @@ function TasksTreeView({
|
||||
<>
|
||||
<div
|
||||
className={cn(
|
||||
"delay-[25ms] flex h-8 cursor-pointer items-center overflow-hidden rounded-l-sm pr-2 transition-colors",
|
||||
"flex h-8 cursor-pointer items-center overflow-hidden rounded-l-sm pr-2",
|
||||
state.selected
|
||||
? "bg-grid-dimmed hover:bg-grid-bright"
|
||||
: "bg-transparent hover:bg-grid-dimmed"
|
||||
)}
|
||||
onClick={() => {
|
||||
toggleNodeSelection(node.id);
|
||||
selectNode(node.id);
|
||||
}}
|
||||
>
|
||||
<div className="flex h-8 items-center">
|
||||
@@ -379,7 +357,15 @@ function TasksTreeView({
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
toggleExpandNode(node.id);
|
||||
if (e.altKey) {
|
||||
if (state.expanded) {
|
||||
collapseAllBelowDepth(node.level);
|
||||
} else {
|
||||
expandAllBelowDepth(node.level);
|
||||
}
|
||||
} else {
|
||||
toggleExpandNode(node.id);
|
||||
}
|
||||
scrollToNode(node.id);
|
||||
}}
|
||||
>
|
||||
@@ -445,6 +431,50 @@ function TasksTreeView({
|
||||
/>
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
<div className="flex items-center justify-between gap-2 border-t border-grid-dimmed px-2">
|
||||
<div className="grow @container">
|
||||
<div className="hidden items-center gap-4 @[42rem]:flex">
|
||||
<KeyboardShortcuts
|
||||
expandAllBelowDepth={expandAllBelowDepth}
|
||||
collapseAllBelowDepth={collapseAllBelowDepth}
|
||||
toggleExpandLevel={toggleExpandLevel}
|
||||
setShowDurations={setShowDurations}
|
||||
/>
|
||||
</div>
|
||||
<div className="@[42rem]:hidden">
|
||||
<Popover>
|
||||
<PopoverArrowTrigger>Shortcuts</PopoverArrowTrigger>
|
||||
<PopoverContent
|
||||
className="min-w-[20rem] overflow-y-auto p-2 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
|
||||
align="start"
|
||||
>
|
||||
<Header3 spacing>Keyboard shortcuts</Header3>
|
||||
<div className="flex flex-col gap-2">
|
||||
<KeyboardShortcuts
|
||||
expandAllBelowDepth={expandAllBelowDepth}
|
||||
collapseAllBelowDepth={collapseAllBelowDepth}
|
||||
toggleExpandLevel={toggleExpandLevel}
|
||||
setShowDurations={setShowDurations}
|
||||
/>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<Slider
|
||||
variant={"tertiary"}
|
||||
className="w-20"
|
||||
LeadingIcon={MagnifyingGlassMinusIcon}
|
||||
TrailingIcon={MagnifyingGlassPlusIcon}
|
||||
value={[scale]}
|
||||
onValueChange={(value) => setScale(value[0])}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.05}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -738,6 +768,7 @@ function ShowParentLink({ runFriendlyId }: { runFriendlyId: string }) {
|
||||
fullWidth
|
||||
textAlignLeft
|
||||
shortcut={{ key: "p" }}
|
||||
className="flex-1"
|
||||
>
|
||||
{mouseOver ? (
|
||||
<ShowParentIconSelected className="h-4 w-4 text-indigo-500" />
|
||||
@@ -884,3 +915,92 @@ function ConnectedDevWarning() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function KeyboardShortcuts({
|
||||
expandAllBelowDepth,
|
||||
collapseAllBelowDepth,
|
||||
toggleExpandLevel,
|
||||
setShowDurations,
|
||||
}: {
|
||||
expandAllBelowDepth: (depth: number) => void;
|
||||
collapseAllBelowDepth: (depth: number) => void;
|
||||
toggleExpandLevel: (depth: number) => void;
|
||||
setShowDurations: (show: (show: boolean) => boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<ArrowKeyShortcuts />
|
||||
<ShortcutWithAction
|
||||
shortcut={{ key: "e" }}
|
||||
action={() => expandAllBelowDepth(0)}
|
||||
title="Expand all"
|
||||
/>
|
||||
<ShortcutWithAction
|
||||
shortcut={{ key: "c" }}
|
||||
action={() => collapseAllBelowDepth(1)}
|
||||
title="Collapse all"
|
||||
/>
|
||||
<NumberShortcuts toggleLevel={(number) => toggleExpandLevel(number)} />
|
||||
<ShortcutWithAction
|
||||
shortcut={{ key: "d" }}
|
||||
action={() => setShowDurations((d) => !d)}
|
||||
title="Toggle durations"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function ArrowKeyShortcuts() {
|
||||
return (
|
||||
<div className="flex items-center gap-0.5">
|
||||
<ShortcutKey shortcut={{ key: "arrowup" }} variant="medium" className="ml-0 mr-0" />
|
||||
<ShortcutKey shortcut={{ key: "arrowdown" }} variant="medium" className="ml-0 mr-0" />
|
||||
<ShortcutKey shortcut={{ key: "arrowleft" }} variant="medium" className="ml-0 mr-0" />
|
||||
<ShortcutKey shortcut={{ key: "arrowright" }} variant="medium" className="ml-0 mr-0" />
|
||||
<Paragraph variant="extra-small" className="ml-1.5 whitespace-nowrap">
|
||||
Navigate
|
||||
</Paragraph>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ShortcutWithAction({
|
||||
shortcut,
|
||||
title,
|
||||
action,
|
||||
}: {
|
||||
shortcut: Shortcut;
|
||||
title: string;
|
||||
action: () => void;
|
||||
}) {
|
||||
useShortcutKeys({
|
||||
shortcut,
|
||||
action,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-0.5">
|
||||
<ShortcutKey shortcut={shortcut} variant="medium" className="ml-0 mr-0" />
|
||||
<Paragraph variant="extra-small" className="ml-1.5 whitespace-nowrap">
|
||||
{title}
|
||||
</Paragraph>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function NumberShortcuts({ toggleLevel }: { toggleLevel: (depth: number) => void }) {
|
||||
useHotkeys(["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"], (event, hotkeysEvent) => {
|
||||
toggleLevel(Number(event.key));
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-0.5">
|
||||
<span className={cn(variants.medium, "ml-0 mr-0")}>0</span>
|
||||
<span className="text-[0.75rem] text-text-dimmed">–</span>
|
||||
<span className={cn(variants.medium, "ml-0 mr-0")}>9</span>
|
||||
<Paragraph variant="extra-small" className="ml-1.5 whitespace-nowrap">
|
||||
Toggle level
|
||||
</Paragraph>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+7
-3
@@ -6,7 +6,6 @@ import { ActionFunction, LoaderFunctionArgs, json } from "@remix-run/server-runt
|
||||
import { TaskRunStatus } from "@trigger.dev/database";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { typedjson, useTypedLoaderData } from "remix-typedjson";
|
||||
import { z } from "zod";
|
||||
import { JSONEditor } from "~/components/code/JSONEditor";
|
||||
import { EnvironmentLabel } from "~/components/environments/EnvironmentLabel";
|
||||
import { Button } from "~/components/primitives/Buttons";
|
||||
@@ -37,7 +36,7 @@ import {
|
||||
TestTaskPresenter,
|
||||
} from "~/presenters/v3/TestTaskPresenter.server";
|
||||
import { requireUserId } from "~/services/session.server";
|
||||
import { docsPath, v3RunPath, v3TaskParamsSchema } from "~/utils/pathBuilder";
|
||||
import { docsPath, v3RunSpanPath, v3TaskParamsSchema } from "~/utils/pathBuilder";
|
||||
import { TestTaskService } from "~/v3/services/testTask.server";
|
||||
import { TestTaskData } from "~/v3/testTask";
|
||||
|
||||
@@ -77,7 +76,12 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
}
|
||||
|
||||
return redirectWithSuccessMessage(
|
||||
v3RunPath({ slug: organizationSlug }, { slug: projectParam }, { friendlyId: run.friendlyId }),
|
||||
v3RunSpanPath(
|
||||
{ slug: organizationSlug },
|
||||
{ slug: projectParam },
|
||||
{ friendlyId: run.friendlyId },
|
||||
{ spanId: run.spanId }
|
||||
),
|
||||
request,
|
||||
"Test run created"
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { LoaderFunctionArgs, redirect } from "@remix-run/server-runtime";
|
||||
import { z } from "zod";
|
||||
import { prisma } from "~/db.server";
|
||||
import { requireUserId } from "~/services/session.server";
|
||||
import { v3RunSpanPath } from "~/utils/pathBuilder";
|
||||
|
||||
const ParamsSchema = z.object({
|
||||
projectRef: z.string(),
|
||||
@@ -33,8 +34,20 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
|
||||
return new Response("Not found", { status: 404 });
|
||||
}
|
||||
|
||||
const run = await prisma.taskRun.findUnique({
|
||||
where: {
|
||||
friendlyId: validatedParams.runParam,
|
||||
},
|
||||
});
|
||||
|
||||
if (!run) {
|
||||
throw new Response("Not found", { status: 404 });
|
||||
}
|
||||
|
||||
// Redirect to the project's runs page
|
||||
return redirect(
|
||||
`/orgs/${project.organization.slug}/projects/v3/${project.slug}/runs/${validatedParams.runParam}`
|
||||
v3RunSpanPath({ slug: project.organization.slug }, { slug: project.slug }, run, {
|
||||
spanId: run.spanId,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
+53
-13
@@ -4,10 +4,11 @@ import {
|
||||
QueueListIcon,
|
||||
StopCircleIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
import { useParams } from "@remix-run/react";
|
||||
import { useFetcher, useParams } from "@remix-run/react";
|
||||
import { LoaderFunctionArgs } from "@remix-run/server-runtime";
|
||||
import { formatDurationNanoseconds, nanosecondsToMilliseconds } from "@trigger.dev/core/v3";
|
||||
import { typedjson, useTypedLoaderData } from "remix-typedjson";
|
||||
import { useEffect } from "react";
|
||||
import { typedjson, useTypedFetcher, useTypedLoaderData } from "remix-typedjson";
|
||||
import { ExitIcon } from "~/assets/icons/ExitIcon";
|
||||
import { CodeBlock } from "~/components/code/CodeBlock";
|
||||
import { EnvironmentLabel } from "~/components/environments/EnvironmentLabel";
|
||||
@@ -17,6 +18,7 @@ import { Dialog, DialogTrigger } from "~/components/primitives/Dialog";
|
||||
import { Header2 } from "~/components/primitives/Headers";
|
||||
import { Paragraph } from "~/components/primitives/Paragraph";
|
||||
import { Property, PropertyTable } from "~/components/primitives/PropertyTable";
|
||||
import { Spinner } from "~/components/primitives/Spinner";
|
||||
import { CancelRunDialog } from "~/components/runs/v3/CancelRunDialog";
|
||||
import { LiveTimer } from "~/components/runs/v3/LiveTimer";
|
||||
import { ReplayRunDialog } from "~/components/runs/v3/ReplayRunDialog";
|
||||
@@ -58,19 +60,57 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
return typedjson({ span });
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
const {
|
||||
span: { event },
|
||||
} = useTypedLoaderData<typeof loader>();
|
||||
export function SpanView({
|
||||
runParam,
|
||||
spanId,
|
||||
closePanel,
|
||||
}: {
|
||||
runParam: string;
|
||||
spanId: string | undefined;
|
||||
closePanel: () => void;
|
||||
}) {
|
||||
const organization = useOrganization();
|
||||
const project = useProject();
|
||||
const { runParam } = useParams();
|
||||
const fetcher = useTypedFetcher<typeof loader>();
|
||||
|
||||
useEffect(() => {
|
||||
if (spanId === undefined) return;
|
||||
fetcher.load(
|
||||
`/resources/orgs/${organization.slug}/projects/v3/${project.slug}/runs/${runParam}/spans/${spanId}`
|
||||
);
|
||||
}, [organization.slug, project.slug, runParam, spanId]);
|
||||
|
||||
if (spanId === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (fetcher.state !== "idle" || fetcher.data === undefined) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"grid h-full max-h-full grid-rows-[2.5rem_1fr] overflow-hidden bg-background-bright"
|
||||
)}
|
||||
>
|
||||
<div className="mx-3 flex items-center gap-2 overflow-x-hidden border-b border-grid-dimmed">
|
||||
<div className="size-4 bg-grid-dimmed" />
|
||||
<div className="h-6 w-[60%] bg-grid-dimmed" />
|
||||
</div>
|
||||
<div className="flex items-center justify-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
span: { event },
|
||||
} = fetcher.data;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"grid h-full max-h-full overflow-hidden bg-background-bright",
|
||||
event.showActionBar ? "grid-rows-[2.5rem_1fr_2.5rem]" : "grid-rows-[2.5rem_1fr]"
|
||||
event.showActionBar ? "grid-rows-[2.5rem_1fr_3.25rem]" : "grid-rows-[2.5rem_1fr]"
|
||||
)}
|
||||
>
|
||||
<div className="mx-3 flex items-center justify-between gap-2 overflow-x-hidden border-b border-grid-dimmed">
|
||||
@@ -85,8 +125,8 @@ export default function Page() {
|
||||
</Header2>
|
||||
</div>
|
||||
{runParam && (
|
||||
<LinkButton
|
||||
to={v3RunPath(organization, project, { friendlyId: runParam })}
|
||||
<Button
|
||||
onClick={closePanel}
|
||||
variant="minimal/medium"
|
||||
LeadingIcon={ExitIcon}
|
||||
shortcut={{ key: "esc" }}
|
||||
@@ -188,7 +228,7 @@ export default function Page() {
|
||||
{ friendlyId: event.runId },
|
||||
{ spanId: event.spanId }
|
||||
)}
|
||||
variant="minimal/small"
|
||||
variant="minimal/medium"
|
||||
LeadingIcon={QueueListIcon}
|
||||
shortcut={{ key: "f" }}
|
||||
>
|
||||
@@ -216,7 +256,7 @@ function RunActionButtons({ span }: { span: Span }) {
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="danger/small" LeadingIcon={StopCircleIcon}>
|
||||
<Button variant="danger/medium" LeadingIcon={StopCircleIcon}>
|
||||
Cancel run
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
@@ -236,7 +276,7 @@ function RunActionButtons({ span }: { span: Span }) {
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="tertiary/small" LeadingIcon={ArrowPathIcon}>
|
||||
<Button variant="tertiary/medium" LeadingIcon={ArrowPathIcon}>
|
||||
Replay run
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
@@ -4,7 +4,7 @@ import { z } from "zod";
|
||||
import { prisma } from "~/db.server";
|
||||
import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
|
||||
import { logger } from "~/services/logger.server";
|
||||
import { v3RunPath } from "~/utils/pathBuilder";
|
||||
import { v3RunSpanPath } from "~/utils/pathBuilder";
|
||||
import { ReplayTaskRunService } from "~/v3/services/replayTaskRun.server";
|
||||
|
||||
const FormSchema = z.object({
|
||||
@@ -54,12 +54,13 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
);
|
||||
}
|
||||
|
||||
const runPath = v3RunPath(
|
||||
const runPath = v3RunSpanPath(
|
||||
{
|
||||
slug: taskRun.project.organization.slug,
|
||||
},
|
||||
{ slug: taskRun.project.slug },
|
||||
{ friendlyId: newRun.friendlyId }
|
||||
{ friendlyId: newRun.friendlyId },
|
||||
{ spanId: newRun.spanId }
|
||||
);
|
||||
|
||||
return redirectWithSuccessMessage(runPath, request, `Replaying run`);
|
||||
|
||||
@@ -14,8 +14,6 @@ import { Job } from "~/models/job.server";
|
||||
import type { Organization } from "~/models/organization.server";
|
||||
import type { Project } from "~/models/project.server";
|
||||
import { objectToSearchParams } from "./searchParams";
|
||||
import { ScheduleListFilters } from "~/components/runs/v3/ScheduleFilters";
|
||||
import { useLocation } from "@remix-run/react";
|
||||
|
||||
export type OrgForPath = Pick<Organization, "slug">;
|
||||
export type ProjectForPath = Pick<Project, "slug">;
|
||||
@@ -368,7 +366,7 @@ export function v3RunSpanPath(
|
||||
run: v3RunForPath,
|
||||
span: v3SpanForPath
|
||||
) {
|
||||
return `${v3RunPath(organization, project, run)}/spans/${span.spanId}`;
|
||||
return `${v3RunPath(organization, project, run)}?span=${span.spanId}`;
|
||||
}
|
||||
|
||||
export function v3TraceSpanPath(
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
The CLI `dev` command runs a server for your tasks. It will watches for changes in your `/trigger` directory and communicates with the Trigger.dev platform to register your tasks, perform runs, and send data back and forth.
|
||||
|
||||
It can also update your `@trigger.dev/*` packages to prevent version mismatches and failed deploys. You will always be prompted first.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash npm
|
||||
|
||||
@@ -21,13 +21,16 @@ yarn dlx trigger.dev@beta deploy
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<Warning>Will fail in CI if any version mismatches are detected. Ensure everything runs locally first using the [dev](/v3/cli-dev) command and don't bypass the version checks!</Warning>
|
||||
|
||||
It performs a few steps to deploy:
|
||||
|
||||
1. Typechecks the code.
|
||||
2. Compiles and bundles the code.
|
||||
3. Checks that [environment variables](/v3/deploy-environment-variables) are set.
|
||||
4. Deploys the code to the cloud.
|
||||
5. Registers the tasks as a new version in the environment (prod by default).
|
||||
1. Optionally updates packages when running locally.
|
||||
2. Typechecks the code.
|
||||
3. Compiles and bundles the code.
|
||||
4. Checks that [environment variables](/v3/deploy-environment-variables) are set.
|
||||
5. Deploys the code to the cloud.
|
||||
6. Registers the tasks as a new version in the environment (prod by default).
|
||||
|
||||
## Options
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ yarn dlx trigger.dev@beta dev
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
It will first perform an update check to prevent version mismatches, failed deploys, and other errors. You will always be prompted first.
|
||||
|
||||
You will see in the terminal that the server is running and listening for requests. When you run a task, you will see it in the terminal along with a link to view it in the dashboard.
|
||||
|
||||
It is worth noting that each task runs in a separate Node process. This means that if you have a long-running task, it will not block other tasks from running.
|
||||
|
||||
@@ -5,6 +5,8 @@ description: "You can easily deploy your tasks with GitHub actions."
|
||||
|
||||
This simple GitHub action file will deploy you Trigger.dev tasks when new code is pushed to the `main` branch and the `trigger` directory has changes in it.
|
||||
|
||||
<Warning>The deploy step will fail if any version mismatches are detected. Please see the [version pinning](/v3/github-actions#version-pinning) section for more details.</Warning>
|
||||
|
||||
```yaml .github/workflows/release-trigger.yml
|
||||
name: Deploy to Trigger.dev
|
||||
|
||||
@@ -42,3 +44,19 @@ If you already have a GitHub action file, you can just add the final step "🚀
|
||||
You need to add the `TRIGGER_ACCESS_TOKEN` secret to your repository. You can create a new access token by going to your profile page and then clicking on the "Personal Access Tokens" tab.
|
||||
|
||||
To set it in GitHub go to your repository, click on "Settings", "Secrets and variables" and then "Actions". Add a new secret with the name `TRIGGER_ACCESS_TOKEN` and use the value of your access token.
|
||||
|
||||
## Version pinning
|
||||
|
||||
The CLI and `@trigger.dev/*` package versions need to be in sync, otherwise there will be errors and unpredictable behavior. Hence, the `deploy` command will automatically fail during CI on any version mismatches.
|
||||
|
||||
To ensure a smooth CI experience you can pin the CLI version in the deploy step, like so:
|
||||
|
||||
```yaml .github/workflows/release-trigger.yml
|
||||
- name: 🚀 Deploy Trigger.dev
|
||||
env:
|
||||
TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }}
|
||||
run: |
|
||||
npx trigger.dev@3.0.0-beta.16 deploy
|
||||
```
|
||||
|
||||
You should use the version you run locally during dev and manual deploy. The current version is displayed in the banner, but you can also check it by appending `--version` to any command.
|
||||
|
||||
@@ -53,7 +53,7 @@ export const config: TriggerConfig = {
|
||||
|
||||
## ESM-only packages
|
||||
|
||||
We'll let you know when run the CLI dev command if this is a problem. Some packages are ESM-only so they don't work directly from CJS when using Node.js. In that case you need to add them to the `dependenciesToBundle` array in your `trigger.config.ts` file.
|
||||
We'll let you know when running the CLI dev command if this is a problem. Some packages are ESM-only so they don't work directly from CJS when using Node.js. In that case you need to add them to the `dependenciesToBundle` array in your `trigger.config.ts` file.
|
||||
|
||||
```ts trigger.config.ts
|
||||
import type { TriggerConfig } from "@trigger.dev/sdk/v3";
|
||||
@@ -92,7 +92,10 @@ Prisma works by generating a client from your `prisma.schema` file. This means y
|
||||
|
||||
<Step title="package.json postinstall `prisma generate`">
|
||||
|
||||
```json
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```json default path
|
||||
{
|
||||
"scripts": {
|
||||
"postinstall": "prisma generate"
|
||||
@@ -100,6 +103,16 @@ Prisma works by generating a client from your `prisma.schema` file. This means y
|
||||
}
|
||||
```
|
||||
|
||||
```json custom path
|
||||
{
|
||||
"scripts": {
|
||||
"postinstall": "prisma generate --schema=./custom/path/to/schema.prisma"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
Anything you put in `postinstall` will be run as part of the install step. This is how Next.js recommends you set up Prisma anyway.
|
||||
|
||||
</Step>
|
||||
@@ -111,7 +124,12 @@ Prisma works by generating a client from your `prisma.schema` file. This means y
|
||||
|
||||
export const config: TriggerConfig = {
|
||||
//..other stuff
|
||||
|
||||
// using the default path
|
||||
additionalFiles: ["./prisma/schema.prisma"],
|
||||
// or a custom path, for example in a monorepo
|
||||
additionalFiles: ["../../custom/path/to/schema.prisma"],
|
||||
|
||||
additionalPackages: ["prisma@5.11.0"],
|
||||
};
|
||||
```
|
||||
|
||||
@@ -171,25 +171,7 @@ async function yourBackendFunction() {
|
||||
|
||||
## Upgrading your project
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Upgrade the v2 Trigger.dev packages">
|
||||
|
||||
You can run this command to upgrade all the packages to the beta:
|
||||
|
||||
```bash
|
||||
npx @trigger.dev/cli@beta update --to beta
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Follow the v3 quick start">
|
||||
|
||||
Follow the [v3 quick start](/v3/quick-start) to get started with v3.
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
Just follow the [v3 quick start](/v3/quick-start) to get started with v3. Our new CLI will take care of the rest.
|
||||
|
||||
## Using v2 together with v3
|
||||
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @trigger.dev/airtable
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.17
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.16
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/airtable",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Trigger.dev integration for airtable",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -25,8 +25,8 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.17",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"airtable": "^0.12.1",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @trigger.dev/github
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.17
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.16
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/github",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "The official GitHub integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -30,8 +30,8 @@
|
||||
"@octokit/request-error": "^5.0.1",
|
||||
"@octokit/webhooks": "^12.0.10",
|
||||
"octokit": "^3.1.2",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.17",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @trigger.dev/linear
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.17
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.16
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/linear",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Trigger.dev integration for @linear/sdk",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -26,8 +26,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@linear/sdk": "^8.0.0",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.17",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @trigger.dev/slack
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.17
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.16
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/openai",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "The official OpenAI integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -42,8 +42,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"openai": "^4.16.1",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.15"
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.17"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @trigger.dev/plain
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.17
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.16
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/plain",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "The official Plain.com integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -24,8 +24,8 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.17",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"@team-plain/typescript-sdk": "^2.7.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @trigger.dev/replicate
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.17
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.16
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/replicate",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Trigger.dev integration for replicate",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -25,8 +25,8 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.17",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"replicate": "^0.18.1",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @trigger.dev/resend
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.17
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.16
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/resend",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "The official Resend.com integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -24,8 +24,8 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.17",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"resend": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @trigger.dev/sendgrid
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.17
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.16
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/sendgrid",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Trigger.dev integration for @sendgrid/mail",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -26,8 +26,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@sendgrid/mail": "^7.7.0",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.15"
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.17"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.8.0"
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @trigger.dev/shopify
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.17
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.16
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/shopify",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Trigger.dev integration for @shopify/shopify-api",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -26,8 +26,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@shopify/shopify-api": "^8.0.2",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.17",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @trigger.dev/slack
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/slack",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "The official Slack integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@slack/web-api": "^6.8.1",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @trigger.dev/stripe
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.17
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.16
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/stripe",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Trigger.dev integration for stripe",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -25,8 +25,8 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.17",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"stripe": "^12.14.0",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @trigger.dev/supabase
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.17
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.16
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/supabase",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Trigger.dev integration for @supabase/supabase-js",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -26,8 +26,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@supabase/supabase-js": "^2.26.0",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.17",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"supabase-management-js": "^1.0.0",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @trigger.dev/typeform
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.17
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.16
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/typeform",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "The official Typeform integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -24,8 +24,8 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.17",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"@typeform/api-client": "^1.8.0",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @trigger.dev/astro
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@trigger.dev/astro",
|
||||
"description": "An Astro-native integration for Trigger.dev background jobs platform",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
@@ -20,7 +20,7 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15"
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"astro": "^3.0.12",
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# trigger.dev
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b20760173: v3 CLI update command and package manager detection fix
|
||||
- @trigger.dev/core@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ed2a26c86: - Fix additionalFiles that aren't decendants
|
||||
- Stop swallowing uncaught exceptions in prod
|
||||
- Improve warnings and errors, fail early on critical warnings
|
||||
- New arg to --save-logs even for successful builds
|
||||
- Updated dependencies [ed2a26c86]
|
||||
- @trigger.dev/core@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "trigger.dev",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "A Command-Line Interface for Trigger.dev (v3) projects",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -85,7 +85,7 @@
|
||||
"@opentelemetry/sdk-trace-base": "^1.22.0",
|
||||
"@opentelemetry/sdk-trace-node": "^1.22.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.22.0",
|
||||
"@trigger.dev/core": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/core": "workspace:^3.0.0-beta.17",
|
||||
"@types/degit": "^2.8.3",
|
||||
"chalk": "^5.2.0",
|
||||
"chokidar": "^3.5.3",
|
||||
@@ -106,7 +106,6 @@
|
||||
"mock-fs": "^5.2.0",
|
||||
"nanoid": "^4.0.2",
|
||||
"node-fetch": "^3.3.0",
|
||||
"npm-check-updates": "^16.12.2",
|
||||
"object-hash": "^3.0.0",
|
||||
"p-debounce": "^4.0.0",
|
||||
"p-throttle": "^6.1.0",
|
||||
|
||||
@@ -8,6 +8,7 @@ import { configureWhoamiCommand } from "../commands/whoami.js";
|
||||
import { COMMAND_NAME } from "../consts.js";
|
||||
import { getVersion } from "../utilities/getVersion.js";
|
||||
import { configureListProfilesCommand } from "../commands/list-profiles.js";
|
||||
import { configureUpdateCommand } from "../commands/update.js";
|
||||
|
||||
export const program = new Command();
|
||||
|
||||
@@ -23,3 +24,4 @@ configureDeployCommand(program);
|
||||
configureWhoamiCommand(program);
|
||||
configureLogoutCommand(program);
|
||||
configureListProfilesCommand(program);
|
||||
configureUpdateCommand(program);
|
||||
|
||||
@@ -43,7 +43,7 @@ import { logger } from "../utilities/logger.js";
|
||||
import { createTaskFileImports, gatherTaskFiles } from "../utilities/taskFiles";
|
||||
import { login } from "./login";
|
||||
|
||||
import { Glob } from "glob";
|
||||
import { Glob, GlobOptions } from "glob";
|
||||
import type { SetOptional } from "type-fest";
|
||||
import { bundleDependenciesPlugin, workerSetupImportConfigPlugin } from "../utilities/build";
|
||||
import { chalkError, chalkPurple, chalkWarning } from "../utilities/cliOutput";
|
||||
@@ -57,6 +57,8 @@ import { safeJsonParse } from "../utilities/safeJsonParse";
|
||||
import { JavascriptProject } from "../utilities/javascriptProject";
|
||||
import { cliRootPath } from "../utilities/resolveInternalFilePath";
|
||||
import { escapeImportPath, spinner } from "../utilities/windows";
|
||||
import { updateTriggerPackages } from "./update";
|
||||
import { docs, getInTouch } from "../utilities/links";
|
||||
|
||||
const DeployCommandOptions = CommonCommandOptions.extend({
|
||||
skipTypecheck: z.boolean().default(false),
|
||||
@@ -72,6 +74,8 @@ const DeployCommandOptions = CommonCommandOptions.extend({
|
||||
projectRef: z.string().optional(),
|
||||
outputMetafile: z.string().optional(),
|
||||
apiUrl: z.string().optional(),
|
||||
saveLogs: z.boolean().default(false),
|
||||
skipUpdateCheck: z.boolean().default(false),
|
||||
});
|
||||
|
||||
type DeployCommandOptions = z.infer<typeof DeployCommandOptions>;
|
||||
@@ -88,6 +92,7 @@ export function configureDeployCommand(program: Command) {
|
||||
"prod"
|
||||
)
|
||||
.option("--skip-typecheck", "Whether to skip the pre-build typecheck")
|
||||
.option("--skip-update-check", "Skip checking for @trigger.dev package updates")
|
||||
.option(
|
||||
"--ignore-env-var-check",
|
||||
"Detected missing environment variables won't block deployment"
|
||||
@@ -140,6 +145,12 @@ export function configureDeployCommand(program: Command) {
|
||||
"If provided, will save the esbuild metafile for the build to the specified path"
|
||||
).hideHelp()
|
||||
)
|
||||
.addOption(
|
||||
new CommandOption(
|
||||
"--save-logs",
|
||||
"If provided, will save logs even for successful builds"
|
||||
).hideHelp()
|
||||
)
|
||||
.action(async (path, options) => {
|
||||
await handleTelemetry(async () => {
|
||||
await printStandloneInitialBanner(true);
|
||||
@@ -159,6 +170,10 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
|
||||
|
||||
intro("Deploying project");
|
||||
|
||||
if (!options.skipUpdateCheck) {
|
||||
await updateTriggerPackages(dir, { ...options }, true, true);
|
||||
}
|
||||
|
||||
const authorization = await login({
|
||||
embedded: true,
|
||||
defaultApiUrl: options.apiUrl,
|
||||
@@ -306,25 +321,42 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
|
||||
|
||||
const image = await buildImage();
|
||||
|
||||
const warnings = checkLogsForWarnings(image.logs);
|
||||
|
||||
if (!warnings.ok) {
|
||||
await failDeploy(
|
||||
deploymentResponse.data.shortCode,
|
||||
warnings.summary,
|
||||
image.logs,
|
||||
deploymentSpinner,
|
||||
warnings.warnings,
|
||||
warnings.errors
|
||||
);
|
||||
|
||||
throw new SkipLoggingError(`Failed to build project image: ${warnings.summary}`);
|
||||
}
|
||||
|
||||
if (!image.ok) {
|
||||
deploymentSpinner.stop(`Failed to build project.`);
|
||||
|
||||
// If there are logs, let's write it out to a temporary file and include the path in the error message
|
||||
if (image.logs.trim() !== "") {
|
||||
const logPath = join(await createTempDir(), `build-${deploymentResponse.data.shortCode}.log`);
|
||||
|
||||
await writeFile(logPath, image.logs);
|
||||
|
||||
logger.log(
|
||||
`${chalkError("X Error:")} ${image.error}. Full build logs have been saved to ${logPath})`
|
||||
);
|
||||
} else {
|
||||
logger.log(`${chalkError("X Error:")} ${image.error}.`);
|
||||
}
|
||||
await failDeploy(
|
||||
deploymentResponse.data.shortCode,
|
||||
image.error,
|
||||
image.logs,
|
||||
deploymentSpinner,
|
||||
warnings.warnings
|
||||
);
|
||||
|
||||
throw new SkipLoggingError(`Failed to build project image: ${image.error}`);
|
||||
}
|
||||
|
||||
const preExitTasks = async () => {
|
||||
printWarnings(warnings.warnings);
|
||||
|
||||
if (options.saveLogs) {
|
||||
const logPath = await saveLogs(deploymentResponse.data.shortCode, image.logs);
|
||||
log.info(`Build logs have been saved to ${logPath}`);
|
||||
}
|
||||
};
|
||||
|
||||
const imageReference = options.selfHosted
|
||||
? `${selfHostedRegistryHost ? `${selfHostedRegistryHost}/` : ""}${image.image}${
|
||||
image.digest ? `@${image.digest}` : ""
|
||||
@@ -340,6 +372,8 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
|
||||
`Project image built: ${imageReference}. Skipping deployment as requested`
|
||||
);
|
||||
|
||||
await preExitTasks();
|
||||
|
||||
throw new SkipCommandError("Skipping deployment as requested");
|
||||
}
|
||||
|
||||
@@ -359,6 +393,8 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
|
||||
if (!startIndexingResponse.success) {
|
||||
deploymentSpinner.stop(`Failed to start indexing: ${startIndexingResponse.error}`);
|
||||
|
||||
await preExitTasks();
|
||||
|
||||
throw new SkipLoggingError(`Failed to start indexing: ${startIndexingResponse.error}`);
|
||||
}
|
||||
|
||||
@@ -370,12 +406,16 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
|
||||
if (!finishedDeployment) {
|
||||
deploymentSpinner.stop(`Deployment failed to complete`);
|
||||
|
||||
await preExitTasks();
|
||||
|
||||
throw new SkipLoggingError("Deployment failed to complete: unknown issue");
|
||||
}
|
||||
|
||||
if (typeof finishedDeployment === "string") {
|
||||
deploymentSpinner.stop(`Deployment failed to complete: ${finishedDeployment}`);
|
||||
|
||||
await preExitTasks();
|
||||
|
||||
throw new SkipLoggingError(`Deployment failed to complete: ${finishedDeployment}`);
|
||||
}
|
||||
|
||||
@@ -386,7 +426,13 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
|
||||
|
||||
switch (finishedDeployment.status) {
|
||||
case "DEPLOYED": {
|
||||
deploymentSpinner.stop("Deployment completed");
|
||||
if (warnings.warnings.length > 0) {
|
||||
deploymentSpinner.stop("Deployment completed with warnings");
|
||||
} else {
|
||||
deploymentSpinner.stop("Deployment completed");
|
||||
}
|
||||
|
||||
await preExitTasks();
|
||||
|
||||
const taskCount = finishedDeployment.worker?.tasks.length ?? 0;
|
||||
|
||||
@@ -417,6 +463,8 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
|
||||
|
||||
logTaskMetadataParseError(parsedError.data.zodIssues, parsedError.data.tasks);
|
||||
|
||||
await preExitTasks();
|
||||
|
||||
throw new SkipLoggingError(
|
||||
`Deployment encountered an error: ${finishedDeployment.errorData.name}`
|
||||
);
|
||||
@@ -439,6 +487,8 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
|
||||
logESMRequireError(parsedError, resolvedConfig);
|
||||
}
|
||||
|
||||
await preExitTasks();
|
||||
|
||||
throw new SkipLoggingError(
|
||||
`Deployment encountered an error: ${finishedDeployment.errorData.name}`
|
||||
);
|
||||
@@ -447,22 +497,189 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
|
||||
`Deployment failed with an unknown error. Please contact eric@trigger.dev for help. ${deploymentLink}`
|
||||
);
|
||||
|
||||
await preExitTasks();
|
||||
|
||||
throw new SkipLoggingError("Deployment failed with an unknown error");
|
||||
}
|
||||
}
|
||||
case "CANCELED": {
|
||||
deploymentSpinner.stop(`Deployment was canceled. ${deploymentLink}`);
|
||||
|
||||
await preExitTasks();
|
||||
|
||||
throw new SkipLoggingError("Deployment was canceled");
|
||||
}
|
||||
case "TIMED_OUT": {
|
||||
deploymentSpinner.stop(`Deployment timed out. ${deploymentLink}`);
|
||||
|
||||
await preExitTasks();
|
||||
|
||||
throw new SkipLoggingError("Deployment timed out");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function printErrors(errors?: string[]) {
|
||||
for (const error of errors ?? []) {
|
||||
log.error(`${chalkError("Error:")} ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
function printWarnings(warnings?: string[]) {
|
||||
for (const warning of warnings ?? []) {
|
||||
log.warn(`${chalkWarning("Warning:")} ${warning}`);
|
||||
}
|
||||
}
|
||||
|
||||
type WarningsCheckReturn =
|
||||
| {
|
||||
ok: true;
|
||||
warnings: string[];
|
||||
}
|
||||
| {
|
||||
ok: false;
|
||||
summary: string;
|
||||
errors: string[];
|
||||
warnings: string[];
|
||||
};
|
||||
|
||||
type LogParserOptions = Array<{
|
||||
regex: RegExp;
|
||||
message: string;
|
||||
shouldFail?: boolean;
|
||||
}>;
|
||||
|
||||
// Try to extract useful warnings from logs. Sometimes we may even want to fail the build. This won't work if the step is cached.
|
||||
function checkLogsForWarnings(logs: string): WarningsCheckReturn {
|
||||
const warnings: LogParserOptions = [
|
||||
{
|
||||
regex: /prisma:warn We could not find your Prisma schema/,
|
||||
message: `Prisma generate failed to find the default schema. Did you include it in config.additionalFiles? ${terminalLink(
|
||||
"Config docs",
|
||||
docs.config.prisma
|
||||
)}\nCustom schema paths require a postinstall script like this: \`prisma generate --schema=./custom/path/to/schema.prisma\``,
|
||||
shouldFail: true,
|
||||
},
|
||||
];
|
||||
|
||||
const errorMessages: string[] = [];
|
||||
const warningMessages: string[] = [];
|
||||
|
||||
let shouldFail = false;
|
||||
|
||||
for (const warning of warnings) {
|
||||
const matches = logs.match(warning.regex);
|
||||
|
||||
if (!matches) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const message = getMessageFromTemplate(warning.message, matches.groups);
|
||||
|
||||
if (warning.shouldFail) {
|
||||
shouldFail = true;
|
||||
errorMessages.push(message);
|
||||
} else {
|
||||
warningMessages.push(message);
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldFail) {
|
||||
return {
|
||||
ok: false,
|
||||
summary: "Build succeeded with critical warnings. Will not proceed",
|
||||
warnings: warningMessages,
|
||||
errors: errorMessages,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
warnings: warningMessages,
|
||||
};
|
||||
}
|
||||
|
||||
// Try to extract useful error messages from the logs
|
||||
function checkLogsForErrors(logs: string) {
|
||||
const errors: LogParserOptions = [
|
||||
{
|
||||
regex: /Error: Provided --schema at (?<schema>.*) doesn't exist/,
|
||||
message: `Prisma generate failed to find the specified schema at "$schema".\nDid you include it in config.additionalFiles? ${terminalLink(
|
||||
"Config docs",
|
||||
docs.config.prisma
|
||||
)}`,
|
||||
},
|
||||
{
|
||||
regex: /sh: 1: (?<packageOrBinary>.*): not found/,
|
||||
message: `$packageOrBinary not found\n\nIf it's a package: Include it in ${terminalLink(
|
||||
"config.additionalPackages",
|
||||
docs.config.prisma
|
||||
)}\nIf it's a binary: Please ${terminalLink(
|
||||
"get in touch",
|
||||
getInTouch
|
||||
)} and we'll see what we can do!`,
|
||||
},
|
||||
];
|
||||
|
||||
for (const error of errors) {
|
||||
const matches = logs.match(error.regex);
|
||||
|
||||
if (!matches) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const message = getMessageFromTemplate(error.message, matches.groups);
|
||||
|
||||
log.error(`${chalkError("Error:")} ${message}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function getMessageFromTemplate(template: string, replacer: RegExpMatchArray["groups"]) {
|
||||
let message = template;
|
||||
|
||||
if (replacer) {
|
||||
for (const [key, value] of Object.entries(replacer)) {
|
||||
message = message.replaceAll(`$${key}`, value);
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
async function saveLogs(shortCode: string, logs: string) {
|
||||
const logPath = join(await createTempDir(), `build-${shortCode}.log`);
|
||||
await writeFile(logPath, logs);
|
||||
return logPath;
|
||||
}
|
||||
|
||||
async function failDeploy(
|
||||
shortCode: string,
|
||||
errorSummary: string,
|
||||
logs: string,
|
||||
deploymentSpinner: ReturnType<typeof spinner>,
|
||||
warnings?: string[],
|
||||
errors?: string[]
|
||||
) {
|
||||
deploymentSpinner.stop(`Failed to deploy project`);
|
||||
|
||||
// If there are logs, let's write it out to a temporary file and include the path in the error message
|
||||
if (logs.trim() !== "") {
|
||||
const logPath = await saveLogs(shortCode, logs);
|
||||
|
||||
printWarnings(warnings);
|
||||
printErrors(errors);
|
||||
|
||||
checkLogsForErrors(logs);
|
||||
|
||||
outro(`${chalkError("Error:")} ${errorSummary}. Full build logs have been saved to ${logPath}`);
|
||||
} else {
|
||||
outro(`${chalkError("Error:")} ${errorSummary}.`);
|
||||
}
|
||||
|
||||
// TODO: Let platform know so it can fail the deploy with an appropriate error
|
||||
}
|
||||
|
||||
async function checkEnvVars(
|
||||
envVars: string[],
|
||||
config: ResolvedConfig,
|
||||
@@ -944,7 +1161,7 @@ async function compileProject(
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
const workerSetupPath = join(cliRootPath(), "workers", "dev", "worker-setup.js");
|
||||
const workerSetupPath = join(cliRootPath(), "workers", "prod", "worker-setup.js");
|
||||
|
||||
let workerContents = workerFacade
|
||||
.replace("__TASKS__", createTaskFileImports(taskFiles))
|
||||
@@ -1139,9 +1356,22 @@ async function compileProject(
|
||||
|
||||
await writeJSONFile(join(tempDir, "package.json"), packageJsonContents);
|
||||
|
||||
await copyAdditionalFiles(config, tempDir);
|
||||
const copyResult = await copyAdditionalFiles(config, tempDir);
|
||||
|
||||
compileSpinner.stop("Project built successfully");
|
||||
if (!copyResult.ok) {
|
||||
compileSpinner.stop("Project built with warnings");
|
||||
|
||||
log.warn(
|
||||
`No additionalFiles matches for:\n\n${copyResult.noMatches
|
||||
.map((glob) => `- "${glob}"`)
|
||||
.join("\n")}\n\nIf this is unexpected you should check your ${terminalLink(
|
||||
"glob patterns",
|
||||
"https://github.com/isaacs/node-glob?tab=readme-ov-file#glob-primer"
|
||||
)} are valid.`
|
||||
);
|
||||
} else {
|
||||
compileSpinner.stop("Project built successfully");
|
||||
}
|
||||
|
||||
const resolvingDependenciesResult = await resolveDependencies(
|
||||
tempDir,
|
||||
@@ -1443,11 +1673,24 @@ async function gatherRequiredDependencies(
|
||||
return Object.fromEntries(Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b)));
|
||||
}
|
||||
|
||||
async function copyAdditionalFiles(config: ResolvedConfig, tempDir: string) {
|
||||
type AdditionalFilesReturn =
|
||||
| {
|
||||
ok: true;
|
||||
}
|
||||
| {
|
||||
ok: false;
|
||||
noMatches: string[];
|
||||
};
|
||||
|
||||
async function copyAdditionalFiles(
|
||||
config: ResolvedConfig,
|
||||
tempDir: string
|
||||
): Promise<AdditionalFilesReturn> {
|
||||
const additionalFiles = config.additionalFiles ?? [];
|
||||
const noMatches: string[] = [];
|
||||
|
||||
if (additionalFiles.length === 0) {
|
||||
return;
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
return await tracer.startActiveSpan(
|
||||
@@ -1463,25 +1706,77 @@ async function copyAdditionalFiles(config: ResolvedConfig, tempDir: string) {
|
||||
additionalFiles,
|
||||
});
|
||||
|
||||
const glob = new Glob(additionalFiles, {
|
||||
const globOptions = {
|
||||
withFileTypes: true,
|
||||
ignore: ["node_modules"],
|
||||
cwd: config.projectDir,
|
||||
nodir: true,
|
||||
});
|
||||
} satisfies GlobOptions;
|
||||
|
||||
for await (const file of glob) {
|
||||
const relativeDestinationPath = join(
|
||||
tempDir,
|
||||
relative(config.projectDir, file.fullpath())
|
||||
);
|
||||
const globs: Array<GlobOptions> = [];
|
||||
let i = 0;
|
||||
|
||||
logger.debug(`Copying file ${file.fullpath()} to ${relativeDestinationPath}`);
|
||||
await mkdir(dirname(relativeDestinationPath), { recursive: true });
|
||||
await copyFile(file.fullpath(), relativeDestinationPath);
|
||||
for (const additionalFile of additionalFiles) {
|
||||
let glob: GlobOptions | Glob<typeof globOptions>;
|
||||
|
||||
if (i === 0) {
|
||||
glob = new Glob(additionalFile, globOptions);
|
||||
} else {
|
||||
const previousGlob = globs[i - 1];
|
||||
if (!previousGlob) {
|
||||
logger.error("No previous glob, this shouldn't happen", { i, additionalFiles });
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use the previous glob's options and cache
|
||||
glob = new Glob(additionalFile, previousGlob);
|
||||
}
|
||||
|
||||
if (!(Symbol.asyncIterator in glob)) {
|
||||
logger.error("Glob should be an async iterator", { glob });
|
||||
throw new Error("Unrecoverable error while copying additional files");
|
||||
}
|
||||
|
||||
let matches = 0;
|
||||
for await (const file of glob) {
|
||||
matches++;
|
||||
|
||||
// Any additional files that aren't a child of projectDir will be moved inside tempDir, so they can be part of the build context
|
||||
// The file "../foo/bar" will be written to "tempDir/foo/bar"
|
||||
// The file "../../bar/baz" will be written to "tempDir/bar/baz"
|
||||
const pathInsideTempDir = relative(config.projectDir, file.fullpath())
|
||||
.split(posix.sep)
|
||||
.filter((p) => p !== "..")
|
||||
.join(posix.sep);
|
||||
|
||||
const relativeDestinationPath = join(tempDir, pathInsideTempDir);
|
||||
|
||||
logger.debug(`Copying file ${file.fullpath()} to ${relativeDestinationPath}`);
|
||||
|
||||
await mkdir(dirname(relativeDestinationPath), { recursive: true });
|
||||
await copyFile(file.fullpath(), relativeDestinationPath);
|
||||
}
|
||||
|
||||
if (matches === 0) {
|
||||
noMatches.push(additionalFile);
|
||||
}
|
||||
|
||||
globs[i] = glob;
|
||||
i++;
|
||||
}
|
||||
|
||||
span.end();
|
||||
|
||||
if (noMatches.length > 0) {
|
||||
return {
|
||||
ok: false,
|
||||
noMatches,
|
||||
} as const;
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
} as const;
|
||||
} catch (error) {
|
||||
recordSpanException(span, error);
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ import {
|
||||
import { findUp, pathExists } from "find-up";
|
||||
import { cliRootPath } from "../utilities/resolveInternalFilePath";
|
||||
import { escapeImportPath } from "../utilities/windows";
|
||||
import { updateTriggerPackages } from "./update";
|
||||
|
||||
let apiClient: CliApiClient | undefined;
|
||||
|
||||
@@ -61,6 +62,7 @@ const DevCommandOptions = CommonCommandOptions.extend({
|
||||
debugOtel: z.boolean().default(false),
|
||||
config: z.string().optional(),
|
||||
projectRef: z.string().optional(),
|
||||
skipUpdateCheck: z.boolean().default(false),
|
||||
});
|
||||
|
||||
type DevCommandOptions = z.infer<typeof DevCommandOptions>;
|
||||
@@ -78,6 +80,7 @@ export function configureDevCommand(program: Command) {
|
||||
)
|
||||
.option("--debugger", "Enable the debugger")
|
||||
.option("--debug-otel", "Enable OpenTelemetry debugging")
|
||||
.option("--skip-update-check", "Skip checking for @trigger.dev package updates")
|
||||
).action(async (path, options) => {
|
||||
wrapCommandAction("dev", DevCommandOptions, options, async (opts) => {
|
||||
await devCommand(path, opts);
|
||||
@@ -132,7 +135,13 @@ async function startDev(
|
||||
}
|
||||
|
||||
await printStandloneInitialBanner(true);
|
||||
printDevBanner();
|
||||
|
||||
if (!options.skipUpdateCheck) {
|
||||
console.log(); // spacing
|
||||
await updateTriggerPackages(dir, { ...options }, false, true);
|
||||
}
|
||||
|
||||
printDevBanner(!options.skipUpdateCheck);
|
||||
|
||||
logger.debug("Starting dev session", { dir, options, authorization });
|
||||
|
||||
|
||||
@@ -1,148 +1,300 @@
|
||||
import { confirm } from "@clack/prompts";
|
||||
import { RunOptions, run } from "npm-check-updates";
|
||||
import path from "path";
|
||||
import { confirm, intro, isCancel, log, outro } from "@clack/prompts";
|
||||
import { z } from "zod";
|
||||
import { chalkError, chalkSuccess } from "../utilities/cliOutput.js";
|
||||
import { readJSONFileSync, writeJSONFile } from "../utilities/fileSystem.js";
|
||||
import { installDependencies } from "../utilities/installDependencies.js";
|
||||
import { readJSONFile, removeFile, writeJSONFile } from "../utilities/fileSystem.js";
|
||||
import { spinner } from "../utilities/windows.js";
|
||||
import { CommonCommandOptions, OutroCommandError, wrapCommandAction } from "../cli/common.js";
|
||||
import { Command } from "commander";
|
||||
import { logger } from "../utilities/logger.js";
|
||||
import { PackageJson } from "type-fest";
|
||||
import { printStandloneInitialBanner, updateCheck } from "../utilities/initialBanner.js";
|
||||
import { join, resolve } from "path";
|
||||
import { JavascriptProject } from "../utilities/javascriptProject.js";
|
||||
import { PackageManager } from "../utilities/getUserPackageManager.js";
|
||||
import { getVersion } from "../utilities/getVersion.js";
|
||||
import { chalkError, prettyWarning } from "../utilities/cliOutput.js";
|
||||
|
||||
export const UpdateCommandOptionsSchema = z.object({
|
||||
to: z.string().optional(),
|
||||
export const UpdateCommandOptions = CommonCommandOptions.pick({
|
||||
logLevel: true,
|
||||
skipTelemetry: true,
|
||||
});
|
||||
|
||||
export type UpdateCommandOptions = z.infer<typeof UpdateCommandOptionsSchema>;
|
||||
export type UpdateCommandOptions = z.infer<typeof UpdateCommandOptions>;
|
||||
|
||||
type NcuRunOptionTarget = "latest" | `@${string}`;
|
||||
|
||||
export async function updateCommand(projectPath: string, anyOptions: any) {
|
||||
const loadingSpinner = spinner();
|
||||
loadingSpinner.start("Checking settings");
|
||||
|
||||
const parseRes = UpdateCommandOptionsSchema.safeParse(anyOptions);
|
||||
if (!parseRes.success) {
|
||||
loadingSpinner.stop(chalkError(parseRes.error.message));
|
||||
return;
|
||||
}
|
||||
const options = parseRes.data;
|
||||
|
||||
const triggerDevPackage = "@trigger.dev";
|
||||
const packageJSONPath = path.join(projectPath, "package.json");
|
||||
const packageData = readJSONFileSync(packageJSONPath);
|
||||
if (!packageData) {
|
||||
loadingSpinner.stop(chalkError("Couldn't load package.json"));
|
||||
return;
|
||||
}
|
||||
|
||||
loadingSpinner.message("Checking for updates");
|
||||
|
||||
const packageMaps: { [k: string]: { type: string; version: string } } = {};
|
||||
const packageDependencies = packageData.dependencies || {};
|
||||
const packageDevDependencies = packageData.devDependencies || {};
|
||||
Object.keys(packageDependencies).forEach((i) => {
|
||||
packageMaps[i] = { type: "dependencies", version: packageDependencies[i] };
|
||||
});
|
||||
Object.keys(packageDevDependencies).forEach((i) => {
|
||||
packageMaps[i] = {
|
||||
type: "devDependencies",
|
||||
version: packageDevDependencies[i],
|
||||
};
|
||||
});
|
||||
|
||||
const targetVersion = getTargetVersion(options.to);
|
||||
|
||||
// Use npm-check-updates to get updated dependency versions
|
||||
const ncuOptions: RunOptions = {
|
||||
packageData,
|
||||
upgrade: true,
|
||||
jsonUpgraded: true,
|
||||
target: targetVersion,
|
||||
};
|
||||
|
||||
// Can either give a json like package.json or just with deps and their new versions
|
||||
const updatedDependencies: { [k: string]: any } | void = await run(ncuOptions);
|
||||
|
||||
if (!updatedDependencies) {
|
||||
loadingSpinner.stop(chalkError("Couldn't update dependencies"));
|
||||
return;
|
||||
}
|
||||
|
||||
const ifUpdatedDependenciesIsPackageJSON =
|
||||
updatedDependencies.hasOwnProperty("dependencies") ||
|
||||
updatedDependencies.hasOwnProperty("devDependencies");
|
||||
|
||||
const dependencies = updatedDependencies.dependencies || {};
|
||||
const devDependencies = updatedDependencies.devDependencies || {};
|
||||
|
||||
const allDependencies = ifUpdatedDependenciesIsPackageJSON
|
||||
? Object.keys({ ...dependencies, ...devDependencies })
|
||||
: Object.keys(updatedDependencies);
|
||||
|
||||
const triggerPackages = allDependencies.filter((pkg) => pkg.startsWith(triggerDevPackage));
|
||||
|
||||
// If there are no @trigger.dev packages
|
||||
if (triggerPackages.length === 0) {
|
||||
loadingSpinner.stop(chalkSuccess(`All @trigger.dev/* packages are already up to date.`));
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter the packages with null and what don't match what
|
||||
// they are installed with so that they can be updated
|
||||
const packagesToUpdate = triggerPackages.filter((pkg: string) => updatedDependencies[pkg]);
|
||||
|
||||
// If no packages require any updation
|
||||
if (packagesToUpdate.length === 0) {
|
||||
loadingSpinner.stop(chalkSuccess(`All @trigger.dev/* packages are already up to date.`));
|
||||
return;
|
||||
}
|
||||
|
||||
let applyUpdates = targetVersion !== "latest";
|
||||
|
||||
if (targetVersion === "latest") {
|
||||
applyUpdates = await hasUserConfirmed(packagesToUpdate, packageMaps, updatedDependencies);
|
||||
}
|
||||
|
||||
if (applyUpdates) {
|
||||
const newPackageJSON = packageData;
|
||||
packagesToUpdate.forEach((packageName) => {
|
||||
const tmp = packageMaps[packageName];
|
||||
if (tmp) {
|
||||
newPackageJSON[tmp.type][packageName] = updatedDependencies[packageName];
|
||||
}
|
||||
export function configureUpdateCommand(program: Command) {
|
||||
return program
|
||||
.command("update")
|
||||
.description("Updates all @trigger.dev/* packages to match the CLI version")
|
||||
.argument("[path]", "The path to the directory that contains the package.json file", ".")
|
||||
.option(
|
||||
"-l, --log-level <level>",
|
||||
"The CLI log level to use (debug, info, log, warn, error, none). This does not effect the log level of your trigger.dev tasks.",
|
||||
"log"
|
||||
)
|
||||
.option("--skip-telemetry", "Opt-out of sending telemetry")
|
||||
.action(async (path, options) => {
|
||||
wrapCommandAction("dev", UpdateCommandOptions, options, async (opts) => {
|
||||
await printStandloneInitialBanner(true);
|
||||
await updateCommand(path, opts);
|
||||
});
|
||||
});
|
||||
await writeJSONFile(packageJSONPath, newPackageJSON);
|
||||
await installDependencies(projectPath);
|
||||
}
|
||||
}
|
||||
|
||||
// expects a version number, or latest.
|
||||
// if version number is specified, prepend it with '@' for ncu.
|
||||
function getTargetVersion(toVersion?: string): NcuRunOptionTarget {
|
||||
if (!toVersion) {
|
||||
return "latest";
|
||||
}
|
||||
return toVersion === "latest" ? "latest" : `@${toVersion}`;
|
||||
const triggerPackageFilter = /^@trigger\.dev/;
|
||||
|
||||
export async function updateCommand(dir: string, options: UpdateCommandOptions) {
|
||||
await updateTriggerPackages(dir, options);
|
||||
}
|
||||
|
||||
async function hasUserConfirmed(
|
||||
packagesToUpdate: string[],
|
||||
packageMaps: { [x: string]: { type: string; version: string } },
|
||||
updatedDependencies: { [x: string]: any }
|
||||
): Promise<boolean> {
|
||||
// Inform the user of the dependencies that can be updated
|
||||
console.log("\nNewer versions found for the following packages:");
|
||||
console.table(
|
||||
packagesToUpdate.map((i) => ({
|
||||
name: i,
|
||||
old: packageMaps[i]?.version,
|
||||
new: updatedDependencies[i],
|
||||
}))
|
||||
export async function updateTriggerPackages(
|
||||
dir: string,
|
||||
options: UpdateCommandOptions,
|
||||
embedded?: boolean,
|
||||
requireUpdate?: boolean
|
||||
) {
|
||||
if (!embedded) {
|
||||
intro("Updating packages");
|
||||
}
|
||||
|
||||
const projectPath = resolve(process.cwd(), dir);
|
||||
|
||||
const { packageJson, readonlyPackageJson, packageJsonPath } = await getPackageJson(projectPath);
|
||||
|
||||
if (!packageJson) {
|
||||
log.error("Failed to load package.json. Try to re-run with `-l debug` to see what's going on.");
|
||||
return;
|
||||
}
|
||||
|
||||
const cliVersion = getVersion();
|
||||
const newCliVersion = await updateCheck();
|
||||
|
||||
if (newCliVersion) {
|
||||
prettyWarning(
|
||||
"You're not running the latest CLI version, please consider updating ASAP",
|
||||
`Current: ${cliVersion}\nLatest: ${newCliVersion}`,
|
||||
"Run latest: npx trigger.dev@beta"
|
||||
);
|
||||
}
|
||||
|
||||
const triggerDependencies = getTriggerDependencies(packageJson);
|
||||
|
||||
function getVersionMismatches(deps: Dependency[], targetVersion: string): Dependency[] {
|
||||
const mismatches: Dependency[] = [];
|
||||
|
||||
for (const dep of deps) {
|
||||
if (dep.version === targetVersion) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mismatches.push(dep);
|
||||
}
|
||||
|
||||
return mismatches;
|
||||
}
|
||||
|
||||
const versionMismatches = getVersionMismatches(triggerDependencies, cliVersion);
|
||||
|
||||
if (versionMismatches.length === 0) {
|
||||
if (!embedded) {
|
||||
outro(`Nothing to do${newCliVersion ? " ..but you should really update your CLI!" : ""}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
prettyWarning(
|
||||
"Mismatch between your CLI version and installed packages",
|
||||
"We recommend pinned versions for guaranteed compatibility"
|
||||
);
|
||||
|
||||
// Ask the user if they want to update the dependencies
|
||||
const shouldContinue = await confirm({
|
||||
message: "Do you want to update these packages in package.json and re-install dependencies?",
|
||||
});
|
||||
if (!process.stdout.isTTY) {
|
||||
// Running in CI with version mismatch detected
|
||||
outro("Deploy failed");
|
||||
|
||||
return shouldContinue as boolean;
|
||||
console.log(
|
||||
`ERROR: Version mismatch detected while running in CI. This won't end well. Aborting.
|
||||
|
||||
Please run the dev command locally and check that your CLI version matches the one printed below. Additionally, all \`@trigger.dev/*\` packages also need to match this version.
|
||||
|
||||
If your local CLI version doesn't match the one below, you may want to add the \`trigger.dev\` package to your dependencies. You will also have to update your workflow deploy command to \`npx trigger.dev deploy\` to ensure your pinned CLI version is used.
|
||||
|
||||
CLI version: ${cliVersion}
|
||||
|
||||
Current package versions that don't match the CLI:
|
||||
${versionMismatches.map((dep) => `- ${dep.name}@${dep.version}`).join("\n")}\n`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
log.message(""); // spacing
|
||||
|
||||
// Always require user confirmation
|
||||
const userWantsToUpdate = await updateConfirmation(versionMismatches, cliVersion);
|
||||
|
||||
if (isCancel(userWantsToUpdate)) {
|
||||
throw new OutroCommandError();
|
||||
}
|
||||
|
||||
if (!userWantsToUpdate) {
|
||||
if (requireUpdate) {
|
||||
outro("You shall not pass!");
|
||||
|
||||
logger.log(
|
||||
`${chalkError(
|
||||
"X Error:"
|
||||
)} Update required: Version mismatches are a common source of bugs and errors. Please update or use \`--skip-update-check\` at your own risk.\n`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!embedded) {
|
||||
outro("You've been warned!");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const installSpinner = spinner();
|
||||
installSpinner.start("Writing new package.json file");
|
||||
|
||||
// Backup package.json
|
||||
const packageJsonBackupPath = `${packageJsonPath}.bak`;
|
||||
await writeJSONFile(packageJsonBackupPath, readonlyPackageJson, true);
|
||||
|
||||
const exitHandler = async (sig: any) => {
|
||||
log.warn(
|
||||
`You may have to manually roll back any package.json changes. Backup written to ${packageJsonBackupPath}`
|
||||
);
|
||||
};
|
||||
|
||||
// Add exit handler to warn about manual rollback of package.json
|
||||
// Automatically rolling back can end up overwriting with an empty file instead
|
||||
process.prependOnceListener("exit", exitHandler);
|
||||
|
||||
// Update package.json
|
||||
mutatePackageJsonWithUpdatedPackages(packageJson, versionMismatches, cliVersion);
|
||||
await writeJSONFile(packageJsonPath, packageJson, true);
|
||||
|
||||
async function revertPackageJsonChanges() {
|
||||
await writeJSONFile(packageJsonPath, readonlyPackageJson, true);
|
||||
await removeFile(packageJsonBackupPath);
|
||||
}
|
||||
|
||||
installSpinner.message("Installing new package versions");
|
||||
|
||||
const jsProject = new JavascriptProject(projectPath);
|
||||
|
||||
let packageManager: PackageManager | undefined;
|
||||
|
||||
try {
|
||||
packageManager = await jsProject.getPackageManager();
|
||||
|
||||
installSpinner.message(`Installing new package versions with ${packageManager}`);
|
||||
|
||||
await jsProject.install();
|
||||
} catch (error) {
|
||||
installSpinner.stop(
|
||||
`Failed to install new package versions${packageManager ? ` with ${packageManager}` : ""}`
|
||||
);
|
||||
|
||||
// Remove exit handler in case of failure
|
||||
process.removeListener("exit", exitHandler);
|
||||
|
||||
await revertPackageJsonChanges();
|
||||
throw error;
|
||||
}
|
||||
|
||||
installSpinner.stop("Installed new package versions");
|
||||
|
||||
// Remove exit handler once packages have been updated, also delete backup file
|
||||
process.removeListener("exit", exitHandler);
|
||||
await removeFile(packageJsonBackupPath);
|
||||
|
||||
if (!embedded) {
|
||||
outro(
|
||||
`Packages updated${newCliVersion ? " ..but you should really update your CLI too!" : ""}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
type Dependency = {
|
||||
type: "dependencies" | "devDependencies";
|
||||
name: string;
|
||||
version: string;
|
||||
};
|
||||
|
||||
function getTriggerDependencies(packageJson: PackageJson): Dependency[] {
|
||||
const deps: Dependency[] = [];
|
||||
|
||||
for (const type of ["dependencies", "devDependencies"] as const) {
|
||||
for (const [name, version] of Object.entries(packageJson[type] ?? {})) {
|
||||
if (!version) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (version.startsWith("workspace")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!triggerPackageFilter.test(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ignoredPackages = ["@trigger.dev/companyicons"];
|
||||
|
||||
if (ignoredPackages.includes(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
deps.push({ type, name, version });
|
||||
}
|
||||
}
|
||||
|
||||
return deps;
|
||||
}
|
||||
|
||||
function mutatePackageJsonWithUpdatedPackages(
|
||||
packageJson: PackageJson,
|
||||
depsToUpdate: Dependency[],
|
||||
targetVersion: string
|
||||
) {
|
||||
for (const { type, name, version } of depsToUpdate) {
|
||||
if (!packageJson[type]) {
|
||||
throw new Error(
|
||||
`No ${type} entry found in package.json. Please try to upgrade manually instead.`
|
||||
);
|
||||
}
|
||||
|
||||
packageJson[type]![name] = targetVersion;
|
||||
}
|
||||
}
|
||||
|
||||
function printUpdateTable(depsToUpdate: Dependency[], targetVersion: string): void {
|
||||
log.message("Suggested updates");
|
||||
|
||||
const tableData = depsToUpdate.map((dep) => ({
|
||||
package: dep.name,
|
||||
old: dep.version,
|
||||
new: targetVersion,
|
||||
}));
|
||||
|
||||
logger.table(tableData);
|
||||
}
|
||||
|
||||
async function updateConfirmation(depsToUpdate: Dependency[], targetVersion: string) {
|
||||
printUpdateTable(depsToUpdate, targetVersion);
|
||||
|
||||
let confirmMessage = "Would you like to apply those updates?";
|
||||
|
||||
return await confirm({
|
||||
message: confirmMessage,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getPackageJson(absoluteProjectPath: string) {
|
||||
const packageJsonPath = join(absoluteProjectPath, "package.json");
|
||||
|
||||
const readonlyPackageJson = Object.freeze((await readJSONFile(packageJsonPath)) as PackageJson);
|
||||
|
||||
const packageJson = structuredClone(readonlyPackageJson);
|
||||
|
||||
return { packageJson, readonlyPackageJson, packageJsonPath };
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export function assertExhaustive(x: never): never {
|
||||
throw new Error("Unexpected object: " + x);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { log } from "@clack/prompts";
|
||||
import chalk from "chalk";
|
||||
|
||||
export const green = "#4FFF54";
|
||||
@@ -63,3 +64,26 @@ export function prettyPrintDate(date: Date = new Date()) {
|
||||
|
||||
return formattedDate;
|
||||
}
|
||||
|
||||
export function prettyWarning(header: string, body?: string, footer?: string) {
|
||||
const prefix = "Warning: ";
|
||||
const indent = Array(prefix.length).fill(" ").join("");
|
||||
const spacing = "\n\n";
|
||||
|
||||
const prettyPrefix = chalkWarning(prefix);
|
||||
|
||||
const withIndents = (text?: string) =>
|
||||
text
|
||||
?.split("\n")
|
||||
.map((line) => `${indent}${line}`)
|
||||
.join("\n");
|
||||
|
||||
const prettyBody = withIndents(body);
|
||||
const prettyFooter = withIndents(footer);
|
||||
|
||||
log.warn(
|
||||
`${prettyPrefix}${header}${prettyBody ? `${spacing}${prettyBody}` : ""}${
|
||||
prettyFooter ? `${spacing}${prettyFooter}` : ""
|
||||
}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@ import { relative } from "node:path";
|
||||
import { chalkError, chalkPurple, chalkGrey, chalkGreen, chalkWarning } from "./cliOutput";
|
||||
import { logger } from "./logger";
|
||||
import { ReadConfigResult } from "./configFiles";
|
||||
import { TaskMetadataParseError } from "../workers/common/errors";
|
||||
import { z } from "zod";
|
||||
import { groupTaskMetadataIssuesByTask } from "@trigger.dev/core/v3";
|
||||
import terminalLink from "terminal-link";
|
||||
import { docs } from "./links";
|
||||
|
||||
export type ESMRequireError = {
|
||||
type: "esm-require-error";
|
||||
@@ -86,6 +87,10 @@ export function logESMRequireError(parsedError: ESMRequireError, resolvedConfig:
|
||||
)}. This will bundle the module with your code.\n`
|
||||
);
|
||||
}
|
||||
|
||||
logger.log(
|
||||
`${chalkGrey("○")} For more info see the ${terminalLink("relevant docs", docs.config.esm)}.\n`
|
||||
);
|
||||
}
|
||||
|
||||
export type PackageNotFoundError = {
|
||||
|
||||
@@ -67,8 +67,8 @@ export async function safeFeadJSONFile(path: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function writeJSONFile(path: string, json: any) {
|
||||
await writeFile(path, JSON.stringify(json), "utf8");
|
||||
export async function writeJSONFile(path: string, json: any, pretty = false) {
|
||||
await writeFile(path, JSON.stringify(json, undefined, pretty ? 2 : undefined), "utf8");
|
||||
}
|
||||
|
||||
export function readJSONFileSync(path: string) {
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import { findUp } from "find-up";
|
||||
import { basename } from "path";
|
||||
import { logger } from "./logger";
|
||||
|
||||
export type PackageManager = "npm" | "pnpm" | "yarn";
|
||||
|
||||
export async function getUserPackageManager(path: string): Promise<PackageManager> {
|
||||
const packageManager = await detectPackageManager(path);
|
||||
logger.debug("Detected package manager", { packageManager });
|
||||
return packageManager;
|
||||
}
|
||||
|
||||
async function detectPackageManager(path: string): Promise<PackageManager> {
|
||||
try {
|
||||
return await detectPackageManagerFromArtifacts(path);
|
||||
} catch (error) {
|
||||
@@ -29,19 +37,30 @@ function detectPackageManagerFromCurrentCommand(): PackageManager {
|
||||
}
|
||||
|
||||
async function detectPackageManagerFromArtifacts(path: string): Promise<PackageManager> {
|
||||
const packageFiles = [
|
||||
{ name: "yarn.lock", pm: "yarn" } as const,
|
||||
{ name: "pnpm-lock.yaml", pm: "pnpm" } as const,
|
||||
{ name: "package-lock.json", pm: "npm" } as const,
|
||||
{ name: "npm-shrinkwrap.json", pm: "npm" } as const,
|
||||
];
|
||||
const artifacts = {
|
||||
yarn: "yarn.lock",
|
||||
pnpm: "pnpm-lock.yaml",
|
||||
npm: "package-lock.json",
|
||||
npmShrinkwrap: "npm-shrinkwrap.json",
|
||||
};
|
||||
|
||||
for (const { name, pm } of packageFiles) {
|
||||
const foundPath = await findUp(name, { cwd: path });
|
||||
if (typeof foundPath === "string") {
|
||||
return pm;
|
||||
}
|
||||
const foundPath = await findUp(Object.values(artifacts), { cwd: path });
|
||||
|
||||
if (!foundPath) {
|
||||
throw new Error("Could not detect package manager from artifacts");
|
||||
}
|
||||
|
||||
throw new Error("Could not detect package manager from artifacts");
|
||||
logger.debug("Found path from package manager artifacts", { foundPath });
|
||||
|
||||
switch (basename(foundPath)) {
|
||||
case artifacts.yarn:
|
||||
return "yarn";
|
||||
case artifacts.pnpm:
|
||||
return "pnpm";
|
||||
case artifacts.npm:
|
||||
case artifacts.npmShrinkwrap:
|
||||
return "npm";
|
||||
default:
|
||||
throw new Error(`Unhandled package manager detection path: ${foundPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import { logger } from "./logger.js";
|
||||
import { spinner } from "./windows";
|
||||
|
||||
export async function printInitialBanner(performUpdateCheck = true) {
|
||||
const packageVersion = getVersion();
|
||||
const text = `\n${logo()} ${chalkGrey(`(${packageVersion})`)}\n`;
|
||||
const cliVersion = getVersion();
|
||||
const text = `\n${logo()} ${chalkGrey(`(${cliVersion})`)}\n`;
|
||||
|
||||
logger.info(text);
|
||||
|
||||
@@ -22,7 +22,7 @@ export async function printInitialBanner(performUpdateCheck = true) {
|
||||
// Log a slightly more noticeable message if this is a major bump
|
||||
if (maybeNewVersion !== undefined) {
|
||||
loadingSpinner.stop(`Update available ${chalk.green(maybeNewVersion)}`);
|
||||
const currentMajor = parseInt(packageVersion.split(".")[0]!);
|
||||
const currentMajor = parseInt(cliVersion.split(".")[0]!);
|
||||
const newMajor = parseInt(maybeNewVersion.split(".")[0]!);
|
||||
if (newMajor > currentMajor) {
|
||||
logger.warn(
|
||||
@@ -38,9 +38,9 @@ After installation, run Trigger.dev with \`npx trigger.dev\`.`
|
||||
}
|
||||
|
||||
export async function printStandloneInitialBanner(performUpdateCheck = true) {
|
||||
const packageVersion = getVersion();
|
||||
const cliVersion = getVersion();
|
||||
|
||||
logger.log(`\n${logo()} ${chalkGrey("(v3 Developer Preview)")}`);
|
||||
logger.log(`\n${logo()} ${chalkGrey(`(${cliVersion})`)}\n`);
|
||||
|
||||
if (performUpdateCheck) {
|
||||
const maybeNewVersion = await updateCheck();
|
||||
@@ -54,7 +54,11 @@ export async function printStandloneInitialBanner(performUpdateCheck = true) {
|
||||
logger.log(`${chalkGrey("-".repeat(54))}`);
|
||||
}
|
||||
|
||||
export function printDevBanner() {
|
||||
export function printDevBanner(printTopBorder = true) {
|
||||
if (printTopBorder) {
|
||||
logger.log(chalkGrey("-".repeat(54)));
|
||||
}
|
||||
|
||||
logger.log(
|
||||
`${chalkGrey("Key:")} ${chalkWorker("Version")} ${chalkGrey("|")} ${chalkTask(
|
||||
"Task"
|
||||
@@ -68,7 +72,7 @@ async function doUpdateCheck(): Promise<string | undefined> {
|
||||
try {
|
||||
// default cache for update check is 1 day
|
||||
update = await checkForUpdate(pkg, {
|
||||
distTag: pkg.version.startsWith("0.0.0") ? "beta" : "latest",
|
||||
distTag: pkg.version.startsWith("3.0.0-beta") ? "beta" : "latest",
|
||||
});
|
||||
} catch (err) {
|
||||
// ignore error
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
import chalk from "chalk";
|
||||
import { execa } from "execa";
|
||||
import { getUserPackageManager, type PackageManager } from "./getUserPackageManager.js";
|
||||
import { logger } from "./logger.js";
|
||||
import { spinner } from "./windows.js";
|
||||
|
||||
export async function installDependencies(projectDir: string) {
|
||||
logger.info("Installing dependencies...");
|
||||
|
||||
const pkgManager = await getUserPackageManager(projectDir);
|
||||
|
||||
const installSpinner = await runInstallCommand(pkgManager, projectDir);
|
||||
|
||||
// If the spinner was used to show the progress, use succeed method on it
|
||||
// If not, use the succeed on a new spinner
|
||||
(installSpinner || spinner()).stop(chalk.green("Successfully installed dependencies!\n"));
|
||||
}
|
||||
|
||||
async function runInstallCommand(
|
||||
pkgManager: PackageManager,
|
||||
projectDir: string
|
||||
): Promise<ReturnType<typeof spinner> | null> {
|
||||
switch (pkgManager) {
|
||||
// When using npm, inherit the stderr stream so that the progress bar is shown
|
||||
case "npm":
|
||||
await execa(pkgManager, ["install"], {
|
||||
cwd: projectDir,
|
||||
stderr: "inherit",
|
||||
});
|
||||
|
||||
return null;
|
||||
// When using yarn or pnpm, use the stdout stream and ora spinner to show the progress
|
||||
case "pnpm": {
|
||||
const loadingSpinner = spinner();
|
||||
loadingSpinner.start("Running pnpm install...");
|
||||
const pnpmSubprocess = execa(pkgManager, ["install"], {
|
||||
cwd: projectDir,
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
await new Promise<void>((res, rej) => {
|
||||
pnpmSubprocess.stdout?.on("data", (data: Buffer) => {
|
||||
const text = data.toString();
|
||||
|
||||
if (text.includes("Progress")) {
|
||||
loadingSpinner.message(text.includes("|") ? text.split(" | ")[1] ?? "" : text);
|
||||
}
|
||||
});
|
||||
pnpmSubprocess.on("error", (e) => rej(e));
|
||||
pnpmSubprocess.on("close", () => res());
|
||||
});
|
||||
|
||||
return loadingSpinner;
|
||||
}
|
||||
case "yarn": {
|
||||
const loadingSpinner = spinner();
|
||||
loadingSpinner.start("Running yarn...");
|
||||
const yarnSubprocess = execa(pkgManager, [], {
|
||||
cwd: projectDir,
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
await new Promise<void>((res, rej) => {
|
||||
yarnSubprocess.stdout?.on("data", (data: Buffer) => {
|
||||
loadingSpinner.message(data.toString());
|
||||
});
|
||||
yarnSubprocess.on("error", (e) => rej(e));
|
||||
yarnSubprocess.on("close", () => res());
|
||||
});
|
||||
|
||||
return loadingSpinner;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,17 +25,6 @@ export async function installPackages(
|
||||
);
|
||||
}
|
||||
|
||||
async function getPackageVersion(path: string) {
|
||||
try {
|
||||
const packageJsonPath = join(path, "package.json");
|
||||
const packageJson = await readJSONFile(packageJsonPath);
|
||||
|
||||
return packageJson.version;
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Expects path to be in the format:
|
||||
// - source-map-support/register.js
|
||||
// - @opentelemetry/api
|
||||
|
||||
@@ -3,6 +3,8 @@ import { join } from "node:path";
|
||||
import { readJSONFileSync } from "./fileSystem";
|
||||
import { logger } from "./logger";
|
||||
import { PackageManager, getUserPackageManager } from "./getUserPackageManager";
|
||||
import { PackageJson } from "type-fest";
|
||||
import { assertExhaustive } from "./assertExhaustive";
|
||||
|
||||
export type ResolveOptions = { allowDev: boolean };
|
||||
|
||||
@@ -49,14 +51,14 @@ const BuiltInModules = new Set([
|
||||
]);
|
||||
|
||||
export class JavascriptProject {
|
||||
private _packageJson?: any;
|
||||
private _packageJson?: PackageJson;
|
||||
private _packageManager?: PackageManager;
|
||||
|
||||
constructor(private projectPath: string) {}
|
||||
|
||||
private get packageJson() {
|
||||
if (!this._packageJson) {
|
||||
this._packageJson = readJSONFileSync(join(this.projectPath, "package.json"));
|
||||
this._packageJson = readJSONFileSync(join(this.projectPath, "package.json")) as PackageJson;
|
||||
}
|
||||
|
||||
return this._packageJson;
|
||||
@@ -64,21 +66,29 @@ export class JavascriptProject {
|
||||
|
||||
public get scripts(): Record<string, string> {
|
||||
return {
|
||||
postinstall: this.packageJson.scripts?.postinstall,
|
||||
postinstall: this.packageJson.scripts?.postinstall ?? "",
|
||||
};
|
||||
}
|
||||
|
||||
async install(): Promise<void> {
|
||||
const command = await this.#getCommand();
|
||||
|
||||
try {
|
||||
await command.installDependencies({
|
||||
cwd: this.projectPath,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.debug(`Failed to install dependencies using ${command.name}`, {
|
||||
error,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async resolve(packageName: string, options?: ResolveOptions): Promise<string | undefined> {
|
||||
if (BuiltInModules.has(packageName)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!this._packageManager) {
|
||||
this._packageManager = await getUserPackageManager(this.projectPath);
|
||||
}
|
||||
|
||||
const packageManager = this._packageManager;
|
||||
|
||||
const opts = { allowDev: false, ...options };
|
||||
|
||||
const packageJsonVersion = this.packageJson.dependencies?.[packageName];
|
||||
@@ -95,12 +105,7 @@ export class JavascriptProject {
|
||||
}
|
||||
}
|
||||
|
||||
const command =
|
||||
packageManager === "npm"
|
||||
? new NPMCommands()
|
||||
: packageManager === "pnpm"
|
||||
? new PNPMCommands()
|
||||
: new YarnCommands();
|
||||
const command = await this.#getCommand();
|
||||
|
||||
try {
|
||||
const version = await command.resolveDependencyVersion(packageName, {
|
||||
@@ -117,6 +122,29 @@ export class JavascriptProject {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async #getCommand(): Promise<PackageManagerCommands> {
|
||||
const packageManager = await this.getPackageManager();
|
||||
|
||||
switch (packageManager) {
|
||||
case "npm":
|
||||
return new NPMCommands();
|
||||
case "pnpm":
|
||||
return new PNPMCommands();
|
||||
case "yarn":
|
||||
return new YarnCommands();
|
||||
default:
|
||||
assertExhaustive(packageManager);
|
||||
}
|
||||
}
|
||||
|
||||
async getPackageManager(): Promise<PackageManager> {
|
||||
if (!this._packageManager) {
|
||||
this._packageManager = await getUserPackageManager(this.projectPath);
|
||||
}
|
||||
|
||||
return this._packageManager;
|
||||
}
|
||||
}
|
||||
|
||||
type PnpmList = {
|
||||
@@ -140,6 +168,10 @@ type PackageManagerOptions = {
|
||||
};
|
||||
|
||||
interface PackageManagerCommands {
|
||||
name: string;
|
||||
|
||||
installDependencies(options: PackageManagerOptions): Promise<void>;
|
||||
|
||||
resolveDependencyVersion(
|
||||
packageName: string,
|
||||
options: PackageManagerOptions
|
||||
@@ -151,15 +183,21 @@ class PNPMCommands implements PackageManagerCommands {
|
||||
return "pnpm";
|
||||
}
|
||||
|
||||
async resolveDependencyVersion(
|
||||
packageName: string,
|
||||
options: PackageManagerOptions
|
||||
): Promise<string | undefined> {
|
||||
const cmd = process.platform === "win32" ? "pnpm.cmd" : "pnpm";
|
||||
const { stdout } = await $({ cwd: options.cwd })`${cmd} list ${packageName} -r --json`;
|
||||
private get cmd() {
|
||||
return process.platform === "win32" ? "pnpm.cmd" : "pnpm";
|
||||
}
|
||||
|
||||
async installDependencies(options: PackageManagerOptions) {
|
||||
const { stdout, stderr } = await $({ cwd: options.cwd })`${this.cmd} install`;
|
||||
|
||||
logger.debug(`Installing dependencies using ${this.name}`, { stdout, stderr });
|
||||
}
|
||||
|
||||
async resolveDependencyVersion(packageName: string, options: PackageManagerOptions) {
|
||||
const { stdout } = await $({ cwd: options.cwd })`${this.cmd} list ${packageName} -r --json`;
|
||||
const result = JSON.parse(stdout) as PnpmList;
|
||||
|
||||
logger.debug(`Resolving ${packageName} version using pnpm`, { result });
|
||||
logger.debug(`Resolving ${packageName} version using ${this.name}`, { result });
|
||||
|
||||
// Return the first dependency version that matches the package name
|
||||
for (const dep of result) {
|
||||
@@ -189,15 +227,21 @@ class NPMCommands implements PackageManagerCommands {
|
||||
return "npm";
|
||||
}
|
||||
|
||||
async resolveDependencyVersion(
|
||||
packageName: string,
|
||||
options: PackageManagerOptions
|
||||
): Promise<string | undefined> {
|
||||
const cmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
||||
const { stdout } = await $({ cwd: options.cwd })`${cmd} list ${packageName} --json`;
|
||||
private get cmd() {
|
||||
return process.platform === "win32" ? "npm.cmd" : "npm";
|
||||
}
|
||||
|
||||
async installDependencies(options: PackageManagerOptions) {
|
||||
const { stdout, stderr } = await $({ cwd: options.cwd })`${this.cmd} install`;
|
||||
|
||||
logger.debug(`Installing dependencies using ${this.name}`, { stdout, stderr });
|
||||
}
|
||||
|
||||
async resolveDependencyVersion(packageName: string, options: PackageManagerOptions) {
|
||||
const { stdout } = await $({ cwd: options.cwd })`${this.cmd} list ${packageName} --json`;
|
||||
const output = JSON.parse(stdout) as NpmListOutput;
|
||||
|
||||
logger.debug(`Resolving ${packageName} version using npm`, { output });
|
||||
logger.debug(`Resolving ${packageName} version using ${this.name}`, { output });
|
||||
|
||||
return this.#recursivelySearchDependencies(output.dependencies, packageName);
|
||||
}
|
||||
@@ -227,17 +271,22 @@ class YarnCommands implements PackageManagerCommands {
|
||||
return "yarn";
|
||||
}
|
||||
|
||||
async resolveDependencyVersion(
|
||||
packageName: string,
|
||||
options: PackageManagerOptions
|
||||
): Promise<string | undefined> {
|
||||
const cmd = process.platform === "win32" ? "yarn.cmd" : "yarn";
|
||||
private get cmd() {
|
||||
return process.platform === "win32" ? "yarn.cmd" : "yarn";
|
||||
}
|
||||
|
||||
const { stdout } = await $({ cwd: options.cwd })`${cmd} info ${packageName} --json`;
|
||||
async installDependencies(options: PackageManagerOptions) {
|
||||
const { stdout, stderr } = await $({ cwd: options.cwd })`${this.cmd} install`;
|
||||
|
||||
logger.debug(`Installing dependencies using ${this.name}`, { stdout, stderr });
|
||||
}
|
||||
|
||||
async resolveDependencyVersion(packageName: string, options: PackageManagerOptions) {
|
||||
const { stdout } = await $({ cwd: options.cwd })`${this.cmd} info ${packageName} --json`;
|
||||
|
||||
const lines = stdout.split("\n");
|
||||
|
||||
logger.debug(`Resolving ${packageName} version using yarn`, { lines });
|
||||
logger.debug(`Resolving ${packageName} version using ${this.name}`, { lines });
|
||||
|
||||
for (const line of lines) {
|
||||
const json = JSON.parse(line);
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
export const docs = {
|
||||
config: {
|
||||
home: "https://trigger.dev/docs/v3/trigger-config",
|
||||
esm: "https://trigger.dev/docs/v3/trigger-config#esm-only-packages",
|
||||
prisma: "https://trigger.dev/docs/v3/trigger-config#prisma-and-other-generators",
|
||||
additionalPackages: "https://trigger.dev/docs/v3/trigger-config#prisma-and-other-generators",
|
||||
},
|
||||
};
|
||||
|
||||
export const getInTouch = "https://trigger.dev/contact";
|
||||
@@ -1,5 +1,20 @@
|
||||
# create-trigger
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/core@3.0.0-beta.17
|
||||
- @trigger.dev/yalt@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ed2a26c86]
|
||||
- @trigger.dev/core@3.0.0-beta.16
|
||||
- @trigger.dev/yalt@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/cli",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "The Trigger.dev CLI",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @trigger.dev/core-apps
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
## 3.0.0-beta.14
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@trigger.dev/core-apps",
|
||||
"description": "Backend core code used across apps",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @trigger.dev/core-backend
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
## 3.0.0-beta.14
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/core-backend",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Core code used across `@trigger.dev/sdk` and Trigger.dev server",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
# internal-platform
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ed2a26c86: - Fix additionalFiles that aren't decendants
|
||||
- Stop swallowing uncaught exceptions in prod
|
||||
- Improve warnings and errors, fail early on critical warnings
|
||||
- New arg to --save-logs even for successful builds
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/core",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Core code used across the Trigger.dev SDK and platform",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -16,6 +16,8 @@ export interface ProjectConfig {
|
||||
* List of additional files to include in your trigger.dev bundle. e.g. ["./prisma/schema.prisma"]
|
||||
*
|
||||
* Supports glob patterns.
|
||||
*
|
||||
* Note: The path separator for glob patterns is `/`, even on Windows!
|
||||
*/
|
||||
additionalFiles?: string[];
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @trigger.dev/eslint-plugin
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
## 3.0.0-beta.14
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/eslint-plugin",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "ESLint plugin with trigger.dev best practices",
|
||||
"keywords": [
|
||||
"eslint",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @trigger.dev/express
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/express",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Official Express adapter for Trigger.dev",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -23,7 +23,7 @@
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"@trigger.dev/tsconfig": "workspace:*",
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/express": "^4.17.13",
|
||||
@@ -39,7 +39,7 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15"
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @trigger.dev/hono
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/hono",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "A Trigger.dev adapter for Hono.dev",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -32,7 +32,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"hono": "3.x",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15"
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trigger.dev/tsconfig": "workspace:*",
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# @trigger.dev/integration-kit
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/core@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ed2a26c86]
|
||||
- @trigger.dev/core@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/integration-kit",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Trigger.dev Integration Kit has helpers to make creating integrations easier",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @trigger.dev/nestjs
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/nestjs",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Official NestJS adapter for Trigger.dev",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -23,7 +23,7 @@
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"@trigger.dev/tsconfig": "workspace:*",
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/express": "^4.17.13",
|
||||
@@ -41,7 +41,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": ">=10.0.0",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15"
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^10.2.4",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @trigger.dev/nextjs
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/nextjs",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Trigger.dev Next.js integration",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -41,7 +41,7 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"next": ">=12.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @trigger.dev/otlp-importer
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
## 3.0.0-beta.14
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/otlp-importer",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "OpenTelemetry OTLP Importer for Node.js written in TypeScript",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# @trigger.dev/react
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/core@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ed2a26c86]
|
||||
- @trigger.dev/core@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/react",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Trigger.dev React SDK",
|
||||
"license": "MIT",
|
||||
"types": "dist/index.d.ts",
|
||||
@@ -27,7 +27,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "5.0.0-beta.2",
|
||||
"@trigger.dev/core": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/core": "workspace:^3.0.0-beta.17",
|
||||
"debug": "^4.3.4",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @trigger.dev/remix
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/remix",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Trigger.dev Remix integration",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -39,7 +39,7 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17",
|
||||
"@remix-run/server-runtime": ">1.19.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @trigger.dev/sveltekit
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/sveltekit",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "Trigger.dev svelteKit integration",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -39,7 +39,7 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.15"
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.17"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4"
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# @trigger.dev/testing
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/core@3.0.0-beta.17
|
||||
- @trigger.dev/sdk@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ed2a26c86]
|
||||
- @trigger.dev/core@3.0.0-beta.16
|
||||
- @trigger.dev/sdk@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@trigger.dev/testing",
|
||||
"description": "A collection of useful tools to write tests for Trigger.dev.",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# @trigger.dev/sdk
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/core@3.0.0-beta.17
|
||||
- @trigger.dev/core-backend@3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ed2a26c86]
|
||||
- @trigger.dev/core@3.0.0-beta.16
|
||||
- @trigger.dev/core-backend@3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/sdk",
|
||||
"version": "3.0.0-beta.15",
|
||||
"version": "3.0.0-beta.17",
|
||||
"description": "trigger.dev Node.JS SDK",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -48,8 +48,8 @@
|
||||
"@opentelemetry/api": "^1.8.0",
|
||||
"@opentelemetry/api-logs": "^0.48.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.22.0",
|
||||
"@trigger.dev/core": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/core-backend": "workspace:^3.0.0-beta.15",
|
||||
"@trigger.dev/core": "workspace:^3.0.0-beta.17",
|
||||
"@trigger.dev/core-backend": "workspace:^3.0.0-beta.17",
|
||||
"chalk": "^5.2.0",
|
||||
"cronstrue": "^2.21.0",
|
||||
"debug": "^4.3.4",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @trigger.dev/yalt
|
||||
|
||||
## 3.0.0-beta.17
|
||||
|
||||
## 3.0.0-beta.16
|
||||
|
||||
## 3.0.0-beta.15
|
||||
|
||||
## 3.0.0-beta.14
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user