From 01e6d848eeb58c4b108a23fcf4541f30f944eda1 Mon Sep 17 00:00:00 2001 From: Lodewiges Date: Fri, 12 Dec 2025 18:13:39 +0100 Subject: [PATCH] make it easier to deploy a new env --- .github/workflows/continuous-delivery.yml | 72 ++++++++++++------- Dockerfile | 5 +- app/controllers/application_controller.rb | 1 + app/controllers/concerns/environment_aware.rb | 30 ++++++++ .../credit_mutations_controller.rb | 4 +- app/jobs/application_job.rb | 1 + .../credit_insufficient_notification_job.rb | 2 +- app/jobs/payment_poll_job.rb | 2 +- app/models/application_record.rb | 2 + app/views/partials/_footer.html.erb | 4 +- config/deploy.rb | 21 +++--- config/deploy_targets.yml | 46 ++++++++++++ config/initializers/deploy_target_config.rb | 30 ++++++++ config/initializers/sentry.rb | 6 +- spec/rails_helper.rb | 4 +- 15 files changed, 182 insertions(+), 48 deletions(-) create mode 100644 app/controllers/concerns/environment_aware.rb create mode 100644 config/deploy_targets.yml create mode 100644 config/initializers/deploy_target_config.rb diff --git a/.github/workflows/continuous-delivery.yml b/.github/workflows/continuous-delivery.yml index 61a7dffc5..e6c48f81a 100644 --- a/.github/workflows/continuous-delivery.yml +++ b/.github/workflows/continuous-delivery.yml @@ -33,6 +33,9 @@ jobs: name: Branch Check runs-on: ubuntu-latest steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Validate branch env: TARGET_ENV: ${{ github.event.inputs.target_env }} @@ -41,11 +44,19 @@ jobs: echo 'This workflow can only be run on branches staging and master.' exit 1 fi - if [ "$TARGET_ENV" == 'luxadmosam' ] || [ "$TARGET_ENV" == 'euros' ]; then - if [ "$GITHUB_REF_NAME" != 'master' ]; then - echo 'Only the master branch can be deployed to external parties.' - exit 1 - fi + # Check branch restriction from deploy_targets.yml + BRANCH_RESTRICTION=$(python3 << 'PYTHON_EOF' + import yaml + with open('config/deploy_targets.yml', 'r') as f: + config = yaml.safe_load(f) + target = config['targets']['${{ github.event.inputs.target_env }}'] + restriction = target.get('branch_restriction') + print(restriction if restriction else '') + PYTHON_EOF + ) + if [ ! -z "$BRANCH_RESTRICTION" ] && [ "$GITHUB_REF_NAME" != "$BRANCH_RESTRICTION" ]; then + echo "Target $TARGET_ENV can only be deployed from branch $BRANCH_RESTRICTION, but current branch is $GITHUB_REF_NAME." + exit 1 fi metadata: @@ -74,18 +85,25 @@ jobs: echo 'has_diff=false' >> "$GITHUB_OUTPUT" fi fi - - if [ "$TARGET_ENV" == 'luxadmosam' ]; then - echo 'stage=luxproduction' >> "$GITHUB_OUTPUT" - elif [ "$TARGET_ENV" == 'euros' ]; then - echo 'stage=euros' >> "$GITHUB_OUTPUT" - else - echo 'stage=production' >> "$GITHUB_OUTPUT" - fi - else - echo 'stage=staging' >> "$GITHUB_OUTPUT" fi + # Get stage from deploy_targets.yml + STAGE=$(python3 << 'PYTHON_EOF' + import yaml + with open('config/deploy_targets.yml', 'r') as f: + config = yaml.safe_load(f) + target_env = '${{ github.event.inputs.target_env }}' + current_branch = '${{ github.ref_name }}' + # If on staging branch, always use staging stage unless targeting external party + if current_branch == 'staging': + print('staging') + else: + # On master: use the stage from the target environment + print(config['targets'][target_env]['stage']) + PYTHON_EOF + ) + echo "stage=$STAGE" >> "$GITHUB_OUTPUT" + merge: name: Merge runs-on: ubuntu-latest @@ -162,15 +180,21 @@ jobs: env: TARGET_ENV: ${{ github.event.inputs.target_env }} run: | - if [ "$TARGET_ENV" == 'luxadmosam' ] && [ "$GITHUB_REF_NAME" = 'master' ]; then - echo 'environment_url=https://luxstreep.csvalpha.nl' >> "$GITHUB_OUTPUT" - elif [ "$TARGET_ENV" == 'euros' ] && [ "$GITHUB_REF_NAME" = 'master' ]; then - echo 'environment_url=https://euros.csvalpha.nl' >> "$GITHUB_OUTPUT" - elif [ "$GITHUB_REF_NAME" = 'master' ]; then - echo 'environment_url=https://streep.csvalpha.nl' >> "$GITHUB_OUTPUT" - else - echo 'environment_url=https://stagingstreep.csvalpha.nl' >> "$GITHUB_OUTPUT" - fi + # Get URL from deploy_targets.yml based on current branch and target + URL=$(python3 << 'PYTHON_EOF' + import yaml + with open('config/deploy_targets.yml', 'r') as f: + config = yaml.safe_load(f) + current_branch = '${{ github.ref_name }}' + target_env = '${{ github.event.inputs.target_env }}' + # If on staging, use stagingstreep URL; otherwise use the target's URL + if current_branch == 'staging': + print(config['targets']['stagingstreep']['url']) + else: + print(config['targets'][target_env]['url']) + PYTHON_EOF + ) + echo "environment_url=$URL" >> "$GITHUB_OUTPUT" - name: Checkout code uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 diff --git a/Dockerfile b/Dockerfile index c24012d84..b7b88f230 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,7 +28,8 @@ WORKDIR /app # Pre-install gems, so that they can be cached. COPY Gemfile* /app/ -RUN if [ "$RAILS_ENV" = 'production' ] || [ "$RAILS_ENV" = 'staging' ] || [ "$RAILS_ENV" = 'luxproduction' ] || [ "$RAILS_ENV" = 'euros' ]; then \ +COPY config/deploy_targets.yml /app/config/ +RUN if ruby -e "require 'yaml'; targets = YAML.load_file('config/deploy_targets.yml')['targets'].keys; exit(targets.include?(ENV['RAILS_ENV']) ? 0 : 1)"; then \ bundle config set --local without 'development test'; \ else \ bundle config set --local without 'development'; \ @@ -43,7 +44,7 @@ RUN yarn install --immutable COPY . /app/ # Precompile assets after copying app because whole Rails pipeline is needed. -RUN if [ "$RAILS_ENV" = 'production' ] || [ "$RAILS_ENV" = 'staging' ] || [ "$RAILS_ENV" = 'luxproduction' ] || [ "$RAILS_ENV" = 'euros' ]; then \ +RUN if ruby -e "require 'yaml'; targets = YAML.load_file('config/deploy_targets.yml')['targets'].keys; exit(targets.include?(ENV['RAILS_ENV']) ? 0 : 1)"; then \ SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile; \ else \ echo "Skipping assets:precompile"; \ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 226c7e3b4..09507463d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,6 @@ class ApplicationController < ActionController::Base include Pundit::Authorization + include EnvironmentAware protect_from_forgery with: :exception, prepend: true diff --git a/app/controllers/concerns/environment_aware.rb b/app/controllers/concerns/environment_aware.rb new file mode 100644 index 000000000..ff5a67746 --- /dev/null +++ b/app/controllers/concerns/environment_aware.rb @@ -0,0 +1,30 @@ +module EnvironmentAware + extend ActiveSupport::Concern + + included do + class_variable_set(:@@deploy_targets, nil) + end + + def production_deployed? + self.class.production_deployed? + end + + class_methods do + def production_deployed? + Rails.env.development? || Rails.env.test? ? false : deployed_environments.include?(Rails.env.to_sym) + end + + def deployed_environments + load_deploy_targets.keys.map(&:to_sym) + end + + private + + def load_deploy_targets + @@deploy_targets ||= begin + config_path = Rails.root.join('config', 'deploy_targets.yml') + YAML.load_file(config_path)['targets'] + end + end + end +end diff --git a/app/controllers/credit_mutations_controller.rb b/app/controllers/credit_mutations_controller.rb index beaa668c4..76d85c3a4 100644 --- a/app/controllers/credit_mutations_controller.rb +++ b/app/controllers/credit_mutations_controller.rb @@ -19,9 +19,7 @@ def create # rubocop:disable Metrics/MethodLength, Metrics/AbcSize respond_to do |format| if @mutation.save - if Rails.env.production? || Rails.env.staging? || Rails.env.luxproduction? || Rails.env.euros? - NewCreditMutationNotificationJob.perform_later(@mutation) - end + NewCreditMutationNotificationJob.perform_later(@mutation) if production_deployed? format.html { redirect_to which_redirect?, flash: { success: 'Inleg of mutatie aangemaakt' } } format.json do render json: @mutation, include: { user: { methods: User.orderscreen_json_includes } } diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb index a009ace51..8f41e0c85 100644 --- a/app/jobs/application_job.rb +++ b/app/jobs/application_job.rb @@ -1,2 +1,3 @@ class ApplicationJob < ActiveJob::Base + include EnvironmentAware end diff --git a/app/jobs/credit_insufficient_notification_job.rb b/app/jobs/credit_insufficient_notification_job.rb index 2984515ff..47db23f4b 100644 --- a/app/jobs/credit_insufficient_notification_job.rb +++ b/app/jobs/credit_insufficient_notification_job.rb @@ -30,7 +30,7 @@ def send_notification_delivery_reports(success_count, unnotifyable_users) ).deliver_later end - return unless Rails.env.production? || Rails.env.staging? || Rails.env.luxproduction? || Rails.env.euros? + return unless production_deployed? HealthCheckJob.perform_later('credit_insufficient') end diff --git a/app/jobs/payment_poll_job.rb b/app/jobs/payment_poll_job.rb index f897f48f7..b50f5e020 100644 --- a/app/jobs/payment_poll_job.rb +++ b/app/jobs/payment_poll_job.rb @@ -9,7 +9,7 @@ def perform # it will be checked again the next time this poll job runs end - return unless Rails.env.production? || Rails.env.staging? || Rails.env.luxproduction? || Rails.env.euros? + return unless production_deployed? HealthCheckJob.perform_later('payment_poll') end diff --git a/app/models/application_record.rb b/app/models/application_record.rb index d6b4851de..306f825d2 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,4 +1,6 @@ class ApplicationRecord < ActiveRecord::Base + include EnvironmentAware + self.abstract_class = true acts_as_paranoid diff --git a/app/views/partials/_footer.html.erb b/app/views/partials/_footer.html.erb index 1cec111ac..6b9d3ec8a 100644 --- a/app/views/partials/_footer.html.erb +++ b/app/views/partials/_footer.html.erb @@ -1,8 +1,8 @@