डॉकटरफाइल के साथ क्लोन प्राइवेट गिट रेपो


240

मैंने इस कोड की प्रतिलिपि बनाई है जो कि विभिन्न काम करने वाले डॉकफाइल्स के आसपास लगता है, यहाँ मेरा है:

FROM ubuntu

MAINTAINER Luke Crooks "luke@pumalo.org"

# Update aptitude with new repo
RUN apt-get update

# Install software 
RUN apt-get install -y git python-virtualenv

# Make ssh dir
RUN mkdir /root/.ssh/

# Copy over private key, and set permissions
ADD id_rsa /root/.ssh/id_rsa
RUN chmod 700 /root/.ssh/id_rsa
RUN chown -R root:root /root/.ssh

# Create known_hosts
RUN touch /root/.ssh/known_hosts

# Remove host checking
RUN echo "Host bitbucket.org\n\tStrictHostKeyChecking no\n" >> /root/.ssh/config

# Clone the conf files into the docker container
RUN git clone git@bitbucket.org:Pumalo/docker-conf.git /home/docker-conf

यह मुझे त्रुटि देता है

Step 10 : RUN git clone git@bitbucket.org:Pumalo/docker-conf.git /home/docker-conf
 ---> Running in 0d244d812a54
Cloning into '/home/docker-conf'...
Warning: Permanently added 'bitbucket.org,131.103.20.167' (RSA) to the list of known hosts.
Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
2014/04/30 16:07:28 The command [/bin/sh -c git clone git@bitbucket.org:Pumalo/docker-conf.git /home/docker-conf] returned a non-zero code: 128

यह डॉकफाइल्स का उपयोग करने का मेरा पहला मौका है, लेकिन मैंने जो कुछ भी पढ़ा है (और काम करने वाले कॉन्फ़िगरेशन से लिया गया है) से मैं यह नहीं देख सकता कि यह काम क्यों नहीं करता है।

मेरी id_rsa मेरे dockerfile के समान फ़ोल्डर में है और मेरे स्थानीय कुंजी की एक प्रति है जो इस खराबी को क्लोन कर सकती है कोई समस्या नहीं है।

संपादित करें:

अपने डॉकफाइल में मैं जोड़ सकता हूं:

RUN cat /root/.ssh/id_rsa

और यह सही कुंजी प्रिंट करता है, इसलिए मुझे पता है कि इसे सही तरीके से कॉपी किया जा रहा है।

मैंने भी सलाह दी है कि नूह सलाह और भागा करे:

RUN echo "Host bitbucket.org\n\tIdentityFile /root/.ssh/id_rsa\n\tStrictHostKeyChecking no" >> /etc/ssh/ssh_config

यह दुख की बात भी काम नहीं करता है।

जवाबों:


300

मेरी कुंजी पासवर्ड संरक्षित थी जो समस्या पैदा कर रही थी, अब एक कामकाजी फ़ाइल नीचे सूचीबद्ध है (भविष्य के गोगलर्स की मदद के लिए)

FROM ubuntu

MAINTAINER Luke Crooks "luke@pumalo.org"

# Update aptitude with new repo
RUN apt-get update

# Install software 
RUN apt-get install -y git
# Make ssh dir
RUN mkdir /root/.ssh/

# Copy over private key, and set permissions
# Warning! Anyone who gets their hands on this image will be able
# to retrieve this private key file from the corresponding image layer
ADD id_rsa /root/.ssh/id_rsa

# Create known_hosts
RUN touch /root/.ssh/known_hosts
# Add bitbuckets key
RUN ssh-keyscan bitbucket.org >> /root/.ssh/known_hosts

# Clone the conf files into the docker container
RUN git clone git@bitbucket.org:User/repo.git

11
बस मामले में, यह एक कुंजी का पासवर्ड सुरक्षा को हटाने का वर्णन करने वाला एक लिंक है
थॉमस

82
सिर्फ एक FYI करें, जब आप RUN ssh-keyscan bitbucket.org >> /root/.ssh/ogn_hosts चलाते हैं, तो छवि उस परत के रूप में सहेजेगी। यदि किसी को आपकी छवि पर पकड़ मिलती है, तो वे कुंजी को पुनः प्राप्त कर सकते हैं ... भले ही आप उस फ़ाइल को बाद की परत में हटा दें, b / c वे चरण 7 में वापस जा सकते हैं जब आपने इसे जोड़ा था।
बर्नी पेरेज़

23
उपयोगी उत्तर के लिए धन्यवाद। लेकिन हमारे लिए यह निर्माण बेतरतीब ढंग से विफल रहा और जांच के बाद हमने देखा कि ssh-keyscanइसमें 5 सेकंड का डिफ़ॉल्ट समय है जो कि बिटकॉइन अक्सर पार कर जाता है। ssh-keyscanत्रुटि की सूचना भी नहीं देगा। इसलिए बेहतर RUN ssh-keyscan -T 60 bitbucket.org >> /root/.ssh/known_hostsहै कि सुरक्षित रहें।
फ्लूयडोनिक

5
क्या कोई समझा सकता है कि दौड़ना ssh-keyscanएक मुद्दा क्यों है? मेरी समझ यह है कि यह केवल जीथब / बिटबकेट की सार्वजनिक कुंजी खींच लेगा। क्या विकल्प का उपयोग किया जा सकता है ताकि यह एक परत में समाप्त न हो?
पेड्रो

9
@Pedro विशेष रूप से कीस्कैन स्टेप कोई समस्या नहीं है, आप स्पष्ट रूप से सही हैं। यदि कुछ भी हो, तो इन सार्वजनिक सार्वजनिक कुंजियों को अधिक से अधिक फैलाना चाहिए। known_hostsफ़ाइल पर विवरण के लिए sshd (8) देखें । जब लोग पर्याप्त ध्वनि करते हैं तो लोग यादृच्छिक चीजों को उभारते हैं।
tain

99

आपको उस Docker छवि के लिए नई SSH कुंजी सेट बनानी चाहिए, क्योंकि आप शायद अपनी निजी कुंजी वहाँ एम्बेड नहीं करना चाहते हैं। इसे काम करने के लिए, आपको उस कुंजी को अपने git रिपॉजिटरी में परिनियोजन कुंजी में जोड़ना होगा। यहां देखें पूरा नुस्खा:

  1. Ssh कीज़ जनरेट करें ssh-keygen -q -t rsa -N '' -f repo-keyजिसके साथ आपको रेपो-की और repo-key.pub फाइल्स मिलेंगी।

  2. अपनी रिपॉजिटरी परिनियोजन कुंजियों में repo-key.pub जोड़ें।
    GitHub पर, [अपनी रिपॉजिटरी] -> सेटिंग -> डिप्लॉय कीज़ पर जाएँ

  3. कुछ इस तरह से अपने Dockerfile में जोड़ें:

    ADD रेपो-की /
    DAUD \
      chmod 600 / रेपो-की और & \ _  
      इको "आइडेंटिटीफाइल / रेपो-की" >> / etc / ssh / ssh_config && \ _  
      इको-ई "स्ट्रिक्टहॉस्टकेयचेकिंग नो" >> / आदि / ssh / ssh_config && \ _  
      // आपका गिट क्लोन यहाँ आदेश ...
    

ध्यान दें कि ऊपर StrictHostKeyChecking से स्विच होता है, इसलिए आपको .ssh / ज्ञात_होस्ट की आवश्यकता नहीं है। हालाँकि मुझे उपरोक्त उत्तरों में से एक में ssh-keycan के साथ अधिक समाधान पसंद है।


6
चेतावनी: मेरे विन्यास पर, प्रतिध्वनि "..." भी फ़ाइल के अंदर -e लिखें। बस ध्वज को हटा दें और यह ठीक काम करता है।
Conchylicultor

आपकी समस्या को हल करने में मेरी मदद करने में आपका जवाब बिल्कुल सही था। धन्यवाद!
डेविड पॉइंटर

मेरे पास अभी भी एक ही मुद्दा है:fatal: Could not read from remote repository.
एलेक्स

1
धन्यवाद लाखों! मैं तुमसे प्यार का ऐलान करने के किनारे पर हूं। आपने एक ऐसा मसला हल कर दिया, जो मैं दिनों से जूझ रहा था!
एलेक्जेंड्रा

इस प्रश्न के लिए चयनित उत्तर अब कोई अच्छा उत्तर नहीं है। यह 2014 में सही था लेकिन 2020 के लिए यह सही उत्तर है।
बिकल बेसनेट

70

वहाँ ssh विन्यास के साथ चारों ओर बेला करने की कोई जरूरत नहीं है। कॉन्फ़िगरेशन फ़ाइल का उपयोग करें (डॉकरीफ़ाइल नहीं) जिसमें पर्यावरण चर होते हैं, और एक शेल स्क्रिप्ट अपडेट होती है जो रनटाइम पर आपकी डॉक फ़ाइल को अपडेट करती है। आप अपने Dockerfiles से टोकन रखते हैं और आप https पर क्लोन कर सकते हैं (ssh कीज़ को जेनरेट या पास करने की कोई आवश्यकता नहीं है)।

पर जाएं सेटिंग> व्यक्तिगत पहुँच टोकन

  • repoस्कोप सक्षम के साथ एक व्यक्तिगत एक्सेस टोकन उत्पन्न करें ।
  • इस तरह क्लोन: git clone https://MY_TOKEN@github.com/user-or-org/repo

कुछ टिप्पणीकारों ने कहा है कि यदि आप एक साझा डॉकरफाइल का उपयोग करते हैं, तो यह आपकी परियोजना पर अन्य लोगों के लिए आपकी पहुंच कुंजी को उजागर कर सकता है। हालांकि यह आपके विशिष्ट उपयोग के मामले के लिए एक चिंता का विषय हो सकता है या नहीं भी हो सकता है, यहाँ कुछ तरीके दिए गए हैं जिनसे आप निपट सकते हैं:

  • तर्क को स्वीकार करने के लिए शेल स्क्रिप्ट का उपयोग करें जिसमें आपकी कुंजी एक चर के रूप में हो सकती है। अपने डॉकरफ़ाइल में वैरिएबल को sedउसी तरह या उसके साथ बदलें , यानी उस स्क्रिप्ट को कॉल sh rundocker.sh MYTOKEN=fooकरना जिसके साथ उसकी जगह होगी https://{{MY_TOKEN}}@github.com/user-or-org/repo। ध्यान दें कि आप एक कॉन्फ़िगरेशन फ़ाइल (इन .yml या जो भी प्रारूप आप चाहते हैं) का उपयोग एक ही काम करने के लिए कर सकते हैं लेकिन पर्यावरण चर के साथ।
  • केवल उस प्रोजेक्ट के लिए एक गीथूब उपयोगकर्ता बनाएं (और उसके लिए एक पहुंच टोकन बनाएं)

आप किस संदर्भ में बात कर रहे हैं Settings > Applications?
टर्बोलेडेन

1
इस दृष्टिकोण के नकारात्मक पक्ष यह है कि आप Dockerfile के भीतर एक निजी रेपो के लिए क्रेडेंशियल संग्रहीत कर रहे हैं @ @ बदमाश के दृष्टिकोण के विपरीत जो आपको एक कुंजी को संदर्भित करने की अनुमति देगा जो डॉकफाइल से अलग संग्रहीत है। इस संदर्भ के बिना कि कैसे ओपी डॉकरफाइल को स्टोर कर रहा है, हम यह निर्धारित नहीं कर सकते हैं कि क्या यह एक समस्या का कारण होगा, लेकिन व्यक्तिगत अनुभव से मैं अपने डॉक्सरेफाइल्स को एक वीसीएस के भीतर स्टोर करना पसंद करता हूं और कुछ भी ऐसा नहीं करना चाहता हूं जिसमें क्रेडेंशियल शामिल हों। एक बार डॉकर ने कमांड बनाने के लिए एनवी वेरिएबल्स पास करने की क्षमता लागू कर दी, तो मैं मानता हूं कि यह सबसे साफ समाधान होगा।
जबब्लास

2
@CalvinFroedge स्थानीय स्तर पर मैं मानता हूं कि आप अपने मेजबान से मतलब रखते हैं? मुझे निर्माण समय में एक कंटेनर में होस्ट पर पर्यावरण चर को उजागर करने के तरीके के बारे में पता नहीं है यही कारण है कि हमारे पास इस github.com/docker/docker/issues/6822 जैसे खुले मुद्दे हैं । कृपया स्पष्ट कर सकते हैं?
Jabbslad

