require 'json' module Shipit class DeploySpec module NpmDiscovery # https://docs.npmjs.com/cli/publish PUBLIC = 'public'.freeze PRIVATE = 'restricted'.freeze VALID_ACCESS = [PUBLIC, PRIVATE].freeze NPM_REGISTRY = "https://registry.npmjs.org/".freeze def discover_dependencies_steps discover_package_json || super end def discover_package_json npm_install if yarn? || npm? end def npm_install [js_command('install --no-progress')] end def discover_review_checklist discover_yarn_checklist || discover_npm_checklist || super end def discover_yarn_checklist if yarn? [%(<strong>Don't forget version and tag before publishing!</strong> You can do this with:<br/> yarn version --new-version <strong><major|minor|patch></strong> && git push --follow-tags</pre>)] end end def discover_npm_checklist if npm? [%(<strong>Don't forget version and tag before publishing!</strong> You can do this with:<br/> npm version <strong><major|minor|patch></strong> && git push --follow-tags</pre>)] end end def npm? public? end def public? file = package_json return false unless file.exist? JSON.parse(file.read)['private'].blank? end def dist_tag(version) # Pre-release SemVer tags such as 'beta', 'alpha', 'rc' and 'next' # are treated as 'next' npm dist-tags. # An 1.0.0-beta.1 would be installable using both: # `yarn add package@1.0.0-beta.1` and `yarn add package@next` return 'next' if ['-beta', '-alpha', '-rc', '-next'].any? { |tag| version.include? tag } 'latest' end def package_json file('package.json') end def package_json_contents @package_json_contents ||= JSON.parse(package_json.read) end def yarn? yarn_lock.exist? && public? end def yarn_lock file('./yarn.lock') end def discover_npm_package publish_npm_package if yarn? || npm? end def discover_deploy_steps discover_npm_package || super end def package_name package_json_contents['name'] end def package_version package_json_contents['version'] end def publish_config package_json_contents['publishConfig'] end def publish_config_access config = publish_config # default to private deploy when we enforce a publishConfig if enforce_publish_config? return PRIVATE if config.blank? config['access'] || PRIVATE end return PUBLIC if config.blank? config['access'] || PUBLIC end def scoped_package? return false if Shipit.npm_org_scope.nil? package_name.start_with?(Shipit.npm_org_scope) end def enforce_publish_config? enforce = Shipit.enforce_publish_config return false if enforce.nil? || enforce.to_s == "0" true end def valid_publish_config? return true unless enforce_publish_config? return false if Shipit.private_npm_registry.nil? return false if publish_config.blank? return true if publish_config_access == PUBLIC valid_publish_config_access? && private_scoped_package? end def valid_publish_config_access? VALID_ACCESS.include?(publish_config_access) end # ensure private packages are scoped def private_scoped_package? publish_config_access == PRIVATE && scoped_package? end def local_npmrc file(".npmrc") end def registry scope = Shipit.npm_org_scope prefix = scoped_package? ? "#{scope}:registry" : "registry" if publish_config_access == PUBLIC return "#{prefix}=#{NPM_REGISTRY}" end "#{prefix}=#{Shipit.private_npm_registry}" end def publish_npm_package return ['misconfigured-npm-publish-config'] unless valid_publish_config? generate_npmrc = "generate-local-npmrc \"#{registry}\"" check_tags = 'assert-npm-version-tag' # `yarn publish` requires user input, so always use npm. publish = "npm publish --tag #{dist_tag(package_version)} --access #{publish_config_access}" return [check_tags, generate_npmrc, publish] if enforce_publish_config? [check_tags, publish] end def js_command(command_args) runner = if yarn? 'yarn' else 'npm' end "#{runner} #{command_args}" end end end end