Merge juju-lint:update-snap into juju-lint:master

Proposed by Gabriel Cocenza
Status: Merged
Approved by: Eric Chen
Approved revision: 57cd0951d0e5e453d304291142a69dabff5c560e
Merged at revision: 14ad2b7c55802329a6be651caa8a79773f0b3c2d
Proposed branch: juju-lint:update-snap
Merge into: juju-lint:master
Diff against target: 808 lines (+138/-279)
13 files modified
Makefile (+4/-1)
dev/null (+0/-199)
jujulint/check_spaces.py (+12/-7)
jujulint/cli.py (+7/-5)
jujulint/cloud.py (+7/-5)
jujulint/config.py (+2/-1)
jujulint/lint.py (+41/-27)
jujulint/logging.py (+2/-1)
snap/snapcraft.yaml (+3/-3)
tests/conftest.py (+3/-2)
tests/requirements.txt (+3/-1)
tests/test_jujulint.py (+28/-18)
tox.ini (+26/-9)
Reviewer Review Type Date Requested Status
Eric Chen Approve
Martin Kalcok (community) Approve
Review via email: mp+424672@code.launchpad.net

Commit message

update the snap

- added black to format the source code of the project and
  applied it into the code.
- added isort to order imports and applied it into the code.
- changed python version from 3.6(deprecated) to 3.8.
- changed base to core20.
- added /snap/bin to $PATH
- removed Pipfile, Pipfile.lock and .python-version that is not
  been used anymore.

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
Martin Kalcok (martin-kalcok) wrote :

I'm very much a fan of adding `black` and enforcing stricter rules. I have just few notes:

* I think that adding "black --check <targets>" to "lint" tox environment will help with CI integration
* Another useful tool is `isort` that manages format/ordering of python imports

If you want to check out example of other project that has these tools set up, you can check juju-verify [1].

Regarding the changes to snapcraft.yaml: Does juju-lint always depend on juju (snap)? If yes, maybe we should add it as `stage-snaps` [2]?

---
[1] https://github.com/canonical/juju-verify/blob/master/tox.ini
[2] https://snapcraft.io/docs/build-and-staging-dependencies#heading--package-names

review: Needs Fixing
Revision history for this message
Garrett Neugent (thogarre) wrote (last edit ):

This is not a blocker in anyway, but one other tool that is worth consideration is pre-commit. It can help enforce linting, unit tests, and other checks before engineers submit a new MR. Juju-backup-all has this implemented [0] if you think it's worth adding here. (Note that if you do consider adding this, you'll likely need to use v22.3.0 of black, not 21.8b0 as currently in juju-backup-all's config [1]).

[0] https://git.launchpad.net/juju-backup-all/tree/.pre-commit-config.yaml
[1] https://code.launchpad.net/~thogarre/juju-backup-all/+git/juju-backup-all/+merge/424590

Revision history for this message
Gabriel Cocenza (gabrielcocenza) wrote :

Hi Martin. Thanks for your review. I've added isort and applied it in the project.

Regarding adding juju snap into `stage-snaps` I guess it's not necessary. I don't see a huge gain of having the binaries of juju snap into juju-lint. Maybe we should consider using pythonlibjuju instead of making subprocess calls to deal with juju?

Revision history for this message
Gabriel Cocenza (gabrielcocenza) wrote :

Thanks for this suggestion Garret. I think it will be a very good thing for the project. I've added a bug[0] to add pre-commit in a future MR.

[0] https://bugs.launchpad.net/juju-lint/+bug/1978739

Revision history for this message
Robert Gildein (rgildein) wrote :

I briefly look at this and how three questions/suggestion:
- How do we use Pipenv here? Do you want to create a requirements.txt file? If
  so, what do you say to add `pipenv lock -> requirements.txt` to tox.ini to
  generate requirements from Pipfile? There is also simply way to check
  requirements in Pipfile.lock and requirements.txt
  `pipenv lock -r | diff requirements.txt -`
- If the above applies, we can use pipenv --dev to have testing requirements
  there.
- You can add `--cov-fail-under <current-percentage>` to ensure that each new
  function will need to have unittests.

Revision history for this message
Martin Kalcok (martin-kalcok) wrote :

Thanks for including my suggestions. Just two more nitpicks

* you have misaligned indentations in the pytest command in `unit` tox environment
* shouldn't the isort line length config (in tox.ini) match the line length of `black` which is 88?

review: Needs Fixing
Revision history for this message
Gabriel Cocenza (gabrielcocenza) wrote :

Thanks Martin. The problem in tox was mixed spaces and tabs. Regarding isort, you are right. I added the black profile [0] that will do the job.

