ஜாவா திட்டத்தை சோனாடைப் மேவன் மத்திய களஞ்சியத்தில் வெளியிட கிரேடில் மற்றும் கிதுப் செயல்களைப் பயன்படுத்துதல்

இந்தக் கட்டுரையில், கிரடில் பில்டரைப் பயன்படுத்தி சோனாடைப் மேவன் மத்திய களஞ்சியத்தில் புதிதாக ஒரு ஜாவா கலைப்பொருளை கிதுப் செயல்கள் மூலம் வெளியிடும் செயல்முறையை நான் கூர்ந்து கவனிக்க விரும்புகிறேன்.

ஒரே இடத்தில் சாதாரண டுடோரியல் இல்லாததால் இந்தக் கட்டுரையை எழுத முடிவு செய்தேன். அனைத்து தகவல்களும் பல்வேறு ஆதாரங்களில் இருந்து துண்டு துண்டாக சேகரிக்கப்பட வேண்டும், மேலும், முற்றிலும் புதியவை அல்ல. யார் கவலைப்படுகிறார்கள், பூனையின் கீழ் வரவேற்கிறோம்.

Sonatype இல் ஒரு களஞ்சியத்தை உருவாக்குதல்

சோனாடைப் மேவன் சென்ட்ரலில் ஒரு களஞ்சியத்தை உருவாக்குவது முதல் படி. இதற்காக நாங்கள் செல்கிறோம் இங்கே, பதிவுசெய்து புதிய பணியை உருவாக்கவும், ஒரு களஞ்சியத்தை உருவாக்கும்படி கேட்கிறோம். நாங்கள் எங்களில் ஓட்டுகிறோம் GroupId திட்டம், திட்ட URL திட்ட இணைப்பு மற்றும் SCM url திட்டம் அமைந்துள்ள பதிப்பு கட்டுப்பாட்டு அமைப்புக்கான இணைப்பு. GroupId இங்கே com.example, com.example.domain, com.example.testsupport என்ற படிவத்தில் இருக்க வேண்டும், மேலும் உங்கள் கிட்ஹப்பிற்கான இணைப்பு வடிவத்திலும் இருக்கலாம்: github.com/yourusername -> io.github.yourusername. எந்தவொரு சந்தர்ப்பத்திலும், இந்த டொமைன் அல்லது சுயவிவரத்தின் உரிமையை நீங்கள் சரிபார்க்க வேண்டும். நீங்கள் ஒரு கிதுப் சுயவிவரத்தைக் குறிப்பிட்டால், விரும்பிய பெயருடன் ஒரு பொது களஞ்சியத்தை உருவாக்கும்படி கேட்கப்படுவீர்கள்.

உறுதிப்படுத்திய சிறிது நேரத்திற்குப் பிறகு, உங்கள் GroupId உருவாக்கப்படும், நாங்கள் அடுத்த படியான Gradle கட்டமைப்புக்கு செல்லலாம்.

கிரேடில் கட்டமைக்கிறது

எழுதும் நேரத்தில், கலைப்பொருளை வெளியிட உதவும் கிரேடில் செருகுநிரல்களை நான் காணவில்லை. இந்த நான் கண்டறிந்த ஒரே செருகுநிரல், இருப்பினும், ஆசிரியர் அதை மேலும் ஆதரிக்க மறுத்துவிட்டார். எனவே, இதைச் செய்வது மிகவும் கடினம் அல்ல என்பதால், எல்லாவற்றையும் நானே செய்ய முடிவு செய்தேன்.

