shopify-cli 1.8.0 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +1 -0
  3. data/.github/workflows/build.yml +28 -0
  4. data/.rubocop_todo.yml +15 -2
  5. data/CHANGELOG.md +24 -1
  6. data/Gemfile.lock +4 -4
  7. data/README.md +2 -1
  8. data/dev.yml +3 -0
  9. data/lib/graphql/extension_create.graphql +17 -2
  10. data/lib/project_types/extension/cli.rb +8 -0
  11. data/lib/project_types/extension/commands/extension_command.rb +4 -4
  12. data/lib/project_types/extension/commands/push.rb +2 -2
  13. data/lib/project_types/extension/commands/register.rb +4 -3
  14. data/lib/project_types/extension/commands/serve.rb +62 -28
  15. data/lib/project_types/extension/commands/tunnel.rb +3 -1
  16. data/lib/project_types/extension/extension_project.rb +16 -4
  17. data/lib/project_types/extension/extension_project_keys.rb +2 -1
  18. data/lib/project_types/extension/features/argo.rb +19 -44
  19. data/lib/project_types/extension/features/argo_runtime.rb +84 -0
  20. data/lib/project_types/extension/features/argo_serve.rb +80 -0
  21. data/lib/project_types/extension/features/argo_serve_options.rb +41 -0
  22. data/lib/project_types/extension/features/argo_setup.rb +1 -1
  23. data/lib/project_types/extension/messages/message_loading.rb +3 -1
  24. data/lib/project_types/extension/messages/messages.rb +5 -4
  25. data/lib/project_types/extension/models/npm_package.rb +14 -0
  26. data/lib/project_types/extension/models/registration.rb +1 -0
  27. data/lib/project_types/extension/models/specification.rb +3 -0
  28. data/lib/project_types/extension/models/specification_handlers/checkout_argo_extension.rb +18 -0
  29. data/lib/project_types/extension/models/specification_handlers/default.rb +49 -1
  30. data/lib/project_types/extension/models/version.rb +1 -1
  31. data/lib/project_types/extension/tasks/choose_next_available_port.rb +36 -0
  32. data/lib/project_types/extension/tasks/configure_features.rb +4 -0
  33. data/lib/project_types/extension/tasks/converters/registration_converter.rb +2 -0
  34. data/lib/project_types/extension/tasks/find_npm_packages.rb +106 -0
  35. data/lib/project_types/node/commands/generate.rb +0 -22
  36. data/lib/project_types/node/messages/messages.rb +4 -4
  37. data/lib/project_types/rails/messages/messages.rb +4 -4
  38. data/lib/project_types/script/cli.rb +7 -8
  39. data/lib/project_types/script/commands/create.rb +0 -7
  40. data/lib/project_types/script/commands/push.rb +3 -3
  41. data/lib/project_types/script/config/extension_points.yml +4 -0
  42. data/lib/project_types/script/errors.rb +0 -19
  43. data/lib/project_types/script/forms/create.rb +3 -14
  44. data/lib/project_types/script/graphql/app_script_update_or_create.graphql +5 -5
  45. data/lib/project_types/script/graphql/get_app_scripts.graphql +6 -0
  46. data/lib/project_types/script/graphql/script_service_proxy.graphql +1 -2
  47. data/lib/project_types/script/layers/application/build_script.rb +1 -2
  48. data/lib/project_types/script/layers/application/create_script.rb +30 -51
  49. data/lib/project_types/script/layers/application/extension_points.rb +3 -2
  50. data/lib/project_types/script/layers/application/push_script.rb +5 -5
  51. data/lib/project_types/script/layers/domain/errors.rb +0 -2
  52. data/lib/project_types/script/layers/domain/extension_point.rb +56 -46
  53. data/lib/project_types/script/layers/domain/metadata.rb +18 -25
  54. data/lib/project_types/script/layers/domain/push_package.rb +4 -4
  55. data/lib/project_types/script/layers/domain/script_project.rb +54 -0
  56. data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +15 -16
  57. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +13 -7
  58. data/lib/project_types/script/layers/infrastructure/command_runner.rb +19 -0
  59. data/lib/project_types/script/layers/infrastructure/errors.rb +40 -11
  60. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +12 -13
  61. data/lib/project_types/script/layers/infrastructure/rust_project_creator.rb +9 -10
  62. data/lib/project_types/script/layers/infrastructure/rust_task_runner.rb +6 -7
  63. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +172 -0
  64. data/lib/project_types/script/layers/infrastructure/script_service.rb +21 -72
  65. data/lib/project_types/script/messages/messages.rb +20 -50
  66. data/lib/project_types/script/tasks/ensure_env.rb +85 -0
  67. data/lib/project_types/script/ui/error_handler.rb +32 -30
  68. data/lib/shopify-cli/context.rb +28 -0
  69. data/lib/shopify-cli/js_system.rb +2 -2
  70. data/lib/shopify-cli/messages/messages.rb +50 -45
  71. data/lib/shopify-cli/method_object.rb +4 -4
  72. data/lib/shopify-cli/oauth.rb +9 -3
  73. data/lib/shopify-cli/packager.rb +1 -1
  74. data/lib/shopify-cli/partners_api/organizations.rb +3 -3
  75. data/lib/shopify-cli/resolve_constant.rb +1 -1
  76. data/lib/shopify-cli/resources/env_file.rb +1 -1
  77. data/lib/shopify-cli/shopifolk.rb +1 -1
  78. data/lib/shopify-cli/tasks/select_org_and_shop.rb +6 -4
  79. data/lib/shopify-cli/transform_data_structure.rb +1 -1
  80. data/lib/shopify-cli/tunnel.rb +22 -1
  81. data/lib/shopify-cli/version.rb +1 -1
  82. data/lib/shopify_cli.rb +0 -1
  83. data/vendor/deps/smart_properties/REVISION +1 -1
  84. data/vendor/deps/smart_properties/lib/smart_properties/property.rb +7 -1
  85. data/vendor/deps/smart_properties/lib/smart_properties/version.rb +1 -1
  86. metadata +16 -12
  87. data/.travis.yml +0 -14
  88. data/lib/project_types/script/commands/disable.rb +0 -25
  89. data/lib/project_types/script/commands/enable.rb +0 -80
  90. data/lib/project_types/script/graphql/shop_script_delete.graphql +0 -14
  91. data/lib/project_types/script/graphql/shop_script_update_or_create.graphql +0 -28
  92. data/lib/project_types/script/layers/application/disable_script.rb +0 -21
  93. data/lib/project_types/script/layers/application/enable_script.rb +0 -23
  94. data/lib/project_types/script/layers/infrastructure/config_ui_repository.rb +0 -46
  95. data/lib/project_types/script/script_project.rb +0 -64
