diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ddfcbbeb..eadd69b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,7 @@ on: jobs: lint: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Checkout code uses: actions/checkout@v5 @@ -23,6 +24,7 @@ jobs: test: runs-on: ubuntu-latest + timeout-minutes: 10 services: postgres: diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 89661066..6f5f4bd3 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -9,6 +9,7 @@ on: jobs: build: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: Checkout code uses: actions/checkout@v5 @@ -18,18 +19,3 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - - name: Login to Docker Hub - if: github.ref == 'refs/heads/main' && github.event_name == 'push' - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Build and push Docker image - uses: docker/build-push-action@v5 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }} - tags: chriszhu12/canine:latest \ No newline at end of file diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 0cb21945..2f2f8a2a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -9,6 +9,7 @@ on: jobs: build: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@v5 - name: Set up Docker Buildx diff --git a/Gemfile b/Gemfile index 6fab723d..c5a7131f 100644 --- a/Gemfile +++ b/Gemfile @@ -111,3 +111,7 @@ gem "sys-proctable", "~> 1.3" gem "omniauth-gitlab", "~> 4.1" gem "actioncable-enhanced-postgresql-adapter", "~> 1.0" + +gem 'flipper', '~> 1.2.2' +gem 'flipper-active_record', '~> 1.2.2' +gem 'flipper-ui', '~> 1.2.2' diff --git a/Gemfile.lock b/Gemfile.lock index 58e6aa2e..21da909a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -218,6 +218,17 @@ GEM ffi-compiler (1.3.2) ffi (>= 1.15.5) rake + flipper (1.2.2) + concurrent-ruby (< 2) + flipper-active_record (1.2.2) + activerecord (>= 4.2, < 8) + flipper (~> 1.2.2) + flipper-ui (1.2.2) + erubi (>= 1.0.0, < 2.0.0) + flipper (~> 1.2.2) + rack (>= 1.4, < 4) + rack-protection (>= 1.5.3, <= 4.0.0) + sanitize (< 7) friendly_id (5.5.1) activerecord (>= 4.0.0) fugit (1.11.1) @@ -521,6 +532,9 @@ GEM ffi (~> 1.12) logger rubyzip (2.4.1) + sanitize (6.1.3) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) sassc (2.4.0) ffi (~> 1.9) sassc-rails (2.1.2) @@ -645,6 +659,9 @@ DEPENDENCIES dotenv (~> 3.1) factory_bot_rails faker (~> 3.5.2) + flipper (~> 1.2.2) + flipper-active_record (~> 1.2.2) + flipper-ui (~> 1.2.2) friendly_id (~> 5.4) good_job (~> 4.0) httparty (~> 0.22.0) diff --git a/TODO.md b/TODO.md index 1a5dfc7c..9d2988e8 100644 --- a/TODO.md +++ b/TODO.md @@ -15,4 +15,4 @@ - [ ] Deployments API - [ ] Pull request preview apps - [ ] Update vocabulary on landing page -- [ ] Extend docker hub support to any arbitrary container registry +- [ ] Clear our historical logs \ No newline at end of file diff --git a/app/views/accounts/account_users/index.html.erb b/app/views/accounts/account_users/index.html.erb index 08417bad..695ebbd4 100644 --- a/app/views/accounts/account_users/index.html.erb +++ b/app/views/accounts/account_users/index.html.erb @@ -1,5 +1,5 @@ <%= content_for :title, "Users" %> -<%= turbo_stream_from :account_users %> +<%= turbo_stream_from [:account_users, current_account] %>
diff --git a/app/views/add_ons/index.html.erb b/app/views/add_ons/index.html.erb index a5009a19..26320955 100644 --- a/app/views/add_ons/index.html.erb +++ b/app/views/add_ons/index.html.erb @@ -1,5 +1,5 @@ <%= content_for :title, "Add Ons" %> -<%= turbo_stream_from :add_ons %> +<%= turbo_stream_from [:add_ons, current_account] %>
diff --git a/app/views/clusters/index.html.erb b/app/views/clusters/index.html.erb index 94b03da7..b437c0db 100644 --- a/app/views/clusters/index.html.erb +++ b/app/views/clusters/index.html.erb @@ -1,5 +1,5 @@ <%= content_for :title, "Clusters" %> -<%= turbo_stream_from :clusters %> +<%= turbo_stream_from [:clusters, current_account] %>
diff --git a/app/views/shared/_default_right_nav.html.erb b/app/views/shared/_default_right_nav.html.erb index cc0907eb..e57f24a8 100644 --- a/app/views/shared/_default_right_nav.html.erb +++ b/app/views/shared/_default_right_nav.html.erb @@ -30,6 +30,14 @@ <% end %>
+
  • +
    + <%= link_to admin_flipper_path do %> + + Feature Flags + <% end %> +
    +
  • <%= link_to admin_good_job_path do %> diff --git a/config/database.yml b/config/database.yml index 405e8421..e4ac1011 100644 --- a/config/database.yml +++ b/config/database.yml @@ -17,12 +17,13 @@ default: &default encoding: unicode # For details on connection pooling, see Rails configuration guide # https://guides.rubyonrails.org/configuring.html#database-pooling - pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + # https://island94.org/2024/09/secret-to-rails-database-connection-pool-size + pool: <%= ENV.fetch("DB_POOL_SIZE", 100).to_i %> docker: &docker adapter: postgresql encoding: unicode - pool: 5 + pool: <%= ENV.fetch("DB_POOL_SIZE", 100).to_i %> username: postgres password: password host: postgres @@ -94,4 +95,4 @@ production: <<: *default database: canine_production username: canine - password: <%= ENV["CANINE_DATABASE_PASSWORD"] %> \ No newline at end of file + password: <%= ENV["CANINE_DATABASE_PASSWORD"] %> diff --git a/config/initializers/flipper.rb b/config/initializers/flipper.rb new file mode 100644 index 00000000..52c07d61 --- /dev/null +++ b/config/initializers/flipper.rb @@ -0,0 +1,45 @@ +Rails.application.configure do + ## Memoization ensures that only one adapter call is made per feature per request. + ## For more info, see https://www.flippercloud.io/docs/optimization#memoization + # config.flipper.memoize = true + + ## Flipper preloads all features before each request, which is recommended if: + ## * you have a limited number of features (< 100?) + ## * most of your requests depend on most of your features + ## * you have limited gate data combined across all features (< 1k enabled gates, like individual actors, across all features) + ## + ## For more info, see https://www.flippercloud.io/docs/optimization#preloading + # config.flipper.preload = true + + ## Warn or raise an error if an unknown feature is checked + ## Can be set to `:warn`, `:raise`, or `false` + # config.flipper.strict = Rails.env.development? && :warn + + ## Show Flipper checks in logs + # config.flipper.log = true + + ## Reconfigure Flipper to use the Memory adapter and disable Cloud in tests + # config.flipper.test_help = true + + ## The path that Flipper Cloud will use to sync features + # config.flipper.cloud_path = "_flipper" + + ## The instrumenter that Flipper will use. Defaults to ActiveSupport::Notifications. + # config.flipper.instrumenter = ActiveSupport::Notifications +end + +Flipper.configure do |config| + ## Configure other adapters that you want to use here: + ## See http://flippercloud.io/docs/adapters + # config.use Flipper::Adapters::ActiveSupportCacheStore, Rails.cache, expires_in: 5.minutes +end + +## Register a group that can be used for enabling features. +## +## Flipper.enable_group :my_feature, :admins +## +## See https://www.flippercloud.io/docs/features#enablement-group +# +# Flipper.register(:admins) do |actor| +# actor.respond_to?(:admin?) && actor.admin? +# end diff --git a/config/routes.rb b/config/routes.rb index ab32b091..d95133e3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -93,6 +93,7 @@ Rails.application.routes.draw do authenticate :user, lambda { |u| u.admin? } do namespace :admin do + mount Flipper::UI.app(Flipper) => '/flipper', as: :flipper mount GoodJob::Engine => "/good_job" end end diff --git a/db/migrate/20250818215548_create_flipper_tables.rb b/db/migrate/20250818215548_create_flipper_tables.rb new file mode 100644 index 00000000..811f528c --- /dev/null +++ b/db/migrate/20250818215548_create_flipper_tables.rb @@ -0,0 +1,22 @@ +class CreateFlipperTables < ActiveRecord::Migration[7.2] + def up + create_table :flipper_features do |t| + t.string :key, null: false + t.timestamps null: false + end + add_index :flipper_features, :key, unique: true + + create_table :flipper_gates do |t| + t.string :feature_key, null: false + t.string :key, null: false + t.text :value + t.timestamps null: false + end + add_index :flipper_gates, [ :feature_key, :key, :value ], unique: true, length: { value: 255 } + end + + def down + drop_table :flipper_gates + drop_table :flipper_features + end +end diff --git a/db/schema.rb b/db/schema.rb index c4db0953..b541e11d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -153,6 +153,22 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_19_200812) do t.index ["user_id"], name: "index_events_on_user_id" end + create_table "flipper_features", force: :cascade do |t| + t.string "key", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["key"], name: "index_flipper_features_on_key", unique: true + end + + create_table "flipper_gates", force: :cascade do |t| + t.string "feature_key", null: false + t.string "key", null: false + t.text "value" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["feature_key", "key", "value"], name: "index_flipper_gates_on_feature_key_and_key_and_value", unique: true + end + create_table "friendly_id_slugs", force: :cascade do |t| t.string "slug", null: false t.integer "sluggable_id", null: false diff --git a/resources/k8/scripts/install_nginx_ingress.sh b/resources/k8/scripts/install_nginx_ingress.sh index 09900140..4dcb5254 100644 --- a/resources/k8/scripts/install_nginx_ingress.sh +++ b/resources/k8/scripts/install_nginx_ingress.sh @@ -11,6 +11,7 @@ helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \ --set controller.config.proxy-body-size=0 \ --set controller.config.proxy-buffer-size=16k \ --set controller.config.proxy-buffers-number=8 \ + --set controller.config.proxy-busy-buffers-size=32k \ --set controller.config.proxy-read-timeout=3600 \ --set controller.config.proxy-send-timeout=3600 \ --set controller.config.h2-backend=true \