#!/usr/bin/python # Copyright 2015 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Language detection script. import json import os import re import sys import subprocess DEV_NULL = open(os.devnull, 'w') # Augment the path with our library directory. ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))) sys.path.append(os.path.join(ROOT_DIR, 'lib')) import comm def asList(val): """Returns the value if it is a list else returns a list with the value. """ return val if isinstance(val, list) else [val] def main(args): if len(args) != 2: # If we're being called incorrectly, this probably isn't happening from a # framework. sys.stderr.write('Invalid Usage: %s ' % sys.argv[0]) return 1 # Get the first argument, should be the source root directory. path = args[1] # Show failures in debug or error depending on whether nodejs has been # explicitly specified. config = comm.get_config() if ((config.params.appinfo and config.params.appinfo.runtime) or config.params.runtime): log_detect_error = comm.error else: log_detect_error = comm.debug comm.info('Checking for Node.js.') package_json = os.path.join(path, 'package.json') yarn_lock_name = 'yarn.lock' yarn_lock = os.path.join(path, yarn_lock_name) got_shrinkwrap = False if not os.path.isfile(package_json): comm.debug('node.js checker: No package.json file.') got_package_json = False got_scripts_start = False node_version = None use_yarn = False else: got_package_json = True # If there's a package.json file, that's unusual enough that any # errors should be surfaced to the user. log_detect_error = comm.error # Consider the yarn.lock file as present if and only if the yarn.lock # file exists and is not specified as being skipped in app.yaml. try: raw_skip_files = config.params.appinfo.skip_files skip_files = [] if raw_skip_files is None else asList(raw_skip_files) except AttributeError: # If the skip_file attribute or any other parent attribute # does not exist, treat the skip_files as not user specified. skip_files = [] yarn_lock_exists = os.path.isfile(yarn_lock) yarn_lock_skipped = False for pattern in skip_files: if re.match(pattern, yarn_lock_name) is not None: yarn_lock_skipped = True break; use_yarn = yarn_lock_exists and not yarn_lock_skipped # Try to read the package.json file. try: with open(package_json) as f: contents = json.load(f) except (IOError, ValueError) as ex: # If we have an invalid or unreadable package.json file, there's # something funny going on here so fail recognition. # A package.json that exists is unusual enough that we want to warn # regardless of whether the nodejs runtime was specified. log_detect_error('node.js checker: error accessing package.json: ' '%r' % ex) return 1 # See if we've got a scripts.start field. got_scripts_start = bool(contents.get('scripts', {}).get('start')) # See if a version of node is specified. try: node_version = contents.get('engines', {}).get('node', None) comm.info('node version is %s', node_version) except AttributeError: # Most likely "engines" wasn't a dictionary. comm.warn('node.js checker: ignoring invalid "engines" field in ' 'package.json') node_version = None if node_version is None: comm.warn('No node version specified. Please add your node ' 'version, see ' 'https://docs.npmjs.com/files/package.json#engines') if got_scripts_start or os.path.exists(os.path.join(path, 'server.js')): runtime = 'custom' if config.params.custom else 'nodejs' appinfo = {'runtime': runtime, 'env': 'flex'} comm.send_runtime_params({'got_package_json': got_package_json, 'got_scripts_start': got_scripts_start, 'node_version': node_version, 'use_yarn': use_yarn}, appinfo=appinfo) else: log_detect_error('node.js checker: Neither "start" in the "scripts" ' 'section of "package.json" nor the "server.js" file ' 'were found.') return 1 return 0 if __name__ == '__main__': sys.exit(main(sys.argv))