feat: provide github workflow for unittest in new app
This commit is contained in:
parent
3fd4759dd4
commit
42942f36a6
2 changed files with 136 additions and 10 deletions
|
|
@ -7,8 +7,14 @@ import unittest
|
|||
from io import StringIO
|
||||
from unittest.mock import patch
|
||||
|
||||
import yaml
|
||||
|
||||
import frappe
|
||||
from frappe.utils.boilerplate import _create_app_boilerplate, _get_inputs
|
||||
from frappe.utils.boilerplate import (
|
||||
_create_app_boilerplate,
|
||||
_get_user_inputs,
|
||||
github_workflow_template,
|
||||
)
|
||||
|
||||
|
||||
class TestBoilerPlate(unittest.TestCase):
|
||||
|
|
@ -24,6 +30,7 @@ class TestBoilerPlate(unittest.TestCase):
|
|||
"app_icon": "octicon octicon-file-directory",
|
||||
"app_color": "grey",
|
||||
"app_license": "MIT",
|
||||
"create_github_workflow": False,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -36,6 +43,7 @@ class TestBoilerPlate(unittest.TestCase):
|
|||
"icon": "", # empty -> default
|
||||
"color": "",
|
||||
"app_license": "MIT",
|
||||
"github_workflow": "n",
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -85,7 +93,7 @@ class TestBoilerPlate(unittest.TestCase):
|
|||
|
||||
def test_simple_input_to_boilerplate(self):
|
||||
with patch("sys.stdin", self.get_user_input_stream(self.default_user_input)):
|
||||
hooks = _get_inputs(self.default_hooks.app_name)
|
||||
hooks = _get_user_inputs(self.default_hooks.app_name)
|
||||
self.assertDictEqual(hooks, self.default_hooks)
|
||||
|
||||
def test_invalid_inputs(self):
|
||||
|
|
@ -95,9 +103,12 @@ class TestBoilerPlate(unittest.TestCase):
|
|||
}
|
||||
)
|
||||
with patch("sys.stdin", self.get_user_input_stream(invalid_inputs)):
|
||||
hooks = _get_inputs(self.default_hooks.app_name)
|
||||
hooks = _get_user_inputs(self.default_hooks.app_name)
|
||||
self.assertEqual(hooks.app_title, "valid title")
|
||||
|
||||
def test_valid_ci_yaml(self):
|
||||
yaml.safe_load(github_workflow_template.format(**self.default_hooks))
|
||||
|
||||
def test_create_app(self):
|
||||
app_name = "test_app"
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
# License: MIT. See LICENSE
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
|
||||
import click
|
||||
import git
|
||||
|
||||
import frappe
|
||||
from frappe.utils import cstr, touch_file
|
||||
from frappe.utils import touch_file
|
||||
|
||||
|
||||
def make_boilerplate(dest, app_name, no_git=False):
|
||||
|
|
@ -18,11 +19,11 @@ def make_boilerplate(dest, app_name, no_git=False):
|
|||
|
||||
# app_name should be in snake_case
|
||||
app_name = frappe.scrub(app_name)
|
||||
hooks = _get_inputs(app_name)
|
||||
hooks = _get_user_inputs(app_name)
|
||||
_create_app_boilerplate(dest, hooks, no_git=no_git)
|
||||
|
||||
|
||||
def _get_inputs(app_name):
|
||||
def _get_user_inputs(app_name):
|
||||
"""Prompt user for various inputs related to new app and return config."""
|
||||
app_name = frappe.scrub(app_name)
|
||||
|
||||
|
|
@ -32,7 +33,7 @@ def _get_inputs(app_name):
|
|||
|
||||
new_app_config = {
|
||||
"app_title": {
|
||||
"prompt": "App Title".format(app_title),
|
||||
"prompt": "App Title",
|
||||
"default": app_title,
|
||||
"validator": is_valid_title,
|
||||
},
|
||||
|
|
@ -42,14 +43,23 @@ def _get_inputs(app_name):
|
|||
"app_icon": {"prompt": "App Icon", "default": "octicon octicon-file-directory"},
|
||||
"app_color": {"prompt": "App Color", "default": "grey"},
|
||||
"app_license": {"prompt": "App License", "default": "MIT"},
|
||||
"create_github_workflow": {
|
||||
"prompt": "Create GitHub Workflow action for unittests",
|
||||
"default": False,
|
||||
"type": bool,
|
||||
},
|
||||
}
|
||||
|
||||
for property, config in new_app_config.items():
|
||||
value = None
|
||||
input_type = config.get("type", str)
|
||||
|
||||
while value is None:
|
||||
value = click.prompt(
|
||||
config["prompt"], default=config.get("default"), type=config.get("type", str)
|
||||
)
|
||||
if input_type == bool:
|
||||
value = click.confirm(config["prompt"], default=config.get("default"))
|
||||
else:
|
||||
value = click.prompt(config["prompt"], default=config.get("default"), type=input_type)
|
||||
|
||||
if validator_function := config.get("validator"):
|
||||
if not validator_function(value):
|
||||
value = None
|
||||
|
|
@ -133,6 +143,9 @@ def _create_app_boilerplate(dest, hooks, no_git=False):
|
|||
|
||||
app_directory = os.path.join(dest, hooks.app_name)
|
||||
|
||||
if hooks.create_github_workflow:
|
||||
_create_github_workflow_files(dest, hooks)
|
||||
|
||||
if not no_git:
|
||||
with open(os.path.join(dest, hooks.app_name, ".gitignore"), "w") as f:
|
||||
f.write(frappe.as_unicode(gitignore_template.format(app_name=hooks.app_name)))
|
||||
|
|
@ -145,6 +158,15 @@ def _create_app_boilerplate(dest, hooks, no_git=False):
|
|||
print(f"'{hooks.app_name}' created at {app_directory}")
|
||||
|
||||
|
||||
def _create_github_workflow_files(dest, hooks):
|
||||
workflows_path = pathlib.Path(dest) / hooks.app_name / ".github" / "workflows"
|
||||
workflows_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
ci_workflow = workflows_path / "ci.yml"
|
||||
with open(ci_workflow, "w") as f:
|
||||
f.write(github_workflow_template.format(**hooks))
|
||||
|
||||
|
||||
manifest_template = """include MANIFEST.in
|
||||
include requirements.txt
|
||||
include *.json
|
||||
|
|
@ -422,3 +444,96 @@ Configuration for docs
|
|||
def get_context(context):
|
||||
context.brand_html = "{app_title}"
|
||||
'''
|
||||
|
||||
|
||||
github_workflow_template = """
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: develop-{app_name}-${{{{ github.event.number }}}}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
name: Server
|
||||
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:10.3
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
ports:
|
||||
- 3306:3306
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.9
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
check-latest: true
|
||||
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{{{ runner.os }}}}-pip-${{{{ hashFiles('**/*requirements.txt') }}}}
|
||||
restore-keys: |
|
||||
${{{{ runner.os }}}}-pip-
|
||||
${{{{ runner.os }}}}-
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: 'echo "::set-output name=dir::$(yarn cache dir)"'
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{{{ steps.yarn-cache-dir-path.outputs.dir }}}}
|
||||
key: ${{{{ runner.os }}}}-yarn-${{{{ hashFiles('**/yarn.lock') }}}}
|
||||
restore-keys: |
|
||||
${{{{ runner.os }}}}-yarn-
|
||||
|
||||
- name: Setup
|
||||
run: |
|
||||
pip install frappe-bench
|
||||
bench init --skip-redis-config-generation --skip-assets --python "$(which python)" ~/frappe-bench
|
||||
mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'"
|
||||
mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"
|
||||
|
||||
- name: Install
|
||||
working-directory: /home/runner/frappe-bench
|
||||
run: |
|
||||
bench get-app {app_name} $GITHUB_WORKSPACE
|
||||
bench setup requirements --dev
|
||||
bench new-site --db-root-password root --admin-password admin test_site
|
||||
bench --site test_site install-app {app_name}
|
||||
bench build
|
||||
env:
|
||||
CI: 'Yes'
|
||||
|
||||
- name: Run Tests
|
||||
working-directory: /home/runner/frappe-bench
|
||||
run: |
|
||||
bench --site test_site set-config allow_tests true
|
||||
bench --site test_site run-tests --app {app_name}
|
||||
env:
|
||||
TYPE: server
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue