shipit-engine 0.27.1 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +30 -1
- data/app/assets/stylesheets/_pages/_commits.scss +2 -0
- data/app/assets/stylesheets/_pages/_deploy.scss +6 -0
- data/app/controllers/shipit/api/release_statuses_controller.rb +22 -0
- data/app/controllers/shipit/api/stacks_controller.rb +6 -1
- data/app/controllers/shipit/commits_controller.rb +12 -1
- data/app/controllers/shipit/deploys_controller.rb +11 -0
- data/app/controllers/shipit/stacks_controller.rb +29 -1
- data/app/controllers/shipit/tasks_controller.rb +13 -1
- data/app/helpers/shipit/merge_status_helper.rb +2 -2
- data/app/helpers/shipit/stacks_helper.rb +10 -4
- data/app/jobs/shipit/perform_task_job.rb +1 -0
- data/app/jobs/shipit/update_github_last_deployed_ref_job.rb +41 -0
- data/app/models/shipit/command_line_user.rb +58 -0
- data/app/models/shipit/commit.rb +42 -2
- data/app/models/shipit/deploy.rb +31 -2
- data/app/models/shipit/deploy_spec/lerna_discovery.rb +43 -19
- data/app/models/shipit/deploy_spec/pypi_discovery.rb +5 -1
- data/app/models/shipit/rollback.rb +4 -2
- data/app/models/shipit/stack.rb +72 -15
- data/app/models/shipit/task.rb +30 -0
- data/app/models/shipit/undeployed_commit.rb +10 -1
- data/app/serializers/shipit/command_line_user_serializer.rb +4 -0
- data/app/views/layouts/shipit.html.erb +5 -1
- data/app/views/shipit/commits/_commit.html.erb +7 -2
- data/app/views/shipit/merge_status/_commit_count_warning.html.erb +1 -5
- data/app/views/shipit/merge_status/backlogged.html.erb +1 -1
- data/app/views/shipit/merge_status/failure.html.erb +1 -1
- data/app/views/shipit/merge_status/locked.html.erb +1 -1
- data/app/views/shipit/merge_status/success.html.erb +1 -1
- data/app/views/shipit/stacks/show.html.erb +10 -1
- data/app/views/shipit/tasks/_task_output.html.erb +2 -2
- data/config/locales/en.yml +2 -1
- data/config/routes.rb +8 -1
- data/db/migrate/20190502020249_add_lock_author_id_to_commits.rb +5 -0
- data/lib/shipit.rb +14 -2
- data/lib/shipit/cast_value.rb +9 -0
- data/lib/shipit/command.rb +62 -16
- data/lib/shipit/line_buffer.rb +42 -0
- data/lib/shipit/version.rb +1 -1
- data/lib/tasks/shipit.rake +27 -0
- data/test/controllers/api/release_statuses_controller_test.rb +66 -0
- data/test/controllers/api/stacks_controller_test.rb +19 -0
- data/test/controllers/commits_controller_test.rb +30 -6
- data/test/controllers/deploys_controller_test.rb +51 -2
- data/test/controllers/tasks_controller_test.rb +24 -0
- data/test/dummy/db/schema.rb +2 -1
- data/test/dummy/db/seeds.rb +2 -0
- data/test/fixtures/shipit/check_runs.yml +11 -0
- data/test/fixtures/shipit/commits.yml +104 -0
- data/test/fixtures/shipit/stacks.yml +98 -3
- data/test/fixtures/shipit/tasks.yml +42 -0
- data/test/jobs/update_github_last_deployed_ref_job_test.rb +88 -0
- data/test/models/commits_test.rb +88 -1
- data/test/models/deploy_spec_test.rb +34 -6
- data/test/models/deploys_test.rb +308 -6
- data/test/models/rollbacks_test.rb +17 -11
- data/test/models/stacks_test.rb +217 -4
- data/test/models/tasks_test.rb +13 -0
- data/test/models/undeployed_commits_test.rb +62 -3
- data/test/test_helper.rb +0 -1
- data/test/unit/command_test.rb +55 -0
- data/test/unit/line_buffer_test.rb +20 -0
- metadata +142 -128
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bdb3736fb9999c0409f7b81081a52467f33a39ee419f2a40e14e6aca73866263
|
4
|
+
data.tar.gz: f58bfcbbc8084bb18cf2234ed0a75658a5c6bc9e2b593bc8a2b90e5f89d030b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19ffafe44e93c6f02f277c5208160a605286557f5080cf3569355fcfa85b2a8583eace47a89c1ac223490ad157de451f0837343d5b818e891c9794b27ca20554
|
7
|
+
data.tar.gz: 57be6895de1d5757a48b603cfb4796316f477b791feef7cfc876dc57b5226d869e65b2332e920e695e3edbba3bd99b39dc75e0e3ceeab2b6aee851eaeea15c2e
|
data/README.md
CHANGED
@@ -35,6 +35,11 @@ This guide aims to help you [set up](#installation-and-setup), [use](#using-ship
|
|
35
35
|
* [Configuring providers](#configuring-providers)
|
36
36
|
* [Free samples](/examples/shipit.yml)
|
37
37
|
|
38
|
+
**IV. CONTRIBUTING**
|
39
|
+
|
40
|
+
* [Instructions](#contributing-instructions)
|
41
|
+
* [Local development](#contributing-local-dev)
|
42
|
+
|
38
43
|
* * *
|
39
44
|
|
40
45
|
<h2 id="installation-and-setup">I. INSTALLATION & SETUP</h2>
|
@@ -112,7 +117,7 @@ The settings in the `shipit.yml` file relate to the different things you can do
|
|
112
117
|
* [CI](#ci) (`ci.require`, `ci.hide`, `ci.allow_failures`)
|
113
118
|
* [Merge Queue](#merge-queue) (`merge.revalidate_after`, `merge.require`, `merge.ignore`, `merge.max_divergence`)
|
114
119
|
* [Custom Tasks](#custom-tasks) (`tasks`)
|
115
|
-
* [Custom links](#custom-links) (`links`)
|
120
|
+
* [Custom links](#custom-links) (`links`)
|
116
121
|
* [Review Process](#review-process) (`review.checklist`, `review.monitoring`, `review.checks`)
|
117
122
|
|
118
123
|
All the settings in `shipit.yml` are optional. Most applications can be deployed from Shipit without any configuration.
|
@@ -628,3 +633,27 @@ For Kubernetes, you have to provision Shipit environment with the following tool
|
|
628
633
|
|
629
634
|
* `kubectl`
|
630
635
|
* `kubernetes-deploy` [gem](https://github.com/Shopify/kubernetes-deploy)
|
636
|
+
|
637
|
+
<h2 id="contributing">IV. CONTRIBUTING</h2>
|
638
|
+
|
639
|
+
<h3 id="contributing-instructions">Instructions</h3>
|
640
|
+
|
641
|
+
1. Fork it ( https://github.com/shopify/shipit-engine/fork )
|
642
|
+
1. Create your feature branch (git checkout -b my-new-feature)
|
643
|
+
1. Commit your changes (git commit -am 'Add some feature')
|
644
|
+
1. Push to the branch (git push origin my-new-feature)
|
645
|
+
1. Create a new Pull Request
|
646
|
+
|
647
|
+
<h3 id="contributing-local-dev">Local development</h3>
|
648
|
+
|
649
|
+
This repository has a [test/dummy](/test/dummy) app in it which can be used for local development without having to setup a new rails application.
|
650
|
+
|
651
|
+
Run `./bin/bootstrap` in order to bootstrap the dummy application. The bootstrap script is going to:
|
652
|
+
|
653
|
+
- Copy `config/secrets.development.example.yml` to `config/secrets.development.yml`;
|
654
|
+
- Make sure all dependencies are installed;
|
655
|
+
- Create and seed database (recreate database if already available);
|
656
|
+
|
657
|
+
Run `./test/dummy/bin/rails server` to run the rails dummy application.
|
658
|
+
|
659
|
+
Set the environment variable `SHIPIT_DISABLE_AUTH=1` in order to disable authentication.
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Shipit
|
2
|
+
module Api
|
3
|
+
class ReleaseStatusesController < BaseController
|
4
|
+
require_permission :deploy, :stack
|
5
|
+
|
6
|
+
params do
|
7
|
+
requires :status, String
|
8
|
+
validates :status, inclusion: {in: %w(success failure)}
|
9
|
+
end
|
10
|
+
def create
|
11
|
+
deploy = stack.deploys_and_rollbacks.find(params[:deploy_id])
|
12
|
+
case params[:status]
|
13
|
+
when 'success'
|
14
|
+
deploy.report_healthy!(user: current_user)
|
15
|
+
when 'failure'
|
16
|
+
deploy.report_faulty!(user: current_user)
|
17
|
+
end
|
18
|
+
render_resource deploy, status: :created
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -2,7 +2,7 @@ module Shipit
|
|
2
2
|
module Api
|
3
3
|
class StacksController < BaseController
|
4
4
|
require_permission :read, :stack, only: %i(index show)
|
5
|
-
require_permission :write, :stack, only: %i(create)
|
5
|
+
require_permission :write, :stack, only: %i(create destroy)
|
6
6
|
|
7
7
|
def index
|
8
8
|
render_resources stacks
|
@@ -25,6 +25,11 @@ module Shipit
|
|
25
25
|
render_resource stack
|
26
26
|
end
|
27
27
|
|
28
|
+
def destroy
|
29
|
+
stack.schedule_for_destroy!
|
30
|
+
head :accepted
|
31
|
+
end
|
32
|
+
|
28
33
|
private
|
29
34
|
|
30
35
|
def stack
|
@@ -1,7 +1,14 @@
|
|
1
1
|
module Shipit
|
2
2
|
class CommitsController < ShipitController
|
3
3
|
def update
|
4
|
-
|
4
|
+
if update_params[:locked].present?
|
5
|
+
if Shipit::CastValue.to_boolean(update_params[:locked])
|
6
|
+
commit.lock(current_user)
|
7
|
+
else
|
8
|
+
commit.unlock
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
5
12
|
head :ok
|
6
13
|
end
|
7
14
|
|
@@ -14,5 +21,9 @@ module Shipit
|
|
14
21
|
def stack
|
15
22
|
@stack ||= Stack.from_param!(params[:stack_id])
|
16
23
|
end
|
24
|
+
|
25
|
+
def update_params
|
26
|
+
@update_params ||= params.require(:commit).permit(:locked)
|
27
|
+
end
|
17
28
|
end
|
18
29
|
end
|
@@ -5,6 +5,7 @@ module Shipit
|
|
5
5
|
before_action :load_stack
|
6
6
|
before_action :load_deploy, only: %i(show rollback revert)
|
7
7
|
before_action :load_until_commit, only: :create
|
8
|
+
helper_method :short_commit_sha
|
8
9
|
|
9
10
|
def new
|
10
11
|
@commit = @stack.commits.by_sha!(params[:sha])
|
@@ -40,6 +41,12 @@ module Shipit
|
|
40
41
|
redirect_to rollback_stack_deploy_path(@stack, previous_deploy)
|
41
42
|
end
|
42
43
|
|
44
|
+
def short_commit_sha(task)
|
45
|
+
if previous_successful_deploy_commit(task)
|
46
|
+
@short_commit_sha ||= @previous_successful_deploy_commit&.short_sha
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
43
50
|
private
|
44
51
|
|
45
52
|
def load_deploy
|
@@ -57,5 +64,9 @@ module Shipit
|
|
57
64
|
def deploy_params
|
58
65
|
@deploy_params ||= params.require(:deploy).permit(:until_commit_id, env: @stack.deploy_variables.map(&:name))
|
59
66
|
end
|
67
|
+
|
68
|
+
def previous_successful_deploy_commit(task)
|
69
|
+
@previous_successful_deploy_commit ||= task.commit_to_rollback_to
|
70
|
+
end
|
60
71
|
end
|
61
72
|
end
|
@@ -17,7 +17,28 @@ module Shipit
|
|
17
17
|
return if flash.empty? && !stale?(last_modified: @stack.updated_at)
|
18
18
|
|
19
19
|
@tasks = @stack.tasks.order(id: :desc).preload(:since_commit, :until_commit, :user).limit(10)
|
20
|
-
|
20
|
+
|
21
|
+
commits = @stack.undeployed_commits do |scope|
|
22
|
+
scope.preload(:author, :statuses, :check_runs, :lock_author)
|
23
|
+
end
|
24
|
+
|
25
|
+
next_expected_commit_to_deploy = @stack.next_expected_commit_to_deploy(commits: commits)
|
26
|
+
|
27
|
+
@active_commits = []
|
28
|
+
@undeployed_commits = []
|
29
|
+
|
30
|
+
commits.each do |commit|
|
31
|
+
(commit.active? ? @active_commits : @undeployed_commits) << commit
|
32
|
+
end
|
33
|
+
|
34
|
+
@active_commits = map_to_undeployed_commit(
|
35
|
+
@active_commits,
|
36
|
+
next_expected_commit_to_deploy: next_expected_commit_to_deploy,
|
37
|
+
)
|
38
|
+
@undeployed_commits = map_to_undeployed_commit(
|
39
|
+
@undeployed_commits,
|
40
|
+
next_expected_commit_to_deploy: next_expected_commit_to_deploy,
|
41
|
+
)
|
21
42
|
end
|
22
43
|
|
23
44
|
def lookup
|
@@ -73,6 +94,13 @@ module Shipit
|
|
73
94
|
|
74
95
|
private
|
75
96
|
|
97
|
+
def map_to_undeployed_commit(commits, next_expected_commit_to_deploy:)
|
98
|
+
commits.map.with_index do |c, i|
|
99
|
+
index = commits.size - i - 1
|
100
|
+
UndeployedCommit.new(c, index: index, next_expected_commit_to_deploy: next_expected_commit_to_deploy)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
76
104
|
def load_stack
|
77
105
|
@stack = Stack.from_param!(params[:id])
|
78
106
|
end
|
@@ -2,7 +2,7 @@ module Shipit
|
|
2
2
|
class TasksController < ShipitController
|
3
3
|
include Pagination
|
4
4
|
|
5
|
-
before_action :stack
|
5
|
+
before_action :stack, except: [:lookup]
|
6
6
|
|
7
7
|
self.default_page_size = 20
|
8
8
|
|
@@ -50,8 +50,20 @@ module Shipit
|
|
50
50
|
render json: TailTaskSerializer.new(task, context: params)
|
51
51
|
end
|
52
52
|
|
53
|
+
def lookup
|
54
|
+
@task = Task.find(params[:id])
|
55
|
+
|
56
|
+
redirect_to url_for_task
|
57
|
+
end
|
58
|
+
|
53
59
|
private
|
54
60
|
|
61
|
+
def url_for_task
|
62
|
+
base_task = @task.is_a?(Deploy) ? @task.becomes(Deploy) : @task
|
63
|
+
|
64
|
+
url_for([base_task.stack, base_task])
|
65
|
+
end
|
66
|
+
|
55
67
|
def task
|
56
68
|
@task ||= stack.tasks.find(params[:id])
|
57
69
|
end
|
@@ -1,9 +1,7 @@
|
|
1
1
|
module Shipit
|
2
2
|
module StacksHelper
|
3
|
-
COMMIT_TITLE_LENGTH = 79
|
4
|
-
|
5
3
|
def redeploy_button(deployed_commit)
|
6
|
-
commit = UndeployedCommit.new(deployed_commit, 0)
|
4
|
+
commit = UndeployedCommit.new(deployed_commit, index: 0)
|
7
5
|
url = new_stack_deploy_path(commit.stack, sha: commit.sha)
|
8
6
|
classes = %W(btn btn--primary deploy-action #{commit.state})
|
9
7
|
|
@@ -46,7 +44,7 @@ module Shipit
|
|
46
44
|
end
|
47
45
|
|
48
46
|
def render_commit_message(pull_request_or_commit)
|
49
|
-
message = pull_request_or_commit.title.to_s
|
47
|
+
message = pull_request_or_commit.title.to_s
|
50
48
|
content_tag(:span, emojify(message), class: 'event-message')
|
51
49
|
end
|
52
50
|
|
@@ -80,5 +78,13 @@ module Shipit
|
|
80
78
|
def render_raw_commit_id_link(commit)
|
81
79
|
link_to(commit.short_sha, github_commit_url(commit), target: '_blank', class: 'number')
|
82
80
|
end
|
81
|
+
|
82
|
+
def unlock_commit_tooltip(commit)
|
83
|
+
if commit.lock_author.present?
|
84
|
+
t('commit.unlock_with_author', author: commit.lock_author.name)
|
85
|
+
else
|
86
|
+
t('commit.unlock')
|
87
|
+
end
|
88
|
+
end
|
83
89
|
end
|
84
90
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Shipit
|
2
|
+
class UpdateGithubLastDeployedRefJob < BackgroundJob
|
3
|
+
queue_as :default
|
4
|
+
|
5
|
+
DEPLOY_PREFIX = 'shipit-deploy'.freeze
|
6
|
+
|
7
|
+
def perform(stack)
|
8
|
+
stack_sha = stack.last_successful_deploy_commit&.sha
|
9
|
+
return unless stack_sha
|
10
|
+
|
11
|
+
environment = stack.environment
|
12
|
+
stack_ref = create_full_ref(environment)
|
13
|
+
client = Shipit.github.api
|
14
|
+
|
15
|
+
full_repo_name = stack.github_repo_name
|
16
|
+
|
17
|
+
update_or_create_ref(client: client, repo_name: full_repo_name, ref: stack_ref, new_sha: stack_sha)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def create_full_ref(stack_environment)
|
23
|
+
[DEPLOY_PREFIX, stack_environment].join("/")
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_ref(client:, repo_name:, ref:, sha:)
|
27
|
+
client.create_ref(repo_name, ref, sha)
|
28
|
+
end
|
29
|
+
|
30
|
+
def update_or_create_ref(client:, repo_name:, ref:, new_sha:)
|
31
|
+
client.update_ref(repo_name, ref, new_sha)
|
32
|
+
rescue Octokit::UnprocessableEntity => e
|
33
|
+
error_msg = e.message
|
34
|
+
if error_msg.include? "Reference does not exist"
|
35
|
+
create_ref(client: client, repo_name: repo_name, ref: ref, sha: new_sha)
|
36
|
+
else
|
37
|
+
raise
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Shipit
|
2
|
+
class CommandLineUser
|
3
|
+
def present?
|
4
|
+
false
|
5
|
+
end
|
6
|
+
|
7
|
+
def email
|
8
|
+
'command_line@example.com'
|
9
|
+
end
|
10
|
+
|
11
|
+
def login
|
12
|
+
'command_line'
|
13
|
+
end
|
14
|
+
|
15
|
+
def name
|
16
|
+
'CommandLine'
|
17
|
+
end
|
18
|
+
|
19
|
+
def avatar_url
|
20
|
+
'https://github.com/images/error/octocat_happy.gif'
|
21
|
+
end
|
22
|
+
|
23
|
+
def id
|
24
|
+
end
|
25
|
+
|
26
|
+
def github_id
|
27
|
+
end
|
28
|
+
|
29
|
+
def logged_in?
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def authorized?
|
34
|
+
Shipit.authentication_disabled?
|
35
|
+
end
|
36
|
+
|
37
|
+
def stacks_contributed_to
|
38
|
+
[]
|
39
|
+
end
|
40
|
+
|
41
|
+
def avatar_uri
|
42
|
+
User::DEFAULT_AVATAR.dup
|
43
|
+
end
|
44
|
+
|
45
|
+
def created_at
|
46
|
+
Time.at(0).utc
|
47
|
+
end
|
48
|
+
alias_method :updated_at, :created_at
|
49
|
+
|
50
|
+
def read_attribute_for_serialization(attr)
|
51
|
+
public_send(attr)
|
52
|
+
end
|
53
|
+
|
54
|
+
def github_api
|
55
|
+
Shipit.github.api
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/app/models/shipit/commit.rb
CHANGED
@@ -7,7 +7,7 @@ module Shipit
|
|
7
7
|
belongs_to :stack
|
8
8
|
has_many :deploys
|
9
9
|
has_many :statuses, -> { order(created_at: :desc) }, dependent: :destroy, inverse_of: :commit
|
10
|
-
has_many :check_runs, dependent: :destroy
|
10
|
+
has_many :check_runs, -> { order(created_at: :desc) }, dependent: :destroy, inverse_of: :commit
|
11
11
|
has_many :commit_deployments, dependent: :destroy
|
12
12
|
has_many :release_statuses, dependent: :destroy
|
13
13
|
belongs_to :pull_request, inverse_of: :merge_commit, optional: true
|
@@ -23,6 +23,7 @@ module Shipit
|
|
23
23
|
|
24
24
|
belongs_to :author, class_name: 'User', inverse_of: :authored_commits
|
25
25
|
belongs_to :committer, class_name: 'User', inverse_of: :commits
|
26
|
+
belongs_to :lock_author, class_name: :User, optional: true, inverse_of: false
|
26
27
|
|
27
28
|
def author
|
28
29
|
super || AnonymousUser.new
|
@@ -32,6 +33,10 @@ module Shipit
|
|
32
33
|
super || AnonymousUser.new
|
33
34
|
end
|
34
35
|
|
36
|
+
def lock_author
|
37
|
+
super || AnonymousUser.new
|
38
|
+
end
|
39
|
+
|
35
40
|
scope :reachable, -> { where(detached: false) }
|
36
41
|
|
37
42
|
delegate :broadcast_update, :github_repo_name, :hidden_statuses, :required_statuses, :blocking_statuses,
|
@@ -47,6 +52,11 @@ module Shipit
|
|
47
52
|
where('id < ?', commit.try(:id) || commit)
|
48
53
|
end
|
49
54
|
|
55
|
+
def self.since(commit)
|
56
|
+
return all unless commit
|
57
|
+
where('id >= ?', commit.try(:id) || commit)
|
58
|
+
end
|
59
|
+
|
50
60
|
def self.until(commit)
|
51
61
|
return all unless commit
|
52
62
|
where('id <= ?', commit.try(:id) || commit)
|
@@ -164,6 +174,18 @@ module Shipit
|
|
164
174
|
|
165
175
|
delegate :pending?, :success?, :error?, :failure?, :blocking?, :state, to: :status
|
166
176
|
|
177
|
+
def active?
|
178
|
+
return false unless stack.active_task?
|
179
|
+
|
180
|
+
active_task = stack.active_task
|
181
|
+
|
182
|
+
if active_task.since_commit == active_task.until_commit
|
183
|
+
id == active_task.since_commit.id
|
184
|
+
else
|
185
|
+
id > active_task.since_commit.id && id <= active_task.until_commit.id
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
167
189
|
def deployable?
|
168
190
|
!locked? && (stack.ignore_ci? || (success? && !blocked?))
|
169
191
|
end
|
@@ -198,7 +220,7 @@ module Shipit
|
|
198
220
|
end
|
199
221
|
|
200
222
|
def message_header
|
201
|
-
message.lines.first.strip
|
223
|
+
message.lines.first.to_s.strip
|
202
224
|
end
|
203
225
|
|
204
226
|
# TODO: remove in a few versions when it is assumed the commits table was backfilled
|
@@ -271,6 +293,24 @@ module Shipit
|
|
271
293
|
end
|
272
294
|
end
|
273
295
|
|
296
|
+
def lock(user)
|
297
|
+
update!(
|
298
|
+
locked: true,
|
299
|
+
lock_author_id: user.id,
|
300
|
+
)
|
301
|
+
end
|
302
|
+
|
303
|
+
def self.lock_all(user)
|
304
|
+
update_all(
|
305
|
+
locked: true,
|
306
|
+
lock_author_id: user.id,
|
307
|
+
)
|
308
|
+
end
|
309
|
+
|
310
|
+
def unlock
|
311
|
+
update!(locked: false, lock_author: nil)
|
312
|
+
end
|
313
|
+
|
274
314
|
private
|
275
315
|
|
276
316
|
def message_parser
|