RULEAPI-606: github action and underlying script for adding a new language to an existing rule

This commit is contained in:
Arseniy Zaostrovnykh 2021-09-30 17:47:25 +02:00 committed by GitHub
parent 8d21500f3b
commit 5f8a2e7ec1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 600 additions and 130 deletions

42
.github/workflows/add_language.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: Add language to a rule
# Workflow runs when manually triggered using the UI or API.
on:
workflow_dispatch:
# Inputs the workflow accepts.
inputs:
rule:
description: 'ID of an existing rule (e.g., S1234).'
required: true
language:
description: 'Language to be added to the rule, (e.g., cfamily)'
required: true
jobs:
add_language_to_rule:
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v2
with:
persist-credentials: true
ref: master
path: 'rspec'
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: 'Install Pipenv'
run: |
pip install pipenv
- name: 'Install rspec-tools'
working-directory: 'rspec/rspec-tools'
run: pipenv install -e .
- name: 'Add Language'
working-directory: 'rspec/rspec-tools'
run: pipenv run rspec-tools add-lang-to-rule --user ${{ github.actor }} --language "${{ github.event.inputs.language }}" --rule "${{ github.event.inputs.rule }}"

View File

@ -10,6 +10,7 @@ rspec-tools = {editable = true, path = "."}
gitpython = "*" gitpython = "*"
pygithub = "*" pygithub = "*"
jsonschema = "*" jsonschema = "*"
fs = "*"
[dev-packages] [dev-packages]
pytest = ">=6.2.2" pytest = ">=6.2.2"

