Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ba4f04db37 | |||
| d7911892ee | |||
| f888f85a09 | |||
| e9a63a4868 | |||
| 93ecbc5e05 | |||
| 60a8a5777b | |||
| ed1cd16753 | |||
| 6bb4dd7b6f | |||
| 0b5a0be807 | |||
| 74d1e61e42 | |||
| 12c83a56af | |||
| 75a54540a4 | |||
| b68012f81c | |||
| fb83d58703 | |||
| 52b2a8289c | |||
| 1e93ec4216 | |||
| cb27b7278a | |||
| 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
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"trigger.dev": patch
|
||||
---
|
||||
|
||||
Remove the env var check during deploy (too many false negatives)
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
---
|
||||
|
||||
Make msw a normal dependency (for now) to fix Module Not Found error in Next.js.
|
||||
|
||||
It turns out that webpack will "hoist" dynamically imported modules and attempt to resolve them at build time, even though it's an optional peer dep:
|
||||
|
||||
https://x.com/maverickdotdev/status/1782465214308319404
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"trigger.dev": patch
|
||||
---
|
||||
|
||||
Fixes an issue that was treating v2 trigger directories as v3
|
||||
+12
-1
@@ -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",
|
||||
@@ -51,7 +53,9 @@
|
||||
"cuddly-feet-approve",
|
||||
"dry-walls-check",
|
||||
"eight-pumas-float",
|
||||
"eleven-paws-join",
|
||||
"few-students-share",
|
||||
"funny-swans-destroy",
|
||||
"green-bags-wink",
|
||||
"khaki-apricots-design",
|
||||
"khaki-poems-lay",
|
||||
@@ -63,6 +67,7 @@
|
||||
"loud-actors-remember",
|
||||
"many-ligers-pump",
|
||||
"mighty-camels-joke",
|
||||
"nasty-jars-pump",
|
||||
"new-rivers-tell",
|
||||
"ninety-pets-travel",
|
||||
"odd-poets-own",
|
||||
@@ -70,12 +75,16 @@
|
||||
"poor-flowers-cross",
|
||||
"rare-roses-float",
|
||||
"real-planets-stare",
|
||||
"rich-kangaroos-unite",
|
||||
"rotten-beers-refuse",
|
||||
"rotten-dryers-exercise",
|
||||
"selfish-ducks-sort",
|
||||
"shaggy-spoons-taste",
|
||||
"sharp-emus-compare",
|
||||
"sharp-zebras-serve",
|
||||
"shiny-coats-cry",
|
||||
"silly-suits-switch",
|
||||
"slow-buses-own",
|
||||
"smart-needles-move",
|
||||
"smart-olives-eat",
|
||||
"spicy-lamps-smoke",
|
||||
@@ -91,6 +100,8 @@
|
||||
"tidy-dryers-sleep",
|
||||
"tiny-doors-type",
|
||||
"tiny-elephants-scream",
|
||||
"tricky-bulldogs-heal"
|
||||
"tricky-bulldogs-heal",
|
||||
"tricky-ladybugs-unite",
|
||||
"two-pumas-wait"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"trigger.dev": patch
|
||||
---
|
||||
|
||||
Fix a bug where revoking the CLI token would prevent you from ever logging in again with the CLI.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"trigger.dev": patch
|
||||
---
|
||||
|
||||
Add git to prod worker image which fixes private package installs
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
"trigger.dev": patch
|
||||
---
|
||||
|
||||
Lock SDK and CLI deps on exact core version
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
"trigger.dev": patch
|
||||
"@trigger.dev/core": patch
|
||||
"@trigger.dev/cli": patch
|
||||
---
|
||||
|
||||
Move to our global system from AsyncLocalStorage for the current task context storage
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
"@trigger.dev/core-apps": patch
|
||||
"trigger.dev": patch
|
||||
"@trigger.dev/core": patch
|
||||
---
|
||||
|
||||
Extracting out all the non-SDK related features from the main @trigger.dev/core/v3 export
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"trigger.dev": patch
|
||||
"@trigger.dev/cli": patch
|
||||
---
|
||||
|
||||
Add support for tasks located in subdirectories inside trigger dirs
|
||||
@@ -27,6 +27,6 @@ jobs:
|
||||
uses: ./.github/workflows/unit-tests.yml
|
||||
secrets: inherit
|
||||
|
||||
e2e:
|
||||
uses: ./.github/workflows/e2e.yml
|
||||
secrets: inherit
|
||||
# e2e:
|
||||
# uses: ./.github/workflows/e2e.yml
|
||||
# secrets: inherit
|
||||
|
||||
@@ -49,11 +49,11 @@ jobs:
|
||||
uses: ./.github/workflows/unit-tests.yml
|
||||
secrets: inherit
|
||||
|
||||
e2e:
|
||||
uses: ./.github/workflows/e2e.yml
|
||||
secrets: inherit
|
||||
# e2e:
|
||||
# uses: ./.github/workflows/e2e.yml
|
||||
# secrets: inherit
|
||||
|
||||
publish:
|
||||
needs: [typecheck, units, e2e]
|
||||
needs: [typecheck, units]
|
||||
uses: ./.github/workflows/publish-docker.yml
|
||||
secrets: inherit
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
"execa": "^8.0.1",
|
||||
"nanoid": "^5.0.6",
|
||||
"prom-client": "^15.1.0",
|
||||
"socket.io": "^4.7.4",
|
||||
"socket.io-client": "^4.7.4"
|
||||
"socket.io": "4.7.4",
|
||||
"socket.io-client": "4.7.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18",
|
||||
|
||||
@@ -8,9 +8,9 @@ import {
|
||||
PlatformToCoordinatorMessages,
|
||||
ProdWorkerSocketData,
|
||||
ProdWorkerToCoordinatorMessages,
|
||||
ZodNamespace,
|
||||
ZodSocketConnection,
|
||||
} from "@trigger.dev/core/v3";
|
||||
import { ZodNamespace } from "@trigger.dev/core/v3/zodNamespace";
|
||||
import { ZodSocketConnection } from "@trigger.dev/core/v3/zodSocket";
|
||||
import { HttpReply, getTextBody, SimpleLogger } from "@trigger.dev/core-apps";
|
||||
|
||||
import { collectDefaultMetrics, register, Gauge } from "prom-client";
|
||||
|
||||
@@ -211,7 +211,7 @@ class KubernetesTaskOperations implements TaskOperations {
|
||||
},
|
||||
{
|
||||
name: "populate-taskinfo",
|
||||
image: "busybox",
|
||||
image: "docker.io/library/busybox",
|
||||
command: ["/bin/sh", "-c"],
|
||||
args: ["printenv COORDINATOR_HOST | tee /etc/taskinfo/coordinator-host"],
|
||||
env: [
|
||||
|
||||
@@ -408,7 +408,7 @@ export class TaskMonitor {
|
||||
const createNonZeroExitPod = async (name: string, labels?: Record<string, string>) => {
|
||||
const container = {
|
||||
name,
|
||||
image: "busybox",
|
||||
image: "docker.io/library/busybox",
|
||||
command: ["sh"],
|
||||
args: ["-c", "exit 1"],
|
||||
} satisfies k8s.V1Container;
|
||||
@@ -419,7 +419,7 @@ export class TaskMonitor {
|
||||
const createOoDiskPod = async (name: string, labels?: Record<string, string>) => {
|
||||
const container = {
|
||||
name,
|
||||
image: "busybox",
|
||||
image: "docker.io/library/busybox",
|
||||
command: ["sh"],
|
||||
args: [
|
||||
"-c",
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/server-runtime";
|
||||
import { json } from "@remix-run/server-runtime";
|
||||
import { parseBatchTriggerTaskRequestBody } from "@trigger.dev/core/v3";
|
||||
import { BatchTriggerTaskRequestBody } from "@trigger.dev/core/v3";
|
||||
import { z } from "zod";
|
||||
import { MAX_BATCH_TRIGGER_ITEMS } from "~/consts";
|
||||
import { authenticateApiRequest } from "~/services/apiAuth.server";
|
||||
@@ -46,7 +46,7 @@ export async function action({ request, params }: ActionFunctionArgs) {
|
||||
// Now parse the request body
|
||||
const anyBody = await request.json();
|
||||
|
||||
const body = parseBatchTriggerTaskRequestBody(anyBody);
|
||||
const body = BatchTriggerTaskRequestBody.safeParse(anyBody);
|
||||
|
||||
if (!body.success) {
|
||||
return json({ error: "Invalid request body" }, { status: 400 });
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/server-runtime";
|
||||
import { json } from "@remix-run/server-runtime";
|
||||
import { parseTriggerTaskRequestBody } from "@trigger.dev/core/v3";
|
||||
import { TriggerTaskRequestBody } from "@trigger.dev/core/v3";
|
||||
import { z } from "zod";
|
||||
import { authenticateApiRequest } from "~/services/apiAuth.server";
|
||||
import { logger } from "~/services/logger.server";
|
||||
@@ -52,7 +52,7 @@ export async function action({ request, params }: ActionFunctionArgs) {
|
||||
// Now parse the request body
|
||||
const anyBody = await request.json();
|
||||
|
||||
const body = parseTriggerTaskRequestBody(anyBody);
|
||||
const body = TriggerTaskRequestBody.safeParse(anyBody);
|
||||
|
||||
if (!body.success) {
|
||||
return json({ error: "Invalid request body" }, { status: 400 });
|
||||
|
||||
@@ -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`);
|
||||
|
||||
@@ -134,20 +134,27 @@ export async function authenticatePersonalAccessToken(
|
||||
|
||||
const hashedToken = hashToken(token);
|
||||
|
||||
const personalAccessToken = await prisma.personalAccessToken.update({
|
||||
const personalAccessToken = await prisma.personalAccessToken.findFirst({
|
||||
where: {
|
||||
hashedToken,
|
||||
revokedAt: null,
|
||||
},
|
||||
});
|
||||
|
||||
if (!personalAccessToken) {
|
||||
// The token may have been revoked or is entirely invalid
|
||||
return;
|
||||
}
|
||||
|
||||
await prisma.personalAccessToken.update({
|
||||
where: {
|
||||
id: personalAccessToken.id,
|
||||
},
|
||||
data: {
|
||||
lastAccessedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
if (!personalAccessToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
const decryptedToken = decryptPersonalAccessToken(personalAccessToken);
|
||||
|
||||
if (decryptedToken !== token) {
|
||||
@@ -210,6 +217,18 @@ export async function createPersonalAccessTokenFromAuthorizationCode(
|
||||
},
|
||||
});
|
||||
|
||||
if (existingCliPersonalAccessToken.revokedAt) {
|
||||
// re-activate revoked CLI PAT so we can use it again
|
||||
await prisma.personalAccessToken.update({
|
||||
where: {
|
||||
id: existingCliPersonalAccessToken.id,
|
||||
},
|
||||
data: {
|
||||
revokedAt: null,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
//we don't return the decrypted token
|
||||
return {
|
||||
id: existingCliPersonalAccessToken.id,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import {
|
||||
ZodMessageHandler,
|
||||
ZodMessageSender,
|
||||
clientWebsocketMessages,
|
||||
serverWebsocketMessages,
|
||||
} from "@trigger.dev/core/v3";
|
||||
import { clientWebsocketMessages, serverWebsocketMessages } from "@trigger.dev/core/v3";
|
||||
import { ZodMessageHandler, ZodMessageSender } from "@trigger.dev/core/v3/zodMessageHandler";
|
||||
import { Evt } from "evt";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import type { CloseEvent, ErrorEvent, MessageEvent, WebSocket } from "ws";
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
PlatformToProviderMessages,
|
||||
ProviderToPlatformMessages,
|
||||
SharedQueueToClientMessages,
|
||||
ZodNamespace,
|
||||
} from "@trigger.dev/core/v3";
|
||||
import { ZodNamespace } from "@trigger.dev/core/v3/zodNamespace";
|
||||
import { Server } from "socket.io";
|
||||
import { env } from "~/env.server";
|
||||
import { singleton } from "~/utils/singleton";
|
||||
@@ -71,6 +71,7 @@ function initializeSocketIOServerInstance() {
|
||||
|
||||
function createCoordinatorNamespace(io: Server) {
|
||||
const coordinator = new ZodNamespace({
|
||||
// @ts-ignore - for some reason the built ZodNamespace Server type is not compatible with the Server type here, but only when doing typechecking
|
||||
io,
|
||||
name: "coordinator",
|
||||
authToken: env.COORDINATOR_SECRET,
|
||||
@@ -147,6 +148,7 @@ function createCoordinatorNamespace(io: Server) {
|
||||
|
||||
function createProviderNamespace(io: Server) {
|
||||
const provider = new ZodNamespace({
|
||||
// @ts-ignore - for some reason the built ZodNamespace Server type is not compatible with the Server type here, but only when doing typechecking
|
||||
io,
|
||||
name: "provider",
|
||||
authToken: env.PROVIDER_SECRET,
|
||||
@@ -181,6 +183,7 @@ function createProviderNamespace(io: Server) {
|
||||
|
||||
function createSharedQueueConsumerNamespace(io: Server) {
|
||||
const sharedQueue = new ZodNamespace({
|
||||
// @ts-ignore - for some reason the built ZodNamespace Server type is not compatible with the Server type here, but only when doing typechecking
|
||||
io,
|
||||
name: "shared-queue",
|
||||
authToken: env.PROVIDER_SECRET,
|
||||
@@ -188,7 +191,9 @@ function createSharedQueueConsumerNamespace(io: Server) {
|
||||
serverMessages: SharedQueueToClientMessages,
|
||||
onConnection: async (socket, handler, sender, logger) => {
|
||||
const sharedSocketConnection = new SharedSocketConnection({
|
||||
// @ts-ignore - for some reason the built ZodNamespace Server type is not compatible with the Server type here, but only when doing typechecking
|
||||
namespace: sharedQueue.namespace,
|
||||
// @ts-ignore - for some reason the built ZodNamespace Server type is not compatible with the Server type here, but only when doing typechecking
|
||||
socket,
|
||||
logger,
|
||||
poolSize: env.SHARED_QUEUE_CONSUMER_POOL_SIZE,
|
||||
|
||||
@@ -3,9 +3,9 @@ import {
|
||||
TaskRunExecution,
|
||||
TaskRunExecutionPayload,
|
||||
TaskRunExecutionResult,
|
||||
ZodMessageSender,
|
||||
serverWebsocketMessages,
|
||||
} from "@trigger.dev/core/v3";
|
||||
import { ZodMessageSender } from "@trigger.dev/core/v3/zodMessageHandler";
|
||||
import { BackgroundWorker, BackgroundWorkerTask } from "@trigger.dev/database";
|
||||
import { z } from "zod";
|
||||
import { prisma } from "~/db.server";
|
||||
|
||||
@@ -8,9 +8,9 @@ import {
|
||||
TaskRunExecutionResult,
|
||||
TaskRunFailedExecutionResult,
|
||||
TaskRunSuccessfulExecutionResult,
|
||||
ZodMessageSender,
|
||||
serverWebsocketMessages,
|
||||
} from "@trigger.dev/core/v3";
|
||||
import { ZodMessageSender } from "@trigger.dev/core/v3/zodMessageHandler";
|
||||
import {
|
||||
BackgroundWorker,
|
||||
BackgroundWorkerTask,
|
||||
@@ -26,7 +26,6 @@ import { EnvironmentVariablesRepository } from "../environmentVariables/environm
|
||||
import { generateFriendlyId } from "../friendlyIdentifiers";
|
||||
import { socketIo } from "../handleSocketIo.server";
|
||||
import { findCurrentWorkerDeployment } from "../models/workerDeployment.server";
|
||||
import { CancelAttemptService } from "../services/cancelAttempt.server";
|
||||
import { RestoreCheckpointService } from "../services/restoreCheckpoint.server";
|
||||
import { tracer } from "../tracer.server";
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CoordinatorToPlatformMessages, InferSocketMessageSchema } from "@trigger.dev/core/v3";
|
||||
import { CoordinatorToPlatformMessages } from "@trigger.dev/core/v3";
|
||||
import type { InferSocketMessageSchema } from "@trigger.dev/core/v3/zodSocket";
|
||||
import type {
|
||||
CheckpointRestoreEvent,
|
||||
TaskRunAttemptStatus,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
CoordinatorToPlatformMessages,
|
||||
InferSocketMessageSchema,
|
||||
TaskRunExecution,
|
||||
TaskRunExecutionResult,
|
||||
} from "@trigger.dev/core/v3";
|
||||
import type { InferSocketMessageSchema } from "@trigger.dev/core/v3/zodSocket";
|
||||
import { $transaction, PrismaClientOrTransaction } from "~/db.server";
|
||||
import { logger } from "~/services/logger.server";
|
||||
import { marqs } from "~/v3/marqs/index.server";
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { clientWebsocketMessages, serverWebsocketMessages } from "@trigger.dev/core/v3";
|
||||
import {
|
||||
MessageCatalogToSocketIoEvents,
|
||||
StructuredLogger,
|
||||
ZodMessageHandler,
|
||||
ZodMessageSender,
|
||||
clientWebsocketMessages,
|
||||
serverWebsocketMessages,
|
||||
} from "@trigger.dev/core/v3";
|
||||
MessageCatalogToSocketIoEvents,
|
||||
} from "@trigger.dev/core/v3/zodMessageHandler";
|
||||
import type { StructuredLogger } from "@trigger.dev/core/v3/utils/structuredLogger";
|
||||
import { Evt } from "evt";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { logger } from "~/services/logger.server";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Logger } from "@trigger.dev/core-backend";
|
||||
import { ZodMessageCatalogSchema, ZodMessageHandler, ZodMessageSender } from "@trigger.dev/core/v3";
|
||||
import { ZodMessageCatalogSchema, ZodMessageHandler } from "@trigger.dev/core/v3/zodMessageHandler";
|
||||
import { Evt } from "evt";
|
||||
import Redis, { RedisOptions } from "ioredis";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
"simple-oauth2": "^5.0.0",
|
||||
"simplur": "^3.0.1",
|
||||
"slug": "^6.0.0",
|
||||
"socket.io": "^4.7.4",
|
||||
"socket.io": "4.7.4",
|
||||
"socket.io-adapter": "^2.5.4",
|
||||
"sonner": "^1.0.3",
|
||||
"sqs-consumer": "^7.4.0",
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,37 @@
|
||||
# @trigger.dev/airtable
|
||||
|
||||
## 3.0.0-beta.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e9a63a486]
|
||||
- @trigger.dev/sdk@3.0.0-beta.19
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.19
|
||||
|
||||
## 3.0.0-beta.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- @trigger.dev/sdk@3.0.0-beta.18
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.18
|
||||
|
||||
## 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.19",
|
||||
"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.19",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.19",
|
||||
"airtable": "^0.12.1",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# @trigger.dev/github
|
||||
|
||||
## 3.0.0-beta.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e9a63a486]
|
||||
- @trigger.dev/sdk@3.0.0-beta.19
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.19
|
||||
|
||||
## 3.0.0-beta.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- @trigger.dev/sdk@3.0.0-beta.18
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.18
|
||||
|
||||
## 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.19",
|
||||
"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.19",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.19",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# @trigger.dev/linear
|
||||
|
||||
## 3.0.0-beta.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e9a63a486]
|
||||
- @trigger.dev/sdk@3.0.0-beta.19
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.19
|
||||
|
||||
## 3.0.0-beta.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- @trigger.dev/sdk@3.0.0-beta.18
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.18
|
||||
|
||||
## 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.19",
|
||||
"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.19",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.19",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# @trigger.dev/slack
|
||||
|
||||
## 3.0.0-beta.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e9a63a486]
|
||||
- @trigger.dev/sdk@3.0.0-beta.19
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.19
|
||||
|
||||
## 3.0.0-beta.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- @trigger.dev/sdk@3.0.0-beta.18
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.18
|
||||
|
||||
## 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.19",
|
||||
"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.19",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.19"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# @trigger.dev/plain
|
||||
|
||||
## 3.0.0-beta.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e9a63a486]
|
||||
- @trigger.dev/sdk@3.0.0-beta.19
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.19
|
||||
|
||||
## 3.0.0-beta.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- @trigger.dev/sdk@3.0.0-beta.18
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.18
|
||||
|
||||
## 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.19",
|
||||
"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.19",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.19",
|
||||
"@team-plain/typescript-sdk": "^2.7.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# @trigger.dev/replicate
|
||||
|
||||
## 3.0.0-beta.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e9a63a486]
|
||||
- @trigger.dev/sdk@3.0.0-beta.19
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.19
|
||||
|
||||
## 3.0.0-beta.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- @trigger.dev/sdk@3.0.0-beta.18
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.18
|
||||
|
||||
## 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.19",
|
||||
"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.19",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.19",
|
||||
"replicate": "^0.18.1",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# @trigger.dev/resend
|
||||
|
||||
## 3.0.0-beta.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e9a63a486]
|
||||
- @trigger.dev/sdk@3.0.0-beta.19
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.19
|
||||
|
||||
## 3.0.0-beta.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- @trigger.dev/sdk@3.0.0-beta.18
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.18
|
||||
|
||||
## 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.19",
|
||||
"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.19",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.19",
|
||||
"resend": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# @trigger.dev/sendgrid
|
||||
|
||||
## 3.0.0-beta.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e9a63a486]
|
||||
- @trigger.dev/sdk@3.0.0-beta.19
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.19
|
||||
|
||||
## 3.0.0-beta.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- @trigger.dev/sdk@3.0.0-beta.18
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.18
|
||||
|
||||
## 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.19",
|
||||
"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.19",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.19"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.8.0"
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# @trigger.dev/shopify
|
||||
|
||||
## 3.0.0-beta.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e9a63a486]
|
||||
- @trigger.dev/sdk@3.0.0-beta.19
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.19
|
||||
|
||||
## 3.0.0-beta.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- @trigger.dev/sdk@3.0.0-beta.18
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.18
|
||||
|
||||
## 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.19",
|
||||
"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.19",
|
||||
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.19",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,33 @@
|
||||
# @trigger.dev/slack
|
||||
|
||||
## 3.0.0-beta.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e9a63a486]
|
||||
- @trigger.dev/sdk@3.0.0-beta.19
|
||||
|
||||
## 3.0.0-beta.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- @trigger.dev/sdk@3.0.0-beta.18
|
||||
|
||||
## 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.19",
|
||||
"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.19",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# @trigger.dev/stripe
|
||||
|
||||
## 3.0.0-beta.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e9a63a486]
|
||||
- @trigger.dev/sdk@3.0.0-beta.19
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.19
|
||||
|
||||
## 3.0.0-beta.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- @trigger.dev/sdk@3.0.0-beta.18
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.18
|
||||
|
||||
## 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.19",
|
||||
"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.19",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.19",
|
||||
"stripe": "^12.14.0",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# @trigger.dev/supabase
|
||||
|
||||
## 3.0.0-beta.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e9a63a486]
|
||||
- @trigger.dev/sdk@3.0.0-beta.19
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.19
|
||||
|
||||
## 3.0.0-beta.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- @trigger.dev/sdk@3.0.0-beta.18
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.18
|
||||
|
||||
## 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.19",
|
||||
"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.19",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.19",
|
||||
"supabase-management-js": "^1.0.0",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# @trigger.dev/typeform
|
||||
|
||||
## 3.0.0-beta.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e9a63a486]
|
||||
- @trigger.dev/sdk@3.0.0-beta.19
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.19
|
||||
|
||||
## 3.0.0-beta.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- @trigger.dev/sdk@3.0.0-beta.18
|
||||
- @trigger.dev/integration-kit@3.0.0-beta.18
|
||||
|
||||
## 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.19",
|
||||
"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.19",
|
||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.19",
|
||||
"@typeform/api-client": "^1.8.0",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
|
||||
+2
-1
@@ -68,7 +68,8 @@
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"@changesets/assemble-release-plan@5.2.4": "patches/@changesets__assemble-release-plan@5.2.4.patch",
|
||||
"tsup@8.0.1": "patches/tsup@8.0.1.patch"
|
||||
"tsup@8.0.1": "patches/tsup@8.0.1.patch",
|
||||
"engine.io-parser@5.2.2": "patches/engine.io-parser@5.2.2.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,33 @@
|
||||
# @trigger.dev/astro
|
||||
|
||||
## 3.0.0-beta.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e9a63a486]
|
||||
- @trigger.dev/sdk@3.0.0-beta.19
|
||||
|
||||
## 3.0.0-beta.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- @trigger.dev/sdk@3.0.0-beta.18
|
||||
|
||||
## 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.19",
|
||||
"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.19"
|
||||
},
|
||||
"devDependencies": {
|
||||
"astro": "^3.0.12",
|
||||
|
||||
@@ -1,5 +1,45 @@
|
||||
# trigger.dev
|
||||
|
||||
## 3.0.0-beta.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- e9a63a486: Lock SDK and CLI deps on exact core version
|
||||
- @trigger.dev/core@3.0.0-beta.19
|
||||
|
||||
## 3.0.0-beta.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b68012f81: Remove the env var check during deploy (too many false negatives)
|
||||
- b68012f81: Fixes an issue that was treating v2 trigger directories as v3
|
||||
- 74d1e61e4: Fix a bug where revoking the CLI token would prevent you from ever logging in again with the CLI.
|
||||
- 52b2a8289: Add git to prod worker image which fixes private package installs
|
||||
- b68012f81: Move to our global system from AsyncLocalStorage for the current task context storage
|
||||
- b68012f81: Extracting out all the non-SDK related features from the main @trigger.dev/core/v3 export
|
||||
- b68012f81: Add support for tasks located in subdirectories inside trigger dirs
|
||||
- Updated dependencies [b68012f81]
|
||||
- Updated dependencies [b68012f81]
|
||||
- @trigger.dev/core@3.0.0-beta.18
|
||||
|
||||
## 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.19",
|
||||
"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.19",
|
||||
"@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",
|
||||
|
||||
@@ -5,6 +5,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
busybox \
|
||||
ca-certificates \
|
||||
dumb-init \
|
||||
git \
|
||||
openssl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { flattenAttributes, recordSpanException } from "@trigger.dev/core/v3";
|
||||
import { flattenAttributes } from "@trigger.dev/core/v3";
|
||||
import { recordSpanException } from "@trigger.dev/core/v3/workers";
|
||||
import { Command } from "commander";
|
||||
import { z } from "zod";
|
||||
import { getTracer, provider } from "../telemetry/tracing";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
TaskMetadataFailedToParseData,
|
||||
detectDependencyVersion,
|
||||
flattenAttributes,
|
||||
recordSpanException,
|
||||
} from "@trigger.dev/core/v3";
|
||||
import { recordSpanException } from "@trigger.dev/core/v3/workers";
|
||||
import chalk from "chalk";
|
||||
import { Command, Option as CommandOption } from "commander";
|
||||
import { Metafile, build } from "esbuild";
|
||||
@@ -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,11 +57,12 @@ 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),
|
||||
skipDeploy: z.boolean().default(false),
|
||||
ignoreEnvVarCheck: z.boolean().default(false),
|
||||
env: z.enum(["prod", "staging"]),
|
||||
loadImage: z.boolean().default(false),
|
||||
buildPlatform: z.enum(["linux/amd64", "linux/arm64"]).default("linux/amd64"),
|
||||
@@ -72,6 +73,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,10 +91,7 @@ export function configureDeployCommand(program: Command) {
|
||||
"prod"
|
||||
)
|
||||
.option("--skip-typecheck", "Whether to skip the pre-build typecheck")
|
||||
.option(
|
||||
"--ignore-env-var-check",
|
||||
"Detected missing environment variables won't block deployment"
|
||||
)
|
||||
.option("--skip-update-check", "Skip checking for @trigger.dev package updates")
|
||||
.option("-c, --config <config file>", "The name of the config file, found at [path]")
|
||||
.option(
|
||||
"-p, --project-ref <project ref>",
|
||||
@@ -122,6 +122,12 @@ export function configureDeployCommand(program: Command) {
|
||||
"(Coming soon) Specify the tag to use when pushing the image to the registry"
|
||||
).hideHelp()
|
||||
)
|
||||
.addOption(
|
||||
new CommandOption(
|
||||
"--ignore-env-var-check",
|
||||
"(deprecated) Detected missing environment variables won't block deployment"
|
||||
).hideHelp()
|
||||
)
|
||||
.addOption(new CommandOption("-D, --skip-deploy", "Skip deploying the image").hideHelp())
|
||||
.addOption(
|
||||
new CommandOption("--load-image", "Load the built image into your local docker").hideHelp()
|
||||
@@ -140,6 +146,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 +171,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,
|
||||
@@ -224,16 +240,6 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
|
||||
|
||||
logger.debug("Compilation result", { compilation });
|
||||
|
||||
if (compilation.envVars.length > 0) {
|
||||
await checkEnvVars(
|
||||
compilation.envVars ?? [],
|
||||
resolvedConfig.config,
|
||||
options,
|
||||
environmentClient,
|
||||
authorization.dashboardUrl
|
||||
);
|
||||
}
|
||||
|
||||
// Step 2: Initialize a deployment on the server (response will have everything we need to build an image)
|
||||
const deploymentResponse = await environmentClient.initializeDeployment({
|
||||
contentHash: compilation.contentHash,
|
||||
@@ -306,25 +312,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 +363,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 +384,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 +397,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 +417,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 +454,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 +478,8 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
|
||||
logESMRequireError(parsedError, resolvedConfig);
|
||||
}
|
||||
|
||||
await preExitTasks();
|
||||
|
||||
throw new SkipLoggingError(
|
||||
`Deployment encountered an error: ${finishedDeployment.errorData.name}`
|
||||
);
|
||||
@@ -447,87 +488,187 @@ 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function checkEnvVars(
|
||||
envVars: string[],
|
||||
config: ResolvedConfig,
|
||||
options: DeployCommandOptions,
|
||||
environmentClient: CliApiClient,
|
||||
apiUrl: string
|
||||
) {
|
||||
return await tracer.startActiveSpan("detectEnvVars", async (span) => {
|
||||
try {
|
||||
span.setAttribute("envVars.check", envVars);
|
||||
function printErrors(errors?: string[]) {
|
||||
for (const error of errors ?? []) {
|
||||
log.error(`${chalkError("Error:")} ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
const environmentVariablesSpinner = spinner();
|
||||
function printWarnings(warnings?: string[]) {
|
||||
for (const warning of warnings ?? []) {
|
||||
log.warn(`${chalkWarning("Warning:")} ${warning}`);
|
||||
}
|
||||
}
|
||||
|
||||
environmentVariablesSpinner.start("Checking environment variables");
|
||||
|
||||
const environmentVariables = await environmentClient.getEnvironmentVariables(config.project);
|
||||
|
||||
if (!environmentVariables.success) {
|
||||
environmentVariablesSpinner.stop(`Failed to fetch environment variables, skipping check`);
|
||||
} else {
|
||||
// Check to see if all the environment variables in the compilation exist
|
||||
const missingEnvironmentVariables = envVars.filter(
|
||||
(envVar) => environmentVariables.data.variables[envVar] === undefined
|
||||
);
|
||||
|
||||
if (missingEnvironmentVariables.length > 0) {
|
||||
environmentVariablesSpinner.stop(
|
||||
`Found missing env vars in ${options.env}: ${arrayToSentence(
|
||||
missingEnvironmentVariables
|
||||
)}. ${
|
||||
options.ignoreEnvVarCheck
|
||||
? "Continuing deployment because of --ignore-env-var-check. "
|
||||
: "Aborting deployment. "
|
||||
}${chalk.bgBlueBright(
|
||||
terminalLink(
|
||||
"Manage env vars",
|
||||
`${apiUrl}/projects/v3/${config.project}/environment-variables`
|
||||
)
|
||||
)}`
|
||||
);
|
||||
|
||||
span.setAttributes({
|
||||
"envVars.missing": missingEnvironmentVariables,
|
||||
});
|
||||
|
||||
if (!options.ignoreEnvVarCheck) {
|
||||
throw new SkipLoggingError("Found missing environment variables");
|
||||
} else {
|
||||
span.end();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
environmentVariablesSpinner.stop(`Environment variable check passed`);
|
||||
}
|
||||
|
||||
span.end();
|
||||
} catch (e) {
|
||||
recordSpanException(span, e);
|
||||
|
||||
span.end();
|
||||
|
||||
throw e;
|
||||
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
|
||||
}
|
||||
|
||||
// Poll every 1 second for the deployment to finish
|
||||
@@ -944,7 +1085,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 +1280,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,
|
||||
@@ -1166,25 +1320,13 @@ async function compileProject(
|
||||
|
||||
const contentHash = contentHasher.digest("hex");
|
||||
|
||||
const workerSetupEnvVars = await findAllEnvironmentVariableReferencesInFile(workerSetupPath);
|
||||
|
||||
const workerFacadeEnvVars = findAllEnvironmentVariableReferences(workerContents);
|
||||
|
||||
const envVars = findAllEnvironmentVariableReferences(workerOutputFile.text);
|
||||
|
||||
// Remove workerFacadeEnvVars and workerSetupEnvVars from envVars
|
||||
const finalEnvVars = envVars.filter(
|
||||
(envVar) => !workerFacadeEnvVars.includes(envVar) && !workerSetupEnvVars.includes(envVar)
|
||||
);
|
||||
|
||||
span.setAttributes({
|
||||
contentHash: contentHash,
|
||||
envVars: finalEnvVars,
|
||||
});
|
||||
|
||||
span.end();
|
||||
|
||||
return { path: tempDir, contentHash, envVars: finalEnvVars };
|
||||
return { path: tempDir, contentHash };
|
||||
} catch (e) {
|
||||
recordSpanException(span, e);
|
||||
|
||||
@@ -1443,11 +1585,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 +1618,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);
|
||||
|
||||
@@ -1513,36 +1720,3 @@ async function ensureLoggedIntoDockerRegistry(
|
||||
|
||||
return tmpDir;
|
||||
}
|
||||
|
||||
async function findAllEnvironmentVariableReferencesInFile(filePath: string) {
|
||||
const fileContents = await readFile(filePath, "utf-8");
|
||||
|
||||
return findAllEnvironmentVariableReferences(fileContents);
|
||||
}
|
||||
|
||||
const IGNORED_ENV_VARS = ["NODE_ENV", "SHELL", "HOME", "PWD", "LOGNAME", "USER", "PATH", "DEBUG"];
|
||||
|
||||
function findAllEnvironmentVariableReferences(code: string): string[] {
|
||||
const regex = /\bprocess\.env\.([a-zA-Z_][a-zA-Z0-9_]*)\b/g;
|
||||
|
||||
const matches = code.matchAll(regex);
|
||||
|
||||
const matchesArray = Array.from(matches, (match) => match[1]).filter(Boolean) as string[];
|
||||
|
||||
const filteredMatches = matchesArray.filter((match) => !IGNORED_ENV_VARS.includes(match));
|
||||
|
||||
// Make sure and remove duplicates
|
||||
return Array.from(new Set(filteredMatches));
|
||||
}
|
||||
|
||||
function arrayToSentence(items: string[]): string {
|
||||
if (items.length === 1 && typeof items[0] === "string") {
|
||||
return items[0];
|
||||
}
|
||||
|
||||
if (items.length === 2) {
|
||||
return `${items[0]} and ${items[1]}`;
|
||||
}
|
||||
|
||||
return `${items.slice(0, -1).join(", ")}, and ${items[items.length - 1]}`;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,11 @@ import {
|
||||
CreateBackgroundWorkerRequestBody,
|
||||
ResolvedConfig,
|
||||
TaskResource,
|
||||
ZodMessageHandler,
|
||||
ZodMessageSender,
|
||||
clientWebsocketMessages,
|
||||
detectDependencyVersion,
|
||||
serverWebsocketMessages,
|
||||
} from "@trigger.dev/core/v3";
|
||||
import { ZodMessageHandler, ZodMessageSender } from "@trigger.dev/core/v3/zodMessageHandler";
|
||||
import { watch } from "chokidar";
|
||||
import { Command } from "commander";
|
||||
import { BuildContext, Metafile, context } from "esbuild";
|
||||
@@ -53,6 +52,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 +61,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 +79,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 +134,13 @@ async function startDev(
|
||||
}
|
||||
|
||||
await printStandloneInitialBanner(true);
|
||||
printDevBanner();
|
||||
|
||||
if (!options.skipUpdateCheck) {
|
||||
console.log(); // spacing
|
||||
await updateTriggerPackages(dir, { ...options }, true, true);
|
||||
}
|
||||
|
||||
printDevBanner(!options.skipUpdateCheck);
|
||||
|
||||
logger.debug("Starting dev session", { dir, options, authorization });
|
||||
|
||||
@@ -416,10 +424,6 @@ function useDev({
|
||||
|
||||
const metaOutputKey = join("out", `stdin.js`).replace(/\\/g, "/");
|
||||
|
||||
logger.debug("Metafile", {
|
||||
metafileOutputs: JSON.stringify(result.metafile?.outputs),
|
||||
});
|
||||
|
||||
const metaOutput = result.metafile!.outputs[metaOutputKey];
|
||||
|
||||
if (!metaOutput) {
|
||||
@@ -500,8 +504,15 @@ function useDev({
|
||||
|
||||
const taskResources: Array<TaskResource> = [];
|
||||
|
||||
if (!backgroundWorker.tasks) {
|
||||
throw new Error(`Background Worker started without tasks`);
|
||||
if (!backgroundWorker.tasks || backgroundWorker.tasks.length === 0) {
|
||||
logger.log(
|
||||
`${chalkError(
|
||||
"X Error:"
|
||||
)} Worker failed to build: no tasks found. Searched in ${config.triggerDirectories.join(
|
||||
", "
|
||||
)}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const task of backgroundWorker.tasks) {
|
||||
@@ -527,6 +538,10 @@ function useDev({
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Creating background worker with tasks", {
|
||||
tasks: taskResources,
|
||||
});
|
||||
|
||||
const backgroundWorkerBody: CreateBackgroundWorkerRequestBody = {
|
||||
localOnly: true,
|
||||
metadata: {
|
||||
@@ -629,7 +644,7 @@ function useDev({
|
||||
const throttledRebuild = pDebounce(runBuild, 250, { before: true });
|
||||
|
||||
const taskFileWatcher = watch(
|
||||
config.triggerDirectories.map((triggerDir) => `${triggerDir}/*.ts`),
|
||||
config.triggerDirectories.map((triggerDir) => `${triggerDir}/**/*.ts`),
|
||||
{
|
||||
ignoreInitial: true,
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { intro, isCancel, log, outro, select, text } from "@clack/prompts";
|
||||
import { context, trace } from "@opentelemetry/api";
|
||||
import {
|
||||
GetProjectResponseBody,
|
||||
flattenAttributes,
|
||||
recordSpanException,
|
||||
} from "@trigger.dev/core/v3";
|
||||
import { GetProjectResponseBody, flattenAttributes } from "@trigger.dev/core/v3";
|
||||
import { recordSpanException } from "@trigger.dev/core/v3/workers";
|
||||
import chalk from "chalk";
|
||||
import { Command } from "commander";
|
||||
import { execa } from "execa";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { intro, log, outro, select } from "@clack/prompts";
|
||||
import { recordSpanException } from "@trigger.dev/core/v3";
|
||||
import { recordSpanException } from "@trigger.dev/core/v3/workers";
|
||||
import { Command } from "commander";
|
||||
import open from "open";
|
||||
import pRetry, { AbortError } from "p-retry";
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
tracer,
|
||||
wrapCommandAction,
|
||||
} from "../cli/common.js";
|
||||
import { chalkLink } from "../utilities/cliOutput.js";
|
||||
import { chalkLink, prettyError } from "../utilities/cliOutput.js";
|
||||
import { readAuthConfigProfile, writeAuthConfigProfile } from "../utilities/configFiles.js";
|
||||
import { getVersion } from "../utilities/getVersion.js";
|
||||
import { printInitialBanner } from "../utilities/initialBanner.js";
|
||||
@@ -109,11 +109,23 @@ export async function login(options?: LoginOptions): Promise<LoginResult> {
|
||||
skipTelemetry: !span.isRecording(),
|
||||
logLevel: logger.loggerLevel,
|
||||
},
|
||||
opts.embedded
|
||||
true
|
||||
);
|
||||
|
||||
if (!whoAmIResult.success) {
|
||||
throw new Error(whoAmIResult.error);
|
||||
prettyError("Unable to validate existing personal access token", whoAmIResult.error);
|
||||
|
||||
if (!opts.embedded) {
|
||||
outro(
|
||||
`Login failed using stored token. To fix, first logout using \`trigger.dev logout${
|
||||
options?.profile ? ` --profile ${options.profile}` : ""
|
||||
}\` and then try again.`
|
||||
);
|
||||
|
||||
throw new SkipLoggingError(whoAmIResult.error);
|
||||
} else {
|
||||
throw new Error(whoAmIResult.error);
|
||||
}
|
||||
} else {
|
||||
if (!opts.embedded) {
|
||||
const continueOption = await select({
|
||||
|
||||
@@ -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 pin the CLI version in this CI step. To do that, just replace \`trigger.dev@beta\` with \`trigger.dev@<FULL_VERSION>\`, for example: \`npx trigger.dev@3.0.0-beta.17 deploy\`
|
||||
|
||||
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 };
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { intro, note } from "@clack/prompts";
|
||||
import { intro, note, outro } from "@clack/prompts";
|
||||
import { chalkLink } from "../utilities/cliOutput.js";
|
||||
import { logger } from "../utilities/logger.js";
|
||||
import { isLoggedIn } from "../utilities/session.js";
|
||||
@@ -66,11 +66,20 @@ export async function whoAmI(
|
||||
if (authentication.error === "fetch failed") {
|
||||
loadingSpinner.stop("Fetch failed. Platform down?");
|
||||
} else {
|
||||
loadingSpinner.stop(
|
||||
`You must login first. Use \`trigger.dev login --profile ${
|
||||
options?.profile ?? "default"
|
||||
}\` to login.`
|
||||
);
|
||||
if (embedded) {
|
||||
loadingSpinner.stop(
|
||||
`Failed to check account details. You may want to run \`trigger.dev logout --profile ${
|
||||
options?.profile ?? "default"
|
||||
}\` and try again.`
|
||||
);
|
||||
} else {
|
||||
loadingSpinner.stop(
|
||||
`You must login first. Use \`trigger.dev login --profile ${
|
||||
options?.profile ?? "default"
|
||||
}\` to login.`
|
||||
);
|
||||
outro("Whoami failed");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -5,6 +5,10 @@ import { NodeTracerProvider, SimpleSpanProcessor } from "@opentelemetry/sdk-trac
|
||||
import { FetchInstrumentation } from "@opentelemetry/instrumentation-fetch";
|
||||
import { DiagConsoleLogger, DiagLogLevel, diag, trace } from "@opentelemetry/api";
|
||||
import * as packageJson from "../../package.json";
|
||||
import {
|
||||
SEMRESATTRS_SERVICE_NAME,
|
||||
SEMRESATTRS_SERVICE_VERSION,
|
||||
} from "@opentelemetry/semantic-conventions";
|
||||
|
||||
function initializeTracing(): NodeTracerProvider | undefined {
|
||||
if (process.argv.includes("--skip-telemetry") || process.env.TRIGGER_DEV_SKIP_TELEMETRY) {
|
||||
@@ -19,7 +23,8 @@ function initializeTracing(): NodeTracerProvider | undefined {
|
||||
detectors: [processDetectorSync],
|
||||
}).merge(
|
||||
new Resource({
|
||||
service: "trigger.dev cli v3",
|
||||
[SEMRESATTRS_SERVICE_NAME]: "trigger.dev cli v3",
|
||||
[SEMRESATTRS_SERVICE_VERSION]: packageJson.version,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -38,9 +43,9 @@ function initializeTracing(): NodeTracerProvider | undefined {
|
||||
|
||||
const spanExporter = new OTLPTraceExporter({
|
||||
url: "https://otel.baselime.io/v1",
|
||||
timeoutMillis: 500,
|
||||
timeoutMillis: 5000,
|
||||
headers: {
|
||||
"x-api-key": "e9f963244f8b092850d42e34a5339b2d5e68070b".split("").reverse().join(""), // this is a joke
|
||||
"x-api-key": "b6e0fbbaf8dc2524773d2152ae2e9eb5c7fbaa52",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -59,5 +64,5 @@ function initializeTracing(): NodeTracerProvider | undefined {
|
||||
export const provider = initializeTracing();
|
||||
|
||||
export function getTracer() {
|
||||
return trace.getTracer("trigger.dev cli", packageJson.version);
|
||||
return trace.getTracer("trigger.dev cli v3", packageJson.version);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export function assertExhaustive(x: never): never {
|
||||
throw new Error("Unexpected object: " + x);
|
||||
}
|
||||
@@ -11,7 +11,7 @@ export function bundleTriggerDevCore(buildIdentifier: string, tsconfigPath?: str
|
||||
name: "trigger-bundle-core",
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /.*/ }, (args) => {
|
||||
if (args.path !== "@trigger.dev/core/v3") {
|
||||
if (!args.path.startsWith("@trigger.dev/core/v3")) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -22,18 +22,15 @@ export function bundleTriggerDevCore(buildIdentifier: string, tsconfigPath?: str
|
||||
triggerSdkPath,
|
||||
});
|
||||
|
||||
const resolvedPath = require.resolve("@trigger.dev/core/v3", {
|
||||
const resolvedPath = require.resolve(args.path, {
|
||||
paths: [triggerSdkPath],
|
||||
});
|
||||
|
||||
logger.debug(
|
||||
`[${buildIdentifier}][trigger-bundle-core] Externalizing @trigger.dev/core/v3`,
|
||||
{
|
||||
...args,
|
||||
triggerSdkPath,
|
||||
resolvedPath,
|
||||
}
|
||||
);
|
||||
logger.debug(`[${buildIdentifier}][trigger-bundle-core] Externalizing ${args.path}`, {
|
||||
...args,
|
||||
triggerSdkPath,
|
||||
resolvedPath,
|
||||
});
|
||||
|
||||
return {
|
||||
path: resolvedPath,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { log } from "@clack/prompts";
|
||||
import chalk from "chalk";
|
||||
|
||||
export const green = "#4FFF54";
|
||||
@@ -63,3 +64,49 @@ export function prettyPrintDate(date: Date = new Date()) {
|
||||
|
||||
return formattedDate;
|
||||
}
|
||||
|
||||
export function prettyError(header: string, body?: string, footer?: string) {
|
||||
const prefix = "Error: ";
|
||||
const indent = Array(prefix.length).fill(" ").join("");
|
||||
const spacing = "\n\n";
|
||||
|
||||
const prettyPrefix = chalkError(prefix);
|
||||
|
||||
const withIndents = (text?: string) =>
|
||||
text
|
||||
?.split("\n")
|
||||
.map((line) => `${indent}${line}`)
|
||||
.join("\n");
|
||||
|
||||
const prettyBody = withIndents(body);
|
||||
const prettyFooter = withIndents(footer);
|
||||
|
||||
log.error(
|
||||
`${prettyPrefix}${header}${prettyBody ? `${spacing}${prettyBody}` : ""}${
|
||||
prettyFooter ? `${spacing}${prettyFooter}` : ""
|
||||
}`
|
||||
);
|
||||
}
|
||||
|
||||
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}` : ""
|
||||
}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -99,6 +99,18 @@ async function getConfigPath(dir: string, fileName?: string): Promise<string | u
|
||||
return await findUp(fileName ? [fileName] : CONFIG_FILES, { cwd: dir });
|
||||
}
|
||||
|
||||
async function findFilePath(dir: string, fileName: string): Promise<string | undefined> {
|
||||
const result = await findUp([fileName], { cwd: dir });
|
||||
|
||||
logger.debug("Searched for the file", {
|
||||
dir,
|
||||
fileName,
|
||||
result,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export type ReadConfigOptions = {
|
||||
projectRef?: string;
|
||||
configFile?: string;
|
||||
@@ -187,6 +199,8 @@ export async function resolveConfig(path: string, config: Config): Promise<Resol
|
||||
|
||||
config.triggerDirectories = resolveTriggerDirectories(config.triggerDirectories);
|
||||
|
||||
logger.debug("Resolved trigger directories", { triggerDirectories: config.triggerDirectories });
|
||||
|
||||
if (!config.triggerUrl) {
|
||||
config.triggerUrl = CLOUD_API_URL;
|
||||
}
|
||||
@@ -196,7 +210,7 @@ export async function resolveConfig(path: string, config: Config): Promise<Resol
|
||||
}
|
||||
|
||||
if (!config.tsconfigPath) {
|
||||
config.tsconfigPath = await getConfigPath(path, "tsconfig.json");
|
||||
config.tsconfigPath = await findFilePath(path, "tsconfig.json");
|
||||
}
|
||||
|
||||
return config as ResolvedConfig;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user