RULEAPI-606: github action and underlying script for adding a new language to an existing rule
This commit is contained in:
parent
8d21500f3b
commit
5f8a2e7ec1
42
.github/workflows/add_language.yml
vendored
Normal file
42
.github/workflows/add_language.yml
vendored
Normal 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 }}"
|
@ -10,6 +10,7 @@ rspec-tools = {editable = true, path = "."}
|
||||
gitpython = "*"
|
||||
pygithub = "*"
|
||||
jsonschema = "*"
|
||||
fs = "*"
|
||||
|
||||
[dev-packages]
|
||||
pytest = ">=6.2.2"
|
||||
|
313
rspec-tools/Pipfile.lock
generated
313
rspec-tools/Pipfile.lock
generated
@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "307552df2c8cbbd087e576ba8c0bcea21c1151baf7f9eb3076e910a9063f66e4"
|
||||
"sha256": "a141a126414372441705df189b3013cdadd7fa27f3114dfc07264686c6291204"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@ -16,6 +16,13 @@
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"appdirs": {
|
||||
"hashes": [
|
||||
"sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41",
|
||||
"sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"
|
||||
],
|
||||
"version": "==1.4.4"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
|
||||
@ -25,11 +32,10 @@
|
||||
},
|
||||
"beautifulsoup4": {
|
||||
"hashes": [
|
||||
"sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35",
|
||||
"sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25",
|
||||
"sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"
|
||||
"sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf",
|
||||
"sha256:c23ad23c521d818955a4151a67d81580319d4bf548d3d49f4223ae041ff98891"
|
||||
],
|
||||
"version": "==4.9.3"
|
||||
"version": "==4.10.0"
|
||||
},
|
||||
"bs4": {
|
||||
"hashes": [
|
||||
@ -45,27 +51,86 @@
|
||||
],
|
||||
"version": "==2021.5.30"
|
||||
},
|
||||
"chardet": {
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
|
||||
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
|
||||
"sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d",
|
||||
"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": {
|
||||
"hashes": [
|
||||
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
|
||||
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
|
||||
"sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a",
|
||||
"sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==7.1.2"
|
||||
"version": "==8.0.1"
|
||||
},
|
||||
"deprecated": {
|
||||
"hashes": [
|
||||
"sha256:08452d69b6b5bc66e8330adde0a4f8642e969b9e1702904d137eeb29c8ffc771",
|
||||
"sha256:6d2de2de7931a968874481ef30208fd4e08da39177d61d3d4ebdf4366e7dbca1"
|
||||
"sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d",
|
||||
"sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"
|
||||
],
|
||||
"version": "==1.2.12"
|
||||
"version": "==1.2.13"
|
||||
},
|
||||
"fs": {
|
||||
"hashes": [
|
||||
"sha256:1d10cc8f9c55fbcf7b23775289a13f6796dca7acd5a135c379f49e87a56a7230",
|
||||
"sha256:caab4dc1561d63c92f36ee78976f6a4a01381830d8420ce34a78d4f1bb1dc95f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.4.13"
|
||||
},
|
||||
"gitdb": {
|
||||
"hashes": [
|
||||
@ -76,18 +141,19 @@
|
||||
},
|
||||
"gitpython": {
|
||||
"hashes": [
|
||||
"sha256:8621a7e777e276a5ec838b59280ba5272dd144a18169c36c903d8b38b99f750a",
|
||||
"sha256:c5347c81d232d9b8e7f47b68a83e5dc92e7952127133c5f2df9133f2c75a1b29"
|
||||
"sha256:dc0a7f2f697657acc8d7f89033e8b1ea94dd90356b2983bca89dc8d2ab3cc647",
|
||||
"sha256:df83fdf5e684fef7c6ee2c02fc68a5ceb7e7e759d08b694088d0cacb4eba59e5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.1.13"
|
||||
"version": "==3.1.24"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
|
||||
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
|
||||
"sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a",
|
||||
"sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"
|
||||
],
|
||||
"version": "==2.10"
|
||||
"markers": "python_version >= '3'",
|
||||
"version": "==3.2"
|
||||
},
|
||||
"jsonschema": {
|
||||
"hashes": [
|
||||
@ -97,33 +163,90 @@
|
||||
"index": "pypi",
|
||||
"version": "==3.2.0"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
|
||||
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
|
||||
],
|
||||
"version": "==2.20"
|
||||
},
|
||||
"pygithub": {
|
||||
"hashes": [
|
||||
"sha256:300bc16e62886ca6537b0830e8f516ea4bc3ef12d308e0c5aff8bdbd099173d4",
|
||||
"sha256:87afd6a67ea582aa7533afdbf41635725f13d12581faed7e3e04b1579c0c0627"
|
||||
"sha256:1bbfff9372047ff3f21d5cd8e07720f3dbfdaf6462fcaed9d815f528f1ba7283",
|
||||
"sha256:2caf0054ea079b71e539741ae56c5a95e073b81fa472ce222e81667381b9601b"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.54.1"
|
||||
"version": "==1.55"
|
||||
},
|
||||
"pyjwt": {
|
||||
"hashes": [
|
||||
"sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e",
|
||||
"sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96"
|
||||
"sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1",
|
||||
"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": {
|
||||
"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": {
|
||||
"hashes": [
|
||||
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
|
||||
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
|
||||
"sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
|
||||
"sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
|
||||
],
|
||||
"version": "==2.25.1"
|
||||
"version": "==2.26.0"
|
||||
},
|
||||
"rspec-tools": {
|
||||
"editable": true,
|
||||
@ -148,16 +271,23 @@
|
||||
"sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc",
|
||||
"sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b"
|
||||
],
|
||||
"markers": "python_version >= '3.0'",
|
||||
"version": "==2.2.1"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e",
|
||||
"sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7",
|
||||
"sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"
|
||||
],
|
||||
"markers": "python_version < '3.10'",
|
||||
"version": "==3.10.0.2"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c",
|
||||
"sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"
|
||||
"sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece",
|
||||
"sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.26.5"
|
||||
"version": "==1.26.7"
|
||||
},
|
||||
"wrapt": {
|
||||
"hashes": [
|
||||
@ -183,31 +313,32 @@
|
||||
},
|
||||
"mypy": {
|
||||
"hashes": [
|
||||
"sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e",
|
||||
"sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064",
|
||||
"sha256:28fb5479c494b1bab244620685e2eb3c3f988d71fd5d64cc753195e8ed53df7c",
|
||||
"sha256:2f9b3407c58347a452fc0736861593e105139b905cca7d097e413453a1d650b4",
|
||||
"sha256:33f159443db0829d16f0a8d83d94df3109bb6dd801975fe86bacb9bf71628e97",
|
||||
"sha256:3f2aca7f68580dc2508289c729bd49ee929a436208d2b2b6aab15745a70a57df",
|
||||
"sha256:499c798053cdebcaa916eef8cd733e5584b5909f789de856b482cd7d069bdad8",
|
||||
"sha256:4eec37370483331d13514c3f55f446fc5248d6373e7029a29ecb7b7494851e7a",
|
||||
"sha256:552a815579aa1e995f39fd05dde6cd378e191b063f031f2acfe73ce9fb7f9e56",
|
||||
"sha256:5873888fff1c7cf5b71efbe80e0e73153fe9212fafdf8e44adfe4c20ec9f82d7",
|
||||
"sha256:61a3d5b97955422964be6b3baf05ff2ce7f26f52c85dd88db11d5e03e146a3a6",
|
||||
"sha256:674e822aa665b9fd75130c6c5f5ed9564a38c6cea6a6432ce47eafb68ee578c5",
|
||||
"sha256:7ce3175801d0ae5fdfa79b4f0cfed08807af4d075b402b7e294e6aa72af9aa2a",
|
||||
"sha256:9743c91088d396c1a5a3c9978354b61b0382b4e3c440ce83cf77994a43e8c521",
|
||||
"sha256:9f94aac67a2045ec719ffe6111df543bac7874cee01f41928f6969756e030564",
|
||||
"sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49",
|
||||
"sha256:abf7e0c3cf117c44d9285cc6128856106183938c68fd4944763003decdcfeb66",
|
||||
"sha256:b09669bcda124e83708f34a94606e01b614fa71931d356c1f1a5297ba11f110a",
|
||||
"sha256:cd07039aa5df222037005b08fbbfd69b3ab0b0bd7a07d7906de75ae52c4e3119",
|
||||
"sha256:d23e0ea196702d918b60c8288561e722bf437d82cb7ef2edcd98cfa38905d506",
|
||||
"sha256:d65cc1df038ef55a99e617431f0553cd77763869eebdf9042403e16089fe746c",
|
||||
"sha256:d7da2e1d5f558c37d6e8c1246f1aec1e7349e4913d8fb3cb289a35de573fe2eb"
|
||||
"sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9",
|
||||
"sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a",
|
||||
"sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9",
|
||||
"sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e",
|
||||
"sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2",
|
||||
"sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212",
|
||||
"sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b",
|
||||
"sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885",
|
||||
"sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150",
|
||||
"sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703",
|
||||
"sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072",
|
||||
"sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457",
|
||||
"sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e",
|
||||
"sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0",
|
||||
"sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb",
|
||||
"sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97",
|
||||
"sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8",
|
||||
"sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811",
|
||||
"sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6",
|
||||
"sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de",
|
||||
"sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504",
|
||||
"sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921",
|
||||
"sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.812"
|
||||
"version": "==0.910"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
@ -218,17 +349,17 @@
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5",
|
||||
"sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
|
||||
"sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7",
|
||||
"sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"
|
||||
],
|
||||
"version": "==20.9"
|
||||
"version": "==21.0"
|
||||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
|
||||
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
|
||||
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
|
||||
"sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
|
||||
],
|
||||
"version": "==0.13.1"
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"py": {
|
||||
"hashes": [
|
||||
@ -246,11 +377,11 @@
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9",
|
||||
"sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"
|
||||
"sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89",
|
||||
"sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==6.2.2"
|
||||
"version": "==6.2.5"
|
||||
},
|
||||
"rspec-tools": {
|
||||
"editable": true,
|
||||
@ -263,48 +394,14 @@
|
||||
],
|
||||
"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": {
|
||||
"hashes": [
|
||||
"sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497",
|
||||
"sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342",
|
||||
"sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"
|
||||
"sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e",
|
||||
"sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7",
|
||||
"sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"
|
||||
],
|
||||
"version": "==3.10.0.0"
|
||||
"markers": "python_version < '3.10'",
|
||||
"version": "==3.10.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ from pathlib import Path
|
||||
import click
|
||||
from rspec_tools.checklinks import check_html_links
|
||||
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.validation.metadata import validate_metadata
|
||||
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')
|
||||
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()
|
||||
@click.argument('rules', nargs=-1)
|
||||
|
@ -1,6 +1,7 @@
|
||||
from rspec_tools.errors import GitError
|
||||
from rspec_tools.errors import InvalidArgumentError
|
||||
import click
|
||||
import tempfile
|
||||
import fs
|
||||
from git import Repo
|
||||
from git.remote import PushInfo
|
||||
from github import Github
|
||||
@ -8,7 +9,7 @@ from github.PullRequest import PullRequest
|
||||
from pathlib import Path
|
||||
from typing import Final, Iterable, Optional, Callable
|
||||
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
|
||||
|
||||
@ -45,6 +46,19 @@ def create_new_rule(languages: str, token: str, user: Optional[str]):
|
||||
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)
|
||||
|
||||
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:
|
||||
''' Create a new Rule in a repository following the official Github 'rspec' repository structure.'''
|
||||
MASTER_BRANCH: Final[str] = 'master'
|
||||
@ -69,7 +83,6 @@ class RuleCreator:
|
||||
for key, value in configuration.items():
|
||||
split_key = key.split('.')
|
||||
config_writer.set_value(*split_key, value)
|
||||
|
||||
|
||||
def reserve_rule_number(self) -> int:
|
||||
'''Reserve an id on the id counter branch of the repository.'''
|
||||
@ -77,7 +90,7 @@ class RuleCreator:
|
||||
counter_file_path = Path(self.repository.working_dir).joinpath(self.ID_COUNTER_FILENAME)
|
||||
counter = int(counter_file_path.read_text())
|
||||
counter_file_path.write_text(str(counter + 1))
|
||||
|
||||
|
||||
self.repository.index.add([str(counter_file_path)])
|
||||
self.repository.index.commit('Increment RSPEC ID counter')
|
||||
|
||||
@ -87,6 +100,31 @@ class RuleCreator:
|
||||
click.echo(f'Reserved Rule ID S{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:
|
||||
'''Create all the files required for a new rule.'''
|
||||
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)
|
||||
|
||||
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}')
|
||||
|
||||
def _create_pull_request(self, githubApi: Callable[[Optional[str]], Github], branch_name: str, title: str, body: str, labels: Iterable[str], user: Optional[str]):
|
||||
repository_url = extract_repository_name(self.origin_url)
|
||||
github = githubApi(user)
|
||||
github_repo = github.get_repo(repository_url)
|
||||
first_lang = next(iter(languages))
|
||||
pull_request = github_repo.create_pull(
|
||||
title=f'Create rule S{rule_number}',
|
||||
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).',
|
||||
title=title,
|
||||
body=body,
|
||||
head=branch_name, base=self.MASTER_BRANCH,
|
||||
draft=True, maintainer_can_modify=True
|
||||
)
|
||||
@ -156,9 +190,33 @@ class RuleCreator:
|
||||
pull_request.add_to_assignees(login)
|
||||
pull_request.add_to_labels(*labels)
|
||||
click.echo(f'Pull request assigned to {login}')
|
||||
|
||||
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
|
||||
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.'''
|
||||
|
@ -1,6 +1,9 @@
|
||||
from rspec_tools.errors import InvalidArgumentError
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import re
|
||||
import tempfile
|
||||
import json
|
||||
|
||||
SUPPORTED_LANGUAGES_FILENAME = '../supported_languages.adoc'
|
||||
LANG_TO_LABEL = {'abap': 'abap',
|
||||
@ -41,6 +44,19 @@ def copy_directory_content(src:Path, dest:Path):
|
||||
else:
|
||||
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():
|
||||
with open(SUPPORTED_LANGUAGES_FILENAME, 'r') as supported_langs_file:
|
||||
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.")
|
||||
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):
|
||||
labels = [LANG_TO_LABEL[lang] for lang in lang_list]
|
||||
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))
|
||||
|
||||
|
@ -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".
|
||||
|
@ -0,0 +1,4 @@
|
||||
=== Message
|
||||
|
||||
Explicitly invoke the template version of this function.
|
||||
|
@ -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": [
|
||||
|
||||
]
|
||||
}
|
53
rspec-tools/tests/resources/rules/S1033/cfamily/rule.adoc
Normal file
53
rspec-tools/tests/resources/rules/S1033/cfamily/rule.adoc
Normal 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[]
|
2
rspec-tools/tests/resources/rules/S1033/metadata.json
Normal file
2
rspec-tools/tests/resources/rules/S1033/metadata.json
Normal file
@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
@ -4,8 +4,10 @@ from pathlib import Path
|
||||
from typing import Optional
|
||||
from unittest.mock import Mock, patch
|
||||
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
|
||||
def git_config():
|
||||
@ -16,20 +18,18 @@ def git_config():
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def mock_rspec_repo(tmpdir):
|
||||
def mock_rspec_repo(tmpdir, mockrules: Path):
|
||||
repo_dir = tmpdir.mkdir("mock_rspec")
|
||||
repo = Repo.init(str(repo_dir))
|
||||
repo.init()
|
||||
rules_dir = repo_dir.join('rules')
|
||||
shutil.copytree(mockrules, rules_dir)
|
||||
|
||||
with repo.config_writer() as config_writer:
|
||||
config_writer.set_value('user', 'name', 'originuser')
|
||||
config_writer.set_value('user', 'email', 'originuser@mock.mock')
|
||||
|
||||
rules_dir = repo_dir.mkdir('rules')
|
||||
# create a file just to have a "rules" directory
|
||||
gitignore = rules_dir.join('.gitignore')
|
||||
gitignore.ensure()
|
||||
repo.index.add([str(gitignore)])
|
||||
repo.git.add('--all')
|
||||
repo.index.commit('init rules')
|
||||
|
||||
# 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
|
||||
with pytest.raises(InvalidArgumentError):
|
||||
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');
|
||||
|
@ -8,7 +8,7 @@ from rspec_tools.rules import RulesRepository
|
||||
def test_list_rules(mockrules: Path):
|
||||
'''Check that rules are all listed.'''
|
||||
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):
|
||||
|
@ -1,5 +1,5 @@
|
||||
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
|
||||
|
||||
def test_fails_when_no_languages_listed():
|
||||
@ -31,3 +31,29 @@ def test_labels_for_languages():
|
||||
lang_list = ['java', 'apex', 'cfamily', 'ruby']
|
||||
labels = ['java', 'slang', 'cfamily']
|
||||
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')
|
||||
|
Loading…
x
Reference in New Issue
Block a user