313
rspec-tools/Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "307552df2c8cbbd087e576ba8c0bcea21c1151baf7f9eb3076e910a9063f66e4" "sha256": "a141a126414372441705df189b3013cdadd7fa27f3114dfc07264686c6291204"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -16,6 +16,13 @@
] ]
}, },
"default": { "default": {
"appdirs": {
"hashes": [
"sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41",
"sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"
],
"version": "==1.4.4"
},
"attrs": { "attrs": {
"hashes": [ "hashes": [
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
@ -25,11 +32,10 @@
}, },
"beautifulsoup4": { "beautifulsoup4": {
"hashes": [ "hashes": [
"sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35", "sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf",
"sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25", "sha256:c23ad23c521d818955a4151a67d81580319d4bf548d3d49f4223ae041ff98891"
"sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"
], ],
"version": "==4.9.3" "version": "==4.10.0"
}, },
"bs4": { "bs4": {
"hashes": [ "hashes": [
@ -45,27 +51,86 @@
], ],
"version": "==2021.5.30" "version": "==2021.5.30"
}, },
"chardet": { "cffi": {
"hashes": [ "hashes": [
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", "sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d",
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" "sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771",
"sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872",
"sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c",
"sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc",
"sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762",
"sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202",
"sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5",
"sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548",
"sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a",
"sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f",
"sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20",
"sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218",
"sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c",
"sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e",
"sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56",
"sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224",
"sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a",
"sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2",
"sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a",
"sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819",
"sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346",
"sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b",
"sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e",
"sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534",
"sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb",
"sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0",
"sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156",
"sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd",
"sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87",
"sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc",
"sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195",
"sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33",
"sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f",
"sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d",
"sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd",
"sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728",
"sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7",
"sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca",
"sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99",
"sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf",
"sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e",
"sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c",
"sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5",
"sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"
], ],
"version": "==4.0.0" "version": "==1.14.6"
},
"charset-normalizer": {
"hashes": [
"sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6",
"sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f"
],
"markers": "python_version >= '3'",
"version": "==2.0.6"
}, },
"click": { "click": {
"hashes": [ "hashes": [
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a",
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"
], ],
"index": "pypi", "index": "pypi",
"version": "==7.1.2" "version": "==8.0.1"
}, },
"deprecated": { "deprecated": {
"hashes": [ "hashes": [
"sha256:08452d69b6b5bc66e8330adde0a4f8642e969b9e1702904d137eeb29c8ffc771", "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d",
"sha256:6d2de2de7931a968874481ef30208fd4e08da39177d61d3d4ebdf4366e7dbca1" "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"
], ],
"version": "==1.2.12" "version": "==1.2.13"
},
"fs": {
"hashes": [
"sha256:1d10cc8f9c55fbcf7b23775289a13f6796dca7acd5a135c379f49e87a56a7230",
"sha256:caab4dc1561d63c92f36ee78976f6a4a01381830d8420ce34a78d4f1bb1dc95f"
],
"index": "pypi",
"version": "==2.4.13"
}, },
"gitdb": { "gitdb": {
"hashes": [ "hashes": [
@ -76,18 +141,19 @@
}, },
"gitpython": { "gitpython": {
"hashes": [ "hashes": [
"sha256:8621a7e777e276a5ec838b59280ba5272dd144a18169c36c903d8b38b99f750a", "sha256:dc0a7f2f697657acc8d7f89033e8b1ea94dd90356b2983bca89dc8d2ab3cc647",
"sha256:c5347c81d232d9b8e7f47b68a83e5dc92e7952127133c5f2df9133f2c75a1b29" "sha256:df83fdf5e684fef7c6ee2c02fc68a5ceb7e7e759d08b694088d0cacb4eba59e5"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.1.13" "version": "==3.1.24"
}, },
"idna": { "idna": {
"hashes": [ "hashes": [
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"
], ],
"version": "==2.10" "markers": "python_version >= '3'",
"version": "==3.2"
}, },
"jsonschema": { "jsonschema": {
"hashes": [ "hashes": [
@ -97,33 +163,90 @@
"index": "pypi", "index": "pypi",
"version": "==3.2.0" "version": "==3.2.0"
}, },
"pycparser": {
"hashes": [
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
],
"version": "==2.20"
},
"pygithub": { "pygithub": {
"hashes": [ "hashes": [
"sha256:300bc16e62886ca6537b0830e8f516ea4bc3ef12d308e0c5aff8bdbd099173d4", "sha256:1bbfff9372047ff3f21d5cd8e07720f3dbfdaf6462fcaed9d815f528f1ba7283",
"sha256:87afd6a67ea582aa7533afdbf41635725f13d12581faed7e3e04b1579c0c0627" "sha256:2caf0054ea079b71e539741ae56c5a95e073b81fa472ce222e81667381b9601b"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.54.1" "version": "==1.55"
}, },
"pyjwt": { "pyjwt": {
"hashes": [ "hashes": [
"sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e", "sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1",
"sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96" "sha256:fba44e7898bbca160a2b2b501f492824fc8382485d3a6f11ba5d0c1937ce6130"
], ],
"version": "==1.7.1" "version": "==2.1.0"
},
"pynacl": {
"hashes": [
"sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4",
"sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4",
"sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574",
"sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d",
"sha256:4e10569f8cbed81cb7526ae137049759d2a8d57726d52c1a000a3ce366779634",
"sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25",
"sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f",
"sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505",
"sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122",
"sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7",
"sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420",
"sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f",
"sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96",
"sha256:c914f78da4953b33d4685e3cdc7ce63401247a21425c16a39760e282075ac4a6",
"sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6",
"sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514",
"sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff",
"sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80"
],
"version": "==1.4.0"
}, },
"pyrsistent": { "pyrsistent": {
"hashes": [ "hashes": [
"sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e" "sha256:097b96f129dd36a8c9e33594e7ebb151b1515eb52cceb08474c10a5479e799f2",
"sha256:2aaf19dc8ce517a8653746d98e962ef480ff34b6bc563fc067be6401ffb457c7",
"sha256:404e1f1d254d314d55adb8d87f4f465c8693d6f902f67eb6ef5b4526dc58e6ea",
"sha256:48578680353f41dca1ca3dc48629fb77dfc745128b56fc01096b2530c13fd426",
"sha256:4916c10896721e472ee12c95cdc2891ce5890898d2f9907b1b4ae0f53588b710",
"sha256:527be2bfa8dc80f6f8ddd65242ba476a6c4fb4e3aedbf281dfbac1b1ed4165b1",
"sha256:58a70d93fb79dc585b21f9d72487b929a6fe58da0754fa4cb9f279bb92369396",
"sha256:5e4395bbf841693eaebaa5bb5c8f5cdbb1d139e07c975c682ec4e4f8126e03d2",
"sha256:6b5eed00e597b5b5773b4ca30bd48a5774ef1e96f2a45d105db5b4ebb4bca680",
"sha256:73ff61b1411e3fb0ba144b8f08d6749749775fe89688093e1efef9839d2dcc35",
"sha256:772e94c2c6864f2cd2ffbe58bb3bdefbe2a32afa0acb1a77e472aac831f83427",
"sha256:773c781216f8c2900b42a7b638d5b517bb134ae1acbebe4d1e8f1f41ea60eb4b",
"sha256:a0c772d791c38bbc77be659af29bb14c38ced151433592e326361610250c605b",
"sha256:b29b869cf58412ca5738d23691e96d8aff535e17390128a1a52717c9a109da4f",
"sha256:c1a9ff320fa699337e05edcaae79ef8c2880b52720bc031b219e5b5008ebbdef",
"sha256:cd3caef37a415fd0dae6148a1b6957a8c5f275a62cca02e18474608cb263640c",
"sha256:d5ec194c9c573aafaceebf05fc400656722793dac57f254cd4741f3c27ae57b4",
"sha256:da6e5e818d18459fa46fac0a4a4e543507fe1110e808101277c5a2b5bab0cd2d",
"sha256:e79d94ca58fcafef6395f6352383fa1a76922268fa02caa2272fff501c2fdc78",
"sha256:f3ef98d7b76da5eb19c37fda834d50262ff9167c65658d1d8f974d2e4d90676b",
"sha256:f4c8cabb46ff8e5d61f56a037974228e978f26bfefce4f61a4b1ac0ba7a2ab72"
], ],
"version": "==0.17.3" "version": "==0.18.0"
},
"pytz": {
"hashes": [
"sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
"sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
],
"version": "==2021.1"
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
], ],
"version": "==2.25.1" "version": "==2.26.0"
}, },
"rspec-tools": { "rspec-tools": {
"editable": true, "editable": true,
@ -148,16 +271,23 @@
"sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc", "sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc",
"sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b" "sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b"
], ],
"markers": "python_version >= '3.0'",
"version": "==2.2.1" "version": "==2.2.1"
}, },
"typing-extensions": {
"hashes": [
"sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e",
"sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7",
"sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"
],
"markers": "python_version < '3.10'",
"version": "==3.10.0.2"
},
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
"sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c", "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece",
"sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098" "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"
], ],
"index": "pypi", "version": "==1.26.7"
"version": "==1.26.5"
}, },
"wrapt": { "wrapt": {
"hashes": [ "hashes": [
@ -183,31 +313,32 @@
}, },
"mypy": { "mypy": {
"hashes": [ "hashes": [
"sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e", "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9",
"sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064", "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a",
"sha256:28fb5479c494b1bab244620685e2eb3c3f988d71fd5d64cc753195e8ed53df7c", "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9",
"sha256:2f9b3407c58347a452fc0736861593e105139b905cca7d097e413453a1d650b4", "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e",
"sha256:33f159443db0829d16f0a8d83d94df3109bb6dd801975fe86bacb9bf71628e97", "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2",
"sha256:3f2aca7f68580dc2508289c729bd49ee929a436208d2b2b6aab15745a70a57df", "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212",
"sha256:499c798053cdebcaa916eef8cd733e5584b5909f789de856b482cd7d069bdad8", "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b",
"sha256:4eec37370483331d13514c3f55f446fc5248d6373e7029a29ecb7b7494851e7a", "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885",
"sha256:552a815579aa1e995f39fd05dde6cd378e191b063f031f2acfe73ce9fb7f9e56", "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150",
"sha256:5873888fff1c7cf5b71efbe80e0e73153fe9212fafdf8e44adfe4c20ec9f82d7", "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703",
"sha256:61a3d5b97955422964be6b3baf05ff2ce7f26f52c85dd88db11d5e03e146a3a6", "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072",
"sha256:674e822aa665b9fd75130c6c5f5ed9564a38c6cea6a6432ce47eafb68ee578c5", "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457",
"sha256:7ce3175801d0ae5fdfa79b4f0cfed08807af4d075b402b7e294e6aa72af9aa2a", "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e",
"sha256:9743c91088d396c1a5a3c9978354b61b0382b4e3c440ce83cf77994a43e8c521", "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0",
"sha256:9f94aac67a2045ec719ffe6111df543bac7874cee01f41928f6969756e030564", "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb",
"sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49", "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97",
"sha256:abf7e0c3cf117c44d9285cc6128856106183938c68fd4944763003decdcfeb66", "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8",
"sha256:b09669bcda124e83708f34a94606e01b614fa71931d356c1f1a5297ba11f110a", "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811",
"sha256:cd07039aa5df222037005b08fbbfd69b3ab0b0bd7a07d7906de75ae52c4e3119", "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6",
"sha256:d23e0ea196702d918b60c8288561e722bf437d82cb7ef2edcd98cfa38905d506", "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de",
"sha256:d65cc1df038ef55a99e617431f0553cd77763869eebdf9042403e16089fe746c", "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504",
"sha256:d7da2e1d5f558c37d6e8c1246f1aec1e7349e4913d8fb3cb289a35de573fe2eb" "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921",
"sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.812" "version": "==0.910"
}, },
"mypy-extensions": { "mypy-extensions": {
"hashes": [ "hashes": [
@ -218,17 +349,17 @@
}, },
"packaging": { "packaging": {
"hashes": [ "hashes": [
"sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5", "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7",
"sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a" "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"
], ],
"version": "==20.9" "version": "==21.0"
}, },
"pluggy": { "pluggy": {
"hashes": [ "hashes": [
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
], ],
"version": "==0.13.1" "version": "==1.0.0"
}, },
"py": { "py": {
"hashes": [ "hashes": [
@ -246,11 +377,11 @@
}, },
"pytest": { "pytest": {
"hashes": [ "hashes": [
"sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9", "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89",
"sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839" "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"
], ],
"index": "pypi", "index": "pypi",
"version": "==6.2.2" "version": "==6.2.5"
}, },
"rspec-tools": { "rspec-tools": {
"editable": true, "editable": true,
@ -263,48 +394,14 @@
], ],
"version": "==0.10.2" "version": "==0.10.2"
}, },
"typed-ast": {
"hashes": [
"sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace",
"sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff",
"sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266",
"sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528",
"sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6",
"sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808",
"sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4",
"sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363",
"sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341",
"sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04",
"sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41",
"sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e",
"sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3",
"sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899",
"sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805",
"sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c",
"sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c",
"sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39",
"sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a",
"sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3",
"sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7",
"sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f",
"sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075",
"sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0",
"sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40",
"sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428",
"sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927",
"sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3",
"sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f",
"sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"
],
"version": "==1.4.3"
},
"typing-extensions": { "typing-extensions": {
"hashes": [ "hashes": [
"sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e",
"sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7",
"sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"
], ],
"version": "==3.10.0.0" "markers": "python_version < '3.10'",
"version": "==3.10.0.2"
} }
} }
} }

View File

@ -5,7 +5,7 @@ from pathlib import Path
import click import click
from rspec_tools.checklinks import check_html_links from rspec_tools.checklinks import check_html_links
from rspec_tools.errors import RuleNotFoundError, RuleValidationError from rspec_tools.errors import RuleNotFoundError, RuleValidationError
from rspec_tools.create_rule import create_new_rule from rspec_tools.create_rule import create_new_rule, add_language_to_rule
from rspec_tools.rules import RulesRepository from rspec_tools.rules import RulesRepository
from rspec_tools.validation.metadata import validate_metadata from rspec_tools.validation.metadata import validate_metadata
from rspec_tools.validation.description import validate_section_names, validate_section_levels from rspec_tools.validation.description import validate_section_names, validate_section_levels
@ -37,6 +37,15 @@ def create_rule(languages: str, user: Optional[str]):
token = os.environ.get('GITHUB_TOKEN') token = os.environ.get('GITHUB_TOKEN')
create_new_rule(languages, token, user) create_new_rule(languages, token, user)
@cli.command()
@click.option('--language', required=True)
@click.option('--rule', required=True)
@click.option('--user', required=False)
def add_lang_to_rule(language: str, rule: str, user: Optional[str]):
'''Add a new language to rule.'''
token = os.environ.get('GITHUB_TOKEN')
add_language_to_rule(language, rule, token, user)
@cli.command() @cli.command()
@click.argument('rules', nargs=-1) @click.argument('rules', nargs=-1)

View File

@ -1,6 +1,7 @@
from rspec_tools.errors import GitError from rspec_tools.errors import InvalidArgumentError
import click import click
import tempfile import tempfile
import fs
from git import Repo from git import Repo
from git.remote import PushInfo from git.remote import PushInfo
from github import Github from github import Github
@ -8,7 +9,7 @@ from github.PullRequest import PullRequest
from pathlib import Path from pathlib import Path
from typing import Final, Iterable, Optional, Callable from typing import Final, Iterable, Optional, Callable
from contextlib import contextmanager from contextlib import contextmanager
from rspec_tools.utils import parse_and_validate_language_list, get_labels_for_languages from rspec_tools.utils import parse_and_validate_language_list, get_labels_for_languages, validate_language, get_label_for_language, resolve_rule, swap_metadata_files, is_empty_metadata
from rspec_tools.utils import copy_directory_content from rspec_tools.utils import copy_directory_content
@ -45,6 +46,19 @@ def create_new_rule(languages: str, token: str, user: Optional[str]):
rule_number = rule_creator.reserve_rule_number() rule_number = rule_creator.reserve_rule_number()
pull_request = rule_creator.create_new_rule_pull_request(authGithub(token), rule_number, lang_list, label_list, user=user) pull_request = rule_creator.create_new_rule_pull_request(authGithub(token), rule_number, lang_list, label_list, user=user)
def add_language_to_rule(language: str, rule: str, token: str, user: Optional[str]):
url = build_github_repository_url(token, user)
config = {}
if user:
config['user.name'] = user
config['user.email'] = f'{user}@users.noreply.github.com'
validate_language(language)
label = get_label_for_language(language)
rule_number = resolve_rule(rule)
with tempfile.TemporaryDirectory() as tmpdirname:
rule_creator = RuleCreator(url, tmpdirname, config)
rule_creator.add_language_pull_request(authGithub(token), rule_number, language, label, user=user)
class RuleCreator: class RuleCreator:
''' Create a new Rule in a repository following the official Github 'rspec' repository structure.''' ''' Create a new Rule in a repository following the official Github 'rspec' repository structure.'''
MASTER_BRANCH: Final[str] = 'master' MASTER_BRANCH: Final[str] = 'master'
@ -70,7 +84,6 @@ class RuleCreator:
split_key = key.split('.') split_key = key.split('.')
config_writer.set_value(*split_key, value) config_writer.set_value(*split_key, value)
def reserve_rule_number(self) -> int: def reserve_rule_number(self) -> int:
'''Reserve an id on the id counter branch of the repository.''' '''Reserve an id on the id counter branch of the repository.'''
with self._current_git_branch(self.ID_COUNTER_BRANCH): with self._current_git_branch(self.ID_COUNTER_BRANCH):
@ -87,6 +100,31 @@ class RuleCreator:
click.echo(f'Reserved Rule ID S{counter}') click.echo(f'Reserved Rule ID S{counter}')
return counter return counter
def add_language_branch(self, rule_number: int, language: str) -> str:
'''Create and move files to add a new language to an existing rule.'''
branch_name = f'rule/S{rule_number}-add-{language}'
with self._current_git_branch(self.MASTER_BRANCH, branch_name):
repo_dir = Path(self.repository.working_dir)
rule_dir = repo_dir.joinpath('rules', f'S{rule_number}')
if not rule_dir.is_dir():
raise InvalidArgumentError(f"Rule \"S{rule_number}\" does not exist.")
lang_dirs = [d for d in rule_dir.glob('*/') if d.is_dir()]
if 1 == len(list(lang_dirs)) and is_empty_metadata(rule_dir):
swap_metadata_files(rule_dir, lang_dirs[0])
lang_dir = rule_dir.joinpath(language)
if lang_dir.is_dir():
lang_url = f"https://github.com/SonarSource/rspec/tree/master/rules/S{rule_number}/{language}"
raise InvalidArgumentError(f"Rule \"S{rule_number}\" is already defined for language {language}. Modify the definition here: {lang_url}.")
lang_dir.mkdir()
lang_specific_template = self.TEMPLATE_PATH.joinpath('multi_language', 'language_specific')
copy_directory_content(lang_specific_template, lang_dir)
self._fill_in_the_blanks_in_the_template(lang_dir, rule_number)
self.repository.git.add('--all')
self.repository.index.commit(f'Add {language} to rule S{rule_number}')
self.repository.git.push('origin', branch_name)
return branch_name
def create_new_rule_branch(self, rule_number: int, languages: Iterable[str]) -> str: def create_new_rule_branch(self, rule_number: int, languages: Iterable[str]) -> str:
'''Create all the files required for a new rule.''' '''Create all the files required for a new rule.'''
branch_name = f'rule/add-RSPEC-S{rule_number}' branch_name = f'rule/add-RSPEC-S{rule_number}'
@ -135,17 +173,13 @@ class RuleCreator:
self._fill_in_the_blanks_in_the_template(rule_dir, rule_number) self._fill_in_the_blanks_in_the_template(rule_dir, rule_number)
def create_new_rule_pull_request(self, githubApi: Callable[[Optional[str]], Github], rule_number: int, languages: Iterable[str], labels: Iterable[str], *, user: Optional[str]) -> PullRequest: def _create_pull_request(self, githubApi: Callable[[Optional[str]], Github], branch_name: str, title: str, body: str, labels: Iterable[str], user: Optional[str]):
branch_name = self.create_new_rule_branch(rule_number, languages)
click.echo(f'Created rule Branch {branch_name}')
repository_url = extract_repository_name(self.origin_url) repository_url = extract_repository_name(self.origin_url)
github = githubApi(user) github = githubApi(user)
github_repo = github.get_repo(repository_url) github_repo = github.get_repo(repository_url)
first_lang = next(iter(languages))
pull_request = github_repo.create_pull( pull_request = github_repo.create_pull(
title=f'Create rule S{rule_number}', title=title,
body=f'You can preview this rule [here](https://sonarsource.github.io/rspec/#/rspec/S{rule_number}/{first_lang}) (updated a few minutes after each push).', body=body,
head=branch_name, base=self.MASTER_BRANCH, head=branch_name, base=self.MASTER_BRANCH,
draft=True, maintainer_can_modify=True draft=True, maintainer_can_modify=True
) )
@ -156,9 +190,33 @@ class RuleCreator:
pull_request.add_to_assignees(login) pull_request.add_to_assignees(login)
pull_request.add_to_labels(*labels) pull_request.add_to_labels(*labels)
click.echo(f'Pull request assigned to {login}') click.echo(f'Pull request assigned to {login}')
return pull_request return pull_request
def add_language_pull_request(self, githubApi: Callable[[Optional[str]], Github], rule_number: int, language: str, label: str, user: Optional[str]):
branch_name = self.add_language_branch(rule_number, language)
click.echo(f'Created rule branch {branch_name}')
return self._create_pull_request(
githubApi,
branch_name,
f'Create rule S{rule_number}[{language}]',
f'You can preview this rule [here](https://sonarsource.github.io/rspec/#/rspec/S{rule_number}/{language}) (updated a few minutes after each push).',
[label],
user
)
def create_new_rule_pull_request(self, githubApi: Callable[[Optional[str]], Github], rule_number: int, languages: Iterable[str], labels: Iterable[str], *, user: Optional[str]) -> PullRequest:
branch_name = self.create_new_rule_branch(rule_number, languages)
click.echo(f'Created rule branch {branch_name}')
first_lang = next(iter(languages))
return self._create_pull_request(
githubApi,
branch_name,
f'Create rule S{rule_number}',
f'You can preview this rule [here](https://sonarsource.github.io/rspec/#/rspec/S{rule_number}/{first_lang}) (updated a few minutes after each push).',
labels,
user
)
@contextmanager @contextmanager
def _current_git_branch(self, base_branch: str, new_branch: Optional[str] = None): def _current_git_branch(self, base_branch: str, new_branch: Optional[str] = None):
'''Checkout a given branch before yielding, then revert to the previous branch.''' '''Checkout a given branch before yielding, then revert to the previous branch.'''

View File

@ -1,6 +1,9 @@
from rspec_tools.errors import InvalidArgumentError from rspec_tools.errors import InvalidArgumentError
from pathlib import Path from pathlib import Path
import shutil import shutil
import re
import tempfile
import json
SUPPORTED_LANGUAGES_FILENAME = '../supported_languages.adoc' SUPPORTED_LANGUAGES_FILENAME = '../supported_languages.adoc'
LANG_TO_LABEL = {'abap': 'abap', LANG_TO_LABEL = {'abap': 'abap',
@ -41,6 +44,19 @@ def copy_directory_content(src:Path, dest:Path):
else: else:
shutil.copy2(item, dest) shutil.copy2(item, dest)
def swap_metadata_files(dir1:Path, dir2:Path):
meta1 = dir1.joinpath('metadata.json')
meta2 = dir2.joinpath('metadata.json')
with tempfile.TemporaryDirectory() as tmpdir:
tmp = Path(tmpdir).joinpath('metadata.json')
shutil.copy2(meta1, tmp)
shutil.copy2(meta2, meta1)
shutil.copy2(tmp, meta2)
def is_empty_metadata(rule_dir:Path):
with open(rule_dir.joinpath('metadata.json'), 'r') as meta:
return not json.load(meta)
def load_valid_languages(): def load_valid_languages():
with open(SUPPORTED_LANGUAGES_FILENAME, 'r') as supported_langs_file: with open(SUPPORTED_LANGUAGES_FILENAME, 'r') as supported_langs_file:
supported_langs = supported_langs_file.read() supported_langs = supported_langs_file.read()
@ -65,7 +81,22 @@ def parse_and_validate_language_list(languages):
raise InvalidArgumentError(f"Unsupported language: \"{lang}\". See {SUPPORTED_LANGUAGES_FILENAME} for the list of supported languages.") raise InvalidArgumentError(f"Unsupported language: \"{lang}\". See {SUPPORTED_LANGUAGES_FILENAME} for the list of supported languages.")
return lang_list return lang_list
def validate_language(language):
valid_langs = load_valid_languages()
if language not in valid_langs:
raise InvalidArgumentError(f"Unsupported language: \"{language}\". See {SUPPORTED_LANGUAGES_FILENAME} for the list of supported languages.")
def get_labels_for_languages(lang_list): def get_labels_for_languages(lang_list):
labels = [LANG_TO_LABEL[lang] for lang in lang_list] labels = [LANG_TO_LABEL[lang] for lang in lang_list]
return list(set(labels)) return list(set(labels))
def get_label_for_language(language: str) -> str:
return LANG_TO_LABEL[language]
def resolve_rule(ruleID: str) -> int:
m = re.search('^S([0-9]{3,4})$', ruleID)
if not m:
raise InvalidArgumentError(f"Unrecognized rule id format: \"{ruleID}\". Rule id must start with an \"S\" followed by 3 or 4 digits.")
else:
return int(m.group(1))

View File

@ -0,0 +1,3 @@
=== on 21 Oct 2014, 18:50:16 Ann Campbell wrote:
\[~samuel.mercier] this is an "inconsistent with developer expectations" rule, & so should probably be tied to Reliability & is also likely at least a "pitfall" if not a "bug".

View File

@ -0,0 +1,4 @@
=== Message
Explicitly invoke the template version of this function.

View File

@ -0,0 +1,27 @@
{
"title": "The viable function set for a function call should either contain no function specializations, or only contain function specializations",
"type": "BUG",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [
"based-on-misra"
],
"extra": {
"replacementRules": [
],
"legacyKeys": [
]
},
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-1033",
"sqKey": "S1033",
"scope": "Main",
"defaultQualityProfiles": [
]
}

View File

@ -0,0 +1,53 @@
If a function and a specialization of a function template are deemed equivalent after overload resolution, the non-specialized function will be chosen over the function specialization, which may be inconsistent with developer expectations.
== Noncompliant Code Example
----
void f ( short ); // Example 1
template <typename T> void f ( T ); // Example 2
void b ( short s )
{
f ( s ); // Noncompliant - Calls Example 1
f ( s + 1 ); // Noncompliant - Calls Example 2
}
----
== Compliant Solution
----
void f ( short ); // Example 1
template <typename T> void f ( T ); // Example 2
void b ( short s )
{
f<>( s ); // Compliant - Explicitly calls Example 2
f<>( s + 1 ); // Compliant - Explicitly calls Example 2
}
----
== Exceptions
This rule does not apply to copy constructors or copy assignment operators.
== See
* MISRA {cpp}:2008, 14-8-2
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
include::message.adoc[]
'''
== Comments And Links
(visible only on this page)
include::comments-and-links.adoc[]
endif::env-github,rspecator-view[]

View File

@ -0,0 +1,2 @@
{
}

View File

@ -4,8 +4,10 @@ from pathlib import Path
from typing import Optional from typing import Optional
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
import pytest import pytest
import shutil
from rspec_tools.create_rule import RuleCreator, create_new_rule from rspec_tools.create_rule import RuleCreator, create_new_rule, add_language_to_rule
from rspec_tools.utils import is_empty_metadata
@pytest.fixture @pytest.fixture
def git_config(): def git_config():
@ -16,20 +18,18 @@ def git_config():
} }
@pytest.fixture @pytest.fixture
def mock_rspec_repo(tmpdir): def mock_rspec_repo(tmpdir, mockrules: Path):
repo_dir = tmpdir.mkdir("mock_rspec") repo_dir = tmpdir.mkdir("mock_rspec")
repo = Repo.init(str(repo_dir)) repo = Repo.init(str(repo_dir))
repo.init() repo.init()
rules_dir = repo_dir.join('rules')
shutil.copytree(mockrules, rules_dir)
with repo.config_writer() as config_writer: with repo.config_writer() as config_writer:
config_writer.set_value('user', 'name', 'originuser') config_writer.set_value('user', 'name', 'originuser')
config_writer.set_value('user', 'email', 'originuser@mock.mock') config_writer.set_value('user', 'email', 'originuser@mock.mock')
rules_dir = repo_dir.mkdir('rules') repo.git.add('--all')
# create a file just to have a "rules" directory
gitignore = rules_dir.join('.gitignore')
gitignore.ensure()
repo.index.add([str(gitignore)])
repo.index.commit('init rules') repo.index.commit('init rules')
# Create the id counter branch. Note that it is an orphan branch. # Create the id counter branch. Note that it is an orphan branch.
@ -175,3 +175,120 @@ def test_create_new_rule_unsupported_language(mockRuleCreator):
prMock = mockRuleCreator.return_value.create_new_rule_pull_request prMock = mockRuleCreator.return_value.create_new_rule_pull_request
with pytest.raises(InvalidArgumentError): with pytest.raises(InvalidArgumentError):
create_new_rule('russian,php', 'my token', 'testuser') create_new_rule('russian,php', 'my token', 'testuser')
def test_add_lang_singlelang_nonconventional_rule_create_branch(rule_creator: RuleCreator, mock_rspec_repo: Repo):
'''Test add_language_branch for a single-language rule with metadata lifted to the generic rule level.'''
rule_number = 4727
language = 'php'
mock_rspec_repo.git.checkout('master')
orig_rule_dir = Path(mock_rspec_repo.working_dir).joinpath('rules', f'S{rule_number}')
assert(not is_empty_metadata(orig_rule_dir)) # nonconventional: singlelang rule with metadata on the upper level
assert(is_empty_metadata(orig_rule_dir.joinpath('cobol')))
original_metadata = orig_rule_dir.joinpath('metadata.json').read_text()
branch = rule_creator.add_language_branch(rule_number, language)
# Check that the branch was pushed successfully to the origin
mock_rspec_repo.git.checkout(branch)
rule_dir = Path(mock_rspec_repo.working_dir).joinpath('rules', f'S{rule_number}')
assert rule_dir.exists()
lang_dir = rule_dir.joinpath(f'{language}')
assert lang_dir.exists()
assert rule_dir.joinpath('metadata.json').read_text() == original_metadata
assert(is_empty_metadata(rule_dir.joinpath('cobol')))
lang_root = rule_creator.TEMPLATE_PATH.joinpath('multi_language', 'language_specific')
for lang_item in lang_root.glob('**/*'):
if lang_item.is_file():
expected_content = lang_item.read_text().replace('${RSPEC_ID}', str(rule_number))
relative_path = lang_item.relative_to(lang_root)
actual_content = rule_dir.joinpath(language, relative_path).read_text()
assert actual_content == expected_content
def test_add_lang_singlelang_conventional_rule_create_branch(rule_creator: RuleCreator, mock_rspec_repo: Repo):
'''Test add_language_branch for a regular single language rule.'''
rule_number = 1033
language = 'php'
mock_rspec_repo.git.checkout('master')
orig_rule_dir = Path(mock_rspec_repo.working_dir).joinpath('rules', f'S{rule_number}')
assert(is_empty_metadata(orig_rule_dir)) # conventional: singlelang rule with metadata on the lang-specific level
assert(not is_empty_metadata(orig_rule_dir.joinpath('cfamily')))
original_lmetadata = orig_rule_dir.joinpath('cfamily', 'metadata.json').read_text()
branch = rule_creator.add_language_branch(rule_number, language)
# Check that the branch was pushed successfully to the origin
mock_rspec_repo.git.checkout(branch)
rule_dir = Path(mock_rspec_repo.working_dir).joinpath('rules', f'S{rule_number}')
assert rule_dir.exists()
lang_dir = rule_dir.joinpath(f'{language}')
assert lang_dir.exists()
assert rule_dir.joinpath('metadata.json').read_text() == original_lmetadata
assert(is_empty_metadata(rule_dir.joinpath('cfamily')))
def test_add_lang_multilang_rule_create_branch(rule_creator: RuleCreator, mock_rspec_repo: Repo):
'''Test add_language_branch for a multi-language rule.'''
rule_number = 120
language = 'php'
branch = rule_creator.add_language_branch(rule_number, language)
# Check that the branch was pushed successfully to the origin
mock_rspec_repo.git.checkout(branch)
rule_dir = Path(mock_rspec_repo.working_dir).joinpath('rules', f'S{rule_number}')
assert rule_dir.exists()
lang_dir = rule_dir.joinpath(f'{language}')
assert lang_dir.exists()
lang_root = rule_creator.TEMPLATE_PATH.joinpath('multi_language', 'language_specific')
for lang_item in lang_root.glob('**/*'):
if lang_item.is_file():
expected_content = lang_item.read_text().replace('${RSPEC_ID}', str(rule_number))
relative_path = lang_item.relative_to(lang_root)
actual_content = rule_dir.joinpath(language, relative_path).read_text()
assert actual_content == expected_content
@patch('rspec_tools.create_rule.RuleCreator')
def test_add_unsupported_language(mockRuleCreator):
'''Test language validation.'''
mockRuleCreator.return_value = Mock()
mockRuleCreator.return_value.create_new_rule_pull_request = Mock()
prMock = mockRuleCreator.return_value.create_new_rule_pull_request
with pytest.raises(InvalidArgumentError):
add_language_to_rule('russian', 'S1033', 'my token', 'testuser')
def test_add_language_the_rule_is_already_defined_for(rule_creator: RuleCreator):
'''Test add_language_branch fails when trying to add a langage already added to the rule.'''
with pytest.raises(InvalidArgumentError):
rule_creator.add_language_branch(100, 'cfamily')
def test_add_language_to_nonexistent_rule(rule_creator: RuleCreator):
'''Test add_language_branch correctly fails when invoked for a non-existent rule.'''
with pytest.raises(InvalidArgumentError):
rule_creator.add_language_branch(101, 'cfamily')
def test_add_language_new_pr(rule_creator: RuleCreator):
'''Test add_language_pull_request adds the right user and labels.'''
rule_number = 120
language = 'php'
ghMock = Mock()
ghRepoMock = Mock()
pullMock = Mock()
ghRepoMock.create_pull = Mock(return_value=pullMock)
ghMock.get_repo = Mock(return_value=ghRepoMock)
def mockGithub(user: Optional[str]):
return ghMock
rule_creator.add_language_pull_request(mockGithub, rule_number, language, 'mylab', user='testuser')
ghRepoMock.create_pull.assert_called_once();
assert ghRepoMock.create_pull.call_args.kwargs['title'].startswith(f'Create rule S{rule_number}[{language}]')
ghRepoMock.create_pull.call_args.kwargs['head'].startswith('rule/')
pullMock.add_to_assignees.assert_called_with('testuser');
pullMock.add_to_labels.assert_called_with('mylab');

View File

@ -8,7 +8,7 @@ from rspec_tools.rules import RulesRepository
def test_list_rules(mockrules: Path): def test_list_rules(mockrules: Path):
'''Check that rules are all listed.''' '''Check that rules are all listed.'''
rules = {rule.id for rule in RulesRepository(rules_path=mockrules).rules} rules = {rule.id for rule in RulesRepository(rules_path=mockrules).rules}
assert rules == {'S100', 'S120', 'S4727'} assert rules == {'S100', 'S120', 'S4727', 'S1033'}
def test_list_languages(mockrules: Path): def test_list_languages(mockrules: Path):

View File

@ -1,5 +1,5 @@
from rspec_tools.errors import InvalidArgumentError from rspec_tools.errors import InvalidArgumentError
from rspec_tools.utils import parse_and_validate_language_list, load_valid_languages, get_mapped_languages, get_labels_for_languages from rspec_tools.utils import parse_and_validate_language_list, load_valid_languages, get_mapped_languages, get_labels_for_languages, resolve_rule, validate_language, get_label_for_language
import pytest import pytest
def test_fails_when_no_languages_listed(): def test_fails_when_no_languages_listed():
@ -31,3 +31,29 @@ def test_labels_for_languages():
lang_list = ['java', 'apex', 'cfamily', 'ruby'] lang_list = ['java', 'apex', 'cfamily', 'ruby']
labels = ['java', 'slang', 'cfamily'] labels = ['java', 'slang', 'cfamily']
assert set(get_labels_for_languages(lang_list)) == set(labels) assert set(get_labels_for_languages(lang_list)) == set(labels)
def test_resolve_rule():
assert resolve_rule('S100') == 100
assert resolve_rule('S1234') == 1234
with pytest.raises(InvalidArgumentError):
resolve_rule('S12')
with pytest.raises(InvalidArgumentError):
resolve_rule('12')
with pytest.raises(InvalidArgumentError):
resolve_rule('SS13')
with pytest.raises(InvalidArgumentError):
resolve_rule('RSPEC-1343')
with pytest.raises(InvalidArgumentError):
resolve_rule(' S1343 ')
with pytest.raises(InvalidArgumentError):
resolve_rule('SXXXX')
with pytest.raises(InvalidArgumentError):
resolve_rule('S90000')
def test_label_for_language():
assert get_label_for_language('java') == 'java'
def test_validate_language():
validate_language('java')
with pytest.raises(InvalidArgumentError):
validate_language('russian')