diff --git a/app/actions/environment_variables/bulk_update.rb b/app/actions/environment_variables/bulk_update.rb index 4b716489..d6b015cd 100644 --- a/app/actions/environment_variables/bulk_update.rb +++ b/app/actions/environment_variables/bulk_update.rb @@ -2,18 +2,30 @@ class EnvironmentVariables::BulkUpdate extend LightService::Action expects :project, :params + expects :current_user, default: nil executed do |context| project = context.project - # Delete all environment variables and create new ones + env_variable_data = context.params[:environment_variables] - project.environment_variables.destroy_all + incoming_variable_names = env_variable_data.map { |ev| ev[:name] } + current_variable_names = project.environment_variables.pluck(:name) - (context.params[:environment_variables] || []).each do |environment_variable_params| - next if environment_variable_params[:name].blank? - name = environment_variable_params[:name].strip.upcase - value = environment_variable_params[:value].strip - project.environment_variables.create!(name:, value:) + new_names = incoming_variable_names - current_variable_names + + if new_names.any? + env_variable_data.filter { |ev| new_names.include?(ev[:name]) }.each do |ev| + next if ev[:name].blank? + project.environment_variables.create!( + name: ev[:name].strip.upcase, + value: ev[:value].strip, + current_user: context.current_user + ) + end end + + destroy_names = current_variable_names - incoming_variable_names + + project.environment_variables.where(name: destroy_names).destroy_all if destroy_names.any? end -end \ No newline at end of file +end diff --git a/app/actions/projects/deploy_latest_commit.rb b/app/actions/projects/deploy_latest_commit.rb index 788ff66a..6e06bb40 100644 --- a/app/actions/projects/deploy_latest_commit.rb +++ b/app/actions/projects/deploy_latest_commit.rb @@ -2,18 +2,20 @@ class Projects::DeployLatestCommit extend LightService::Action expects :project + expects :current_user, default: nil promises :project executed do |context| # Fetch the latest commit from the default branch project = context.project + current_user = context.current_user || project.account.owner client = Octokit::Client.new(access_token: project.account.github_access_token) commit = client.commits(project.repository_url).first - build = Build.create!( - project: project, + build = project.builds.create!( commit_sha: commit.sha, - commit_message: commit.commit[:message] + commit_message: commit.commit[:message], + current_user: current_user ) Projects::BuildJob.perform_later(build) end diff --git a/app/controllers/inbound_webhooks/github_controller.rb b/app/controllers/inbound_webhooks/github_controller.rb index 3b396d5c..13469b85 100644 --- a/app/controllers/inbound_webhooks/github_controller.rb +++ b/app/controllers/inbound_webhooks/github_controller.rb @@ -7,7 +7,7 @@ module InboundWebhooks record = InboundWebhook.create(body: payload) # Queue webhook for processing - InboundWebhooks::GithubJob.perform_later(record) + InboundWebhooks::GithubJob.perform_later(record, current_user:) # Tell service we received the webhook successfully head :ok diff --git a/app/controllers/projects/deployments_controller.rb b/app/controllers/projects/deployments_controller.rb index 1c196a90..c0f4fdf9 100644 --- a/app/controllers/projects/deployments_controller.rb +++ b/app/controllers/projects/deployments_controller.rb @@ -11,6 +11,7 @@ class Projects::DeploymentsController < Projects::BaseController def redeploy new_build = @build.dup.tap do |build| build.status = :in_progress + build.current_user = current_user end if new_build.save Projects::BuildJob.perform_later(new_build) @@ -21,7 +22,7 @@ class Projects::DeploymentsController < Projects::BaseController end def deploy - result = Projects::DeployLatestCommit.execute(project: @project) + result = Projects::DeployLatestCommit.execute(project: @project, current_user:) if result.success? redirect_to @project, notice: "Deploying project..." else diff --git a/app/helpers/eventable.rb b/app/helpers/eventable.rb new file mode 100644 index 00000000..2c1867f2 --- /dev/null +++ b/app/helpers/eventable.rb @@ -0,0 +1,17 @@ +module Eventable + extend ActiveSupport::Concern + + included do + attr_accessor :current_user + has_many :events, as: :eventable, dependent: :destroy + after_save :create_event + end + + def create_event + events.create!( + user: current_user, + event_action: id_changed? ? :create : :update, + project: + ) if current_user + end +end diff --git a/app/jobs/inbound_webhooks/github_job.rb b/app/jobs/inbound_webhooks/github_job.rb index 48de87a7..dc54c1f7 100644 --- a/app/jobs/inbound_webhooks/github_job.rb +++ b/app/jobs/inbound_webhooks/github_job.rb @@ -2,14 +2,14 @@ module InboundWebhooks class GithubJob < ApplicationJob queue_as :default - def perform(inbound_webhook) + def perform(inbound_webhook, current_user: nil) inbound_webhook.processing! # Process webhook # Determine the project # Trigger a docker build & docker deploy if auto deploy is on for the project body = JSON.parse(inbound_webhook.body) - process_webhook(body) + process_webhook(body, current_user:) inbound_webhook.processed! @@ -17,16 +17,16 @@ module InboundWebhooks # inbound_webhook.failed! end - def process_webhook(body) - return if body['pusher'].nil? - branch = body['ref'].gsub('refs/heads/', '') - projects = Project.where(repository_url: body['repository']['full_name'], branch: branch, autodeploy: true) + def process_webhook(body, current_user:) + return if body["pusher"].nil? + branch = body["ref"].gsub("refs/heads/", "") + projects = Project.where(repository_url: body["repository"]["full_name"], branch: branch, autodeploy: true) projects.each do |project| # Trigger a docker build & docker deploy - build = Build.create!( - project_id: project.id, - commit_sha: body['head_commit']['id'], - commit_message: body['head_commit']['message'] + build = project.builds.create!( + current_user:, + commit_sha: body["head_commit"]["id"], + commit_message: body["head_commit"]["message"] ) Projects::BuildJob.perform_later(build) end diff --git a/app/models/build.rb b/app/models/build.rb index 290d4075..dd0951fb 100644 --- a/app/models/build.rb +++ b/app/models/build.rb @@ -22,8 +22,11 @@ # class Build < ApplicationRecord include Loggable + include Eventable + belongs_to :project has_one :deployment, dependent: :destroy + enum status: { in_progress: 0, completed: 1, diff --git a/app/models/environment_variable.rb b/app/models/environment_variable.rb index d751b850..3408e0a8 100644 --- a/app/models/environment_variable.rb +++ b/app/models/environment_variable.rb @@ -19,6 +19,8 @@ # fk_rails_... (project_id => projects.id) # class EnvironmentVariable < ApplicationRecord + include Eventable + belongs_to :project validates :name, presence: true, uniqueness: { scope: :project_id } diff --git a/app/models/event.rb b/app/models/event.rb new file mode 100644 index 00000000..3f627fc2 --- /dev/null +++ b/app/models/event.rb @@ -0,0 +1,28 @@ +# == Schema Information +# +# Table name: events +# +# id :bigint not null, primary key +# event_action :integer not null +# eventable_type :string not null +# created_at :datetime not null +# updated_at :datetime not null +# eventable_id :bigint not null +# project_id :bigint not null +# user_id :bigint not null +# +# Indexes +# +# index_events_on_eventable (eventable_type,eventable_id) +# index_events_on_project_id (project_id) +# index_events_on_user_id (user_id) +# +class Event < ApplicationRecord + belongs_to :eventable, polymorphic: true + belongs_to :user + belongs_to :project + enum event_action: { + create: 0, + update: 1 + }, _prefix: true +end diff --git a/app/models/project.rb b/app/models/project.rb index 6c60d540..96e9d41c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -34,9 +34,11 @@ class Project < ApplicationRecord has_many :builds, dependent: :destroy has_many :deployments, through: :builds has_many :domains, through: :services + has_many :events validates :name, presence: true, format: { with: /\A[a-z0-9_-]+\z/, message: "must be lowercase, numbers, hyphens, and underscores only" } + validates_uniqueness_of :name, scope: :cluster_id enum :status, { diff --git a/app/views/projects/deployments/index.html.erb b/app/views/projects/deployments/index.html.erb index 505a059b..b7d2d952 100644 --- a/app/views/projects/deployments/index.html.erb +++ b/app/views/projects/deployments/index.html.erb @@ -28,6 +28,9 @@ Deployed At + + User + @@ -65,6 +68,13 @@
<%= distance_of_time_in_words(build.created_at, Time.current) %> ago
+ + <% if build.events.present? %> +
+ <%= build.events.last.user.email %> +
+ <% end %> +
<%= button_to redeploy_project_deployment_path(@project, build), method: :post, class: "btn btn-primary btn-sm min-w-max" do %> diff --git a/config/routes.rb b/config/routes.rb index d403df35..9d8d9df3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -36,7 +36,7 @@ Rails.application.routes.draw do end resources :metrics, only: [ :index ], module: :projects resources :project_add_ons, only: %i[create destroy], module: :projects - resources :environment_variables, only: %i[index create], module: :projects + resources :environment_variables, only: %i[index create destroy], module: :projects resources :deployments, only: %i[index show], module: :projects do collection do post :deploy diff --git a/db/migrate/20241023225911_create_events.rb b/db/migrate/20241023225911_create_events.rb new file mode 100644 index 00000000..1c347d61 --- /dev/null +++ b/db/migrate/20241023225911_create_events.rb @@ -0,0 +1,11 @@ +class CreateEvents < ActiveRecord::Migration[7.2] + def change + create_table :events do |t| + t.references :user, null: false + t.references :project, null: false + t.references :eventable, polymorphic: true, null: false + t.integer :event_action, null: false + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 96ccfb64..20052c83 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_10_09_172011) do +ActiveRecord::Schema[7.2].define(version: 2024_10_23_225911) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -134,6 +134,19 @@ ActiveRecord::Schema[7.2].define(version: 2024_10_09_172011) do t.index ["project_id"], name: "index_environment_variables_on_project_id" end + create_table "events", force: :cascade do |t| + t.bigint "user_id", null: false + t.bigint "project_id", null: false + t.string "eventable_type", null: false + t.bigint "eventable_id", null: false + t.integer "event_action", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["eventable_type", "eventable_id"], name: "index_events_on_eventable" + t.index ["project_id"], name: "index_events_on_project_id" + t.index ["user_id"], name: "index_events_on_user_id" + end + create_table "friendly_id_slugs", force: :cascade do |t| t.string "slug", null: false t.integer "sluggable_id", null: false diff --git a/test/fixtures/account_users.yml b/test/fixtures/account_users.yml deleted file mode 100644 index b2a8d4c5..00000000 --- a/test/fixtures/account_users.yml +++ /dev/null @@ -1,28 +0,0 @@ -# == Schema Information -# -# Table name: account_users -# -# id :bigint not null, primary key -# created_at :datetime not null -# updated_at :datetime not null -# account_id :bigint not null -# user_id :bigint not null -# -# Indexes -# -# index_account_users_on_account_id (account_id) -# index_account_users_on_user_id (user_id) -# -# Foreign Keys -# -# fk_rails_... (account_id => accounts.id) -# fk_rails_... (user_id => users.id) -# - -one: - user: one - account: one - -two: - user: two - account: two diff --git a/test/fixtures/accounts.yml b/test/fixtures/accounts.yml deleted file mode 100644 index 94ba4c3c..00000000 --- a/test/fixtures/accounts.yml +++ /dev/null @@ -1,24 +0,0 @@ -# == Schema Information -# -# Table name: accounts -# -# id :bigint not null, primary key -# name :string not null -# created_at :datetime not null -# updated_at :datetime not null -# owner_id :bigint -# -# Indexes -# -# index_accounts_on_owner_id (owner_id) -# -# Foreign Keys -# -# fk_rails_... (owner_id => users.id) -# - -one: - owner_id: - -two: - owner_id: diff --git a/test/models/account_test.rb b/test/models/account_test.rb deleted file mode 100644 index eebaf52a..00000000 --- a/test/models/account_test.rb +++ /dev/null @@ -1,25 +0,0 @@ -# == Schema Information -# -# Table name: accounts -# -# id :bigint not null, primary key -# name :string not null -# created_at :datetime not null -# updated_at :datetime not null -# owner_id :bigint -# -# Indexes -# -# index_accounts_on_owner_id (owner_id) -# -# Foreign Keys -# -# fk_rails_... (owner_id => users.id) -# -require "test_helper" - -class AccountTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end diff --git a/test/models/account_user_test.rb b/test/models/account_user_test.rb deleted file mode 100644 index 24d44197..00000000 --- a/test/models/account_user_test.rb +++ /dev/null @@ -1,27 +0,0 @@ -# == Schema Information -# -# Table name: account_users -# -# id :bigint not null, primary key -# created_at :datetime not null -# updated_at :datetime not null -# account_id :bigint not null -# user_id :bigint not null -# -# Indexes -# -# index_account_users_on_account_id (account_id) -# index_account_users_on_user_id (user_id) -# -# Foreign Keys -# -# fk_rails_... (account_id => accounts.id) -# fk_rails_... (user_id => users.id) -# -require "test_helper" - -class AccountUserTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end