Skip to content

Instantly share code, notes, and snippets.

@jnunemaker
Last active March 18, 2026 22:34
Show Gist options
  • Select an option

  • Save jnunemaker/7e4b909bce0bb8796d342bed06bd4c3e to your computer and use it in GitHub Desktop.

Select an option

Save jnunemaker/7e4b909bce0bb8796d342bed06bd4c3e to your computer and use it in GitHub Desktop.
Biggest thing is default PORT to CONDUCTOR_PORT and derive everything from that. Be sure to symlink any git ignored files that you need from root to workspace. Stuff like that.
#!/bin/sh
#/ Usage: bin/conductor-setup
#/
#/ Setup files that are not tracked in git for Conductor workspaces.
#/ This is run automatically when a new workspace is created.
set -e
cd $(dirname "$0")/..
# Symlink .env from the root repo so all workspaces share the same config.
# Set up your .env once at $CONDUCTOR_ROOT_PATH/.env and all workspaces use it.
if [ -n "$CONDUCTOR_ROOT_PATH" ]; then
if [ -f "$CONDUCTOR_ROOT_PATH/.env" ]; then
echo "Symlinking .env from $CONDUCTOR_ROOT_PATH/.env..."
ln -sf "$CONDUCTOR_ROOT_PATH/.env" .env
else
echo "Warning: $CONDUCTOR_ROOT_PATH/.env not found."
echo "Create it from .env.example: cp .env.example $CONDUCTOR_ROOT_PATH/.env"
fi
# Copy database.yml and credential keys from repo root
if [ -f "$CONDUCTOR_ROOT_PATH/config/database.yml" ]; then
echo "Copying database.yml from repo root..."
cp "$CONDUCTOR_ROOT_PATH/config/database.yml" config/database.yml
fi
if [ -f "$CONDUCTOR_ROOT_PATH/config/credentials/development.key" ]; then
echo "Copying development.key from repo root..."
cp "$CONDUCTOR_ROOT_PATH/config/credentials/development.key" config/credentials/development.key
fi
if [ -f "$CONDUCTOR_ROOT_PATH/config/credentials/test.key" ]; then
echo "Copying test.key from repo root..."
cp "$CONDUCTOR_ROOT_PATH/config/credentials/test.key" config/credentials/test.key
fi
# Symlink storage directory for Active Storage
if [ -d "$CONDUCTOR_ROOT_PATH/storage" ]; then
echo "Symlinking storage from $CONDUCTOR_ROOT_PATH/storage..."
ln -sf "$CONDUCTOR_ROOT_PATH/storage" storage
else
echo "Creating storage directory at $CONDUCTOR_ROOT_PATH/storage..."
mkdir -p "$CONDUCTOR_ROOT_PATH/storage"
ln -sf "$CONDUCTOR_ROOT_PATH/storage" storage
fi
# Symlink ngrok.yml from repo root if it exists
if [ -f "$CONDUCTOR_ROOT_PATH/ngrok.yml" ]; then
echo "Symlinking ngrok.yml from $CONDUCTOR_ROOT_PATH/ngrok.yml..."
ln -sf "$CONDUCTOR_ROOT_PATH/ngrok.yml" ngrok.yml
fi
# Symlink .bundle for private gem credentials
if [ -d "$CONDUCTOR_ROOT_PATH/.bundle" ]; then
if [ -e .bundle ] && [ ! -L .bundle ]; then
echo "Error: .bundle exists and is not a symlink. Remove it manually first."
exit 1
fi
echo "Symlinking .bundle from $CONDUCTOR_ROOT_PATH/.bundle..."
ln -sf "$CONDUCTOR_ROOT_PATH/.bundle" .bundle
fi
else
# Fallback for running outside Conductor
if [ ! -f .env ]; then
echo "Creating .env from .env.example..."
cp .env.example .env
else
echo ".env already exists, skipping..."
fi
fi
# Run full setup (dependencies, database, fixtures)
script/bootstrap
echo "Conductor setup complete!"
{
"scripts": {
"setup": "bin/conductor-setup",
"server": "script/server"
}
}
canonical_host = ENV["CANONICAL_HOST"]
CANONICAL_HOST = if canonical_host
canonical_host
elsif Rails.env.development?
"localhost:#{ENV.fetch("PORT", 3000)}"
elsif Rails.env.test?
"www.example.com"
else
"#{ENV["HEROKU_APP_NAME"]}.herokuapp.com"
end
scheme = Rails.configuration.force_ssl ? "https" : "http"
APP_URL = "#{scheme}://#{CANONICAL_HOST}"
Rails.application.routes.default_url_options[:host] = CANONICAL_HOST
Rails.application.config.action_mailer.default_url_options = { host: CANONICAL_HOST }
Rails.application.config.asset_host = CANONICAL_HOST
if canonical_host
Rails.application.middleware.use Rack::CanonicalHost, canonical_host
end
workers_count = Integer(ENV['WEB_CONCURRENCY'] || 1)
max_threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 2)
min_threads_count = Integer(ENV['RAILS_MIN_THREADS'] || max_threads_count)
threads min_threads_count, max_threads_count
port ENV['PORT'] || 3000
environment ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
# FIXME(ezekg) https://www.heroku.com/blog/pumas-routers-keepalives-ohmy/
enable_keep_alives false
if workers_count > 1
preload_app!
workers workers_count
before_fork do
if defined?(::ActiveRecord) && defined?(::ActiveRecord::Base)
ApplicationRecord.connection_pool.disconnect!
end
end
on_worker_boot do
if defined?(::ActiveRecord) && defined?(::ActiveRecord::Base)
ApplicationRecord.establish_connection
end
end
end
if ENV['RACK_ENV'] == 'development' || ENV['RAILS_ENV'] == 'development' || (ENV['RACK_ENV'].nil? && ENV['RAILS_ENV'].nil?)
on_booted do
port = ENV['PORT'] || 3000
url = "http://localhost:#{port}"
puts
puts " ✅ App running at #{url}"
puts
end
end
# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart
#!/bin/sh
#/ Usage: server
#/
#/ Run all the processes necessary to for the app.
set -e
cd $(dirname "$0")/..
[ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && {
grep '^#/' <"$0"| cut -c4-
exit 0
}
# Use CONDUCTOR_PORT if set, otherwise PORT, defaulting to 3000
# Set Vite port to PORT + 36 (e.g., 3000 -> 3036)
export PORT=${CONDUCTOR_PORT:-${PORT:-3000}}
export VITE_RUBY_PORT=$((PORT + 36))
# sqs_worker turned off because we set active job to inline for shoryuken jobs
# worker turned off because we set good job to inline for all other jobs
bundle exec foreman start -p ${PORT} -f Procfile.dev
import { defineConfig } from 'vite'
import ViteRails from 'vite-plugin-rails'
import vue from '@vitejs/plugin-vue'
// Default Vite port to PORT + 36 (e.g., Rails on 4000 -> Vite on 4036)
const basePort = parseInt(process.env.PORT || '4000', 10)
const vitePort = parseInt(process.env.VITE_PORT || String(basePort + 36), 10)
export default defineConfig({
server: {
port: vitePort,
strictPort: true,
},
plugins: [
ViteRails(),
vue()
]
})
@jeremysmithco
Copy link
Copy Markdown

@jnunemaker Hey, thanks for this! 🙏

How does your setup know which version of Ruby to run? I use chruby (configured within .bashrc), but since the setup and run scripts aren't run from interactive shells, it doesn't load the proper version and defaults to system Ruby for me.

@jnunemaker
Copy link
Copy Markdown
Author

@jeremysmithco I have .tool-versions either symlinked or checked into git depending on the project and mise/asdf pick it up.

@jeremysmithco
Copy link
Copy Markdown

@jnunemaker Ahh, gotcha! The universe seems to be telling me to switch to mise today.

Is the cd $(dirname "$0")/.. there for mise's Hook on cd?

@jnunemaker
Copy link
Copy Markdown
Author

I’m using asdf. Maybe? I just had Claude make it and it’s worked so I didn’t over analyze. Haha

@jeremysmithco
Copy link
Copy Markdown

😄 Ok, fair enough! I'm going to switch to mise and see if that solves the issue.

@jeremysmithco
Copy link
Copy Markdown

For posterity, I finally got things working and I'm 90% sure the issue was that Conductor is using zsh for Setup and Run scripts, which I hadn’t configured for version management (though for Terminal, it seems to use your default shell, which for me is Bash).

@adam12
Copy link
Copy Markdown

adam12 commented Jan 31, 2026

Thanks for sharing @jnunemaker !

It would be nice to be able to choose the shell used to run commands, since I ran into same issue as @jeremysmithco (tho fish+mise). I've submitted it as feedback to the Conductor team.

EDIT: Just providing my own binscript with a fish shebang seems like it will do for now.

@jeremysmithco
Copy link
Copy Markdown

@adam12 Ahh, thanks for the info! Glad it wasn't just me. :) Should've thought to submit feedback myself, but glad you did! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment