diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md index 07cc889b1227f00cba6b2bb2a1c35a0ac25520a0..1d081573f01a39c6264429e5f9282d14354cd82a 100644 --- a/doc/development/rake_tasks.md +++ b/doc/development/rake_tasks.md @@ -566,3 +566,16 @@ This task clones the remote repository, recursively walks the file system lookin ending in `.pub`, parses those files as SSH public keys, and then adds the public key fingerprints to `output_file`. The contents of `config/security/banned_ssh_keys.yml` is read by GitLab and kept in memory. It is not recommended to increase the size of this file beyond 1 megabyte in size. + +## Output current navigation structure to YAML + +_This task relies on your current environment setup (licensing, feature flags, projects/groups), so output may vary from run-to-run or environment-to-environment. We may look to standardize output in a future iteration._ + +Product, UX, and tech writing need a way to audit the entire GitLab navigation, +yet may not be comfortable directly reviewing the code in `lib/sidebars`. You +can dump the entire nav structure to YAML via the `gitlab:nav:dump_structure` +Rake task: + +```shell +bundle exec rake gitlab:nav:dump_structure +``` diff --git a/ee/lib/tasks/gitlab/nav/dump_structure.rake b/ee/lib/tasks/gitlab/nav/dump_structure.rake new file mode 100644 index 0000000000000000000000000000000000000000..841fe18d1d07c04af8b4a4e03b7a15d7e2c80eda --- /dev/null +++ b/ee/lib/tasks/gitlab/nav/dump_structure.rake @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +return if Rails.env.production? + +namespace :gitlab do + namespace :nav do + desc "GitLab | Nav | Dump the complete navigation structure for all navigation contexts" + task :dump_structure, [] => :gitlab_environment do + dumper = Tasks::Gitlab::Nav::DumpStructure.new + puts dumper.dump + end + end +end diff --git a/ee/lib/tasks/gitlab/nav/dump_structure.rb b/ee/lib/tasks/gitlab/nav/dump_structure.rb new file mode 100644 index 0000000000000000000000000000000000000000..42016802b1bbf96c252780ec25b64134aa996909 --- /dev/null +++ b/ee/lib/tasks/gitlab/nav/dump_structure.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +module Tasks + module Gitlab + module Nav + class DumpStructure + attr_accessor :context_defaults + + def initialize + @context_defaults = { + current_user: User.first, + is_super_sidebar: true, + + # Turn features on that impact the list of items rendered + can_view_pipeline_editor: true, + learn_gitlab_enabled: true, + show_discover_group_security: true, + show_discover_project_security: true, + show_security_dashboard: true, + + # Turn features off that do not add/remove items + show_cluster_hint: false, + show_promotions: false, + + current_ref: 'master' + } + end + + def panels + panels = [] + panels << Sidebars::UserProfile::Panel.new(Sidebars::Context.new( + container: User.first, + **@context_defaults + )) + panels << Sidebars::UserSettings::Panel.new(Sidebars::Context.new( + container: User.first, + **@context_defaults + )) + panels << Sidebars::YourWork::Panel.new(Sidebars::Context.new( + container: User.first, + **@context_defaults + )) + panels << Sidebars::Projects::SuperSidebarPanel.new(Sidebars::Projects::Context.new( + container: Project.first, + **@context_defaults + )) + panels << Sidebars::Groups::SuperSidebarPanel.new(Sidebars::Groups::Context.new( + container: Group.first, + **@context_defaults + )) + panels << Sidebars::Organizations::Panel.new(Sidebars::Context.new( + container: Organizations::Organization.first, + **@context_defaults + )) + panels << Sidebars::Admin::Panel.new(Sidebars::Context.new( + container: nil, + **@context_defaults + )) + panels << Sidebars::Explore::Panel.new(Sidebars::Context.new( + container: nil, + **@context_defaults + )) + + panels + end + + def current_time + Time.now.utc.iso8601 + end + + def current_sha + `git rev-parse --short HEAD`.strip + end + + def dump + contexts = panels.map do |panel| + { + title: panel.aria_label, + items: panel.super_sidebar_menu_items + } + end + + # Recurse through structure to drop info we don't need + clean_keys!(contexts) + + YAML.dump({ + generated_at: current_time, + commit_sha: current_sha, + contexts: contexts + }.deep_stringify_keys) + end + + private + + def clean_keys!(entries) + entries.each do |entry| + clean_keys!(entry[:items]) if entry[:items] + + entry[:id] = entry[:id].to_s if entry[:id] + entry.slice!(:id, :title, :icon, :link, :items) + end + end + end + end + end +end diff --git a/ee/spec/tasks/gitlab/nav/dump_structure_spec.rb b/ee/spec/tasks/gitlab/nav/dump_structure_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..cfded2d1130df4b5097e8b1cce33f96969bbd95e --- /dev/null +++ b/ee/spec/tasks/gitlab/nav/dump_structure_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'gitlab:nav:dump_structure', :silence_stdout, feature_category: :navigation do + let!(:user) { create(:user) } + + before do + # Build out scaffold records required for rake task + create(:project) + create(:group) + create(:organization) + + Rake.application.rake_require 'tasks/gitlab/nav/dump_structure' + end + + it 'outputs YAML describing the current nav structure' do + # Sample items that _hopefully_ won't change very often. + expected = { + "generated_at" => an_instance_of(String), + "commit_sha" => an_instance_of(String), + "contexts" => a_collection_including(a_hash_including({ + "title" => "User profile navigation", + "items" => a_collection_including(a_hash_including({ + "id" => "activity_menu", + "title" => "Activity", + "icon" => "history", + "link" => "/users/#{user.username}/activity" + })) + })) + } + expect(YAML).to receive(:dump).with(expected) + + run_rake_task('gitlab:nav:dump_structure') + end +end