முதலில் கண்டுபிடிக்க வேண்டியது சோனாடைப்பின் வெளியீட்டிற்கான தேவைகள். அவை பின்வருமாறு:

  • மூல குறியீடுகள் மற்றும் JavaDoc கிடைப்பது, அதாவது. கலந்து கொள்ள வேண்டும் -sources.jar и-javadoc.jar கோப்புகள். ஆவணத்தில் கூறப்பட்டுள்ளபடி, மூலக் குறியீடுகள் அல்லது ஆவணங்களை வழங்க முடியாவிட்டால், நீங்கள் ஒரு போலி உருவாக்கலாம் -sources.jar அல்லது -javadoc.jar தேர்வில் தேர்ச்சி பெற எளிய README உடன்.
  • அனைத்து கோப்புகளும் கையொப்பமிடப்பட வேண்டும் GPG/PGPமற்றும் .asc கையொப்பம் கொண்ட கோப்பு ஒவ்வொரு கோப்பிலும் சேர்க்கப்பட வேண்டும்.
  • கிடைக்கும் pom கோப்பு
  • சரியான மதிப்புகள் groupId, artifactId и version. பதிப்பு ஒரு தன்னிச்சையான சரமாக இருக்கலாம் மற்றும் முடிக்க முடியாது -SNAPSHOT
  • இருப்பு தேவை name, description и url
  • உரிமம், டெவலப்பர்கள் மற்றும் பதிப்பு கட்டுப்பாட்டு அமைப்பு பற்றிய தகவல்களின் இருப்பு

வெளியிடும் போது பின்பற்ற வேண்டிய அடிப்படை விதிகள் இவை. முழு தகவல் கிடைக்கும் இங்கே.

இந்த தேவைகளை நாங்கள் செயல்படுத்துகிறோம் build.gradle கோப்பு. முதலில், டெவலப்பர்கள், உரிமங்கள், பதிப்பு கட்டுப்பாட்டு அமைப்பு பற்றிய தேவையான அனைத்து தகவல்களையும் சேர்ப்போம், மேலும் திட்டத்தின் url, பெயர் மற்றும் விளக்கத்தை அமைக்கவும். இதற்கு ஒரு எளிய முறையை எழுதுவோம்:

def customizePom(pom) {
    pom.withXml {
        def root = asNode()

        root.dependencies.removeAll { dep ->
            dep.scope == "test"
        }

        root.children().last() + {
            resolveStrategy = DELEGATE_FIRST

            description 'Some description of artifact'
            name 'Artifct name'
            url 'https://github.com/login/projectname'
            organization {
                name 'com.github.login'
                url 'https://github.com/login'
            }
            issueManagement {
                system 'GitHub'
                url 'https://github.com/login/projectname/issues'
            }
            licenses {
                license {
                    name 'The Apache License, Version 2.0'
                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                }
            }
            scm {
                url 'https://github.com/login/projectname'
                connection 'scm:https://github.com/login/projectname.git'
                developerConnection 'scm:git://github.com/login/projectname.git'
            }
            developers {
                developer {
                    id 'dev'
                    name 'DevName'
                    email '[email protected]'
                }
            }
        }
    }
}

அடுத்து, உருவாக்கப்பட்ட சட்டசபையின் போது நீங்கள் குறிப்பிட வேண்டும் -sources.jar и-javadoc.jar கோப்புகள். இந்த பகுதிக்கு java நீங்கள் பின்வருவனவற்றைச் சேர்க்க வேண்டும்:

java {
    withJavadocJar()
    withSourcesJar()
}

GPG/PGP கையொப்பத்தை அமைக்கும் கடைசி தேவைக்கு செல்லலாம். இதைச் செய்ய, செருகுநிரலை இணைக்கவும் signing:

plugins {
    id 'signing'
}

மற்றும் ஒரு பகுதியைச் சேர்க்கவும்:

signing {
    sign publishing.publications
}

இறுதியாக, ஒரு பகுதியைச் சேர்ப்போம் publishing:

publishing {
    publications {
        mavenJava(MavenPublication) {
            customizePom(pom)
            groupId group
            artifactId archivesBaseName
            version version

            from components.java
        }
    }
    repositories {
        maven {
            url "https://oss.sonatype.org/service/local/staging/deploy/maven2"
            credentials {
                username sonatypeUsername
                password sonatypePassword
            }
        }
    }
}

இது sonatype பயனர்பெயர் и சொனாடைப் பாஸ்வேர்டு பதிவு செய்யும் போது உருவாக்கப்பட்ட உள்நுழைவு மற்றும் கடவுச்சொல்லைக் கொண்ட மாறிகள் sonatype.org.

இதனால் இறுதி build.gradle இப்படி இருக்கும்:

முழு build.gradle குறியீடு