Regarding the Pipfile(s), it looks like they are not been used (maybe just for those that use pipenv, so I removed then. The MR that added then it's this one [1].

[0] https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#profile
[1] https://code.launchpad.net/~ec0/juju-lint/+git/juju-lint/+merge/384517

Revision history for this message
Martin Kalcok (martin-kalcok) wrote :

LGTM, thanks

review: Approve
Revision history for this message
Eric Chen (eric-chen) wrote :

LGTM, please go ahead.

review: Approve
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision 14ad2b7c55802329a6be651caa8a79773f0b3c2d

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/.python-version b/.python-version
0deleted file mode 1006440deleted file mode 100644
index a08ffae..0000000
--- a/.python-version
+++ /dev/null
@@ -1 +0,0 @@
13.8.2
diff --git a/Makefile b/Makefile
index 70a2762..85be332 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
1lint:1lint:
2 tox -e py3-lint2 tox -e lintverbose
33
4dch:4dch:
5 if ! which gbp > /dev/null; then sudo apt-get install -y git-buildpackage; fi5 if ! which gbp > /dev/null; then sudo apt-get install -y git-buildpackage; fi
@@ -11,6 +11,9 @@ deb-src:
11test:11test:
12 tox -e unit12 tox -e unit
1313
14format-code:
15 tox -e format-code
16
14build:17build:
15 snapcraft --use-lxd --debug18 snapcraft --use-lxd --debug
1619
diff --git a/Pipfile b/Pipfile
17deleted file mode 10064420deleted file mode 100644
index d2acf80..0000000
--- a/Pipfile
+++ /dev/null
@@ -1,17 +0,0 @@
1[[source]]
2url = "https://pypi.python.org/simple"
3verify_ssl = true
4name = "pypi"
5
6[packages]
7attrs = ">=18.1.0"
8colorlog = ">=4.1.0"
9confuse = ">=1.1.0"
10fabric2 = ">=2.5.0"
11setuptools = ">=46.1.3"
12PyYAML = ">=5.3.1"
13
14[dev-packages]
15
16[requires]
17python_version = "3.6"
diff --git a/Pipfile.lock b/Pipfile.lock
18deleted file mode 1006440deleted file mode 100644
index c43f7b6..0000000
--- a/Pipfile.lock
+++ /dev/null
@@ -1,199 +0,0 @@
1{
2 "_meta": {
3 "hash": {
4 "sha256": "e1534fcda4a023c68f70df4e53b34a9bafcd9ec48c4f8d93e20dda25b3c43cd4"
5 },
6 "pipfile-spec": 6,
7 "requires": {
8 "python_version": "3.6"
9 },
10 "sources": [
11 {
12 "name": "pypi",
13 "url": "https://pypi.python.org/simple",
14 "verify_ssl": true
15 }
16 ]
17 },
18 "default": {
19 "attrs": {
20 "hashes": [
21 "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
22 "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
23 ],
24 "index": "pypi",
25 "version": "==19.3.0"
26 },
27 "bcrypt": {
28 "hashes": [
29 "sha256:0258f143f3de96b7c14f762c770f5fc56ccd72f8a1857a451c1cd9a655d9ac89",
30 "sha256:0b0069c752ec14172c5f78208f1863d7ad6755a6fae6fe76ec2c80d13be41e42",
31 "sha256:19a4b72a6ae5bb467fea018b825f0a7d917789bcfe893e53f15c92805d187294",
32 "sha256:5432dd7b34107ae8ed6c10a71b4397f1c853bd39a4d6ffa7e35f40584cffd161",
33 "sha256:6305557019906466fc42dbc53b46da004e72fd7a551c044a827e572c82191752",
34 "sha256:69361315039878c0680be456640f8705d76cb4a3a3fe1e057e0f261b74be4b31",
35 "sha256:6fe49a60b25b584e2f4ef175b29d3a83ba63b3a4df1b4c0605b826668d1b6be5",
36 "sha256:74a015102e877d0ccd02cdeaa18b32aa7273746914a6c5d0456dd442cb65b99c",
37 "sha256:763669a367869786bb4c8fcf731f4175775a5b43f070f50f46f0b59da45375d0",
38 "sha256:8b10acde4e1919d6015e1df86d4c217d3b5b01bb7744c36113ea43d529e1c3de",
39 "sha256:9fe92406c857409b70a38729dbdf6578caf9228de0aef5bc44f859ffe971a39e",
40 "sha256:a190f2a5dbbdbff4b74e3103cef44344bc30e61255beb27310e2aec407766052",
41 "sha256:a595c12c618119255c90deb4b046e1ca3bcfad64667c43d1166f2b04bc72db09",
42 "sha256:c9457fa5c121e94a58d6505cadca8bed1c64444b83b3204928a866ca2e599105",
43 "sha256:cb93f6b2ab0f6853550b74e051d297c27a638719753eb9ff66d1e4072be67133",
44 "sha256:ce4e4f0deb51d38b1611a27f330426154f2980e66582dc5f438aad38b5f24fc1",
45 "sha256:d7bdc26475679dd073ba0ed2766445bb5b20ca4793ca0db32b399dccc6bc84b7",
46 "sha256:ff032765bb8716d9387fd5376d987a937254b0619eff0972779515b5c98820bc"
47 ],
48 "version": "==3.1.7"
49 },
50 "cffi": {
51 "hashes": [
52 "sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff",
53 "sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b",
54 "sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac",
55 "sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0",
56 "sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384",
57 "sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26",
58 "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6",
59 "sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b",
60 "sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e",
61 "sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd",
62 "sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2",
63 "sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66",
64 "sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc",
65 "sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8",
66 "sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55",
67 "sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4",
68 "sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5",
69 "sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d",
70 "sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78",
71 "sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa",
72 "sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793",
73 "sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f",
74 "sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a",
75 "sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f",
76 "sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30",
77 "sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f",
78 "sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3",
79 "sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c"
80 ],
81 "version": "==1.14.0"
82 },
83 "colorlog": {
84 "hashes": [
85 "sha256:30aaef5ab2a1873dec5da38fd6ba568fa761c9fa10b40241027fa3edea47f3d2",
86 "sha256:732c191ebbe9a353ec160d043d02c64ddef9028de8caae4cfa8bd49b6afed53e"
87 ],
88 "index": "pypi",
89 "version": "==4.1.0"
90 },
91 "confuse": {
92 "hashes": [
93 "sha256:adc1979ea6f4c0dd3d6fe06020c189843a649082ab8f6fb54db16f4ac5e5e1da"
94 ],
95 "index": "pypi",
96 "version": "==1.1.0"
97 },
98 "cryptography": {
99 "hashes": [
100 "sha256:091d31c42f444c6f519485ed528d8b451d1a0c7bf30e8ca583a0cac44b8a0df6",
101 "sha256:18452582a3c85b96014b45686af264563e3e5d99d226589f057ace56196ec78b",
102 "sha256:1dfa985f62b137909496e7fc182dac687206d8d089dd03eaeb28ae16eec8e7d5",
103 "sha256:1e4014639d3d73fbc5ceff206049c5a9a849cefd106a49fa7aaaa25cc0ce35cf",
104 "sha256:22e91636a51170df0ae4dcbd250d318fd28c9f491c4e50b625a49964b24fe46e",
105 "sha256:3b3eba865ea2754738616f87292b7f29448aec342a7c720956f8083d252bf28b",
106 "sha256:651448cd2e3a6bc2bb76c3663785133c40d5e1a8c1a9c5429e4354201c6024ae",
107 "sha256:726086c17f94747cedbee6efa77e99ae170caebeb1116353c6cf0ab67ea6829b",
108 "sha256:844a76bc04472e5135b909da6aed84360f522ff5dfa47f93e3dd2a0b84a89fa0",
109 "sha256:88c881dd5a147e08d1bdcf2315c04972381d026cdb803325c03fe2b4a8ed858b",
110 "sha256:96c080ae7118c10fcbe6229ab43eb8b090fccd31a09ef55f83f690d1ef619a1d",
111 "sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229",
112 "sha256:bb1f0281887d89617b4c68e8db9a2c42b9efebf2702a3c5bf70599421a8623e3",
113 "sha256:c447cf087cf2dbddc1add6987bbe2f767ed5317adb2d08af940db517dd704365",
114 "sha256:c4fd17d92e9d55b84707f4fd09992081ba872d1a0c610c109c18e062e06a2e55",
115 "sha256:d0d5aeaedd29be304848f1c5059074a740fa9f6f26b84c5b63e8b29e73dfc270",
116 "sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e",
117 "sha256:e993468c859d084d5579e2ebee101de8f5a27ce8e2159959b6673b418fd8c785",
118 "sha256:f118a95c7480f5be0df8afeb9a11bd199aa20afab7a96bcf20409b411a3a85f0"
119 ],
120 "version": "==2.9.2"
121 },
122 "fabric2": {
123 "hashes": [
124 "sha256:29edd7848420df589a49743394a0ae6874ccb6a9fe6413c0076d42cc290dcad6",
125 "sha256:8838d9641fd4e95bfc2568aa16fc683a600de860ac52a1dc9675a4db3c6cef7c"
126 ],
127 "index": "pypi",
128 "version": "==2.5.0"
129 },
130 "invoke": {
131 "hashes": [
132 "sha256:87b3ef9d72a1667e104f89b159eaf8a514dbf2f3576885b2bbdefe74c3fb2132",
133 "sha256:93e12876d88130c8e0d7fd6618dd5387d6b36da55ad541481dfa5e001656f134",
134 "sha256:de3f23bfe669e3db1085789fd859eb8ca8e0c5d9c20811e2407fa042e8a5e15d"
135 ],
136 "version": "==1.4.1"
137 },
138 "paramiko": {
139 "hashes": [
140 "sha256:920492895db8013f6cc0179293147f830b8c7b21fdfc839b6bad760c27459d9f",
141 "sha256:9c980875fa4d2cb751604664e9a2d0f69096643f5be4db1b99599fe114a97b2f"
142 ],
143 "version": "==2.7.1"
144 },
145 "pycparser": {
146 "hashes": [
147 "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
148 "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
149 ],
150 "version": "==2.20"
151 },
152 "pynacl": {
153 "hashes": [
154 "sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4",
155 "sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4",
156 "sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574",
157 "sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d",
158 "sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25",
159 "sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f",
160 "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505",
161 "sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122",
162 "sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7",
163 "sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420",
164 "sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f",
165 "sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96",
166 "sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6",
167 "sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514",
168 "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff",
169 "sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80"
170 ],
171 "version": "==1.4.0"
172 },
173 "pyyaml": {
174 "hashes": [
175 "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97",
176 "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76",
177 "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2",
178 "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648",
179 "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf",
180 "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f",
181 "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2",
182 "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee",
183 "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d",
184 "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c",
185 "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"
186 ],
187 "index": "pypi",
188 "version": "==5.3.1"
189 },
190 "six": {
191 "hashes": [
192 "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
193 "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
194 ],
195 "version": "==1.15.0"
196 }
197 },
198 "develop": {}
199}
diff --git a/jujulint/check_spaces.py b/jujulint/check_spaces.py
index 47c0c63..17b8d8c 100644
--- a/jujulint/check_spaces.py
+++ b/jujulint/check_spaces.py
@@ -20,7 +20,9 @@ class Relation:
20 While Juju does define separate provider and requirer roles, we'll ignore20 While Juju does define separate provider and requirer roles, we'll ignore
21 those here.21 those here.
22 """22 """
23 return set([self.endpoint1, self.endpoint2]) == set([other.endpoint1, other.endpoint2])23 return set([self.endpoint1, self.endpoint2]) == set(
24 [other.endpoint1, other.endpoint2]
25 )
2426
25 @property27 @property
26 def endpoints(self):28 def endpoints(self):
@@ -45,7 +47,8 @@ class SpaceMismatch:
45 def __str__(self):47 def __str__(self):
46 """Stringify the object."""48 """Stringify the object."""
47 return "SpaceMismatch({} (space {}) != {} (space {}))".format(49 return "SpaceMismatch({} (space {}) != {} (space {}))".format(
48 self.endpoint1, self.space1, self.endpoint2, self.space2)50 self.endpoint1, self.space1, self.endpoint2, self.space2
51 )
4952
50 @property53 @property
51 def relation(self):54 def relation(self):
@@ -54,8 +57,8 @@ class SpaceMismatch:
5457
55 def get_charm_relation(self, app_to_charm_map):58 def get_charm_relation(self, app_to_charm_map):
56 """Return a relation object, mapping applications to charms."""59 """Return a relation object, mapping applications to charms."""
57 app1, endpoint1 = self.endpoint1.split(':')60 app1, endpoint1 = self.endpoint1.split(":")
58 app2, endpoint2 = self.endpoint2.split(':')61 app2, endpoint2 = self.endpoint2.split(":")
59 charm1 = app_to_charm_map[app1]62 charm1 = app_to_charm_map[app1]
60 charm2 = app_to_charm_map[app2]63 charm2 = app_to_charm_map[app2]
61 return Relation(":".join([charm1, endpoint1]), ":".join([charm2, endpoint2]))64 return Relation(":".join([charm1, endpoint1]), ":".join([charm2, endpoint2]))
@@ -92,7 +95,9 @@ def find_space_mismatches(parsed_yaml, debug=False):
92 space1 = get_relation_space(relation.endpoint1, app_spaces)95 space1 = get_relation_space(relation.endpoint1, app_spaces)
93 space2 = get_relation_space(relation.endpoint2, app_spaces)96 space2 = get_relation_space(relation.endpoint2, app_spaces)
94 if space1 != space2:97 if space1 != space2:
95 mismatch = SpaceMismatch(relation.endpoint1, space1, relation.endpoint2, space2)98 mismatch = SpaceMismatch(
99 relation.endpoint1, space1, relation.endpoint2, space2
100 )
96 mismatches.append(mismatch)101 mismatches.append(mismatch)
97102
98 if debug:103 if debug:
@@ -112,7 +117,7 @@ def get_application_spaces(application_list, parsed_yaml):
112 app_spaces = {}117 app_spaces = {}
113 for app in application_list:118 for app in application_list:
114 app_spaces[app] = {}119 app_spaces[app] = {}
115 bindings = parsed_yaml["applications"][app]['bindings']120 bindings = parsed_yaml["applications"][app]["bindings"]
116 for name, value in bindings.items():121 for name, value in bindings.items():
117 app_spaces[app][name] = value122 app_spaces[app][name] = value
118 return app_spaces123 return app_spaces
@@ -129,7 +134,7 @@ def get_application_relations(parsed_yaml):
129134
130def get_relation_space(endpoint, app_spaces):135def get_relation_space(endpoint, app_spaces):
131 """Get space for specified app and service."""136 """Get space for specified app and service."""
132 app, service = endpoint.split(':')137 app, service = endpoint.split(":")
133 if app in app_spaces:138 if app in app_spaces:
134 if service not in app_spaces[app]:139 if service not in app_spaces[app]:
135 return app_spaces[app][""]140 return app_spaces[app][""]
diff --git a/jujulint/cli.py b/jujulint/cli.py
index af5190a..2d76832 100755
--- a/jujulint/cli.py
+++ b/jujulint/cli.py
@@ -17,15 +17,17 @@
17# You should have received a copy of the GNU General Public License17# You should have received a copy of the GNU General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19"""Main entrypoint for the juju-lint CLI."""19"""Main entrypoint for the juju-lint CLI."""
20from jujulint.config import Config
21from jujulint.lint import Linter
22from jujulint.logging import Logger
23from jujulint.openstack import OpenStack
24import logging20import logging
25import os.path21import os.path
22import sys
23
26import pkg_resources24import pkg_resources
27import yaml25import yaml
28import sys26
27from jujulint.config import Config
28from jujulint.lint import Linter
29from jujulint.logging import Logger
30from jujulint.openstack import OpenStack
2931
3032
31class Cli:33class Cli:
diff --git a/jujulint/cloud.py b/jujulint/cloud.py
index b3cde24..3382e3b 100644
--- a/jujulint/cloud.py
+++ b/jujulint/cloud.py
@@ -33,13 +33,15 @@ Todo:
33 * Add function to run command on a unit, via fabric and jump host if configured33 * Add function to run command on a unit, via fabric and jump host if configured
3434
35"""35"""
36from fabric2 import Connection, Config
37from paramiko.ssh_exception import SSHException
38from jujulint.logging import Logger
39from jujulint.lint import Linter
40from subprocess import check_output
41import socket36import socket
37from subprocess import check_output
38
42import yaml39import yaml
40from fabric2 import Config, Connection
41from paramiko.ssh_exception import SSHException
42
43from jujulint.lint import Linter
44from jujulint.logging import Logger
4345
4446
45class Cloud:47class Cloud:
diff --git a/jujulint/config.py b/jujulint/config.py
index 1173187..d87677a 100644
--- a/jujulint/config.py
+++ b/jujulint/config.py
@@ -18,9 +18,10 @@
18# along with this program. If not, see <http://www.gnu.org/licenses/>.18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19"""Config handling routines."""19"""Config handling routines."""
2020
21from confuse import Configuration
22from argparse import ArgumentParser21from argparse import ArgumentParser
2322
23from confuse import Configuration
24
2425
25class Config(Configuration):26class Config(Configuration):
26 """Helper class for holding parsed config, extending confuse's BaseConfiguraion class."""27 """Helper class for holding parsed config, extending confuse's BaseConfiguraion class."""
diff --git a/jujulint/lint.py b/jujulint/lint.py
index 404e8b8..e908b7a 100755
--- a/jujulint/lint.py
+++ b/jujulint/lint.py
@@ -19,24 +19,23 @@
19# along with this program. If not, see <http://www.gnu.org/licenses/>.19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20"""Lint operations and rule processing engine."""20"""Lint operations and rule processing engine."""
21import collections21import collections
22from datetime import datetime, timezone
23import json22import json
24import logging23import logging
25import os.path24import os.path
26import pprint25import pprint
27import re26import re
28import traceback27import traceback
28from datetime import datetime, timezone
2929
30import yaml
31
32from attr import attrs, attrib
33import attr30import attr
34import dateutil.parser31import dateutil.parser
32import yaml
33from attr import attrib, attrs
35from dateutil import relativedelta34from dateutil import relativedelta
3635
37import jujulint.util as utils36import jujulint.util as utils
37from jujulint.check_spaces import Relation, find_space_mismatches
38from jujulint.logging import Logger38from jujulint.logging import Logger
39from jujulint.check_spaces import find_space_mismatches, Relation
4039
41VALID_CONFIG_CHECKS = ("isset", "eq", "neq", "gte")40VALID_CONFIG_CHECKS = ("isset", "eq", "neq", "gte")
4241
@@ -195,7 +194,7 @@ class Linter:
195 if val[-1].lower() == val[-1]:194 if val[-1].lower() == val[-1]:
196 quotient = 1000195 quotient = 1000
197196
198 conv = {"g": quotient ** 3, "m": quotient ** 2, "k": quotient}197 conv = {"g": quotient**3, "m": quotient**2, "k": quotient}
199198
200 return _int * conv[val[-1].lower()]199 return _int * conv[val[-1].lower()]
201200
@@ -684,21 +683,39 @@ class Linter:
684 enforce_endpoints = space_checks.get("enforce endpoints", [])683 enforce_endpoints = space_checks.get("enforce endpoints", [])
685 enforce_relations = [684 enforce_relations = [
686 Relation(*relation)685 Relation(*relation)
687 for relation in space_checks.get("enforce relations", [])]686 for relation in space_checks.get("enforce relations", [])
687 ]
688 ignore_endpoints = space_checks.get("ignore endpoints", [])688 ignore_endpoints = space_checks.get("ignore endpoints", [])
689 ignore_relations = [689 ignore_relations = [
690 Relation(*relation)690 Relation(*relation) for relation in space_checks.get("ignore relations", [])
691 for relation in space_checks.get("ignore relations", [])]691 ]
692692
693 mismatches = find_space_mismatches(parsed_yaml)693 mismatches = find_space_mismatches(parsed_yaml)
694 for mismatch in mismatches:694 for mismatch in mismatches:
695 try:695 try:
696 self._handle_space_mismatch(mismatch, enforce_endpoints, enforce_relations, ignore_endpoints, ignore_relations)696 self._handle_space_mismatch(
697 mismatch,
698 enforce_endpoints,
699 enforce_relations,
700 ignore_endpoints,
701 ignore_relations,
702 )
697 except Exception:703 except Exception:
698 # FOR NOW: super quick and dirty704 # FOR NOW: super quick and dirty
699 print('Exception caught during space check; please check space by hand. {}'.format(traceback.format_exc()))705 print(
706 "Exception caught during space check; please check space by hand. {}".format(
707 traceback.format_exc()
708 )
709 )
700710
701 def _handle_space_mismatch(self, mismatch, enforce_endpoints, enforce_relations, ignore_endpoints, ignore_relations):711 def _handle_space_mismatch(
712 self,
713 mismatch,
714 enforce_endpoints,
715 enforce_relations,
716 ignore_endpoints,
717 ignore_relations,
718 ):
702 # By default: treat mismatches as warnings.719 # By default: treat mismatches as warnings.
703 # If we have a matching enforcement rule, treat as an error.720 # If we have a matching enforcement rule, treat as an error.
704 # If we have a matching ignore rule, do not warn.721 # If we have a matching ignore rule, do not warn.
@@ -722,12 +739,14 @@ class Linter:
722739
723 message = "Space binding mismatch: {}".format(mismatch)740 message = "Space binding mismatch: {}".format(mismatch)
724 if error:741 if error:
725 self.handle_error({742 self.handle_error(
726 "id": "space-binding-mismatch",743 {
727 "tags": ["mismatch", "space", "binding"],744 "id": "space-binding-mismatch",
728 "description": "Unhandled space binding mismatch",745 "tags": ["mismatch", "space", "binding"],
729 "message": message,746 "description": "Unhandled space binding mismatch",
730 })747 "message": message,
748 }
749 )
731 elif warning:750 elif warning:
732 # DEFAULT: not a critical error, so just warn751 # DEFAULT: not a critical error, so just warn
733 self._log_with_header(message, level=logging.WARN)752 self._log_with_header(message, level=logging.WARN)
@@ -1041,7 +1060,7 @@ class Linter:
1041 offer_overlay = True1060 offer_overlay = True
1042 if parsed_yaml is None or not offer_overlay:1061 if parsed_yaml is None or not offer_overlay:
1043 parsed_yaml = doc1062 parsed_yaml = doc
1044 return(parsed_yaml)1063 return parsed_yaml
10451064
1046 def lint_yaml_string(self, yaml_string):1065 def lint_yaml_string(self, yaml_string):
1047 """Lint provided YAML string."""1066 """Lint provided YAML string."""
@@ -1086,15 +1105,10 @@ class Linter:
10861105
1087 if "relations" in parsed_yaml:1106 if "relations" in parsed_yaml:
1088 # "bindings" *should* be in exported bundles, *unless* no custom bindings exist,1107 # "bindings" *should* be in exported bundles, *unless* no custom bindings exist,
1089 # in which case "juju export-bundle" omits them.1108 # in which case "juju export-bundle" omits them. See LP#1949883.
1090 if "bindings" in list(parsed_yaml[applications].values())[0]:1109 if "bindings" in list(parsed_yaml[applications].values())[0]:
1091 #try:1110 self.check_spaces(parsed_yaml)
1092 self.check_spaces(parsed_yaml)1111
1093 #except Exception as e:
1094 # self._log_with_header(
1095 # "Encountered error while checking spaces: {}".format(e),
1096 # level=logging.WARN
1097 # )
1098 else:1112 else:
1099 self._log_with_header(1113 self._log_with_header(
1100 "Relations detected but custom bindings not found; "1114 "Relations detected but custom bindings not found; "
diff --git a/jujulint/logging.py b/jujulint/logging.py
index e3c42f8..1d1586d 100644
--- a/jujulint/logging.py
+++ b/jujulint/logging.py
@@ -17,10 +17,11 @@
17# You should have received a copy of the GNU General Public License17# You should have received a copy of the GNU General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19"""Logging helper functions."""19"""Logging helper functions."""
20import colorlog
21import logging20import logging
22import sys21import sys
2322
23import colorlog
24
2425
25class Logger:26class Logger:
26 """Helper class for logging."""27 """Helper class for logging."""
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 9f84064..555e9af 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -1,6 +1,6 @@
1---1---
2name: juju-lint2name: juju-lint
3base: core183base: core20
4summary: Linter for Juju models to compare deployments with configurable policy4summary: Linter for Juju models to compare deployments with configurable policy
5adopt-info: juju-lint5adopt-info: juju-lint
6description: |6description: |
@@ -13,8 +13,8 @@ apps:
13 juju-lint:13 juju-lint:
14 command: bin/juju-lint14 command: bin/juju-lint
15 environment:15 environment:
16 PATH: "/snap/juju-lint/current/bin:/snap/juju-lint/current/usr/bin:/bin:/usr/bin:"16 PATH: "/snap/juju-lint/current/bin:/snap/juju-lint/current/usr/bin:/bin:/usr/bin:/snap/bin"
17 PYTHONPATH: $SNAP/usr/lib/python3.6/site-packages:$SNAP/usr/lib/python3.6/dist-packages:$PYTHONPATH17 PYTHONPATH: $SNAP/usr/lib/python3.8/site-packages:$SNAP/usr/lib/python3.8/dist-packages:$PYTHONPATH
18parts:18parts:
19 juju-lint:19 juju-lint:
20 plugin: python20 plugin: python
diff --git a/tests/conftest.py b/tests/conftest.py
index fc7093e..55ac1ac 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -8,11 +8,12 @@
88
9"""Test fixtures for juju-lint tool."""9"""Test fixtures for juju-lint tool."""
1010
11import mock
12import os11import os
13import pytest
14import sys12import sys
1513
14import mock
15import pytest
16
16# bring in top level library to path17# bring in top level library to path
17test_path = os.path.dirname(os.path.abspath(__file__))18test_path = os.path.dirname(os.path.abspath(__file__))
18sys.path.insert(0, test_path + "/../")19sys.path.insert(0, test_path + "/../")
diff --git a/tests/requirements.txt b/tests/requirements.txt
index b40fd2a..2575eb3 100644
--- a/tests/requirements.txt
+++ b/tests/requirements.txt
@@ -1,8 +1,10 @@
1# Module requirements1# Module requirements
2black
2flake83flake8
3flake8-colors4flake8-colors
4flake8-docstrings5flake8-docstrings
5flake8-html6flake8-html
7isort
6mock8mock
7pep8-naming9pep8-naming
8pycodestyle10pycodestyle
@@ -10,4 +12,4 @@ pyflakes
10pytest12pytest
11pytest-cov13pytest-cov
12pytest-html14pytest-html
13pytest-mock
14\ No newline at end of file15\ No newline at end of file
16pytest-mock
diff --git a/tests/test_jujulint.py b/tests/test_jujulint.py
index c4c51f3..cff1d6b 100644
--- a/tests/test_jujulint.py
+++ b/tests/test_jujulint.py
@@ -1,8 +1,8 @@
1#!/usr/bin/python31#!/usr/bin/python3
2"""Tests for jujulint."""2"""Tests for jujulint."""
3import logging
3from datetime import datetime, timezone4from datetime import datetime, timezone
4from unittest import mock5from unittest import mock
5import logging
66
7import pytest7import pytest
88
@@ -106,7 +106,7 @@ class TestLinter:
106 "test-app-4": {"charm": "local:SERIES/TEST-CHARM12"},106 "test-app-4": {"charm": "local:SERIES/TEST-CHARM12"},
107 "test-app-5": {"charm": "local:TEST-CHARM12"},107 "test-app-5": {"charm": "local:TEST-CHARM12"},
108 "test-app-6": {"charm": "cs:~TEST-CHARMERS/TEST-CHARM12-123"},108 "test-app-6": {"charm": "cs:~TEST-CHARMERS/TEST-CHARM12-123"},
109 "test-app-7": {"charm": "ch:amd64/bionic/TEST-CHARM12-123"}109 "test-app-7": {"charm": "ch:amd64/bionic/TEST-CHARM12-123"},
110 }110 }
111 linter.map_charms(applications)111 linter.map_charms(applications)
112 for charm in linter.model.charms:112 for charm in linter.model.charms:
@@ -524,9 +524,7 @@ applications:
524524
525 def test_config_eq_no_suffix_check_all(self, linter, juju_status):525 def test_config_eq_no_suffix_check_all(self, linter, juju_status):
526 """Test the config condition 'eq'. when no suffix all should be checked."""526 """Test the config condition 'eq'. when no suffix all should be checked."""
527 linter.lint_rules["config"] = {527 linter.lint_rules["config"] = {"ubuntu": {"fake-opt": {"eq": False}}}
528 "ubuntu": {"fake-opt": {"eq": False}}
529 }
530 juju_status["applications"]["ubuntu"]["options"] = {"fake-opt": True}528 juju_status["applications"]["ubuntu"]["options"] = {"fake-opt": True}
531 juju_status["applications"]["ubuntu-host"] = juju_status["applications"].pop(529 juju_status["applications"]["ubuntu-host"] = juju_status["applications"].pop(
532 "ubuntu"530 "ubuntu"
@@ -720,21 +718,21 @@ applications:
720 }718 }
721719
722 def test_check_spaces_detect_mismatches(self, linter, mocker):720 def test_check_spaces_detect_mismatches(self, linter, mocker):
723 mock_log: mock.MagicMock = mocker.patch('jujulint.lint.Linter._log_with_header')721 mock_log: mock.MagicMock = mocker.patch("jujulint.lint.Linter._log_with_header")
724 linter.model.app_to_charm = self.check_spaces_example_app_charm_map722 linter.model.app_to_charm = self.check_spaces_example_app_charm_map
725723
726 # Run the space check.724 # Run the space check.
727 # Based on the above bundle, we should have exactly one mismatch.725 # Based on the above bundle, we should have exactly one mismatch.
728 linter.check_spaces(self.check_spaces_example_bundle)726 linter.check_spaces(self.check_spaces_example_bundle)
729 727
730 # By default the mismatch should only trigger a warning, not an error.728 # By default the mismatch should only trigger a warning, not an error.
731 errors = linter.output_collector["errors"]729 errors = linter.output_collector["errors"]
732 assert len(errors) == 0730 assert len(errors) == 0
733 assert mock_log.call_count == 1731 assert mock_log.call_count == 1
734 assert mock_log.mock_calls[0].kwargs['level'] == logging.WARN732 assert mock_log.mock_calls[0].kwargs["level"] == logging.WARN
735 assert mock_log.mock_calls[0].args[0] == (733 assert mock_log.mock_calls[0].args[0] == (
736 'Space binding mismatch: SpaceMismatch(telegraf-app:prometheus-client '734 "Space binding mismatch: SpaceMismatch(prometheus-app:target "
737 '(space external-space) != prometheus-app:target (space internal-space))'735 "(space internal-space) != telegraf-app:prometheus-client (space external-space))"
738 )736 )
739737
740 def test_check_spaces_enforce_endpoints(self, linter):738 def test_check_spaces_enforce_endpoints(self, linter):
@@ -749,7 +747,9 @@ applications:
749747
750 # Enforce the opposite end of the relation.748 # Enforce the opposite end of the relation.
751 # This should also generate an error.749 # This should also generate an error.
752 linter.lint_rules["space checks"] = {"enforce endpoints": ["telegraf:prometheus-client"]}750 linter.lint_rules["space checks"] = {
751 "enforce endpoints": ["telegraf:prometheus-client"]
752 }
753 linter.check_spaces(self.check_spaces_example_bundle)753 linter.check_spaces(self.check_spaces_example_bundle)
754 errors = linter.output_collector["errors"]754 errors = linter.output_collector["errors"]
755 assert len(errors) == 2755 assert len(errors) == 2
@@ -759,20 +759,24 @@ applications:
759759
760 # Run the space check with prometheus:target endpoint enforced.760 # Run the space check with prometheus:target endpoint enforced.
761 # This should generate an error.761 # This should generate an error.
762 linter.lint_rules["space checks"] = {"enforce relations": [["prometheus:target", "telegraf:prometheus-client"]]}762 linter.lint_rules["space checks"] = {
763 "enforce relations": [["prometheus:target", "telegraf:prometheus-client"]]
764 }
763 linter.check_spaces(self.check_spaces_example_bundle)765 linter.check_spaces(self.check_spaces_example_bundle)
764 errors = linter.output_collector["errors"]766 errors = linter.output_collector["errors"]
765 assert len(errors) == 1767 assert len(errors) == 1
766768
767 # Reverse the relation's definition order.769 # Reverse the relation's definition order.
768 # This should work the same way and also generate an error.770 # This should work the same way and also generate an error.
769 linter.lint_rules["space checks"] = {"enforce relations": [["telegraf:prometheus-client", "prometheus:target"]]}771 linter.lint_rules["space checks"] = {
772 "enforce relations": [["telegraf:prometheus-client", "prometheus:target"]]
773 }
770 linter.check_spaces(self.check_spaces_example_bundle)774 linter.check_spaces(self.check_spaces_example_bundle)
771 errors = linter.output_collector["errors"]775 errors = linter.output_collector["errors"]
772 assert len(errors) == 2776 assert len(errors) == 2
773777
774 def test_check_spaces_ignore_endpoints(self, linter, mocker):778 def test_check_spaces_ignore_endpoints(self, linter, mocker):
775 mock_log: mock.MagicMock = mocker.patch('jujulint.lint.Linter._log_with_header')779 mock_log: mock.MagicMock = mocker.patch("jujulint.lint.Linter._log_with_header")
776 linter.model.app_to_charm = self.check_spaces_example_app_charm_map780 linter.model.app_to_charm = self.check_spaces_example_app_charm_map
777781
778 # Run the space check with prometheus:target endpoint ignored.782 # Run the space check with prometheus:target endpoint ignored.
@@ -785,19 +789,23 @@ applications:
785789
786 # Enforce the opposite end of the relation.790 # Enforce the opposite end of the relation.
787 # This should also generate an error.791 # This should also generate an error.
788 linter.lint_rules["space checks"] = {"ignore endpoints": ["telegraf:prometheus-client"]}792 linter.lint_rules["space checks"] = {
793 "ignore endpoints": ["telegraf:prometheus-client"]
794 }
789 linter.check_spaces(self.check_spaces_example_bundle)795 linter.check_spaces(self.check_spaces_example_bundle)
790 errors = linter.output_collector["errors"]796 errors = linter.output_collector["errors"]
791 assert len(errors) == 0797 assert len(errors) == 0
792 assert mock_log.call_count == 0798 assert mock_log.call_count == 0
793799
794 def test_check_spaces_ignore_relations(self, linter, mocker):800 def test_check_spaces_ignore_relations(self, linter, mocker):
795 mock_log: mock.MagicMock = mocker.patch('jujulint.lint.Linter._log_with_header')801 mock_log: mock.MagicMock = mocker.patch("jujulint.lint.Linter._log_with_header")
796 linter.model.app_to_charm = self.check_spaces_example_app_charm_map802 linter.model.app_to_charm = self.check_spaces_example_app_charm_map
797803
798 # Run the space check with prometheus:target endpoint ignored.804 # Run the space check with prometheus:target endpoint ignored.
799 # This should neither generate an error nor a warning.805 # This should neither generate an error nor a warning.
800 linter.lint_rules["space checks"] = {"ignore relations": [["prometheus:target", "telegraf:prometheus-client"]]}806 linter.lint_rules["space checks"] = {
807 "ignore relations": [["prometheus:target", "telegraf:prometheus-client"]]
808 }
801 linter.check_spaces(self.check_spaces_example_bundle)809 linter.check_spaces(self.check_spaces_example_bundle)
802 errors = linter.output_collector["errors"]810 errors = linter.output_collector["errors"]
803 assert len(errors) == 0811 assert len(errors) == 0
@@ -805,7 +813,9 @@ applications:
805813
806 # Reverse the relation's definition order.814 # Reverse the relation's definition order.
807 # This should work the same way and also not generate errors/warnings.815 # This should work the same way and also not generate errors/warnings.
808 linter.lint_rules["space checks"] = {"ignore relations": [["telegraf:prometheus-client", "prometheus:target"]]}816 linter.lint_rules["space checks"] = {
817 "ignore relations": [["telegraf:prometheus-client", "prometheus:target"]]
818 }
809 linter.check_spaces(self.check_spaces_example_bundle)819 linter.check_spaces(self.check_spaces_example_bundle)
810 errors = linter.output_collector["errors"]820 errors = linter.output_collector["errors"]
811 assert len(errors) == 0821 assert len(errors) == 0
diff --git a/tox.ini b/tox.ini
index 3e9dbdf..843a442 100644
--- a/tox.ini
+++ b/tox.ini
@@ -15,7 +15,7 @@ envlist = lintverbose, unit
15skip_missing_interpreters = True15skip_missing_interpreters = True
1616
17[testenv]17[testenv]
18basepython = python318basepython = python3.8
19deps =19deps =
20 -r{toxinidir}/tests/requirements.txt20 -r{toxinidir}/tests/requirements.txt
21 -r{toxinidir}/requirements.txt21 -r{toxinidir}/requirements.txt
@@ -24,20 +24,34 @@ commands =
24 test: python3 -m unittest discover tests24 test: python3 -m unittest discover tests
2525
26[testenv:unit]26[testenv:unit]
27commands = pytest -v \27commands =
28 --cov=jujulint \28 pytest -v \
29 --cov-report=term \29 --cov=jujulint \
30 --cov-report=annotate:tests/report/coverage-annotated \30 --new-first \
31 --cov-report=html:tests/report/coverage-html \31 --last-failed \
32 --html=tests/report/index.html \32 --last-failed-no-failures all \
33 --junitxml=tests/report/junit.xml33 --cov-report=term-missing \
34 --cov-report=annotate:tests/report/coverage-annotated \
35 --cov-report=html:tests/report/coverage-html \
36 --html=tests/report/index.html \
37 --junitxml=tests/report/junit.xml
34setenv = PYTHONPATH={toxinidir}/lib38setenv = PYTHONPATH={toxinidir}/lib
3539
36[testenv:lint]40[testenv:lint]
37commands = flake8 jujulint --format=html --htmldir=tests/report/lint/ --tee41commands = flake8 jujulint --format=html --htmldir=tests/report/lint/ --tee
3842
39[testenv:lintverbose]43[testenv:lintverbose]
40commands = flake8 jujulint44commands =
45 flake8 {toxinidir}/jujulint {toxinidir}/tests
46 black --check {toxinidir}/jujulint {toxinidir}/tests
47 isort --check {toxinidir}/jujulint {toxinidir}/tests
48
49
50[testenv:format-code]
51commands =
52 black {toxinidir}/jujulint {toxinidir}/tests
53 isort {toxinidir}/jujulint {toxinidir}/tests
54
4155
42[testenv:lintjunit]56[testenv:lintjunit]
43commands = flake8 jujulint --format junit-xml --output-file=report/lint/junit.xml57commands = flake8 jujulint --format junit-xml --output-file=report/lint/junit.xml
@@ -45,3 +59,6 @@ commands = flake8 jujulint --format junit-xml --output-file=report/lint/junit.xm
45[pytest]59[pytest]
46filterwarnings = 60filterwarnings =
47 ignore::DeprecationWarning61 ignore::DeprecationWarning
62
63[isort]
64profile = black

Subscribers

People subscribed via source and target branches

to all changes: