shipit-engine 0.31.0 → 0.32.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 +4 -2
- data/Rakefile +4 -2
- data/app/assets/stylesheets/_pages/_stacks.scss +0 -3
- data/app/assets/stylesheets/merge_status.scss +0 -3
- data/app/controllers/concerns/shipit/active_model_serializers_patch.rb +1 -0
- data/app/controllers/concerns/shipit/api/cacheable.rb +1 -0
- data/app/controllers/concerns/shipit/api/paginable.rb +3 -2
- data/app/controllers/concerns/shipit/api/rendering.rb +5 -4
- data/app/controllers/concerns/shipit/authentication.rb +3 -2
- data/app/controllers/concerns/shipit/pagination.rb +2 -1
- data/app/controllers/shipit/api/base_controller.rb +11 -6
- data/app/controllers/shipit/api/ccmenu_controller.rb +2 -1
- data/app/controllers/shipit/api/commits_controller.rb +2 -1
- data/app/controllers/shipit/api/deploys_controller.rb +4 -3
- data/app/controllers/shipit/api/hooks_controller.rb +6 -5
- data/app/controllers/shipit/api/locks_controller.rb +5 -4
- data/app/controllers/shipit/api/outputs_controller.rb +2 -1
- data/app/controllers/shipit/api/pull_requests_controller.rb +7 -6
- data/app/controllers/shipit/api/release_statuses_controller.rb +3 -2
- data/app/controllers/shipit/api/rollbacks_controller.rb +33 -0
- data/app/controllers/shipit/api/stacks_controller.rb +18 -5
- data/app/controllers/shipit/api/tasks_controller.rb +6 -5
- data/app/controllers/shipit/api_clients_controller.rb +4 -3
- data/app/controllers/shipit/ccmenu_url_controller.rb +4 -3
- data/app/controllers/shipit/commit_checks_controller.rb +2 -1
- data/app/controllers/shipit/commits_controller.rb +2 -1
- data/app/controllers/shipit/deploys_controller.rb +3 -2
- data/app/controllers/shipit/github_authentication_controller.rb +4 -3
- data/app/controllers/shipit/merge_status_controller.rb +17 -16
- data/app/controllers/shipit/pull_requests_controller.rb +3 -2
- data/app/controllers/shipit/release_statuses_controller.rb +3 -2
- data/app/controllers/shipit/rollbacks_controller.rb +3 -2
- data/app/controllers/shipit/shipit_controller.rb +2 -1
- data/app/controllers/shipit/stacks_controller.rb +22 -7
- data/app/controllers/shipit/status_controller.rb +2 -1
- data/app/controllers/shipit/tasks_controller.rb +6 -5
- data/app/controllers/shipit/webhooks_controller.rb +3 -2
- data/app/helpers/shipit/chunks_helper.rb +1 -0
- data/app/helpers/shipit/deploys_helper.rb +4 -3
- data/app/helpers/shipit/github_url_helper.rb +1 -0
- data/app/helpers/shipit/merge_status_helper.rb +1 -0
- data/app/helpers/shipit/shipit_helper.rb +1 -0
- data/app/helpers/shipit/stacks_helper.rb +1 -0
- data/app/helpers/shipit/tasks_helper.rb +1 -0
- data/app/jobs/shipit/background_job.rb +4 -0
- data/app/jobs/shipit/background_job/unique.rb +1 -0
- data/app/jobs/shipit/cache_deploy_spec_job.rb +1 -0
- data/app/jobs/shipit/chunk_rollup_job.rb +4 -0
- data/app/jobs/shipit/clear_git_cache_job.rb +1 -0
- data/app/jobs/shipit/continuous_delivery_job.rb +2 -1
- data/app/jobs/shipit/create_on_github_job.rb +6 -1
- data/app/jobs/shipit/create_release_statuses_job.rb +1 -0
- data/app/jobs/shipit/deferred_touch_job.rb +4 -0
- data/app/jobs/shipit/deliver_hook_job.rb +1 -0
- data/app/jobs/shipit/destroy_job.rb +1 -0
- data/app/jobs/shipit/destroy_stack_job.rb +1 -0
- data/app/jobs/shipit/emit_event_job.rb +2 -1
- data/app/jobs/shipit/fetch_commit_stats_job.rb +1 -0
- data/app/jobs/shipit/fetch_deployed_revision_job.rb +1 -0
- data/app/jobs/shipit/github_sync_job.rb +2 -1
- data/app/jobs/shipit/mark_deploy_healthy_job.rb +1 -0
- data/app/jobs/shipit/merge_pull_requests_job.rb +1 -0
- data/app/jobs/shipit/perform_commit_checks_job.rb +1 -0
- data/app/jobs/shipit/perform_task_job.rb +12 -5
- data/app/jobs/shipit/purge_old_deliveries_job.rb +1 -0
- data/app/jobs/shipit/reap_dead_tasks_job.rb +21 -0
- data/app/jobs/shipit/refresh_check_runs_job.rb +1 -0
- data/app/jobs/shipit/refresh_github_user_job.rb +1 -0
- data/app/jobs/shipit/refresh_pull_request_job.rb +1 -0
- data/app/jobs/shipit/refresh_statuses_job.rb +1 -0
- data/app/jobs/shipit/setup_github_hook_job.rb +1 -0
- data/app/jobs/shipit/update_estimated_deploy_duration_job.rb +1 -0
- data/app/jobs/shipit/update_github_last_deployed_ref_job.rb +4 -3
- data/app/models/concerns/shipit/deferred_touch.rb +4 -3
- data/app/models/shipit/anonymous_user.rb +5 -0
- data/app/models/shipit/api_client.rb +3 -2
- data/app/models/shipit/application_record.rb +2 -1
- data/app/models/shipit/check_run.rb +4 -3
- data/app/models/shipit/command_line_user.rb +1 -0
- data/app/models/shipit/commit.rb +11 -4
- data/app/models/shipit/commit_checks.rb +1 -0
- data/app/models/shipit/commit_deployment.rb +3 -2
- data/app/models/shipit/commit_deployment_status.rb +2 -1
- data/app/models/shipit/commit_message.rb +1 -0
- data/app/models/shipit/delivery.rb +4 -3
- data/app/models/shipit/deploy.rb +6 -17
- data/app/models/shipit/deploy_spec.rb +22 -3
- data/app/models/shipit/deploy_spec/bundler_discovery.rb +1 -0
- data/app/models/shipit/deploy_spec/capistrano_discovery.rb +1 -0
- data/app/models/shipit/deploy_spec/file_system.rb +10 -3
- data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +1 -0
- data/app/models/shipit/deploy_spec/lerna_discovery.rb +1 -0
- data/app/models/shipit/deploy_spec/npm_discovery.rb +5 -4
- data/app/models/shipit/deploy_spec/pypi_discovery.rb +1 -0
- data/app/models/shipit/deploy_spec/rubygems_discovery.rb +1 -0
- data/app/models/shipit/deploy_stats.rb +2 -1
- data/app/models/shipit/duration.rb +3 -2
- data/app/models/shipit/ephemeral_commit_checks.rb +1 -0
- data/app/models/shipit/github_hook.rb +2 -1
- data/app/models/shipit/github_status.rb +2 -1
- data/app/models/shipit/hook.rb +6 -5
- data/app/models/shipit/membership.rb +3 -2
- data/app/models/shipit/output_chunk.rb +7 -2
- data/app/models/shipit/pull_request.rb +6 -5
- data/app/models/shipit/record.rb +18 -0
- data/app/models/shipit/release_status.rb +3 -2
- data/app/models/shipit/repository.rb +10 -5
- data/app/models/shipit/rollback.rb +1 -0
- data/app/models/shipit/stack.rb +50 -16
- data/app/models/shipit/status.rb +3 -2
- data/app/models/shipit/status/common.rb +7 -6
- data/app/models/shipit/status/group.rb +1 -0
- data/app/models/shipit/status/missing.rb +2 -1
- data/app/models/shipit/status/unknown.rb +2 -1
- data/app/models/shipit/task.rb +38 -5
- data/app/models/shipit/task_definition.rb +1 -0
- data/app/models/shipit/team.rb +2 -1
- data/app/models/shipit/undeployed_commit.rb +1 -0
- data/app/models/shipit/unlimited_api_client.rb +1 -0
- data/app/models/shipit/user.rb +10 -8
- data/app/models/shipit/variable_definition.rb +1 -0
- data/app/models/shipit/webhooks.rb +1 -0
- data/app/models/shipit/webhooks/handlers/check_suite_handler.rb +1 -0
- data/app/models/shipit/webhooks/handlers/handler.rb +1 -0
- data/app/models/shipit/webhooks/handlers/membership_handler.rb +1 -0
- data/app/models/shipit/webhooks/handlers/push_handler.rb +1 -0
- data/app/models/shipit/webhooks/handlers/status_handler.rb +1 -0
- data/app/serializers/concerns/shipit/conditional_attributes.rb +1 -0
- data/app/serializers/shipit/anonymous_user_serializer.rb +1 -0
- data/app/serializers/shipit/command_line_user_serializer.rb +1 -0
- data/app/serializers/shipit/commit_serializer.rb +1 -0
- data/app/serializers/shipit/deploy_serializer.rb +2 -1
- data/app/serializers/shipit/hook_serializer.rb +1 -0
- data/app/serializers/shipit/pull_request_serializer.rb +1 -0
- data/app/serializers/shipit/rollback_serializer.rb +1 -0
- data/app/serializers/shipit/short_commit_serializer.rb +1 -0
- data/app/serializers/shipit/stack_serializer.rb +1 -0
- data/app/serializers/shipit/tail_task_serializer.rb +1 -0
- data/app/serializers/shipit/task_serializer.rb +2 -17
- data/app/serializers/shipit/user_serializer.rb +6 -1
- data/app/validators/ascii_only_validator.rb +1 -0
- data/app/validators/subset_validator.rb +1 -0
- data/app/views/layouts/shipit.html.erb +1 -1
- data/app/views/shipit/_variables.html.erb +1 -1
- data/app/views/shipit/ccmenu/project.xml.builder +2 -1
- data/app/views/shipit/deploys/show.html.erb +2 -2
- data/app/views/shipit/stacks/_header.html.erb +3 -0
- data/app/views/shipit/stacks/all_tasks.html.erb +28 -0
- data/app/views/shipit/stacks/index.html.erb +1 -1
- data/app/views/shipit/tasks/show.html.erb +1 -1
- data/config/initializers/inflections.rb +2 -1
- data/config/locales/en.yml +3 -3
- data/config/routes.rb +10 -2
- data/db/migrate/20200226211925_add_index_to_tasks_status.rb +5 -0
- data/db/migrate/20200427135152_add_pull_request_head_sha_to_commit.rb +5 -0
- data/db/migrate/20200615181558_add_rollback_once_aborted_to.rb +5 -0
- data/lib/shipit.rb +12 -2
- data/lib/shipit/cast_value.rb +1 -0
- data/lib/shipit/command.rb +13 -12
- data/lib/shipit/commands.rb +5 -4
- data/lib/shipit/csv_serializer.rb +1 -0
- data/lib/shipit/deploy_commands.rb +1 -0
- data/lib/shipit/engine.rb +7 -2
- data/lib/shipit/environment_variables.rb +2 -1
- data/lib/shipit/first_parent_commits_iterator.rb +1 -0
- data/lib/shipit/flock.rb +1 -0
- data/lib/shipit/github_app.rb +7 -6
- data/lib/shipit/github_http_cache_middleware.rb +1 -0
- data/lib/shipit/null_serializer.rb +1 -0
- data/lib/shipit/octokit_check_runs.rb +3 -2
- data/lib/shipit/octokit_iterator.rb +3 -2
- data/lib/shipit/paginator.rb +3 -2
- data/lib/shipit/rollback_commands.rb +1 -0
- data/lib/shipit/same_site_cookie_middleware.rb +29 -0
- data/lib/shipit/simple_message_verifier.rb +1 -0
- data/lib/shipit/stack_commands.rb +3 -2
- data/lib/shipit/stat.rb +1 -0
- data/lib/shipit/task_commands.rb +2 -0
- data/lib/shipit/version.rb +2 -1
- data/lib/snippets/release-gem +5 -1
- data/lib/tasks/cron.rake +2 -0
- data/lib/tasks/dev.rake +3 -2
- data/lib/tasks/shipit.rake +15 -14
- data/lib/tasks/teams.rake +1 -0
- data/test/controllers/api/base_controller_test.rb +3 -2
- data/test/controllers/api/ccmenu_controller_test.rb +8 -7
- data/test/controllers/api/commits_controller_test.rb +3 -2
- data/test/controllers/api/deploys_controller_test.rb +15 -14
- data/test/controllers/api/hooks_controller_test.rb +8 -7
- data/test/controllers/api/locks_controller_test.rb +7 -6
- data/test/controllers/api/outputs_controller_test.rb +2 -1
- data/test/controllers/api/pull_requests_controller_test.rb +8 -7
- data/test/controllers/api/release_statuses_controller_test.rb +2 -1
- data/test/controllers/api/rollback_controller_test.rb +113 -0
- data/test/controllers/api/stacks_controller_test.rb +30 -9
- data/test/controllers/api/tasks_controller_test.rb +13 -12
- data/test/controllers/api_clients_controller_test.rb +5 -4
- data/test/controllers/ccmenu_controller_test.rb +4 -3
- data/test/controllers/commit_checks_controller_test.rb +4 -3
- data/test/controllers/commits_controller_test.rb +3 -2
- data/test/controllers/deploys_controller_test.rb +32 -21
- data/test/controllers/github_authentication_controller_test.rb +1 -0
- data/test/controllers/merge_status_controller_test.rb +7 -6
- data/test/controllers/pull_requests_controller_test.rb +4 -3
- data/test/controllers/release_statuses_controller_test.rb +3 -2
- data/test/controllers/rollbacks_controller_test.rb +9 -8
- data/test/controllers/stacks_controller_test.rb +33 -19
- data/test/controllers/status_controller_test.rb +1 -0
- data/test/controllers/tasks_controller_test.rb +19 -18
- data/test/controllers/webhooks_controller_test.rb +11 -10
- data/test/dummy/config/environments/development.rb +2 -0
- data/test/dummy/config/environments/test.rb +2 -0
- data/test/dummy/db/schema.rb +4 -1
- data/test/fixtures/shipit/commits.yml +15 -0
- data/test/fixtures/shipit/repositories.yml +4 -0
- data/test/fixtures/shipit/stacks.yml +90 -14
- data/test/fixtures/timeout +2 -1
- data/test/helpers/api_helper.rb +1 -0
- data/test/helpers/fixture_aliases_helper.rb +1 -0
- data/test/helpers/hooks_helper.rb +2 -1
- data/test/helpers/json_helper.rb +15 -11
- data/test/helpers/links_helper.rb +4 -3
- data/test/helpers/payloads_helper.rb +1 -0
- data/test/helpers/queries_helper.rb +3 -2
- data/test/jobs/cache_deploy_spec_job_test.rb +2 -1
- data/test/jobs/chunk_rollup_job_test.rb +1 -0
- data/test/jobs/deliver_hook_job_test.rb +1 -0
- data/test/jobs/destroy_stack_job_test.rb +1 -0
- data/test/jobs/emit_event_job_test.rb +2 -1
- data/test/jobs/fetch_commit_stats_job_test.rb +1 -0
- data/test/jobs/fetch_deployed_revision_job_test.rb +1 -0
- data/test/jobs/github_sync_job_test.rb +1 -0
- data/test/jobs/mark_deploy_healthy_job_test.rb +1 -0
- data/test/jobs/merge_pull_requests_job_test.rb +1 -0
- data/test/jobs/perform_task_job_test.rb +4 -3
- data/test/jobs/purge_old_deliveries_job_test.rb +1 -0
- data/test/jobs/reap_dead_tasks_job_test.rb +68 -0
- data/test/jobs/refresh_github_user_job_test.rb +1 -0
- data/test/jobs/refresh_status_job_test.rb +1 -0
- data/test/jobs/unique_job_test.rb +1 -0
- data/test/jobs/update_github_last_deployed_ref_job_test.rb +1 -0
- data/test/middleware/same_site_cookie_middleware_test.rb +52 -0
- data/test/models/api_client_test.rb +1 -0
- data/test/models/commit_checks_test.rb +1 -0
- data/test/models/commit_deployment_status_test.rb +1 -0
- data/test/models/commit_deployment_test.rb +2 -1
- data/test/models/commits_test.rb +72 -6
- data/test/models/delivery_test.rb +2 -1
- data/test/models/deploy_spec_test.rb +47 -42
- data/test/models/deploy_stats_test.rb +1 -0
- data/test/models/deploys_test.rb +31 -22
- data/test/models/duration_test.rb +1 -0
- data/test/models/github_hook_test.rb +1 -0
- data/test/models/hook_test.rb +18 -10
- data/test/models/membership_test.rb +1 -0
- data/test/models/output_chunk_test.rb +1 -0
- data/test/models/pull_request_test.rb +4 -3
- data/test/models/release_statuses_test.rb +1 -0
- data/test/models/rollbacks_test.rb +1 -0
- data/test/models/shipit/check_run_test.rb +1 -0
- data/test/models/shipit/repository_test.rb +1 -0
- data/test/models/shipit/wehbooks/handlers_test.rb +1 -0
- data/test/models/stacks_test.rb +30 -8
- data/test/models/status/group_test.rb +1 -0
- data/test/models/status/missing_test.rb +1 -0
- data/test/models/status_test.rb +1 -0
- data/test/models/task_definitions_test.rb +9 -8
- data/test/models/tasks_test.rb +18 -1
- data/test/models/team_test.rb +4 -2
- data/test/models/undeployed_commits_test.rb +1 -0
- data/test/models/users_test.rb +13 -5
- data/test/test_command_integration.rb +3 -2
- data/test/test_helper.rb +34 -31
- data/test/unit/anonymous_user_serializer_test.rb +14 -0
- data/test/unit/command_test.rb +8 -7
- data/test/unit/commands_test.rb +1 -0
- data/test/unit/commit_serializer_test.rb +16 -0
- data/test/unit/csv_serializer_test.rb +3 -2
- data/test/unit/deploy_commands_test.rb +5 -4
- data/test/unit/deploy_serializer_test.rb +17 -0
- data/test/unit/environment_variables_test.rb +5 -4
- data/test/unit/github_app_test.rb +1 -0
- data/test/unit/github_url_helper_test.rb +1 -0
- data/test/unit/rollback_commands_test.rb +2 -1
- data/test/unit/shipit_helper_test.rb +17 -0
- data/test/unit/shipit_test.rb +1 -0
- data/test/unit/user_serializer_test.rb +14 -0
- data/test/unit/variable_definition_test.rb +1 -0
- metadata +155 -130
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'test_helper'
|
|
2
3
|
|
|
3
4
|
module Shipit
|
|
@@ -10,26 +11,26 @@ module Shipit
|
|
|
10
11
|
|
|
11
12
|
test "a request with insufficient permissions will render a 403" do
|
|
12
13
|
@client.update!(permissions: [])
|
|
13
|
-
get :show, params: {stack_id: @stack.to_param}
|
|
14
|
+
get :show, params: { stack_id: @stack.to_param }
|
|
14
15
|
assert_response :forbidden
|
|
15
16
|
assert_json 'message', 'This operation requires the `read:stack` permission'
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
test "#show renders the xml" do
|
|
19
|
-
get :show, params: {stack_id: @stack.to_param}
|
|
20
|
+
get :show, params: { stack_id: @stack.to_param }
|
|
20
21
|
assert_response :ok
|
|
21
22
|
assert_payload 'name', @stack.to_param
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
test "can authenticate with query string token" do
|
|
25
26
|
request.headers['Authorization'] = 'bleh'
|
|
26
|
-
get :show, params: {stack_id: @stack.to_param, token: @client.authentication_token}
|
|
27
|
+
get :show, params: { stack_id: @stack.to_param, token: @client.authentication_token }
|
|
27
28
|
assert_response :ok
|
|
28
29
|
assert_payload 'name', @stack.to_param
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
test "xml contains required attributes" do
|
|
32
|
-
get :show, params: {stack_id: @stack.to_param}
|
|
33
|
+
get :show, params: { stack_id: @stack.to_param }
|
|
33
34
|
project = get_project_from_xml(response.body)
|
|
34
35
|
%w(name activity lastBuildStatus lastBuildLabel lastBuildTime webUrl).each do |attribute|
|
|
35
36
|
assert_includes project, attribute, "Response missing required attribute: #{attribute}"
|
|
@@ -38,13 +39,13 @@ module Shipit
|
|
|
38
39
|
|
|
39
40
|
test "locked stacks show as failed" do
|
|
40
41
|
@stack.lock('test', @user)
|
|
41
|
-
get :show, params: {stack_id: @stack.to_param}
|
|
42
|
+
get :show, params: { stack_id: @stack.to_param }
|
|
42
43
|
assert_payload 'lastBuildStatus', 'Failure'
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
test "stacks with no deploys render correctly" do
|
|
46
47
|
stack = Stack.create!(repository: Repository.new(owner: "foo", name: "bar"))
|
|
47
|
-
get :show, params: {stack_id: stack.to_param}
|
|
48
|
+
get :show, params: { stack_id: stack.to_param }
|
|
48
49
|
assert_payload 'lastBuildStatus', 'Success'
|
|
49
50
|
end
|
|
50
51
|
|
|
@@ -56,7 +57,7 @@ module Shipit
|
|
|
56
57
|
|
|
57
58
|
def assert_payload(k, v)
|
|
58
59
|
@project ||= get_project_from_xml(response.body)
|
|
59
|
-
assert_equal
|
|
60
|
+
assert_equal(v, @project[k])
|
|
60
61
|
end
|
|
61
62
|
end
|
|
62
63
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'test_helper'
|
|
2
3
|
|
|
3
4
|
module Shipit
|
|
@@ -11,7 +12,7 @@ module Shipit
|
|
|
11
12
|
test "#index returns a list of commits" do
|
|
12
13
|
commit = @stack.commits.reachable.last
|
|
13
14
|
|
|
14
|
-
get :index, params: {stack_id: @stack.to_param}
|
|
15
|
+
get :index, params: { stack_id: @stack.to_param }
|
|
15
16
|
assert_response :ok
|
|
16
17
|
assert_json '0.sha', commit.sha
|
|
17
18
|
end
|
|
@@ -19,7 +20,7 @@ module Shipit
|
|
|
19
20
|
test "#index with undeployed=1 returns a list of undeployed commits" do
|
|
20
21
|
commits = @stack.undeployed_commits.pluck(:sha)
|
|
21
22
|
|
|
22
|
-
get :index, params: {stack_id: @stack.to_param, undeployed: 1}
|
|
23
|
+
get :index, params: { stack_id: @stack.to_param, undeployed: 1 }
|
|
23
24
|
assert_response :ok
|
|
24
25
|
JSON.parse(response.body).each do |commit|
|
|
25
26
|
assert commits.include?(commit.fetch("sha"))
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'test_helper'
|
|
2
3
|
|
|
3
4
|
module Shipit
|
|
@@ -13,7 +14,7 @@ module Shipit
|
|
|
13
14
|
test "#deploys returns the deploys and revisions for a given stack" do
|
|
14
15
|
tasks = @stack.deploys_and_rollbacks.order(id: :desc)
|
|
15
16
|
|
|
16
|
-
get :index, params: {stack_id: @stack.to_param}
|
|
17
|
+
get :index, params: { stack_id: @stack.to_param }
|
|
17
18
|
assert_response :ok
|
|
18
19
|
|
|
19
20
|
(0...tasks.length).each do |i|
|
|
@@ -23,60 +24,60 @@ module Shipit
|
|
|
23
24
|
|
|
24
25
|
test "#create triggers a new deploy for the stack" do
|
|
25
26
|
assert_difference -> { @stack.deploys.count }, 1 do
|
|
26
|
-
post :create, params: {stack_id: @stack.to_param, sha: @commit.sha}
|
|
27
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha }
|
|
27
28
|
end
|
|
28
29
|
assert_response :accepted
|
|
29
30
|
assert_json 'status', 'pending'
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
test "#create triggers a new deploy for whitelisted variables" do
|
|
33
|
-
correct_env = {'SAFETY_DISABLED' => 1}
|
|
34
|
-
post :create, params: {stack_id: @stack.to_param, sha: @commit.sha, env: correct_env}
|
|
34
|
+
correct_env = { 'SAFETY_DISABLED' => 1 }
|
|
35
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha, env: correct_env }
|
|
35
36
|
assert_response :accepted
|
|
36
37
|
assert_json 'type', 'deploy'
|
|
37
38
|
assert_json 'status', 'pending'
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
test "#create refuses to trigger a new deploy with incorrect variables" do
|
|
41
|
-
incorrect_env = {'DANGEROUS_VARIABLE' => 1}
|
|
42
|
-
post :create, params: {stack_id: @stack.to_param, sha: @commit.sha, env: incorrect_env}
|
|
42
|
+
incorrect_env = { 'DANGEROUS_VARIABLE' => 1 }
|
|
43
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha, env: incorrect_env }
|
|
43
44
|
assert_response :unprocessable_entity
|
|
44
45
|
assert_json 'message', 'Variables DANGEROUS_VARIABLE have not been whitelisted'
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
test "#create use the claimed user as author" do
|
|
48
49
|
request.headers['X-Shipit-User'] = @user.login
|
|
49
|
-
post :create, params: {stack_id: @stack.to_param, sha: @commit.sha}
|
|
50
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha }
|
|
50
51
|
deploy = Deploy.last
|
|
51
52
|
deploy.user == @user
|
|
52
53
|
end
|
|
53
54
|
|
|
54
55
|
test "#create normalises the claimed user" do
|
|
55
56
|
request.headers['X-Shipit-User'] = @user.login.swapcase
|
|
56
|
-
post :create, params: {stack_id: @stack.to_param, sha: @commit.sha}
|
|
57
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha }
|
|
57
58
|
deploy = Deploy.last
|
|
58
59
|
assert_equal deploy.user, @user
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
test "#create renders a 422 if the sha isn't found" do
|
|
62
|
-
post :create, params: {stack_id: @stack.to_param, sha: '123443543545'}
|
|
63
|
+
post :create, params: { stack_id: @stack.to_param, sha: '123443543545' }
|
|
63
64
|
assert_response :unprocessable_entity
|
|
64
65
|
assert_json 'errors', 'sha' => ['Unknown revision']
|
|
65
66
|
end
|
|
66
67
|
|
|
67
68
|
test "#create renders a 422 if the sha format is invalid" do
|
|
68
|
-
post :create, params: {stack_id: @stack.to_param, sha: '1'}
|
|
69
|
+
post :create, params: { stack_id: @stack.to_param, sha: '1' }
|
|
69
70
|
assert_response :unprocessable_entity
|
|
70
71
|
assert_json 'errors', 'sha' => ['is too short (minimum is 6 characters)']
|
|
71
72
|
end
|
|
72
73
|
|
|
73
74
|
test "#create renders a 409 if a concurrent task is already running" do
|
|
74
75
|
assert_difference -> { @stack.deploys.count }, 1 do
|
|
75
|
-
post :create, params: {stack_id: @stack.to_param, sha: @commit.sha}
|
|
76
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha }
|
|
76
77
|
end
|
|
77
78
|
|
|
78
79
|
assert_no_difference -> { @stack.deploys.count } do
|
|
79
|
-
post :create, params: {stack_id: @stack.to_param, sha: @commit.sha}
|
|
80
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha }
|
|
80
81
|
end
|
|
81
82
|
|
|
82
83
|
assert_response :conflict
|
|
@@ -86,7 +87,7 @@ module Shipit
|
|
|
86
87
|
@stack.update!(lock_reason: 'Something broken')
|
|
87
88
|
|
|
88
89
|
assert_no_difference -> { @stack.deploys.count } do
|
|
89
|
-
post :create, params: {stack_id: @stack.to_param, sha: @commit.sha}
|
|
90
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha }
|
|
90
91
|
end
|
|
91
92
|
assert_response :unprocessable_entity
|
|
92
93
|
assert_json 'errors.force', ["Can't deploy a locked stack"]
|
|
@@ -96,7 +97,7 @@ module Shipit
|
|
|
96
97
|
@stack.update!(lock_reason: 'Something broken')
|
|
97
98
|
|
|
98
99
|
assert_difference -> { @stack.deploys.count }, 1 do
|
|
99
|
-
post :create, params: {stack_id: @stack.to_param, sha: @commit.sha, force: 'true'}
|
|
100
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha, force: 'true' }
|
|
100
101
|
end
|
|
101
102
|
assert_response :accepted
|
|
102
103
|
assert_json 'status', 'pending'
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'test_helper'
|
|
2
3
|
|
|
3
4
|
module Shipit
|
|
@@ -9,7 +10,7 @@ module Shipit
|
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
test "the route has priority over stacks one" do
|
|
12
|
-
assert_recognizes({controller: 'shipit/api/hooks', action: 'show', id: '42'}, '/api/hooks/42')
|
|
13
|
+
assert_recognizes({ controller: 'shipit/api/hooks', action: 'show', id: '42' }, '/api/hooks/42')
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
test "#index without a stack_id returns the list of global hooks" do
|
|
@@ -26,7 +27,7 @@ module Shipit
|
|
|
26
27
|
test "#index with a stack_id returns the list of scoped hooks" do
|
|
27
28
|
hook = Hook.scoped_to(@stack).first
|
|
28
29
|
|
|
29
|
-
get :index, params: {stack_id: @stack.to_param}
|
|
30
|
+
get :index, params: { stack_id: @stack.to_param }
|
|
30
31
|
assert_response :ok
|
|
31
32
|
assert_json '0.id', hook.id
|
|
32
33
|
assert_json '0.delivery_url', hook.delivery_url
|
|
@@ -37,7 +38,7 @@ module Shipit
|
|
|
37
38
|
test "#show returns the hooks" do
|
|
38
39
|
hook = Hook.scoped_to(@stack).first
|
|
39
40
|
|
|
40
|
-
get :show, params: {stack_id: @stack.to_param, id: hook.id}
|
|
41
|
+
get :show, params: { stack_id: @stack.to_param, id: hook.id }
|
|
41
42
|
assert_response :ok
|
|
42
43
|
|
|
43
44
|
assert_json 'id', hook.id
|
|
@@ -48,7 +49,7 @@ module Shipit
|
|
|
48
49
|
|
|
49
50
|
test "#create adds a new hook" do
|
|
50
51
|
assert_difference -> { Hook.count }, 1 do
|
|
51
|
-
post :create, params: {delivery_url: 'https://example.com/hook', events: %w(deploy rollback)}
|
|
52
|
+
post :create, params: { delivery_url: 'https://example.com/hook', events: %w(deploy rollback) }
|
|
52
53
|
end
|
|
53
54
|
hook = Hook.last
|
|
54
55
|
assert_json 'delivery_url', 'https://example.com/hook'
|
|
@@ -66,21 +67,21 @@ module Shipit
|
|
|
66
67
|
end
|
|
67
68
|
|
|
68
69
|
test "#create returns validation errors" do
|
|
69
|
-
post :create, params: {delivery_url: '../etc/passwd', events: %w(deploy)}
|
|
70
|
+
post :create, params: { delivery_url: '../etc/passwd', events: %w(deploy) }
|
|
70
71
|
assert_response :unprocessable_entity
|
|
71
72
|
assert_json 'errors', 'delivery_url' => ['is not a valid URL']
|
|
72
73
|
end
|
|
73
74
|
|
|
74
75
|
test "#update changes an existing hook" do
|
|
75
76
|
hook = Hook.global.first
|
|
76
|
-
patch :update, params: {id: hook.id, delivery_url: 'https://shipit.com/'}
|
|
77
|
+
patch :update, params: { id: hook.id, delivery_url: 'https://shipit.com/' }
|
|
77
78
|
assert_response :ok
|
|
78
79
|
assert_json 'delivery_url', 'https://shipit.com/'
|
|
79
80
|
end
|
|
80
81
|
|
|
81
82
|
test "#destroy removes an existing hook" do
|
|
82
83
|
hook = Hook.global.first
|
|
83
|
-
delete :destroy, params: {id: hook.id}
|
|
84
|
+
delete :destroy, params: { id: hook.id }
|
|
84
85
|
assert_response :no_content
|
|
85
86
|
end
|
|
86
87
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'test_helper'
|
|
2
3
|
|
|
3
4
|
module Shipit
|
|
@@ -9,7 +10,7 @@ module Shipit
|
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
test "#create sets a lock" do
|
|
12
|
-
post :create, params: {stack_id: @stack.to_param, reason: 'Just for fun!'}
|
|
13
|
+
post :create, params: { stack_id: @stack.to_param, reason: 'Just for fun!' }
|
|
13
14
|
assert_response :ok
|
|
14
15
|
assert_json 'is_locked', true
|
|
15
16
|
assert_json 'lock_reason', 'Just for fun!'
|
|
@@ -18,12 +19,12 @@ module Shipit
|
|
|
18
19
|
|
|
19
20
|
test "#create fails if already locked" do
|
|
20
21
|
@stack.update!(lock_reason: "Don't forget me")
|
|
21
|
-
post :create, params: {stack_id: @stack.to_param, reason: 'Just for fun!'}
|
|
22
|
+
post :create, params: { stack_id: @stack.to_param, reason: 'Just for fun!' }
|
|
22
23
|
assert_response :conflict
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
test "#update sets a lock" do
|
|
26
|
-
put :update, params: {stack_id: @stack.to_param, reason: 'Just for fun!'}
|
|
27
|
+
put :update, params: { stack_id: @stack.to_param, reason: 'Just for fun!' }
|
|
27
28
|
assert_response :ok
|
|
28
29
|
assert_json 'is_locked', true
|
|
29
30
|
assert_json 'lock_reason', 'Just for fun!'
|
|
@@ -31,7 +32,7 @@ module Shipit
|
|
|
31
32
|
|
|
32
33
|
test "#update can override a previous lock" do
|
|
33
34
|
@stack.update!(lock_reason: 'Meh...')
|
|
34
|
-
put :update, params: {stack_id: @stack.to_param, reason: 'Just for fun!'}
|
|
35
|
+
put :update, params: { stack_id: @stack.to_param, reason: 'Just for fun!' }
|
|
35
36
|
assert_response :ok
|
|
36
37
|
assert_json 'is_locked', true
|
|
37
38
|
assert_json 'lock_reason', 'Just for fun!'
|
|
@@ -40,14 +41,14 @@ module Shipit
|
|
|
40
41
|
test "#update does not override previous locked_since" do
|
|
41
42
|
since = Time.current.round
|
|
42
43
|
@stack.update!(lock_reason: 'Meh...', locked_since: since)
|
|
43
|
-
put :update, params: {stack_id: @stack.to_param, reason: 'Just for fun!'}
|
|
44
|
+
put :update, params: { stack_id: @stack.to_param, reason: 'Just for fun!' }
|
|
44
45
|
assert_response :ok
|
|
45
46
|
assert_json 'locked_since', since.utc.iso8601(3)
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
test "#destroy clears the lock" do
|
|
49
50
|
@stack.update!(lock_reason: 'Meh...', locked_since: Time.current)
|
|
50
|
-
delete :destroy, params: {stack_id: @stack.to_param}
|
|
51
|
+
delete :destroy, params: { stack_id: @stack.to_param }
|
|
51
52
|
assert_response :ok
|
|
52
53
|
assert_json 'is_locked', false
|
|
53
54
|
assert_json { |json| assert_nil json['locked_since'] }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'test_helper'
|
|
2
3
|
|
|
3
4
|
module Shipit
|
|
@@ -11,7 +12,7 @@ module Shipit
|
|
|
11
12
|
test "#show returns the task output as plain text" do
|
|
12
13
|
task = @stack.tasks.last
|
|
13
14
|
|
|
14
|
-
get :show, params: {stack_id: @stack.to_param, task_id: task.id}
|
|
15
|
+
get :show, params: { stack_id: @stack.to_param, task_id: task.id }
|
|
15
16
|
assert_response :ok
|
|
16
17
|
assert_equal 'text/plain', response.media_type
|
|
17
18
|
assert_equal task.chunk_output, response.body
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'test_helper'
|
|
2
3
|
|
|
3
4
|
module Shipit
|
|
@@ -12,46 +13,46 @@ module Shipit
|
|
|
12
13
|
test "#index returns a list of pull requests" do
|
|
13
14
|
pull_request = @stack.pull_requests.last
|
|
14
15
|
|
|
15
|
-
get :index, params: {stack_id: @stack.to_param}
|
|
16
|
+
get :index, params: { stack_id: @stack.to_param }
|
|
16
17
|
assert_response :ok
|
|
17
18
|
assert_json '0.id', pull_request.id
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
test "#show returns a single pull requests" do
|
|
21
|
-
get :show, params: {stack_id: @stack.to_param, id: @pull_request.number.to_s}
|
|
22
|
+
get :show, params: { stack_id: @stack.to_param, id: @pull_request.number.to_s }
|
|
22
23
|
assert_response :ok
|
|
23
24
|
assert_json 'id', @pull_request.id
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
test "#update responds with Accepted if the pull request was queued" do
|
|
27
28
|
assert_enqueued_with(job: RefreshPullRequestJob) do
|
|
28
|
-
put :update, params: {stack_id: @stack.to_param, id: '64'}
|
|
29
|
+
put :update, params: { stack_id: @stack.to_param, id: '64' }
|
|
29
30
|
end
|
|
30
31
|
assert_response :accepted
|
|
31
32
|
end
|
|
32
33
|
|
|
33
34
|
test "#update responds with Accepted if the pull request was already queued" do
|
|
34
35
|
assert_enqueued_with(job: RefreshPullRequestJob) do
|
|
35
|
-
put :update, params: {stack_id: @stack.to_param, id: '65'}
|
|
36
|
+
put :update, params: { stack_id: @stack.to_param, id: '65' }
|
|
36
37
|
end
|
|
37
38
|
assert_response :accepted
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
test "#update responds with method not allowed if the pull request was already merged" do
|
|
41
42
|
@pull_request.complete!
|
|
42
|
-
put :update, params: {stack_id: @stack.to_param, id: @pull_request.number.to_s}
|
|
43
|
+
put :update, params: { stack_id: @stack.to_param, id: @pull_request.number.to_s }
|
|
43
44
|
assert_response :method_not_allowed
|
|
44
45
|
assert_json 'message', 'This pull request was already merged.'
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
test "#destroy cancels the merge if the pull request was waiting" do
|
|
48
|
-
delete :destroy, params: {stack_id: @stack.to_param, id: @pull_request.number.to_s}
|
|
49
|
+
delete :destroy, params: { stack_id: @stack.to_param, id: @pull_request.number.to_s }
|
|
49
50
|
assert_response :no_content
|
|
50
51
|
assert_predicate @pull_request.reload, :canceled?
|
|
51
52
|
end
|
|
52
53
|
|
|
53
54
|
test "#destroy silently fail if the pull request was unknown" do
|
|
54
|
-
delete :destroy, params: {stack_id: @stack.to_param, id: '83453489'}
|
|
55
|
+
delete :destroy, params: { stack_id: @stack.to_param, id: '83453489' }
|
|
55
56
|
assert_response :no_content
|
|
56
57
|
end
|
|
57
58
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'test_helper'
|
|
2
3
|
|
|
3
4
|
module Shipit
|
|
@@ -10,7 +11,7 @@ module Shipit
|
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
test "#create renders a 422 if status is not found" do
|
|
13
|
-
post :create, params: {stack_id: @stack.to_param, deploy_id: @deploy.id}
|
|
14
|
+
post :create, params: { stack_id: @stack.to_param, deploy_id: @deploy.id }
|
|
14
15
|
assert_response :unprocessable_entity
|
|
15
16
|
assert_json 'errors', 'status' => ['is required', 'is not included in the list']
|
|
16
17
|
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'test_helper'
|
|
3
|
+
|
|
4
|
+
module Shipit
|
|
5
|
+
module Api
|
|
6
|
+
class RollbacksControllerTest < ActionController::TestCase
|
|
7
|
+
setup do
|
|
8
|
+
authenticate!
|
|
9
|
+
@user = shipit_users(:walrus)
|
|
10
|
+
@stack = shipit_stacks(:shipit)
|
|
11
|
+
@commit = shipit_commits(:fourth)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test "#create triggers a new rollback for the stack" do
|
|
15
|
+
assert_difference -> { @stack.deploys.count }, 1 do
|
|
16
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha }
|
|
17
|
+
end
|
|
18
|
+
assert_response :accepted
|
|
19
|
+
assert_json 'status', 'pending'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
test "#create triggers a new rollback for whitelisted variables" do
|
|
23
|
+
correct_env = { 'SAFETY_DISABLED' => 1 }
|
|
24
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha, env: correct_env }
|
|
25
|
+
assert_response :accepted
|
|
26
|
+
assert_json 'type', 'rollback'
|
|
27
|
+
assert_json 'status', 'pending'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
test "#create refuses to trigger a new rollback with incorrect variables" do
|
|
31
|
+
incorrect_env = { 'DANGEROUS_VARIABLE' => 1 }
|
|
32
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha, env: incorrect_env }
|
|
33
|
+
assert_response :unprocessable_entity
|
|
34
|
+
assert_json 'message', 'Variables DANGEROUS_VARIABLE have not been whitelisted'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
test "#create use the claimed user as author" do
|
|
38
|
+
request.headers['X-Shipit-User'] = @user.login
|
|
39
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha }
|
|
40
|
+
rollback = Rollback.last
|
|
41
|
+
rollback.user == @user
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
test "#create normalises the claimed user" do
|
|
45
|
+
request.headers['X-Shipit-User'] = @user.login.swapcase
|
|
46
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha }
|
|
47
|
+
rollback = Rollback.last
|
|
48
|
+
rollback.user == @user
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
test "#create renders a 422 if the sha isn't found" do
|
|
52
|
+
post :create, params: { stack_id: @stack.to_param, sha: '123443543545' }
|
|
53
|
+
assert_response :unprocessable_entity
|
|
54
|
+
assert_json 'errors', 'sha' => ['Unknown revision']
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
test "#create renders a 422 if the sha format is invalid" do
|
|
58
|
+
post :create, params: { stack_id: @stack.to_param, sha: '1' }
|
|
59
|
+
assert_response :unprocessable_entity
|
|
60
|
+
assert_json 'errors', 'sha' => ['is too short (minimum is 6 characters)']
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
test "#create renders a 422 if deploy attached to sha isn't found" do
|
|
64
|
+
post :create, params: { stack_id: @stack.to_param, sha: shipit_commits(:fifth).sha }
|
|
65
|
+
assert_response :unprocessable_entity
|
|
66
|
+
assert_json 'errors', 'sha' => ['Cant find associated deploy']
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
test "#create refuses to rollback on locked stacks" do
|
|
70
|
+
@stack.update!(lock_reason: 'Something broken')
|
|
71
|
+
|
|
72
|
+
assert_no_difference -> { @stack.deploys.count } do
|
|
73
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha }
|
|
74
|
+
end
|
|
75
|
+
assert_response :unprocessable_entity
|
|
76
|
+
assert_json 'errors.force', ["Can't rollback a locked stack"]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
test "#create rollbacks on locked stack if force mode is enabled" do
|
|
80
|
+
@stack.update!(lock_reason: 'Something broken')
|
|
81
|
+
|
|
82
|
+
assert_difference -> { @stack.deploys.count }, 1 do
|
|
83
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha, force: 'true' }
|
|
84
|
+
end
|
|
85
|
+
assert_response :accepted
|
|
86
|
+
assert_json 'status', 'pending'
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
test "#create refuses to rollback if active task" do
|
|
90
|
+
@stack.deploys.last.update!(status: 'running')
|
|
91
|
+
|
|
92
|
+
assert_no_difference -> { @stack.deploys.count } do
|
|
93
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha }
|
|
94
|
+
end
|
|
95
|
+
assert_response :unprocessable_entity
|
|
96
|
+
assert_json 'errors.force', ["Can't rollback, deploy in progress"]
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
test "#create aborts active task and sets rollback to" do
|
|
100
|
+
last_deploy = @stack.deploys.last
|
|
101
|
+
last_deploy.update!(status: 'running')
|
|
102
|
+
|
|
103
|
+
assert_no_difference -> { @stack.deploys.count } do
|
|
104
|
+
post :create, params: { stack_id: @stack.to_param, sha: @commit.sha, force: 'true' }
|
|
105
|
+
end
|
|
106
|
+
last_deploy.reload
|
|
107
|
+
assert_response :accepted
|
|
108
|
+
refute_predicate last_deploy, :active?
|
|
109
|
+
assert_json 'rollback_once_aborted_to.until_commit_id', @commit.id
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|