feat(supervisor): add automated worker setup script#2904
feat(supervisor): add automated worker setup script#2904yagoveloso wants to merge 1 commit intotriggerdotdev:mainfrom
Conversation
- Add interactive bash script to automate worker group creation - Support CLI arguments, environment variables, and .env files - Auto-resolve project external refs to internal IDs - Include dry-run mode for testing - Add --list-projects command to discover project IDs - Add comprehensive documentation in README with Quick Setup section - Include finding PAT instructions for all platforms - Add appendix explaining project ID types
|
WalkthroughThis pull request adds comprehensive documentation and a new automation script to the supervisor application. The README.md is expanded with setup instructions, prerequisites, usage examples, script options, and guidance on finding credentials and managing worker groups. A new setup-worker.sh Bash script is introduced that automates the creation and configuration of Trigger.dev worker groups for self-hosted instances, including features for dependency checking, environment loading, argument parsing, interactive prompting, API validation, project listing, and worker creation with dry-run support. Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Review CompleteYour review story is ready! Comment !reviewfast on this PR to re-generate the story. |
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Fix all issues with AI agents
In `@apps/supervisor/README.md`:
- Around line 68-72: The PAT placeholder in the options table for `--pat`
currently uses `tr*pat*...` which renders with emphasis; replace that
placeholder with escaped underscores so it reads `tr\_pat\_...` (matching the
existing `proj\_...` pattern used for `--project-ref`) to prevent markdown
formatting and display the literal underscores.
- Around line 98-102: Replace the Windows PowerShell snippet that uses CMD-style
%APPDATA% and the alias type with PowerShell-native equivalents: use
$env:APPDATA instead of %APPDATA% and use Get-Content in place of type, then
pipe to jq as before (locate the README snippet containing "%APPDATA%" and "type
%APPDATA%\trigger\config.json | jq -r \".profiles.default.accessToken\"").
In `@apps/supervisor/scripts/setup-worker.sh`:
- Around line 186-195: The interactive prompt in function prompt_interactive
currently reads the PAT with read -r which echoes the token; change the read
invocation for PAT to use a silent read (add the -s flag) so input is not shown,
and after the silent read emit a newline (e.g., an extra echo) so the terminal
prompt formatting stays correct; update the read that assigns to the PAT
variable and ensure subsequent logic that uses PAT remains unchanged.
- Around line 227-235: In validate_pat(), avoid printing the full secret;
instead build a masked version of the PAT that shows only the first 7 and last 4
characters and replace the middle with asterisks (e.g., "${PAT:0:7}***${PAT:
-4}"), then use that masked value in the echo that currently prints "Got:
$PAT" so logs show the masked token while preserving the existing error messages
and behavior.
- Around line 336-352: In create_worker, the jq payload currently uses
($projectId // null) which returns an empty string instead of null when
PROJECT_ID is empty; update the jq expression in the json_payload construction
so it explicitly converts an empty projectId to null (e.g., by testing length or
equality and returning null for empty strings) while still passing PROJECT_ID
via --arg, so projectId becomes null when no project is selected.
- Around line 255-274: In test_api_connection(), curl can fail and abort the
script under set -euo pipefail; modify the curl invocation to capture its exit
code (e.g., initialize curl_exit=0, run response=$(curl -sS -w "\n%{http_code}"
-H "Authorization: Bearer $PAT" "$API_URL/api/v1/projects" 2>&1) ||
curl_exit=$?), then check curl_exit first and on non-zero emit a clear error via
error "Curl failed (exit $curl_exit)" plus the captured response/body and exit
1; otherwise parse http_code/body as before and keep the existing HTTP status
check.
- Around line 488-499: The CLI allows the --default flag without a project,
causing an invalid payload; add a validation that if the default flag is set
(e.g., DEFAULT or MAKE_DEFAULT variable) then either PROJECT_REF or PROJECT_ID
must be present (or resolve_project_id must succeed) and otherwise print an
error and exit; implement this check after resolve_project_id is run (use
resolve_project_id, PROJECT_REF, PROJECT_ID and the default flag variable names
from the script) so the CLI path mirrors the interactive prompt enforcement.
- Around line 138-183: In parse_arguments, validate the presence and content of
the value for options that consume $2 (e.g., --name -> WORKER_NAME, --pat ->
PAT, --api-url -> API_URL, --project-ref -> PROJECT_REF, --project-id ->
PROJECT_ID) before shifting; specifically check that a non-empty argument exists
(guarding against set -u unbound errors) and that the next token is not another
flag (starts with '-') and emit a clear error and exit if validation fails, then
only assign and shift 2 when the check passes.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/supervisor/README.mdapps/supervisor/scripts/setup-worker.sh
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}
📄 CodeRabbit inference engine (AGENTS.md)
Format code using Prettier before committing
Files:
apps/supervisor/README.md
🧠 Learnings (6)
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Run `npx trigger.devlatest init` to initialize a Trigger.dev project
Applied to files:
apps/supervisor/README.md
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Use `trigger.dev/redis-worker` for background job and worker system needs in the webapp and run engine
Applied to files:
apps/supervisor/README.md
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Run `npx trigger.devlatest dev` to start the Trigger.dev development server
Applied to files:
apps/supervisor/README.md
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure Trigger.dev project in `trigger.config.ts` using `defineConfig()` with project ref and task directories
Applied to files:
apps/supervisor/README.md
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Export tasks with unique IDs within the project to enable proper task discovery and execution
Applied to files:
apps/supervisor/README.md
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use the `task()` function from `trigger.dev/sdk/v3` to define tasks with id and run properties
Applied to files:
apps/supervisor/README.md
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| | Option | Description | | ||
| | --------------------- | ----------------------------------------- | | ||
| | `--name <name>` | Worker group name (required) | | ||
| | `--pat <token>` | Personal Access Token (tr*pat*...) | | ||
| | `--api-url <url>` | Trigger.dev API URL | |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
head -n 75 apps/supervisor/README.md | tail -n 15Repository: triggerdotdev/trigger.dev
Length of output: 717
Use escaped underscores for the PAT format placeholder, matching the project-ref pattern.
In the options table, tr*pat*... will render with asterisks creating emphasis. However, the suggested fix using tr_pat_... doesn't solve this since underscores also trigger markdown formatting. Use escaped underscores instead: tr\_pat\_... to match the proj\_... pattern already used in the same table for the --project-ref option.
✅ Suggested fix
-| `--pat <token>` | Personal Access Token (tr*pat*...) |
+| `--pat <token>` | Personal Access Token (tr\_pat\_...) |🤖 Prompt for AI Agents
In `@apps/supervisor/README.md` around lines 68 - 72, The PAT placeholder in the
options table for `--pat` currently uses `tr*pat*...` which renders with
emphasis; replace that placeholder with escaped underscores so it reads
`tr\_pat\_...` (matching the existing `proj\_...` pattern used for
`--project-ref`) to prevent markdown formatting and display the literal
underscores.
| **Windows:** | ||
|
|
||
| ```powershell | ||
| type %APPDATA%\trigger\config.json | jq -r ".profiles.default.accessToken" | ||
| ``` |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
git ls-files "apps/supervisor/README.md"Repository: triggerdotdev/trigger.dev
Length of output: 194
🏁 Script executed:
cat -n apps/supervisor/README.md | sed -n '95,105p'Repository: triggerdotdev/trigger.dev
Length of output: 339
🌐 Web query:
PowerShell %APPDATA% variable expansion vs $env:APPDATA
💡 Result:
Short answer
- %APPDATA% is the CMD/Windows “%VAR%” expansion syntax; PowerShell does not expand it. Use $env:APPDATA (or .NET APIs) in PowerShell. [1][2][3]
Details + examples
- PowerShell exposes environment variables through the Env: drive and $env:Name shorthand; $env:APPDATA returns the AppData path. Use that inside strings: "$env:APPDATA\MyApp". [3][4]
- %APPDATA% will only be expanded if you run it inside cmd.exe (e.g. & cmd /c "echo %APPDATA%") or explicitly call an API that expands %VAR% (e.g. [Environment]::ExpandEnvironmentVariables). PowerShell itself treats "%APPDATA%" as a literal string. [2][5]
- Alternatives:
- $env:APPDATA — recommended for scripts.
- [Environment]::GetEnvironmentVariable("APPDATA") — explicit .NET call.
- [Environment]::ExpandEnvironmentVariables("%APPDATA%\x") — expands percent notation if you must use it. [5]
Example
- Correct PowerShell:
- $p = "$env:APPDATA\MyApp"
- If you have a literal "%APPDATA%\X" and want it expanded:
- $p = [Environment]::ExpandEnvironmentVariables("%APPDATA%\MyApp") . [5]
References
- StackOverflow: % is for cmd.exe; use $env:... in PowerShell. [1].
- Microsoft Q&A / PowerShell guidance recommending $env:USERNAME. [2].
- SS64 / PowerShell Env: drive documentation. [4].
- Opensource.com article on environment variables in PowerShell. [3].
- .NET Environment methods (ExpandEnvironmentVariables / GetEnvironmentVariable) referenced in SS64 and docs. [4][5].
PowerShell syntax: %APPDATA% is CMD-style and will not expand in PowerShell.
Use $env:APPDATA instead. Additionally, use Get-Content (the native PowerShell command) rather than type.
✅ Suggested fix
-type %APPDATA%\trigger\config.json | jq -r ".profiles.default.accessToken"
+Get-Content "$env:APPDATA\trigger\config.json" | jq -r ".profiles.default.accessToken"🤖 Prompt for AI Agents
In `@apps/supervisor/README.md` around lines 98 - 102, Replace the Windows
PowerShell snippet that uses CMD-style %APPDATA% and the alias type with
PowerShell-native equivalents: use $env:APPDATA instead of %APPDATA% and use
Get-Content in place of type, then pipe to jq as before (locate the README
snippet containing "%APPDATA%" and "type %APPDATA%\trigger\config.json | jq -r
\".profiles.default.accessToken\"").
| parse_arguments() { | ||
| while [[ $# -gt 0 ]]; do | ||
| case $1 in | ||
| --name) | ||
| WORKER_NAME="$2" | ||
| shift 2 | ||
| ;; | ||
| --pat) | ||
| PAT="$2" | ||
| shift 2 | ||
| ;; | ||
| --api-url) | ||
| API_URL="$2" | ||
| shift 2 | ||
| ;; | ||
| --project-ref) | ||
| PROJECT_REF="$2" | ||
| shift 2 | ||
| ;; | ||
| --project-id) | ||
| PROJECT_ID="$2" | ||
| shift 2 | ||
| ;; | ||
| --default) | ||
| MAKE_DEFAULT=1 | ||
| shift | ||
| ;; | ||
| --list-projects) | ||
| LIST_PROJECTS=1 | ||
| shift | ||
| ;; | ||
| --dry-run) | ||
| DRY_RUN=1 | ||
| shift | ||
| ;; | ||
| --help|-h) | ||
| show_help | ||
| exit 0 | ||
| ;; | ||
| *) | ||
| error "Unknown option: $1" | ||
| echo "Run with --help for usage information" | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| done |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n apps/supervisor/scripts/setup-worker.sh | head -200Repository: triggerdotdev/trigger.dev
Length of output: 6127
🏁 Script executed:
sed -n '200,350p' apps/supervisor/scripts/setup-worker.sh | cat -nRepository: triggerdotdev/trigger.dev
Length of output: 5153
🏁 Script executed:
sed -n '350,450p' apps/supervisor/scripts/setup-worker.sh | cat -nRepository: triggerdotdev/trigger.dev
Length of output: 3378
🏁 Script executed:
sed -n '400,500p' apps/supervisor/scripts/setup-worker.sh | cat -nRepository: triggerdotdev/trigger.dev
Length of output: 3115
🏁 Script executed:
# Test the actual behavior of parse_arguments with missing argument value
cat > /tmp/test_parse.sh << 'EOF'
#!/bin/bash
set -euo pipefail
ERROR_TRIGGERED=0
trap 'ERROR_TRIGGERED=1' ERR
# Simulate parse_arguments
parse_arguments() {
while [[ $# -gt 0 ]]; do
case $1 in
--name)
WORKER_NAME="$2"
shift 2
;;
*)
echo "Unknown: $1"
exit 1
;;
esac
done
}
# Test 1: Missing argument value
echo "Test 1: --name without value"
if ! parse_arguments --name 2>&1; then
echo "ERROR: Script failed as expected"
fi
echo ""
echo "Test 2: --name with value"
parse_arguments --name "my-worker"
echo "SUCCESS: Script completed normally"
EOF
chmod +x /tmp/test_parse.sh
bash /tmp/test_parse.sh 2>&1 || trueRepository: triggerdotdev/trigger.dev
Length of output: 147
🏁 Script executed:
# Test more edge cases to understand the behavior fully
cat > /tmp/test_parse2.sh << 'EOF'
#!/bin/bash
set -euo pipefail
WORKER_NAME=""
PAT=""
parse_arguments() {
while [[ $# -gt 0 ]]; do
case $1 in
--name)
WORKER_NAME="$2"
shift 2
;;
--pat)
PAT="$2"
shift 2
;;
*)
echo "Unknown: $1"
exit 1
;;
esac
done
}
# Test 1: --name followed by another flag
echo "Test 1: --name followed by --pat"
if parse_arguments --name --pat 2>&1; then
echo "RESULT: parse_arguments completed"
echo "WORKER_NAME='$WORKER_NAME'"
echo "PAT='$PAT'"
else
echo "FAILED"
fi
EOF
chmod +x /tmp/test_parse2.sh
bash /tmp/test_parse2.sh 2>&1 || trueRepository: triggerdotdev/trigger.dev
Length of output: 162
🏁 Script executed:
# Test the projectId handling and PAT echo issue
cat > /tmp/test_json.sh << 'EOF'
#!/bin/bash
# Test 1: Empty PROJECT_ID
echo "Test 1: Empty PROJECT_ID"
PROJECT_ID=""
WORKER_NAME="test"
MAKE_DEFAULT="false"
json_payload=$(jq -n \
--arg name "$WORKER_NAME" \
--arg projectId "$PROJECT_ID" \
--argjson makeDefault "$MAKE_DEFAULT" \
'{name: $name, projectId: ($projectId // null), makeDefaultForProject: $makeDefault}')
echo "Result: $json_payload"
echo ""
echo "Test 2: Non-empty PROJECT_ID"
PROJECT_ID="cmk_123"
json_payload=$(jq -n \
--arg name "$WORKER_NAME" \
--arg projectId "$PROJECT_ID" \
--argjson makeDefault "$MAKE_DEFAULT" \
'{name: $name, projectId: ($projectId // null), makeDefaultForProject: $makeDefault}')
echo "Result: $json_payload"
EOF
bash /tmp/test_json.shRepository: triggerdotdev/trigger.dev
Length of output: 294
🏁 Script executed:
# Verify the PAT echo issue
grep -n "Got:" apps/supervisor/scripts/setup-worker.shRepository: triggerdotdev/trigger.dev
Length of output: 98
🏁 Script executed:
# Verify curl error handling with set -euo pipefail
cat > /tmp/test_curl.sh << 'EOF'
#!/bin/bash
set -euo pipefail
# Simulate curl failure
test_curl_error() {
local response
response=$(curl -sS "http://invalid.example.com/api" 2>&1 || echo "CURL_FAILED")
echo "Response: $response"
}
echo "Testing curl error with set -euo pipefail:"
test_curl_error 2>&1 || echo "Function failed"
EOF
bash /tmp/test_curl.shRepository: triggerdotdev/trigger.dev
Length of output: 187
Validate option values before consuming $2.
With set -u, a missing argument value causes an unbound variable error that exits the script abruptly. Additionally, if an option is followed by another flag instead of a value (e.g., --name --pat), the next flag gets consumed as the value, misaligning subsequent argument parsing. Add explicit guards for options requiring values.
✅ Suggested fix
parse_arguments() {
+ require_value() {
+ local opt="$1"
+ local val="${2-}"
+ if [[ -z "$val" || "$val" == --* ]]; then
+ error "Option $opt requires a value"
+ exit 1
+ fi
+ }
while [[ $# -gt 0 ]]; do
case $1 in
--name)
+ require_value --name "$2"
WORKER_NAME="$2"
shift 2
;;
--pat)
+ require_value --pat "$2"
PAT="$2"
shift 2
;;
--api-url)
+ require_value --api-url "$2"
API_URL="$2"
shift 2
;;
--project-ref)
+ require_value --project-ref "$2"
PROJECT_REF="$2"
shift 2
;;
--project-id)
+ require_value --project-id "$2"
PROJECT_ID="$2"
shift 2
;;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| parse_arguments() { | |
| while [[ $# -gt 0 ]]; do | |
| case $1 in | |
| --name) | |
| WORKER_NAME="$2" | |
| shift 2 | |
| ;; | |
| --pat) | |
| PAT="$2" | |
| shift 2 | |
| ;; | |
| --api-url) | |
| API_URL="$2" | |
| shift 2 | |
| ;; | |
| --project-ref) | |
| PROJECT_REF="$2" | |
| shift 2 | |
| ;; | |
| --project-id) | |
| PROJECT_ID="$2" | |
| shift 2 | |
| ;; | |
| --default) | |
| MAKE_DEFAULT=1 | |
| shift | |
| ;; | |
| --list-projects) | |
| LIST_PROJECTS=1 | |
| shift | |
| ;; | |
| --dry-run) | |
| DRY_RUN=1 | |
| shift | |
| ;; | |
| --help|-h) | |
| show_help | |
| exit 0 | |
| ;; | |
| *) | |
| error "Unknown option: $1" | |
| echo "Run with --help for usage information" | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| parse_arguments() { | |
| require_value() { | |
| local opt="$1" | |
| local val="${2-}" | |
| if [[ -z "$val" || "$val" == --* ]]; then | |
| error "Option $opt requires a value" | |
| exit 1 | |
| fi | |
| } | |
| while [[ $# -gt 0 ]]; do | |
| case $1 in | |
| --name) | |
| require_value --name "$2" | |
| WORKER_NAME="$2" | |
| shift 2 | |
| ;; | |
| --pat) | |
| require_value --pat "$2" | |
| PAT="$2" | |
| shift 2 | |
| ;; | |
| --api-url) | |
| require_value --api-url "$2" | |
| API_URL="$2" | |
| shift 2 | |
| ;; | |
| --project-ref) | |
| require_value --project-ref "$2" | |
| PROJECT_REF="$2" | |
| shift 2 | |
| ;; | |
| --project-id) | |
| require_value --project-id "$2" | |
| PROJECT_ID="$2" | |
| shift 2 | |
| ;; | |
| --default) | |
| MAKE_DEFAULT=1 | |
| shift | |
| ;; | |
| --list-projects) | |
| LIST_PROJECTS=1 | |
| shift | |
| ;; | |
| --dry-run) | |
| DRY_RUN=1 | |
| shift | |
| ;; | |
| --help|-h) | |
| show_help | |
| exit 0 | |
| ;; | |
| *) | |
| error "Unknown option: $1" | |
| echo "Run with --help for usage information" | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| } |
🤖 Prompt for AI Agents
In `@apps/supervisor/scripts/setup-worker.sh` around lines 138 - 183, In
parse_arguments, validate the presence and content of the value for options that
consume $2 (e.g., --name -> WORKER_NAME, --pat -> PAT, --api-url -> API_URL,
--project-ref -> PROJECT_REF, --project-id -> PROJECT_ID) before shifting;
specifically check that a non-empty argument exists (guarding against set -u
unbound errors) and that the next token is not another flag (starts with '-')
and emit a clear error and exit if validation fails, then only assign and shift
2 when the check passes.
| prompt_interactive() { | ||
| # Prompt for PAT if missing | ||
| if [[ -z "$PAT" ]]; then | ||
| warning "PAT not found in environment or .env" | ||
| echo "" | ||
| echo "Please enter your Personal Access Token (PAT):" | ||
| echo " Format: tr_pat_..." | ||
| echo " Location: ~/.config/trigger/config.json or ~/Library/Preferences/trigger/config.json" | ||
| read -r -p "PAT: " PAT | ||
| echo "" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Check if file exists and view the content at specified lines
head -200 apps/supervisor/scripts/setup-worker.sh | tail -20Repository: triggerdotdev/trigger.dev
Length of output: 610
🏁 Script executed:
# Also verify the exact lines 186-195 with line numbers
sed -n '186,195p' apps/supervisor/scripts/setup-worker.shRepository: triggerdotdev/trigger.dev
Length of output: 438
🏁 Script executed:
# Check for other instances of read commands in this file
rg "read\s+" apps/supervisor/scripts/setup-worker.sh -nRepository: triggerdotdev/trigger.dev
Length of output: 429
Mask PAT input during interactive prompt to prevent accidental credential exposure.
The current read -r command echoes input characters to the terminal. Add the -s flag to suppress echo of the token:
Suggested fix
- read -r -p "PAT: " PAT
+ read -rs -p "PAT: " PAT
echo ""📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| prompt_interactive() { | |
| # Prompt for PAT if missing | |
| if [[ -z "$PAT" ]]; then | |
| warning "PAT not found in environment or .env" | |
| echo "" | |
| echo "Please enter your Personal Access Token (PAT):" | |
| echo " Format: tr_pat_..." | |
| echo " Location: ~/.config/trigger/config.json or ~/Library/Preferences/trigger/config.json" | |
| read -r -p "PAT: " PAT | |
| echo "" | |
| prompt_interactive() { | |
| # Prompt for PAT if missing | |
| if [[ -z "$PAT" ]]; then | |
| warning "PAT not found in environment or .env" | |
| echo "" | |
| echo "Please enter your Personal Access Token (PAT):" | |
| echo " Format: tr_pat_..." | |
| echo " Location: ~/.config/trigger/config.json or ~/Library/Preferences/trigger/config.json" | |
| read -rs -p "PAT: " PAT | |
| echo "" |
🤖 Prompt for AI Agents
In `@apps/supervisor/scripts/setup-worker.sh` around lines 186 - 195, The
interactive prompt in function prompt_interactive currently reads the PAT with
read -r which echoes the token; change the read invocation for PAT to use a
silent read (add the -s flag) so input is not shown, and after the silent read
emit a newline (e.g., an extra echo) so the terminal prompt formatting stays
correct; update the read that assigns to the PAT variable and ensure subsequent
logic that uses PAT remains unchanged.
| validate_pat() { | ||
| if [[ ! "$PAT" =~ ^tr_pat_ ]]; then | ||
| error "Invalid Personal Access Token format" | ||
| echo "" | ||
| echo "Expected: tr_pat_... (40 characters)" | ||
| echo "Got: $PAT" | ||
| echo "" | ||
| echo "PAT starts with 'tr_pat_', not 'tr_prod_' or 'tr_dev_'" | ||
| echo "" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "setup-worker.sh" -type fRepository: triggerdotdev/trigger.dev
Length of output: 110
🏁 Script executed:
# Once we find the file, let's read lines 227-235
sed -n '220,240p' apps/supervisor/scripts/setup-worker.shRepository: triggerdotdev/trigger.dev
Length of output: 670
Mask the PAT before displaying in error output.
The code echoes the full PAT to stdout/stderr, which leaks the secret credential into logs and terminal history. Use the suggested masking approach to display only the first 7 and last 4 characters:
Suggested fix
- echo "Got: $PAT"
+ local pat_redacted="$PAT"
+ if [[ ${`#PAT`} -gt 10 ]]; then
+ pat_redacted="${PAT:0:7}***${PAT: -4}"
+ fi
+ echo "Got: $pat_redacted"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| validate_pat() { | |
| if [[ ! "$PAT" =~ ^tr_pat_ ]]; then | |
| error "Invalid Personal Access Token format" | |
| echo "" | |
| echo "Expected: tr_pat_... (40 characters)" | |
| echo "Got: $PAT" | |
| echo "" | |
| echo "PAT starts with 'tr_pat_', not 'tr_prod_' or 'tr_dev_'" | |
| echo "" | |
| validate_pat() { | |
| if [[ ! "$PAT" =~ ^tr_pat_ ]]; then | |
| error "Invalid Personal Access Token format" | |
| echo "" | |
| echo "Expected: tr_pat_... (40 characters)" | |
| local pat_redacted="$PAT" | |
| if [[ ${`#PAT`} -gt 10 ]]; then | |
| pat_redacted="${PAT:0:7}***${PAT: -4}" | |
| fi | |
| echo "Got: $pat_redacted" | |
| echo "" | |
| echo "PAT starts with 'tr_pat_', not 'tr_prod_' or 'tr_dev_'" | |
| echo "" |
🤖 Prompt for AI Agents
In `@apps/supervisor/scripts/setup-worker.sh` around lines 227 - 235, In
validate_pat(), avoid printing the full secret; instead build a masked version
of the PAT that shows only the first 7 and last 4 characters and replace the
middle with asterisks (e.g., "${PAT:0:7}***${PAT: -4}"), then use that masked
value in the echo that currently prints "Got: $PAT" so logs show the masked
token while preserving the existing error messages and behavior.
| test_api_connection() { | ||
| info "Testing API connection..." | ||
|
|
||
| local response | ||
| local http_code | ||
| local body | ||
|
|
||
| response=$(curl -sS -w "\n%{http_code}" \ | ||
| -H "Authorization: Bearer $PAT" \ | ||
| "$API_URL/api/v1/projects" 2>&1) | ||
|
|
||
| http_code=$(echo "$response" | tail -n1) | ||
| body=$(echo "$response" | sed '$d') | ||
|
|
||
| if [[ "$http_code" != "200" ]]; then | ||
| error "Failed to connect to API (HTTP $http_code)" | ||
| echo "" | ||
| echo "Response: $body" | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n apps/supervisor/scripts/setup-worker.sh | head -50Repository: triggerdotdev/trigger.dev
Length of output: 1513
🏁 Script executed:
sed -n '255,274p' apps/supervisor/scripts/setup-worker.shRepository: triggerdotdev/trigger.dev
Length of output: 536
Handle curl failures explicitly to avoid set -e aborts.
The script has set -euo pipefail enabled (line 11). When curl encounters a network error (connection refused, timeout, etc.), it exits with a non-zero code, causing the script to abort immediately before the HTTP code check runs. Capture curl's exit status with the || curl_exit=$? pattern so you can emit a clear error message.
✅ Suggested fix
- response=$(curl -sS -w "\n%{http_code}" \
- -H "Authorization: Bearer $PAT" \
- "$API_URL/api/v1/projects" 2>&1)
+ local curl_exit=0
+ response=$(curl -sS -w "\n%{http_code}" \
+ -H "Authorization: Bearer $PAT" \
+ "$API_URL/api/v1/projects" 2>&1) || curl_exit=$?
+
+ if [[ $curl_exit -ne 0 ]]; then
+ error "Failed to connect to API (curl exit $curl_exit)"
+ echo ""
+ echo "Response: $response"
+ exit 1
+ fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| test_api_connection() { | |
| info "Testing API connection..." | |
| local response | |
| local http_code | |
| local body | |
| response=$(curl -sS -w "\n%{http_code}" \ | |
| -H "Authorization: Bearer $PAT" \ | |
| "$API_URL/api/v1/projects" 2>&1) | |
| http_code=$(echo "$response" | tail -n1) | |
| body=$(echo "$response" | sed '$d') | |
| if [[ "$http_code" != "200" ]]; then | |
| error "Failed to connect to API (HTTP $http_code)" | |
| echo "" | |
| echo "Response: $body" | |
| exit 1 | |
| fi | |
| test_api_connection() { | |
| info "Testing API connection..." | |
| local response | |
| local http_code | |
| local body | |
| local curl_exit=0 | |
| response=$(curl -sS -w "\n%{http_code}" \ | |
| -H "Authorization: Bearer $PAT" \ | |
| "$API_URL/api/v1/projects" 2>&1) || curl_exit=$? | |
| if [[ $curl_exit -ne 0 ]]; then | |
| error "Failed to connect to API (curl exit $curl_exit)" | |
| echo "" | |
| echo "Response: $response" | |
| exit 1 | |
| fi | |
| http_code=$(echo "$response" | tail -n1) | |
| body=$(echo "$response" | sed '$d') | |
| if [[ "$http_code" != "200" ]]; then | |
| error "Failed to connect to API (HTTP $http_code)" | |
| echo "" | |
| echo "Response: $body" | |
| exit 1 | |
| fi |
🤖 Prompt for AI Agents
In `@apps/supervisor/scripts/setup-worker.sh` around lines 255 - 274, In
test_api_connection(), curl can fail and abort the script under set -euo
pipefail; modify the curl invocation to capture its exit code (e.g., initialize
curl_exit=0, run response=$(curl -sS -w "\n%{http_code}" -H "Authorization:
Bearer $PAT" "$API_URL/api/v1/projects" 2>&1) || curl_exit=$?), then check
curl_exit first and on non-zero emit a clear error via error "Curl failed (exit
$curl_exit)" plus the captured response/body and exit 1; otherwise parse
http_code/body as before and keep the existing HTTP status check.
| create_worker() { | ||
| local json_payload | ||
| local make_default_bool | ||
|
|
||
| # Convert 0/1 to false/true for JSON boolean | ||
| if [[ $MAKE_DEFAULT -eq 1 ]]; then | ||
| make_default_bool="true" | ||
| else | ||
| make_default_bool="false" | ||
| fi | ||
|
|
||
| json_payload=$(jq -n \ | ||
| --arg name "$WORKER_NAME" \ | ||
| --arg projectId "$PROJECT_ID" \ | ||
| --argjson makeDefault "$make_default_bool" \ | ||
| '{name: $name, projectId: ($projectId // null), makeDefaultForProject: $makeDefault}') | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, locate and examine the file
find . -name "setup-worker.sh" -type fRepository: triggerdotdev/trigger.dev
Length of output: 110
🏁 Script executed:
# Test jq behavior with the alternative operator and empty strings
# Create a test to see if // operator treats empty strings as falsy
cat << 'EOF' | jq '.projectId = (if $projectId == "" then null else $projectId end)' --arg projectId ""
{"projectId": ""}
EOF
echo "---"
cat << 'EOF' | jq '.projectId = ($projectId // null)' --arg projectId ""
{"projectId": ""}
EOF
echo "---"
# Test with a non-empty projectId
cat << 'EOF' | jq '.projectId = ($projectId // null)' --arg projectId "my-project"
{"projectId": ""}
EOF
EOFRepository: triggerdotdev/trigger.dev
Length of output: 216
🏁 Script executed:
# Examine the actual file to confirm the code matches the review comment
sed -n '336,352p' ./apps/supervisor/scripts/setup-worker.shRepository: triggerdotdev/trigger.dev
Length of output: 530
Fix empty projectId conversion in JSON payload.
jq's alternative operator // only treats null and false as falsy; empty strings are truthy. The current code ($projectId // null) produces "projectId": "" when no project is chosen, but it should be null.
Fix
- '{name: $name, projectId: ($projectId // null), makeDefaultForProject: $makeDefault}')
+ '{
+ name: $name,
+ projectId: (if $projectId == "" then null else $projectId end),
+ makeDefaultForProject: $makeDefault
+ }')🤖 Prompt for AI Agents
In `@apps/supervisor/scripts/setup-worker.sh` around lines 336 - 352, In
create_worker, the jq payload currently uses ($projectId // null) which returns
an empty string instead of null when PROJECT_ID is empty; update the jq
expression in the json_payload construction so it explicitly converts an empty
projectId to null (e.g., by testing length or equality and returning null for
empty strings) while still passing PROJECT_ID via --arg, so projectId becomes
null when no project is selected.
| # 9. Validate worker name | ||
| if [[ -z "$WORKER_NAME" ]]; then | ||
| error "Worker name is required" | ||
| echo "Run with --help for usage information" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # 10. Resolve project ID | ||
| if [[ -n "$PROJECT_REF" ]]; then | ||
| resolve_project_id | ||
| fi | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
fd setup-worker.shRepository: triggerdotdev/trigger.dev
Length of output: 108
🏁 Script executed:
wc -l apps/supervisor/scripts/setup-worker.shRepository: triggerdotdev/trigger.dev
Length of output: 112
🏁 Script executed:
cat -n apps/supervisor/scripts/setup-worker.shRepository: triggerdotdev/trigger.dev
Length of output: 16241
Validate that --default requires a project reference.
Currently, the --default flag can be set via CLI without requiring --project-ref or --project-id. This would result in sending makeDefaultForProject: true with projectId: null to the API, which is invalid since a worker cannot be made default for an unspecified project. The interactive prompt correctly enforces this requirement (lines 211–224), but the CLI path does not.
Suggested fix
# 9. Validate worker name
if [[ -z "$WORKER_NAME" ]]; then
error "Worker name is required"
echo "Run with --help for usage information"
exit 1
fi
+
+ if [[ $MAKE_DEFAULT -eq 1 && -z "$PROJECT_REF" && -z "$PROJECT_ID" ]]; then
+ error "--default requires --project-ref or --project-id"
+ exit 1
+ fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # 9. Validate worker name | |
| if [[ -z "$WORKER_NAME" ]]; then | |
| error "Worker name is required" | |
| echo "Run with --help for usage information" | |
| exit 1 | |
| fi | |
| # 10. Resolve project ID | |
| if [[ -n "$PROJECT_REF" ]]; then | |
| resolve_project_id | |
| fi | |
| # 9. Validate worker name | |
| if [[ -z "$WORKER_NAME" ]]; then | |
| error "Worker name is required" | |
| echo "Run with --help for usage information" | |
| exit 1 | |
| fi | |
| if [[ $MAKE_DEFAULT -eq 1 && -z "$PROJECT_REF" && -z "$PROJECT_ID" ]]; then | |
| error "--default requires --project-ref or --project-id" | |
| exit 1 | |
| fi | |
| # 10. Resolve project ID | |
| if [[ -n "$PROJECT_REF" ]]; then | |
| resolve_project_id | |
| fi |
🤖 Prompt for AI Agents
In `@apps/supervisor/scripts/setup-worker.sh` around lines 488 - 499, The CLI
allows the --default flag without a project, causing an invalid payload; add a
validation that if the default flag is set (e.g., DEFAULT or MAKE_DEFAULT
variable) then either PROJECT_REF or PROJECT_ID must be present (or
resolve_project_id must succeed) and otherwise print an error and exit;
implement this check after resolve_project_id is run (use resolve_project_id,
PROJECT_REF, PROJECT_ID and the default flag variable names from the script) so
the CLI path mirrors the interactive prompt enforcement.
|
hey @yagoveloso good idea to add a helper script for this. Is auto-bootstrap not working for you? What's your setup and use-case? also please check coderabbit comments, mostly quite sensible |
Adds an interactive bash script to automate worker group creation and configuration for self-hosted Trigger.dev supervisor instances.
✅ Checklist
Testing
Successfully tested all features:
--helpdisplays complete usage information--list-projectsagainst live API, correctly displays both external refs and internal IDs--dry-runshows expected actions without executingproj_...) to internal IDAll tests passed successfully.
Changelog
Added
apps/supervisor/scripts/setup-worker.sh):--list-projectscommand to discover project identifiers--dry-runmode for testing without executionapps/supervisor/README.md):Benefits
Screenshots
