feat(timer): update reason without restart
This commit is contained in:
parent
547e1c3933
commit
7fae7e2d95
|
|
@ -205,12 +205,13 @@ class TournamentsController < ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def timer_params
|
def timer_params
|
||||||
{
|
timer_params = {
|
||||||
timestamp: params[:timestamp],
|
|
||||||
timer_mode: params[:timer_mode],
|
|
||||||
timer_reason: params[:timer_reason],
|
timer_reason: params[:timer_reason],
|
||||||
timer_reason_text: params[:timer_reason_text]
|
timer_reason_text: params[:timer_reason_text]
|
||||||
}
|
}
|
||||||
|
timer_params[:timestamp] = params[:timestamp] if params.key?(:timestamp)
|
||||||
|
timer_params[:timer_mode] = params[:timer_mode] if params.key?(:timer_mode)
|
||||||
|
timer_params
|
||||||
end
|
end
|
||||||
|
|
||||||
def serialize_timer_state(tournament)
|
def serialize_timer_state(tournament)
|
||||||
|
|
@ -371,6 +372,9 @@ class TournamentsController < ApplicationController
|
||||||
timestamp = params[:timestamp]
|
timestamp = params[:timestamp]
|
||||||
timestamp_seconds = params[:timestamp_seconds]
|
timestamp_seconds = params[:timestamp_seconds]
|
||||||
timer_mode = params[:timer_mode]
|
timer_mode = params[:timer_mode]
|
||||||
|
timer_reason_param_present = params.key?(:timer_reason)
|
||||||
|
timer_reason_text_param_present = params.key?(:timer_reason_text)
|
||||||
|
timer_reason_update_requested = timer_reason_param_present || timer_reason_text_param_present
|
||||||
normalize_timer_reason_params
|
normalize_timer_reason_params
|
||||||
timer_reason = params[:timer_reason]
|
timer_reason = params[:timer_reason]
|
||||||
timer_reason_text = params[:timer_reason_text]
|
timer_reason_text = params[:timer_reason_text]
|
||||||
|
|
@ -384,7 +388,12 @@ class TournamentsController < ApplicationController
|
||||||
return render_timer_validation_error('Invalid timer_mode')
|
return render_timer_validation_error('Invalid timer_mode')
|
||||||
end
|
end
|
||||||
|
|
||||||
timer_reason_error = validate_timer_reason_params(timer_reason, timer_reason_text)
|
timer_reason_error = validate_timer_reason_params(
|
||||||
|
timer_reason,
|
||||||
|
timer_reason_text,
|
||||||
|
timer_reason_param_present,
|
||||||
|
timer_reason_text_param_present
|
||||||
|
)
|
||||||
return render_timer_validation_error(timer_reason_error) if timer_reason_error
|
return render_timer_validation_error(timer_reason_error) if timer_reason_error
|
||||||
|
|
||||||
# throw error if both timestamp and timestamp_seconds are present
|
# throw error if both timestamp and timestamp_seconds are present
|
||||||
|
|
@ -428,6 +437,8 @@ class TournamentsController < ApplicationController
|
||||||
params[:timestamp] = Time.zone.now
|
params[:timestamp] = Time.zone.now
|
||||||
elsif timer_mode.present?
|
elsif timer_mode.present?
|
||||||
return render_timer_validation_error('timer_mode requires timestamp, timestamp_seconds, or clear_timer')
|
return render_timer_validation_error('timer_mode requires timestamp, timestamp_seconds, or clear_timer')
|
||||||
|
elsif timer_reason_update_requested
|
||||||
|
return render_timer_validation_error('timer_reason requires active timer') unless timer_active?
|
||||||
else
|
else
|
||||||
return render_timer_validation_error('timestamp, timestamp_seconds, or clear_timer is required')
|
return render_timer_validation_error('timestamp, timestamp_seconds, or clear_timer is required')
|
||||||
end
|
end
|
||||||
|
|
@ -456,8 +467,17 @@ class TournamentsController < ApplicationController
|
||||||
params[:timer_reason_text] = nil
|
params[:timer_reason_text] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_timer_reason_params(timer_reason, timer_reason_text)
|
def validate_timer_reason_params(
|
||||||
|
timer_reason,
|
||||||
|
timer_reason_text,
|
||||||
|
timer_reason_param_present,
|
||||||
|
timer_reason_text_param_present
|
||||||
|
)
|
||||||
|
if timer_reason_text_param_present && !timer_reason_param_present
|
||||||
|
return 'timer_reason is required when timer_reason_text is provided'
|
||||||
|
end
|
||||||
return if timer_reason.blank? && timer_reason_text.blank?
|
return if timer_reason.blank? && timer_reason_text.blank?
|
||||||
|
|
||||||
return 'timer_reason_text is only allowed when timer_reason is custom' if timer_reason.blank?
|
return 'timer_reason_text is only allowed when timer_reason is custom' if timer_reason.blank?
|
||||||
return 'Invalid timer_reason' unless Tournament::TIMER_REASONS.include?(timer_reason)
|
return 'Invalid timer_reason' unless Tournament::TIMER_REASONS.include?(timer_reason)
|
||||||
|
|
||||||
|
|
@ -470,6 +490,10 @@ class TournamentsController < ApplicationController
|
||||||
'timer_reason_text is only allowed when timer_reason is custom'
|
'timer_reason_text is only allowed when timer_reason is custom'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def timer_active?
|
||||||
|
@tournament.timestamp.present? && @tournament.timer_mode.present?
|
||||||
|
end
|
||||||
|
|
||||||
def render_timer_validation_error(message)
|
def render_timer_validation_error(message)
|
||||||
render json: { error: message }, status: :unprocessable_content
|
render json: { error: message }, status: :unprocessable_content
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,39 @@ Useful for:
|
||||||
- rehydrating synced follower state
|
- rehydrating synced follower state
|
||||||
- advanced admin tooling
|
- advanced admin tooling
|
||||||
|
|
||||||
|
### Change timer reason without restarting
|
||||||
|
|
||||||
|
After a timer is active, owner may change only the reason fields.
|
||||||
|
Backend keeps current `timestamp` and `timer_mode` unchanged.
|
||||||
|
|
||||||
|
Set a predefined reason:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"timer_reason": "playoff_start"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Set a custom reason:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"timer_reason": "custom",
|
||||||
|
"timer_reason_text": "Opening ceremony ran long"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Remove reason:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"timer_reason": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Response shape is the normal timer state with the same active `timestamp` and
|
||||||
|
`timer_mode`, plus updated reason fields.
|
||||||
|
|
||||||
### Clear timer
|
### Clear timer
|
||||||
|
|
||||||
Request:
|
Request:
|
||||||
|
|
@ -215,6 +248,8 @@ Backend rejects:
|
||||||
- `timer_mode` by itself unless it is `countup`
|
- `timer_mode` by itself unless it is `countup`
|
||||||
- `clear_timer` mixed with timer values
|
- `clear_timer` mixed with timer values
|
||||||
- invalid `timer_reason`
|
- invalid `timer_reason`
|
||||||
|
- reason-only updates when no timer is active
|
||||||
|
- `timer_reason_text` without `timer_reason`
|
||||||
- `timer_reason = "custom"` without `timer_reason_text`
|
- `timer_reason = "custom"` without `timer_reason_text`
|
||||||
- `timer_reason_text` with any non-custom `timer_reason`
|
- `timer_reason_text` with any non-custom `timer_reason`
|
||||||
|
|
||||||
|
|
@ -338,6 +373,7 @@ Implemented coverage:
|
||||||
- API surface timer lifecycle:
|
- API surface timer lifecycle:
|
||||||
- countdown
|
- countdown
|
||||||
- countup
|
- countup
|
||||||
|
- reason-only set / change / remove
|
||||||
- clear
|
- clear
|
||||||
- validation
|
- validation
|
||||||
- [spec/e2e/http/api_surface_spec.rb](../spec/e2e/http/api_surface_spec.rb)
|
- [spec/e2e/http/api_surface_spec.rb](../spec/e2e/http/api_surface_spec.rb)
|
||||||
|
|
|
||||||
|
|
@ -307,6 +307,53 @@ RSpec.describe TournamentsController, type: :controller do
|
||||||
expect(deserialize_response(response)[:timer_reason_text]).to be_nil
|
expect(deserialize_response(response)[:timer_reason_text]).to be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'updates timer reason without changing active timer clock' do
|
||||||
|
original_timestamp = Time.utc(2030, 4, 13, 18, 0, 0)
|
||||||
|
@tournament.update!(
|
||||||
|
timestamp: original_timestamp,
|
||||||
|
timer_mode: 'countdown',
|
||||||
|
timer_reason: 'tournament_start'
|
||||||
|
)
|
||||||
|
|
||||||
|
patch :set_timer, params: {
|
||||||
|
id: @tournament.to_param,
|
||||||
|
timer_reason: 'custom',
|
||||||
|
timer_reason_text: 'Schedule pause'
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response).to be_successful
|
||||||
|
expect(@tournament.reload.timestamp).to eq(original_timestamp)
|
||||||
|
expect(@tournament.timer_mode).to eq('countdown')
|
||||||
|
expect(@tournament.timer_reason).to eq('custom')
|
||||||
|
expect(@tournament.timer_reason_text).to eq('Schedule pause')
|
||||||
|
expect(deserialize_response(response)[:timestamp]).to eq(original_timestamp.iso8601)
|
||||||
|
expect(deserialize_response(response)[:timer_mode]).to eq('countdown')
|
||||||
|
expect(deserialize_response(response)[:timer_reason]).to eq('custom')
|
||||||
|
expect(deserialize_response(response)[:timer_reason_text]).to eq('Schedule pause')
|
||||||
|
|
||||||
|
patch :set_timer, params: {
|
||||||
|
id: @tournament.to_param,
|
||||||
|
timer_reason: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response).to be_successful
|
||||||
|
expect(@tournament.reload.timestamp).to eq(original_timestamp)
|
||||||
|
expect(@tournament.timer_mode).to eq('countdown')
|
||||||
|
expect(@tournament.timer_reason).to be_nil
|
||||||
|
expect(@tournament.timer_reason_text).to be_nil
|
||||||
|
expect(deserialize_response(response)[:timestamp]).to eq(original_timestamp.iso8601)
|
||||||
|
expect(deserialize_response(response)[:timer_mode]).to eq('countdown')
|
||||||
|
expect(deserialize_response(response)[:timer_reason]).to be_nil
|
||||||
|
expect(deserialize_response(response)[:timer_reason_text]).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'rejects timer reason updates when no timer is active' do
|
||||||
|
patch :set_timer, params: { id: @tournament.to_param, timer_reason: 'match_end' }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unprocessable_content)
|
||||||
|
expect(deserialize_response(response)[:error]).to eq('timer_reason requires active timer')
|
||||||
|
end
|
||||||
|
|
||||||
it 'rejects timestamp_seconds for countup mode' do
|
it 'rejects timestamp_seconds for countup mode' do
|
||||||
patch :set_timer, params: { id: @tournament.to_param, timer_mode: 'countup', timestamp_seconds: 120 }
|
patch :set_timer, params: { id: @tournament.to_param, timer_mode: 'countup', timestamp_seconds: 120 }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,13 @@ RSpec.describe 'Backend API surface HTTP E2E' do
|
||||||
timer_reason: 'tournament_start'
|
timer_reason: 'tournament_start'
|
||||||
})
|
})
|
||||||
timer_show = anonymous_client.get("/tournaments/#{tournament.fetch(:id)}/timer")
|
timer_show = anonymous_client.get("/tournaments/#{tournament.fetch(:id)}/timer")
|
||||||
|
timer_reason_custom = owner_client.patch("/tournaments/#{tournament.fetch(:id)}/timer", body: {
|
||||||
|
timer_reason: 'custom',
|
||||||
|
timer_reason_text: 'Pause ran long'
|
||||||
|
})
|
||||||
|
timer_reason_remove = owner_client.patch("/tournaments/#{tournament.fetch(:id)}/timer", body: {
|
||||||
|
timer_reason: nil
|
||||||
|
})
|
||||||
timer_countup = owner_client.patch("/tournaments/#{tournament.fetch(:id)}/timer", body: {
|
timer_countup = owner_client.patch("/tournaments/#{tournament.fetch(:id)}/timer", body: {
|
||||||
timer_mode: 'countup',
|
timer_mode: 'countup',
|
||||||
timer_reason: 'custom',
|
timer_reason: 'custom',
|
||||||
|
|
@ -194,6 +201,16 @@ RSpec.describe 'Backend API surface HTTP E2E' do
|
||||||
expect(timer_show.dig(:json, :timestamp)).to eq(timer_seconds.dig(:json, :timestamp))
|
expect(timer_show.dig(:json, :timestamp)).to eq(timer_seconds.dig(:json, :timestamp))
|
||||||
expect(timer_show.dig(:json, :timer_mode)).to eq('countdown')
|
expect(timer_show.dig(:json, :timer_mode)).to eq('countdown')
|
||||||
expect(timer_show.dig(:json, :timer_reason)).to eq('tournament_start')
|
expect(timer_show.dig(:json, :timer_reason)).to eq('tournament_start')
|
||||||
|
expect(timer_reason_custom[:status]).to eq(200)
|
||||||
|
expect(timer_reason_custom.dig(:json, :timestamp)).to eq(timer_seconds.dig(:json, :timestamp))
|
||||||
|
expect(timer_reason_custom.dig(:json, :timer_mode)).to eq('countdown')
|
||||||
|
expect(timer_reason_custom.dig(:json, :timer_reason)).to eq('custom')
|
||||||
|
expect(timer_reason_custom.dig(:json, :timer_reason_text)).to eq('Pause ran long')
|
||||||
|
expect(timer_reason_remove[:status]).to eq(200)
|
||||||
|
expect(timer_reason_remove.dig(:json, :timestamp)).to eq(timer_seconds.dig(:json, :timestamp))
|
||||||
|
expect(timer_reason_remove.dig(:json, :timer_mode)).to eq('countdown')
|
||||||
|
expect(timer_reason_remove.dig(:json, :timer_reason)).to be_nil
|
||||||
|
expect(timer_reason_remove.dig(:json, :timer_reason_text)).to be_nil
|
||||||
expect(timer_countup[:status]).to eq(200)
|
expect(timer_countup[:status]).to eq(200)
|
||||||
expect(timer_countup.dig(:json, :timestamp)).to be_a(String)
|
expect(timer_countup.dig(:json, :timestamp)).to be_a(String)
|
||||||
expect(timer_countup.dig(:json, :timer_mode)).to eq('countup')
|
expect(timer_countup.dig(:json, :timer_mode)).to eq('countup')
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,35 @@ RSpec.describe 'Tournament timer follower sync websocket HTTP E2E' do
|
||||||
expect(countdown_payload.fetch('timer_reason')).to eq('match_end')
|
expect(countdown_payload.fetch('timer_reason')).to eq('match_end')
|
||||||
expect(countdown_payload.fetch('timer_reason_text')).to be_nil
|
expect(countdown_payload.fetch('timer_reason_text')).to be_nil
|
||||||
|
|
||||||
|
reason_update = source_client.patch("/tournaments/#{source.fetch(:id)}/timer", body: {
|
||||||
|
timer_reason: 'custom',
|
||||||
|
timer_reason_text: 'Follower reason after start'
|
||||||
|
})
|
||||||
|
expect(reason_update[:status]).to eq(200)
|
||||||
|
sync_now_if_requested!(source.fetch(:id))
|
||||||
|
|
||||||
|
reason_payload = wait_for_timer_payload_matching!(cable_client) do |payload|
|
||||||
|
payload.fetch('timestamp') == countdown.dig(:json, :timestamp) &&
|
||||||
|
payload.fetch('timer_mode') == 'countdown' &&
|
||||||
|
payload.fetch('timer_reason') == 'custom' &&
|
||||||
|
payload.fetch('timer_reason_text') == 'Follower reason after start'
|
||||||
|
end
|
||||||
|
expect(reason_payload.fetch('timestamp')).to eq(countdown.dig(:json, :timestamp))
|
||||||
|
|
||||||
|
reason_clear = source_client.patch("/tournaments/#{source.fetch(:id)}/timer", body: {
|
||||||
|
timer_reason: nil
|
||||||
|
})
|
||||||
|
expect(reason_clear[:status]).to eq(200)
|
||||||
|
sync_now_if_requested!(source.fetch(:id))
|
||||||
|
|
||||||
|
reason_cleared_payload = wait_for_timer_payload_matching!(cable_client) do |payload|
|
||||||
|
payload.fetch('timestamp') == countdown.dig(:json, :timestamp) &&
|
||||||
|
payload.fetch('timer_mode') == 'countdown' &&
|
||||||
|
payload.fetch('timer_reason').nil? &&
|
||||||
|
payload.fetch('timer_reason_text').nil?
|
||||||
|
end
|
||||||
|
expect(reason_cleared_payload.fetch('timestamp')).to eq(countdown.dig(:json, :timestamp))
|
||||||
|
|
||||||
countup = source_client.patch("/tournaments/#{source.fetch(:id)}/timer", body: {
|
countup = source_client.patch("/tournaments/#{source.fetch(:id)}/timer", body: {
|
||||||
timer_mode: 'countup',
|
timer_mode: 'countup',
|
||||||
timer_reason: 'custom',
|
timer_reason: 'custom',
|
||||||
|
|
@ -121,10 +150,16 @@ RSpec.describe 'Tournament timer follower sync websocket HTTP E2E' do
|
||||||
end
|
end
|
||||||
|
|
||||||
def wait_for_timer_state!(cable_client, timer_mode:, timeout: 20)
|
def wait_for_timer_state!(cable_client, timer_mode:, timeout: 20)
|
||||||
|
wait_for_timer_payload_matching!(cable_client, timeout: timeout) do |payload|
|
||||||
|
payload.fetch('timer_mode') == timer_mode
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def wait_for_timer_payload_matching!(cable_client, timeout: 20)
|
||||||
deadline = Time.now + timeout
|
deadline = Time.now + timeout
|
||||||
loop do
|
loop do
|
||||||
payload = cable_client.wait_for_timer_payload!(timeout: [deadline - Time.now, 0.1].max)
|
payload = cable_client.wait_for_timer_payload!(timeout: [deadline - Time.now, 0.1].max)
|
||||||
return payload if payload.fetch('timer_mode') == timer_mode
|
return payload if yield(payload)
|
||||||
|
|
||||||
raise "follower timer websocket payload not received within #{timeout}s" if Time.now >= deadline
|
raise "follower timer websocket payload not received within #{timeout}s" if Time.now >= deadline
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,33 @@ RSpec.describe 'Tournament timer websocket HTTP E2E' do
|
||||||
expect(countdown_payload.fetch('timer_reason')).to eq('playoff_start')
|
expect(countdown_payload.fetch('timer_reason')).to eq('playoff_start')
|
||||||
expect(countdown_payload.fetch('timer_reason_text')).to be_nil
|
expect(countdown_payload.fetch('timer_reason_text')).to be_nil
|
||||||
|
|
||||||
|
reason_update = client.patch("/tournaments/#{tournament.fetch(:id)}/timer", body: {
|
||||||
|
timer_reason: 'custom',
|
||||||
|
timer_reason_text: 'Reason changed after start'
|
||||||
|
})
|
||||||
|
expect(reason_update[:status]).to eq(200)
|
||||||
|
|
||||||
|
reason_payload = wait_for_timer_payload_matching!(cable_client) do |payload|
|
||||||
|
payload.fetch('timestamp') == countdown.dig(:json, :timestamp) &&
|
||||||
|
payload.fetch('timer_mode') == 'countdown' &&
|
||||||
|
payload.fetch('timer_reason') == 'custom' &&
|
||||||
|
payload.fetch('timer_reason_text') == 'Reason changed after start'
|
||||||
|
end
|
||||||
|
expect(reason_payload.fetch('timestamp')).to eq(countdown.dig(:json, :timestamp))
|
||||||
|
|
||||||
|
reason_clear = client.patch("/tournaments/#{tournament.fetch(:id)}/timer", body: {
|
||||||
|
timer_reason: nil
|
||||||
|
})
|
||||||
|
expect(reason_clear[:status]).to eq(200)
|
||||||
|
|
||||||
|
reason_cleared_payload = wait_for_timer_payload_matching!(cable_client) do |payload|
|
||||||
|
payload.fetch('timestamp') == countdown.dig(:json, :timestamp) &&
|
||||||
|
payload.fetch('timer_mode') == 'countdown' &&
|
||||||
|
payload.fetch('timer_reason').nil? &&
|
||||||
|
payload.fetch('timer_reason_text').nil?
|
||||||
|
end
|
||||||
|
expect(reason_cleared_payload.fetch('timestamp')).to eq(countdown.dig(:json, :timestamp))
|
||||||
|
|
||||||
countup = client.patch("/tournaments/#{tournament.fetch(:id)}/timer", body: {
|
countup = client.patch("/tournaments/#{tournament.fetch(:id)}/timer", body: {
|
||||||
timer_mode: 'countup',
|
timer_mode: 'countup',
|
||||||
timer_reason: 'custom',
|
timer_reason: 'custom',
|
||||||
|
|
@ -90,10 +117,16 @@ RSpec.describe 'Tournament timer websocket HTTP E2E' do
|
||||||
end
|
end
|
||||||
|
|
||||||
def wait_for_timer_state!(cable_client, timer_mode:, timeout: 20)
|
def wait_for_timer_state!(cable_client, timer_mode:, timeout: 20)
|
||||||
|
wait_for_timer_payload_matching!(cable_client, timeout: timeout) do |payload|
|
||||||
|
payload.fetch('timer_mode') == timer_mode
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def wait_for_timer_payload_matching!(cable_client, timeout: 20)
|
||||||
deadline = Time.now + timeout
|
deadline = Time.now + timeout
|
||||||
loop do
|
loop do
|
||||||
payload = cable_client.wait_for_timer_payload!(timeout: [deadline - Time.now, 0.1].max)
|
payload = cable_client.wait_for_timer_payload!(timeout: [deadline - Time.now, 0.1].max)
|
||||||
return payload if payload.fetch('timer_mode') == timer_mode
|
return payload if yield(payload)
|
||||||
|
|
||||||
raise "timer websocket payload not received within #{timeout}s" if Time.now >= deadline
|
raise "timer websocket payload not received within #{timeout}s" if Time.now >= deadline
|
||||||
end
|
end
|
||||||
|
|
|
||||||
11
tasks.py
11
tasks.py
|
|
@ -633,10 +633,17 @@ def _start_server(command, env, log_path="/tmp/turniere-e2e-server.log"):
|
||||||
return process, log_handle
|
return process, log_handle
|
||||||
|
|
||||||
|
|
||||||
|
def _server_env(env=None):
|
||||||
|
server_env = _env(GIT_COMMIT_SHA=_git_commit_sha())
|
||||||
|
if _rails_env(env) == "test":
|
||||||
|
server_env["WEB_CONCURRENCY"] = os.environ.get("WEB_CONCURRENCY", "1")
|
||||||
|
return server_env
|
||||||
|
|
||||||
|
|
||||||
def _start_test_server(host=SERVER_HOST, port=SERVER_PORT):
|
def _start_test_server(host=SERVER_HOST, port=SERVER_PORT):
|
||||||
return _start_server(
|
return _start_server(
|
||||||
f"{_bundle_exec('rails', 's')} -e test -b {host} -p {port}",
|
f"{_bundle_exec('rails', 's')} -e test -b {host} -p {port}",
|
||||||
_env(GIT_COMMIT_SHA=_git_commit_sha()),
|
_server_env("test"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -704,7 +711,7 @@ def server(c, host=SERVER_HOST, port=SERVER_PORT, env=None):
|
||||||
"""Start the backend locally in development."""
|
"""Start the backend locally in development."""
|
||||||
c.run(
|
c.run(
|
||||||
f"RAILS_ENV={_rails_env(env)} {_bundle_exec('rails', 's')} -b {host} -p {port}",
|
f"RAILS_ENV={_rails_env(env)} {_bundle_exec('rails', 's')} -b {host} -p {port}",
|
||||||
env=_env(GIT_COMMIT_SHA=_git_commit_sha()),
|
env=_server_env(env),
|
||||||
pty=True,
|
pty=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue