<% end %>
diff --git a/app/components/timeline_component.rb b/app/components/timeline_component.rb
index 812aae4fa..977a41904 100644
--- a/app/components/timeline_component.rb
+++ b/app/components/timeline_component.rb
@@ -3,6 +3,7 @@
class TimelineComponent < ViewComponent::Base
include ApplicationHelper
+ DEFAULT_TRUNCATE = 2
EVENT_TYPE = {
success: "bg-green-500 border-white dark:border-main-900 dark:bg-green-500",
error: "bg-red-500 border-white dark:border-main-900 dark:bg-red-500",
@@ -10,13 +11,25 @@ class TimelineComponent < ViewComponent::Base
notice: "bg-sky-500 border-white dark:border-sky-900 dark:bg-sky-700"
}
- def initialize(events:)
+ def initialize(events:, truncate: false, view_all_link: nil)
+ raise ArgumentError, "you must supply a 'view all' link when truncate is on" if truncate && view_all_link.nil?
+
@events = events
+ @truncate = truncate
+ @view_all_link = view_all_link
end
- attr_reader :events
+ attr_reader :truncate, :view_all_link
def event_color(event)
EVENT_TYPE.fetch(event[:type] || :neutral)
end
+
+ def events
+ if truncate
+ @events.take(DEFAULT_TRUNCATE)
+ else
+ @events
+ end
+ end
end
diff --git a/app/components/v2/build_health_component.rb b/app/components/v2/build_health_component.rb
index c51fd09f8..b7675fb8f 100644
--- a/app/components/v2/build_health_component.rb
+++ b/app/components/v2/build_health_component.rb
@@ -1,8 +1,7 @@
# frozen_string_literal: true
class V2::BuildHealthComponent < ViewComponent::Base
- def initialize(release_platform_run:, builds:)
- @release_platform_run = release_platform_run
+ def initialize(builds)
@builds = builds
end
diff --git a/app/components/v2/live_release/internal_builds_component.html.erb b/app/components/v2/live_release/internal_builds_component.html.erb
index 2a8867b99..83e10607a 100644
--- a/app/components/v2/live_release/internal_builds_component.html.erb
+++ b/app/components/v2/live_release/internal_builds_component.html.erb
@@ -3,50 +3,42 @@
<% container.with_back_button %>
<% container.with_tab(title: "Internal Builds", icon:) do %>
<%= render V2::PlatformViewComponent.new(release) do |component| %>
- <% component.runs do |release_platform_run| %>
- <% if configured?(release_platform_run) %>
- <%= render V2::LiveRelease::SubmissionConfigComponent.new(configuration(release_platform_run), internal_workflow_config(release_platform_run), release_platform_run:) %>
- <% else %>
- <%= render V2::EmptyStateComponent.new(
- title: "No internal build step configured",
- text: "Please configure an internal build step for the train to see the build status.",
- banner_image: "v2/drill.svg",
- type: :subdued) %>
- <% end %>
- <% end %>
-
<% component.runs do |run| %>
- <% latest_release = latest_internal_release(run) %>
- <% if latest_release.present? %>
- <%= render V2::LiveRelease::PreProdRelease::CurrentReleaseComponent.new(latest_release) %>
- <% elsif configured?(run) %>
- <% if run.hotfix? && run.active? %>
- <%= render V2::AlertComponent.new(kind: :announcement, type: :announce, title: "Changes were not automatically applied for the hotfix") do |ann| %>
- <% ann.with_announcement_button(label: "Create internal build",
- scheme: :default,
- type: :button,
- options: pre_prod_internal_run_path(run),
- size: :xxs,
- html_options: html_opts(:post, "Are you sure you want to create the internal build?")) %>
-
- <%= render V2::CommitComponent.new(commit: applicable_commit(run), detailed: false) %>
- Internal builds are not automatically triggered for hotfixes. But you can manually apply the latest commit and kickoff the build.
-
+
+ <% if configured?(run) %>
+ <% latest_release = latest_internal_release(run) %>
+ <%= render V2::LiveRelease::PreProdRelease::InternalHeaderComponent.new(run, latest_release) %>
+
+ <% if latest_release.present? %>
+ <%= render V2::LiveRelease::PreProdRelease::CurrentReleaseComponent.new(latest_release) %>
+ <% elsif run.hotfix? && run.active? %>
+ <%= render V2::AlertComponent.new(kind: :announcement, type: :announce, title: "Changes were not automatically applied for the hotfix") do |ann| %>
+ <% ann.with_announcement_button(label: "Create internal build",
+ scheme: :default,
+ type: :button,
+ options: pre_prod_internal_run_path(run),
+ size: :xxs,
+ html_options: html_opts(:post, "Are you sure you want to create the internal build?")) %>
+
+ <%= render V2::CommitComponent.new(commit: applicable_commit(run), detailed: false) %>
+ Internal builds are not automatically triggered for hotfixes. But you can manually apply the latest commit and kickoff the build.
+
+ <% end %>
+ <% else %>
+ <%= render V2::EmptyStateComponent.new(
+ title: "Not ready yet",
+ text: "Please wait for changes to be applied before starting.",
+ banner_image: icon,
+ type: :subdued) %>
<% end %>
<% else %>
<%= render V2::EmptyStateComponent.new(
- title: "Not ready yet",
- text: "Please wait for changes to be applied before starting.",
- banner_image: icon,
+ title: "No internal build step configured",
+ text: "Please configure an internal build step for the train to see the build status.",
+ banner_image: "v2/drill.svg",
type: :subdued) %>
<% end %>
- <% else %>
-
- <% end %>
- <% end %>
-
- <% component.runs do |run| %>
- <%= render partial: "pre_prod_releases/build_insights", locals: {builds: builds(run), run:} %>
+
<% end %>
<% component.runs do |release_platform_run| %>
diff --git a/app/components/v2/live_release/internal_builds_component.rb b/app/components/v2/live_release/internal_builds_component.rb
index 814551fa6..46084a342 100644
--- a/app/components/v2/live_release/internal_builds_component.rb
+++ b/app/components/v2/live_release/internal_builds_component.rb
@@ -21,10 +21,6 @@ def configured?(run)
configuration(run).present?
end
- def internal_workflow_config(run)
- run.conf.pick_internal_workflow
- end
-
def configuration(run)
run.conf.internal_release
end
@@ -36,8 +32,4 @@ def latest_internal_release(run)
def previous_internal_releases(run)
run.older_internal_releases
end
-
- def builds(run)
- run.internal_builds.includes(:external_build)
- end
end
diff --git a/app/components/v2/live_release/metadata_component.html.erb b/app/components/v2/live_release/metadata_component.html.erb
index 4fe3742b1..bb35f52e3 100644
--- a/app/components/v2/live_release/metadata_component.html.erb
+++ b/app/components/v2/live_release/metadata_component.html.erb
@@ -26,7 +26,7 @@
<%= render V2::ButtonComponent.new(label: "App Store Connect metadata requirements",
scheme: :link,
type: :link_external,
- options: "/",
+ options: "https://help.apple.com/asc/appsspec/en.lproj/static.html",
html_options: { class: "text-sm" },
authz: false,
size: :none,
diff --git a/app/components/v2/live_release/pre_prod_release/header_component.html.erb b/app/components/v2/live_release/pre_prod_release/header_component.html.erb
new file mode 100644
index 000000000..56e787f7c
--- /dev/null
+++ b/app/components/v2/live_release/pre_prod_release/header_component.html.erb
@@ -0,0 +1,44 @@
+
+
+ <% if events.present? %>
+ <%= render TimelineComponent.new(events:, truncate: true, view_all_link: timeline_release_path(pre_prod_release.release)) %>
+ <% end %>
+
+
+
+
+
+ <% icon = V2::IconComponent.new("v2/info_full.svg", size: :base) %>
+ <% icon.with_tooltip("", placement: "bottom", type: :detailed) do |tooltip| %>
+ <% tooltip.with_detailed_text do %>
+ The build will be created from this workflow. Once the workflow completes and Tramline is able to find
+ the build, Tramline will send the build to the configured submission channels automatically or otherwise
+ (based on your settings).
+ <% end %>
+ <% end %>
+
+ <%= render icon %>
+
+ Workflow
+
+
+
+
+
+ <%= render V2::BadgeComponent.new(text: workflow_config.name, kind: :badge) do |badge| %>
+ <% badge.with_icon(ci_cd_provider_logo) %>
+ <% end %>
+
- <% end %>
- <% if builds.size > 1 %>
- <%= render V2::BuildHealthComponent.new(release_platform_run: run, builds:) %>
- <% else %>
- <%= render V2::EmptyStateComponent.new(
- title: "No build metadata trend to show",
- text: "As you add more builds to Tramline, the trend of their metadata will be displayed here.",
- banner_image: "v2/drill.svg",
- type: :subdued) %>
- <% end %>
- <% end %>
+<% if builds.size > 1 %>
+
+
+ †
+ The following metadata trends are computed from the last <%= builds.size %> builds on this release step.
+
+
+ Graphs shown below are populated from a combination of data fetched by Tramline and data that is (optionally)
+ sent to Tramline
+ <%= link_to_external "through the API ↗",
+ "https://docs.tramline.app/api#send-custom-metadata-for-a-build",
+ class: "hover:underline", title: "API docs on sending build metadata" %>.
+
+
+
+ <%= render V2::BuildHealthComponent.new(builds) %>
+<% else %>
+ <%= render V2::EmptyStateComponent.new(
+ title: "No build metadata trend to show",
+ text: "As you add more builds to Tramline, the trend of their metadata will be displayed here.",
+ banner_image: "v2/drill.svg",
+ type: :subdued) %>
<% end %>
diff --git a/lib/tasks/anonymize.rake b/lib/tasks/anonymize.rake
index 000bf81c0..b0eaab516 100644
--- a/lib/tasks/anonymize.rake
+++ b/lib/tasks/anonymize.rake
@@ -19,7 +19,6 @@ namespace :anonymize do
abort "Train ID not found!" if train_id.blank?
puts "Train with id #{train_id} will be copied to #{app.name}!" if train_id.present?
- ci_cd_integration = app.integrations.ci_cd.first
app_store_integration = app.integrations.build_channel.find(&:app_store_integration?)
play_store_integration = app.integrations.build_channel.find(&:google_play_store_integration?)
firebase_integration = app.integrations.build_channel.find(&:google_firebase_integration?)
@@ -36,6 +35,7 @@ namespace :anonymize do
user_ids = app.organization.users.pluck(:id)
user_github_logins = app.organization.users.pluck(:github_login)
+ ignored_stampable_types = %w[StepRun DeploymentRun StagedRollout]
database "TramlineDatabase" do
strategy DataAnon::Strategy::Whitelist
@@ -93,68 +93,63 @@ namespace :anonymize do
anonymize("app_id") { |field| app.id }
end
- table "release_health_rules" do
- continue { |index, record| ReleasePlatform.exists?(record["release_platform_id"]) && !ReleaseHealthRule.exists?(record["id"]) }
+ table "release_platform_configs" do
+ continue { |index, record| ReleasePlatform.exists?(record["release_platform_id"]) && !Config::ReleasePlatform.exists?(record["id"]) }
primary_key "id"
- whitelist "is_halting", "release_platform_id", "discarded_at"
+ whitelist "release_platform_id"
whitelist_timestamps
- anonymize("name") { |_| (Faker::Hacker.noun + " Rule").titleize }
end
- table "rule_expressions" do
- continue { |index, record| ReleaseHealthRule.exists?(record["release_health_rule_id"]) && !RuleExpression.exists?(record["id"]) }
+ table "workflow_configs" do
+ continue { |index, record| Config::ReleasePlatform.exists?(record["release_platform_config_id"]) && !Config::Workflow.exists?(record["id"]) }
primary_key "id"
- whitelist "comparator", "metric", "threshold_value", "type", "release_health_rule_id"
+ whitelist "artifact_name_pattern", "identifier", "kind", "name", "release_platform_config_id"
whitelist_timestamps
end
- # TODO [V2]: Remove this
- table "steps" do
- continue { |index, record| ReleasePlatform.exists?(record["release_platform_id"]) && !Step.exists?(record["id"]) }
+ table "release_step_configs" do
+ continue { |index, record| Config::ReleasePlatform.exists?(record["release_platform_config_id"]) && !Config::ReleaseStep.exists?(record["id"]) }
primary_key "id"
- whitelist "release_platform_id", "status", "step_number", "slug", "release_suffix", "kind", "auto_deploy", "app_variant_id", "discarded_at"
+ whitelist "auto_promote", "kind", "release_platform_config_id"
whitelist_timestamps
- anonymize("ci_cd_channel") do |field|
- {"id" => Faker::Internet.uuid, "name" => "CI Workflow #{Faker::JapaneseMedia::StudioGhibli.character}"}
- end
- anonymize("name").using FieldStrategy::LoremIpsum.new
- anonymize("description").using FieldStrategy::LoremIpsum.new
- anonymize("integration_id") do |field|
- ci_cd_integration.id
- end
end
- # TODO [V2]: Remove this
- table "deployments" do
- continue { |index, record| Step.exists?(record["step_id"]) && !Deployment.exists?(record["id"]) }
+ table "submission_configs" do
+ continue { |index, record| Config::ReleaseStep.exists?(record["release_step_config_id"]) && !Config::Submission.exists?(record["id"]) }
primary_key "id"
- whitelist "step_id", "build_artifact_channel", "deployment_number", "staged_rollout_config", "is_staged_rollout", "discarded_at"
+ whitelist "submission_type", "number", "auto_promote", "finish_rollout_in_next_release", "rollout_enabled", "rollout_stages", "release_step_config_id"
+ whitelist_timestamps
+ anonymize("integrable_id") { |_| app.id }
+ anonymize("integrable_type") { |_| "App" }
+ end
+
+ table "submission_external_configs" do
+ continue { |index, record| Config::Submission.exists?(record["submission_config_id"]) && !Config::SubmissionExternal.exists?(record["id"]) }
+
+ primary_key "id"
+ whitelist "identifier", "name", "internal", "submission_config_id"
+ whitelist_timestamps
+ end
+
+ table "release_health_rules" do
+ continue { |index, record| ReleasePlatform.exists?(record["release_platform_id"]) && !ReleaseHealthRule.exists?(record["id"]) }
+
+ primary_key "id"
+ whitelist "is_halting", "release_platform_id", "discarded_at"
+ whitelist_timestamps
+ anonymize("name") { |_| (Faker::Hacker.noun + " Rule").titleize }
+ end
+
+ table "rule_expressions" do
+ continue { |index, record| ReleaseHealthRule.exists?(record["release_health_rule_id"]) && !RuleExpression.exists?(record["id"]) }
+
+ primary_key "id"
+ whitelist "comparator", "metric", "threshold_value", "type", "release_health_rule_id"
whitelist_timestamps
- anonymize("build_artifact_channel") do |field|
- step = Step.find(field.ar_record.step_id)
- if step.kind == "release"
- field.value
- else
- val = field.value
- val["name"] = Faker::TvShows::TwinPeaks.location
- val
- end
- end
- anonymize("integration_id") do |field|
- step = Step.find(field.ar_record.step_id)
- release_platform = ReleasePlatform.find(step.release_platform_id)
- if release_platform.platform == "android" && step.kind == "release"
- play_store_integration.id
- elsif release_platform.platform == "ios"
- app_store_integration.id
- else
- firebase_integration.id
- end
- end
end
table "scheduled_releases" do
@@ -268,54 +263,6 @@ namespace :anonymize do
anonymize("promo_text").using FieldStrategy::LoremIpsum.new
end
- # TODO [V2]: Remove this
- table "step_runs" do
- continue { |index, record| Step.exists?(record["step_id"]) && ReleasePlatformRun.exists?(record["release_platform_run_id"]) && !StepRun.exists?(record["id"]) }
-
- primary_key "id"
- whitelist "step_id", "release_platform_run_id", "scheduled_at", "status", "commit_id", "build_version",
- "sign_required", "approval_status"
- whitelist_timestamps
- anonymize("ci_link").using FieldStrategy::RandomUrl.new
- anonymize("build_number").using FieldStrategy::FormattedStringNumber.new
- end
-
- table "external_builds" do
- continue { |index, record| StepRun.exists?(record["step_run_id"]) && !ExternalBuild.exists?(record["id"]) }
-
- primary_key "id"
- whitelist "metadata", "step_run_id", "build_id"
- whitelist_timestamps
- end
-
- # TODO [V2]: Remove this
- table "deployment_runs" do
- continue { |index, record| Deployment.exists?(record["deployment_id"]) && StepRun.exists?(record["step_run_id"]) && !DeploymentRun.exists?(record["id"]) }
-
- primary_key "id"
- whitelist "deployment_id", "step_run_id", "scheduled_at", "status", "initial_rollout_percentage", "failure_reason"
- whitelist_timestamps
- end
-
- # TODO [V2]: Remove this
- table "external_releases" do
- continue { |index, record| DeploymentRun.exists?(record["deployment_run_id"]) && !ExternalRelease.exists?(record["id"]) }
-
- primary_key "id"
- whitelist "deployment_run_id", "name", "status", "added_at", "size_in_bytes", "external_id", "reviewed_at", "released_at"
- whitelist_timestamps
- anonymize("external_link").using FieldStrategy::RandomUrl.new
- anonymize("build_number").using FieldStrategy::FormattedStringNumber.new
- end
-
- # TODO [V2]: Remove this
- table "staged_rollouts" do
- continue { |index, record| DeploymentRun.exists?(record["deployment_run_id"]) && !StagedRollout.exists?(record["id"]) }
- primary_key "id"
- whitelist "deployment_run_id", "config", "status", "current_stage"
- whitelist_timestamps
- end
-
table "pre_prod_releases" do
continue { |index, record| ReleasePlatformRun.exists?(record["release_platform_run_id"]) && !PreProdRelease.exists?(record["id"]) }
primary_key "id"
@@ -345,6 +292,17 @@ namespace :anonymize do
whitelist_timestamps
end
+ table "external_builds" do
+ continue { |index, record| Build.exists?(record["build_id"]) && !ExternalBuild.exists?(record["id"]) }
+
+ primary_key "id"
+ whitelist "metadata", "build_id"
+ whitelist_timestamps
+ anonymize("step_run_id") do |_|
+ nil
+ end
+ end
+
table "production_releases" do
continue { |index, record| ReleasePlatformRun.exists?(record["release_platform_run_id"]) && !ProductionRelease.exists?(record["id"]) }
primary_key "id"
@@ -381,7 +339,11 @@ namespace :anonymize do
end
table "passports" do
- continue { |index, record| record["stampable_type"].constantize.exists?(record["stampable_id"]) && !Passport.exists?(record["id"]) }
+ continue { |index, record|
+ ignored_stampable_types.exclude?(record["stampable_type"]) &&
+ record["stampable_type"].constantize.exists?(record["stampable_id"]) &&
+ !Passport.exists?(record["id"])
+ }
primary_key "id"
whitelist "stampable_type", "stampable_id", "reason", "kind", "message", "metadata", "author_id",
@@ -403,23 +365,7 @@ namespace :anonymize do
end
end
- # app.releases.finished.each do |release|
- # Queries::ReleaseSummary.warm(release.id)
- # end
- # train = app.trains.reload.find(train_id)
- # Charts::DevopsReport.warm(train)
- #
- # # NOTE: The code below will no longer be necessary once we have moved all the data over to the new models
- # puts "Populating config for train: #{train.name}"
- # train.release_platforms.each do |release_platform|
- # if release_platform.platform_config.present?
- # puts "Skipping #{train.name} platform #{release_platform.platform} as it already has a config"
- # next
- # end
- # populate_config(release_platform)
- # end
- #
- # populate_v2_models_for_train(train)
+ RefreshReldexJob.perform_later(train_id)
end
desc 'Anonymize release health metric data from source db into local db
@@ -444,7 +390,7 @@ namespace :anonymize do
destination_db destination_db_config
table "release_health_metrics" do
- continue { |index, record| DeploymentRun.exists?(record["deployment_run_id"]) && !ReleaseHealthMetric.exists?(record["id"]) }
+ continue { |index, record| ProductionRelease.exists?(record["production_release_id"]) && !ReleaseHealthMetric.exists?(record["id"]) }
primary_key "id"
whitelist "deployment_run_id", "production_release_id", "sessions", "sessions_in_last_day", "sessions_with_errors", "daily_users",
"daily_users_with_errors", "errors_count", "new_errors_count", "fetched_at", "total_sessions_in_last_day", "external_release_id"
@@ -453,7 +399,7 @@ namespace :anonymize do
table "release_health_events" do
continue do |index, record|
- DeploymentRun.exists?(record["deployment_run_id"]) &&
+ ProductionRelease.exists?(record["production_release_id"]) &&
ReleaseHealthRule.exists?(record["release_health_rule_id"]) &&
ReleaseHealthMetric.exists?(record["release_health_metric_id"]) &&
!ReleaseHealthEvent.exists?(record["id"])
@@ -464,8 +410,6 @@ namespace :anonymize do
whitelist_timestamps
end
end
-
- # populate_v2_metrics_models(train)
end
def source_db_config
@@ -485,17 +429,4 @@ namespace :anonymize do
def whitelist_timestamps
whitelist "created_at", "updated_at"
end
-
- def populate_v2_metrics_models(train)
- ActiveRecord::Base.transaction do
- train.releases.where(is_v2: true).find_each do |release|
- release.deployment_runs.each do |drun|
- next unless ProductionRelease.exists?(drun.id)
- # rubocop:disable Rails/SkipsModelValidations
- drun.release_health_metrics.update_all(production_release_id: drun.id)
- # rubocop:enable Rails/SkipsModelValidations
- end
- end
- end
- end
end
diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake
index b98b68b9a..079bf6b31 100644
--- a/lib/tasks/db.rake
+++ b/lib/tasks/db.rake
@@ -1,14 +1,8 @@
namespace :db do
desc "Nuke everything except users, organizations and apps"
task nuke: [:destructive, :environment] do
- CommitListener.delete_all
BuildArtifact.delete_all
- DeploymentRun.delete_all
- Deployment.delete_all
- StepRun.delete_all
Commit.delete_all
- StepRun.delete_all
- Step.delete_all
ReleasePlatformRun.delete_all
ReleasePlatform.delete_all
Release.delete_all
@@ -57,21 +51,6 @@ end
def nuke_train(train)
train.releases.each do |run|
run.release_platform_runs.each do |prun|
- prun.step_runs.each do |srun|
- srun.deployment_runs.each do |drun|
- drun.staged_rollout&.delete
- drun.staged_rollout&.passports&.delete_all
- drun.external_release&.delete
- drun.release_health_events&.delete_all
- drun.release_health_metrics&.delete_all
- drun.passports&.delete_all
- end
- srun.deployment_runs&.delete_all
- srun.build_artifact&.delete
- srun.passports&.delete_all
- srun.external_build&.delete
- end
- prun.step_runs&.delete_all
prun.passports&.delete_all
prun.release_metadata&.delete_all
prun.store_rollouts&.delete_all
@@ -136,10 +115,6 @@ def nuke_train(train)
rule.filter_rule_expressions&.delete_all
end
release_platform.all_release_health_rules&.delete_all
- release_platform.all_steps.each do |step|
- step.all_deployments.delete_all
- end
- release_platform.all_steps&.delete_all
sql = "delete from commit_listeners where release_platform_id = '#{release_platform.id}'"
ActiveRecord::Base.connection.execute(sql)
end