Skip to content

Commit 44d3b57

Browse files
authored
Merge pull request #14 from sommersoft/cli_args
Updates: Command Line Arguments, am vs apply choice, New pylintrc patchfile, README updates
2 parents d2c6ffd + e290ed0 commit 44d3b57

4 files changed

+165
-58
lines changed

README.rst

+30-8
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,34 @@ To apply a patch to all CircuitPython libraries (only guaranteed for files share
117117
among all libraries, such as those included in the cookiecutter (e.g. README.rst,
118118
.travis.yml, etc), do the following:
119119

120-
1. Apply your update(s) to any library as normal, using ``git.commit``.
120+
1. Apply your update(s) to any library as normal, using ``git commit``. It is recommended to
121+
give a short, detailed description of the patch. This description will be used by the next
122+
step for both the name of the patchfile and the subsequent patch messages.
123+
121124
2. Create a patch file using `git format-patch <https://git-scm.com/docs/git-format-patch>`_.
122125
There are many techniques to using `git format-patch`; choose the one that makes
123-
sense for your updates. ``--signoff`` is not necessary; adabot will force a
124-
``--signoff`` when she uses ``git am``.
125-
3. Place the patch file into the ``adabot/patches`` directory, and ``git commit`` with a
126-
description of the patch(es).
127-
4. Push the update to the adabot repository.
128-
5. Run the patch update script.
126+
sense for your updates. As a general usage example, ``format-patch -n`` will create patches
127+
for ``n`` number of commits starting with the most recent:
128+
129+
.. code-block:: shell
130+
131+
# creates a patch file based on the last commit
132+
git format-patch -1
133+
134+
# creates patch files based on the last 5 commits
135+
git format-patch -5
136+
137+
# creates a patch file with zero lines of context (to eliminate any unique
138+
# text that will cause the patch to not be applicable). must use
139+
# 'git apply --unidiff-zero' flag to apply the patch.
140+
git format-patch -1 -U0
141+
142+
3. Place the new patch file into the ``adabot/patches`` directory on a fork of the
143+
adafruit/adabot repository, and ``git commit`` with a description of the patch(es).
144+
145+
4. Submit a Pull Request (PR) to the adafruit/adabot repository from the updated fork.
146+
147+
5. Run the patch update script after the PR has been merged.
129148

130149

131150
To run the patch update script you must be inside this cloned adabot directory and
@@ -135,9 +154,12 @@ run the following command:
135154
136155
# note: ensure the local clone is current with the github repo that contains the patch(es)
137156
# by using git pull before running the script.
138-
139157
python3 -m adabot.circuitpython_library_patches
140158
159+
# The 'circuitpython_library_patches' script accepts command line arguments. Use
160+
# the help argument to display usage.
161+
python3 -m adabot.circuitpython_library_patches -h
162+
141163
Contributing
142164
============
143165

adabot/circuitpython_library_patches.py

+110-23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import json
22
import requests
33
import os
4+
import sys
5+
import argparse
46
import shutil
57
import sh
68
from sh.contrib import git
@@ -15,6 +17,25 @@
1517
apply_errors = []
1618
stats = []
1719

20+
"""
21+
Setup the command line argument parsing object.
22+
"""
23+
cli_parser = argparse.ArgumentParser(description="Apply patches to any common file(s) in"
24+
" all Adafruit CircuitPython Libraries.")
25+
cli_parser.add_argument("-l", "--list", help="Lists the available patches to run.",
26+
action='store_true')
27+
cli_parser.add_argument("-p", help="Runs only the single patch referenced.",
28+
metavar="<PATCH FILENAME>", dest="patch")
29+
cli_parser.add_argument("-f", help="Adds the referenced FLAGS to the git.am call."
30+
" Only available when using '-p'. Enclose flags in brackets '[]'."
31+
" Multiple flags can be passed. NOTE: '--signoff' is already used "
32+
" used by default, and will be ignored. EXAMPLE: -f [-C0] -f [-s]",
33+
metavar="FLAGS", action="append", dest="flags", type=str)
34+
cli_parser.add_argument("--use-apply", help="Forces use of 'git apply' instead of 'git am'."
35+
" This is necessary when needing to use 'apply' flags not available"
36+
" to 'am' (e.g. '--unidiff-zero'). Only available when using '-p'.",
37+
action="store_true", dest="use_apply")
38+
1839
def get_repo_list():
1940
""" Uses adabot.circuitpython_libraries module to get a list of
2041
CircuitPython repositories. Filters the list down to adafruit
@@ -36,48 +57,77 @@ def get_patches():
3657
"""
3758
return_list = []
3859
contents = requests.get("https://api.github.com/repos/adafruit/adabot/contents/patches")
39-
4060
if contents.ok:
4161
for patch in contents.json():
4262
patch_name = patch["name"]
4363
return_list.append(patch_name)
4464

4565
return return_list
4666

47-
def apply_patch(repo_directory, patch_filepath, repo, patch):
67+
def apply_patch(repo_directory, patch_filepath, repo, patch, flags, use_apply):
4868
""" Apply the `patch` in `patch_filepath` to the `repo` in
49-
`repo_directory` using git am. --signoff will sign the commit
69+
`repo_directory` using git am or git apply. The commit
5070
with the user running the script (adabot if credentials are set
5171
for that).
72+
73+
When `use_apply` is true, the `--apply` flag is automatically added
74+
to ensure that any passed flags that turn off apply (e.g. `--check`)
75+
are overridden.
5276
"""
5377
if not os.getcwd() == repo_directory:
5478
os.chdir(repo_directory)
5579

56-
try:
57-
git.am("--signoff", patch_filepath)
58-
except sh.ErrorReturnCode as Err:
59-
apply_errors.append(dict(repo_name=repo,
60-
patch_name=patch, error=Err.stderr))
61-
return False
80+
if not use_apply:
81+
try:
82+
git.am(flags, patch_filepath)
83+
except sh.ErrorReturnCode as Err:
84+
apply_errors.append(dict(repo_name=repo,
85+
patch_name=patch, error=Err.stderr))
86+
return False
87+
else:
88+
apply_flags = ["--apply"]
89+
for flag in flags:
90+
if not flag == "--signoff":
91+
apply_flags.append(flag)
92+
try:
93+
git.apply(apply_flags, patch_filepath)
94+
except sh.ErrorReturnCode as Err:
95+
apply_errors.append(dict(repo_name=repo,
96+
patch_name=patch, error=Err.stderr))
97+
return False
98+
99+
with open(patch_filepath) as f:
100+
for line in f:
101+
if "[PATCH]" in line:
102+
message = '"' + line[(line.find("]") + 2):] + '"'
103+
break
104+
try:
105+
git.commit("-a", "-m", message)
106+
except sh.ErrorReturnCode as Err:
107+
apply_errors.append(dict(repo_name=repo,
108+
patch_name=patch, error=Err.stderr))
109+
return False
62110

63111
try:
64112
git.push()
65113
except sh.ErrorReturnCode as Err:
66114
apply_errors.append(dict(repo_name=repo,
67115
patch_name=patch, error=Err.stderr))
68116
return False
69-
70117
return True
71118

72-
def check_patches(repo):
119+
def check_patches(repo, patches, flags, use_apply):
73120
""" Gather a list of patches from the `adabot/patches` directory
74121
on the adabot repo. Clone the `repo` and run git apply --check
75122
to test wether it requires any of the gathered patches.
123+
124+
When `use_apply` is true, any flags except `--apply` are passed
125+
through to the check call. This ensures that the check call is
126+
representative of the actual apply call.
76127
"""
77128
applied = 0
78129
skipped = 0
79130
failed = 0
80-
patches = get_patches()
81131

82132
repo_directory = lib_directory + repo["name"]
83133

@@ -100,19 +150,31 @@ def check_patches(repo):
100150
patch_filepath = patch_directory + patch
101151

102152
try:
103-
git.apply("--check", patch_filepath)
153+
check_flags = ["--check"]
154+
if use_apply:
155+
for flag in flags:
156+
if not flag in ("--apply", "--signoff"):
157+
check_flags.append(flag)
158+
git.apply(check_flags, patch_filepath)
104159
run_apply = True
105-
except sh.ErrorReturnCode_1:
160+
except sh.ErrorReturnCode_1 as Err:
106161
run_apply = False
107-
skipped += 1
108-
except sh.ErrorReturnCode_128 as Err:
162+
if not b"error" in Err.stderr:
163+
skipped += 1
164+
else:
165+
failed += 1
166+
check_errors.append(dict(repo_name=repo["name"],
167+
patch_name=patch, error=Err.stderr))
168+
169+
except sh.ErrorReturnCode as Err:
109170
run_apply = False
110171
failed += 1
111172
check_errors.append(dict(repo_name=repo["name"],
112173
patch_name=patch, error=Err.stderr))
113174

114175
if run_apply:
115-
result = apply_patch(repo_directory, patch_filepath, repo["name"], patch)
176+
result = apply_patch(repo_directory, patch_filepath, repo["name"],
177+
patch, flags, use_apply)
116178
if result:
117179
applied += 1
118180
else:
@@ -121,6 +183,31 @@ def check_patches(repo):
121183
return [applied, skipped, failed]
122184

123185
if __name__ == "__main__":
186+
187+
run_patches = get_patches()
188+
flags = ["--signoff"]
189+
190+
cli_args = cli_parser.parse_args()
191+
if cli_args.list:
192+
print("Available Patches:", run_patches)
193+
sys.exit()
194+
if cli_args.patch:
195+
if not cli_args.patch in run_patches:
196+
raise ValueError("'{}' is not an available patchfile.".format(cli_args.patch))
197+
run_patches = [cli_args.patch]
198+
if not cli_args.flags == None:
199+
if not cli_args.patch:
200+
raise RuntimeError("Must be used with a single patch. See help (-h) for usage.")
201+
if "[-i]" in cli_args.flags:
202+
raise ValueError("Interactive Mode flag not allowed.")
203+
for flag in cli_args.flags:
204+
if not flag == "[--signoff]":
205+
flags.append(flag.strip("[]"))
206+
if cli_args.use_apply:
207+
if not cli_args.patch:
208+
raise RuntimeError("Must be used with a single patch. See help (-h) for usage.")
209+
use_apply = cli_args.use_apply
210+
124211
print(".... Beginning Patch Updates ....")
125212
print(".... Working directory:", working_directory)
126213
print(".... Library directory:", lib_directory)
@@ -136,23 +223,23 @@ def check_patches(repo):
136223
for lib in libs:
137224
shutil.rmtree(lib_directory + lib)
138225
except FileNotFoundError:
139-
pass
226+
pass
140227

141228
repos = get_repo_list()
142229
print(".... Running Patch Checks On", len(repos), "Repos ....")
143230

144231
for repo in repos:
145-
results = check_patches(repo)
146-
for k in range(len(stats)):
147-
stats[k] += results[k]
232+
results = check_patches(repo, run_patches, flags, use_apply)
233+
for k in range(3):
234+
stats[k] += results[k]
148235

149236
print(".... Patch Updates Completed ....")
150237
print(".... Patches Applied:", stats[0])
151238
print(".... Patches Skipped:", stats[1])
152239
print(".... Patches Failed:", stats[2], "\n")
153240
print(".... Patch Check Failure Report ....")
154241
if len(check_errors) > 0:
155-
for error, _ in check_errors:
242+
for error in check_errors:
156243
print(">>", error)
157244
else:
158245
print("No Failures")
@@ -163,4 +250,4 @@ def check_patches(repo):
163250
print(">>", error)
164251
else:
165252
print("No Failures")
166-
253+

patches/0001-force-Travis-to-use-pylint-version-1.9.2-to-avoid-ne.patch

-27
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
From b5290f3869789fbf8a57004003c94131cdad82c5 Mon Sep 17 00:00:00 2001
2+
From: sommersoft <[email protected]>
3+
Date: Tue, 14 Aug 2018 20:01:03 -0500
4+
Subject: [PATCH] ignore the board module imports in .pylintrc
5+
6+
---
7+
.pylintrc | 2 +-
8+
1 file changed, 1 insertion(+), 1 deletion(-)
9+
10+
diff --git a/.pylintrc b/.pylintrc
11+
index 946d694..cb8d23d 100644
12+
--- a/.pylintrc
13+
+++ b/.pylintrc
14+
@@ -155,7 +155,7 @@ ignored-classes=optparse.Values,thread._local,_thread._local
15+
# (useful for modules/projects where namespaces are manipulated during runtime
16+
# and thus existing member attributes cannot be deduced by static analysis. It
17+
# supports qualified module names, as well as Unix pattern matching.
18+
-ignored-modules=
19+
+ignored-modules=board
20+
21+
# Show a hint with possible names when a member name was not found. The aspect
22+
# of finding the hint is based on edit distance.
23+
--
24+
2.15.1.windows.2
25+

0 commit comments

Comments
 (0)