@@ -3,11 +3,10 @@
3
3
  module Script
4
4
  module Forms
5
5
  class Create < ShopifyCli::Form
6
- flag_arguments :extension_point, :name, :language, :description
6
+ flag_arguments :extension_point, :name, :language
7
7
 
8
8
  def ask
9
9
  self.name = valid_name
10
- self.description ||= ask_description
11
10
  self.extension_point ||= ask_extension_point
12
11
  self.language = ask_language
13
12
  end
@@ -17,7 +16,7 @@ module Script
17
16
  def ask_extension_point
18
17
  CLI::UI::Prompt.ask(
19
18
  @ctx.message("script.forms.create.select_extension_point"),
20
- options: Layers::Application::ExtensionPoints.non_deprecated_types
19
+ options: Layers::Application::ExtensionPoints.available_types
21
20
  )
22
21
  end
23
22
 
@@ -25,10 +24,6 @@ module Script
25
24
  CLI::UI::Prompt.ask(@ctx.message("script.forms.create.script_name"))
26
25
  end
27
26
 
28
- def ask_description
29
- CLI::UI::Prompt.ask(@ctx.message("script.forms.create.description"))
30
- end
31
-
32
27
  def valid_name
33
28
  n = (name || ask_name).downcase.gsub(" ", "_")
34
29
  return n if n.match?(/^[0-9A-Za-z_-]*$/)
@@ -36,13 +31,7 @@ module Script
36
31
  end
37
32
 
38
33
  def ask_language
39
- if language
40
- if Layers::Application::ExtensionPoints.supported_language?(type: extension_point, language: language)
41
- return language.downcase
42
- else
43
- raise Errors::InvalidLanguageError.new(language, extension_point)
44
- end
45
- end
34
+ return language.downcase if language
46
35
 
