Split app into py2 and py3 services. (#1460)

* Split app into py2 and py3 services.

* fix typo

* Also deploy app-py3 and upgrade skipfiles to .gcloudignore.

* fix typo

* addressed review comments
This commit is contained in:
Jason Robbins 2021-08-17 15:35:08 -07:00 коммит произвёл GitHub
Родитель 3f5c81ce22
Коммит ecd96ba8dd
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 411 добавлений и 264 удалений

46
.gcloudignore Normal file
Просмотреть файл

@ -0,0 +1,46 @@
# This file specifies files that are *not* uploaded to Google Cloud Platform
# using gcloud. It follows the same syntax as .gitignore, with the addition of
# "#!include" directives (which insert the entries of the given .gitignore-style
# file at that point).
#
# For more information, run:
# $ gcloud topic gcloudignore
#
.gcloudignore
# If you would like to upload your .git directory, .gitignore file or files
# from your .gitignore file, remove the corresponding line
# below:
.git
.gitignore
# Python pycache:
__pycache__/
# Ignored by the build system
/setup.cfg
*~
*.pyc
*.swp
.sass-cache
.vscode
*oauth2.data
.fcm_server_key
*.iml
.idea/*
bulkloader-*
cookie
# Dependency directories
node_modules
# Source for generated files
static/elements/
static/sass/
static/js-src/
# OS files
*.DS_Store
# Test coverage tool
.coverage
htmlcov

Просмотреть файл

@ -1,3 +1,6 @@
# TODO(jrobbins): This GAE service will eventually be almost emptied
# out as most handlers are moved over to app-p3.yaml.
runtime: python27
threadsafe: false
api_version: 1
@ -43,13 +46,15 @@ handlers:
script: internals.fetchmetrics.app
# Any cron job must be harmless if it is called too often or with bad args
- url: /tasks/.*
script: internals.notifier.app
# Note: This handler must remain in this file because it requires GAE py2.
- url: /tasks/outbound-email
script: internals.sendemail.app
# Header checks prevent raw access to this handler. Tasks have headers.
# Note: This handler must remain in this file because it requires GAE py2.
- url: /_ah/bounce
# TODO(jrobbins): phase out this handler when we redo email.
script: internals.notifier.app
script: internals.sendemail.app
login: admin # Prevents raw access to this handler.
- url: /admin/blink.*
@ -119,7 +124,6 @@ handlers:
includes:
- skip_files.yaml
- env_vars.yaml
inbound_services:

9
app-py3.yaml Normal file
Просмотреть файл

@ -0,0 +1,9 @@
# TODO(jrobbins): This file is not used yet.
# It will contain lines to configure a GAE service running in py3.
runtime: python39
service: app-py3
handlers:
- url: /hello
script: auto

Просмотреть файл

@ -1,3 +1,17 @@
dispatch:
- url: "*/tasks/*"
# These will never match, because /_ah/ requests are not routed by dispatch.yaml.
# https://cloud.google.com/appengine/docs/standard/python/reference/dispatch-yaml
# So, these two handlers must remain in the default service (app-py2).
# - url: "*/_ah/bounce"
# service: app-py2
# - url: "*/tasks/outbound-email"
# service: app-py2
- url: "*/tasks/email-subscribers"
service: notifier
- url: "*/hello"
service: app-py3
# TODO(jrobbins): Gradually route most requests to service app-py3.

Просмотреть файл

@ -23,15 +23,12 @@ import logging
import datetime
import json
import os
import re
from framework import ramcache
from google.cloud import ndb
from google.appengine.api import mail
import requests
# from google.appengine.api import users
from framework import users
from google.appengine.ext.webapp.mail_handlers import BounceNotification
from django.template.loader import render_to_string
from django.utils.html import conditional_escape as escape
@ -257,88 +254,21 @@ class FeatureChangeHandler(basehandlers.FlaskHandler):
feature, is_update=is_update, changes=changes)
logging.info('Processing %d email tasks', len(email_tasks))
for one_email_dict in email_tasks:
cloud_tasks_helpers.enqueue_task(
'/tasks/outbound-email', one_email_dict)
if settings.SEND_EMAIL:
cloud_tasks_helpers.enqueue_task(
'/tasks/outbound-email', one_email_dict)
else:
logging.info(
'Would send the following email:\n'
'To: %s\n'
'Subject: %s\n'
'Body:\n%s',
one_email_dict['to'], one_email_dict['subject'],
one_email_dict['html'])
return {'message': 'Done'}
class OutboundEmailHandler(basehandlers.FlaskHandler):
"""Task to send a notification email to one recipient."""
IS_INTERNAL_HANDLER = True
def process_post_data(self):
self.require_task_header()
to = self.get_param('to', required=True)
subject = self.get_param('subject', required=True)
email_html = self.get_param('html', required=True)
if settings.SEND_ALL_EMAIL_TO:
to_user, to_domain = to.split('@')
to = settings.SEND_ALL_EMAIL_TO % {'user': to_user, 'domain': to_domain}
message = mail.EmailMessage(
sender='Chromestatus <admin@%s.appspotmail.com>' % settings.APP_ID,
to=to, subject=subject, html=email_html)
message.check_initialized()
logging.info('Will send the following email:\n')
logging.info('To: %s', message.to)
logging.info('Subject: %s', message.subject)
logging.info('Body:\n%s', message.html)
if settings.SEND_EMAIL:
message.send()
logging.info('Email sent')
else:
logging.info('Email not sent because of settings.SEND_EMAIL')
return {'message': 'Done'}
class BouncedEmailHandler(basehandlers.FlaskHandler):
BAD_WRAP_RE = re.compile('=\r\n')
BAD_EQ_RE = re.compile('=3D')
IS_INTERNAL_HANDLER = True
"""Handler to notice when email to given user is bouncing."""
# For docs on AppEngine's bounce email handling, see:
# https://cloud.google.com/appengine/docs/python/mail/bounce
# Source code is in file:
# google_appengine/google/appengine/ext/webapp/mail_handlers.py
def process_post_data(self):
self.receive(BounceNotification(self.form))
return {'message': 'Done'}
def receive(self, bounce_message):
email_addr = bounce_message.original.get('to')
subject = 'Mail to %r bounced' % email_addr
logging.info(subject)
pref_list = models.UserPref.get_prefs_for_emails([email_addr])
user_pref = pref_list[0]
user_pref.bounced = True
user_pref.put()
# Escalate to someone who might do something about it, e.g.
# find a new owner for a component.
body = ('The following message bounced.\n'
'=================\n'
'From: {from}\n'
'To: {to}\n'
'Subject: {subject}\n\n'
'{text}\n'.format(**bounce_message.original))
logging.info(body)
message = mail.EmailMessage(
sender='Chromestatus <admin@%s.appspotmail.com>' % settings.APP_ID,
to=settings.BOUNCE_ESCALATION_ADDR, subject=subject, body=body)
message.check_initialized()
if settings.SEND_EMAIL:
message.send()
app = basehandlers.FlaskApplication([
('/tasks/email-subscribers', FeatureChangeHandler),
('/tasks/outbound-email', OutboundEmailHandler),
('/_ah/bounce', BouncedEmailHandler),
], debug=settings.DEBUG)