plugins {
    id 'java'
    id 'maven-publish'
    id 'signing'
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
    withJavadocJar()
    withSourcesJar()
}

group 'io.github.githublogin'
archivesBaseName = 'projectname'
version = System.getenv('RELEASE_VERSION') ?: "0.0.1"

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
}

test {
    useJUnitPlatform()
}

jar {
    from sourceSets.main.output
    from sourceSets.main.allJava
}

signing {
    sign publishing.publications
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            customizePom(pom)
            groupId group
            artifactId archivesBaseName
            version version

            from components.java
        }
    }
    repositories {
        maven {
            url "https://oss.sonatype.org/service/local/staging/deploy/maven2"
            credentials {
                username sonatypeUsername
                password sonatypePassword
            }
        }
    }
}

def customizePom(pom) {
    pom.withXml {
        def root = asNode()

        root.dependencies.removeAll { dep ->
            dep.scope == "test"
        }

        root.children().last() + {
            resolveStrategy = DELEGATE_FIRST

            description 'Some description of artifact'
            name 'Artifct name'
            url 'https://github.com/login/projectname'
            organization {
                name 'com.github.login'
                url 'https://github.com/githublogin'
            }
            issueManagement {
                system 'GitHub'
                url 'https://github.com/githublogin/projectname/issues'
            }
            licenses {
                license {
                    name 'The Apache License, Version 2.0'
                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                }
            }
            scm {
                url 'https://github.com/githublogin/projectname'
                connection 'scm:https://github.com/githublogin/projectname.git'
                developerConnection 'scm:git://github.com/githublogin/projectname.git'
            }
            developers {
                developer {
                    id 'dev'
                    name 'DevName'
                    email '[email protected]'
                }
            }
        }
    }
}

சூழல் மாறியிலிருந்து பதிப்பைப் பெறுகிறோம் என்பதை நான் கவனிக்க விரும்புகிறேன்: System.getenv('RELEASE_VERSION'). சட்டசபையின் போது அதை அம்பலப்படுத்தி டேக் பெயரில் இருந்து எடுப்போம்.

PGP முக்கிய உருவாக்கம்

அனைத்து கோப்புகளும் GPG/PGP விசையுடன் கையொப்பமிடப்பட வேண்டும் என்பது Sonatype இன் தேவைகளில் ஒன்றாகும். இதற்காக நாங்கள் செல்கிறோம் இங்கே உங்கள் இயக்க முறைமைக்கான GnuPG பயன்பாட்டைப் பதிவிறக்கவும்.

  • நாங்கள் ஒரு முக்கிய ஜோடியை உருவாக்குகிறோம்: gpg --gen-key, ஒரு பயனர் பெயர், மின்னஞ்சல் மற்றும் கடவுச்சொல்லை உள்ளிடவும்.
  • கண்டுபிடிக்க id கட்டளையுடன் எங்கள் விசை: gpg --list-secret-keys --keyid-format short. சாய்வுக்குப் பிறகு ஐடி குறிப்பிடப்படும், எடுத்துக்காட்டாக: rsa2048/9B695056
  • சேவையகத்திற்கான பொது விசையை வெளியிடுகிறது https://keys.openpgp.org கட்டளை: gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056
  • ரகசிய விசையை தன்னிச்சையான இடத்திற்கு ஏற்றுமதி செய்கிறோம், எதிர்காலத்தில் இது தேவைப்படும்: gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg

கிதுப் செயல்களை அமைத்தல்

இறுதிக் கட்டத்திற்குச் செல்வோம், கிதுப் செயல்களைப் பயன்படுத்தி கட்டமைப்பை அமைத்து தானாக வெளியிடுவோம்.
கிதுப் செயல்கள் என்பது முழு சிஐ / சிடி சுழற்சியை செயல்படுத்துவதன் மூலம் பணிப்பாய்வுகளை தானியங்குபடுத்த உங்களை அனுமதிக்கும் அம்சமாகும். உருவாக்குதல், சோதனை செய்தல் மற்றும் வரிசைப்படுத்துதல் ஆகியவை பல்வேறு நிகழ்வுகளால் தூண்டப்படலாம்: குறியீடு புஷ், வெளியீடு உருவாக்கம் அல்லது சிக்கல்கள். பொது களஞ்சியங்களுக்கு இந்த செயல்பாடு முற்றிலும் இலவசம்.

