Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static CI checks #9

Merged
merged 2 commits into from
Nov 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[flake8]
max-line-length = 120
max-complexity = 10
ignore =
E126,
E501,
E722,
E741,
F401,
F811,
C901
3 changes: 2 additions & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# See for instructions on this file https://help.github.com/articles/about-codeowners/

index.json @derekbekoe
/src/index.json @derekbekoe

/src/image-copy/ @tamirkamara
7 changes: 7 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---

This checklist is used to make sure that common guidelines for a pull request are followed.

### General Guidelines

- [ ] Have you run `./scripts/ci/test_static.sh` locally? (`pip install pylint flake8` required)
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,8 @@ ENV/
# mkdocs documentation
/site

# VS Code
.vscode/settings.json

# mypy
.mypy_cache/
19 changes: 12 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
dist: trusty
sudo: off
language: python
python:
- "2.7"
- "3.5"
- "3.6"
install: true
script:
- ls
install:
- pip install pylint flake8
jobs:
include:
- stage: verify
script: ./scripts/ci/test_static.sh
env: PURPOSE='VerifySource-StaticCheck'
python: 3.6
- stage: verify
script: ./scripts/ci/test_static.sh
env: PURPOSE='VerifySource-StaticCheck'
python: 2.7
9 changes: 9 additions & 0 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Contribute Code
===================================

This project has adopted the `Microsoft Open Source Code of Conduct <https://opensource.microsoft.com/codeofconduct/>`__.

For more information see the `Code of Conduct FAQ <https://opensource.microsoft.com/codeofconduct/faq/>`__ or contact `opencode@microsoft.com <mailto:opencode@microsoft.com>`__ with any additional questions or comments.

If you would like to become an active contributor to this project please
follow the instructions provided in `Microsoft Azure Projects Contribution Guidelines <http://azure.github.io/guidelines.html>`__
48 changes: 48 additions & 0 deletions pylintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[MASTER]
reports=no
score=no

[MESSAGES CONTROL]
# For all codes, run 'pylint --list-msgs' or go to 'https://pylint.readthedocs.io/en/latest/reference_guide/features.html'
# locally-disabled: Warning locally suppressed using disable-msg
# cyclic-import: because of https://github.com/PyCQA/pylint/issues/850
# too-many-arguments: Due to the nature of the CLI many commands have large arguments set which reflect in large arguments set in corresponding methods.
disable=missing-docstring,locally-disabled,fixme,cyclic-import,too-many-arguments,invalid-name,duplicate-code

[TYPECHECK]
# For Azure CLI extensions, we ignore import errors for azure.cli as they'll be available in the environment of the CLI
ignored-modules=azure.cli

[FORMAT]
max-line-length=120

[VARIABLES]
# Tells whether we should check for unused import in __init__ files.
init-import=yes

[DESIGN]
# Maximum number of locals for function / method body
max-locals=25
# Maximum number of branch for function / method body
max-branches=20

[SIMILARITIES]
min-similarity-lines=10

[BASIC]
# Naming hints based on PEP 8 (https://www.python.org/dev/peps/pep-0008/#naming-conventions).
# Consider these guidelines and not hard rules. Read PEP 8 for more details.

# The invalid-name checker must be **enabled** for these hints to be used.
include-naming-hint=yes

module-name-hint=lowercase (keep short; underscores are discouraged)
const-name-hint=UPPER_CASE_WITH_UNDERSCORES
class-name-hint=CapitalizedWords
class-attribute-name-hint=lower_case_with_underscores
attr-name-hint=lower_case_with_underscores
method-name-hint=lower_case_with_underscores
function-name-hint=lower_case_with_underscores
argument-name-hint=lower_case_with_underscores
variable-name-hint=lower_case_with_underscores
inlinevar-name-hint=lower_case_with_underscores (short is OK)
12 changes: 12 additions & 0 deletions scripts/ci/test_static.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -e

proc_number=`python -c 'import multiprocessing; print(multiprocessing.cpu_count())'`
pylint ./src/*/azext_*/ --rcfile=./pylintrc -j $proc_number
flake8 --statistics --append-config=./.flake8 ./src/*/azext_*/