Просмотреть файл

@ -23,7 +23,6 @@ import flask
import mock
import werkzeug.exceptions # Flask HTTP stuff.
from google.appengine.api import mail
# TODO(jrobbins): phase out gae_users.
from google.appengine.api import users as gae_users
from framework import users
@ -335,154 +334,3 @@ class FeatureStarTest(testing_config.CustomTestCase):
self.assertItemsEqual(
[app_user_1.email, app_user_2.email],
[au.email for au in actual])
class OutboundEmailHandlerTest(testing_config.CustomTestCase):
def setUp(self):
self.handler = notifier.OutboundEmailHandler()
self.request_path = '/tasks/outbound-email'
self.to = 'user@example.com'
self.subject = 'test subject'
self.html = '<b>body</b>'
self.sender = ('Chromestatus <admin@%s.appspotmail.com>' %
settings.APP_ID)
@mock.patch('settings.SEND_EMAIL', True)
@mock.patch('settings.SEND_ALL_EMAIL_TO', None)
@mock.patch('google.appengine.api.mail.EmailMessage')
def test_post__prod(self, mock_emailmessage_constructor):
"""On cr-status, we send emails to real users."""
params = {
'to': self.to,
'subject': self.subject,
'html': self.html,
}
with notifier.app.test_request_context(self.request_path, json=params):
actual_response = self.handler.process_post_data()
mock_emailmessage_constructor.assert_called_once_with(
sender=self.sender, to=self.to, subject=self.subject,
html=self.html)
mock_message = mock_emailmessage_constructor.return_value
mock_message.check_initialized.assert_called_once_with()
mock_message.send.assert_called_once_with()
self.assertEqual({'message': 'Done'}, actual_response)
@mock.patch('settings.SEND_EMAIL', True)
@mock.patch('google.appengine.api.mail.EmailMessage')
def test_post__staging(self, mock_emailmessage_constructor):
"""On cr-status-staging, we send emails to an archive."""
params = {
'to': self.to,
'subject': self.subject,
'html': self.html,
}
with notifier.app.test_request_context(self.request_path, json=params):
actual_response = self.handler.process_post_data()
expected_to = 'cr-status-staging-emails+user+example.com@google.com'
mock_emailmessage_constructor.assert_called_once_with(
sender=self.sender, to=expected_to, subject=self.subject,
html=self.html)
mock_message = mock_emailmessage_constructor.return_value
mock_message.check_initialized.assert_called_once_with()
mock_message.send.assert_called_once_with()
self.assertEqual({'message': 'Done'}, actual_response)
@mock.patch('settings.SEND_EMAIL', False)
@mock.patch('google.appengine.api.mail.EmailMessage')
def test_post__local(self, mock_emailmessage_constructor):
"""When running locally, we don't actually send emails."""
params = {
'to': self.to,
'subject': self.subject,
'html': self.html,
}
with notifier.app.test_request_context(self.request_path, json=params):
actual_response = self.handler.process_post_data()
expected_to = 'cr-status-staging-emails+user+example.com@google.com'
mock_emailmessage_constructor.assert_called_once_with(
sender=self.sender, to=expected_to, subject=self.subject,
html=self.html)
mock_message = mock_emailmessage_constructor.return_value
mock_message.check_initialized.assert_called_once_with()
mock_message.send.assert_not_called()
self.assertEqual({'message': 'Done'}, actual_response)
class BouncedEmailHandlerTest(testing_config.CustomTestCase):
def setUp(self):
self.handler = notifier.BouncedEmailHandler()
self.sender = ('Chromestatus <admin@%s.appspotmail.com>' %
settings.APP_ID)
self.expected_to = settings.BOUNCE_ESCALATION_ADDR
@mock.patch('internals.notifier.BouncedEmailHandler.receive')
def test_process_post_data(self, mock_receive):
with notifier.app.test_request_context('/_ah/bounce'):
actual_json = self.handler.process_post_data()
self.assertEqual({'message': 'Done'}, actual_json)
mock_receive.assert_called_once()
@mock.patch('settings.SEND_EMAIL', True)
@mock.patch('google.appengine.api.mail.EmailMessage')
def test_receive__user_has_prefs(self, mock_emailmessage_constructor):
"""When we get a bounce, we update the UserPrefs for that user."""
starrer_3_pref = models.UserPref(
email='starrer_3@example.com',
notify_as_starrer=False)
starrer_3_pref.put()
bounce_message = testing_config.Blank(
original={'to': 'starrer_3@example.com',
'from': 'sender',
'subject': 'subject',
'text': 'body'})
self.handler.receive(bounce_message)
updated_pref = models.UserPref.get_by_id(starrer_3_pref.key.integer_id())
self.assertEqual('starrer_3@example.com', updated_pref.email)
self.assertTrue(updated_pref.bounced)
self.assertFalse(updated_pref.notify_as_starrer)
expected_subject = "Mail to 'starrer_3@example.com' bounced"
mock_emailmessage_constructor.assert_called_once_with(
sender=self.sender, to=self.expected_to, subject=expected_subject,
body=mock.ANY)
mock_message = mock_emailmessage_constructor.return_value
mock_message.check_initialized.assert_called_once_with()
mock_message.send.assert_called()
@mock.patch('settings.SEND_EMAIL', True)
@mock.patch('google.appengine.api.mail.EmailMessage')
def test_receive__create_prefs(self, mock_emailmessage_constructor):
"""When we get a bounce, we create the UserPrefs for that user."""
# Note, no existing UserPref for starrer_4.
bounce_message = testing_config.Blank(
original={'to': 'starrer_4@example.com',
'from': 'sender',
'subject': 'subject',
'text': 'body'})
self.handler.receive(bounce_message)
prefs_list = models.UserPref.get_prefs_for_emails(
['starrer_4@example.com'])
updated_pref = prefs_list[0]
self.assertEqual('starrer_4@example.com', updated_pref.email)
self.assertTrue(updated_pref.bounced)
self.assertTrue(updated_pref.notify_as_starrer)
expected_subject = "Mail to 'starrer_4@example.com' bounced"
mock_emailmessage_constructor.assert_called_once_with(
sender=self.sender, to=self.expected_to, subject=expected_subject,
body=mock.ANY)
mock_message = mock_emailmessage_constructor.return_value
mock_message.check_initialized.assert_called_once_with()
mock_message.send.assert_called()

109
internals/sendemail.py Normal file
Просмотреть файл

@ -0,0 +1,109 @@
# -*- coding: utf-8 -*-
# Copyright 2021 Google Inc.
#
# 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.
from __future__ import division
from __future__ import print_function
import logging
import re
from google.appengine.api import mail
from google.appengine.ext.webapp.mail_handlers import BounceNotification
# TODO(jrobbins): For now, these files and files that they import must
# remain compatible with both py2 and py3. We could split those files too.
import settings
from framework import basehandlers
from internals import models
class OutboundEmailHandler(basehandlers.FlaskHandler):
"""Task to send a notification email to one recipient."""
IS_INTERNAL_HANDLER = True
def process_post_data(self):
self.require_task_header()
to = self.get_param('to', required=True)
subject = self.get_param('subject', required=True)
email_html = self.get_param('html', required=True)
if settings.SEND_ALL_EMAIL_TO:
to_user, to_domain = to.split('@')
to = settings.SEND_ALL_EMAIL_TO % {'user': to_user, 'domain': to_domain}
message = mail.EmailMessage(
sender='Chromestatus <admin@%s.appspotmail.com>' % settings.APP_ID,
to=to, subject=subject, html=email_html)
message.check_initialized()
logging.info('Will send the following email:\n')
logging.info('To: %s', message.to)
logging.info('Subject: %s', message.subject)
logging.info('Body:\n%s', message.html)
if settings.SEND_EMAIL:
message.send()
logging.info('Email sent')
else:
logging.info('Email not sent because of settings.SEND_EMAIL')
return {'message': 'Done'}
class BouncedEmailHandler(basehandlers.FlaskHandler):
BAD_WRAP_RE = re.compile('=\r\n')
BAD_EQ_RE = re.compile('=3D')
IS_INTERNAL_HANDLER = True
"""Handler to notice when email to given user is bouncing."""
# For docs on AppEngine's bounce email handling, see:
# https://cloud.google.com/appengine/docs/python/mail/bounce
# Source code is in file:
# google_appengine/google/appengine/ext/webapp/mail_handlers.py
def process_post_data(self):
self.receive(BounceNotification(self.form))
return {'message': 'Done'}
def receive(self, bounce_message):
email_addr = bounce_message.original.get('to')
subject = 'Mail to %r bounced' % email_addr
logging.info(subject)
pref_list = models.UserPref.get_prefs_for_emails([email_addr])
user_pref = pref_list[0]
user_pref.bounced = True
user_pref.put()
# Escalate to someone who might do something about it, e.g.
# find a new owner for a component.
body = ('The following message bounced.\n'
'=================\n'
'From: {from}\n'
'To: {to}\n'
'Subject: {subject}\n\n'
'{text}\n'.format(**bounce_message.original))
logging.info(body)
message = mail.EmailMessage(
sender='Chromestatus <admin@%s.appspotmail.com>' % settings.APP_ID,
to=settings.BOUNCE_ESCALATION_ADDR, subject=subject, body=body)
message.check_initialized()
if settings.SEND_EMAIL:
message.send()
app = basehandlers.FlaskApplication([
('/tasks/outbound-email', OutboundEmailHandler),
('/_ah/bounce', BouncedEmailHandler),
], debug=settings.DEBUG)

180
internals/sendemail_test.py Normal file
Просмотреть файл

@ -0,0 +1,180 @@
from __future__ import division
from __future__ import print_function
# Copyright 2021 Google Inc.
#
# 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.
import collections
import json
import testing_config # Must be imported before the module under test.
import flask
import mock
import werkzeug.exceptions # Flask HTTP stuff.
from google.appengine.api import mail
import settings
from internals import models
from internals import sendemail
class OutboundEmailHandlerTest(testing_config.CustomTestCase):
def setUp(self):
self.handler = sendemail.OutboundEmailHandler()
self.request_path = '/tasks/outbound-email'
self.to = 'user@example.com'
self.subject = 'test subject'
self.html = '<b>body</b>'
self.sender = ('Chromestatus <admin@%s.appspotmail.com>' %
settings.APP_ID)
@mock.patch('settings.SEND_EMAIL', True)
@mock.patch('settings.SEND_ALL_EMAIL_TO', None)
@mock.patch('google.appengine.api.mail.EmailMessage')
def test_post__prod(self, mock_emailmessage_constructor):
"""On cr-status, we send emails to real users."""
params = {
'to': self.to,
'subject': self.subject,
'html': self.html,
}
with sendemail.app.test_request_context(self.request_path, json=params):
actual_response = self.handler.process_post_data()
mock_emailmessage_constructor.assert_called_once_with(
sender=self.sender, to=self.to, subject=self.subject,
html=self.html)
mock_message = mock_emailmessage_constructor.return_value
mock_message.check_initialized.assert_called_once_with()
mock_message.send.assert_called_once_with()
self.assertEqual({'message': 'Done'}, actual_response)
@mock.patch('settings.SEND_EMAIL', True)
@mock.patch('google.appengine.api.mail.EmailMessage')
def test_post__staging(self, mock_emailmessage_constructor):
"""On cr-status-staging, we send emails to an archive."""
params = {
'to': self.to,
'subject': self.subject,
'html': self.html,
}
with sendemail.app.test_request_context(self.request_path, json=params):
actual_response = self.handler.process_post_data()
expected_to = 'cr-status-staging-emails+user+example.com@google.com'
mock_emailmessage_constructor.assert_called_once_with(
sender=self.sender, to=expected_to, subject=self.subject,
html=self.html)
mock_message = mock_emailmessage_constructor.return_value
mock_message.check_initialized.assert_called_once_with()
mock_message.send.assert_called_once_with()
self.assertEqual({'message': 'Done'}, actual_response)
@mock.patch('settings.SEND_EMAIL', False)
@mock.patch('google.appengine.api.mail.EmailMessage')
def test_post__local(self, mock_emailmessage_constructor):
"""When running locally, we don't actually send emails."""
params = {
'to': self.to,
'subject': self.subject,
'html': self.html,
}
with sendemail.app.test_request_context(self.request_path, json=params):
actual_response = self.handler.process_post_data()
expected_to = 'cr-status-staging-emails+user+example.com@google.com'
mock_emailmessage_constructor.assert_called_once_with(
sender=self.sender, to=expected_to, subject=self.subject,
html=self.html)
mock_message = mock_emailmessage_constructor.return_value
mock_message.check_initialized.assert_called_once_with()
mock_message.send.assert_not_called()
self.assertEqual({'message': 'Done'}, actual_response)
class BouncedEmailHandlerTest(testing_config.CustomTestCase):
def setUp(self):
self.handler = sendemail.BouncedEmailHandler()
self.sender = ('Chromestatus <admin@%s.appspotmail.com>' %
settings.APP_ID)
self.expected_to = settings.BOUNCE_ESCALATION_ADDR
@mock.patch('internals.sendemail.BouncedEmailHandler.receive')
def test_process_post_data(self, mock_receive):
with sendemail.app.test_request_context('/_ah/bounce'):
actual_json = self.handler.process_post_data()
self.assertEqual({'message': 'Done'}, actual_json)
mock_receive.assert_called_once()
@mock.patch('settings.SEND_EMAIL', True)
@mock.patch('google.appengine.api.mail.EmailMessage')
def test_receive__user_has_prefs(self, mock_emailmessage_constructor):
"""When we get a bounce, we update the UserPrefs for that user."""
starrer_3_pref = models.UserPref(
email='starrer_3@example.com',
notify_as_starrer=False)
starrer_3_pref.put()
bounce_message = testing_config.Blank(
original={'to': 'starrer_3@example.com',
'from': 'sender',
'subject': 'subject',
'text': 'body'})
self.handler.receive(bounce_message)
updated_pref = models.UserPref.get_by_id(starrer_3_pref.key.integer_id())
self.assertEqual('starrer_3@example.com', updated_pref.email)
self.assertTrue(updated_pref.bounced)
self.assertFalse(updated_pref.notify_as_starrer)
expected_subject = "Mail to 'starrer_3@example.com' bounced"
mock_emailmessage_constructor.assert_called_once_with(
sender=self.sender, to=self.expected_to, subject=expected_subject,
body=mock.ANY)
mock_message = mock_emailmessage_constructor.return_value
mock_message.check_initialized.assert_called_once_with()
mock_message.send.assert_called()
@mock.patch('settings.SEND_EMAIL', True)
@mock.patch('google.appengine.api.mail.EmailMessage')
def test_receive__create_prefs(self, mock_emailmessage_constructor):
"""When we get a bounce, we create the UserPrefs for that user."""
# Note, no existing UserPref for starrer_4.
bounce_message = testing_config.Blank(
original={'to': 'starrer_4@example.com',
'from': 'sender',
'subject': 'subject',
'text': 'body'})
self.handler.receive(bounce_message)
prefs_list = models.UserPref.get_prefs_for_emails(
['starrer_4@example.com'])
updated_pref = prefs_list[0]
self.assertEqual('starrer_4@example.com', updated_pref.email)
self.assertTrue(updated_pref.bounced)
self.assertTrue(updated_pref.notify_as_starrer)
expected_subject = "Mail to 'starrer_4@example.com' bounced"
mock_emailmessage_constructor.assert_called_once_with(
sender=self.sender, to=self.expected_to, subject=expected_subject,
body=mock.ANY)
mock_message = mock_emailmessage_constructor.return_value
mock_message.check_initialized.assert_called_once_with()
mock_message.send.assert_called()

21
main.py Normal file
Просмотреть файл

@ -0,0 +1,21 @@
from flask import Flask
import logging
logging.basicConfig(level=logging.INFO)
# If `entrypoint` is not defined in app.yaml, App Engine will look for an app
# called `app` in `main.py`.
app = Flask(__name__)
@app.route('/hello')
def hello():
"""Return a friendly HTTP greeting."""
logging.info('In hello() !')
return 'Hello python 3!'
if __name__ == '__main__':
# This is used when running locally only. When deploying to Google App
# Engine, a webserver process such as Gunicorn will serve the app. This
# can be configured by adding an `entrypoint` to app.yaml.
app.run(host='127.0.0.1', port=8080, debug=True)

Просмотреть файл

@ -9,7 +9,6 @@ handlers:
# Header checks prevent raw access to this handler. Tasks have headers.
includes:
- skip_files.yaml
- env_vars.yaml
libraries:

Просмотреть файл

@ -29,5 +29,8 @@ gcloud app deploy \
--project $appName \
--version $deployVersion \
--no-promote \
$BASEDIR/../app.yaml $BASEDIR/../notifier.yaml \
$BASEDIR/../app-py2.yaml $BASEDIR/../notifier.yaml \
$BASEDIR/../app-py3.yaml \
$BASEDIR/../dispatch.yaml $BASEDIR/../cron.yaml
# TODO(jrobbins): Add app_py3.yaml when it has some contents.