இந்தப் பிரிவில், எவ்வாறு உருவாக்குவது மற்றும் புஷ் குறியீட்டை அமைப்பது மற்றும் வெளியீட்டின் போது Sonatype களஞ்சியத்தில் வரிசைப்படுத்துவது மற்றும் இரகசியங்களை அமைப்பது எப்படி என்பதைக் காண்பிப்பேன்.

நாங்கள் ரகசியங்களை அமைக்கிறோம்

தானியங்கி அசெம்பிளி மற்றும் வரிசைப்படுத்தலுக்கு, கீ ஐடி, கீயை உருவாக்கும் போது நாம் உள்ளிட்ட கடவுச்சொல், பிஜிபி விசை மற்றும் சோனாடைப் உள்நுழைவு/கடவுச்சொல் போன்ற பல ரகசிய மதிப்புகள் நமக்குத் தேவை. களஞ்சிய அமைப்புகளில் சிறப்புப் பிரிவில் அவற்றை அமைக்கலாம்:

ஜாவா திட்டத்தை சோனாடைப் மேவன் மத்திய களஞ்சியத்தில் வெளியிட கிரேடில் மற்றும் கிதுப் செயல்களைப் பயன்படுத்துதல்

பின்வரும் மாறிகளை நாங்கள் அமைக்கிறோம்:

  • SONATYPE_USERNAME / SONATYPE_PASSWORD - Sonatype இல் பதிவு செய்யும் போது நாம் உள்ளிட்ட உள்நுழைவு / கடவுச்சொல்
  • SIGNING_KEYID/SIGNING_PASSWORD — PGP விசை ஐடி மற்றும் கடவுச்சொல் உருவாக்கத்தின் போது அமைக்கப்பட்டது.

GPG_KEY_CONTENTS மாறியைப் பற்றி மேலும் விரிவாகப் படிக்க விரும்புகிறேன். உண்மை என்னவென்றால், வெளியீட்டிற்கு எங்களுக்கு ஒரு தனிப்பட்ட PGP விசை தேவை. அதை ரகசியங்களில் இடுகையிட, நான் பயன்படுத்தினேன் அறிவுறுத்தல்கள் மேலும் பல செயல்களையும் செய்தார்.

  • gpg மூலம் நமது விசையை குறியாக்கம் செய்வோம்: gpg --symmetric --cipher-algo AES256 9B695056.gpgகடவுச்சொல்லை உள்ளிடுவதன் மூலம். இது ஒரு மாறியில் வைக்கப்பட வேண்டும்: SECRET_PASSPHRASE
  • பெறப்பட்ட மறைகுறியாக்கப்பட்ட விசையை Base64 ஐப் பயன்படுத்தி உரை வடிவத்தில் மொழிபெயர்ப்போம்: base64 9B695056.gpg.gpg > 9B695056.txt. உள்ளடக்கம் மாறியில் வைக்கப்படும்: GPG_KEY_CONTENTS.

குறியீட்டை அழுத்தி PR ஐ உருவாக்கும் போது அமைப்பை உருவாக்கவும்

முதலில் உங்கள் திட்டத்தின் மூலத்தில் ஒரு கோப்புறையை உருவாக்க வேண்டும்: .github/workflows.

அதில், கோப்பைக் குறிக்கவும், எடுத்துக்காட்டாக, gradle-ci-build.yml பின்வரும் உள்ளடக்கத்துடன்:

name: build

on:
  push:
    branches:
      - master
      - dev
      - testing
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Set up JDK 8
        uses: actions/setup-java@v1
        with:
          java-version: 8

      - name: Build with Gradle
        uses: eskatos/gradle-command-action@v1
        with:
          gradle-version: current
          arguments: build -PsonatypeUsername=${{secrets.SONATYPE_USERNAME}} -PsonatypePassword=${{secrets.SONATYPE_PASSWORD}}

