Skip to content

Commit 9cd4618

Browse files
authored
Merge branch 'main' into fix/trigger-writer-db-error-leak
2 parents 57604d8 + df964ea commit 9cd4618

52 files changed

Lines changed: 1659 additions & 129 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/cli-init-ai-tooling.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"trigger.dev": patch
3+
---
4+
5+
`trigger init` now sets up your AI coding assistant as part of project setup: pick the MCP server, the agent skills, or both, then scaffold with the CLI or hand off to your assistant. Adds a new `getting-started` agent skill that teaches assistants how to bootstrap Trigger.dev (install the SDK, write `trigger.config.ts`, create a first task, run `trigger dev`), so the AI-driven setup path works end to end. It ships in the CLI alongside the existing skills, version-matched to your SDK.

.changeset/duplicate-task-ids.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@trigger.dev/core": patch
3+
"trigger.dev": patch
4+
---
5+
6+
`dev` and `deploy` now fail with a clear error when two tasks are defined with the same id, including across different task types (e.g. a scheduled task and a regular task sharing an id). Previously the second definition silently overwrote the first, so one of the tasks would vanish with no warning. Task ids are detected as duplicates during indexing (naming each offending id and the files it was found in), and the same rule is enforced server-side when the background worker is registered.

.github/workflows/publish-webapp.yml

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,24 @@ on:
1414
type: string
1515
required: false
1616
default: ""
17+
image_registry:
18+
description: The registry namespace to publish under (e.g. ghcr.io/<owner>)
19+
type: string
20+
required: false
21+
default: ""
1722
outputs:
1823
version:
1924
description: The published image tag
2025
value: ${{ jobs.publish.outputs.version }}
2126
short_sha:
2227
description: Short commit SHA of the published build
2328
value: ${{ jobs.publish.outputs.short_sha }}
29+
image_repo:
30+
description: The image repository the build was published to (without tag)
31+
value: ${{ jobs.publish.outputs.image_repo }}
32+
digest:
33+
description: Multi-arch index digest (sha256:...) of the published image
34+
value: ${{ jobs.publish.outputs.digest }}
2435
secrets:
2536
SENTRY_AUTH_TOKEN:
2637
required: false
@@ -33,6 +44,8 @@ jobs:
3344
outputs:
3445
version: ${{ steps.get_tag.outputs.tag }}
3546
short_sha: ${{ steps.get_commit.outputs.sha_short }}
47+
image_repo: ${{ steps.set_tags.outputs.image_repo }}
48+
digest: ${{ steps.build_push.outputs.digest }}
3649
steps:
3750
- name: 🏭 Setup Depot CLI
3851
uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1.7.1
@@ -57,17 +70,22 @@ jobs:
5770
- name: 📛 Set the tags
5871
id: set_tags
5972
run: |
60-
ref_without_tag=ghcr.io/triggerdotdev/trigger.dev
61-
image_tags=$ref_without_tag:${STEPS_GET_TAG_OUTPUTS_TAG}
73+
# The registry namespace is resolved by the caller (defaulting to
74+
# ghcr.io/<owner>, overridable via the IMAGE_REGISTRY repository
75+
# variable); the webapp image lives at <registry>/<repo-name>. A fork
76+
# therefore publishes to its own package automatically.
77+
image_tags=$REF_WITHOUT_TAG:${STEPS_GET_TAG_OUTPUTS_TAG}
6278
6379
# when pushing the mutable main tag, also push an immutable-by-convention
6480
# full-commit-sha tag so a commit can be resolved to a specific digest
6581
if [[ "${STEPS_GET_TAG_OUTPUTS_TAG}" == "main" ]]; then
66-
image_tags=$image_tags,$ref_without_tag:${GITHUB_SHA}
82+
image_tags=$image_tags,$REF_WITHOUT_TAG:${GITHUB_SHA}
6783
fi
6884
6985
echo "image_tags=${image_tags}" >> "$GITHUB_OUTPUT"
86+
echo "image_repo=${REF_WITHOUT_TAG}" >> "$GITHUB_OUTPUT"
7087
env:
88+
REF_WITHOUT_TAG: ${{ format('{0}/{1}', inputs.image_registry || vars.IMAGE_REGISTRY || format('ghcr.io/{0}', github.repository_owner), github.event.repository.name) }}
7189
STEPS_GET_TAG_OUTPUTS_TAG: ${{ steps.get_tag.outputs.tag }}
7290
STEPS_GET_TAG_OUTPUTS_IS_SEMVER: ${{ steps.get_tag.outputs.is_semver }}
7391