Просмотреть файл

@ -11,4 +11,11 @@ readonly BASEDIR=$(dirname $BASH_SOURCE)
dev_appserver.py -A cr-status --enable_console=1 \
--support_datastore_emulator=1 --datastore_emulator_port=15606 \
--env_var DATASTORE_EMULATOR_HOST='localhost:15606' \
$BASEDIR/../app.yaml $BASEDIR/../notifier.yaml
$BASEDIR/../dispatch.yaml \
$BASEDIR/../app-py2.yaml $BASEDIR/../notifier.yaml \
$BASEDIR/../app-py3.yaml
# TODO(jrobbins): Add app-py3.yaml when it has some contents. At some point in
# the future when we cannot run the app-py2 service locally, tasks for
# outbound-email would simply queue up forever. However, we don't even create
# those tasks in dev mode.

Просмотреть файл

@ -1,23 +0,0 @@
skip_files:
- ^(.*/)?app\.yaml
- ^(.*/)?app\.yml
- ^(.*/)?index\.yaml
- ^(.*/)?index\.yml
- ^(.*/)?#.*#
- ^(.*/)?.*~
- ^(.*/)?.*\.py[co]
- ^(.*/)?.*/RCS/.*
- ^(.*/)?\..*
- ^(.*/)?.*\.csv$
- ^(.*/)?.*\.psd$
- ^(.*/)?.*\.sql[3]$
- ^(.*/)?.*\.sh$
- ^(.*/)?.*\.scss$
- ^(.*/)?node_modules
- ^(.*/)?tests/
- ^(.*/)?.[LICENSE|PATENTS|AUTHORS|CONTRIBUTING|COPYING](\.md)?
- ^(.*/)?.*\.md$
- ^http2push/example
- ^http2push/site
- ^static/sass