47
36
  all_languages = Layers::Application::ExtensionPoints.languages(type: extension_point)
48
37
  return all_languages.first if all_languages.count == 1
@@ -1,26 +1,26 @@
1
1
  mutation AppScriptUpdateOrCreate(
2
2
  $extensionPointName: ExtensionPointName!,
3
3
  $title: String,
4
- $description: String,
5
4
  $configUi: String,
6
5
  $sourceCode: String,
7
6
  $language: String,
8
7
  $force: Boolean,
9
8
  $schemaMajorVersion: String,
10
9
  $schemaMinorVersion: String,
11
- $useMsgpack: Boolean
10
+ $useMsgpack: Boolean,
11
+ $uuid: String
12
12
  ) {
13
13
  appScriptUpdateOrCreate(
14
14
  extensionPointName: $extensionPointName
15
15
  title: $title
16
- description: $description
17
16
  configUi: $configUi
18
17
  sourceCode: $sourceCode
19
18
  language: $language
20
19
  force: $force
21
20
  schemaMajorVersion: $schemaMajorVersion
22
21
  schemaMinorVersion: $schemaMinorVersion
23
- useMsgpack: $useMsgpack
22
+ useMsgpack: $useMsgpack,
23
+ uuid: $uuid
24
24
  ) {
25
25
  userErrors {
26
26
  field
@@ -28,11 +28,11 @@ mutation AppScriptUpdateOrCreate(
28
28
  tag
29
29
  }
30
30
  appScript {
31
+ uuid
31
32
  appKey
32
33
  configSchema
33
34
  extensionPointName
34
35
  title
35
- description
36
36
  }
37
37
  }
38
38
  }
@@ -0,0 +1,6 @@
1
+ query GetAppScripts($appKey: String!, $extensionPointName: ExtensionPointName!) {
2
+ appScripts(appKeys: [$appKey], extensionPointName: $extensionPointName) {
3
+ uuid
4
+ title
5
+ }
6
+ }
@@ -1,7 +1,6 @@
1
- query ProxyRequest($api_key: String, $shop_domain: String, $query: String!, $variables: String) {
1
+ query ProxyRequest($api_key: String, $query: String!, $variables: String) {
2
2
  scriptServiceProxy(
3
3
  apiKey: $api_key
4
- shopDomain: $shop_domain
5
4
  query: $query
6
5
  variables: $variables
7
6
  )
@@ -5,7 +5,7 @@ module Script
5
5
  module Application
6
6
  class BuildScript
7
7
  class << self
8
- def call(ctx:, task_runner:, script_project:, config_ui:)
8
+ def call(ctx:, task_runner:, script_project:)
9
9
  CLI::UI::Frame.open(ctx.message("script.application.building")) do
10
10
  begin
11
11
  UI::StrictSpinner.spin(ctx.message("script.application.building_script")) do |spinner|
@@ -14,7 +14,6 @@ module Script
14
14
  script_content: task_runner.build,
15
15
  compiled_type: task_runner.compiled_type,
16
16
  metadata: task_runner.metadata,
17
- config_ui: config_ui,
18
17
  )
19
18
  spinner.update_title(ctx.message("script.application.built"))
20
19
  end
@@ -7,54 +7,27 @@ module Script
7
7
  module Application
8
8
  class CreateScript
9
9
  class << self
10
- def call(ctx:, language:, script_name:, extension_point_type:, description:, no_config_ui:)
11
- extension_point = ExtensionPoints.get(type: extension_point_type)
12
- project = setup_project(
13
- ctx: ctx,
14
- language: language,
15
- script_name: script_name,
16
- extension_point: extension_point,
17
- description: description,
18
- no_config_ui: no_config_ui
19
- )
20
- project_creator = Infrastructure::ProjectCreator
21
- .for(ctx, language, extension_point, script_name, project.directory)
22
- install_dependencies(ctx, language, script_name, project_creator)
23
- bootstrap(ctx, project_creator)
24
- project
10
+ def call(ctx:, language:, script_name:, extension_point_type:, no_config_ui:)
11
+ raise Infrastructure::Errors::ScriptProjectAlreadyExistsError, script_name if ctx.dir_exist?(script_name)
12
+
13
+ in_new_directory_context(ctx, script_name) do
14
+ extension_point = ExtensionPoints.get(type: extension_point_type)
15
+ project = Infrastructure::ScriptProjectRepository.new(ctx: ctx).create(
16
+ script_name: script_name,
17
+ extension_point_type: extension_point_type,
18
+ language: language,
19
+ no_config_ui: no_config_ui
20
+ )
21
+ project_creator = Infrastructure::ProjectCreator
22
+ .for(ctx, language, extension_point, script_name, project.id)
23
+ install_dependencies(ctx, language, script_name, project_creator)
24
+ bootstrap(ctx, project_creator)
25
+ project
26
+ end
25
27
  end
26
28
 
27
29
  private
28
30
 
29
- DEFAULT_CONFIG_UI_FILENAME = "config-ui.yml"
30
-
31
- def setup_project(ctx:, language:, script_name:, extension_point:, description:, no_config_ui:)
32
- ScriptProject.create(ctx, script_name)
33
-
34
- identifiers = {
35
- extension_point_type: extension_point.type,
36
- script_name: script_name,
37
- language: language,
38
- description: description,
39
- }
40
-
41
- unless no_config_ui
42
- require "yaml" # takes 20ms, so deferred as late as possible.
43
- identifiers.merge!(config_ui_file: DEFAULT_CONFIG_UI_FILENAME)
44
- Infrastructure::ConfigUiRepository
45
- .new(ctx: ctx)
46
- .create_config_ui(DEFAULT_CONFIG_UI_FILENAME, default_config_ui_content(script_name))
47
- end
48
-
49
- ScriptProject.write(
50
- ctx,
51
- project_type: :script,
52
- organization_id: nil, # TODO: can you provide this at creation
53
- **identifiers
54
- )
55
- ScriptProject.current
56
- end
57
-
58
31
  def install_dependencies(ctx, language, script_name, project_creator)
59
32
  task_runner = Infrastructure::TaskRunner.for(ctx, language, script_name)
60
33
  project_creator.setup_dependencies
@@ -68,13 +41,19 @@ module Script
68
41
  end
69
42
  end
70
43
 
71
- def default_config_ui_content(title)
72
- YAML.dump({
73
- "version" => 1,
74
- "type" => "single",
75
- "title" => title,
76
- "fields" => [],
77
- })
44
+ def in_new_directory_context(ctx, directory)
45
+ initial_directory = ctx.root
46
+ begin
47
+ ctx.mkdir_p(directory)
48
+ ctx.chdir(directory)
49
+ yield
50
+ rescue
51
+ ctx.chdir(initial_directory)
52
+ ctx.rm_r(directory)
53
+ raise
54
+ ensure
55
+ ctx.chdir(initial_directory)
56
+ end
78
57
  end
79
58
  end
80
59
  end
@@ -12,9 +12,10 @@ module Script
12
12
  Infrastructure::ExtensionPointRepository.new.extension_point_types
13
13
  end
14
14
 
15
- def self.non_deprecated_types
15
+ def self.available_types
16
16
  Infrastructure::ExtensionPointRepository.new.extension_points.select do |ep|
17
- !ep.deprecated?
17
+ next false if ep.deprecated?
18
+ !ep.beta? || ShopifyCli::Feature.enabled?(:scripts_beta_extension_points)
18
19
  end.map(&:type)
19
20
  end
20
21
 
@@ -6,21 +6,21 @@ module Script
6
6
  class PushScript
7
7
  class << self
8
8
  def call(ctx:, force:)
9
- script_project = ScriptProject.current
9
+ script_project_repo = Infrastructure::ScriptProjectRepository.new(ctx: ctx)
10
+ script_project = script_project_repo.get
10
11
  task_runner = Infrastructure::TaskRunner.for(ctx, script_project.language, script_project.script_name)
11
- config_ui = Infrastructure::ConfigUiRepository.new(ctx: ctx).get_config_ui(script_project.config_ui_file)
12
12
 
13
13
  ProjectDependencies.install(ctx: ctx, task_runner: task_runner)
14
- BuildScript.call(ctx: ctx, task_runner: task_runner, script_project: script_project, config_ui: config_ui)
14
+ BuildScript.call(ctx: ctx, task_runner: task_runner, script_project: script_project)
15
15
 
16
16
  UI::PrintingSpinner.spin(ctx, ctx.message("script.application.pushing")) do |p_ctx, spinner|
17
17
  package = Infrastructure::PushPackageRepository.new(ctx: p_ctx).get_push_package(
18
18
  script_project: script_project,
19
19
  compiled_type: task_runner.compiled_type,
20
20
  metadata: task_runner.metadata,
21
- config_ui: config_ui,
22
21
  )
23
- package.push(Infrastructure::ScriptService.new(ctx: p_ctx), script_project.api_key, force)
22
+ uuid = package.push(Infrastructure::ScriptService.new(ctx: p_ctx), script_project.api_key, force)
23
+ script_project_repo.update_env(uuid: uuid)
24
24
  spinner.update_title(p_ctx.message("script.application.pushed"))
25
25
  end
26
26
  end
@@ -39,8 +39,6 @@ module Script
39
39
  end
40
40
  end
41
41
 
42
- class ServiceFailureError < ScriptProjectError; end
43
-
44
42
  class MetadataNotFoundError < ScriptProjectError; end
45
43
 
46
44
  class MetadataValidationError < ScriptProjectError; end
@@ -4,15 +4,20 @@ module Script
4
4
  module Layers
5
5
  module Domain
6
6
  class ExtensionPoint
7
- attr_reader :type, :deprecated, :sdks, :domain
7
+ attr_reader :type, :beta, :deprecated, :sdks, :domain
8
8
 
9
9
  def initialize(type, config)
10
10
  @type = type
11
+ @beta = config["beta"] || false
11
12
  @deprecated = config["deprecated"] || false
12
13
  @domain = config["domain"] || nil
13
14
  @sdks = ExtensionPointSDKs.new(config)
14
15
  end
15
16
 
17
+ def beta?
18
+ @beta
19
+ end
20
+
16
21
  def deprecated?
17
22
  @deprecated
18
23
  end
@@ -20,68 +25,73 @@ module Script
20
25
  def dasherize_type
21
26
  @type.gsub("_", "-")
22
27
  end
23
- end
24
28
 
25
- class ExtensionPointSDKs
26
- def initialize(config)
27
- @config = config
28
- end
29
+ class ExtensionPointSDKs
30
+ def initialize(config)
31
+ @config = config
32
+ end
29
33
 
30
- def all
31
- [assemblyscript, rust].compact
32
- end
34
+ def all
35
+ [assemblyscript, rust].compact
36
+ end
33
37
 
34
- def assemblyscript
35
- @assemblyscript ||= new_sdk(ExtensionPointAssemblyScriptSDK)
36
- end
38
+ def assemblyscript
39
+ @assemblyscript ||= new_sdk(ExtensionPointAssemblyScriptSDK)
40
+ end
37
41
 
38
- def rust
39
- @rust ||= new_sdk(ExtensionPointRustSDK)
40
- end
42
+ def rust
43
+ @rust ||= new_sdk(ExtensionPointRustSDK)
44
+ end
41
45
 
42
- private
46
+ private
43
47
 
44
- def new_sdk(klass)
45
- config = @config[klass.language]
46
- return nil if config.nil?
47
- klass.new(config)
48
+ def new_sdk(klass)
49
+ config = @config[klass.language]
50
+ return nil if config.nil?
51
+ klass.new(config)
52
+ end
48
53
  end
49
- end
50
54
 
51
- class ExtensionPointSDK
52
- attr_reader :beta, :package
55
+ class ExtensionPointSDK
56
+ attr_reader :version, :beta, :package
53
57
 
54
- def initialize(config)
55
- @beta = config["beta"] || false
56
- @package = config["package"]
57
- end
58
+ def initialize(config)
59
+ @beta = config["beta"] || false
60
+ @package = config["package"]
61
+ @version = config["package-version"]
62
+ end
58
63
 
59
- def beta?
60
- @beta
61
- end
64
+ def beta?
65
+ @beta
66
+ end
67
+
68
+ def versioned?
69
+ @version
70
+ end
62
71
 
63
- def self.language
64
- raise NotImplementedError
72
+ def self.language
73
+ raise NotImplementedError
74
+ end
65
75
  end
66
- end
67
76
 
68
- class ExtensionPointAssemblyScriptSDK < ExtensionPointSDK
69
- attr_reader :sdk_version, :toolchain_version
77
+ class ExtensionPointAssemblyScriptSDK < ExtensionPointSDK
78
+ attr_reader :sdk_version, :toolchain_version
70
79
 
71
- def initialize(config)
72
- super
73
- @sdk_version = config["sdk-version"]
74
- @toolchain_version = config["toolchain-version"]
75
- end
80
+ def initialize(config)
81
+ super
82
+ @sdk_version = config["sdk-version"]
83
+ @toolchain_version = config["toolchain-version"]
84
+ end
76
85
 
77
- def self.language
78
- "assemblyscript"
86
+ def self.language
87
+ "assemblyscript"
88
+ end
79
89
  end
80
- end
81
90
 
82
- class ExtensionPointRustSDK < ExtensionPointSDK
83
- def self.language
84
- "rust"
91
+ class ExtensionPointRustSDK < ExtensionPointSDK
92
+ def self.language
93
+ "rust"
94
+ end
85
95
  end
86
96
  end
87
97
  end
@@ -14,39 +14,32 @@ module Script
14
14
 
15
15
  class << self
16
16
  def create_from_json(ctx, metadata_json)
17
+ err_tag = nil
17
18
  metadata_hash = JSON.parse(metadata_json)
18
- schema_versions = metadata_hash["schemaVersions"]
19
- if schema_versions.nil?
20
- err_msg = "script.error.metadata_schema_versions_missing"
21
- raise ::Script::Layers::Domain::Errors::MetadataValidationError, ctx.message(err_msg)
22
- end
23
- # Scripts may be attached to more than one EP in the future but not right now
24
- unless schema_versions.count == 1
25
- err_msg = "script.error.metadata_schema_versions_single_key"
26
- raise ::Script::Layers::Domain::Errors::MetadataValidationError, ctx.message(err_msg)
27
- end
28
19
 
29
- _, version = schema_versions.first
20
+ use_msgpack = !!metadata_hash.dig("flags", "use_msgpack")
21
+ schema_versions = metadata_hash["schemaVersions"] || {}
22
+
23
+ version = schema_versions.values.first || {}
30
24
  schema_major_version = version["major"]
31
25
  schema_minor_version = version["minor"]
32
- if schema_major_version.nil?
33
- err_msg = "script.error.metadata_schema_versions_missing_major"
34
- raise ::Script::Layers::Domain::Errors::MetadataValidationError, ctx.message(err_msg)
35
- end
36
26
 
37
- if schema_minor_version.nil?
38
- err_msg = "script.error.metadata_schema_versions_missing_minor"
39
- raise ::Script::Layers::Domain::Errors::MetadataValidationError, ctx.message(err_msg)
27
+ if schema_versions.empty?
28
+ err_tag = "script.error.metadata_schema_versions_missing"
29
+ elsif schema_versions.count != 1
30
+ # Scripts may be attached to more than one EP in the future but not right now
31
+ err_tag = "script.error.metadata_schema_versions_single_key"
32
+ elsif schema_major_version.nil?
33
+ err_tag = "script.error.metadata_schema_versions_missing_major"
34
+ elsif schema_minor_version.nil?
35
+ err_tag = "script.error.metadata_schema_versions_missing_minor"
40
36
  end
41
37
 
42
- use_msgpack = !!metadata_hash.dig("flags", "use_msgpack")
43
-
44
38
  Metadata.new(schema_major_version, schema_minor_version, use_msgpack)
45
- rescue ::Script::Layers::Domain::Errors::MetadataValidationError
46
- raise
47
- rescue
48
- err_msg = "script.error.metadata_validation_cause"
49
- raise ::Script::Layers::Domain::Errors::MetadataValidationError, ctx.message(err_msg)
39
+ rescue JSON::ParserError
40
+ err_tag = "script.error.metadata_validation_cause"
41
+ ensure
42
+ raise Errors::MetadataValidationError, ctx.message(err_tag) if err_tag
50
43
  end
51
44
  end
52
45
  end