chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [triggerdotdev]
|
||||
@@ -0,0 +1,38 @@
|
||||
name: 🐞 Bug Report
|
||||
description: Create a bug report to help us improve
|
||||
title: "bug: "
|
||||
labels: ["🐞 unconfirmed bug"]
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Provide environment information
|
||||
description: |
|
||||
Run this command in your project root and paste the results:
|
||||
```bash
|
||||
npx envinfo --system --binaries
|
||||
```
|
||||
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear and concise description of the bug, as well as what you expected to happen when encountering it.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Reproduction repo
|
||||
description: If applicable, please provide a link to a reproduction repo or a Stackblitz / CodeSandbox project. Your issue may be closed if this is not provided and we are unable to reproduce the issue. If your bug is a docs issue, link the appropriate page.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: To reproduce
|
||||
description: Describe how to reproduce your bug. Steps, code snippets, reproduction repos etc.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: Add any other information related to the bug here, screenshots if applicable.
|
||||
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Ask a Question
|
||||
url: https://trigger.dev/discord
|
||||
about: Ask questions and discuss with other community members
|
||||
@@ -0,0 +1,27 @@
|
||||
name: Feature Request
|
||||
description: Suggest an idea for this project
|
||||
title: "feat: "
|
||||
labels: ["🌟 enhancement"]
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Is your feature request related to a problem? Please describe.
|
||||
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the solution you'd like to see
|
||||
description: A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe alternate solutions
|
||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: Add any other information related to the feature here. If your feature request is related to any issues or discussions, link them here.
|
||||
@@ -0,0 +1,21 @@
|
||||
name: OpenTelemetry Auto-Instrumentation Request
|
||||
description: Suggest an SDK that you'd like to be auto-instrumented in the Run log view
|
||||
title: "auto-instrumentation: "
|
||||
labels: ["🌟 enhancement"]
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What API or SDK would you to have automatic spans for?
|
||||
description: A clear description of which API or SDK you'd like, and links to it.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Is there an existing OpenTelemetry auto-instrumentation package?
|
||||
description: You can search for existing ones – https://opentelemetry.io/ecosystem/registry/?component=instrumentation&language=js
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: Add any other information related to the feature here. If your feature request is related to any issues or discussions, link them here.
|
||||
@@ -0,0 +1,28 @@
|
||||
name: Vouch Request
|
||||
description: Request to be vouched as a contributor
|
||||
labels: ["vouch-request"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Vouch Request
|
||||
|
||||
We use [vouch](https://github.com/mitchellh/vouch) to manage contributor trust. PRs from unvouched users are automatically closed.
|
||||
|
||||
To get vouched, fill out this form. A maintainer will review your request and vouch for you by commenting on this issue.
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Why do you want to contribute?
|
||||
description: Tell us a bit about yourself and what you'd like to work on.
|
||||
placeholder: "I'd like to fix a bug I found in..."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: prior-work
|
||||
attributes:
|
||||
label: Prior contributions or relevant experience
|
||||
description: Links to previous open source work, relevant projects, or anything that helps us understand your background.
|
||||
placeholder: "https://github.com/..."
|
||||
validations:
|
||||
required: false
|
||||
@@ -0,0 +1,27 @@
|
||||
# Vouched contributors for Trigger.dev
|
||||
# See: https://github.com/mitchellh/vouch
|
||||
#
|
||||
# Org members
|
||||
0ski
|
||||
D-K-P
|
||||
ericallam
|
||||
matt-aitken
|
||||
mpcgrid
|
||||
myftija
|
||||
nicktrn
|
||||
samejr
|
||||
isshaddad
|
||||
# Bots
|
||||
devin-ai-integration[bot]
|
||||
dependabot[bot]
|
||||
# Outside contributors
|
||||
gautamsi
|
||||
capaj
|
||||
chengzp
|
||||
bharathkumar39293
|
||||
bhekanik
|
||||
jrossi
|
||||
ThullyoCunha
|
||||
ConProgramming
|
||||
saasjesus
|
||||
brentshulman-silkline
|
||||
@@ -0,0 +1,5 @@
|
||||
self-hosted-runner:
|
||||
labels:
|
||||
- warp-ubuntu-*
|
||||
- warp-macos-*
|
||||
- warp-windows-*
|
||||
@@ -0,0 +1,91 @@
|
||||
name: "#️⃣ Get image tag (action)"
|
||||
|
||||
description: This action gets the image tag from the commit ref or input (if provided)
|
||||
|
||||
outputs:
|
||||
tag:
|
||||
description: The image tag
|
||||
value: ${{ steps.get_tag.outputs.tag }}
|
||||
is_semver:
|
||||
description: Whether the tag is a semantic version
|
||||
value: ${{ steps.check_semver.outputs.is_semver }}
|
||||
|
||||
inputs:
|
||||
tag:
|
||||
description: The image tag. If this is set it will return the tag as is.
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: "#️⃣ Get image tag (step)"
|
||||
id: get_tag
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ -n "${INPUTS_TAG}" ]]; then
|
||||
tag="${INPUTS_TAG}"
|
||||
elif [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
|
||||
if [[ "${GITHUB_REF_NAME}" == infra-*-* ]]; then
|
||||
env=$(echo ${GITHUB_REF_NAME} | cut -d- -f2)
|
||||
sha=$(echo "${GITHUB_SHA}" | head -c7)
|
||||
ts=$(date +%s)
|
||||
tag=${env}-${sha}-${ts}
|
||||
elif [[ "${GITHUB_REF_NAME}" == re2-*-* ]]; then
|
||||
env=$(echo ${GITHUB_REF_NAME} | cut -d- -f2)
|
||||
sha=$(echo "${GITHUB_SHA}" | head -c7)
|
||||
ts=$(date +%s)
|
||||
tag=${env}-${sha}-${ts}
|
||||
elif [[ "${GITHUB_REF_NAME}" == v.docker.* ]]; then
|
||||
version="${GITHUB_REF_NAME#v.docker.}"
|
||||
tag="v${version}"
|
||||
elif [[ "${GITHUB_REF_NAME}" == build-* ]]; then
|
||||
tag="${GITHUB_REF_NAME#build-}"
|
||||
else
|
||||
echo "Invalid git tag: ${GITHUB_REF_NAME}"
|
||||
exit 1
|
||||
fi
|
||||
elif [[ "${GITHUB_REF_NAME}" == "main" ]]; then
|
||||
tag="main"
|
||||
else
|
||||
echo "Invalid git ref: ${GITHUB_REF}"
|
||||
exit 1
|
||||
fi
|
||||
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
INPUTS_TAG: ${{ inputs.tag }}
|
||||
|
||||
- name: 🔍 Check for validity
|
||||
id: check_validity
|
||||
shell: bash
|
||||
env:
|
||||
tag: ${{ steps.get_tag.outputs.tag }}
|
||||
run: |
|
||||
if [[ "${tag}" =~ ^[a-z0-9]+([._-][a-z0-9]+)*$ ]]; then
|
||||
echo "Tag is valid: ${tag}"
|
||||
else
|
||||
echo "Tag is not valid: ${tag}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: 🆚 Check for semver
|
||||
id: check_semver
|
||||
shell: bash
|
||||
env:
|
||||
tag: ${{ steps.get_tag.outputs.tag }}
|
||||
# Will match most semver formats except build metadata, i.e. v1.2.3+build.1
|
||||
# Valid matches:
|
||||
# v1.2.3
|
||||
# v1.2.3-alpha
|
||||
# v1.2.3-alpha.1
|
||||
# v1.2.3-rc.1
|
||||
# v1.2.3-beta-1
|
||||
run: |
|
||||
if [[ "${tag}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then
|
||||
echo "Tag is a semantic version: ${tag}"
|
||||
is_semver=true
|
||||
else
|
||||
echo "Tag is not a semantic version: ${tag}"
|
||||
is_semver=false
|
||||
fi
|
||||
echo "is_semver=${is_semver}" >> "$GITHUB_OUTPUT"
|
||||
@@ -0,0 +1 @@
|
||||
This is the repo for Trigger.dev, a background jobs platform written in TypeScript. Our webapp at apps/webapp is a Remix 2.1 app that uses Node.js v20. Our SDK is an isomorphic TypeScript SDK at packages/trigger-sdk. Always prefer using isomorphic code like fetch, ReadableStream, etc. instead of Node.js specific code. Our tests are all vitest. We use prisma in internal-packages/database for our database interactions using PostgreSQL. For TypeScript, we usually use types over interfaces. We use zod a lot in packages/core and in the webapp. Avoid enums. Use strict mode. No default exports, use function declarations.
|
||||
@@ -0,0 +1,12 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
cooldown:
|
||||
default-days: 7
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
@@ -0,0 +1,12 @@
|
||||
"📌 area: cli":
|
||||
- any: ["cli/**/*"]
|
||||
|
||||
"📌 area: t3-app":
|
||||
- any: ["cli/template/**/*"]
|
||||
|
||||
"📚 documentation":
|
||||
- any: ["www/**/*"]
|
||||
- any: ["**/*.md"]
|
||||
|
||||
"📌 area: ci":
|
||||
- any: [".github/**/*"]
|
||||
@@ -0,0 +1,27 @@
|
||||
Closes #<issue>
|
||||
|
||||
## ✅ Checklist
|
||||
|
||||
- [ ] I have followed every step in the [contributing guide](https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md)
|
||||
- [ ] The PR title follows the convention.
|
||||
- [ ] I ran and tested the code works
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
_[Describe the steps you took to test this change]_
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
_[Short description of what has changed]_
|
||||
|
||||
---
|
||||
|
||||
## Screenshots
|
||||
|
||||
_[Screenshots]_
|
||||
|
||||
💯
|
||||
@@ -0,0 +1,70 @@
|
||||
# GitHub Action Tests
|
||||
|
||||
This directory contains necessary files to allow local testing of GitHub Actions workflows, composite actions, etc. You will need to install [act](https://github.com/nektos/act) to perform tests.
|
||||
|
||||
## Workflow tests
|
||||
|
||||
Trigger specific workflow files by specifying their full path:
|
||||
|
||||
```
|
||||
act -W .github/workflow/release.yml
|
||||
```
|
||||
|
||||
You will likely need to override any custom runners we use, e.g. buildjet. For example:
|
||||
|
||||
```
|
||||
override=catthehacker/ubuntu:act-latest
|
||||
|
||||
act -W .github/workflow/release.yml \
|
||||
-P buildjet-8vcpu-ubuntu-2204=$override
|
||||
|
||||
# override multiple images at the same time
|
||||
act -W .github/workflow/release.yml \
|
||||
-P buildjet-8vcpu-ubuntu-2204=$override \
|
||||
-P buildjet-16vcpu-ubuntu-2204=$override
|
||||
```
|
||||
|
||||
Trigger with specific event payloads to test pushing to branches or tags:
|
||||
|
||||
```
|
||||
override=catthehacker/ubuntu:act-latest
|
||||
|
||||
# simulate push to main
|
||||
act -W .github/workflow/publish.yml \
|
||||
-P buildjet-8vcpu-ubuntu-2204=$override \
|
||||
-P buildjet-16vcpu-ubuntu-2204=$override \
|
||||
-e .github/events/push-tag-main.json
|
||||
|
||||
# simulate a `build-` prefixed tag
|
||||
act -W .github/workflow/publish.yml \
|
||||
-P buildjet-8vcpu-ubuntu-2204=$override \
|
||||
-P buildjet-16vcpu-ubuntu-2204=$override \
|
||||
-e .github/events/push-tag-buld.json
|
||||
```
|
||||
|
||||
By default, `act` will send a push event. To trigger a different event:
|
||||
|
||||
```
|
||||
# basic syntax
|
||||
act <EVENT> ...
|
||||
|
||||
# simulate a pull request
|
||||
act pull_request
|
||||
|
||||
# only trigger a specific workflow
|
||||
act pull_request -W .github/workflow/pr_checks.yml
|
||||
```
|
||||
|
||||
## Composite action tests
|
||||
|
||||
The composite (custom) action tests can be run by triggering the `test-actions` workflow:
|
||||
|
||||
```
|
||||
act -W .github/test/test-actions.yml
|
||||
```
|
||||
|
||||
## Helpful flags
|
||||
|
||||
- `--pull=false` - perform fully offline tests if all images are already present
|
||||
- `-j <job_name>` - run the specified job only
|
||||
- `-l push` - list all workflows with push triggers
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ref": "refs/heads/main"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ref": "refs/tags/build-buildtag"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ref": "refs/tags/v.docker.nonsemver"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ref": "refs/tags/v.docker.1.2.3"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ref": "refs/tags/infra-prod-anything"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ref": "refs/tags/infra-test-anything"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ref": "refs/tags/1.2.3"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ref": "refs/tags/standard-tag"
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
name: Test Actions
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
get-image-tag-none:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log current ref
|
||||
run: |
|
||||
echo "ref: ${{ github.ref }}"
|
||||
echo "ref_type: ${{ github.ref_type }}"
|
||||
echo "ref_name: ${{ github.ref_name }}"
|
||||
|
||||
- name: Run without input tag
|
||||
id: get_tag
|
||||
# this step may fail depending on the current ref
|
||||
continue-on-error: true
|
||||
uses: ./.github/actions/get-image-tag
|
||||
|
||||
- name: Verify output
|
||||
run: |
|
||||
echo "${{ toJson(steps.get_tag) }}"
|
||||
|
||||
get-image-tag-null:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log current ref
|
||||
run: |
|
||||
echo "ref: ${{ github.ref }}"
|
||||
echo "ref_type: ${{ github.ref_type }}"
|
||||
echo "ref_name: ${{ github.ref_name }}"
|
||||
|
||||
- name: Run without input tag
|
||||
id: get_tag
|
||||
uses: ./.github/actions/get-image-tag
|
||||
# this step may fail depending on the current ref
|
||||
continue-on-error: true
|
||||
with:
|
||||
# this should behave exactly as when no tag is provided
|
||||
tag: null
|
||||
|
||||
- name: Verify output
|
||||
run: |
|
||||
echo "${{ toJson(steps.get_tag) }}"
|
||||
|
||||
get-image-tag-override:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run with tag override
|
||||
id: get_tag
|
||||
uses: ./.github/actions/get-image-tag
|
||||
with:
|
||||
tag: "abc-123"
|
||||
|
||||
- name: Verify output
|
||||
run: |
|
||||
echo "${{ toJson(steps.get_tag) }}"
|
||||
|
||||
get-image-tag-invalid-string:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run with invalid string
|
||||
id: get_tag
|
||||
uses: ./.github/actions/get-image-tag
|
||||
# this step is expected to fail
|
||||
continue-on-error: true
|
||||
with:
|
||||
# does not end with alphanumeric character
|
||||
tag: "abc-123-"
|
||||
|
||||
- name: Fail job if previous step did not fail
|
||||
if: steps.get_tag.outcome != 'failure'
|
||||
run: exit 1
|
||||
|
||||
- name: Verify output
|
||||
run: |
|
||||
echo "${{ toJson(steps.get_tag) }}"
|
||||
|
||||
get-image-tag-prerelease:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run with prerelease semver
|
||||
id: get_tag
|
||||
uses: ./.github/actions/get-image-tag
|
||||
with:
|
||||
tag: "v1.2.3-beta.4"
|
||||
|
||||
- name: Verify output
|
||||
run: |
|
||||
echo "${{ toJson(steps.get_tag) }}"
|
||||
|
||||
get-image-tag-semver:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run with basic semver
|
||||
id: get_tag
|
||||
uses: ./.github/actions/get-image-tag
|
||||
with:
|
||||
tag: "v1.2.3"
|
||||
|
||||
- name: Verify output
|
||||
run: |
|
||||
echo "${{ toJson(steps.get_tag) }}"
|
||||
|
||||
get-image-tag-invalid-semver:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run with invalid semver
|
||||
id: get_tag
|
||||
uses: ./.github/actions/get-image-tag
|
||||
# this step is expected to fail
|
||||
continue-on-error: true
|
||||
with:
|
||||
tag: "v1.2.3-"
|
||||
|
||||
- name: Fail job if previous step did not fail
|
||||
if: steps.get_tag.outcome != 'failure'
|
||||
run: exit 1
|
||||
|
||||
- name: Verify output
|
||||
run: |
|
||||
echo "${{ toJson(steps.get_tag) }}"
|
||||
@@ -0,0 +1,99 @@
|
||||
name: 🦋 Changesets PR
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "packages/**"
|
||||
- ".changeset/**"
|
||||
- ".server-changes/**"
|
||||
- "package.json"
|
||||
- "pnpm-lock.yaml"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
release-pr:
|
||||
name: Create Release PR
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
checks: write
|
||||
if: github.repository == 'triggerdotdev/trigger.dev'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 # zizmor: ignore[artipacked] changesets/action pushes the release branch; no artifact upload here so no leak path
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Create release PR
|
||||
id: changesets
|
||||
uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1.9.0
|
||||
with:
|
||||
version: pnpm run changeset:version
|
||||
commit: "chore: release"
|
||||
title: "chore: release"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Update PR title and enhance body
|
||||
if: steps.changesets.outputs.published != 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
PR_NUMBER=$(gh pr list --head changeset-release/main --json number --jq '.[0].number')
|
||||
if [ -n "$PR_NUMBER" ]; then
|
||||
git fetch origin changeset-release/main
|
||||
# we arbitrarily reference the version of the cli package here; it is the same for all package releases
|
||||
VERSION=$(git show origin/changeset-release/main:packages/cli-v3/package.json | jq -r '.version')
|
||||
gh pr edit "$PR_NUMBER" --title "chore: release v$VERSION"
|
||||
|
||||
# Enhance the PR body with a clean, deduplicated summary
|
||||
RAW_BODY=$(gh pr view "$PR_NUMBER" --json body --jq '.body')
|
||||
ENHANCED_BODY=$(CHANGESET_PR_BODY="$RAW_BODY" node scripts/enhance-release-pr.mjs "$VERSION")
|
||||
if [ -n "$ENHANCED_BODY" ]; then
|
||||
gh api repos/triggerdotdev/trigger.dev/pulls/"$PR_NUMBER" \
|
||||
-X PATCH \
|
||||
-f body="$ENHANCED_BODY"
|
||||
fi
|
||||
fi
|
||||
|
||||
# The changesets bot authors release PRs with GITHUB_TOKEN, which by GitHub
|
||||
# design cannot trigger downstream workflows. That leaves the required
|
||||
# "All PR Checks" status permanently Expected and the PR unmergeable.
|
||||
# The release PR only bumps package.json + lockfile + CHANGELOGs from
|
||||
# changesets already on main, so we self-report the required check as
|
||||
# success. If a human ever pushes to changeset-release/main, the real
|
||||
# pr_checks.yml fires and its result overwrites this one (last write wins
|
||||
# for the same context on the same SHA).
|
||||
- name: Self-report "All PR Checks" success on release PR
|
||||
if: steps.changesets.outputs.published != 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
PR_NUMBER=$(gh pr list --head changeset-release/main --json number --jq '.[0].number')
|
||||
if [ -z "$PR_NUMBER" ]; then exit 0; fi
|
||||
HEAD_SHA=$(gh pr view "$PR_NUMBER" --json headRefOid --jq '.headRefOid')
|
||||
gh api -X POST repos/${{ github.repository }}/check-runs \
|
||||
-f name="All PR Checks" \
|
||||
-f head_sha="$HEAD_SHA" \
|
||||
-f status=completed \
|
||||
-f conclusion=success \
|
||||
-f 'output[title]=Auto-pass for changeset release PR' \
|
||||
-f 'output[summary]=Required check auto-satisfied for changeset-release/main PRs. Full CI ran on the underlying commits before they landed on main.'
|
||||
@@ -0,0 +1,95 @@
|
||||
name: 🔎 REVIEW.md Drift Audit
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, ready_for_review, synchronize]
|
||||
paths-ignore:
|
||||
- "docs/**"
|
||||
- ".changeset/**"
|
||||
- ".server-changes/**"
|
||||
|
||||
concurrency:
|
||||
group: review-md-drift-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
audit:
|
||||
# Set the ENABLE_CLAUDE_CODE repository variable to 'false' to turn off Claude
|
||||
# jobs; leave it unset (the default) to keep them enabled.
|
||||
if: >-
|
||||
vars.ENABLE_CLAUDE_CODE != 'false' &&
|
||||
github.event.pull_request.draft == false &&
|
||||
github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Run Claude Code
|
||||
id: claude
|
||||
uses: anthropics/claude-code-action@428971d2ecd6e3a7cb0ee0da2a3a8b33fdb3678d # v1.0.157
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
use_sticky_comment: true
|
||||
allowed_bots: "devin-ai-integration[bot]"
|
||||
|
||||
claude_args: |
|
||||
--max-turns 30
|
||||
--allowedTools "Read,Glob,Grep,Bash(git diff:*)"
|
||||
|
||||
prompt: |
|
||||
You are auditing this PR for drift against `.claude/REVIEW.md`.
|
||||
|
||||
## Context
|
||||
|
||||
`.claude/REVIEW.md` is the repo's source of truth for what AI / agent code reviewers should treat as critical findings (rolling-deploy safety, hot-table indexes, recovery-path queries, testcontainers usage, Lua versioning, etc.). It is consumed by review agents to calibrate severity. If REVIEW.md goes stale, every future agent review degrades.
|
||||
|
||||
## Strategy — read this first
|
||||
|
||||
You have a hard turn budget. Spend it on signal, not coverage. The audit is allowed to miss things; it is NOT allowed to time out.
|
||||
|
||||
1. Read `.claude/REVIEW.md` once, in full.
|
||||
2. Run `git diff origin/main...HEAD --name-only` to get the list of changed files. Do NOT read the diff content yet.
|
||||
3. Scan the file-list for relevance to REVIEW.md scope. Relevance signals: changes to Prisma schema, Redis / queue / Lua code, hot tables, recovery / restart loops, new packages, deletions of paths REVIEW.md cites. Skim everything else.
|
||||
4. Open at most **5 files** total — only the ones most likely to surface a real signal. If nothing in the file-list looks relevant to any REVIEW.md rule, do NOT read any files; go straight to the verdict.
|
||||
5. Form a verdict and stop. Do not exhaust the turn budget exploring.
|
||||
|
||||
Large PRs (>50 files changed) are a strong signal to be MORE selective, not more thorough. Pick 3-5 files at most.
|
||||
|
||||
## What to look for
|
||||
|
||||
- **Stale references** — does any REVIEW.md rule cite a file, directory, function, table, Prisma model, or package name that has been removed or renamed in this PR (or is already gone from `main`)?
|
||||
- **Contradictions** — does code in this PR clearly violate a current REVIEW.md rule? (Don't re-review the PR. Only flag if REVIEW.md and the PR plainly disagree.)
|
||||
- **Missing rules** — does this PR introduce a new pattern future reviewers should know about? Examples: a new hot table, a new Lua-script versioning convention, a new safety wrapper, a new "must always check" invariant.
|
||||
- **Obsolete rules** — has the repo moved past a constraint REVIEW.md still asserts? (e.g. a deprecated path is gone, a pattern is now linted, V1 code is deleted.)
|
||||
|
||||
## Response format
|
||||
|
||||
If nothing needs changing:
|
||||
|
||||
✅ REVIEW.md looks current for this PR.
|
||||
|
||||
Otherwise:
|
||||
|
||||
📝 **REVIEW.md updates suggested:**
|
||||
|
||||
- **[stale]** `<rule excerpt>` — <what's stale and why>
|
||||
- **[contradiction]** `<rule excerpt>` — <what in this PR disagrees>
|
||||
- **[missing]** under `## <section>` — <one-sentence draft rule>
|
||||
- **[obsolete]** `<rule excerpt>` — <why this rule no longer applies>
|
||||
|
||||
## Rules
|
||||
|
||||
- Maximum 3 suggestions per audit. Pick the highest-signal ones.
|
||||
- Only flag things that would actually mislead a future reviewer. Style and wording do not count.
|
||||
- Do NOT review the PR itself. Do NOT propose rules outside REVIEW.md's existing sections.
|
||||
- Do NOT propose rules for one-off PR specifics that don't generalize to future PRs.
|
||||
- If REVIEW.md does not exist in the repo, respond with `(skip)` and stop.
|
||||
- When in doubt between "one more file read" and "finish now" — finish now.
|
||||
@@ -0,0 +1,83 @@
|
||||
name: 📝 Agent Instructions Audit
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, ready_for_review, synchronize]
|
||||
paths-ignore:
|
||||
- "docs/**"
|
||||
- ".changeset/**"
|
||||
- ".server-changes/**"
|
||||
- "**/*.md"
|
||||
|
||||
concurrency:
|
||||
group: agent-instructions-audit-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
audit:
|
||||
# Set the ENABLE_CLAUDE_CODE repository variable to 'false' to turn off Claude
|
||||
# jobs; leave it unset (the default) to keep them enabled.
|
||||
if: >-
|
||||
vars.ENABLE_CLAUDE_CODE != 'false' &&
|
||||
github.event.pull_request.draft == false &&
|
||||
github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Run Claude Code
|
||||
id: claude
|
||||
uses: anthropics/claude-code-action@428971d2ecd6e3a7cb0ee0da2a3a8b33fdb3678d # v1.0.157
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
use_sticky_comment: true
|
||||
allowed_bots: "devin-ai-integration[bot]"
|
||||
|
||||
claude_args: |
|
||||
--max-turns 25
|
||||
--model claude-opus-4-8
|
||||
--allowedTools "Read,Glob,Grep,Bash(git diff:*)"
|
||||
|
||||
prompt: |
|
||||
You are reviewing a PR to check whether any agent instruction files need updating.
|
||||
|
||||
In this repo:
|
||||
- Root shared agent guidance lives in `AGENTS.md`.
|
||||
- Root `CLAUDE.md` is only a Claude Code adapter that imports `AGENTS.md`.
|
||||
- Subdirectories may still have scoped `CLAUDE.md` files.
|
||||
- `.claude/rules/` contains additional Claude Code guidance.
|
||||
|
||||
## Your task
|
||||
|
||||
1. Run `git diff origin/main...HEAD --name-only` to see which files changed in this PR.
|
||||
2. For each changed directory, check the applicable instruction files: root `AGENTS.md`, any `CLAUDE.md` in that directory or a parent directory, and relevant `.claude/rules/` files.
|
||||
3. Determine if any instruction file should be updated based on the changes. Consider:
|
||||
- New files/directories that aren't covered by existing documentation
|
||||
- Changed architecture or patterns that contradict current agent guidance
|
||||
- New dependencies, services, or infrastructure that agents should know about
|
||||
- Renamed or moved files that are referenced in an instruction file
|
||||
- Changes to build commands, test patterns, or development workflows
|
||||
|
||||
## Response format
|
||||
|
||||
If NO updates are needed, respond with exactly:
|
||||
✅ Agent instruction files look current for this PR.
|
||||
|
||||
If updates ARE needed, respond with a short list:
|
||||
📝 **Agent instruction updates suggested:**
|
||||
- `AGENTS.md`: [what should be added/changed]
|
||||
- `path/to/CLAUDE.md`: [what should be added/changed]
|
||||
- `.claude/rules/file.md`: [what should be added/changed]
|
||||
|
||||
Keep suggestions specific and brief. Only flag things that would actually mislead agents in future sessions.
|
||||
Do NOT suggest updates for trivial changes (bug fixes, small refactors within existing patterns).
|
||||
Do NOT suggest creating new CLAUDE.md files - only updates to existing instruction files.
|
||||
@@ -0,0 +1,76 @@
|
||||
name: Claude Code
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
issues:
|
||||
types: [opened, assigned]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
|
||||
jobs:
|
||||
claude:
|
||||
# Set the ENABLE_CLAUDE_CODE repository variable to 'false' to turn off Claude
|
||||
# jobs; leave it unset (the default) to keep them enabled.
|
||||
if: |
|
||||
vars.ENABLE_CLAUDE_CODE != 'false' &&
|
||||
(
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
||||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
|
||||
)
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
id-token: write
|
||||
actions: read # Required for Claude to read CI results on PRs
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
- name: 📥 Download deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: 📀 Generate Prisma Client
|
||||
run: pnpm run generate
|
||||
|
||||
- name: Run Claude Code
|
||||
id: claude
|
||||
uses: anthropics/claude-code-action@428971d2ecd6e3a7cb0ee0da2a3a8b33fdb3678d # v1.0.157
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
|
||||
# This is an optional setting that allows Claude to read CI results on PRs
|
||||
additional_permissions: |
|
||||
actions: read
|
||||
|
||||
claude_args: |
|
||||
--model claude-opus-4-5-20251101
|
||||
--allowedTools "Bash(pnpm:*),Bash(turbo:*),Bash(git:*),Bash(gh:*),Bash(npx:*),Bash(docker:*),Edit,MultiEdit,Read,Write,Glob,Grep,LS,Task"
|
||||
|
||||
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
|
||||
# prompt: 'Update the pull request description to include a summary of changes.'
|
||||
|
||||
# Optional: Add claude_args to customize behavior and configuration
|
||||
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
||||
# or https://code.claude.com/docs/en/cli-reference for available options
|
||||
# claude_args: '--allowed-tools Bash(gh pr:*)'
|
||||
@@ -0,0 +1,38 @@
|
||||
name: "🎨 Format & Lint"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
code-quality:
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
|
||||
steps:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
- name: 📥 Download deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: 💅 Check formatting
|
||||
run: pnpm exec oxfmt --check .
|
||||
|
||||
- name: 🔎 Lint
|
||||
run: pnpm exec oxlint .
|
||||
@@ -0,0 +1,72 @@
|
||||
name: "🤖 Deploy dashboard agent"
|
||||
|
||||
# Deploys the @internal/dashboard-agent chat.agent to its Trigger.dev project
|
||||
# with --skip-promotion, so a deploy never becomes "current" on its own. The
|
||||
# consuming app cuts over by pinning DASHBOARD_AGENT_VERSION to the new version.
|
||||
# Runs a leg per environment (staging + prod), each gated by its own environment;
|
||||
# a push to main that touches the agent or its store triggers both. Version
|
||||
# numbers are per-environment, so pin each environment to its own leg's version.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "internal-packages/dashboard-agent/**"
|
||||
- "internal-packages/dashboard-agent-db/**"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy dashboard agent (${{ matrix.environment }})
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
# One environment at a time: parallel deploys of the same project race.
|
||||
max-parallel: 1
|
||||
matrix:
|
||||
environment: [staging, prod]
|
||||
# Per-environment reviewer gate + source of the scoped deploy PAT.
|
||||
environment: dashboard-agent-${{ matrix.environment }}
|
||||
concurrency:
|
||||
group: dashboard-agent-deploy-${{ matrix.environment }}
|
||||
cancel-in-progress: false
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
TRIGGER_API_URL: https://api.trigger.dev
|
||||
TRIGGER_DASHBOARD_AGENT_PROJECT_REF: ${{ vars.TRIGGER_DASHBOARD_AGENT_PROJECT_REF }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install + build the CLI and the agent's deps
|
||||
run: |
|
||||
set -euo pipefail
|
||||
pnpm install --frozen-lockfile
|
||||
# Prisma client is needed because the build closure pulls in @trigger.dev/database.
|
||||
pnpm run generate
|
||||
# Config-time imports the agent's trigger.config.ts needs: defineConfig (sdk), aptGet (build).
|
||||
pnpm run build --filter trigger.dev --filter @trigger.dev/build --filter @trigger.dev/sdk
|
||||
|
||||
- name: Deploy (--skip-promotion)
|
||||
working-directory: internal-packages/dashboard-agent
|
||||
env:
|
||||
TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_DASHBOARD_AGENT_DEPLOY_TOKEN }}
|
||||
# Invoke the built CLI directly (what the workspace .bin/trigger wrapper does),
|
||||
# so a not-yet-linked bin after a fresh install can't break the deploy.
|
||||
run: node ../../packages/cli-v3/dist/esm/index.js deploy --skip-promotion --env ${{ matrix.environment }}
|
||||
@@ -0,0 +1,87 @@
|
||||
name: Dependabot Critical Alerts
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 8 * * *" # Daily 08:00 UTC
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
severity:
|
||||
description: "Severity to alert on"
|
||||
type: choice
|
||||
options:
|
||||
- critical
|
||||
- high
|
||||
- medium
|
||||
- low
|
||||
default: critical
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
alert:
|
||||
name: Post critical alerts
|
||||
# Set the ENABLE_DEPENDABOT_ALERTS repository variable to 'false' to turn off
|
||||
# the Dependabot alert/summary notifiers — e.g. forks/mirrors that lack the
|
||||
# DEPENDABOT_ALERTS_TOKEN / SLACK_BOT_TOKEN secrets. Defaults to enabled.
|
||||
if: ${{ vars.ENABLE_DEPENDABOT_ALERTS != 'false' }}
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
environment: dependabot-summary
|
||||
env:
|
||||
SEVERITY: ${{ inputs.severity || 'critical' }}
|
||||
steps:
|
||||
- name: Fetch alerts
|
||||
id: alerts
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.DEPENDABOT_ALERTS_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
gh api -X GET "/repos/$REPO/dependabot/alerts" \
|
||||
-F state=open -F severity="$SEVERITY" --paginate > pages.json
|
||||
jq -s 'add' pages.json > alerts.json
|
||||
TOTAL=$(jq 'length' alerts.json)
|
||||
echo "total=$TOTAL" >> "$GITHUB_OUTPUT"
|
||||
if [ "$TOTAL" = "0" ]; then
|
||||
exit 0
|
||||
fi
|
||||
LIST=$(jq -r '
|
||||
map("• <\(.html_url)|#\(.number)> *\(.dependency.package.name)* - \(.security_advisory.summary)")
|
||||
| join("\n")
|
||||
' alerts.json)
|
||||
{
|
||||
echo "list<<EOF"
|
||||
echo "$LIST"
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build Slack payload
|
||||
if: steps.alerts.outputs.total != '0'
|
||||
env:
|
||||
REPO: ${{ github.repository }}
|
||||
CHANNEL: ${{ vars.SLACK_CHANNEL_ID }}
|
||||
TOTAL: ${{ steps.alerts.outputs.total }}
|
||||
LIST: ${{ steps.alerts.outputs.list }}
|
||||
run: |
|
||||
jq -n \
|
||||
--arg channel "$CHANNEL" \
|
||||
--arg repo "$REPO" \
|
||||
--arg total "$TOTAL" \
|
||||
--arg list "$LIST" \
|
||||
--arg severity "$SEVERITY" \
|
||||
'{
|
||||
channel: $channel,
|
||||
text: ":bufo-alarma: `\($repo)` - *\($total) open \($severity) alert(s)*\n\($list)\n\n<https://github.com/\($repo)/security/dependabot?q=is%3Aopen+severity%3A\($severity)|View \($severity) alerts>"
|
||||
}' > payload.json
|
||||
|
||||
- name: Post Slack alert
|
||||
if: steps.alerts.outputs.total != '0'
|
||||
uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3
|
||||
with:
|
||||
method: chat.postMessage
|
||||
token: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
payload-file-path: payload.json
|
||||
@@ -0,0 +1,210 @@
|
||||
name: Dependabot Weekly Summary
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 8 * * 1" # Mon 08:00 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
# Single-purpose monitoring workflow; serialise on workflow name only - we never
|
||||
# want two concurrent summary runs racing to post the same digest.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: read # gh CLI baseline
|
||||
pull-requests: read # gh pr list (open dependabot PRs)
|
||||
actions: read # gh run list / view (parse latest dependabot run logs)
|
||||
|
||||
jobs:
|
||||
summary:
|
||||
name: Post weekly Dependabot summary
|
||||
# Set the ENABLE_DEPENDABOT_ALERTS repository variable to 'false' to turn off
|
||||
# the Dependabot alert/summary notifiers — e.g. forks/mirrors that lack the
|
||||
# DEPENDABOT_ALERTS_TOKEN / SLACK_BOT_TOKEN secrets. Defaults to enabled.
|
||||
if: ${{ vars.ENABLE_DEPENDABOT_ALERTS != 'false' }}
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
environment: dependabot-summary
|
||||
env:
|
||||
# Severities surface in the actions list when their remaining TTR drops
|
||||
# below this many days. Override via repo/env var ACTION_THRESHOLD_DAYS.
|
||||
THRESHOLD_DAYS: ${{ vars.ACTION_THRESHOLD_DAYS || '7' }}
|
||||
steps:
|
||||
- name: Fetch alerts and compute summaries
|
||||
id: alerts
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.DEPENDABOT_ALERTS_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
if ! gh api -X GET "/repos/$REPO/dependabot/alerts" --paginate > pages.json 2> err.txt; then
|
||||
echo "total=?" >> "$GITHUB_OUTPUT"
|
||||
ERR=$(head -c 200 err.txt | tr '\n' ' ')
|
||||
echo "by_severity=:x: _failed to fetch alerts: ${ERR}_" >> "$GITHUB_OUTPUT"
|
||||
echo "actions=:x: _alerts unavailable_" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
jq -s '[.[][] | select(.state == "open")]' pages.json > open.json
|
||||
|
||||
TOTAL=$(jq 'length' open.json)
|
||||
echo "total=$TOTAL" >> "$GITHUB_OUTPUT"
|
||||
|
||||
if [ "$TOTAL" = "0" ]; then
|
||||
echo "by_severity=:white_check_mark: No open alerts." >> "$GITHUB_OUTPUT"
|
||||
echo "actions=_None_" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Severity breakdown - real newlines so jq --arg in the payload
|
||||
# builder encodes them as proper \n in JSON (Slack renders as breaks).
|
||||
BY_SEV=$(jq -r '
|
||||
group_by(.security_advisory.severity)
|
||||
| map({sev: .[0].security_advisory.severity,
|
||||
count: length,
|
||||
weight: ({"critical":0,"high":1,"medium":2,"low":3}[.[0].security_advisory.severity])})
|
||||
| sort_by(.weight)
|
||||
| map("• *\(.count)* \(.sev)")
|
||||
| join("\n")
|
||||
' open.json)
|
||||
{
|
||||
echo "by_severity<<EOF"
|
||||
echo "$BY_SEV"
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Actions: alerts within THRESHOLD_DAYS of their TTR (P0=7d, P1=30d, P2=90d, P3=no deadline)
|
||||
# Grouped by (package, severity); shows earliest deadline per group.
|
||||
ACTIONS=$(jq -r --argjson threshold "$THRESHOLD_DAYS" '
|
||||
[.[]
|
||||
| (.security_advisory.severity) as $sev
|
||||
| ({"critical":7,"high":30,"medium":90,"low":null}[$sev]) as $ttr
|
||||
| select($ttr != null)
|
||||
| ((now - (.created_at | fromdateiso8601)) / 86400 | floor) as $age
|
||||
| {pkg: .dependency.package.name, sev: $sev, remaining: ($ttr - $age)}
|
||||
]
|
||||
| group_by([.pkg, .sev])
|
||||
| map({pkg: .[0].pkg, sev: .[0].sev, count: length, min_remaining: ([.[].remaining] | min)})
|
||||
| map(select(.min_remaining < $threshold))
|
||||
| sort_by(.min_remaining)
|
||||
| if length == 0 then "_None_"
|
||||
else (map(
|
||||
"• *\(.pkg)* (\(.sev))" +
|
||||
(if .count > 1 then " ×\(.count)" else "" end) + " - " +
|
||||
(if .min_remaining < 0 then "*OVERDUE* by \(-.min_remaining)d"
|
||||
else "\(.min_remaining)d remaining" end)
|
||||
) | join("\n"))
|
||||
end
|
||||
' open.json)
|
||||
{
|
||||
echo "actions<<EOF"
|
||||
echo "$ACTIONS"
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Fetch open dependabot PRs
|
||||
id: prs
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
REPO_URL: https://github.com/${{ github.repository }}
|
||||
run: |
|
||||
if ! PR_JSON=$(gh pr list --repo "$REPO" --state open --author "app/dependabot" --json number,title 2> err.txt); then
|
||||
ERR=$(head -c 200 err.txt | tr '\n' ' ')
|
||||
echo "list=:x: _failed to fetch PRs: ${ERR}_" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
LIST=$(echo "$PR_JSON" | jq -r --arg url "$REPO_URL" '
|
||||
if length == 0 then "_None_"
|
||||
else (map("• <\($url)/pull/\(.number)|#\(.number)> \(.title)") | join("\n"))
|
||||
end
|
||||
')
|
||||
{
|
||||
echo "list<<EOF"
|
||||
echo "$LIST"
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Find latest npm dependabot run
|
||||
id: latest
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
# Repos without a dependabot.yml have no "Dependabot Updates" workflow;
|
||||
# treat the lookup failure as "no recent run found" rather than failing.
|
||||
if ! RUN_ID=$(gh run list --repo "$REPO" --workflow "Dependabot Updates" --status success --limit 30 --json databaseId,name --jq 'first(.[] | select(.name | startswith("npm_and_yarn")) | .databaseId) // empty' 2>/dev/null); then
|
||||
RUN_ID=""
|
||||
fi
|
||||
echo "run_id=$RUN_ID" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Extract stuck deps (only if actions pending)
|
||||
id: stuck
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
RUN_ID: ${{ steps.latest.outputs.run_id }}
|
||||
ACTIONS: ${{ steps.alerts.outputs.actions }}
|
||||
run: |
|
||||
# Skip the stuck section entirely when nothing in the actions list
|
||||
# - keeps the digest tidy when there's nothing to actually act on.
|
||||
if [ "$ACTIONS" = "_None_" ]; then
|
||||
echo "section=" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
HEADER=$'\n\n*Couldn\'t auto-fix (need manual `pnpm.overrides`):*\n'
|
||||
if [ -z "$RUN_ID" ]; then
|
||||
{
|
||||
echo "section<<EOF"
|
||||
echo "${HEADER}_(no recent npm run found)_"
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
gh run view "$RUN_ID" --repo "$REPO" --log > log.txt 2>&1 || true
|
||||
STUCK=$(grep -oE "No update possible for [^[:space:]]+ [0-9][^[:space:]]*" log.txt | sed 's/No update possible for //' | sort -u || true)
|
||||
if [ -z "$STUCK" ]; then
|
||||
{
|
||||
echo "section<<EOF"
|
||||
echo "${HEADER}_None_"
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
LIST=$(echo "$STUCK" | awk 'NR>1{printf "\n"} {printf "• *%s* %s", $1, $2}')
|
||||
{
|
||||
echo "section<<EOF"
|
||||
echo "${HEADER}${LIST}"
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Build Slack payload
|
||||
env:
|
||||
REPO: ${{ github.repository }}
|
||||
CHANNEL: ${{ vars.SLACK_CHANNEL_ID }}
|
||||
TOTAL: ${{ steps.alerts.outputs.total }}
|
||||
BY_SEVERITY: ${{ steps.alerts.outputs.by_severity }}
|
||||
PRS_LIST: ${{ steps.prs.outputs.list }}
|
||||
ACTIONS: ${{ steps.alerts.outputs.actions }}
|
||||
STUCK: ${{ steps.stuck.outputs.section }}
|
||||
run: |
|
||||
# Build payload via jq so PR titles or error strings containing
|
||||
# quotes/backslashes/newlines can't break the JSON.
|
||||
jq -n \
|
||||
--arg channel "$CHANNEL" \
|
||||
--arg repo "$REPO" \
|
||||
--arg total "$TOTAL" \
|
||||
--arg by_severity "$BY_SEVERITY" \
|
||||
--arg prs_list "$PRS_LIST" \
|
||||
--arg actions "$ACTIONS" \
|
||||
--arg stuck "$STUCK" \
|
||||
--arg threshold "$THRESHOLD_DAYS" \
|
||||
'{
|
||||
channel: $channel,
|
||||
text: ":calendar: *Weekly Dependabot summary* - `\($repo)`\n\n*Open alerts (\($total)):*\n\($by_severity)\n\n*Open Dependabot PRs:*\n\($prs_list)\n\n*Actions needed (<\($threshold)d remaining):*\n\($actions)\($stuck)\n\n<https://github.com/\($repo)/security/dependabot|Dependabot alerts>"
|
||||
}' > payload.json
|
||||
|
||||
- name: Post Slack summary
|
||||
uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3
|
||||
with:
|
||||
method: chat.postMessage
|
||||
token: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
payload-file-path: payload.json
|
||||
@@ -0,0 +1,44 @@
|
||||
name: 📚 Docs Checks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "docs/**"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "docs/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check-broken-links:
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./docs
|
||||
steps:
|
||||
- name: 📥 Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 📦 Cache npm
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: |
|
||||
~/.npm
|
||||
key: |
|
||||
${{ runner.os }}-mintlify
|
||||
restore-keys: |
|
||||
${{ runner.os }}-mintlify
|
||||
|
||||
- name: 🔗 Check for broken links
|
||||
run: npx mintlify@4.0.393 broken-links
|
||||
@@ -0,0 +1,120 @@
|
||||
name: "🛡️ E2E Tests: Webapp Auth (full)"
|
||||
|
||||
# Comprehensive RBAC auth test suite — see TRI-8731. Runs separately from
|
||||
# the smoke e2e-webapp.yml because it covers every route family with a
|
||||
# pass/fail matrix and would otherwise dominate per-PR CI time.
|
||||
#
|
||||
# Triggered:
|
||||
# - Manually via workflow_dispatch.
|
||||
# - Nightly via schedule.
|
||||
# - On pull requests touching auth-relevant files only (paths filter).
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 4 * * *" # 04:00 UTC daily
|
||||
pull_request:
|
||||
paths:
|
||||
- "apps/webapp/app/services/routeBuilders/**"
|
||||
- "apps/webapp/app/services/rbac.server.ts"
|
||||
- "apps/webapp/app/services/apiAuth.server.ts"
|
||||
- "apps/webapp/app/services/personalAccessToken.server.ts"
|
||||
- "apps/webapp/app/services/sessionStorage.server.ts"
|
||||
- "apps/webapp/app/routes/api.v*.**"
|
||||
- "apps/webapp/app/routes/realtime.v*.**"
|
||||
- "apps/webapp/test/**/*.e2e.full.test.ts"
|
||||
- "apps/webapp/test/setup/global-e2e-full-setup.ts"
|
||||
- "apps/webapp/test/helpers/sharedTestServer.ts"
|
||||
- "apps/webapp/test/helpers/seedTestSession.ts"
|
||||
- "apps/webapp/vitest.e2e.full.config.ts"
|
||||
- "internal-packages/rbac/**"
|
||||
- "packages/plugins/**"
|
||||
- ".github/workflows/e2e-webapp-auth-full.yml"
|
||||
|
||||
jobs:
|
||||
e2eAuthFull:
|
||||
name: "🛡️ E2E Auth Tests (full)"
|
||||
runs-on: warp-ubuntu-latest-x64-8x
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
steps:
|
||||
- name: 🔧 Disable IPv6
|
||||
run: |
|
||||
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
|
||||
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
|
||||
sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1
|
||||
|
||||
- name: 🔧 Configure docker address pool
|
||||
run: |
|
||||
CONFIG='{
|
||||
"default-address-pools" : [
|
||||
{
|
||||
"base" : "172.17.0.0/12",
|
||||
"size" : 20
|
||||
},
|
||||
{
|
||||
"base" : "192.168.0.0/16",
|
||||
"size" : 24
|
||||
}
|
||||
]
|
||||
}'
|
||||
mkdir -p /etc/docker
|
||||
echo "$CONFIG" | sudo tee /etc/docker/daemon.json
|
||||
|
||||
- name: 🔧 Restart docker daemon
|
||||
run: sudo systemctl restart docker
|
||||
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# Don't leave the GITHUB_TOKEN in .git/config — this job
|
||||
# doesn't need to push and the persisted creds would be
|
||||
# readable from any subsequent step (zizmor/artipacked).
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
- name: 🐳 Login to DockerHub
|
||||
if: ${{ env.DOCKERHUB_USERNAME }}
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: 🐳 Skipping DockerHub login (no secrets available)
|
||||
if: ${{ !env.DOCKERHUB_USERNAME }}
|
||||
run: echo "DockerHub login skipped because secrets are not available."
|
||||
|
||||
- name: 🐳 Pre-pull testcontainer images
|
||||
if: ${{ env.DOCKERHUB_USERNAME }}
|
||||
run: |
|
||||
docker pull postgres:14
|
||||
docker pull redis:7.2
|
||||
docker pull testcontainers/ryuk:0.11.0
|
||||
|
||||
- name: 📥 Download deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: 📀 Generate Prisma Client
|
||||
run: pnpm run generate
|
||||
|
||||
- name: 🏗️ Build Webapp
|
||||
run: pnpm run build --filter webapp
|
||||
|
||||
- name: 🛡️ Run Webapp Full Auth E2E Tests
|
||||
run: cd apps/webapp && pnpm exec vitest run --config vitest.e2e.full.config.ts --reporter=default
|
||||
env:
|
||||
WEBAPP_TEST_VERBOSE: "1"
|
||||
@@ -0,0 +1,97 @@
|
||||
name: "🧪 E2E Tests: Webapp"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME:
|
||||
required: false
|
||||
DOCKERHUB_TOKEN:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
e2eTests:
|
||||
name: "🧪 E2E Tests: Webapp"
|
||||
runs-on: warp-ubuntu-latest-x64-8x
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
steps:
|
||||
- name: 🔧 Disable IPv6
|
||||
run: |
|
||||
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
|
||||
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
|
||||
sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1
|
||||
|
||||
- name: 🔧 Configure docker address pool
|
||||
run: |
|
||||
CONFIG='{
|
||||
"default-address-pools" : [
|
||||
{
|
||||
"base" : "172.17.0.0/12",
|
||||
"size" : 20
|
||||
},
|
||||
{
|
||||
"base" : "192.168.0.0/16",
|
||||
"size" : 24
|
||||
}
|
||||
]
|
||||
}'
|
||||
mkdir -p /etc/docker
|
||||
echo "$CONFIG" | sudo tee /etc/docker/daemon.json
|
||||
|
||||
- name: 🔧 Restart docker daemon
|
||||
run: sudo systemctl restart docker
|
||||
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
# ..to avoid rate limits when pulling images
|
||||
- name: 🐳 Login to DockerHub
|
||||
if: ${{ env.DOCKERHUB_USERNAME }}
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: 🐳 Skipping DockerHub login (no secrets available)
|
||||
if: ${{ !env.DOCKERHUB_USERNAME }}
|
||||
run: echo "DockerHub login skipped because secrets are not available."
|
||||
|
||||
- name: 🐳 Pre-pull testcontainer images
|
||||
if: ${{ env.DOCKERHUB_USERNAME }}
|
||||
run: |
|
||||
echo "Pre-pulling Docker images with authenticated session..."
|
||||
docker pull postgres:14
|
||||
docker pull redis:7.2
|
||||
docker pull testcontainers/ryuk:0.11.0
|
||||
echo "Image pre-pull complete"
|
||||
|
||||
- name: 📥 Download deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: 📀 Generate Prisma Client
|
||||
run: pnpm run generate
|
||||
|
||||
- name: 🏗️ Build Webapp
|
||||
run: pnpm run build --filter webapp
|
||||
|
||||
- name: 🧪 Run Webapp E2E Tests
|
||||
run: cd apps/webapp && pnpm exec vitest run --config vitest.e2e.config.ts --reporter=default
|
||||
env:
|
||||
WEBAPP_TEST_VERBOSE: "1"
|
||||
@@ -0,0 +1,62 @@
|
||||
name: "E2E"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
package:
|
||||
description: The identifier of the job to run
|
||||
default: webapp
|
||||
required: false
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
cli-v3:
|
||||
name: "🧪 CLI v3 tests (${{ matrix.os }} - ${{ matrix.package-manager }})"
|
||||
if: inputs.package == 'cli-v3' || inputs.package == ''
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [warp-ubuntu-latest-x64-4x, warp-windows-latest-x64-4x]
|
||||
package-manager: ["npm", "pnpm"]
|
||||
steps:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
|
||||
- name: 📥 Download deps
|
||||
run: pnpm install --frozen-lockfile --filter trigger.dev...
|
||||
|
||||
# Prisma clients generate concurrently and share one engine binary in the
|
||||
# pnpm store; the generate script retries the transient Windows EPERM race.
|
||||
- name: 📀 Generate Prisma Client
|
||||
run: pnpm run generate
|
||||
|
||||
- name: 🔧 Build v3 cli monorepo dependencies
|
||||
run: pnpm run build --filter trigger.dev^...
|
||||
|
||||
- name: 🔧 Build worker template files
|
||||
run: pnpm --filter trigger.dev run --if-present build:workers
|
||||
|
||||
- name: Enable corepack
|
||||
run: corepack enable
|
||||
|
||||
- name: Run E2E Tests
|
||||
shell: bash
|
||||
run: |
|
||||
LOG=debug PM=${{ matrix.package-manager }} pnpm --filter trigger.dev run test:e2e
|
||||
@@ -0,0 +1,205 @@
|
||||
name: 🧭 Helm Chart Prerelease
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "hosting/k8s/helm/**"
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "hosting/k8s/helm/**"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
app_version:
|
||||
description: "Override appVersion (e.g. 'main', 'v4.4.4'). Leave empty to keep Chart.yaml value."
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
concurrency:
|
||||
group: helm-prerelease-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
CHART_NAME: trigger
|
||||
|
||||
jobs:
|
||||
lint-and-test:
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
|
||||
with:
|
||||
version: "3.18.3"
|
||||
|
||||
- name: Build dependencies
|
||||
run: helm dependency build ./hosting/k8s/helm/
|
||||
|
||||
- name: Extract dependency charts
|
||||
run: |
|
||||
cd ./hosting/k8s/helm/
|
||||
for file in ./charts/*.tgz; do echo "Extracting $file"; tar -xzf "$file" -C ./charts; done
|
||||
|
||||
- name: Lint Helm Chart
|
||||
run: |
|
||||
helm lint ./hosting/k8s/helm/
|
||||
|
||||
- name: Render templates
|
||||
run: |
|
||||
helm template test-release ./hosting/k8s/helm/ \
|
||||
--values ./hosting/k8s/helm/values.yaml \
|
||||
--output-dir ./helm-output
|
||||
|
||||
- name: Validate manifests
|
||||
uses: docker://ghcr.io/yannh/kubeconform:v0.7.0@sha256:85dbef6b4b312b99133decc9c6fc9495e9fc5f92293d4ff3b7e1b30f5611823c
|
||||
with:
|
||||
entrypoint: "/kubeconform"
|
||||
args: "-summary -output json ./helm-output"
|
||||
|
||||
prerelease:
|
||||
needs: lint-and-test
|
||||
# Set the ENABLE_HELM_PRERELEASE repository variable to 'false' to turn off
|
||||
# publishing the chart to GHCR — e.g. forks/mirrors that lack write_package
|
||||
# on the owner's charts namespace. Defaults to enabled; the lint-and-test
|
||||
# job above always runs regardless.
|
||||
if: |
|
||||
vars.ENABLE_HELM_PRERELEASE != 'false' &&
|
||||
((github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) ||
|
||||
github.event_name == 'push' ||
|
||||
github.event_name == 'workflow_dispatch')
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
|
||||
with:
|
||||
version: "3.18.3"
|
||||
|
||||
- name: Build dependencies
|
||||
run: helm dependency build ./hosting/k8s/helm/
|
||||
|
||||
- name: Extract dependency charts
|
||||
run: |
|
||||
cd ./hosting/k8s/helm/
|
||||
for file in ./charts/*.tgz; do echo "Extracting $file"; tar -xzf "$file" -C ./charts; done
|
||||
|
||||
- name: Log in to Container Registry
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Generate prerelease version
|
||||
id: version
|
||||
run: |
|
||||
BASE_VERSION=$(grep '^version:' ./hosting/k8s/helm/Chart.yaml | awk '{print $2}')
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
PR_NUMBER=${{ github.event.pull_request.number }}
|
||||
SHORT_SHA=$(echo "${{ github.event.pull_request.head.sha }}" | cut -c1-7)
|
||||
PRERELEASE_VERSION="${BASE_VERSION}-pr${PR_NUMBER}.${SHORT_SHA}"
|
||||
elif [[ "${{ github.event_name }}" == "push" ]]; then
|
||||
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
||||
PRERELEASE_VERSION="${BASE_VERSION}-main.${SHORT_SHA}"
|
||||
else
|
||||
SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
|
||||
REF_SLUG=$(echo "${GITHUB_REF_NAME}" | tr '/' '-' | tr -cd 'a-zA-Z0-9-')
|
||||
if [[ -z "$REF_SLUG" ]]; then
|
||||
REF_SLUG="manual"
|
||||
fi
|
||||
PRERELEASE_VERSION="${BASE_VERSION}-${REF_SLUG}.${SHORT_SHA}"
|
||||
fi
|
||||
echo "version=$PRERELEASE_VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "Prerelease version: $PRERELEASE_VERSION"
|
||||
|
||||
- name: Update Chart.yaml with prerelease version
|
||||
run: |
|
||||
sed -i "s/^version:.*/version: ${STEPS_VERSION_OUTPUTS_VERSION}/" ./hosting/k8s/helm/Chart.yaml
|
||||
env:
|
||||
STEPS_VERSION_OUTPUTS_VERSION: ${{ steps.version.outputs.version }}
|
||||
|
||||
- name: Override appVersion
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.app_version != ''
|
||||
env:
|
||||
APP_VERSION: ${{ inputs.app_version }}
|
||||
run: |
|
||||
yq -i '.appVersion = strenv(APP_VERSION)' ./hosting/k8s/helm/Chart.yaml
|
||||
|
||||
- name: Package Helm Chart
|
||||
run: |
|
||||
helm package ./hosting/k8s/helm/ --destination /tmp/
|
||||
|
||||
- name: Push Helm Chart to GHCR
|
||||
run: |
|
||||
VERSION="${STEPS_VERSION_OUTPUTS_VERSION}"
|
||||
CHART_PACKAGE="/tmp/${{ env.CHART_NAME }}-${VERSION}.tgz"
|
||||
|
||||
# Push to GHCR OCI registry
|
||||
helm push "$CHART_PACKAGE" "oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/charts"
|
||||
env:
|
||||
STEPS_VERSION_OUTPUTS_VERSION: ${{ steps.version.outputs.version }}
|
||||
|
||||
- name: Write run summary
|
||||
run: |
|
||||
{
|
||||
echo "### 🧭 Helm Chart Prerelease Published"
|
||||
echo ""
|
||||
echo "**Version:** \`${STEPS_VERSION_OUTPUTS_VERSION}\`"
|
||||
echo ""
|
||||
echo "**Install:**"
|
||||
echo '```bash'
|
||||
echo "helm upgrade --install trigger \\"
|
||||
echo " oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/charts/${{ env.CHART_NAME }} \\"
|
||||
echo " --version \"${STEPS_VERSION_OUTPUTS_VERSION}\""
|
||||
echo '```'
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
env:
|
||||
STEPS_VERSION_OUTPUTS_VERSION: ${{ steps.version.outputs.version }}
|
||||
|
||||
- name: Find existing comment
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0
|
||||
id: find-comment
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-author: "github-actions[bot]"
|
||||
body-includes: "Helm Chart Prerelease Published"
|
||||
|
||||
- name: Create or update PR comment
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
|
||||
with:
|
||||
comment-id: ${{ steps.find-comment.outputs.comment-id }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: |
|
||||
### 🧭 Helm Chart Prerelease Published
|
||||
|
||||
**Version:** `${{ steps.version.outputs.version }}`
|
||||
|
||||
**Install:**
|
||||
```bash
|
||||
helm upgrade --install trigger \
|
||||
oci://ghcr.io/${{ github.repository_owner }}/charts/trigger \
|
||||
--version "${{ steps.version.outputs.version }}"
|
||||
```
|
||||
|
||||
> ⚠️ This is a prerelease for testing. Do not use in production.
|
||||
edit-mode: replace
|
||||
@@ -0,0 +1,183 @@
|
||||
name: 🤖 PR Checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
name: Detect changes
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
outputs:
|
||||
code: ${{ steps.code_filter.outputs.code }}
|
||||
typecheck_self: ${{ steps.filter.outputs.typecheck_self }}
|
||||
webapp: ${{ steps.filter.outputs.webapp }}
|
||||
packages: ${{ steps.filter.outputs.packages }}
|
||||
internal: ${{ steps.filter.outputs.internal }}
|
||||
cli: ${{ steps.filter.outputs.cli }}
|
||||
sdk: ${{ steps.filter.outputs.sdk }}
|
||||
steps:
|
||||
# `code` uses `every` semantics so the negation patterns actually subtract.
|
||||
# With the default `some` quantifier, `**` matches every file and the
|
||||
# subsequent `!...` patterns are no-ops (each pattern is OR'd, not AND'd).
|
||||
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
id: code_filter
|
||||
with:
|
||||
predicate-quantifier: every
|
||||
filters: |
|
||||
code:
|
||||
- '**'
|
||||
- '!docs/**'
|
||||
- '!.changeset/**'
|
||||
- '!hosting/**'
|
||||
- '!.github/**'
|
||||
- '!**/*.md'
|
||||
- '!**/.env.example'
|
||||
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
typecheck_self:
|
||||
- '.github/workflows/pr_checks.yml'
|
||||
- '.github/workflows/typecheck.yml'
|
||||
- '.github/workflows/code-quality.yml'
|
||||
webapp:
|
||||
- 'apps/webapp/**'
|
||||
- 'packages/**'
|
||||
- 'internal-packages/**'
|
||||
- '.github/workflows/pr_checks.yml'
|
||||
- '.github/workflows/unit-tests-webapp.yml'
|
||||
- '.github/workflows/e2e-webapp.yml'
|
||||
- '.configs/**'
|
||||
- 'package.json'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'pnpm-workspace.yaml'
|
||||
- 'turbo.json'
|
||||
packages:
|
||||
- 'packages/**'
|
||||
- '.github/workflows/pr_checks.yml'
|
||||
- '.github/workflows/unit-tests-packages.yml'
|
||||
- '.configs/**'
|
||||
- 'package.json'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'pnpm-workspace.yaml'
|
||||
- 'turbo.json'
|
||||
internal:
|
||||
- 'internal-packages/**'
|
||||
- 'packages/**'
|
||||
- '.github/workflows/pr_checks.yml'
|
||||
- '.github/workflows/unit-tests-internal.yml'
|
||||
- '.configs/**'
|
||||
- 'package.json'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'pnpm-workspace.yaml'
|
||||
- 'turbo.json'
|
||||
cli:
|
||||
- 'packages/cli-v3/**'
|
||||
- 'packages/build/**'
|
||||
- 'packages/core/**'
|
||||
- 'packages/schema-to-json/**'
|
||||
- '.github/workflows/pr_checks.yml'
|
||||
- '.github/workflows/e2e.yml'
|
||||
- '.configs/**'
|
||||
- 'package.json'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'pnpm-workspace.yaml'
|
||||
- 'turbo.json'
|
||||
sdk:
|
||||
- 'packages/trigger-sdk/**'
|
||||
- 'packages/core/**'
|
||||
- '.github/workflows/pr_checks.yml'
|
||||
- '.github/workflows/sdk-compat.yml'
|
||||
- '.configs/**'
|
||||
- 'package.json'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'pnpm-workspace.yaml'
|
||||
- 'turbo.json'
|
||||
|
||||
code-quality:
|
||||
uses: ./.github/workflows/code-quality.yml
|
||||
|
||||
typecheck:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.code == 'true' || needs.changes.outputs.typecheck_self == 'true'
|
||||
uses: ./.github/workflows/typecheck.yml
|
||||
|
||||
webapp:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.webapp == 'true'
|
||||
uses: ./.github/workflows/unit-tests-webapp.yml
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
e2e-webapp:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.webapp == 'true'
|
||||
uses: ./.github/workflows/e2e-webapp.yml
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
packages:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.packages == 'true'
|
||||
uses: ./.github/workflows/unit-tests-packages.yml
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
internal:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.internal == 'true'
|
||||
uses: ./.github/workflows/unit-tests-internal.yml
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
e2e:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.cli == 'true'
|
||||
uses: ./.github/workflows/e2e.yml
|
||||
with:
|
||||
package: cli-v3
|
||||
|
||||
sdk-compat:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.sdk == 'true'
|
||||
uses: ./.github/workflows/sdk-compat.yml
|
||||
|
||||
all-checks:
|
||||
name: All PR Checks
|
||||
needs:
|
||||
- changes
|
||||
- code-quality
|
||||
- typecheck
|
||||
- webapp
|
||||
- e2e-webapp
|
||||
- packages
|
||||
- internal
|
||||
- e2e
|
||||
- sdk-compat
|
||||
if: always()
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
steps:
|
||||
- name: Verify all checks
|
||||
run: |
|
||||
if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then
|
||||
echo "One or more checks failed"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
|
||||
echo "One or more checks were cancelled"
|
||||
exit 1
|
||||
fi
|
||||
echo "All checks passed or were skipped due to path filters"
|
||||
@@ -0,0 +1,76 @@
|
||||
name: 🌱 Preview environment dispatch
|
||||
|
||||
# Opt-in per-PR preview environments
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize, closed, labeled, unlabeled]
|
||||
|
||||
# Serialize a PR's events so dispatches arrive in order. Cloud-side concurrency
|
||||
# collapses by branch but can't fix out-of-order arrival — e.g. a push racing a
|
||||
# close could cancel the in-flight destroy and leak the preview. One short API
|
||||
# call, so queuing is cheap; cancel-in-progress: false lets an in-flight
|
||||
# dispatch finish (GitHub keeps only the latest pending, the desired behavior).
|
||||
concurrency:
|
||||
group: preview-dispatch-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
dispatch:
|
||||
name: Dispatch preview-deploy to cloud
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
# label added -> create
|
||||
# new commit while labeled -> update
|
||||
# label removed / PR closed -> destroy
|
||||
if: >-
|
||||
github.event.pull_request.head.repo.full_name == github.repository &&
|
||||
(
|
||||
(github.event.action == 'labeled' && github.event.label.name == 'preview') ||
|
||||
(github.event.action == 'unlabeled' && github.event.label.name == 'preview') ||
|
||||
(
|
||||
contains(github.event.pull_request.labels.*.name, 'preview') &&
|
||||
contains(fromJSON('["opened","reopened","synchronize","closed"]'), github.event.action)
|
||||
)
|
||||
)
|
||||
steps:
|
||||
- name: Build dispatch payload
|
||||
id: payload
|
||||
env:
|
||||
ACTION: ${{ github.event.action }}
|
||||
BRANCH: ${{ github.event.pull_request.head.ref }}
|
||||
COMMIT: ${{ github.event.pull_request.head.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Map the GitHub PR action to the cloud pipeline's lifecycle event.
|
||||
case "$ACTION" in
|
||||
labeled | opened | reopened) EVENT=opened ;;
|
||||
synchronize) EVENT=synchronize ;;
|
||||
unlabeled | closed) EVENT=closed ;;
|
||||
*) echo "unexpected action: $ACTION" >&2; exit 1 ;;
|
||||
esac
|
||||
# jq --arg JSON-escapes every value, so a branch name containing
|
||||
# quotes/braces can't break or inject into the client payload.
|
||||
payload=$(jq -nc \
|
||||
--arg b "$BRANCH" \
|
||||
--arg c "$COMMIT" \
|
||||
--arg e "$EVENT" \
|
||||
'{branch_name: $b, commit: $c, pull_request_event: $e}')
|
||||
{
|
||||
echo "client_payload=$payload"
|
||||
echo "summary=$EVENT for $BRANCH @ ${COMMIT:0:7}"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Log dispatch
|
||||
env:
|
||||
SUMMARY: ${{ steps.payload.outputs.summary }}
|
||||
run: echo "Dispatching preview-deploy event ($SUMMARY)"
|
||||
|
||||
- name: Send repository_dispatch
|
||||
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
|
||||
with:
|
||||
token: ${{ secrets.CROSS_REPO_PAT }}
|
||||
repository: triggerdotdev/cloud
|
||||
event-type: preview-deploy
|
||||
client-payload: ${{ steps.payload.outputs.client_payload }}
|
||||
@@ -0,0 +1,83 @@
|
||||
name: 📦 Preview packages (pkg.pr.new)
|
||||
|
||||
# Publishes installable preview builds of the public @trigger.dev/* packages
|
||||
# for every push to a branch, via https://pkg.pr.new. These are NOT published
|
||||
# to npm — pkg.pr.new serves them by commit SHA and drops install instructions
|
||||
# in a comment on the associated PR, e.g.
|
||||
# npm i https://pkg.pr.new/@trigger.dev/sdk@<sha>
|
||||
#
|
||||
# Prerequisites:
|
||||
# - The pkg.pr.new GitHub App must be installed on triggerdotdev/trigger.dev
|
||||
# (https://github.com/apps/pkg-pr-new). Publishing fails until it is.
|
||||
#
|
||||
# Fork note: pkg.pr.new authenticates with a GitHub Actions OIDC token, which
|
||||
# GitHub does not issue to pull_request workflows from forks. This `push`
|
||||
# trigger therefore covers branches pushed to this repo (the core team), not
|
||||
# external fork PRs. Adding fork coverage would require a workflow_run two-stage
|
||||
# setup.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- main
|
||||
- changeset-release/main
|
||||
paths:
|
||||
- "package.json"
|
||||
- "packages/**"
|
||||
- "pnpm-lock.yaml"
|
||||
- "pnpm-workspace.yaml"
|
||||
- "turbo.json"
|
||||
- ".github/workflows/preview-packages.yml"
|
||||
- "scripts/stamp-preview-version.mjs"
|
||||
- "scripts/updateVersion.ts"
|
||||
|
||||
concurrency:
|
||||
group: preview-packages-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # OIDC token used by pkg.pr.new to authenticate the publish
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Build and publish previews
|
||||
runs-on: warp-ubuntu-latest-x64-8x
|
||||
if: github.repository == 'triggerdotdev/trigger.dev'
|
||||
steps:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
- name: 📥 Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: 📀 Generate Prisma client
|
||||
run: pnpm run generate
|
||||
|
||||
# Stamp a unique 0.0.0-preview-<sha> version before building so it can't
|
||||
# collide with real npm versions and so updateVersion.ts bakes it into the
|
||||
# runtime VERSION constant. See scripts/stamp-preview-version.mjs.
|
||||
- name: 🏷️ Stamp preview version
|
||||
run: node scripts/stamp-preview-version.mjs
|
||||
env:
|
||||
GITHUB_SHA: ${{ github.sha }}
|
||||
|
||||
- name: 🔨 Build packages
|
||||
run: pnpm run build --filter "@trigger.dev/*" --filter "trigger.dev"
|
||||
|
||||
- name: 🚀 Publish previews to pkg.pr.new
|
||||
run: pnpm exec pkg-pr-new publish --pnpm --compact --commentWithSha './packages/*'
|
||||
@@ -0,0 +1,36 @@
|
||||
name: 📚 Publish docs
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "docs-release-*"
|
||||
|
||||
# Only needs to move the docs-live ref; Mintlify's GitHub app deploys from it.
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: publish-docs
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
steps:
|
||||
- name: 📥 Checkout tagged commit
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 🔗 Check for broken links
|
||||
working-directory: ./docs
|
||||
run: npx mintlify@4.0.393 broken-links
|
||||
|
||||
- name: 🚀 Fast-forward docs-live to the tagged commit
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh api -X PATCH \
|
||||
"repos/${{ github.repository }}/git/refs/heads/docs-live" \
|
||||
-f sha="${{ github.sha }}" \
|
||||
-F force=false
|
||||
@@ -0,0 +1,149 @@
|
||||
name: "🐳 Publish Webapp"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
image_tag:
|
||||
description: The image tag to publish
|
||||
type: string
|
||||
required: false
|
||||
default: ""
|
||||
image_registry:
|
||||
description: The registry namespace to publish under (e.g. ghcr.io/<owner>)
|
||||
type: string
|
||||
required: false
|
||||
default: ""
|
||||
outputs:
|
||||
version:
|
||||
description: The published image tag
|
||||
value: ${{ jobs.publish.outputs.version }}
|
||||
short_sha:
|
||||
description: Short commit SHA of the published build
|
||||
value: ${{ jobs.publish.outputs.short_sha }}
|
||||
image_repo:
|
||||
description: The image repository the build was published to (without tag)
|
||||
value: ${{ jobs.publish.outputs.image_repo }}
|
||||
digest:
|
||||
description: Multi-arch index digest (sha256:...) of the published image
|
||||
value: ${{ jobs.publish.outputs.digest }}
|
||||
secrets:
|
||||
SENTRY_AUTH_TOKEN:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
env:
|
||||
PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING: 1
|
||||
outputs:
|
||||
version: ${{ steps.get_tag.outputs.tag }}
|
||||
short_sha: ${{ steps.get_commit.outputs.sha_short }}
|
||||
image_repo: ${{ steps.set_tags.outputs.image_repo }}
|
||||
digest: ${{ steps.build_push.outputs.digest }}
|
||||
steps:
|
||||
- name: 🏭 Setup Depot CLI
|
||||
uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1.7.1
|
||||
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
|
||||
- name: "#️⃣ Get the image tag"
|
||||
id: get_tag
|
||||
uses: ./.github/actions/get-image-tag
|
||||
with:
|
||||
tag: ${{ inputs.image_tag }}
|
||||
|
||||
- name: 🔢 Get the commit hash
|
||||
id: get_commit
|
||||
run: |
|
||||
echo "sha_short=$(echo "${GITHUB_SHA}" | cut -c1-7)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 📛 Set the tags
|
||||
id: set_tags
|
||||
run: |
|
||||
# The registry namespace is resolved by the caller (defaulting to
|
||||
# ghcr.io/<owner>, overridable via the IMAGE_REGISTRY repository
|
||||
# variable); the webapp image lives at <registry>/<repo-name>. A fork
|
||||
# therefore publishes to its own package automatically.
|
||||
image_tags=$REF_WITHOUT_TAG:${STEPS_GET_TAG_OUTPUTS_TAG}
|
||||
|
||||
if [[ "${STEPS_GET_TAG_OUTPUTS_TAG}" =~ ^v4\.[0-9]+\.[0-9]+$ ]]; then
|
||||
image_tags=$image_tags,$REF_WITHOUT_TAG:v4,$REF_WITHOUT_TAG:latest
|
||||
fi
|
||||
|
||||
# when pushing the mutable main tag, also push an immutable-by-convention
|
||||
# full-commit-sha tag so a commit can be resolved to a specific digest
|
||||
if [[ "${STEPS_GET_TAG_OUTPUTS_TAG}" == "main" ]]; then
|
||||
image_tags=$image_tags,$REF_WITHOUT_TAG:${GITHUB_SHA}
|
||||
fi
|
||||
|
||||
echo "image_tags=${image_tags}" >> "$GITHUB_OUTPUT"
|
||||
echo "image_repo=${REF_WITHOUT_TAG}" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
REF_WITHOUT_TAG: ${{ format('{0}/{1}', inputs.image_registry || vars.IMAGE_REGISTRY || format('ghcr.io/{0}', github.repository_owner), github.event.repository.name) }}
|
||||
STEPS_GET_TAG_OUTPUTS_TAG: ${{ steps.get_tag.outputs.tag }}
|
||||
STEPS_GET_TAG_OUTPUTS_IS_SEMVER: ${{ steps.get_tag.outputs.is_semver }}
|
||||
|
||||
- name: 📝 Set the build info
|
||||
id: set_build_info
|
||||
run: |
|
||||
{
|
||||
tag="${STEPS_GET_TAG_OUTPUTS_TAG}"
|
||||
if [[ "${STEPS_GET_TAG_OUTPUTS_IS_SEMVER}" == true ]]; then
|
||||
echo "BUILD_APP_VERSION=${tag}"
|
||||
fi
|
||||
echo "BUILD_GIT_SHA=${GITHUB_SHA}"
|
||||
echo "BUILD_GIT_REF_NAME=${GITHUB_REF_NAME}"
|
||||
echo "BUILD_TIMESTAMP_SECONDS=$(date +%s)"
|
||||
echo "BUILD_TIMESTAMP_RFC3339=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
STEPS_GET_TAG_OUTPUTS_TAG: ${{ steps.get_tag.outputs.tag }}
|
||||
STEPS_GET_TAG_OUTPUTS_IS_SEMVER: ${{ steps.get_tag.outputs.is_semver }}
|
||||
|
||||
- name: 🐙 Login to GitHub Container Registry
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: 🐳 Build image and push to GitHub Container Registry
|
||||
id: build_push
|
||||
uses: depot/build-push-action@98e78adca7817480b8185f474a400b451d74e287 # v1.18.0
|
||||
with:
|
||||
file: ./docker/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: ${{ steps.set_tags.outputs.image_tags }}
|
||||
push: true
|
||||
build-args: |
|
||||
BUILD_APP_VERSION=${{ steps.set_build_info.outputs.BUILD_APP_VERSION }}
|
||||
BUILD_GIT_SHA=${{ steps.set_build_info.outputs.BUILD_GIT_SHA }}
|
||||
BUILD_GIT_REF_NAME=${{ steps.set_build_info.outputs.BUILD_GIT_REF_NAME }}
|
||||
BUILD_TIMESTAMP_SECONDS=${{ steps.set_build_info.outputs.BUILD_TIMESTAMP_SECONDS }}
|
||||
BUILD_TIMESTAMP_RFC3339=${{ steps.set_build_info.outputs.BUILD_TIMESTAMP_RFC3339 }}
|
||||
SENTRY_RELEASE=${{ steps.set_build_info.outputs.BUILD_GIT_SHA }}
|
||||
SENTRY_ORG=triggerdev
|
||||
SENTRY_PROJECT=trigger-cloud
|
||||
secrets: |
|
||||
sentry_auth_token=${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
|
||||
- name: 🪪 Attest build provenance
|
||||
# Image is already pushed by this point — don't fail releases (and the
|
||||
# downstream publish-helm job) on a Sigstore/GHCR-referrer hiccup. Real
|
||||
# config errors still surface as a step warning in the workflow run.
|
||||
continue-on-error: true
|
||||
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
|
||||
with:
|
||||
subject-name: ${{ steps.set_tags.outputs.image_repo }}
|
||||
subject-digest: ${{ steps.build_push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
@@ -0,0 +1,103 @@
|
||||
name: "⚒️ Publish Worker (v4)"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
image_tag:
|
||||
description: The image tag to publish
|
||||
type: string
|
||||
required: false
|
||||
default: ""
|
||||
image_registry:
|
||||
description: The registry namespace to publish under (e.g. ghcr.io/<owner>)
|
||||
type: string
|
||||
required: false
|
||||
default: ""
|
||||
push:
|
||||
tags:
|
||||
- "re2-test-*"
|
||||
- "re2-prod-*"
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# check-branch:
|
||||
# runs-on: warp-ubuntu-latest-x64-2x
|
||||
# steps:
|
||||
# - name: Fail if re2-prod-* is pushed from a non-main branch
|
||||
# if: startsWith(github.ref_name, 're2-prod-') && github.base_ref != 'main'
|
||||
# run: |
|
||||
# echo "🚫 re2-prod-* tags can only be pushed from the main branch."
|
||||
# exit 1
|
||||
build:
|
||||
# needs: check-branch
|
||||
strategy:
|
||||
matrix:
|
||||
package: [supervisor]
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
env:
|
||||
DOCKER_BUILDKIT: "1"
|
||||
steps:
|
||||
- name: 🏭 Setup Depot CLI
|
||||
uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1.7.1
|
||||
|
||||
- name: ⬇️ Checkout git repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 📦 Get image repo
|
||||
id: get_repository
|
||||
env:
|
||||
PACKAGE: ${{ matrix.package }}
|
||||
run: |
|
||||
if [[ "$PACKAGE" == *-provider ]]; then
|
||||
repo="provider/${PACKAGE%-provider}"
|
||||
else
|
||||
repo="$PACKAGE"
|
||||
fi
|
||||
echo "repo=${repo}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: "#️⃣ Get image tag"
|
||||
id: get_tag
|
||||
uses: ./.github/actions/get-image-tag
|
||||
with:
|
||||
tag: ${{ inputs.image_tag }}
|
||||
|
||||
- name: 📛 Set tags to push
|
||||
id: set_tags
|
||||
run: |
|
||||
# Resolved by the caller when invoked from publish.yml; falls back to the
|
||||
# IMAGE_REGISTRY repository variable (or ghcr.io/<owner>) for the direct
|
||||
# push triggers above, so a fork publishes to its own namespace.
|
||||
ref_without_tag=${IMAGE_REGISTRY}/${STEPS_GET_REPOSITORY_OUTPUTS_REPO}
|
||||
image_tags=$ref_without_tag:${STEPS_GET_TAG_OUTPUTS_TAG}
|
||||
|
||||
if [[ "${STEPS_GET_TAG_OUTPUTS_TAG}" =~ ^v4\.[0-9]+\.[0-9]+$ ]]; then
|
||||
image_tags=$image_tags,$ref_without_tag:v4,$ref_without_tag:latest
|
||||
fi
|
||||
|
||||
echo "image_tags=${image_tags}" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
IMAGE_REGISTRY: ${{ inputs.image_registry || vars.IMAGE_REGISTRY || format('ghcr.io/{0}', github.repository_owner) }}
|
||||
STEPS_GET_REPOSITORY_OUTPUTS_REPO: ${{ steps.get_repository.outputs.repo }}
|
||||
STEPS_GET_TAG_OUTPUTS_TAG: ${{ steps.get_tag.outputs.tag }}
|
||||
STEPS_GET_TAG_OUTPUTS_IS_SEMVER: ${{ steps.get_tag.outputs.is_semver }}
|
||||
|
||||
- name: 🐙 Login to GitHub Container Registry
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: 🐳 Build image and push to GitHub Container Registry
|
||||
uses: depot/build-push-action@98e78adca7817480b8185f474a400b451d74e287 # v1.18.0
|
||||
with:
|
||||
file: ./apps/${{ matrix.package }}/Containerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: ${{ steps.set_tags.outputs.image_tags }}
|
||||
push: true
|
||||
@@ -0,0 +1,149 @@
|
||||
name: 🚀 Publish Trigger.dev Docker
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
inputs:
|
||||
image_tag:
|
||||
description: The image tag to publish
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME:
|
||||
required: false
|
||||
DOCKERHUB_TOKEN:
|
||||
required: false
|
||||
SENTRY_AUTH_TOKEN:
|
||||
required: false
|
||||
CROSS_REPO_PAT:
|
||||
required: false
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- "v.docker.*"
|
||||
- "build-*"
|
||||
paths:
|
||||
- ".github/actions/**/*.yml"
|
||||
- ".github/workflows/publish.yml"
|
||||
- ".github/workflows/typecheck.yml"
|
||||
- ".github/workflows/unit-tests.yml"
|
||||
- ".github/workflows/e2e.yml"
|
||||
- ".github/workflows/publish-webapp.yml"
|
||||
- "packages/**"
|
||||
- "!packages/**/*.md"
|
||||
- "!packages/**/*.eslintrc"
|
||||
- "internal-packages/**"
|
||||
- "apps/**"
|
||||
- "!apps/**/*.md"
|
||||
- "!apps/**/*.eslintrc"
|
||||
- "pnpm-lock.yaml"
|
||||
- "pnpm-workspace.yaml"
|
||||
- "turbo.json"
|
||||
- "docker/Dockerfile"
|
||||
- "docker/scripts/**"
|
||||
- "tests/**"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
env:
|
||||
AWS_REGION: us-east-1
|
||||
|
||||
jobs:
|
||||
typecheck:
|
||||
uses: ./.github/workflows/typecheck.yml
|
||||
|
||||
units:
|
||||
uses: ./.github/workflows/unit-tests.yml
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
publish-webapp:
|
||||
needs: [typecheck]
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
uses: ./.github/workflows/publish-webapp.yml
|
||||
secrets:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
with:
|
||||
image_tag: ${{ inputs.image_tag }}
|
||||
# Target registry namespace. Defaults to ghcr.io/<owner> so a fork publishes
|
||||
# to its own namespace; set the IMAGE_REGISTRY repository variable to override.
|
||||
image_registry: ${{ vars.IMAGE_REGISTRY || format('ghcr.io/{0}', github.repository_owner) }}
|
||||
|
||||
publish-worker-v4:
|
||||
needs: [typecheck]
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
uses: ./.github/workflows/publish-worker-v4.yml
|
||||
with:
|
||||
image_tag: ${{ inputs.image_tag }}
|
||||
image_registry: ${{ vars.IMAGE_REGISTRY || format('ghcr.io/{0}', github.repository_owner) }}
|
||||
|
||||
# OS-level CVE scan of the image just published above. Report-only (writes to
|
||||
# the run summary); runs alongside the worker publishes and never blocks them.
|
||||
scan-webapp:
|
||||
needs: [publish-webapp]
|
||||
permissions:
|
||||
contents: read
|
||||
packages: read # pull the just-published image from GHCR
|
||||
uses: ./.github/workflows/trivy-image-webapp.yml
|
||||
with:
|
||||
image-ref: ${{ needs.publish-webapp.outputs.image_repo }}:${{ needs.publish-webapp.outputs.version }}
|
||||
|
||||
# Announce the freshly published mutable `main` webapp image to subscriber
|
||||
# repos via repository_dispatch, handing them a digest-pinned ref to build or
|
||||
# deploy from. The repo, ref prefix, and dispatch target all default to the
|
||||
# canonical values and can be overridden by repository variables.
|
||||
#
|
||||
# `push` only: release builds reach publish.yml via workflow_call (from
|
||||
# release.yml) with an explicit image_tag while github.ref_name is still
|
||||
# `main`, so gate on the event to avoid dispatching — and failing on the
|
||||
# absent CROSS_REPO_PAT — during a release.
|
||||
dispatch-main-image:
|
||||
name: 📣 Dispatch main image
|
||||
needs: [publish-webapp]
|
||||
if: github.repository == (vars.MAIN_IMAGE_DISPATCH_REPO || 'triggerdotdev/trigger.dev') && github.event_name == 'push' && startsWith(github.ref_name, vars.MAIN_IMAGE_DISPATCH_REF_PREFIX || 'main')
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
permissions: {}
|
||||
steps:
|
||||
- name: Build dispatch payload
|
||||
id: payload
|
||||
env:
|
||||
IMAGE_REPO: ${{ needs.publish-webapp.outputs.image_repo }}
|
||||
DIGEST: ${{ needs.publish-webapp.outputs.digest }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Pin to the exact multi-arch index just pushed so subscribers resolve a
|
||||
# single immutable artifact rather than chasing the moving `main` tag.
|
||||
if [[ -z "${DIGEST}" ]]; then
|
||||
echo "::error::publish-webapp produced no image digest; refusing to dispatch"
|
||||
exit 1
|
||||
fi
|
||||
image="${IMAGE_REPO}@${DIGEST}"
|
||||
# jq --arg JSON-escapes every value, so the ref/commit can't break out of
|
||||
# or inject into the client payload.
|
||||
payload=$(jq -nc \
|
||||
--arg img "$image" \
|
||||
--arg c "$COMMIT" \
|
||||
'{image: $img, commit: $c}')
|
||||
echo "client_payload=$payload" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Send repository_dispatch
|
||||
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
|
||||
with:
|
||||
token: ${{ secrets.CROSS_REPO_PAT }}
|
||||
repository: ${{ vars.MAIN_IMAGE_DISPATCH_TARGET || 'triggerdotdev/cloud' }}
|
||||
event-type: main-image-published
|
||||
client-payload: ${{ steps.payload.outputs.client_payload }}
|
||||
@@ -0,0 +1,163 @@
|
||||
name: 🧭 Helm Chart Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "helm-v*"
|
||||
workflow_call:
|
||||
inputs:
|
||||
chart_version:
|
||||
description: "Chart version to release"
|
||||
required: true
|
||||
type: string
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
chart_version:
|
||||
description: "Chart version to release"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
CHART_NAME: trigger
|
||||
|
||||
jobs:
|
||||
lint-and-test:
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
|
||||
with:
|
||||
version: "3.18.3"
|
||||
|
||||
- name: Build dependencies
|
||||
run: helm dependency build ./hosting/k8s/helm/
|
||||
|
||||
- name: Extract dependency charts
|
||||
run: |
|
||||
cd ./hosting/k8s/helm/
|
||||
for file in ./charts/*.tgz; do echo "Extracting $file"; tar -xzf "$file" -C ./charts; done
|
||||
|
||||
- name: Lint Helm Chart
|
||||
run: |
|
||||
helm lint ./hosting/k8s/helm/
|
||||
|
||||
- name: Render templates
|
||||
run: |
|
||||
helm template test-release ./hosting/k8s/helm/ \
|
||||
--values ./hosting/k8s/helm/values.yaml \
|
||||
--output-dir ./helm-output
|
||||
|
||||
- name: Validate manifests
|
||||
uses: docker://ghcr.io/yannh/kubeconform:v0.7.0@sha256:85dbef6b4b312b99133decc9c6fc9495e9fc5f92293d4ff3b7e1b30f5611823c
|
||||
with:
|
||||
entrypoint: "/kubeconform"
|
||||
args: "-summary -output json ./helm-output"
|
||||
|
||||
release:
|
||||
needs: lint-and-test
|
||||
# Set the ENABLE_HELM_PRERELEASE repository variable to 'false' to turn off
|
||||
# publishing the chart to GHCR — e.g. forks/mirrors that lack write_package
|
||||
# on the owner's charts namespace. Defaults to enabled; the lint-and-test
|
||||
# job above always runs regardless.
|
||||
if: ${{ vars.ENABLE_HELM_PRERELEASE != 'false' }}
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
permissions:
|
||||
contents: write # for gh-release
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
|
||||
with:
|
||||
version: "3.18.3"
|
||||
|
||||
- name: Build dependencies
|
||||
run: helm dependency build ./hosting/k8s/helm/
|
||||
|
||||
- name: Extract dependency charts
|
||||
run: |
|
||||
cd ./hosting/k8s/helm/
|
||||
for file in ./charts/*.tgz; do echo "Extracting $file"; tar -xzf "$file" -C ./charts; done
|
||||
|
||||
- name: Log in to Container Registry
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract version from tag or input
|
||||
id: version
|
||||
run: |
|
||||
if [ -n "${INPUTS_CHART_VERSION}" ]; then
|
||||
VERSION="${INPUTS_CHART_VERSION}"
|
||||
else
|
||||
VERSION="${GITHUB_REF_NAME}"
|
||||
VERSION="${VERSION#helm-v}"
|
||||
fi
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "Releasing version: $VERSION"
|
||||
env:
|
||||
INPUTS_CHART_VERSION: ${{ inputs.chart_version }}
|
||||
|
||||
- name: Check Chart.yaml version matches release version
|
||||
run: |
|
||||
VERSION="${STEPS_VERSION_OUTPUTS_VERSION}"
|
||||
CHART_VERSION=$(grep '^version:' ./hosting/k8s/helm/Chart.yaml | awk '{print $2}')
|
||||
echo "Chart.yaml version: $CHART_VERSION"
|
||||
echo "Release version: $VERSION"
|
||||
if [ "$CHART_VERSION" != "$VERSION" ]; then
|
||||
echo "❌ Chart.yaml version does not match release version!"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Chart.yaml version matches release version."
|
||||
env:
|
||||
STEPS_VERSION_OUTPUTS_VERSION: ${{ steps.version.outputs.version }}
|
||||
|
||||
- name: Package Helm Chart
|
||||
run: |
|
||||
helm package ./hosting/k8s/helm/ --destination /tmp/
|
||||
|
||||
- name: Push Helm Chart to GHCR
|
||||
run: |
|
||||
VERSION="${STEPS_VERSION_OUTPUTS_VERSION}"
|
||||
CHART_PACKAGE="/tmp/${{ env.CHART_NAME }}-${VERSION}.tgz"
|
||||
|
||||
# Push to GHCR OCI registry
|
||||
helm push "$CHART_PACKAGE" "oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/charts"
|
||||
env:
|
||||
STEPS_VERSION_OUTPUTS_VERSION: ${{ steps.version.outputs.version }}
|
||||
|
||||
- name: Create GitHub Release
|
||||
id: release
|
||||
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
|
||||
with:
|
||||
tag_name: helm-v${{ steps.version.outputs.version }}
|
||||
name: "Helm Chart ${{ steps.version.outputs.version }}"
|
||||
body: |
|
||||
### Installation
|
||||
```bash
|
||||
helm upgrade --install trigger \
|
||||
oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/charts/${{ env.CHART_NAME }} \
|
||||
--version "${{ steps.version.outputs.version }}"
|
||||
```
|
||||
|
||||
### Changes
|
||||
See commit history for detailed changes in this release.
|
||||
files: |
|
||||
/tmp/${{ env.CHART_NAME }}-${{ steps.version.outputs.version }}.tgz
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
draft: true
|
||||
prerelease: true
|
||||
@@ -0,0 +1,343 @@
|
||||
name: 🦋 Changesets Release
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
type:
|
||||
description: "Select release type"
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- release
|
||||
- prerelease
|
||||
default: "prerelease"
|
||||
ref:
|
||||
description: "The ref (branch, tag, or SHA) to checkout and release from"
|
||||
required: true
|
||||
type: string
|
||||
prerelease_tag:
|
||||
description: "The npm dist-tag for the prerelease (e.g., 'v4-prerelease')"
|
||||
required: false
|
||||
type: string
|
||||
default: "prerelease"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
show-release-summary:
|
||||
name: 📋 Release Summary
|
||||
runs-on: ubuntu-latest
|
||||
permissions: {}
|
||||
if: |
|
||||
github.repository == 'triggerdotdev/trigger.dev' &&
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.merged == true &&
|
||||
github.event.pull_request.head.ref == 'changeset-release/main'
|
||||
steps:
|
||||
- name: Show release summary
|
||||
env:
|
||||
PR_BODY: ${{ github.event.pull_request.body }}
|
||||
run: |
|
||||
echo "$PR_BODY" | sed -n '/^# Releases/,$p' >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
release:
|
||||
name: 🚀 Release npm packages
|
||||
runs-on: ubuntu-latest
|
||||
environment: npm-publish
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
id-token: write
|
||||
if: |
|
||||
github.repository == 'triggerdotdev/trigger.dev' &&
|
||||
(
|
||||
(github.event_name == 'workflow_dispatch' && github.event.inputs.type == 'release') ||
|
||||
(github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.head.ref == 'changeset-release/main')
|
||||
)
|
||||
outputs:
|
||||
published: ${{ steps.changesets.outputs.published }}
|
||||
published_packages: ${{ steps.changesets.outputs.publishedPackages }}
|
||||
published_package_version: ${{ steps.get_version.outputs.package_version }}
|
||||
is_prerelease: ${{ steps.get_version.outputs.is_prerelease }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 # zizmor: ignore[artipacked] needs persisted git creds for tag push; no artifact upload here so no leak path
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.sha }}
|
||||
|
||||
- name: Verify ref is on main
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
if ! git merge-base --is-ancestor "${GITHUB_EVENT_INPUTS_REF}" origin/main; then
|
||||
echo "Error: ref must be an ancestor of main (i.e., already merged)"
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
GITHUB_EVENT_INPUTS_REF: ${{ github.event.inputs.ref }}
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
# npm v11.5.1 or newer is required for OIDC support
|
||||
# https://github.blog/changelog/2025-07-31-npm-trusted-publishing-with-oidc-is-generally-available/#whats-new
|
||||
- name: Setup npm 11.x for OIDC
|
||||
run: npm install -g npm@11.6.4
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Generate Prisma client
|
||||
run: pnpm run generate
|
||||
|
||||
- name: Build
|
||||
run: pnpm run build --filter "@trigger.dev/*" --filter "trigger.dev"
|
||||
|
||||
- name: Type check
|
||||
run: pnpm run typecheck --filter "@trigger.dev/*" --filter "trigger.dev"
|
||||
|
||||
- name: Publish
|
||||
id: changesets
|
||||
uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1.9.0
|
||||
with:
|
||||
publish: pnpm run changeset:release
|
||||
createGithubReleases: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Show package version
|
||||
if: steps.changesets.outputs.published == 'true'
|
||||
id: get_version
|
||||
run: |
|
||||
package_version=$(echo "${STEPS_CHANGESETS_OUTPUTS_PUBLISHEDPACKAGES}" | jq -r '.[0].version')
|
||||
echo "package_version=${package_version}" >> "$GITHUB_OUTPUT"
|
||||
# Any semver with a hyphen is a prerelease (e.g. 4.5.0-rc.0, 0.0.0-snapshot-...)
|
||||
if [[ "${package_version}" == *-* ]]; then
|
||||
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
env:
|
||||
STEPS_CHANGESETS_OUTPUTS_PUBLISHEDPACKAGES: ${{ steps.changesets.outputs.publishedPackages }}
|
||||
|
||||
- name: Create unified GitHub release
|
||||
if: steps.changesets.outputs.published == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RELEASE_PR_BODY: ${{ github.event.pull_request.body }}
|
||||
STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION: ${{ steps.get_version.outputs.package_version }}
|
||||
STEPS_GET_VERSION_OUTPUTS_IS_PRERELEASE: ${{ steps.get_version.outputs.is_prerelease }}
|
||||
run: |
|
||||
VERSION="${STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION}"
|
||||
|
||||
# On the pull_request merge event RELEASE_PR_BODY is the merged "Version Packages"
|
||||
# PR body, which holds the changelog. On a workflow_dispatch re-run (the recovery
|
||||
# path after a failed merge-triggered run) there is no pull_request context, so it's
|
||||
# empty. Fall back to the body of the most recently merged changeset-release/main PR
|
||||
# so the "What's changed" section is still populated.
|
||||
if [ -z "${RELEASE_PR_BODY}" ]; then
|
||||
echo "RELEASE_PR_BODY is empty (workflow_dispatch re-run); fetching merged changeset-release/main PR body"
|
||||
RELEASE_PR_BODY="$(gh pr list --repo triggerdotdev/trigger.dev \
|
||||
--head changeset-release/main --state merged \
|
||||
--json body,mergedAt --limit 10 \
|
||||
-q 'sort_by(.mergedAt) | reverse | .[0].body')"
|
||||
fi
|
||||
export RELEASE_PR_BODY
|
||||
|
||||
node scripts/generate-github-release.mjs "$VERSION" > /tmp/release-body.md
|
||||
PRERELEASE_FLAG=""
|
||||
if [ "${STEPS_GET_VERSION_OUTPUTS_IS_PRERELEASE}" = "true" ]; then
|
||||
PRERELEASE_FLAG="--prerelease"
|
||||
fi
|
||||
gh release create "v${VERSION}" \
|
||||
--title "trigger.dev v${VERSION}" \
|
||||
--notes-file /tmp/release-body.md \
|
||||
--target main \
|
||||
$PRERELEASE_FLAG
|
||||
|
||||
- name: Create and push Docker tag
|
||||
if: steps.changesets.outputs.published == 'true'
|
||||
run: |
|
||||
set -e
|
||||
git tag "v.docker.${STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION}"
|
||||
git push origin "v.docker.${STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION}"
|
||||
env:
|
||||
STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION: ${{ steps.get_version.outputs.package_version }}
|
||||
|
||||
- name: Create and push Helm chart tag
|
||||
if: steps.changesets.outputs.published == 'true'
|
||||
run: |
|
||||
set -e
|
||||
git tag "helm-v${STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION}"
|
||||
git push origin "helm-v${STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION}"
|
||||
env:
|
||||
STEPS_GET_VERSION_OUTPUTS_PACKAGE_VERSION: ${{ steps.get_version.outputs.package_version }}
|
||||
|
||||
# Trigger Docker builds directly via workflow_call since tags pushed with
|
||||
# GITHUB_TOKEN don't trigger other workflows (GitHub Actions limitation).
|
||||
publish-docker:
|
||||
name: 🐳 Publish Docker images
|
||||
needs: release
|
||||
if: needs.release.outputs.published == 'true'
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
uses: ./.github/workflows/publish.yml
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
with:
|
||||
image_tag: v${{ needs.release.outputs.published_package_version }}
|
||||
|
||||
# Trigger Helm chart release directly via workflow_call (same GITHUB_TOKEN
|
||||
# limitation as the Docker path). Runs after Docker images are published so
|
||||
# the chart never references images that don't exist yet.
|
||||
publish-helm:
|
||||
name: 🧭 Publish Helm chart
|
||||
needs: [release, publish-docker]
|
||||
if: needs.release.outputs.published == 'true'
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
uses: ./.github/workflows/release-helm.yml
|
||||
with:
|
||||
chart_version: ${{ needs.release.outputs.published_package_version }}
|
||||
|
||||
# After Docker images are published, update the GitHub release with the exact GHCR tag URL.
|
||||
# The GHCR package version ID is only known after the image is pushed, so we query for it here.
|
||||
update-release:
|
||||
name: 🔗 Update release Docker link
|
||||
needs: [release, publish-docker]
|
||||
if: needs.release.outputs.published == 'true'
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
permissions:
|
||||
contents: write
|
||||
packages: read
|
||||
steps:
|
||||
- name: Update GitHub release with Docker image link
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NEEDS_RELEASE_OUTPUTS_PUBLISHED_PACKAGE_VERSION: ${{ needs.release.outputs.published_package_version }}
|
||||
run: |
|
||||
set -e
|
||||
VERSION="${NEEDS_RELEASE_OUTPUTS_PUBLISHED_PACKAGE_VERSION}"
|
||||
TAG="v${VERSION}"
|
||||
|
||||
# Query GHCR for the version ID matching this tag
|
||||
VERSION_ID=$(gh api --paginate -H "Accept: application/vnd.github+json" \
|
||||
/orgs/triggerdotdev/packages/container/trigger.dev/versions \
|
||||
--jq ".[] | select(.metadata.container.tags[] == \"${TAG}\") | .id" \
|
||||
| head -1)
|
||||
|
||||
if [ -z "$VERSION_ID" ]; then
|
||||
echo "Warning: Could not find GHCR version ID for tag ${TAG}, skipping update"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
DOCKER_URL="https://github.com/triggerdotdev/trigger.dev/pkgs/container/trigger.dev/${VERSION_ID}?tag=${TAG}"
|
||||
GENERIC_URL="https://github.com/triggerdotdev/trigger.dev/pkgs/container/trigger.dev"
|
||||
|
||||
# Get current release body and replace the generic link with the tag-specific one.
|
||||
# Use word boundary after GENERIC_URL (closing paren) to avoid matching URLs that
|
||||
# already have a version ID appended (idempotent on re-runs).
|
||||
gh release view "${TAG}" --repo triggerdotdev/trigger.dev --json body --jq '.body' > /tmp/release-body.md
|
||||
sed -i "s|${GENERIC_URL})|${DOCKER_URL})|g" /tmp/release-body.md
|
||||
|
||||
gh release edit "${TAG}" --repo triggerdotdev/trigger.dev --notes-file /tmp/release-body.md
|
||||
|
||||
# Dispatch changelog entry creation to the marketing site repo.
|
||||
# Runs after update-release so the GitHub release body already has the exact Docker image URL.
|
||||
dispatch-changelog:
|
||||
name: 📝 Dispatch changelog PR
|
||||
needs: [release, update-release]
|
||||
if: needs.release.outputs.published == 'true' && needs.release.outputs.is_prerelease != 'true'
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
permissions: {}
|
||||
steps:
|
||||
- uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
|
||||
with:
|
||||
token: ${{ secrets.CROSS_REPO_PAT }}
|
||||
repository: triggerdotdev/trigger.dev-site-v3
|
||||
event-type: new-release
|
||||
client-payload: '{"version": "${{ needs.release.outputs.published_package_version }}"}'
|
||||
|
||||
# The prerelease job needs to be on the same workflow file due to a limitation related to how npm verifies OIDC claims.
|
||||
prerelease:
|
||||
name: 🧪 Prerelease
|
||||
runs-on: ubuntu-latest
|
||||
environment: npm-publish
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
if: github.repository == 'triggerdotdev/trigger.dev' && github.event_name == 'workflow_dispatch' && github.event.inputs.type == 'prerelease'
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.inputs.ref }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
# npm v11.5.1 or newer is required for OIDC support
|
||||
# https://github.blog/changelog/2025-07-31-npm-trusted-publishing-with-oidc-is-generally-available/#whats-new
|
||||
- name: Setup npm 11.x for OIDC
|
||||
run: npm install -g npm@11.6.4
|
||||
|
||||
- name: Download deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Generate Prisma Client
|
||||
run: pnpm run generate
|
||||
|
||||
- name: Exit changeset pre mode (if active)
|
||||
run: |
|
||||
if [ -f .changeset/pre.json ]; then
|
||||
echo "Repo is in changeset pre mode; exiting so snapshot release can run"
|
||||
pnpm exec changeset pre exit
|
||||
fi
|
||||
|
||||
- name: Snapshot version
|
||||
run: pnpm exec changeset version --snapshot "${GITHUB_EVENT_INPUTS_PRERELEASE_TAG}"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_EVENT_INPUTS_PRERELEASE_TAG: ${{ github.event.inputs.prerelease_tag }}
|
||||
|
||||
- name: Clean
|
||||
run: pnpm run clean --filter "@trigger.dev/*" --filter "trigger.dev"
|
||||
|
||||
- name: Build
|
||||
run: pnpm run build --filter "@trigger.dev/*" --filter "trigger.dev"
|
||||
|
||||
- name: Publish prerelease
|
||||
run: pnpm exec changeset publish --no-git-tag --snapshot --tag "${GITHUB_EVENT_INPUTS_PRERELEASE_TAG}"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_EVENT_INPUTS_PRERELEASE_TAG: ${{ github.event.inputs.prerelease_tag }}
|
||||
@@ -0,0 +1,182 @@
|
||||
name: "🔌 SDK Compatibility Tests"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
node-compat:
|
||||
name: "Node.js ${{ matrix.node }} (${{ matrix.os }})"
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [warp-ubuntu-latest-x64-4x]
|
||||
node: ["20.20", "22.23", "24.18", "26.4"]
|
||||
|
||||
steps:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: "pnpm"
|
||||
|
||||
- name: 📥 Download deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: 📀 Generate Prisma Client
|
||||
run: pnpm run generate
|
||||
|
||||
- name: 🔨 Build SDK dependencies
|
||||
shell: bash
|
||||
run: pnpm run build --filter '@trigger.dev/sdk^...'
|
||||
|
||||
- name: 🔨 Build SDK
|
||||
shell: bash
|
||||
run: pnpm run build --filter '@trigger.dev/sdk'
|
||||
|
||||
- name: 🧪 Run SDK Compatibility Tests
|
||||
shell: bash
|
||||
run: pnpm --filter @internal/sdk-compat-tests test
|
||||
|
||||
bun-compat:
|
||||
name: "Bun Runtime"
|
||||
runs-on: warp-ubuntu-latest-x64-4x
|
||||
steps:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
- name: 🥟 Setup Bun
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: 📥 Download deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: 📀 Generate Prisma Client
|
||||
run: pnpm run generate
|
||||
|
||||
- name: 🔨 Build SDK dependencies
|
||||
run: pnpm run build --filter @trigger.dev/sdk^...
|
||||
|
||||
- name: 🔨 Build SDK
|
||||
run: pnpm run build --filter @trigger.dev/sdk
|
||||
|
||||
- name: 🧪 Run Bun Compatibility Test
|
||||
working-directory: internal-packages/sdk-compat-tests/src/fixtures/bun
|
||||
run: bun run test.ts
|
||||
|
||||
deno-compat:
|
||||
name: "Deno Runtime"
|
||||
runs-on: warp-ubuntu-latest-x64-4x
|
||||
steps:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
- name: 🦕 Setup Deno
|
||||
uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4
|
||||
with:
|
||||
deno-version: v2.x
|
||||
|
||||
- name: 📥 Download deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: 📀 Generate Prisma Client
|
||||
run: pnpm run generate
|
||||
|
||||
- name: 🔨 Build SDK dependencies
|
||||
run: pnpm run build --filter @trigger.dev/sdk^...
|
||||
|
||||
- name: 🔨 Build SDK
|
||||
run: pnpm run build --filter @trigger.dev/sdk
|
||||
|
||||
- name: 🔗 Link node_modules for Deno fixture
|
||||
working-directory: internal-packages/sdk-compat-tests/src/fixtures/deno
|
||||
run: ln -s ../../../../../node_modules node_modules
|
||||
|
||||
- name: 🧪 Run Deno Compatibility Test
|
||||
working-directory: internal-packages/sdk-compat-tests/src/fixtures/deno
|
||||
run: deno run --allow-read --allow-env --allow-sys test.ts
|
||||
|
||||
cloudflare-compat:
|
||||
name: "Cloudflare Workers"
|
||||
runs-on: warp-ubuntu-latest-x64-4x
|
||||
steps:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
- name: 📥 Download deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: 📀 Generate Prisma Client
|
||||
run: pnpm run generate
|
||||
|
||||
- name: 🔨 Build SDK dependencies
|
||||
run: pnpm run build --filter @trigger.dev/sdk^...
|
||||
|
||||
- name: 🔨 Build SDK
|
||||
run: pnpm run build --filter @trigger.dev/sdk
|
||||
|
||||
- name: 📥 Install Cloudflare fixture deps
|
||||
working-directory: internal-packages/sdk-compat-tests/src/fixtures/cloudflare-worker
|
||||
run: pnpm install
|
||||
|
||||
- name: 🧪 Run Cloudflare Workers Compatibility Test (dry-run)
|
||||
working-directory: internal-packages/sdk-compat-tests/src/fixtures/cloudflare-worker
|
||||
run: npx wrangler deploy --dry-run --outdir dist
|
||||
@@ -0,0 +1,75 @@
|
||||
name: Trivy Image Scan (webapp)
|
||||
|
||||
# OS-level CVE scan of a published webapp image. Called by the publish pipeline
|
||||
# (publish.yml) to scan each build right after it's pushed to GHCR — so every
|
||||
# main build and every release is scanned, not rebuilt. Also runnable ad-hoc
|
||||
# via workflow_dispatch against any image ref.
|
||||
#
|
||||
# Report-only: writes a table to the run summary. No SARIF upload, no gate.
|
||||
# Library/dependency CVEs are covered by Dependabot, so this is restricted to
|
||||
# OS packages (`vuln-type: os`) to avoid double-reporting.
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
image-ref:
|
||||
description: "Full image ref to scan (e.g. ghcr.io/triggerdotdev/trigger.dev:main)"
|
||||
type: string
|
||||
required: true
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
image-ref:
|
||||
description: "Full image ref to scan"
|
||||
type: string
|
||||
required: false
|
||||
default: "ghcr.io/triggerdotdev/trigger.dev:main"
|
||||
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: trivy-image-webapp-${{ inputs.image-ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
scan:
|
||||
name: Scan
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
permissions:
|
||||
contents: read
|
||||
packages: read # pull the image from GHCR
|
||||
steps:
|
||||
# Authenticate to GHCR so the scan also works for private images
|
||||
# (GITHUB_TOKEN isn't forwarded to Docker automatically). Harmless for
|
||||
# public images. Pairs with the packages: read permission above.
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run Trivy image scan
|
||||
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
||||
with:
|
||||
scan-type: image
|
||||
image-ref: ${{ inputs.image-ref }}
|
||||
# vuln-type maps to --pkg-types: OS packages only (library deps are
|
||||
# Dependabot's job). ignore-unfixed drops vulns with no patch yet.
|
||||
vuln-type: os
|
||||
ignore-unfixed: true
|
||||
severity: HIGH,CRITICAL
|
||||
format: table
|
||||
output: trivy-image-webapp.txt
|
||||
|
||||
- name: Job summary
|
||||
if: always()
|
||||
env:
|
||||
IMAGE_REF: ${{ inputs.image-ref }}
|
||||
run: |
|
||||
{
|
||||
echo "## Trivy Image Scan (webapp) — \`${IMAGE_REF}\`"
|
||||
echo '```'
|
||||
# GitHub step summary is capped at 1 MiB; truncate large reports.
|
||||
head -c 900000 trivy-image-webapp.txt 2>/dev/null || echo "(no report produced)"
|
||||
echo '```'
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
@@ -0,0 +1,43 @@
|
||||
name: "ʦ TypeScript"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
typecheck:
|
||||
runs-on: warp-ubuntu-latest-x64-8x
|
||||
|
||||
steps:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
- name: 📥 Download deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: 📀 Generate Prisma Client
|
||||
run: pnpm run generate
|
||||
|
||||
- name: 🔎 Type check
|
||||
run: pnpm run typecheck
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
|
||||
- name: 🔎 Check exports
|
||||
run: pnpm run check-exports
|
||||
@@ -0,0 +1,161 @@
|
||||
name: "🧪 Unit Tests: Internal"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME:
|
||||
required: false
|
||||
DOCKERHUB_TOKEN:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
unitTests:
|
||||
name: "🧪 Unit Tests: Internal"
|
||||
runs-on: warp-ubuntu-latest-x64-8x
|
||||
strategy:
|
||||
# one flaky shard shouldn't cancel its siblings - lets us re-run only the failed shard
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
||||
shardTotal: [12]
|
||||
env:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
SHARD_INDEX: ${{ matrix.shardIndex }}
|
||||
SHARD_TOTAL: ${{ matrix.shardTotal }}
|
||||
steps:
|
||||
- name: 🔧 Disable IPv6
|
||||
run: |
|
||||
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
|
||||
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
|
||||
sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1
|
||||
|
||||
- name: 🔧 Configure docker address pool
|
||||
run: |
|
||||
CONFIG='{
|
||||
"default-address-pools" : [
|
||||
{
|
||||
"base" : "172.17.0.0/12",
|
||||
"size" : 20
|
||||
},
|
||||
{
|
||||
"base" : "192.168.0.0/16",
|
||||
"size" : 24
|
||||
}
|
||||
]
|
||||
}'
|
||||
mkdir -p /etc/docker
|
||||
echo "$CONFIG" | sudo tee /etc/docker/daemon.json
|
||||
|
||||
- name: 🔧 Restart docker daemon
|
||||
run: sudo systemctl restart docker
|
||||
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
# ..to avoid rate limits when pulling images
|
||||
- name: 🐳 Login to DockerHub
|
||||
if: ${{ env.DOCKERHUB_USERNAME }}
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: 🐳 Skipping DockerHub login (no secrets available)
|
||||
if: ${{ !env.DOCKERHUB_USERNAME }}
|
||||
run: echo "DockerHub login skipped because secrets are not available."
|
||||
|
||||
- name: 🐳 Pre-pull testcontainer images
|
||||
if: ${{ env.DOCKERHUB_USERNAME }}
|
||||
run: |
|
||||
# Retry each pull - DockerHub registry timeouts are a recurring transient CI flake.
|
||||
pull() {
|
||||
for attempt in 1 2 3; do
|
||||
docker pull "$1" && return 0
|
||||
echo "::warning::docker pull $1 failed (attempt ${attempt}/3); retrying in 10s"
|
||||
sleep 10
|
||||
done
|
||||
echo "::error::docker pull $1 failed after 3 attempts"
|
||||
return 1
|
||||
}
|
||||
echo "Pre-pulling Docker images with authenticated session..."
|
||||
pull postgres:14
|
||||
pull postgres:17 # for the heterogeneous PG14/17 test fixture gate (heteroPostgresTest)
|
||||
pull clickhouse/clickhouse-server:26.2.19.43-alpine@sha256:c6ad6a7eb2fb5999df3adfb8b69a0c7222c68fa9b8f6b04a088564ebbc959251
|
||||
pull redis:7.2
|
||||
pull testcontainers/ryuk:0.14.0
|
||||
pull electricsql/electric:1.2.4
|
||||
echo "Image pre-pull complete"
|
||||
|
||||
- name: 📥 Download deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: 📀 Generate Prisma Client
|
||||
run: pnpm run generate
|
||||
|
||||
- name: 🧪 Run Internal Unit Tests
|
||||
run: pnpm run test:internal --reporter=default --reporter=blob --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --passWithNoTests
|
||||
|
||||
- name: Gather all reports
|
||||
if: ${{ !cancelled() }}
|
||||
run: |
|
||||
mkdir -p .vitest-reports
|
||||
find . -type f -path '*/.vitest-reports/blob-*.json' \
|
||||
-exec bash -c 'src="$1"; basename=$(basename "$src"); pkg=$(dirname "$src" | sed "s|^\./||;s|/\.vitest-reports$||;s|/|_|g"); cp "$src" ".vitest-reports/${pkg}-${basename}"' _ {} \;
|
||||
|
||||
- name: Upload blob reports to GitHub Actions Artifacts
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: internal-blob-report-${{ matrix.shardIndex }}
|
||||
path: .vitest-reports/*
|
||||
include-hidden-files: true
|
||||
retention-days: 1
|
||||
|
||||
merge-reports:
|
||||
name: "📊 Merge Reports"
|
||||
if: ${{ !cancelled() }}
|
||||
needs: [unitTests]
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
steps:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
# no cache enabled, we're not installing deps
|
||||
|
||||
- name: Download blob reports from GitHub Actions Artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
path: .vitest-reports
|
||||
pattern: internal-blob-report-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Merge reports
|
||||
run: pnpm dlx vitest@4.1.7 run --merge-reports --pass-with-no-tests
|
||||
@@ -0,0 +1,160 @@
|
||||
name: "🧪 Unit Tests: Packages"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME:
|
||||
required: false
|
||||
DOCKERHUB_TOKEN:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
unitTests:
|
||||
name: "🧪 Unit Tests: Packages"
|
||||
runs-on: warp-ubuntu-latest-x64-4x
|
||||
strategy:
|
||||
# one flaky shard shouldn't cancel its siblings - lets us re-run only the failed shard
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shardIndex: [1, 2, 3]
|
||||
shardTotal: [3]
|
||||
env:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
SHARD_INDEX: ${{ matrix.shardIndex }}
|
||||
SHARD_TOTAL: ${{ matrix.shardTotal }}
|
||||
steps:
|
||||
- name: 🔧 Disable IPv6
|
||||
run: |
|
||||
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
|
||||
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
|
||||
sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1
|
||||
|
||||
- name: 🔧 Configure docker address pool
|
||||
run: |
|
||||
CONFIG='{
|
||||
"default-address-pools" : [
|
||||
{
|
||||
"base" : "172.17.0.0/12",
|
||||
"size" : 20
|
||||
},
|
||||
{
|
||||
"base" : "192.168.0.0/16",
|
||||
"size" : 24
|
||||
}
|
||||
]
|
||||
}'
|
||||
mkdir -p /etc/docker
|
||||
echo "$CONFIG" | sudo tee /etc/docker/daemon.json
|
||||
|
||||
- name: 🔧 Restart docker daemon
|
||||
run: sudo systemctl restart docker
|
||||
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
# ..to avoid rate limits when pulling images
|
||||
- name: 🐳 Login to DockerHub
|
||||
if: ${{ env.DOCKERHUB_USERNAME }}
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: 🐳 Skipping DockerHub login (no secrets available)
|
||||
if: ${{ !env.DOCKERHUB_USERNAME }}
|
||||
run: echo "DockerHub login skipped because secrets are not available."
|
||||
|
||||
- name: 🐳 Pre-pull testcontainer images
|
||||
if: ${{ env.DOCKERHUB_USERNAME }}
|
||||
run: |
|
||||
# Retry each pull - DockerHub registry timeouts are a recurring transient CI flake.
|
||||
pull() {
|
||||
for attempt in 1 2 3; do
|
||||
docker pull "$1" && return 0
|
||||
echo "::warning::docker pull $1 failed (attempt ${attempt}/3); retrying in 10s"
|
||||
sleep 10
|
||||
done
|
||||
echo "::error::docker pull $1 failed after 3 attempts"
|
||||
return 1
|
||||
}
|
||||
echo "Pre-pulling Docker images with authenticated session..."
|
||||
pull postgres:14
|
||||
pull clickhouse/clickhouse-server:26.2.19.43-alpine@sha256:c6ad6a7eb2fb5999df3adfb8b69a0c7222c68fa9b8f6b04a088564ebbc959251
|
||||
pull redis:7.2
|
||||
pull testcontainers/ryuk:0.14.0
|
||||
pull electricsql/electric:1.2.4
|
||||
echo "Image pre-pull complete"
|
||||
|
||||
- name: 📥 Download deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: 📀 Generate Prisma Client
|
||||
run: pnpm run generate
|
||||
|
||||
- name: 🧪 Run Package Unit Tests
|
||||
run: pnpm run test:packages --reporter=default --reporter=blob --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --passWithNoTests
|
||||
|
||||
- name: Gather all reports
|
||||
if: ${{ !cancelled() }}
|
||||
run: |
|
||||
mkdir -p .vitest-reports
|
||||
find . -type f -path '*/.vitest-reports/blob-*.json' \
|
||||
-exec bash -c 'src="$1"; basename=$(basename "$src"); pkg=$(dirname "$src" | sed "s|^\./||;s|/\.vitest-reports$||;s|/|_|g"); cp "$src" ".vitest-reports/${pkg}-${basename}"' _ {} \;
|
||||
|
||||
- name: Upload blob reports to GitHub Actions Artifacts
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: packages-blob-report-${{ matrix.shardIndex }}
|
||||
path: .vitest-reports/*
|
||||
include-hidden-files: true
|
||||
retention-days: 1
|
||||
|
||||
merge-reports:
|
||||
name: "📊 Merge Reports"
|
||||
if: ${{ !cancelled() }}
|
||||
needs: [unitTests]
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
steps:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
# no cache enabled, we're not installing deps
|
||||
|
||||
- name: Download blob reports from GitHub Actions Artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
path: .vitest-reports
|
||||
pattern: packages-blob-report-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Merge reports
|
||||
run: pnpm dlx vitest@4.1.7 run --merge-reports --pass-with-no-tests
|
||||
@@ -0,0 +1,169 @@
|
||||
name: "🧪 Unit Tests: Webapp"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME:
|
||||
required: false
|
||||
DOCKERHUB_TOKEN:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
unitTests:
|
||||
name: "🧪 Unit Tests: Webapp"
|
||||
runs-on: warp-ubuntu-latest-x64-8x
|
||||
strategy:
|
||||
# one flaky shard shouldn't cancel its siblings - lets us re-run only the failed shard
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
shardTotal: [10]
|
||||
env:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
SHARD_INDEX: ${{ matrix.shardIndex }}
|
||||
SHARD_TOTAL: ${{ matrix.shardTotal }}
|
||||
steps:
|
||||
- name: 🔧 Disable IPv6
|
||||
run: |
|
||||
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
|
||||
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
|
||||
sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1
|
||||
|
||||
- name: 🔧 Configure docker address pool
|
||||
run: |
|
||||
CONFIG='{
|
||||
"default-address-pools" : [
|
||||
{
|
||||
"base" : "172.17.0.0/12",
|
||||
"size" : 20
|
||||
},
|
||||
{
|
||||
"base" : "192.168.0.0/16",
|
||||
"size" : 24
|
||||
}
|
||||
]
|
||||
}'
|
||||
mkdir -p /etc/docker
|
||||
echo "$CONFIG" | sudo tee /etc/docker/daemon.json
|
||||
|
||||
- name: 🔧 Restart docker daemon
|
||||
run: sudo systemctl restart docker
|
||||
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
cache: "pnpm"
|
||||
|
||||
# ..to avoid rate limits when pulling images
|
||||
- name: 🐳 Login to DockerHub
|
||||
if: ${{ env.DOCKERHUB_USERNAME }}
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: 🐳 Skipping DockerHub login (no secrets available)
|
||||
if: ${{ !env.DOCKERHUB_USERNAME }}
|
||||
run: echo "DockerHub login skipped because secrets are not available."
|
||||
|
||||
- name: 🐳 Pre-pull testcontainer images
|
||||
if: ${{ env.DOCKERHUB_USERNAME }}
|
||||
run: |
|
||||
# Retry each pull - DockerHub registry timeouts are a recurring transient CI flake.
|
||||
pull() {
|
||||
for attempt in 1 2 3; do
|
||||
docker pull "$1" && return 0
|
||||
echo "::warning::docker pull $1 failed (attempt ${attempt}/3); retrying in 10s"
|
||||
sleep 10
|
||||
done
|
||||
echo "::error::docker pull $1 failed after 3 attempts"
|
||||
return 1
|
||||
}
|
||||
echo "Pre-pulling Docker images with authenticated session..."
|
||||
pull postgres:14
|
||||
pull clickhouse/clickhouse-server:26.2.19.43-alpine@sha256:c6ad6a7eb2fb5999df3adfb8b69a0c7222c68fa9b8f6b04a088564ebbc959251
|
||||
pull redis:7.2
|
||||
pull testcontainers/ryuk:0.14.0
|
||||
pull electricsql/electric:1.2.4
|
||||
pull minio/minio:latest
|
||||
echo "Image pre-pull complete"
|
||||
|
||||
- name: 📥 Download deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: 📀 Generate Prisma Client
|
||||
run: pnpm run generate
|
||||
|
||||
- name: 🧪 Run Webapp Unit Tests
|
||||
run: pnpm run test:webapp --reporter=default --reporter=blob --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --passWithNoTests
|
||||
env:
|
||||
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/postgres
|
||||
DIRECT_URL: postgresql://postgres:postgres@localhost:5432/postgres
|
||||
SESSION_SECRET: "secret"
|
||||
MAGIC_LINK_SECRET: "secret"
|
||||
ENCRYPTION_KEY: "dummy-encryption-keeeey-32-bytes"
|
||||
DEPLOY_REGISTRY_HOST: "docker.io"
|
||||
CLICKHOUSE_URL: "http://default:password@localhost:8123"
|
||||
|
||||
- name: Gather all reports
|
||||
if: ${{ !cancelled() }}
|
||||
run: |
|
||||
mkdir -p .vitest-reports
|
||||
find . -type f -path '*/.vitest-reports/blob-*.json' \
|
||||
-exec bash -c 'src="$1"; basename=$(basename "$src"); pkg=$(dirname "$src" | sed "s|^\./||;s|/\.vitest-reports$||;s|/|_|g"); cp "$src" ".vitest-reports/${pkg}-${basename}"' _ {} \;
|
||||
|
||||
- name: Upload blob reports to GitHub Actions Artifacts
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: webapp-blob-report-${{ matrix.shardIndex }}
|
||||
path: .vitest-reports/*
|
||||
include-hidden-files: true
|
||||
retention-days: 1
|
||||
|
||||
merge-reports:
|
||||
name: "📊 Merge Reports"
|
||||
if: ${{ !cancelled() }}
|
||||
needs: [unitTests]
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
steps:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
version: 10.33.2
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
# no cache enabled, we're not installing deps
|
||||
|
||||
- name: Download blob reports from GitHub Actions Artifacts
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
path: .vitest-reports
|
||||
pattern: webapp-blob-report-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Merge reports
|
||||
run: pnpm dlx vitest@4.1.7 run --merge-reports --pass-with-no-tests
|
||||
@@ -0,0 +1,34 @@
|
||||
name: "🧪 Unit Tests"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME:
|
||||
required: false
|
||||
DOCKERHUB_TOKEN:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
webapp:
|
||||
uses: ./.github/workflows/unit-tests-webapp.yml
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
e2e-webapp:
|
||||
uses: ./.github/workflows/e2e-webapp.yml
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
packages:
|
||||
uses: ./.github/workflows/unit-tests-packages.yml
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
internal:
|
||||
uses: ./.github/workflows/unit-tests-internal.yml
|
||||
secrets:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Vouch - Check PR
|
||||
|
||||
on:
|
||||
pull_request_target: # zizmor: ignore[dangerous-triggers] needed to comment/close fork PRs; safe because we never check out PR HEAD ref so no fork-controlled code runs
|
||||
types: [opened, reopened]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
check-vouch:
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write # auto-close unvouched PRs
|
||||
issues: read
|
||||
steps:
|
||||
- uses: mitchellh/vouch/action/check-pr@c6d80ead49839655b61b422700b7a3bc9d0804a9 # v1.4.2
|
||||
with:
|
||||
pr-number: ${{ github.event.pull_request.number }}
|
||||
auto-close: true
|
||||
require-vouch: true
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
require-draft:
|
||||
needs: check-vouch
|
||||
permissions:
|
||||
pull-requests: write # close non-draft PRs with a comment
|
||||
if: >
|
||||
github.event.pull_request.draft == false &&
|
||||
github.event.pull_request.author_association != 'MEMBER' &&
|
||||
github.event.pull_request.author_association != 'OWNER' &&
|
||||
github.event.pull_request.author_association != 'COLLABORATOR' &&
|
||||
github.event.pull_request.user.login != 'devin-ai-integration[bot]' &&
|
||||
github.event.pull_request.user.login != 'dependabot[bot]' &&
|
||||
github.event.pull_request.user.login != 'github-actions[bot]'
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
steps:
|
||||
- name: Close non-draft PR
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
STATE=$(gh pr view ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --json state -q '.state')
|
||||
if [ "$STATE" != "OPEN" ]; then
|
||||
echo "PR is already closed, skipping."
|
||||
exit 0
|
||||
fi
|
||||
gh pr close ${{ github.event.pull_request.number }} \
|
||||
--repo ${{ github.repository }} \
|
||||
--comment "Thanks for your contribution! We require all external PRs to be opened in **draft** status first so you can address CodeRabbit review comments and ensure CI passes before requesting a review. Please re-open this PR as a draft. See [CONTRIBUTING.md](https://github.com/${{ github.repository }}/blob/main/CONTRIBUTING.md#pr-workflow) for details."
|
||||
@@ -0,0 +1,24 @@
|
||||
name: Vouch - Manage by Issue
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
manage:
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
if: >-
|
||||
contains(github.event.comment.body, 'vouch') ||
|
||||
contains(github.event.comment.body, 'denounce') ||
|
||||
contains(github.event.comment.body, 'unvouch')
|
||||
steps:
|
||||
- uses: mitchellh/vouch/action/manage-by-issue@c6d80ead49839655b61b422700b7a3bc9d0804a9 # v1.4.2
|
||||
with:
|
||||
comment-id: ${{ github.event.comment.id }}
|
||||
issue-id: ${{ github.event.issue.number }}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
@@ -0,0 +1,58 @@
|
||||
name: Workflow Checks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- ".github/workflows/**"
|
||||
- ".github/actions/**"
|
||||
- ".github/zizmor.yml"
|
||||
- ".github/actionlint.yaml"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/**"
|
||||
- ".github/actions/**"
|
||||
- ".github/zizmor.yml"
|
||||
- ".github/actionlint.yaml"
|
||||
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
actionlint:
|
||||
name: Actionlint
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Run actionlint
|
||||
uses: docker://rhysd/actionlint:1.7.12@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667
|
||||
|
||||
zizmor:
|
||||
name: Zizmor
|
||||
# Uploads SARIF to the Security tab, which requires GitHub code scanning to be
|
||||
# enabled on the repository. Set the ENABLE_WORKFLOW_SECURITY_SCAN repository
|
||||
# variable to 'false' to skip this job where code scanning isn't available;
|
||||
# leave it unset (the default) to run the scan.
|
||||
if: ${{ vars.ENABLE_WORKFLOW_SECURITY_SCAN != 'false' }}
|
||||
runs-on: warp-ubuntu-latest-x64-2x
|
||||
permissions:
|
||||
security-events: write # Upload SARIF to GitHub Security tab
|
||||
contents: read # Read workflow files for analysis
|
||||
actions: read # Read workflow run metadata
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Run zizmor
|
||||
uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7
|
||||
@@ -0,0 +1,5 @@
|
||||
rules:
|
||||
unpinned-uses:
|
||||
config:
|
||||
policies:
|
||||
"*": hash-pin
|
||||
Reference in New Issue
Block a user