1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
|
---
stage: Enablement
group: Distribution
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# GitLab Rails Console Cheat Sheet **(FREE SELF)**
This is the GitLab Support Team's collection of information regarding the GitLab Rails
console, for use while troubleshooting. It is listed here for transparency,
and it may be useful for users with experience with these tools. If you are currently
having an issue with GitLab, it is highly recommended that you first check
our guide on [navigating our Rails console](navigating_gitlab_via_rails_console.md),
and your [support options](https://about.gitlab.com/support/), before attempting to use
this information.
WARNING:
Some of these scripts could be damaging if not run correctly,
or under the right conditions. We highly recommend running them under the
guidance of a Support Engineer, or running them in a test environment with a
backup of the instance ready to be restored, just in case.
WARNING:
As GitLab changes, changes to the code are inevitable,
and so some scripts may not work as they once used to. These are not kept
up-to-date as these scripts/commands were added as they were found/needed. As
mentioned above, we recommend running these scripts under the supervision of a
Support Engineer, who can also verify that they continue to work as they
should and, if needed, update the script for the latest version of GitLab.
## Find specific methods for an object
```ruby
Array.methods.select { |m| m.to_s.include? "sing" }
Array.methods.grep(/sing/)
```
## Find method source
```ruby
instance_of_object.method(:foo).source_location
# Example for when we would call project.private?
project.method(:private?).source_location
```
## Attributes
View available attributes, formatted using pretty print (`pp`).
For example, determine what attributes contain users' names and email addresses:
```ruby
u = User.find_by_username('someuser')
pp u.attributes
```
Partial output:
```plaintext
{"id"=>1234,
"email"=>"someuser@example.com",
"sign_in_count"=>99,
"name"=>"S User",
"username"=>"someuser",
"first_name"=>nil,
"last_name"=>nil,
"bot_type"=>nil}
```
Then make use of the attributes, [testing SMTP, for example](https://docs.gitlab.com/omnibus/settings/smtp.html#testing-the-smtp-configuration):
```ruby
e = u.email
n = u.name
Notify.test_email(e, "Test email for #{n}", 'Test email').deliver_now
#
Notify.test_email(u.email, "Test email for #{u.name}", 'Test email').deliver_now
```
## Query the database using an ActiveRecord Model
```ruby
m = Model.where('attribute like ?', 'ex%')
# for example to query the projects
projects = Project.where('path like ?', 'Oumua%')
```
## View all keys in cache
```ruby
Rails.cache.instance_variable_get(:@data).keys
```
## Profile a page
```ruby
url = '<url/of/the/page>'
# Before 11.6.0
logger = Logger.new($stdout)
admin_token = User.find_by_username('<admin-username>').personal_access_tokens.first.token
app.get("#{url}/?private_token=#{admin_token}")
# From 11.6.0
admin = User.find_by_username('<admin-username>')
Gitlab::Profiler.with_user(admin) { app.get(url) }
```
## Using the GitLab profiler inside console (used as of 10.5)
```ruby
logger = Logger.new($stdout)
admin = User.find_by_username('<admin-username>')
Gitlab::Profiler.profile('<url/of/the/page>', logger: logger, user: admin)
```
## Time an operation
```ruby
# A single operation
Benchmark.measure { <operation> }
# A breakdown of multiple operations
Benchmark.bm do |x|
x.report(:label1) { <operation_1> }
x.report(:label2) { <operation_2> }
end
```
## Feature flags
### Show all feature flags that are enabled
```ruby
# Regular output
Feature.all
# Nice output
Feature.all.map {|f| [f.name, f.state]}
```
## Command Line
### Check the GitLab version fast
```shell
grep -m 1 gitlab /opt/gitlab/version-manifest.txt
```
### Debugging SSH
```shell
GIT_SSH_COMMAND="ssh -vvv" git clone <repository>
```
### Debugging over HTTPS
```shell
GIT_CURL_VERBOSE=1 GIT_TRACE=1 git clone <repository>
```
## Projects
### Clear a project's cache
```ruby
ProjectCacheWorker.perform_async(project.id)
```
### Expire the .exists? cache
```ruby
project.repository.expire_exists_cache
```
### Make all projects private
```ruby
Project.update_all(visibility_level: 0)
```
### Find projects that are pending deletion
```ruby
#
# This section lists all the projects which are pending deletion
#
projects = Project.where(pending_delete: true)
projects.each do |p|
puts "Project ID: #{p.id}"
puts "Project name: #{p.name}"
puts "Repository path: #{p.repository.full_path}"
end
#
# Assign a user (the root user does)
#
user = User.find_by_username('root')
#
# For each project listed repeat these two commands
#
# Find the project, update the xxx-changeme values from above
project = Project.find_by_full_path('group-changeme/project-changeme')
# Immediately delete the project
::Projects::DestroyService.new(project, user, {}).execute
```
### Destroy a project
```ruby
project = Project.find_by_full_path('<project_path>')
user = User.find_by_username('<username>')
ProjectDestroyWorker.perform_async(project.id, user.id, {})
# or ProjectDestroyWorker.new.perform(project.id, user.id, {})
# or Projects::DestroyService.new(project, user).execute
```
### Remove fork relationship manually
```ruby
p = Project.find_by_full_path('<project_path>')
u = User.find_by_username('<username>')
::Projects::UnlinkForkService.new(p, u).execute
```
### Make a project read-only (can only be done in the console)
```ruby
# Make a project read-only
project.repository_read_only = true; project.save
# OR
project.update!(repository_read_only: true)
```
### Transfer project from one namespace to another
```ruby
p = Project.find_by_full_path('<project_path>')
# To set the owner of the project
current_user= p.creator
# Namespace where you want this to be moved.
namespace = Namespace.find_by_full_path("<new_namespace>")
::Projects::TransferService.new(p, current_user).execute(namespace)
```
### For Removing webhooks that is getting timeout due to large webhook logs
```ruby
# ID is the webhook_id
hook=WebHook.find(ID)
WebHooks::DestroyService.new(current_user).execute(hook)
#In case the service gets timeout consider removing webhook_logs
hook.web_hook_logs.limit(BATCH_SIZE).delete_all
```
### Bulk update service integration password for _all_ projects
For example, change the Jira user's password for all projects that have the Jira
integration active:
```ruby
p = Project.find_by_sql("SELECT p.id FROM projects p LEFT JOIN services s ON p.id = s.project_id WHERE s.type = 'JiraService' AND s.active = true")
p.each do |project|
project.jira_integration.update_attribute(:password, '<your-new-password>')
end
```
### Bulk update push rules for _all_ projects
For example, enable **Check whether the commit author is a GitLab user** and **Do not allow users to remove Git tags with `git push`** checkboxes, and create a filter for allowing commits from a specific e-mail domain only:
``` ruby
Project.find_each do |p|
pr = p.push_rule || PushRule.new(project: p)
# Check whether the commit author is a GitLab user
pr.member_check = true
# Do not allow users to remove Git tags with `git push`
pr.deny_delete_tag = true
# Commit author's email
pr.author_email_regex = '@domain\.com$'
pr.save!
end
```
## Bulk update to change all the Jira integrations to Jira instance-level values
To change all Jira project to use the instance-level integration settings:
1. In a Rails console:
```ruby
jira_integration_instance_id = Integrations::Jira.find_by(instance: true).id
Integrations::Jira.where(active: true, instance: false, template: false, inherit_from_id: nil).find_each do |integration|
integration.update_attribute(:inherit_from_id, jira_integration_instance_id)
end
```
1. Modify and save again the instance-level integration from the UI to propagate the changes to all the group-level and project-level integrations.
### Bulk update to disable the Slack Notification service
To disable notifications for all projects that have Slack service enabled, do:
```ruby
# Grab all projects that have the Slack notifications enabled
p = Project.find_by_sql("SELECT p.id FROM projects p LEFT JOIN services s ON p.id = s.project_id WHERE s.type = 'SlackService' AND s.active = true")
# Disable the service on each of the projects that were found.
p.each do |project|
project.slack_service.update_attribute(:active, false)
end
```
### Incorrect repository statistics shown in the GUI
After [reducing a repository size with third-party tools](../../user/project/repository/reducing_the_repo_size_using_git.md)
the displayed size may still show old sizes or commit numbers. To force an update, do:
```ruby
p = Project.find_by_full_path('<namespace>/<project>')
pp p.statistics
p.statistics.refresh!
pp p.statistics
# compare with earlier values
# check the total artifact storage space separately
builds_with_artifacts = p.builds.with_downloadable_artifacts.all
artifact_storage = 0
builds_with_artifacts.find_each do |build|
artifact_storage += build.artifacts_size
end
puts "#{artifact_storage} bytes"
```
### Identify deploy keys associated with blocked and non-member users
When the user who created a deploy key is blocked or removed from the project, the key
can no longer be used to push to protected branches in a private project (see [issue #329742](https://gitlab.com/gitlab-org/gitlab/-/issues/329742)).
The following script identifies unusable deploy keys:
```ruby
ghost_user_id = User.ghost.id
DeployKeysProject.with_write_access.find_each do |deploy_key_mapping|
project = deploy_key_mapping.project
deploy_key = deploy_key_mapping.deploy_key
user = deploy_key.user
access_checker = Gitlab::DeployKeyAccess.new(deploy_key, container: project)
# can_push_for_ref? tests if deploy_key can push to default branch, which is likely to be protected
can_push = access_checker.can_do_action?(:push_code)
can_push_to_default = access_checker.can_push_for_ref?(project.repository.root_ref)
next if access_checker.allowed? && can_push && can_push_to_default
if user.nil? || user.id == ghost_user_id
username = 'none'
state = '-'
else
username = user.username
user_state = user.state
end
puts "Deploy key: #{deploy_key.id}, Project: #{project.full_path}, Can push?: " + (can_push ? 'YES' : 'NO') +
", Can push to default branch #{project.repository.root_ref}?: " + (can_push_to_default ? 'YES' : 'NO') +
", User: #{username}, User state: #{user_state}"
end
```
### Find projects using an SQL query
Find and store an array of projects based on an SQL query:
```ruby
# Finds projects that end with '%ject'
projects = Project.find_by_sql("SELECT * FROM projects WHERE name LIKE '%ject'")
=> [#<Project id:12 root/my-first-project>>, #<Project id:13 root/my-second-project>>]
```
## Wikis
### Recreate
WARNING:
This is a destructive operation, the Wiki becomes empty.
A Projects Wiki can be recreated by this command:
```ruby
p = Project.find_by_full_path('<username-or-group>/<project-name>') ### enter your projects path
GitlabShellWorker.perform_in(0, :remove_repository, p.repository_storage, p.wiki.disk_path) ### deletes the wiki project from the filesystem
p.create_wiki ### creates the wiki project on the filesystem
```
## Issue boards
### In case of issue boards not loading properly and it's getting time out. We need to call the Issue Rebalancing service to fix this
```ruby
p = Project.find_by_full_path('<username-or-group>/<project-name>')
Issues::RelativePositionRebalancingService.new(p.root_namespace.all_projects).execute
```
## Imports and exports
### Import a project
```ruby
# Find the project and get the error
p = Project.find_by_full_path('<username-or-group>/<project-name>')
p.import_error
# To finish the import on GitLab running version before 11.6
p.import_finish
# To finish the import on GitLab running version 11.6 or after
p.import_state.mark_as_failed("Failed manually through console.")
```
### Rename imported repository
In a specific situation, an imported repository needed to be renamed. The Support
Team was informed of a backup restore that failed on a single repository, which created
the project with an empty repository. The project was successfully restored to a development
instance, then exported, and imported into a new project under a different name.
The Support Team was able to transfer the incorrectly named imported project into the
correctly named empty project using the steps below.
Move the new repository to the empty repository:
```shell
mv /var/opt/gitlab/git-data/repositories/<group>/<new-project> /var/opt/gitlab/git-data/repositories/<group>/<empty-project>
```
Make sure the permissions are correct:
```shell
chown -R git:git <path-to-directory>.git
```
Clear the cache:
```shell
sudo gitlab-rake cache:clear
```
### Export a project
It's typically recommended to export a project through [the web interface](../../user/project/settings/import_export.md#export-a-project-and-its-data) or through [the API](../../api/project_import_export.md). In situations where this is not working as expected, it may be preferable to export a project directly via the Rails console:
```ruby
user = User.find_by_username('<username>')
# Sufficient permissions needed
# Read https://docs.gitlab.com/ee/user/permissions.html#project-members-permissions
project = Project.find_by_full_path('<username-or-group>/<project-name')
Projects::ImportExport::ExportService.new(project, user).execute
```
If this all runs successfully, you see an output like the following before being returned to the Rails console prompt:
```ruby
=> nil
```
The exported project is located within a `.tar.gz` file in `/var/opt/gitlab/gitlab-rails/uploads/-/system/import_export_upload/export_file/`.
If this fails, [enable verbose logging](navigating_gitlab_via_rails_console.md#looking-up-database-persisted-objects),
repeat the above procedure after,
and report the output to
[GitLab Support](https://about.gitlab.com/support/).
## Repository
### Search sequence of pushes to a repository
If it seems that a commit has gone "missing", search the sequence of pushes to a repository.
[This StackOverflow article](https://stackoverflow.com/questions/13468027/the-mystery-of-the-missing-commit-across-merges)
describes how you can end up in this state without a force push. Another cause can be a misconfigured [server hook](../server_hooks.md) that changes a HEAD ref via a `git reset` operation.
If you look at the output from the sample code below for the target branch, you
see a discontinuity in the from/to commits as you step through the output. The `commit_from` of each new push should equal the `commit_to` of the previous push. A break in that sequence indicates one or more commits have been "lost" from the repository history.
The following example checks the last 100 pushes and prints the `commit_from` and `commit_to` entries:
```ruby
p = Project.find_by_full_path('u/p')
p.events.pushed_action.last(100).each do |e|
printf "%-20.20s %8s...%8s (%s)
", e.push_event_payload[:ref], e.push_event_payload[:commit_from], e.push_event_payload[:commit_to], e.author.try(:username)
end
```
Example output showing break in sequence at line 4:
```plaintext
master f21b07713251e04575908149bdc8ac1f105aabc3...6bc56c1f46244792222f6c85b11606933af171de (root)
master 6bc56c1f46244792222f6c85b11606933af171de...132da6064f5d3453d445fd7cb452b148705bdc1b (root)
master 132da6064f5d3453d445fd7cb452b148705bdc1b...a62e1e693150a2e46ace0ce696cd4a52856dfa65 (root)
master 58b07b719a4b0039fec810efa52f479ba1b84756...f05321a5b5728bd8a89b7bf530aa44043c951dce (root)
master f05321a5b5728bd8a89b7bf530aa44043c951dce...7d02e575fd790e76a3284ee435368279a5eb3773 (root)
```
## Mirrors
### Find mirrors with "bad decrypt" errors
This content has been converted to a Rake task, see [verify database values can be decrypted using the current secrets](../raketasks/check.md#verify-database-values-can-be-decrypted-using-the-current-secrets).
### Transfer mirror users and tokens to a single service account
Use case: If you have multiple users using their own GitHub credentials to set up
repository mirroring, mirroring breaks when people leave the company. Use this
script to migrate disparate mirroring users and tokens into a single service account:
```ruby
svc_user = User.find_by(username: 'ourServiceUser')
token = 'githubAccessToken'
Project.where(mirror: true).each do |project|
import_url = project.import_url
# The url we want is https://token@project/path.git
repo_url = if import_url.include?('@')
# Case 1: The url is something like https://23423432@project/path.git
import_url.split('@').last
elsif import_url.include?('//')
# Case 2: The url is something like https://project/path.git
import_url.split('//').last
end
next unless repo_url
final_url = "https://#{token}@#{repo_url}"
project.mirror_user = svc_user
project.import_url = final_url
project.username_only_import_url = final_url
project.save
end
```
## Users
### Create new user
```ruby
u = User.new(username: 'test_user', email: 'test@example.com', name: 'Test User', password: 'password', password_confirmation: 'password')
u.skip_confirmation! # Use it only if you wish user to be automatically confirmed. If skipped, user receives confirmation e-mail
u.save!
```
### Skip reconfirmation
```ruby
user = User.find_by_username('<username>')
user.skip_reconfirmation!
```
### Disable 2fa for single user
```ruby
user = User.find_by_username('<username>')
user.disable_two_factor!
```
### Active users & Historical users
```ruby
# Active users on the instance, now
User.active.count
# Users taking a seat on the instance
User.billable.count
# The historical max on the instance as of the past year
::HistoricalData.max_historical_user_count(from: 1.year.ago.beginning_of_day, to: Time.current.end_of_day)
```
Using cURL and jq (up to a max 100, see the [pagination docs](../../api/index.md#pagination)):
```shell
curl --silent --header "Private-Token: ********************" \
"https://gitlab.example.com/api/v4/users?per_page=100&active" | jq --compact-output '.[] | [.id,.name,.username]'
```
### Update Daily Billable & Historical users
```ruby
# Forces recount of historical (max) users
::HistoricalDataWorker.new.perform
# Forces recount of daily billable users
identifier = Analytics::UsageTrends::Measurement.identifiers[:billable_users]
::Analytics::UsageTrends::CounterJobWorker.new.perform(identifier, User.minimum(:id), User.maximum(:id), Time.zone.now)
```
### Block or Delete Users that have no projects or groups
```ruby
users = User.where('id NOT IN (select distinct(user_id) from project_authorizations)')
# How many users are removed?
users.count
# If that count looks sane:
# You can either block the users:
users.each { |user| user.blocked? ? nil : user.block! }
# Or you can delete them:
# need 'current user' (your user) for auditing purposes
current_user = User.find_by(username: '<your username>')
users.each do |user|
DeleteUserWorker.perform_async(current_user.id, user.id)
end
```
### Deactivate Users that have no recent activity
```ruby
days_inactive = 90
inactive_users = User.active.where("last_activity_on <= ?", days_inactive.days.ago)
inactive_users.each do |user|
puts "user '#{user.username}': #{user.last_activity_on}"
user.deactivate!
end
```
### Block Users that have no recent activity
```ruby
days_inactive = 90
inactive_users = User.active.where("last_activity_on <= ?", days_inactive.days.ago)
inactive_users.each do |user|
puts "user '#{user.username}': #{user.last_activity_on}"
user.block!
end
```
### Find a user's max permissions for project/group
```ruby
user = User.find_by_username 'username'
project = Project.find_by_full_path 'group/project'
user.max_member_access_for_project project.id
```
```ruby
user = User.find_by_username 'username'
group = Group.find_by_full_path 'group'
user.max_member_access_for_group group.id
```
## Groups
### Transfer group to another location
```ruby
user = User.find_by_username('<username>')
group = Group.find_by_name("<group_name>")
parent_group = Group.find_by(id: "<group_id>")
service = ::Groups::TransferService.new(group, user)
service.execute(parent_group)
```
### Count unique users in a group and subgroups
```ruby
group = Group.find_by_path_or_name("groupname")
members = []
for member in group.members_with_descendants
members.push(member.user_name)
end
members.uniq.length
```
```ruby
group = Group.find_by_path_or_name("groupname")
# Count users from subgroup and up (inherited)
group.members_with_parents.count
# Count users from the parent group and down (specific grants)
parent.members_with_descendants.count
```
### Delete a group
```ruby
GroupDestroyWorker.perform_async(group_id, user_id)
```
### Modify group project creation
```ruby
# Project creation levels: 0 - No one, 1 - Maintainers, 2 - Developers + Maintainers
group = Group.find_by_path_or_name('group-name')
group.project_creation_level=0
```
### Modify group - disable 2FA requirement
WARNING:
When disabling the 2FA Requirement on a subgroup, the whole parent group (including all subgroups) is affected by this change.
```ruby
group = Group.find_by_path_or_name('group-name')
group.require_two_factor_authentication=false
group.save
```
## Authentication
### Re-enable standard web sign-in form
Re-enable the standard username and password-based sign-in form if it was disabled as a [Sign-in restriction](../../user/admin_area/settings/sign_in_restrictions.md#password-authentication-enabled).
You can use this method when a configured external authentication provider (through SSO or an LDAP configuration) is facing an outage and direct sign-in access to GitLab is required.
```ruby
Gitlab::CurrentSettings.update!(password_authentication_enabled_for_web: true)
```
## SCIM
### Fixing bad SCIM identities
```ruby
def delete_bad_scim(email, group_path)
output = ""
u = User.find_by_email(email)
uid = u.id
g = Group.find_by_full_path(group_path)
saml_prov_id = SamlProvider.find_by(group_id: g.id).id
saml = Identity.where(user_id: uid, saml_provider_id: saml_prov_id)
scim = ScimIdentity.where(user_id: uid , group_id: g.id)
if saml[0]
saml_eid = saml[0].extern_uid
output += "%s," % [email]
output += "SAML: %s," % [saml_eid]
if scim[0]
scim_eid = scim[0].extern_uid
output += "SCIM: %s" % [scim_eid]
if saml_eid == scim_eid
output += " Identities matched, not deleted \n"
else
scim[0].destroy
output += " Deleted \n"
end
else
output = "ERROR No SCIM identify found for: [%s]\n" % [email]
puts output
return 1
end
else
output = "ERROR No SAML identify found for: [%s]\n" % [email]
puts output
return 1
end
puts output
return 0
end
# In case of multiple emails
emails = [email1, email2]
emails.each do |e|
delete_bad_scim(e,'<group-path>')
end
```
### Find groups using an SQL query
Find and store an array of groups based on an SQL query:
```ruby
# Finds groups and subgroups that end with '%oup'
Group.find_by_sql("SELECT * FROM namespaces WHERE name LIKE '%oup'")
=> [#<Group id:3 @test-group>, #<Group id:4 @template-group/template-subgroup>]
```
## Routes
### Remove redirecting routes
See <https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41758#note_54828133>.
```ruby
path = 'foo'
conflicting_permanent_redirects = RedirectRoute.matching_path_and_descendants(path)
# Check that conflicting_permanent_redirects is as expected
conflicting_permanent_redirects.destroy_all
```
## Merge Requests
### Close a merge request properly (if merged but still marked as open)
```ruby
u = User.find_by_username('<username>')
p = Project.find_by_full_path('<namespace/project>')
m = p.merge_requests.find_by(iid: <iid>)
MergeRequests::PostMergeService.new(project: p, current_user: u).execute(m)
```
### Delete a merge request
```ruby
u = User.find_by_username('<username>')
p = Project.find_by_full_path('<namespace/project>')
m = p.merge_requests.find_by(iid: <iid>)
Issuable::DestroyService.new(project: m.project, current_user: u).execute(m)
```
### Rebase manually
```ruby
u = User.find_by_username('<username>')
p = Project.find_by_full_path('<namespace/project>')
m = p.merge_requests.find_by(iid: <iid>)
MergeRequests::RebaseService.new(project: m.target_project, current_user: u).execute(m)
```
## CI
### Cancel stuck pending pipelines
For more information, see the [confidential issue](../../user/project/issues/confidential_issues.md)
`https://gitlab.com/gitlab-com/support-forum/issues/2449#note_41929707`.
```ruby
Ci::Pipeline.where(project_id: p.id).where(status: 'pending').count
Ci::Pipeline.where(project_id: p.id).where(status: 'pending').each {|p| p.cancel if p.stuck?}
Ci::Pipeline.where(project_id: p.id).where(status: 'pending').count
```
### Remove artifacts more than a week old
This section has been moved to the [job artifacts troubleshooting documentation](../job_artifacts.md#delete-job-artifacts-from-jobs-completed-before-a-specific-date).
### Find reason failure (for when build trace is empty) (Introduced in 10.3.0)
See <https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41111>.
```ruby
build = Ci::Build.find(78420)
build.failure_reason
build.dependencies.each do |d| { puts "status: #{d.status}, finished at: #{d.finished_at},
completed: #{d.complete?}, artifacts_expired: #{d.artifacts_expired?}, erased: #{d.erased?}" }
```
### Try CI integration
```ruby
p = Project.find_by_full_path('<project_path>')
m = project.merge_requests.find_by(iid: )
m.project.try(:ci_integration)
```
### Validate the `.gitlab-ci.yml`
```ruby
project = Project.find_by_full_path 'group/project'
content = project.repository.gitlab_ci_yml_for(project.repository.root_ref_sha)
Gitlab::Ci::Lint.new(project: project, current_user: User.first).validate(content)
```
### Disable AutoDevOps on Existing Projects
```ruby
Project.all.each do |p|
p.auto_devops_attributes={"enabled"=>"0"}
p.save
end
```
### Obtain runners registration token
```ruby
Gitlab::CurrentSettings.current_application_settings.runners_registration_token
```
### Seed runners registration token
```ruby
appSetting = Gitlab::CurrentSettings.current_application_settings
appSetting.set_runners_registration_token('<new-runners-registration-token>')
appSetting.save!
```
### Run pipeline schedules manually
You can run pipeline schedules manually through the Rails console to reveal any errors that are usually not visible.
```ruby
# schedule_id can be obtained from Edit Pipeline Schedule page
schedule = Ci::PipelineSchedule.find_by(id: <schedule_id>)
# Select the user that you want to run the schedule for
user = User.find_by_username('<username>')
# Run the schedule
ps = Ci::CreatePipelineService.new(schedule.project, user, ref: schedule.ref).execute!(:schedule, ignore_skip_ci: true, save_on_errors: false, schedule: schedule)
```
## License
### See current license information
```ruby
# License information (name, company, email address)
License.current.licensee
# Plan:
License.current.plan
# Uploaded:
License.current.created_at
# Started:
License.current.starts_at
# Expires at:
License.current.expires_at
# Is this a trial license?
License.current.trial?
```
### Check if a project feature is available on the instance
Features listed in <https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/models/license.rb>.
```ruby
License.current.feature_available?(:jira_dev_panel_integration)
```
### Check if a project feature is available in a project
Features listed in [`license.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/models/license.rb).
```ruby
p = Project.find_by_full_path('<group>/<project>')
p.feature_available?(:jira_dev_panel_integration)
```
### Add a license through the console
```ruby
key = "<key>"
license = License.new(data: key)
license.save
License.current # check to make sure it applied
```
This is needed for example in a known edge-case with
[expired license and multiple LDAP servers](../auth/ldap/ldap-troubleshooting.md#expired-license-causes-errors-with-multiple-ldap-servers).
### Remove licenses
To clean up the [License History table](../../user/admin_area/license.md#view-license-details-and-history):
```ruby
TYPE = :trial?
# or :expired?
License.select(&TYPE).each(&:destroy!)
# or even License.all.each(&:destroy!)
```
## Registry
### Registry Disk Space Usage by Project
As a GitLab administrator, you may need to reduce disk space consumption.
A common culprit is Docker Registry images that are no longer in use. To find
the storage broken down by each project, run the following in the
[GitLab Rails console](../troubleshooting/navigating_gitlab_via_rails_console.md):
```ruby
projects_and_size = [["project_id", "creator_id", "registry_size_bytes", "project path"]]
# You need to specify the projects that you want to look through. You can get these in any manner.
projects = Project.last(100)
projects.each do |p|
project_total_size = 0
container_repositories = p.container_repositories
container_repositories.each do |c|
c.tags.each do |t|
project_total_size = project_total_size + t.total_size unless t.total_size.nil?
end
end
if project_total_size > 0
projects_and_size << [p.project_id, p.creator.id, project_total_size, p.full_path]
end
end
# projects_and_size is filled out now
# maybe print it as comma separated output?
projects_and_size.each do |ps|
puts "%s,%s,%s,%s" % ps
end
```
### Run the Cleanup policy now
Find this content in the [Container Registry troubleshooting docs](../packages/container_registry.md#run-the-cleanup-policy-now).
## Sidekiq
This content has been moved to the [Troubleshooting Sidekiq docs](sidekiq.md).
## Redis
### Connect to Redis (omnibus)
```shell
/opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket
```
## LFS
### Get information about LFS objects and associated project
```ruby
o = LfsObject.find_by(oid: "<oid>")
p = Project.find(LfsObjectsProject.find_by_lfs_object_id(o.id).project_id)
```
You can then delete these records from the database with:
```ruby
LfsObjectsProject.find_by_lfs_object_id(o.id).destroy
o.destroy
```
You would also want to combine this with deleting the LFS file in the LFS storage
area on disk. It remains to be seen exactly how or whether the deletion is useful, however.
## Decryption Problems
### Bad Decrypt Script (for encrypted variables)
This content has been converted to a Rake task, see [verify database values can be decrypted using the current secrets](../raketasks/check.md#verify-database-values-can-be-decrypted-using-the-current-secrets).
As an example of repairing, if `ProjectImportData Bad count:` is detected and the decision is made to delete the
encrypted credentials to allow manual reentry:
```ruby
# Find the ids of the corrupt ProjectImportData objects
total = 0
bad = []
ProjectImportData.find_each do |data|
begin
total += 1
data.credentials
rescue => e
bad << data.id
end
end
puts "Bad count: #{bad.count} / #{total}"
# See the bad ProjectImportData ids
bad
# Remove the corrupted credentials
import_data = ProjectImportData.where(id: bad)
import_data.each do |data|
data.update_columns({ encrypted_credentials: nil, encrypted_credentials_iv: nil, encrypted_credentials_salt: nil})
end
```
If `User OTP Secret Bad count:` is detected. For each user listed disable/enable
two-factor authentication.
The following script searches in some of the tables for encrypted tokens that are
causing decryption errors, and update or reset as needed:
```shell
wget -O /tmp/encrypted-tokens.rb https://gitlab.com/snippets/1876342/raw
gitlab-rails runner /tmp/encrypted-tokens.rb
```
### Decrypt Script for encrypted tokens
This content has been converted to a Rake task, see [verify database values can be decrypted using the current secrets](../raketasks/check.md#verify-database-values-can-be-decrypted-using-the-current-secrets).
## Geo
### Artifacts
#### Find failed artifacts
```ruby
Geo::JobArtifactRegistry.failed
```
#### Download artifact
```ruby
Gitlab::Geo::JobArtifactDownloader.new(:job_artifact, <artifact_id>).execute
```
#### Get a count of the synced artifacts
```ruby
Geo::JobArtifactRegistry.synced.count
```
#### Find `ID` of synced artifacts that are missing on primary
```ruby
Geo::JobArtifactRegistry.synced.missing_on_primary.pluck(:artifact_id)
```
### Repository verification failures
#### Get the number of verification failed repositories
```ruby
Geo::ProjectRegistry.verification_failed('repository').count
```
#### Find the verification failed repositories
```ruby
Geo::ProjectRegistry.verification_failed('repository')
```
### Find repositories that failed to sync
```ruby
Geo::ProjectRegistry.sync_failed('repository')
```
### Resync repositories
#### Queue up all repositories for resync. Sidekiq handles each sync
```ruby
Geo::ProjectRegistry.update_all(resync_repository: true, resync_wiki: true)
```
#### Sync individual repository now
```ruby
project = Project.find_by_full_path('<group/project>')
Geo::RepositorySyncService.new(project).execute
```
### Blob types newer than uploads/artifacts/LFS
- `Packages::PackageFile`
- `Terraform::StateVersion`
- `MergeRequestDiff`
`Packages::PackageFile` is used in the following examples, but things generally work the same for the other Blob types.
#### The Replicator
The main kinds of classes are Registry, Model, and Replicator. If you have an instance of one of these classes, you can get the others. The Registry and Model mostly manage PostgreSQL DB state. The Replicator knows how to replicate/verify (or it can call a service to do it):
```ruby
model_record = Packages::PackageFile.last
model_record.replicator.registry.replicator.model_record # just showing that these methods exist
```
#### Replicate a package file, synchronously, given an ID
```ruby
model_record = Packages::PackageFile.find(id)
model_record.replicator.send(:download)
```
#### Replicate a package file, synchronously, given a registry ID
```ruby
registry = Geo::PackageFileRegistry.find(registry_id)
registry.replicator.send(:download)
```
#### Verify package files on the secondary manually
This iterates over all package files on the secondary, looking at the
`verification_checksum` stored in the database (which came from the primary)
and then calculate this value on the secondary to check if they match. This
does not change anything in the UI:
```ruby
# Run on secondary
status = {}
Packages::PackageFile.find_each do |package_file|
primary_checksum = package_file.verification_checksum
secondary_checksum = Packages::PackageFile.hexdigest(package_file.file.path)
verification_status = (primary_checksum == secondary_checksum)
status[verification_status.to_s] ||= []
status[verification_status.to_s] << package_file.id
end
# Count how many of each value we get
status.keys.each {|key| puts "#{key} count: #{status[key].count}"}
# See the output in its entirety
status
```
### Repository types newer than project/wiki repositories
- `SnippetRepository`
- `GroupWikiRepository`
`SnippetRepository` is used in the examples below, but things generally work the same for the other Repository types.
#### The Replicator
The main kinds of classes are Registry, Model, and Replicator. If you have an instance of one of these classes, you can get the others. The Registry and Model mostly manage PostgreSQL DB state. The Replicator knows how to replicate/verify (or it can call a service to do it).
```ruby
model_record = SnippetRepository.last
model_record.replicator.registry.replicator.model_record # just showing that these methods exist
```
#### Replicate a snippet repository, synchronously, given an ID
```ruby
model_record = SnippetRepository.find(id)
model_record.replicator.send(:sync_repository)
```
#### Replicate a snippet repository, synchronously, given a registry ID
```ruby
registry = Geo::SnippetRepositoryRegistry.find(registry_id)
registry.replicator.send(:sync_repository)
```
## Gitaly
### Find available and used space
A Gitaly storage resource can be polled through Rails to determine the available and used space.
```ruby
Gitlab::GitalyClient::ServerService.new("default").storage_disk_statistics
```
## Generate Service Ping
The [Service Ping Guide](../../development/service_ping/index.md) in our developer documentation
has more information about Service Ping.
### Generate or get the cached Service Ping
```ruby
Gitlab::UsageData.to_json
```
### Generate a fresh new Service Ping
This also refreshes the cached Service Ping displayed in the Admin Area
```ruby
Gitlab::UsageData.to_json(force_refresh: true)
```
### Generate and print
Generates Service Ping data in JSON format.
```shell
rake gitlab:usage_data:generate
```
Generates Service Ping data in YAML format:
```shell
rake gitlab:usage_data:dump_sql_in_yaml
```
### Generate and send Service Ping
Prints the metrics saved in `conversational_development_index_metrics`.
```shell
rake gitlab:usage_data:generate_and_send
```
## Kubernetes integration
Find cluster:
```ruby
cluster = Clusters::Cluster.find(1)
cluster = Clusters::Cluster.find_by(name: 'cluster_name')
```
Delete cluster without associated resources:
```ruby
# Find users with the Administrator role
user = User.find_by(username: 'admin_user')
# Find the cluster with the ID
cluster = Clusters::Cluster.find(1)
# Delete the cluster
Clusters::DestroyService.new(user).execute(cluster)
```
## Elasticsearch
### Configuration attributes
Open the rails console (`gitlab rails c`) and run the following command to see all the available attributes:
```ruby
ApplicationSetting.last.attributes
```
Among other attributes, the output contains all the settings available in the [Elasticsearch Integration page](../../integration/elasticsearch.md), such as `elasticsearch_indexing`, `elasticsearch_url`, `elasticsearch_replicas`, and `elasticsearch_pause_indexing`.
#### Setting attributes
You can then set anyone of Elasticsearch integration settings by issuing a command similar to:
```ruby
ApplicationSetting.last.update(elasticsearch_url: '<your ES URL and port>')
#or
ApplicationSetting.last.update(elasticsearch_indexing: false)
```
#### Getting attributes
You can then check if the settings have been set in the [Elasticsearch Integration page](../../integration/elasticsearch.md) or in the rails console by issuing:
```ruby
Gitlab::CurrentSettings.elasticsearch_url
#or
Gitlab::CurrentSettings.elasticsearch_indexing
```
|