1
यहां तक ​​कि क्लीनर (चिंताओं का पृथक्करण): क्लोन किए गए रेपो के लिए एक जुड़ा हुआ वॉल्यूम + केवल क्लोनिंग कार्य के लिए एक समर्पित कंटेनर + केवल एसएसएच कुंजी (या टोकन, जैसा कि आप सुझाव देते हैं) के साथ एक जुड़ा हुआ वॉल्यूम। Stackoverflow.com/a/30992047 देखें , हो सकता है कि stackoverflow.com/a/29981990 के साथ संयुक्त हो ।
पीटरिनो

9
इसके अलावा प्रश्न एक बिटकॉइन रेपो के लिए है, न कि जीथब्यु रेपो के लिए।
माइकल ड्रेपर

25

एक अन्य विकल्प यह सुनिश्चित करने के लिए कि आपके SSH कुंजियों को अंतिम छवि में शामिल नहीं किया गया है, मल्टी-स्टेज डॉकटर बिल्ड का उपयोग करना है।

जैसा कि मेरी पोस्ट में बताया गया है कि आप COPYअपनी अंतिम छवि को अपनी अंतिम छवि में लाने के लिए आवश्यक निर्भरता के साथ अपनी मध्यवर्ती छवि तैयार कर सकते हैं ।

इसके अतिरिक्त यदि हम LABELअपनी मध्यवर्ती परतें बनाते हैं, तो हम उन्हें समाप्त होने पर मशीन से हटा भी सकते हैं।

# Choose and name our temporary image.
FROM alpine as intermediate
# Add metadata identifying these images as our build containers (this will be useful later!)
LABEL stage=intermediate

# Take an SSH key as a build argument.
ARG SSH_KEY

# Install dependencies required to git clone.
RUN apk update && \
    apk add --update git && \
    apk add --update openssh

# 1. Create the SSH directory.
# 2. Populate the private key file.
# 3. Set the required permissions.
# 4. Add github to our list of known hosts for ssh.
RUN mkdir -p /root/.ssh/ && \
    echo "$SSH_KEY" > /root/.ssh/id_rsa && \
    chmod -R 600 /root/.ssh/ && \
    ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts

# Clone a repository (my website in this case)
RUN git clone git@github.com:janakerman/janakerman.git

# Choose the base image for our final image
FROM alpine

# Copy across the files from our `intermediate` container
RUN mkdir files
COPY --from=intermediate /janakerman/README.md /files/README.md

हम तब निर्माण कर सकते हैं:

MY_KEY=$(cat ~/.ssh/id_rsa)
docker build --build-arg SSH_KEY="$MY_KEY" --tag clone-example .

सिद्ध करें कि हमारी SSH कुंजियाँ समाप्त हो गई हैं:

docker run -ti --rm clone-example cat /root/.ssh/id_rsa

बिल्ड मशीन से स्वच्छ मध्यवर्ती चित्र:

docker rmi -f $(docker images -q --filter label=stage=intermediate)

ARG SSH_PRIVATE_KEY को ARG SSH_KEY के साथ बदलने की आवश्यकता है
जोसफ फारसी

कठबोली हम सिर्फ चाबियाँ हटा सकते हैं एक बार गिट क्लोन किया जाता है?
ब्रॉन्च

1
आप कर सकते हैं, लेकिन आपको इसे एकल के हिस्से के रूप में करने की आवश्यकता होगी RUNताकि आप पिछली छवि परत में कुंजी को न छोड़ें। डॉकटर के रूप में 1.13आप --squash प्रायोगिक तर्क का उपयोग कर सकते हैं जो आपकी अंतिम छवि परतों में एसएसएच कुंजी को हटा देगा।
जेकर

19

बिटबकेट रिपॉजिटरी के लिए, रेपो और प्रोजेक्ट के रीड एक्सेस के साथ ऐप पासवर्ड (बिटबकेट सेटिंग्स -> एक्सेस मैनेजमेंट -> ऐप पासवर्ड, इमेज देखें) जेनरेट करें।

बिटबकेट उपयोगकर्ता मेनू

तब आपको जो कमांड का उपयोग करना चाहिए वह है:

git clone https://username:generated_password@bitbucket.org/reponame/projectname.git

