diff --git a/.kamal/secrets.staging b/.kamal/secrets.staging new file mode 100644 index 000000000..f7d1a309f --- /dev/null +++ b/.kamal/secrets.staging @@ -0,0 +1,5 @@ +SECRETS=$(kamal secrets fetch --adapter 1password --account basecamp --from Deploy/Fizzy Deployments/BASECAMP_REGISTRY_PASSWORD Staging/RAILS_MASTER_KEY) + +GITHUB_TOKEN=$(gh config get -h github.com oauth_token) +BASECAMP_REGISTRY_PASSWORD=$(kamal secrets extract BASECAMP_REGISTRY_PASSWORD $SECRETS) +RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY $SECRETS) diff --git a/README.md b/README.md index 013638830..57987418c 100644 --- a/README.md +++ b/README.md @@ -45,29 +45,40 @@ For testing OpenAI API requests, we use [VCR](https://github.com/vcr/vcr). If yo that you can get from 1Password in "Fizzy - test.key". Then, when running tests that use Open AI API, you must either set the env variable VCR_RECORD=1 or to add `vcr_record!` to the test. See `VcrTestHelper`. -## Deploying +## Environments Fizzy is deployed with Kamal. You'll need to have the 1Password CLI set up in order to access the secrets that are used when deploying. Provided you have that, it should be as simple as `bin/kamal deploy` to the correct environment. ### Beta -For beta: - - bin/kamal deploy -d beta +Beta is primarily intended for testing product features. Beta tenant is: - https://fizzy.37signals.works/ +This environment uses local disk for Active Storage. + + +### Staging + +Staging is primarily intended for testing infrastructure changes. + +Production tenants are: + +- https://37s.fizzy.37signals-staging.com/ +- https://dev.fizzy.37signals-staging.com/ +- https://qa.fizzy.37signals-staging.com/ + +This environment uses a FlashBlade bucket for blob storage, and shares nothing with Production. We may periodically copy data here from production. + ### Production -And for production: - - bin/kamal deploy -d production - Production tenants are: - https://37s.fizzy.37signals.com/ - https://dev.fizzy.37signals.com/ - https://qa.fizzy.37signals.com/ + +This environment uses a FlashBlade bucket for blob storage. diff --git a/config/cable.yml b/config/cable.yml index 535a25fe7..c4072aeaa 100644 --- a/config/cable.yml +++ b/config/cable.yml @@ -6,14 +6,9 @@ cable: &cable polling_interval: 0.1.seconds message_retention: 1.day -development: - <<: *cable - +development: *cable test: adapter: test - -production: &production - <<: *cable - -beta: - <<: *production +beta: *cable +staging: *cable +production: *cable diff --git a/config/cache.yml b/config/cache.yml index c4fb2c2ae..6801a4eae 100644 --- a/config/cache.yml +++ b/config/cache.yml @@ -10,16 +10,12 @@ default_connection: &default_connection database: writing: :cache -development: +default: &default <<: *default_connection <<: *default_options -test: - <<: *default_options - -production: &production - <<: *default_connection - <<: *default_options - -beta: - <<: *production +development: *default +test: *default_options +beta: *default +staging: *default +production: *default diff --git a/config/credentials/beta.yml.enc b/config/credentials/beta.yml.enc index 71b1e744d..bff4f4de1 100644 --- a/config/credentials/beta.yml.enc +++ b/config/credentials/beta.yml.enc @@ -1 +1 @@ -I2oqW56RTDrRxQ5HUGXGUbSiVoeICiF6tjSo1oxmGuZKE6wu99S+RvQ6Mf0enCQdbHG0osoCpUvu7iuOMEA3X6vO7ku1YFbCDKugu6tY913bzRl166SWZdSlEwtcr6wtmUkXLoWfDzOt98lJ03nU8YbeDC2AzuBqcYWS2qjqpFrrDK8WQI7IukM4iFEDjesW2Noekyy6z+b5+DxB2lkCMFgim4ExW/ndFmrvDnt+QeR+rB9XW9BF6CEg5pPsBQ9EwtICsacCWdvFFOZ8EaNoOuF+hKIHoKP60UCfQj5EUir/r5pGE0Z83z91+UpdpVCuGvh62uSZoZOjdGAplg/DWWKpGcj5JOA7V2yyGEMlz7NueBInG8t/eEyeX/98fAy4TNUxxvncNB+xsmgPJIqlek7vXtYe06AMwrRvac4QSSpUuwWuKUV8JF4gYT5Gp92rzqmmwsTmjvEMiPlnnVYH83GCtd2f1Se7EkDVTvgLA3idnjLvFCelAU4Vdo9/pLqG+0xorSxQ4D/UXqykxtTSjz6pC2P8J1g6T91TQhcH4tDFuZICOYLyFV8F5OD3jPaP+ml01oME42FIKmyhvcNhMLqluUvFqqtOxgWp54jCnde6MBHJ9W7gejwfSmjyD+pr/5NXgaGaTq9h6NMdMkr0Bis6Cc/EgHZKBpJx6ARH1ulcp9uZ9tBjNCePBNxATrJLKwiXh0ZTLDfDTkgdznkj8u+FAKclxjIZ63td3hGe--5kL30BIpNtC7wcsf--YaV9PXL74Wl2ePaAdT5Crg== \ No newline at end of file +/M5cK3B/Wq1R2f4XsQQ8t7qJGCNMaBU3itDDKNZapeGMjrlE4A9CKOh2RPQsKlrsGjJdgGL4YdfDiT1LiF0pD/KL/t5Fw8Z8GMndzA+hmpvQ+hgufvd8n2ZzK/SIK6RmfC9BabdlvuQ0rr3/EdQJ5yIgk3WtWv73bOWDopqHerEAcaNl8KjCu5n2vokAidqEh0/O30yQQB9PAFMRqEwapLVcDSnaJXr6OQnAAm9tIoWaEaIF8ZJH3Rmn0COs8xZqYFyFv/pP3gVy3Mw+VL3aIkEf/eZ3IQWYNf7fiKPvIdNVsmghmeoQRqCp4E0dpn2XDSEGx5i5oq7ynSDCPky4GahiLtpe3vas+Z+lzD5hAAZmlX0G615b4hWjAViTEQU+CMXhmpj7eVpT9co+1oKmqtbDvHr7uCJIf4d17kC13u/8yjgl2SaCjRWx94wh8su/h6h9Xw0qewITbfM32pFRiH+R5bc=--fxg4Lf+qmYeuvyZp--qVZ+ELV0FRzBb69MUAqOsQ== \ No newline at end of file diff --git a/config/credentials/staging.yml.enc b/config/credentials/staging.yml.enc new file mode 100644 index 000000000..8387cc6d8 --- /dev/null +++ b/config/credentials/staging.yml.enc @@ -0,0 +1 @@ +Tc514fYVlm1c2c81KUFmuAGZ9rSZT7XfeGUEdTEvJNDD6JE9IghKbAAGgc6AalIt2/VxzSV6AGvNagb0/qlJrMOFeSJLqzufKt1YoLRUZtSUZDtzmI/2WSzju0Tyu4za6rnLEu+WvDc0NM8KZEv/TwwUbSOwoEVDQmava/iuWyvFr5sOH1wNr2kwYbErwDpThKcYqacI4CdvgpgXc//3T0Z5vsVCs6d8723DymM0KkSK9so8WrcCesk2fmhM1K2hQV7QdckaVuOL0JEj1AI7KzdJm7REoeLwRwaBeVDkqDNo2fKxwZ2XmU2ViJOozPsowx+324wQQZjbsf/0ohtDkgjbfMmA2tjkuNJdJFwIIc5VyvILhVs5WQIJHSXmabhUKxzKRCRZNBSaadLGCb7/AGfULKvh2vP1BWqm80EkDmhHm42ucJW1vA377y9UYWVcdvqWtp5m2F7HlXqi9Uw/+kFV2X6Jw1c07FsCu35TrUb5u8G7mu/866GBgf5VlfDVs1BA3aru05CtcsXi2qKMw0II7fZps/4XpYCB+VehXAyLi2ABVeTnqSfF7HwasXvfD3Jgh7zJLD0hudNDpu6xkmoyeksVBn034dmQf0TwAIKmNjEKMDxgAG50T2JhnEvHZEnqzGl6O8if8DKM5rPgt3XEU3zN/U3yA8T0eTyFdGlWbYK7Ergu4TcGo3WPk6bJhTGOHQ9puD89t6JGHbKDYW77w44SHv+C42flmNkzcndXlpvZvnEafDYeW6ofRC4eeTRX4+WKL4CYhlel78+twD6Mh0MV42MYM2rmtl66NHN8fWxyfoyjrNcXveKVAsGMatB0+dOglq6GmOiXRAt/I6D+7QYZLyaUhYNmmiuZXF4=--H6KWPnHc9qUQE5AI--PN98cg9t9ZhxMUEax/Mfgg== \ No newline at end of file diff --git a/config/database.yml b/config/database.yml index a242b1555..0ab08729c 100644 --- a/config/database.yml +++ b/config/database.yml @@ -63,5 +63,5 @@ production: &production database: storage/production_queue.sqlite3 migrations_paths: db/queue_migrate -beta: - <<: *production +beta: *production +staging: *production diff --git a/config/deploy.staging.yml b/config/deploy.staging.yml new file mode 100644 index 000000000..61c673377 --- /dev/null +++ b/config/deploy.staging.yml @@ -0,0 +1,58 @@ +servers: + web: + hosts: + - fizzy-staging-app-01 + jobs: + hosts: + - fizzy-staging-app-01 + web-replica: + hosts: + - fizzy-staging-app-101 + proxy: true + +proxy: + ssl: false + +ssh: + user: app + +env: + clear: + RAILS_ENV: staging + +x-beamer-accessory: &beamer-accessory + image: basecamp/beamer + registry: + username: bcbot + password: + - BASECAMP_REGISTRY_PASSWORD + options: + user: 1000 # Match the UID of the Rails app, for volume permissions + volumes: + - fizzy:/storage + + +accessories: + beamer-primary: + <<: *beamer-accessory + service: beamer-primary + cmd: beamer primary --remove-after=1h --dir=/storage --disable-tls + port: 5000:80 + roles: + - web + + beamer-replica: + <<: *beamer-accessory + service: beamer-replica + cmd: beamer replica --primary=http://fizzy-staging-app-01:5000/ --dir=/storage + roles: + - web-replica + + load-balancer: + image: basecamp/kamal-proxy:lb + roles: + - web + options: + publish: + - 80:80 + - 443:443 diff --git a/config/environments/staging.rb b/config/environments/staging.rb new file mode 100644 index 000000000..27868fd80 --- /dev/null +++ b/config/environments/staging.rb @@ -0,0 +1,5 @@ +require_relative "production" + +Rails.application.configure do + config.action_mailer.default_url_options = { host: "%{tenant}.fizzy.37signals-staging.com" } +end diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb index c1944f8dd..4226de1d9 100644 --- a/config/initializers/sentry.rb +++ b/config/initializers/sentry.rb @@ -1,4 +1,4 @@ -if (Rails.env.production? || Rails.env.beta?) && ENV["SKIP_TELEMETRY"].blank? +if !Rails.env.local? && ENV["SKIP_TELEMETRY"].blank? Sentry.init do |config| config.dsn = "https://ca338fb1fe6f677d6aeec2336a86f0ee@o33603.ingest.us.sentry.io/4508093839179776" config.breadcrumbs_logger = %i[ active_support_logger http_logger ] diff --git a/config/initializers/tenanting.rb b/config/initializers/tenanting.rb index 5ada06981..78e5d8e05 100644 --- a/config/initializers/tenanting.rb +++ b/config/initializers/tenanting.rb @@ -1,10 +1,10 @@ Rails.application.config.after_initialize do - # in production, we're using a two-level subdomain like "tenant.fizzy.37signals.com", but in - # development and beta it's only a single-level subdomain. + # in production and staging, we're using a two-level subdomain like "tenant.fizzy.37signals.com". + # in development and beta it's only a single-level subdomain. Rails.application.config.active_record_tenanted.tenant_resolver = ->(request) do next nil if request.path == "/up" - tld_length = request.domain == "37signals.com" ? 2 : 1 + tld_length = [ "37signals.com", "37signals-staging.com" ].include?(request.domain) ? 2 : 1 request.subdomain(tld_length).presence || "return-404" # this is a quick fix for now end end diff --git a/config/queue.yml b/config/queue.yml index ecab73581..6a199e535 100644 --- a/config/queue.yml +++ b/config/queue.yml @@ -8,14 +8,8 @@ default: &default processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %> polling_interval: 0.1 -development: - <<: *default - -test: - <<: *default - -production: &production - <<: *default - -beta: - <<: *production +development: *default +test: *default +beta: *default +staging: *default +production: *default diff --git a/config/recurring.yml b/config/recurring.yml index 6862cfd5c..27b9898e4 100644 --- a/config/recurring.yml +++ b/config/recurring.yml @@ -15,5 +15,5 @@ production: &production class: DeleteUnusedTagsJob schedule: every day at 04:00 -beta: - <<: *production +beta: *production +staging: *production