கிளைகளுக்கு தள்ளும் போது இந்த பணிப்பாய்வு செயல்படுத்தப்படும் master, dev и testing, இழுக்கும் கோரிக்கைகளை உருவாக்கும் போது.

வேலைகள் பிரிவு குறிப்பிட்ட நிகழ்வுகளில் செயல்படுத்தப்பட வேண்டிய படிகளைக் குறிப்பிடுகிறது. இந்த வழக்கில், உபுண்டுவின் சமீபத்திய பதிப்பை உருவாக்குவோம், ஜாவா 8 ஐப் பயன்படுத்துவோம், மேலும் கிரேடலுக்கான செருகுநிரலைப் பயன்படுத்துவோம். eskatos/gradle-command-action@v1இது, பில்டரின் சமீபத்திய பதிப்பைப் பயன்படுத்தி, குறிப்பிடப்பட்ட கட்டளைகளை இயக்கும் arguments. மாறிகள் secrets.SONATYPE_USERNAME и secrets.SONATYPE_PASSWORD இவை நாம் முன்பு கேட்ட ரகசியங்கள்.

உருவாக்க முடிவுகள் செயல்கள் தாவலில் பிரதிபலிக்கும்:

ஜாவா திட்டத்தை சோனாடைப் மேவன் மத்திய களஞ்சியத்தில் வெளியிட கிரேடில் மற்றும் கிதுப் செயல்களைப் பயன்படுத்துதல்

புதிய வெளியீடு வெளியிடப்படும் போது தானாக வரிசைப்படுத்தவும்

தானியங்கு வரிசைப்படுத்துதலுக்கான தனி பணிப்பாய்வு கோப்பை உருவாக்குவோம் gradle-ci-publish.yml:

name: publish

on:
  push:
    tags:
      - 'v*'

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Set up JDK 8
        uses: actions/setup-java@v1
        with:
          java-version: 8

      - name: Prepare to publish
        run: |
          echo '${{secrets.GPG_KEY_CONTENTS}}' | base64 -d > publish_key.gpg
          gpg --quiet --batch --yes --decrypt --passphrase="${{secrets.SECRET_PASSPHRASE}}" 
          --output secret.gpg publish_key.gpg
          echo "::set-env name=RELEASE_VERSION::${GITHUB_REF:11}"

      - name: Publish with Gradle
        uses: eskatos/gradle-command-action@v1
        with:
          gradle-version: current
          arguments: test publish -Psigning.secretKeyRingFile=secret.gpg -Psigning.keyId=${{secrets.SIGNING_KEYID}} -Psigning.password=${{secrets.SIGNING_PASSWORD}} -PsonatypeUsername=${{secrets.SONATYPE_USERNAME}} -PsonatypePassword=${{secrets.SONATYPE_PASSWORD}}

கோப்பு தூண்டப்படும் நிகழ்வைத் தவிர, முந்தையதைப் போலவே உள்ளது. இந்த வழக்கில், இது v இல் தொடங்கும் பெயருடன் ஒரு குறிச்சொல்லை உருவாக்கும் நிகழ்வு ஆகும்.

வரிசைப்படுத்துவதற்கு முன், நாம் இரகசியங்களிலிருந்து பிஜிபி விசையைப் பிரித்தெடுத்து, திட்டத்தின் மூலத்தில் வைக்க வேண்டும், அத்துடன் அதை மறைகுறியாக்க வேண்டும். அடுத்து, நாம் ஒரு சிறப்பு சூழல் மாறியை அமைக்க வேண்டும் RELEASE_VERSION நாம் குறிப்பிடுவது gradle.build கோப்பு. இவை அனைத்தும் பிரிவில் செய்யப்படுகின்றன Prepare to publish. GPG_KEY_CONTENTS மாறியிலிருந்து எங்கள் விசையைப் பெறுகிறோம், அதை ஜிபிஜி கோப்பாக மொழிபெயர்த்து, அதை கோப்பில் வைத்து மறைகுறியாக்குகிறோம் secret.gpg.