1
सबसे सरल :) मुझे मानना ​​होगा कि मैं SSH आधारित दृष्टिकोण को पसंद करूंगा, लेकिन मुझे उपरोक्त किसी भी प्रकार का काम नहीं मिल सका ... फाइलें नहीं मिलीं, आदि
Janos

मैं "एक्सेस मैनेजमेंट" नहीं देखता ... मुझे लगता है कि यह पुराना है?
मार्टिन थोमा

1
काम किया! सादा और सरल ... शानदार!
जोसमी

2
बेशक ... आपको बस बाईं पट्टी पर अपनी प्रोफ़ाइल तस्वीर पर क्लिक करना होगा, फिर बिटबकैट सेटिंग्स पर और आपको कुछ इस तरह दिखाई देगा: imgur.com/EI33zj3
जोसमी

1
इसने मेरे लिए काम किया। हालांकि, मेरे पास सबमॉड्यूल हैं और --recursiveकाम नहीं किया। मुझे git cloneप्रत्येक सबमॉड्यूल के लिए लगाना था , जो ठीक है लेकिन अगर यह पुनरावर्ती होता तो बहुत अच्छा होता।
जेलीन तामायो

14

आप अक्सर git cloneडॉक बिल्ड के भीतर से एक निजी रेपो प्रदर्शन नहीं करना चाहते हैं । वहाँ क्लोन करने से छवि के अंदर निजी ssh क्रेडेंशियल्स रखना शामिल होता है जहाँ उन्हें बाद में आपकी छवि के साथ किसी के द्वारा भी निकाला जा सकता है।

इसके बजाय, सामान्य अभ्यास अपनी पसंद के सीआई उपकरण में डॉक के बाहर से गिट रेपो को क्लोन करना है, और बस COPYफाइलों को छवि में। इसका दूसरा लाभ है: डॉकटर कैशिंग। डॉकर कैचिंग को चलाए जा रहे कमांड को देखता है, इसमें पर्यावरण चर शामिल हैं, इनपुट फाइलें, आदि, और यदि वे एक ही मूल कदम से पिछले बिल्ड के समान हैं, तो यह उस पिछले कैश का पुन: उपयोग करता है। एक git cloneकमांड के साथ , कमांड स्वयं समान है, इसलिए डॉकटर कैश को फिर से उपयोग करेगा भले ही बाहरी गिट रेपो को बदल दिया जाए। हालांकि, एक COPYकमांड बिल्ड संदर्भ में फाइलों को देखेगा और यह देख सकता है कि क्या वे समान हैं या अपडेट किए गए हैं, और उपयुक्त होने पर ही कैश का उपयोग करें।


यदि आप अपने बिल्ड में क्रेडेंशियल्स जोड़ने जा रहे हैं, तो एक मल्टी-स्टेज बिल्ड के साथ ऐसा करने पर विचार करें, और केवल उन क्रेडेंशियल्स को एक प्रारंभिक चरण में रखें जो कभी भी आपके बिल्ड होस्ट के बाहर टैग और पुश न किए गए हों। परिणाम जैसा दिखता है:

FROM ubuntu as clone

# Update aptitude with new repo
RUN apt-get update \
 && apt-get install -y git
# Make ssh dir
# Create known_hosts
# Add bitbuckets key
RUN mkdir /root/.ssh/ \
 && touch /root/.ssh/known_hosts \
 && ssh-keyscan bitbucket.org >> /root/.ssh/known_hosts

# Copy over private key, and set permissions
# Warning! Anyone who gets their hands on this image will be able
# to retrieve this private key file from the corresponding image layer
COPY id_rsa /root/.ssh/id_rsa

# Clone the conf files into the docker container
RUN git clone git@bitbucket.org:User/repo.git

FROM ubuntu as release
LABEL maintainer="Luke Crooks <luke@pumalo.org>"

COPY --from=clone /repo /repo
...

अभी हाल ही में, BuildKit कुछ प्रायोगिक विशेषताओं का परीक्षण कर रहा है जो आपको एक ssh कुंजी को माउंट के रूप में पारित करने की अनुमति देते हैं जो कभी भी छवि को नहीं लिखा जाता है:

# syntax=docker/dockerfile:experimental
FROM ubuntu as clone
LABEL maintainer="Luke Crooks <luke@pumalo.org>"

# Update aptitude with new repo
RUN apt-get update \
 && apt-get install -y git

# Make ssh dir
# Create known_hosts
# Add bitbuckets key
RUN mkdir /root/.ssh/ \
 && touch /root/.ssh/known_hosts \
 && ssh-keyscan bitbucket.org >> /root/.ssh/known_hosts

# Clone the conf files into the docker container
RUN --mount=type=secret,id=ssh_id,target=/root/.ssh/id_rsa \
    git clone git@bitbucket.org:User/repo.git

और आप इसके साथ निर्माण कर सकते हैं:

$ DOCKER_BUILDKIT=1 docker build -t your_image_name \
  --secret id=ssh_id,src=$(pwd)/id_rsa .

ध्यान दें कि इसके लिए अभी भी आपकी ssh कुंजी की आवश्यकता है कि पासवर्ड सुरक्षित न हो, लेकिन आप कम से कम एक चरण में निर्माण को चला सकते हैं, एक COPY कमांड को हटा सकते हैं, और किसी छवि का हिस्सा होने से ssh क्रेडेंशियल से बच सकते हैं।


बिल्डकिट ने सिर्फ ssh के लिए एक फीचर जोड़ा है जो आपको अभी भी आपके पासवर्ड को सुरक्षित ssh कीज़ की अनुमति देता है, परिणाम इस तरह दिखता है:

# syntax=docker/dockerfile:experimental
FROM ubuntu as clone
LABEL maintainer="Luke Crooks <luke@pumalo.org>"

# Update aptitude with new repo
RUN apt-get update \
 && apt-get install -y git

# Make ssh dir
# Create known_hosts
# Add bitbuckets key
RUN mkdir /root/.ssh/ \
 && touch /root/.ssh/known_hosts \
 && ssh-keyscan bitbucket.org >> /root/.ssh/known_hosts

# Clone the conf files into the docker container
RUN --mount=type=ssh \
    git clone git@bitbucket.org:User/repo.git

और आप इसके साथ निर्माण कर सकते हैं:

$ eval $(ssh-agent)
$ ssh-add ~/.ssh/id_rsa
(Input your passphrase here)
$ DOCKER_BUILDKIT=1 docker build -t your_image_name \
  --ssh default=$SSH_AUTH_SOCK .

फिर, इसे बिल्ड में बिना किसी इमेज लेयर के लिखे जाने पर इंजेक्ट किया जाता है, जिससे यह जोखिम दूर हो जाता है कि क्रेडेंशियल गलती से लीक हो सकता है।


डॉकटर को बाध्य करने के लिए मजबूर करने के लिए git cloneजब भी लाइनों को कैश किया गया हो, तो आप एक बिल्ड एआरजी को इंजेक्ट कर सकते हैं जो कैश को तोड़ने के लिए प्रत्येक बिल्ड के साथ बदलता है। जो दिखता है:

# inject a datestamp arg which is treated as an environment variable and
# will break the cache for the next RUN command
ARG DATE_STAMP
# Clone the conf files into the docker container
RUN git clone git@bitbucket.org:User/repo.git

तब आप उस बदलते बिल्ड कमांड में arg को इंजेक्ट करते हैं:

date_stamp=$(date +%Y%m%d-%H%M%S)
docker build --build-arg DATE_STAMP=$date_stamp .

आप डॉकर के बाहर से गिट का उपयोग करने का सुझाव देते हैं, हालांकि आप बताते हैं कि वैसे भी ssh कीज़ से कैसे निपटें। आप इसे कब आवश्यक / उचित मानते हैं?
JCarlosR

2
@JCarlosR जब आपके पास एक बाहरी प्रणाली नहीं है जिसमें निर्माण को चलाने के लिए (उदाहरण के लिए एक CI / CD सिस्टम है जो पहले से क्लोन चलाने में सक्षम है)। अपवाद हो सकते हैं, लेकिन डॉकरफाइल के अंदर एक क्लोन एक कोड गंध है।
BMitch

1

ऊपर के समाधान बिटबकेट के लिए काम नहीं करते थे। मुझे लगा कि यह चाल है:

RUN ssh-keyscan bitbucket.org >> /root/.ssh/known_hosts \
    && eval `ssh-agent` \
    && ssh-add ~/.ssh/[key] \
    && git clone git@bitbucket.org:[team]/[repo].git
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.