pylint ./scripts/ci/*.py --rcfile=./pylintrc
flake8 --append-config=./.flake8 ./scripts/ci/*.py

python ./scripts/ci/verify_codeowners.py
python ./scripts/ci/verify_license.py
13 changes: 13 additions & 0 deletions scripts/ci/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

import os


def get_repo_root():
current_dir = os.path.dirname(os.path.abspath(__file__))
while not os.path.exists(os.path.join(current_dir, 'CONTRIBUTING.rst')):
current_dir = os.path.dirname(current_dir)
return current_dir
42 changes: 42 additions & 0 deletions scripts/ci/verify_codeowners.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from __future__ import print_function

import os
import sys

from util import get_repo_root

REPO_ROOT = get_repo_root()
CODEOWNERS = os.path.join(REPO_ROOT, '.github', 'CODEOWNERS')
SRC_DIR = os.path.join(REPO_ROOT, 'src')


def get_src_dir_codeowners():
contents = []
with open(CODEOWNERS) as f:
contents = [x.strip() for x in f.readlines()]
return dict([x.split(' ') for x in contents if x.startswith('/src/') and x.split(' ')[0].endswith('/')])


def main():
owners = get_src_dir_codeowners()
dangling_entries = [e for e in owners if not os.path.isdir(os.path.join(REPO_ROOT, e[1:]))]
missing_entries = ['/src/{}/'.format(p) for p in os.listdir(SRC_DIR)
if os.path.isdir(os.path.join(SRC_DIR, p)) and '/src/{}/'.format(p) not in owners]
if dangling_entries or missing_entries:
print('Errors whilst verifying {}!'.format(CODEOWNERS))
if dangling_entries:
print("Remove the following {} as these directories don't exist.".format(dangling_entries),
file=sys.stderr)
if missing_entries:
print("The following directories are missing codeowners {}.".format(missing_entries),
file=sys.stderr)
sys.exit(1)


if __name__ == '__main__':
main()
44 changes: 44 additions & 0 deletions scripts/ci/verify_license.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from __future__ import print_function

import os
import sys

from util import get_repo_root

REPO_ROOT = get_repo_root()
SRC_DIR = os.path.join(REPO_ROOT, 'src')

LICENSE_HEADER = """# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
"""


def main():
env_path = os.path.join(REPO_ROOT, 'env')

files_without_header = []
for current_dir, _, files in os.walk(get_repo_root()):
if current_dir.startswith(env_path):
continue
file_itr = (os.path.join(current_dir, p) for p in files if p.endswith('.py'))
for python_file in file_itr:
with open(python_file, 'r') as f:
file_text = f.read()
if file_text and LICENSE_HEADER not in file_text:
files_without_header.append(os.path.join(current_dir, python_file))

if files_without_header:
print("Error: The following files don't have the required license headers: \n{}".format(
'\n'.join(files_without_header)), file=sys.stderr)
sys.exit(1)


if __name__ == '__main__':
main()
26 changes: 18 additions & 8 deletions src/image-copy/azext_imagecopy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,29 @@

helps['image copy'] = """
type: command
short-summary: Allows to copy a managed image (or vm) to other regions. Keep in mind that it requires the source disk to be available.
short-summary: Copy a managed image (or vm) to other regions
long-summary: >
Allows to copy a managed image (or vm) to other regions.
Keep in mind that it requires the source disk to be available.
"""


def load_params(_):
with ParametersContext('image copy') as c:
c.register('source_resource_group_name', '--source-resource-group', help='Name of the resource group of the source resource')
c.register('source_object_name', '--source-object-name', help='The name of the image or vm resource')
c.register('target_location', '--target-location', nargs='+', help='Space separated location list to create the image in (use location short codes like westeurope etc.)')
c.register('source_resource_group_name', '--source-resource-group',
help='Name of the resource group of the source resource')
c.register('source_object_name', '--source-object-name',
help='The name of the image or vm resource')
c.register('target_location', '--target-location', nargs='+',
help='Space separated location list to create the image in (e.g. westeurope etc.)')
c.register('source_type', '--source-type', default='image', choices=['image', 'vm'], help='image or vm')
c.register('target_resource_group_name', '--target-resource-group', help='Name of the resource group to create images in')
c.register('parallel_degree', '--parallel-degree', type=int, default=-1, help='Number of parallel copy operations')
c.register('cleanup', '--cleanup', action='store_true', default=False, \
help='Include this switch to delete temporary resources upon completion')
c.register('target_resource_group_name', '--target-resource-group',
help='Name of the resource group to create images in')
c.register('parallel_degree', '--parallel-degree', type=int, default=-1,
help='Number of parallel copy operations')
c.register('cleanup', '--cleanup', action='store_true', default=False,
help='Include this switch to delete temporary resources upon completion')


def load_commands():
from azure.cli.core.commands import cli_command
Expand Down
6 changes: 6 additions & 0 deletions src/image-copy/azext_imagecopy/cli_utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

import sys
import json

Expand Down Expand Up @@ -29,6 +34,7 @@ def run_cli_command(cmd, return_as_json=False):
logger.error('command ended with an error: %s', cmd)
raise


def prepare_cli_command(cmd, output_as_json=True):
full_cmd = [sys.executable, '-m', 'azure.cli'] + cmd

Expand Down
Loading