Skip to content

Commit 669734d

Browse files
authored
Support cloning multiple repositories and specifying AZDO_BRANCH (#29)
* Support cloning multiple repositories * Support AZDO_BRANCH scenarios * New tests for both features
1 parent b4ebf92 commit 669734d

7 files changed

Lines changed: 300 additions & 146 deletions

File tree

src/external-repository/NOTES.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,26 @@ If a user configures a Codespaces User Secret named `ADO_SECRET` and assigns thi
5454
Codespace, then the value of that secret will be used as a PAT for authentication. If the secret
5555
is not defined by the user it will fallback to the browser login.
5656

57+
## Multiple Repository Support
58+
59+
As of version 3, you can clone multiple repositories by separating the URL's with a comma. In this
60+
mode all of the repositories will be cloned to the folder. Each will get a local folder name from the
61+
last part of the clone URL so this value has to be unique for each repository specified.
62+
63+
## AzDO Branch Support
64+
65+
When `external-git config` is executed it will check the branch name of the Codespaces bridge repository
66+
and if it begins with "azdo/" then it will treat the rest of the branch name as an AzDO branch name
67+
to checkout on the external repository. The idea here is that a utility could be created in AzDO that
68+
would let you open a Pull Request in a Codespace. The process would create a new branch in the bridge
69+
repository named "azdo/branch/name" and then create the Codespace on that branch name. When the Codespace
70+
opens and clones the AzDO repository default branch it will then detect the need to fetch and checkout
71+
the requested branch.
72+
73+
If a different process is desired for determining the branch name, then an environment variabled named
74+
`AZDO_BRANCH` can be created with the name of the branch that should be checked out. When the `external-git config`
75+
command runs it will also detect that this envvar is set and checkout that
76+
5777
## Usage Telemetry
5878

5979
If you are looking for ways to track usage of Codespaces within your team, we offer a mechanism

src/external-repository/devcontainer-feature.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "External Git Repository in Codespace",
33
"id": "external-repository",
4-
"version": "2.0.1",
4+
"version": "3.0.0",
55
"description": "Configures Codespace to work with an external Git repository",
66
"options": {
77
"gitProvider": {
@@ -16,7 +16,7 @@
1616
"cloneUrl": {
1717
"type": "string",
1818
"default": "",
19-
"description": "Clone URL without username: https://dev.azure.com/{organization}/{project}/_git/{repository}"
19+
"description": "Clone URL without username: https://dev.azure.com/{organization}/{project}/_git/{repository}. Separate multiple URLs with comma."
2020
},
2121
"folder": {
2222
"type": "string",
Lines changed: 103 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,93 @@
11
#!/usr/bin/env bash
22

3-
43
# Change to the directory where this script is located
54
cd "$(dirname "$0")"
65

76
# Load the variables
87
source ./variables.sh
98

9+
clone_repository() {
10+
set -e
11+
GIT_REPO_URL=${1}
12+
GIT_LOCAL_PATH=${2}
13+
14+
# If .git directory exists, then we can assume that the repo has already been cloned
15+
if [ ! -d "${GIT_LOCAL_PATH}"/.git ]; then
16+
# Check if ${HOME}/.gitconfig exists and save it
17+
if [ -f ${HOME}/.gitconfig ]; then
18+
mv ${HOME}/.gitconfig ${HOME}/.gitconfig.external_git_feature
19+
fi
20+
21+
if [[ "${EXT_GIT_PREBUILD_PAT}" == "" ]]; then
22+
# Put the ado-auth-helper git config in place
23+
ADO_HELPER=$(echo ~)/ado-auth-helper
24+
sed "s|ADO_HELPER_PATH|${ADO_HELPER}|g" "./ado-git.config" > ${HOME}/.gitconfig
25+
else
26+
# Put the prebuild git config in place
27+
cp /usr/local/external-repository-feature/prebuild-git.config ${HOME}/.gitconfig
28+
fi
29+
30+
# Perform a git clone
31+
if [[ "${EXT_GIT_SCALAR}" != "true" ]]; then
32+
echo "Cloning ${GIT_REPO_URL} to ${GIT_LOCAL_PATH}"
33+
timeout ${EXT_GIT_CLONE_TIMEOUT} git clone ${EXT_GIT_OPTIONS} "${GIT_REPO_URL}" "${GIT_LOCAL_PATH}"
34+
if [ $? -eq 124 ]; then
35+
echo "git clone command timed out..."
36+
fi
37+
else
38+
# Perform a scalar clone
39+
echo "Cloning ${GIT_REPO_URL} to ${GIT_LOCAL_PATH} using scalar"
40+
41+
# Scalar cannot clone into an existing folder so we need to remove it
42+
# Anyone using workspaceFolder in Codespaces will have created this folder already
43+
# so this will be a common scenario. We have already confirmed there is no .git folder
44+
# let's just do one more check to make sure there is not a src/.git folder which
45+
# would indicate a previous Scalar clone has been done
46+
if [ -d "${GIT_LOCAL_PATH}"/src/.git ]; then
47+
echo "Repository already cloned"
48+
rm ${HOME}/.gitconfig
49+
# Put back the original .gitconfig if it exists
50+
if [ -f ${HOME}/.gitconfig.external_git_feature ]; then
51+
mv ${HOME}/.gitconfig.external_git_feature ${HOME}/.gitconfig
52+
fi
53+
return 0
54+
fi
55+
56+
# Remove the local path if it exists
57+
if [ -d "${GIT_LOCAL_PATH}" ]; then
58+
rm -rf "${GIT_LOCAL_PATH}"
59+
fi
60+
61+
timeout ${EXT_GIT_CLONE_TIMEOUT} scalar clone ${EXT_GIT_OPTIONS} "${GIT_REPO_URL}" "${GIT_LOCAL_PATH}"
62+
if [ $? -eq 124 ]; then
63+
echo "scalar clone command timed out..."
64+
fi
65+
# Figure out the where the .git directory is and change to the parent. Can vary whether --no-src is used
66+
if [ -d "${GIT_LOCAL_PATH}"/.git ]; then
67+
cd "${GIT_LOCAL_PATH}"
68+
else
69+
cd "${GIT_LOCAL_PATH}"/src
70+
fi
71+
if [[ "${EXT_GIT_SPARSECHECKOUT}" != "" ]]; then
72+
timeout ${EXT_GIT_CLONE_TIMEOUT} git sparse-checkout add ${EXT_GIT_SPARSECHECKOUT}
73+
if [ $? -eq 124 ]; then
74+
echo "git sparse-checkout command timed out..."
75+
fi
76+
fi
77+
cd "$(dirname "$0")"
78+
fi
79+
80+
rm ${HOME}/.gitconfig
81+
# Put back the original .gitconfig if it exists
82+
if [ -f ${HOME}/.gitconfig.external_git_feature ]; then
83+
mv ${HOME}/.gitconfig.external_git_feature ${HOME}/.gitconfig
84+
fi
85+
else
86+
echo "Repository already cloned"
87+
fi
88+
89+
}
90+
1091
if [[ "${EXT_GIT_REPO_URL}" == "" ]]; then
1192
echo "Clone URL is not set"
1293
exit 0;
@@ -39,79 +120,24 @@ else
39120
fi
40121
fi
41122

42-
set -e
43-
44-
# If .git directory exists, then we can assume that the repo has already been cloned
45-
if [ ! -d "${EXT_GIT_LOCAL_PATH}"/.git ]; then
46-
# Check if ${HOME}/.gitconfig exists and save it
47-
if [ -f ${HOME}/.gitconfig ]; then
48-
mv ${HOME}/.gitconfig ${HOME}/.gitconfig.external_git_feature
49-
fi
50-
51-
if [[ "${EXT_GIT_PREBUILD_PAT}" == "" ]]; then
52-
# Put the ado-auth-helper git config in place
53-
ADO_HELPER=$(echo ~)/ado-auth-helper
54-
sed "s|ADO_HELPER_PATH|${ADO_HELPER}|g" "./ado-git.config" > ${HOME}/.gitconfig
55-
else
56-
# Put the prebuild git config in place
57-
cp /usr/local/external-repository-feature/prebuild-git.config ${HOME}/.gitconfig
58-
fi
59-
60-
# Perform a git clone
61-
if [[ "${EXT_GIT_SCALAR}" != "true" ]]; then
62-
echo "Cloning ${EXT_GIT_REPO_URL} to ${EXT_GIT_LOCAL_PATH}"
63-
timeout ${EXT_GIT_CLONE_TIMEOUT} git clone ${EXT_GIT_OPTIONS} "${EXT_GIT_REPO_URL}" "${EXT_GIT_LOCAL_PATH}"
64-
if [ $? -eq 124 ]; then
65-
echo "git clone command timed out..."
66-
fi
67-
else
68-
# Perform a scalar clone
69-
echo "Cloning ${EXT_GIT_REPO_URL} to ${EXT_GIT_LOCAL_PATH} using scalar"
70-
71-
# Scalar cannot clone into an existing folder so we need to remove it
72-
# Anyone using workspaceFolder in Codespaces will have created this folder already
73-
# so this will be a common scenario. We have already confirmed there is no .git folder
74-
# let's just do one more check to make sure there is not a src/.git folder which
75-
# would indicate a previous Scalar clone has been done
76-
if [ -d "${EXT_GIT_LOCAL_PATH}"/src/.git ]; then
77-
echo "Repository already cloned"
78-
rm ${HOME}/.gitconfig
79-
# Put back the original .gitconfig if it exists
80-
if [ -f ${HOME}/.gitconfig.external_git_feature ]; then
81-
mv ${HOME}/.gitconfig.external_git_feature ${HOME}/.gitconfig
82-
fi
83-
exit 0
84-
fi
85-
86-
# Remove the local path if it exists
87-
if [ -d "${EXT_GIT_LOCAL_PATH}" ]; then
88-
rm -rf "${EXT_GIT_LOCAL_PATH}"
89-
fi
90-
91-
timeout ${EXT_GIT_CLONE_TIMEOUT} scalar clone ${EXT_GIT_OPTIONS} "${EXT_GIT_REPO_URL}" "${EXT_GIT_LOCAL_PATH}"
92-
if [ $? -eq 124 ]; then
93-
echo "scalar clone command timed out..."
94-
fi
95-
# Figure out the where the .git directory is and change to the parent. Can vary whether --no-src is used
96-
if [ -d "${EXT_GIT_LOCAL_PATH}"/.git ]; then
97-
cd "${EXT_GIT_LOCAL_PATH}"
98-
else
99-
cd "${EXT_GIT_LOCAL_PATH}"/src
100-
fi
101-
if [[ "${EXT_GIT_SPARSECHECKOUT}" != "" ]]; then
102-
timeout ${EXT_GIT_CLONE_TIMEOUT} git sparse-checkout add ${EXT_GIT_SPARSECHECKOUT}
103-
if [ $? -eq 124 ]; then
104-
echo "git sparse-checkout command timed out..."
105-
fi
106-
fi
107-
cd "$(dirname "$0")"
108-
fi
109-
110-
rm ${HOME}/.gitconfig
111-
# Put back the original .gitconfig if it exists
112-
if [ -f ${HOME}/.gitconfig.external_git_feature ]; then
113-
mv ${HOME}/.gitconfig.external_git_feature ${HOME}/.gitconfig
114-
fi
115-
else
116-
echo "Repository already cloned"
117-
fi
123+
# Split EXT_GIT_REPO_URL into an array based on a comma delimiter
124+
IFS=',' read -ra EXT_GIT_REPO_URL_ARRAY <<< "${EXT_GIT_REPO_URL}"
125+
# If there is more than one repo URL, then we need to clone each one
126+
# When there is more than one repo URL, the EXT_GIT_LOCAL_PATH is the parent folder
127+
# that will contain the repositories. When there is only one repo URL, the EXT_GIT_LOCAL_PATH
128+
# is the folder that will contain the repository
129+
if [ ${#EXT_GIT_REPO_URL_ARRAY[@]} -gt 1 ]; then
130+
# Loop through each repo URL
131+
for i in "${EXT_GIT_REPO_URL_ARRAY[@]}"; do
132+
# Set the repo URL to the current value
133+
REPO_URL=$i
134+
# Get the folder name from the last part of the URL
135+
REPO_FOLDER=$(echo "${REPO_URL}" | awk -F'/' '{print $NF}' | awk -F'.' '{print $1}')
136+
LOCAL_PATH=${EXT_GIT_LOCAL_PATH}/${REPO_FOLDER}
137+
# Clone the repo
138+
clone_repository ${REPO_URL} ${LOCAL_PATH}
139+
done
140+
exit 0
141+
fi
142+
# There was only one repository specified
143+
clone_repository ${EXT_GIT_REPO_URL} ${EXT_GIT_LOCAL_PATH}

0 commit comments

Comments
 (0)