VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Մենք ակտիվորեն օգտագործում ենք հարթակը մեր աշխատանքում soundQube կոդի որակը բարձր մակարդակի վրա պահպանելու համար: Գրված նախագծերից մեկը ինտեգրելիս VueJs+Typescript, խնդիրներ են առաջացել։ Ուստի կցանկանայի ավելի մանրամասն պատմել, թե ինչպես մեզ հաջողվեց լուծել դրանք։

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Այս հոդվածում մենք կխոսենք, ինչպես վերևում գրել եմ, SonarQube հարթակի մասին: Մի փոքր տեսություն. ինչ է դա ընդհանրապես, նրանց համար, ովքեր առաջին անգամ են լսում դրա մասին.

soundQube (նախկին Սոնար) բաց կոդով հարթակ է շարունակական ստուգման և կոդի որակի չափման համար:
Աջակցում է կոդի վերլուծությանը և սխալի հայտնաբերմանը` համաձայն MISRA C, MISRA C++, MITER/CWE և CERT Secure Coding Standards ծրագրավորման ստանդարտների կանոնների: Այն կարող է նաև ճանաչել սխալները OWASP Top 10 և CWE/SANS Top 25 ծրագրավորման սխալների ցուցակներից:
Չնայած այն հանգամանքին, որ պլատֆորմն օգտագործում է տարբեր պատրաստի գործիքներ, SonarQube-ն արդյունքները նվազեցնում է մեկ վահանակի վրա՝ պահելով գործարկումների պատմություն և դրանով իսկ թույլ տալով տեսնել ծրագրային ապահովման որակի փոփոխությունների ընդհանուր միտումը մշակման ընթացքում:

Լրացուցիչ մանրամասները կարող եք գտնել այստեղ պաշտոնական կայքը

Աջակցվում են մեծ թվով ծրագրավորման լեզուներ: Դատելով վերը նշված հղումից ստացված տեղեկություններից՝ դրանք ավելի քան 25 լեզու են։ Հատուկ լեզվին աջակցելու համար դուք պետք է տեղադրեք համապատասխան փլագինը: Համայնքի տարբերակը ներառում է պլագին հետ աշխատելու համար Javascript (ներառյալ Typeсript), չնայած վիքին հակառակն է ասում։ Հետևում Javascript plugin պատասխաններ SonarJS, Typescript-ի համար SonarTS համապատասխանաբար:

Պաշտոնական հաճախորդը օգտագործվում է ծածկույթի մասին տեղեկատվություն ուղարկելու համար sonarqube-սկաներ, որը, օգտագործելով կարգավորումները config-ֆայլ, այս տվյալները ուղարկում է սերվերին soundQube հետագա համախմբման և համախմբման համար:

Համար Javascript կա npm փաթաթան. Այսպիսով, եկեք սկսենք քայլ առ քայլ իրականացումը soundQube в Vue- նախագծի օգտագործումը Տպագիր.

Սերվերը տեղակայելու համար soundQube օգտվենք դոկտոր-կոմպոզիցիա.

sonar.yaml:

version: '1'
    services:
        simplesample-sonar:
            image: sonarqube:lts
            ports:
                - 9001:9000
                - 9092:9092
            network_mode: bridge

Գործարկել:

docker-compose -f sonar.yml up

Այն ժամանակվանից soundQube հասանելի կլինի հետևյալ հասցեով՝ http://localhost:9001 .

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ
Դրանում դեռ նախագծեր չկան, և դա արդարացի է: Մենք կշտկենք այս իրավիճակը։ Ես վերցրեցի պաշտոնական օրինակ նախագիծը VueJS+TS+Jest. Եկեք այն թեքենք դեպի ինքներս մեզ.

git clone https://github.com/vuejs/vue-test-utils-typescript-example.git

Սկզբում մենք պետք է տեղադրենք հաճախորդը soundQubeորը կոչվում է սոնար-սկաներհամար npm կա փաթաթան:

yarn add sonarqube-scanner

Եվ անմիջապես ավելացրեք հրամանը Հաղորդագրություն աշխատել դրա հետ:

package.json:

{
 … 
   scripts: {
      ...
      "sonar": "sonar-scanner"
      ...
   },
 …
}

Հաջորդը, որպեսզի սկաները աշխատի, դուք պետք է սահմանեք նախագծի կարգավորումները հատուկ ֆայլում: Սկսենք հիմունքներից:

sonar-project.properties:

sonar.host.url=http://localhost:9001

sonar.projectKey=test-project-vuejs-ts
sonar.projectName=Test Application (VueJS+TS)

sonar.sources=src
# sonar.tests=
sonar.test.inclusions=src/**/*tests*/**
sonar.sourceEncoding=UTF-8

  • sonar.host.url - հասցեն Սոնար«Ա;
  • sonar.projectKey - եզակի նախագծի նույնացուցիչ սերվերի վրա Սոնար«Ա;
  • sonar.projectName – իր անունը, այն կարող է փոխվել ցանկացած ժամանակ, քանի որ նախագիծը նույնականացվում է projectKey;
  • sonar.աղբյուրներ – թղթապանակ աղբյուրներով, սովորաբար սա src, բայց կարող է լինել ամեն ինչ: Այս թղթապանակը տեղադրված է արմատային թղթապանակի համեմատ, որն այն թղթապանակն է, որտեղից գործարկվում է սկաները.
  • sonar.tests – պարամետր, որը համընկնում է նախորդի հետ: Սա այն թղթապանակն է, որտեղ գտնվում են թեստերը: Այս նախագծում նման թղթապանակ չկա, և թեստը գտնվում է թղթապանակում փորձարկվող բաղադրիչի կողքին:փորձարկում', այնպես որ մենք առայժմ անտեսելու ենք այն և կօգտագործենք հաջորդ պարամետրը.
  • sonar.test.inclusions – դիմակ օգտագործելով թեստերի ուղին, կարող են լինել ստորակետերով առանձնացված մի քանի տարրեր.
  • sonar.sourceԿոդավորում - կոդավորում աղբյուրի ֆայլերի համար:

Սկաների առաջին գործարկման համար ամեն ինչ պատրաստ է, բացառությամբ նախորդ հիմնական գործողությունի՝ ինքնին փորձարկման շարժիչի գործարկումը, որպեսզի այն կարողանա տեղեկատվություն ստեղծել ծածկույթի մասին, որը սկաները հետագայում կօգտագործի:

Բայց դա անելու համար դուք պետք է կարգավորեք թեստային շարժիչը, որպեսզի ստեղծի այս տեղեկատվությունը: Այս նախագծում փորձարկման շարժիչն է Ջեսթ. Իսկ դրա կարգավորումները գտնվում են ֆայլի համապատասխան բաժնում package.json.

Եկեք ավելացնենք այս կարգավորումները.

"collectCoverage": true,
"collectCoverageFrom": [
      "src/**/*",
      "!src/main.ts",
      "!src/App.vue",
      "!src/**/*.d.*",
      "!src/**/*__tests__*"
],

Այսինքն՝ մենք ինքնին դրոշ ենք սահմանել ծածկույթը հաշվարկելու անհրաժեշտության և աղբյուրի (բացառությունների հետ մեկտեղ), որի հիման վրա այն կձևավորվի։

Հիմա եկեք կատարենք թեստը.

yarn test

Մենք կտեսնենք հետևյալը.

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Պատճառն այն է, որ բուն բաղադրիչում կոդ չկա։ Եկեք սա շտկենք:

HelloWorld.vue:

...
methods: {
    calc(n) {
      return n + 1;
    }
  },
mounted() {
  this.msg1 = this.msg + this.calc(1);
},
...

Սա բավարար կլինի ծածկույթը հաշվարկելու համար:

Թեստը վերսկսելուց հետո մենք կհամոզվենք հետևյալում.

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Էկրանի վրա մենք պետք է տեսնենք ծածկույթի մասին տեղեկատվություն, և նախագծի թղթապանակում կստեղծվի թղթապանակ լուսաբանում թեստային ծածկույթի տեղեկատվությամբ ունիվերսալ ձևաչափով LCOV (LTP GCOV ընդլայնում).

Գկով անվճար բաշխված կոմունալ ծրագիր է՝ կոդերի ծածկույթն ուսումնասիրելու համար: Gcov-ը ստեղծում է ծրագրում յուրաքանչյուր հայտարարության համար կատարվող կատարումների ճշգրիտ թիվը և թույլ է տալիս ծանոթագրություններ ավելացնել սկզբնաղբյուրին: Gcov-ը գալիս է որպես ստանդարտ կոմունալ, որպես GCC փաթեթի մաս:
Լկով - գրաֆիկական ինտերֆեյս gcov-ի համար: Այն հավաքում է gcov ֆայլեր մի քանի աղբյուր ֆայլերի համար և արտադրում է HTML էջերի մի շարք կոդով և ծածկույթի մասին տեղեկություններով: Էջերը ստեղծվում են նաև նավիգացիան հեշտացնելու համար: Lcov-ն աջակցում է տողերի, ֆունկցիաների և ճյուղերի ծածկույթին:

Թեստերն ավարտելուց հետո ծածկույթի մասին տեղեկատվությունը կտեղադրվի այնտեղ ծածկույթ/lcov.info.
Պետք է ասել Սոնար― Որտեղի՞ց կարող եմ ստանալ։ Հետևաբար, եկեք ավելացնենք հետևյալ տողերը նրա կազմաձևման ֆայլին. Բայց կա մի կետ՝ նախագծերը կարող են լինել բազմալեզու, այսինքն՝ թղթապանակում src կան մի քանի ծրագրավորման լեզուների աղբյուրի կոդեր և պատկանելություն մեկ կամ մյուսի հետ, և իր հերթին, այս կամ այն ​​պլագինի օգտագործումը որոշվում է դրա ընդլայնմամբ: Իսկ ծածկույթի մասին տեղեկատվությունը կարող է պահվել տարբեր վայրերում տարբեր ծրագրավորման լեզուների համար, այնպես որ յուրաքանչյուր լեզու ունի իր բաժինը սա կարգավորելու համար: Մեր նախագիծը օգտագործում է Տպագիր, ուստի մեզ անհրաժեշտ է կարգավորումների բաժին հենց դրա համար.

sonar-project.properties:

sonar.typescript.coveragePlugin=lcov
sonar.typescript.lcov.reportPaths=coverage/lcov.info

Ամեն ինչ պատրաստ է սկաների առաջին գործարկման համար: Նշեմ, որ նախագիծն է Սոնար«e»-ն ստեղծվում է ավտոմատ կերպով, երբ առաջին անգամ գործարկում եք սկաները տվյալ նախագծի համար: Հետագա ժամանակներում տեղեկատվություն կկուտակվի՝ ժամանակի ընթացքում ծրագրի պարամետրերի փոփոխությունների դինամիկան տեսնելու համար:

Այսպիսով, եկեք օգտագործենք ավելի վաղ ստեղծված հրամանը package.json:

yarn run sonar 

Նշում: կարող եք նաև օգտագործել պարամետրը -X ավելի մանրամասն անտառահատումների համար:

Եթե ​​սկաները գործարկվել է առաջին անգամ, ապա առաջինը կներբեռնվի սկաների երկուական տարբերակը: Դրանից հետո այն սկսում է և սկսում սկանավորել սերվերը Սոնար«տեղադրված փլագինների համար՝ դրանով իսկ հաշվարկելով աջակցվող լեզուն։ Բեռնվում են նաև դրա գործունեության տարբեր այլ պարամետրեր. որակի պրոֆիլներ, ակտիվ կանոններ, չափումների պահեստ, սերվերի կանոններ.

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Նշում: Այս հոդվածի շրջանակներում մենք դրանց մասին մանրամասն չենք անդրադառնա, բայց միշտ կարող եք կապվել պաշտոնական աղբյուրների հետ:

Հաջորդը, թղթապանակի վերլուծությունը սկսվում է src աղբյուրի ֆայլերի առկայության համար բոլոր (եթե կոնկրետ մեկը հստակորեն նշված չէ) աջակցվող լեզուների համար՝ դրանց հետագա ինդեքսավորմամբ:

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Հաջորդը գալիս են զանազան այլ վերլուծություններ, որոնց վրա մենք չենք կենտրոնանում այս հոդվածում (օրինակ, օրինակ՝ լինտինգ, կոդի կրկնօրինակման հայտնաբերում և այլն):

Սկաների աշխատանքի հենց վերջում հավաքված ամբողջ տեղեկատվությունը հավաքվում է, արխիվացվում և ուղարկվում սերվեր:

Դրանից հետո մենք արդեն կարող ենք տեսնել, թե ինչ է տեղի ունեցել վեբ ինտերֆեյսում.

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Ինչպես տեսնում ենք, ինչ-որ բան աշխատել է, և նույնիսկ ինչ-որ լուսաբանում է ցույց տալիս, բայց դա չի համապատասխանում մերին Ջեսթ- հաշվետվություն.

Եկեք պարզենք այն: Եկեք ավելի մանրամասն նայենք նախագծին, սեղմենք ծածկույթի արժեքի վրա և «ընկնենք» մանրամասն ֆայլի զեկույցի մեջ.

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Այստեղ մենք տեսնում ենք, բացի հիմնական, ուսումնասիրված ֆայլից HelloWorld.vue, կա նաև ֆայլ հիմնական.ց, որը փչացնում է լուսաբանման ողջ պատկերը։ Բայց ինչպե՞ս է, որ մենք դա բացառեցինք ծածկույթի հաշվարկից։ Այո, ամեն ինչ ճիշտ է, բայց դա մակարդակի վրա էր Ջեսթ, բայց սկաները ինդեքսավորեց այն, ուստի այն հայտնվեց իր հաշվարկներում:

Եկեք շտկենք սա.

sonar-project.properties:

...
sonar.exclusions=src/main.ts
...

Ուզում եմ պարզաբանում անել՝ բացի այս պարամետրում նշված թղթապանակներից, ավելացվում են նաև պարամետրում թվարկված բոլոր թղթապանակները։ sonar.test.inclusions.

Սկաների գործարկումից հետո մենք տեսնում ենք ճիշտ տեղեկատվությունը.

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Եկեք նայենք հաջորդ կետին - Որակի պրոֆիլներ. Վերևում խոսեցի աջակցության մասին Սոնարմի քանի լեզուներով միաժամանակ: Սա հենց այն է, ինչ մենք տեսնում ենք։ Բայց մենք գիտենք, որ մեր նախագիծը գրված է TS, ուրեմն ինչու լարել սկաները անհարկի մանիպուլյացիաներով և ստուգումներով։ Մենք կսահմանենք վերլուծության լեզուն՝ կոնֆիգուրացիայի ֆայլին ավելացնելով ևս մեկ պարամետր ՍոնարA:

sonar-project.properties:

...
sonar.language=ts
...

Եկեք նորից գործարկենք սկաները և տեսնենք արդյունքը.

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Ծածկույթն ամբողջությամբ վերացել էր։

Եթե ​​նայենք սկաների մատյանին, ապա կարող ենք տեսնել հետևյալ տողը.

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Այսինքն՝ մեր նախագծի ֆայլերը պարզապես չեն ինդեքսավորվել։

Իրավիճակը հետևյալն է՝ պաշտոնապես աջակցություն VueJs plugin-ում է SonarJSով է պատասխանատու Javascript.

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Բայց այս աջակցությունը plugin-ում չէ SonarTS համար TS, որի մասին պաշտոնական տոմս է բացվել bug tracker-ում ՍոնարA:

  1. https://jira.sonarsource.com/browse/MMF-1441
  2. https://github.com/SonarSource/SonarJS/issues/1281

Ահա SonarQube ծրագրավորողների ներկայացուցիչներից մեկի պատասխանները, որոնք հաստատում են այս փաստը։

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Բայց մեզ մոտ ամեն ինչ ստացվեց, դու առարկում ես։ Այո, այդպես է, եկեք մի փոքր փորձենք «կոտրել».
Եթե ​​կա աջակցություն .vue- ֆայլեր Սոնար― Օ՜, ուրեմն եկեք փորձենք նրան ասել, որ դրանք համարի Տպագիր.

Ավելացնենք պարամետր.

sonar-project.properties:

...
sonar.typescript.file.suffixes=.ts,.tsx,.vue
...

Եկեք գործարկենք սկաները.

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Եվ, voila, ամեն ինչ վերադարձել է նորմալ, և միայն մեկ պրոֆիլով Տպագիր. Այսինքն՝ մենք կարողացանք խնդիրը լուծել ի աջակցություն VueJs+TS համար soundQube.

Փորձենք ավելի հեռուն գնալ և մի փոքր բարելավել լուսաբանման տեղեկատվությունը։

Այն, ինչ մենք արել ենք մինչ այժմ.

  • ավելացվել է նախագծին Սոնար-սկաներ;
  • ստեղծել Ջեսթ ծածկույթի մասին տեղեկատվություն ստեղծելու համար;
  • կազմաձևված Սոնար-սկաներ;
  • լուծեց աջակցության խնդիրը .vue-ֆայլեր + Տպագիր.

Բացի թեստային ծածկույթից, կան կոդի որակի այլ հետաքրքիր և օգտակար չափանիշներ, օրինակ՝ կոդերի կրկնօրինակումը և նախագծի տողերի քանակը (կոդերի բարդության հետ կապված գործակիցների հաշվարկում):

հետ աշխատելու համար հավելվածի ընթացիկ ներդրման մեջ TS (SonarTS) չի աշխատի CPD (Copy Paste Detector) և կոդերի տողերի հաշվում .vue- ֆայլեր.

Կոդի կրկնօրինակման սինթետիկ իրավիճակ ստեղծելու համար պարզապես կրկնօրինակեք բաղադրիչի ֆայլը այլ անունով և ավելացրեք այն կոդի մեջ: հիմնական.ց կեղծ գործառույթ և կրկնօրինակեք այն այլ անունով: Ստուգելու համար կրկնօրինակումը, ինչպես նշված է .vueեւ ներս - ֆայլեր.

main.ts:

...
function name(params:string): void {
  console.log(params);
}
...

Դա անելու համար դուք պետք է ժամանակավորապես մեկնաբանեք կազմաձևման տողը.

sonar-project.properties:

...
sonar.exclusions=src/main.ts
...

Եկեք վերագործարկենք սկաները փորձարկման հետ մեկտեղ.

yarn test && yarn run sonar

Իհարկե, մեր լուսաբանումը կընկնի, բայց հիմա դա մեզ չի հետաքրքրում։

Կոդի տողերի կրկնօրինակման առումով մենք կտեսնենք.

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Ստուգելու համար մենք կօգտագործենք ԿԿԲ- օգտակար - jscpd:

npx jscpd src

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Կոդի տողերի համար՝

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Միգուցե դա կլուծվի հավելումների հետագա տարբերակներում SonarJS (TS). Ցանկանում եմ նշել, որ նրանք աստիճանաբար սկսում են այս երկու պլագինները միավորել մեկի մեջ SonarJS, որը կարծում եմ ճիշտ է։

Այժմ ես ուզում էի դիտարկել ծածկույթի մասին տեղեկատվության բարելավման տարբերակը:

Առայժմ մենք կարող ենք տեսնել թեստային ծածկույթը տոկոսային արտահայտությամբ ամբողջ նախագծի և մասնավորապես ֆայլերի համար: Բայց այս ցուցանիշը հնարավոր է ընդլայնել քանակի մասին տեղեկություններով միավոր- թեստեր նախագծի համար, ինչպես նաև ֆայլերի համատեքստում:

Կա գրադարան, որը կարող է Ջեսթ- հաշվետվության ձևաչափի վերածել ՍոնարA:
ընդհանուր թեստի տվյալներ - https://docs.sonarqube.org/display/SONAR/Generic+Test+Data.

Եկեք տեղադրենք այս գրադարանը մեր նախագծում.

yarn add jest-sonar-reporter

Եվ ավելացրեք այն կոնֆիգուրացիայի մեջ Ջեսթ:

package.json:

…
"testResultsProcessor": "jest-sonar-reporter"
…

Հիմա եկեք կատարենք թեստը.

yarn test

Որից հետո ֆայլ կստեղծվի նախագծի արմատում test-report.xml.

Եկեք օգտագործենք այն կոնֆիգուրացիայի մեջ ՍոնարA:

sonar-project.properties:

…
sonar.testExecutionReportPaths=test-report.xml
…

Եվ վերագործարկեք սկաները.

yarn run sonar

Տեսնենք, թե ինչ է փոխվել ինտերֆեյսի մեջ ՍոնարA:

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Եվ ոչինչ չի փոխվել։ Բանն այն է, որ Sonar-ը Jest զեկույցում նկարագրված ֆայլերը ֆայլեր չի համարում միավոր- թեստեր. Այս իրավիճակը շտկելու համար մենք օգտագործում ենք կազմաձևման պարամետրը Սոնար sonar.tests, որում մենք հստակորեն նշելու ենք թեստերով թղթապանակները (մենք առայժմ ունենք միայն մեկը).

sonar-project.properties:

…
sonar.tests=src/components/__tests__
…

Եկեք վերագործարկենք սկաները.

yarn run sonar

Տեսնենք, թե ինչ է փոխվել ինտերֆեյսի մեջ.

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Հիմա մենք տեսանք մեր թիվը միավոր- թեստեր և ձախողվելով ներս սեղմելով, մենք կարող ենք տեսնել այս թվի բաշխումը նախագծի ֆայլերի միջև.

VueJS+TS նախագծի ինտեգրում SonarQube-ի հետ

Ամփոփում

Այսպիսով, մենք դիտարկեցինք շարունակական վերլուծության գործիք soundQube. Մենք հաջողությամբ ինտեգրեցինք դրա մեջ գրված մի նախագիծ VueJs+TS. Շտկվել են համատեղելիության որոշ խնդիրներ: Մենք ավելացրել ենք թեստի ծածկույթի ցուցիչի տեղեկատվական բովանդակությունը։ Այս հոդվածում մենք ուսումնասիրեցինք կոդի որակի չափանիշներից միայն մեկը (գուցե հիմնականներից մեկը), բայց soundQube աջակցում է որակի այլ չափանիշներին, ներառյալ անվտանգության փորձարկումը: Բայց այս հատկանիշներից ոչ բոլորն են ամբողջությամբ հասանելի համայնք- տարբերակները. Հետաքրքիր և օգտակար հատկություններից մեկը ինտեգրումն է soundQube կոդերի պահեստի կառավարման տարբեր համակարգերով, ինչպիսիք են GitLab-ը և BitBucket-ը: Կանխել միաձուլման pull(միաձուլման) հարցում«a դեպի պահեստի հիմնական մասնաճյուղ, երբ ծածկույթը դեգրադացված է: Բայց սա պատմություն է բոլորովին այլ հոդվածի համար։

PS: Այն ամենը, ինչ նկարագրված է հոդվածում կոդի տեսքով, հասանելի է իմ պատառաքաղը.

Հարցմանը կարող են մասնակցել միայն գրանցված օգտվողները։ Մուտք գործել, խնդրում եմ:

Դուք օգտվո՞ւմ եք SonarQube հարթակից.

  • 26,3%Այո 5

  • 15,8%No3

  • 15,8%Ես լսել եմ այս հարթակի մասին և ուզում եմ օգտագործել3

  • 10,5%Ես լսել եմ այս հարթակի մասին և չեմ ուզում օգտագործել2

  • 0,0%Ես օգտագործում եմ այլ հարթակ0

  • 31,6%Ես առաջին անգամ եմ լսում նրա մասին6

Քվեարկել է 19 օգտատեր։ 3 օգտատեր ձեռնպահ է մնացել։

Source: www.habr.com

Добавить комментарий