அடுத்து, நாம் ஒரு சிறப்பு மாறிக்கு திரும்புவோம் GITHUB_REF, அதில் இருந்து டேக் உருவாக்கும் போது நாம் அமைக்கும் பதிப்பைப் பெறலாம். இந்த வழக்கில் இந்த மாறி பொருத்தமானது. refs/tags/v0.0.2 அதிலிருந்து ஒரு குறிப்பிட்ட பதிப்பைப் பெற முதல் 11 எழுத்துகளை துண்டிக்கிறோம். அடுத்து, வெளியிடுவதற்கு நிலையான Gradle கட்டளைகளைப் பயன்படுத்துகிறோம்: test publish

Sonatype களஞ்சியத்தில் வரிசைப்படுத்தல் முடிவுகளைச் சரிபார்க்கிறது

வெளியீடு உருவாக்கப்பட்டவுடன், முந்தைய பிரிவில் விவரிக்கப்பட்டுள்ள பணிப்பாய்வு தொடங்க வேண்டும். இதைச் செய்ய, ஒரு வெளியீட்டை உருவாக்கவும்:

ஜாவா திட்டத்தை சோனாடைப் மேவன் மத்திய களஞ்சியத்தில் வெளியிட கிரேடில் மற்றும் கிதுப் செயல்களைப் பயன்படுத்துதல்

குறிச்சொல் பெயர் v உடன் தொடங்க வேண்டும். வெளியீட்டை வெளியிடு என்பதைக் கிளிக் செய்த பிறகு, பணிப்பாய்வு வெற்றிகரமாக முடிந்தால், நாம் செல்லலாம் சொனாட்டாப் நெக்ஸஸ் உறுதி செய்ய:

ஜாவா திட்டத்தை சோனாடைப் மேவன் மத்திய களஞ்சியத்தில் வெளியிட கிரேடில் மற்றும் கிதுப் செயல்களைப் பயன்படுத்துதல்

கலைப்பொருள் ஸ்டேஜிங் களஞ்சியத்தில் தோன்றியது. இது உடனடியாக திறந்த நிலையில் தோன்றும், பின்னர் பொருத்தமான பொத்தானை அழுத்துவதன் மூலம் அதை கைமுறையாக மூடு நிலைக்கு மாற்ற வேண்டும். அனைத்துத் தேவைகளும் பூர்த்தி செய்யப்பட்டுள்ளதா என்பதைச் சரிபார்த்த பிறகு, கலைப்பொருள் மூடு நிலைக்குச் சென்று, மாற்றுவதற்கு இனி கிடைக்காது. இந்த வடிவத்தில், இது MavenCentral இல் முடிவடையும். எல்லாம் நன்றாக இருந்தால், நீங்கள் பொத்தானை அழுத்தலாம் வெளியீட்டு, மற்றும் கலைப்பொருள் சோனாடைப் களஞ்சியத்தில் முடிவடையும்.

கலைப்பொருள் மேவன்சென்ட்ரலுக்குள் செல்ல, ஆரம்பத்தில் நாங்கள் உருவாக்கிய பணியில் நீங்கள் அதைக் கேட்க வேண்டும். நீங்கள் இதை ஒரு முறை மட்டுமே செய்ய வேண்டும், எனவே நாங்கள் முதல் முறையாக வெளியிடுகிறோம். அடுத்தடுத்த காலங்களில், இது தேவையில்லை, எல்லாம் தானாகவே ஒத்திசைக்கப்படும். அவர்கள் எனக்கு ஒத்திசைவை விரைவாக இயக்கினர், ஆனால் மேவன்சென்ட்ரலில் கலைப்பொருள் கிடைக்க சுமார் 5 நாட்கள் ஆனது.

அவ்வளவுதான், எங்கள் கலைப்பொருளை மேவன்சென்ட்ரலில் வெளியிட்டோம்.

பயனுள்ள இணைப்புகள்

  • ஒத்த கட்டுரை, மேவன் வழியாக மட்டுமே வெளியிடவும்
  • நோயின் களஞ்சியம் சொனாடைப்
  • JIRA பணியை உருவாக்கும் சொனாடைப்
  • உதாரணமாக களஞ்சியம் அனைத்தும் அமைக்கப்பட்டுள்ளது

ஆதாரம்: www.habr.com