Skip to content

Commit 05aaf34

Browse files
authored
Merge pull request #58 from alex-feel/alex-feel-dev
Convert GitLab web URLs to API format for authentication
2 parents 9bf38c0 + 225b288 commit 05aaf34

File tree

2 files changed

+116
-10
lines changed

2 files changed

+116
-10
lines changed

README.md

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,14 @@ iex (irm 'https://raw.githubusercontent.com/alex-feel/claude-code-toolbox/main/s
3636

3737
##### Option 5: Using private repository configurations
3838
```powershell
39-
# For private GitLab repositories
40-
$env:CLAUDE_ENV_CONFIG='https://gitlab.company.com/path/to/config.yaml'
39+
# For private GitLab repositories (supports both web and API URLs)
40+
# Web URL format (automatically converted to API format):
41+
$env:CLAUDE_ENV_CONFIG='https://gitlab.company.com/namespace/project/-/raw/main/path/to/config.yaml'
42+
$env:GITLAB_TOKEN='glpat-YOUR-TOKEN-HERE'
43+
iex (irm 'https://raw.githubusercontent.com/alex-feel/claude-code-toolbox/main/scripts/windows/setup-environment.ps1')
44+
45+
# API URL format (if you prefer to use it directly):
46+
$env:CLAUDE_ENV_CONFIG='https://gitlab.company.com/api/v4/projects/namespace%2Fproject/repository/files/path%2Fto%2Fconfig.yaml/raw?ref=main'
4147
$env:GITLAB_TOKEN='glpat-YOUR-TOKEN-HERE'
4248
iex (irm 'https://raw.githubusercontent.com/alex-feel/claude-code-toolbox/main/scripts/windows/setup-environment.ps1')
4349
@@ -47,7 +53,7 @@ $env:GITHUB_TOKEN='ghp_YOUR-TOKEN-HERE'
4753
iex (irm 'https://raw.githubusercontent.com/alex-feel/claude-code-toolbox/main/scripts/windows/setup-environment.ps1')
4854
4955
# One-liner with escaping (use -NoExit to see any errors)
50-
powershell -NoProfile -NoExit -ExecutionPolicy Bypass -Command "`$env:CLAUDE_ENV_CONFIG='https://gitlab.company.com/path/to/config.yaml'; `$env:GITLAB_TOKEN='glpat-YOUR-TOKEN'; iex (irm 'https://raw.githubusercontent.com/alex-feel/claude-code-toolbox/main/scripts/windows/setup-environment.ps1')"
56+
powershell -NoProfile -NoExit -ExecutionPolicy Bypass -Command "`$env:CLAUDE_ENV_CONFIG='https://gitlab.company.com/namespace/project/-/raw/main/config.yaml'; `$env:GITLAB_TOKEN='glpat-YOUR-TOKEN'; iex (irm 'https://raw.githubusercontent.com/alex-feel/claude-code-toolbox/main/scripts/windows/setup-environment.ps1')"
5157
```
5258

5359
#### macOS
@@ -58,8 +64,8 @@ CLAUDE_ENV_CONFIG=python curl -fsSL https://raw.githubusercontent.com/alex-feel/
5864
# Local config file
5965
CLAUDE_ENV_CONFIG=./my-env.yaml curl -fsSL https://raw.githubusercontent.com/alex-feel/claude-code-toolbox/main/scripts/macos/setup-environment.sh | bash
6066

61-
# Private GitLab repository
62-
CLAUDE_ENV_CONFIG='https://gitlab.company.com/path/to/config.yaml' \
67+
# Private GitLab repository (web URL - auto-converted)
68+
CLAUDE_ENV_CONFIG='https://gitlab.company.com/namespace/project/-/raw/main/config.yaml' \
6369
GITLAB_TOKEN='glpat-YOUR-TOKEN' \
6470
curl -fsSL https://raw.githubusercontent.com/alex-feel/claude-code-toolbox/main/scripts/macos/setup-environment.sh | bash
6571

@@ -77,8 +83,8 @@ CLAUDE_ENV_CONFIG=python curl -fsSL https://raw.githubusercontent.com/alex-feel/
7783
# Local config file
7884
CLAUDE_ENV_CONFIG=./my-env.yaml curl -fsSL https://raw.githubusercontent.com/alex-feel/claude-code-toolbox/main/scripts/linux/setup-environment.sh | bash
7985

80-
# Private GitLab repository
81-
CLAUDE_ENV_CONFIG='https://gitlab.company.com/path/to/config.yaml' \
86+
# Private GitLab repository (web URL - auto-converted)
87+
CLAUDE_ENV_CONFIG='https://gitlab.company.com/namespace/project/-/raw/main/config.yaml' \
8288
GITLAB_TOKEN='glpat-YOUR-TOKEN' \
8389
curl -fsSL https://raw.githubusercontent.com/alex-feel/claude-code-toolbox/main/scripts/linux/setup-environment.sh | bash
8490

@@ -146,9 +152,12 @@ For configurations stored in private repositories, you need to provide authentic
146152

147153
- **Use `-NoExit` flag on Windows**: When running from PowerShell shortcuts or Run dialog, add `-NoExit` to see any authentication errors
148154
- **Check token permissions**: Ensure your token has read access to the repository and all referenced resources
149-
- **URL format matters**: Use the raw file URL, not the web interface URL
150-
- GitLab: `https://gitlab.com/api/v4/projects/{id}/repository/files/{path}/raw`
151-
- GitHub: `https://raw.githubusercontent.com/{org}/{repo}/{branch}/{path}`
155+
- **GitLab URLs**: Both web and API formats are supported
156+
- Web format (auto-converted): `https://gitlab.com/namespace/project/-/raw/branch/path/to/file`
157+
- API format: `https://gitlab.com/api/v4/projects/namespace%2Fproject/repository/files/path%2Fto%2Ffile/raw?ref=branch`
158+
- The setup script automatically converts web URLs to API format for authentication
159+
- **GitHub URLs**: Use raw content URL
160+
- Format: `https://raw.githubusercontent.com/{org}/{repo}/{branch}/{path}`
152161

153162
---
154163

scripts/setup_environment.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,96 @@ def detect_repo_type(url: str) -> str | None:
162162
return None
163163

164164

165+
def convert_gitlab_url_to_api(url: str) -> str:
166+
"""Convert GitLab web UI URL to API URL for authentication.
167+
168+
GitLab web UI URLs don't accept API tokens via headers.
169+
We need to use the API endpoint for private repo access.
170+
171+
Converts:
172+
- From: https://gitlab.com/namespace/project/-/raw/branch/path/to/file
173+
- To: https://gitlab.com/api/v4/projects/namespace%2Fproject/repository/files/path%2Fto%2Ffile/raw?ref=branch
174+
175+
Args:
176+
url: GitLab web UI raw URL
177+
178+
Returns:
179+
GitLab API URL that accepts PRIVATE-TOKEN header
180+
"""
181+
# Check if it's already an API URL
182+
if '/api/v4/projects/' in url:
183+
return url
184+
185+
# Check if it's a GitLab web UI raw URL
186+
if '/-/raw/' not in url:
187+
return url # Not a GitLab raw URL, return as-is
188+
189+
# Parse the URL to extract components
190+
# Format: https://gitlab.com/namespace/project/-/raw/branch/path/to/file?query
191+
try:
192+
# Split off query parameters first
193+
base_url, _, query = url.partition('?')
194+
195+
# Extract the domain and path
196+
if base_url.startswith('https://'):
197+
domain_end = base_url.index('/', 8) # Find end of domain after https://
198+
domain = base_url[:domain_end]
199+
path = base_url[domain_end + 1:] # Skip the /
200+
elif base_url.startswith('http://'):
201+
domain_end = base_url.index('/', 7) # Find end of domain after http://
202+
domain = base_url[:domain_end]
203+
path = base_url[domain_end + 1:] # Skip the /
204+
else:
205+
return url # Unknown format
206+
207+
# Split the path by /-/raw/
208+
parts = path.split('/-/raw/')
209+
if len(parts) != 2:
210+
return url # Unexpected format
211+
212+
project_path = parts[0] # e.g., "ai/claude-code-configs"
213+
remainder = parts[1] # e.g., "main/environments/examples/file.yaml"
214+
215+
# Split remainder into branch and file path
216+
# The branch is the first part before /
217+
branch_end = remainder.find('/')
218+
if branch_end == -1:
219+
# No file path, just branch
220+
branch = remainder
221+
file_path = ''
222+
else:
223+
branch = remainder[:branch_end]
224+
file_path = remainder[branch_end + 1:]
225+
226+
# URL-encode the project path for API (namespace/project -> namespace%2Fproject)
227+
encoded_project = urllib.parse.quote(project_path, safe='')
228+
229+
# URL-encode the file path for API
230+
encoded_file = urllib.parse.quote(file_path, safe='')
231+
232+
# Extract ref parameter from query if present (it overrides branch)
233+
ref = branch
234+
if query:
235+
# Parse query parameters
236+
params = urllib.parse.parse_qs(query)
237+
# Check for ref or ref_type parameters
238+
if 'ref' in params:
239+
ref = params['ref'][0]
240+
elif 'ref_type' in params and branch:
241+
# ref_type is just metadata, use the branch from path
242+
ref = branch
243+
244+
# Build the API URL
245+
api_url = f'{domain}/api/v4/projects/{encoded_project}/repository/files/{encoded_file}/raw?ref={ref}'
246+
247+
info('Converted GitLab URL to API format for authentication')
248+
return api_url
249+
250+
except (ValueError, IndexError) as e:
251+
warning(f'Could not convert GitLab URL to API format: {e}')
252+
return url # Return original if conversion fails
253+
254+
165255
def get_auth_headers(url: str, auth_param: str | None = None) -> dict[str, str]:
166256
"""Get authentication headers using multiple fallback methods.
167257
@@ -524,6 +614,13 @@ def fetch_url_with_auth(url: str, auth_headers: dict[str, str] | None = None, au
524614
HTTPError: If the HTTP request fails after authentication attempts
525615
URLError: If there's a URL/network error (including SSL issues)
526616
"""
617+
# Convert GitLab web URLs to API URLs for authentication
618+
original_url = url
619+
if detect_repo_type(url) == 'gitlab' and '/-/raw/' in url:
620+
url = convert_gitlab_url_to_api(url)
621+
if url != original_url:
622+
info(f'Using API URL: {url}')
623+
527624
# First try without auth (for public repos)
528625
try:
529626
request = Request(url)

0 commit comments

Comments
 (0)