@@ -122,6 +140,6 @@ jobs:
122140
continue-on-error: true
123141
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
124142
with:
125-
subject-name: ghcr.io/triggerdotdev/trigger.dev
143+
subject-name: ${{ steps.set_tags.outputs.image_repo }}
126144
subject-digest: ${{ steps.build_push.outputs.digest }}
127145
push-to-registry: true

.github/workflows/publish-worker-v4.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ on:
88
type: string
99
required: false
1010
default: ""
11+
image_registry:
12+
description: The registry namespace to publish under (e.g. ghcr.io/<owner>)
13+
type: string
14+
required: false
15+
default: ""
1116
push:
1217
tags:
1318
- "re2-test-*"
@@ -65,11 +70,15 @@ jobs:
6570
- name: 📛 Set tags to push
6671
id: set_tags
6772
run: |
68-
ref_without_tag=ghcr.io/triggerdotdev/${STEPS_GET_REPOSITORY_OUTPUTS_REPO}
73+
# Resolved by the caller when invoked from publish.yml; falls back to the
74+
# IMAGE_REGISTRY repository variable (or ghcr.io/<owner>) for the direct
75+
# push triggers above, so a fork publishes to its own namespace.
76+
ref_without_tag=${IMAGE_REGISTRY}/${STEPS_GET_REPOSITORY_OUTPUTS_REPO}
6977
image_tags=$ref_without_tag:${STEPS_GET_TAG_OUTPUTS_TAG}
7078
7179
echo "image_tags=${image_tags}" >> "$GITHUB_OUTPUT"
7280
env:
81+
IMAGE_REGISTRY: ${{ inputs.image_registry || vars.IMAGE_REGISTRY || format('ghcr.io/{0}', github.repository_owner) }}
7382
STEPS_GET_REPOSITORY_OUTPUTS_REPO: ${{ steps.get_repository.outputs.repo }}
7483
STEPS_GET_TAG_OUTPUTS_TAG: ${{ steps.get_tag.outputs.tag }}
7584
STEPS_GET_TAG_OUTPUTS_IS_SEMVER: ${{ steps.get_tag.outputs.is_semver }}

.github/workflows/publish-worker.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ on:
88
type: string
99
required: false
1010
default: ""
11+
image_registry:
12+
description: The registry namespace to publish under (e.g. ghcr.io/<owner>)
13+
type: string
14+
required: false
15+
default: ""
1116
secrets:
1217
DOCKERHUB_USERNAME:
1318
required: false
@@ -83,7 +88,10 @@ jobs:
8388
docker tag infra_image "$REGISTRY/$REPOSITORY:$IMAGE_TAG"
8489
docker push "$REGISTRY/$REPOSITORY:$IMAGE_TAG"
8590
env:
86-
REGISTRY: ghcr.io/triggerdotdev
91+
# Resolved by the caller when invoked from publish.yml; falls back to the
92+
# IMAGE_REGISTRY repository variable (or ghcr.io/<owner>) for the direct
93+
# push triggers above, so a fork publishes to its own namespace.
94+
REGISTRY: ${{ inputs.image_registry || vars.IMAGE_REGISTRY || format('ghcr.io/{0}', github.repository_owner) }}
8795
REPOSITORY: ${{ steps.get_repository.outputs.repo }}
8896
IMAGE_TAG: ${{ steps.get_tag.outputs.tag }}
8997

.github/workflows/publish.yml

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ on:
1515
required: false
1616
SENTRY_AUTH_TOKEN:
1717
required: false
18+
CROSS_REPO_PAT:
19+
required: false
1820
push:
1921
branches:
2022
- main
@@ -74,6 +76,9 @@ jobs:
7476
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
7577
with:
7678
image_tag: ${{ inputs.image_tag }}
79+
# Target registry namespace. Defaults to ghcr.io/<owner> so a fork publishes
80+
# to its own namespace; set the IMAGE_REGISTRY repository variable to override.
81+
image_registry: ${{ vars.IMAGE_REGISTRY || format('ghcr.io/{0}', github.repository_owner) }}
7782

7883
publish-worker:
7984
needs: [typecheck]
@@ -86,6 +91,7 @@ jobs:
8691
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
8792
with:
8893
image_tag: ${{ inputs.image_tag }}
94+
image_registry: ${{ vars.IMAGE_REGISTRY || format('ghcr.io/{0}', github.repository_owner) }}
8995

9096
publish-worker-v4:
9197
needs: [typecheck]
@@ -96,6 +102,7 @@ jobs:
96102
uses: ./.github/workflows/publish-worker-v4.yml
97103
with:
98104
image_tag: ${{ inputs.image_tag }}
105+
image_registry: ${{ vars.IMAGE_REGISTRY || format('ghcr.io/{0}', github.repository_owner) }}
99106

100107
# OS-level CVE scan of the image just published above. Report-only (writes to
101108
# the run summary); runs alongside the worker publishes and never blocks them.
@@ -106,4 +113,46 @@ jobs:
106113
packages: read # pull the just-published image from GHCR
107114
uses: ./.github/workflows/trivy-image-webapp.yml
108115
with:
109-
image-ref: ghcr.io/triggerdotdev/trigger.dev:${{ needs.publish-webapp.outputs.version }}
116+
image-ref: ${{ needs.publish-webapp.outputs.image_repo }}:${{ needs.publish-webapp.outputs.version }}
117+
118+
# Announce the freshly published mutable `main` webapp image to subscriber
119+
# repos in the org via repository_dispatch, handing them a digest-pinned ref to
120+
# build or deploy from. Fires only for the `main` tag — never semver releases or
121+
# other tag builds — and only from the canonical repo (forks have no PAT).
122+
dispatch-main-image:
123+
name: 📣 Dispatch main image
124+
needs: [publish-webapp]
125+
if: github.repository == 'triggerdotdev/trigger.dev' && needs.publish-webapp.outputs.version == 'main'
126+
runs-on: ubuntu-latest
127+
permissions: {}
128+
steps:
129+
- name: Build dispatch payload
130+
id: payload
131+
env:
132+
IMAGE_REPO: ${{ needs.publish-webapp.outputs.image_repo }}
133+
DIGEST: ${{ needs.publish-webapp.outputs.digest }}
134+
COMMIT: ${{ github.sha }}
135+
run: |
136+
set -euo pipefail
137+
# Pin to the exact multi-arch index just pushed so subscribers resolve a
138+
# single immutable artifact rather than chasing the moving `main` tag.
139+
if [[ -z "${DIGEST}" ]]; then
140+
echo "::error::publish-webapp produced no image digest; refusing to dispatch"
141+
exit 1
142+
fi
143+
image="${IMAGE_REPO}@${DIGEST}"
144+
# jq --arg JSON-escapes every value, so the ref/commit can't break out of
145+
# or inject into the client payload.
146+
payload=$(jq -nc \
147+
--arg img "$image" \
148+
--arg c "$COMMIT" \
149+
'{image: $img, commit: $c}')
150+
echo "client_payload=$payload" >> "$GITHUB_OUTPUT"
151+
152+
- name: Send repository_dispatch
153+
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
154+
with:
155+
token: ${{ secrets.CROSS_REPO_PAT }}
156+
repository: triggerdotdev/cloud
157+
event-type: main-image-published
158+
client-payload: ${{ steps.payload.outputs.client_payload }}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: improvement
4+
---
5+
6+
Add bounded `enrolled` and `org` labels to the `mollifier.decisions` metric so per-enrolled-org pass-through vs mollify is visible (the `org` label is attached only for the enrolled cohort to keep cardinality bounded).
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
area: webapp
3+
type: feature
4+
---
5+
6+
Add `REQUIRE_PLUGINS=1` env var. When set, the RBAC plugin loader throws instead of silently falling back to the default implementation if the plugin module fails to load (missing, broken transitive dep, etc.). The webapp's `/healthcheck` route now resolves the lazy plugin controller so the throw surfaces during readiness probes — a deploy where the plugin didn't load fails the probe and is rolled back.
7+
8+
Self-hosters leave `REQUIRE_PLUGINS` unset and continue to use the fallback when no plugin is installed.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
area: webapp
3+
type: fix
4+
---
5+
6+
Fix an off-by-one in `ClickHouseRunsRepository.listRunIds` backward pagination.
7+
When paging backward with more rows before the page (`hasMore`), the displayed
8+
page was sliced as `rows.slice(1, size + 1)`, which dropped the row closest to
9+
the cursor and kept the extra "has-more" sentinel — returning a page that
10+
straddled two logical pages (one row from the correct previous page plus one
11+
from the page before it). The result set is always the first `page.size` rows
12+
(the sentinel is the trailing element in both directions), so the slice is now
13+
`rows.slice(0, size)` for forward and backward alike. Forward pagination and the
14+
cursor values were already correct and are unchanged.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: fix
4+
---
5+
6+
Scheduled runs now show under their correct region in the dashboard, run details, and the API, and match region filters, instead of appearing under a separate region.

0 commit comments

Comments
 (0)