Skip to content

Add GitLab API base URL setting (Self-hosted)#631

Open
PhilixTheExplorer wants to merge 6 commits into
fossasia:mainfrom
PhilixTheExplorer:gitlab-base-url-setting
Open

Add GitLab API base URL setting (Self-hosted)#631
PhilixTheExplorer wants to merge 6 commits into
fossasia:mainfrom
PhilixTheExplorer:gitlab-base-url-setting

Conversation

@PhilixTheExplorer
Copy link
Copy Markdown
Contributor

@PhilixTheExplorer PhilixTheExplorer commented May 11, 2026

📌 Fixes

Part of #627


📝 Summary of Changes

  • Added a GitLab-only GitLab API Base URL field in the popup/settings UI.
  • Persisted gitlabBaseUrl in browser storage and restored it when the popup initializes.
  • Normalized the saved value by trimming whitespace and removing trailing slashes.
  • Required custom GitLab URLs to be explicit API v4 URLs, including subpath installs such as https://gitlab.example.com/gitlab/api/v4.
  • Added optional host permission handling for custom GitLab hosts.
  • Requested custom host permission only when generating a GitLab report.
  • Updated dark mode styling for the new URL input.
  • Addressed review feedback around duplicate storage writes, refresh behavior, and GitLab project mapping safety.

📸 Screenshots / Demo (if UI-related)

Both default & self-hosted gitlab flow:

OC7TaTYD6G

Permission request:

Screenshot (198)

Access granted:

Screenshot (199)

Report generation:

Screenshot (200)

Invalid base URL validation:

Screenshot (201)

✅ Checklist

  • I’ve tested my changes locally
  • I’ve added tests (if applicable)
  • I’ve updated documentation (if applicable)
  • My code follows the project’s code style guidelines

👀 Reviewer Notes

This PR intentionally keeps the scope limited to GitLab API base URL configuration and custom host permission handling.

Summary by Sourcery

Add support for configuring a GitLab API base URL, including self-hosted instances, and wire it through the popup UI, storage, and GitLab data fetching.

New Features:

  • Introduce a GitLab API base URL setting in the popup UI for GitLab users, including support for self-hosted instances.
  • Persist the GitLab API base URL in browser storage and apply it when generating GitLab-based scrum reports.
  • Request optional host permissions dynamically for custom GitLab hosts when generating GitLab reports.

Enhancements:

  • Refactor GitLab report data mapping to share logic and derive repository URLs from the configured API base URL.
  • Include the GitLab API base URL in the GitLabHelper cache key and allow GitLabHelper to be initialized with a custom base URL.
  • Update dark mode styling to support the new GitLab API base URL input field.

Build:

  • Adjust browser extension manifests to support optional permissions for custom GitLab host origins.

Summary by Sourcery

Add configurable GitLab API base URL support, including self-hosted instances, and wire it through the popup UI, storage, permissions, and GitLab data fetching.

New Features:

  • Introduce a GitLab API base URL input in the popup for GitLab users with self-hosted support.
  • Persist the GitLab API base URL in browser storage and apply it when generating GitLab-based scrum reports.
  • Dynamically request optional host permissions for custom GitLab hosts when generating GitLab reports.

Enhancements:

  • Normalize and validate GitLab API base URLs and surface user-facing validation feedback.
  • Ensure GitLab report generation and helper instances respect the configured API base URL and refresh behavior.
  • Improve dark mode styling to cover the new GitLab API base URL input field.

Build:

  • Update browser extension manifests to support optional permissions for configurable GitLab host origins.

Copilot AI review requested due to automatic review settings May 11, 2026 09:10
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented May 11, 2026

Reviewer's Guide

Adds a configurable GitLab API base URL for GitLab/self-hosted users, wires it through popup UI and storage, normalizes and validates it, and introduces dynamic optional host-permission handling plus small GitLab helper and styling tweaks.

Sequence diagram for GitLab API base URL handling on report generation

sequenceDiagram
    actor User
    participant PopupUI as popup.js
    participant Storage as browser.storage.local
    participant Permissions as browser.permissions
    participant GitLabHelper as window.GitLabHelper

    User ->> PopupUI: click generateBtn
    PopupUI ->> PopupUI: normalizeGitLabBaseUrl(gitlabBaseUrlInput.value)
    PopupUI ->> PopupUI: platform = platformSelect.value
    alt [platform is gitlab]
        PopupUI ->> PopupUI: isValidGitLabApiBaseUrl(gitlabBaseUrl)
        alt [url invalid]
            PopupUI ->> PopupUI: scrumHelperToast(gitlabBaseUrlInvalid)
            PopupUI -->> User: stop
        else [url valid]
            PopupUI ->> Permissions: requestGitLabHostPermission(gitlabBaseUrl)
            alt [permission granted]
                PopupUI ->> Storage: set({ platform, gitlabBaseUrl, gitlabUsername })
            else [permission denied or failed]
                PopupUI -->> User: stop
            end
        end
    else [platform is not gitlab]
        PopupUI ->> Storage: set({ platform, platformUsername })
    end

    Storage -->> PopupUI: get(['platform'])
    PopupUI ->> PopupUI: updatePlatformUI(platformSelect.value)
    PopupUI ->> PopupUI: generateScrumReport()

    Note over GitLabHelper,Storage: gitlabBaseUrl also read in forceGitlabDataRefresh
    GitLabHelper ->> Storage: chrome.storage.local.get(['gitlabBaseUrl'])
    Storage -->> GitLabHelper: gitlabBaseUrl
    GitLabHelper ->> GitLabHelper: new GitLabHelper(gitlabBaseUrl)
Loading

File-Level Changes

Change Details Files
Introduce GitLab API base URL input and persistence in popup and main scripts.
  • Add gitlabBaseUrl input field to the GitLab-only section in the popup HTML with label, tooltip, and placeholder wiring.
  • Read and write gitlabBaseUrl from browser.storage.local in popup and main scripts, including initial load and change handlers.
  • Normalize GitLab base URLs by trimming whitespace and removing trailing slashes before saving or using them.
src/popup.html
src/scripts/popup.js
src/scripts/main.js
Validate GitLab API base URL and request optional host permissions when generating GitLab reports.
  • Add isValidGitLabApiBaseUrl helper that enforces http/https, /api/v4 suffix, and no search/hash for custom URLs.
  • Compute an origin pattern from the GitLab base URL and request browser optional permissions for non-gitlab.com hosts before report generation.
  • Update generate button click handler to use the selected platform directly, include gitlabBaseUrl in storage, and gate GitLab generation on URL validity and permission success.
src/scripts/popup.js
Align GitLab helper usage and report mapping with configurable base URL and improve safety.
  • Make mapGitLabReportItem resilient to missing projectById map when deriving project/repo fields.
  • On forced GitLab refresh, reload gitlabBaseUrl from chrome.storage.local and reinitialize GitLabHelper with that base URL for subsequent API calls.
  • Include gitlabBaseUrl in the GitLab helper cache key and usage path via existing wiring (implied by helper re-init and storage use).
src/scripts/scrumHelper.js
Update dark mode styling to support the new base URL input type.
  • Extend dark mode CSS selector to style input[type="url"] alongside existing text/date inputs.
src/index.css
Adjust localization and manifests to support new GitLab base URL messaging and optional host permissions.
  • Add new i18n strings for GitLab base URL label, tooltip, placeholder, and host-permission toast messages.
  • Update Chrome and Firefox extension manifests to declare optional origins for custom GitLab hosts and/or permission prompts.
src/_locales/en/messages.json
src/manifests/chrome.json
src/manifests/firefox.json

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • The GitLab base URL normalization logic is duplicated in popup.js, main.js, and the GitLabHelper constructor; consider centralizing this in a small shared utility (or on the helper class) so trimming/removal of trailing slashes stays consistent in one place.
  • The new mapGitLabReportItem / mapGitLabReportData logic in scrumHelper.js is tightly coupled to GitLab API responses; consider moving this mapping into gitlabHelper.js so the helper encapsulates GitLab-specific transformations and scrumHelper only consumes a GitHub-shaped model.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The GitLab base URL normalization logic is duplicated in `popup.js`, `main.js`, and the `GitLabHelper` constructor; consider centralizing this in a small shared utility (or on the helper class) so trimming/removal of trailing slashes stays consistent in one place.
- The new `mapGitLabReportItem` / `mapGitLabReportData` logic in `scrumHelper.js` is tightly coupled to GitLab API responses; consider moving this mapping into `gitlabHelper.js` so the helper encapsulates GitLab-specific transformations and `scrumHelper` only consumes a GitHub-shaped model.

## Individual Comments

### Comment 1
<location path="src/scripts/scrumHelper.js" line_range="64-73" />
<code_context>
 	}
 }

+function mapGitLabReportItem(item, projects, type, gitlabApiBaseUrl) {
+	const project = projects.find((p) => p.id === item.project_id);
+	const repoName = project ? project.name : 'unknown';
+
+	return {
+		...item,
+		repository_url: `${gitlabApiBaseUrl}/projects/${item.project_id}`,
+		html_url:
+			type === 'issue'
+				? item.web_url || (project ? `${project.web_url}/-/issues/${item.iid}` : '')
+				: item.web_url || (project ? `${project.web_url}/-/merge_requests/${item.iid}` : ''),
+		number: item.iid,
+		title: item.title,
+		state: type === 'issue' && item.state === 'opened' ? 'open' : item.state,
+		project: repoName,
+		pull_request: type === 'mr',
+	};
+}
+
+function mapGitLabReportData(data, gitlabApiBaseUrl) {
+	const mappedIssues = (data.issues || []).map((issue) =>
+		mapGitLabReportItem(issue, data.projects, 'issue', gitlabApiBaseUrl),
+	);
</code_context>
<issue_to_address>
**issue (bug_risk):** Handle missing or undefined `data.projects` to avoid runtime errors when mapping GitLab items.

`mapGitLabReportItem` calls `projects.find(...)` assuming `projects` is always defined. If `data.projects` is `undefined` or missing from the GitLab response, `mapGitLabReportData` will pass `undefined` and this will throw. Consider defaulting `projects` to an empty array (e.g., `data.projects || []` or a default parameter in `mapGitLabReportItem`) so the mapping still works when project data is absent.
</issue_to_address>

### Comment 2
<location path="src/scripts/main.js" line_range="244-246" />
<code_context>
 	const value = gitlabTokenElement.value;
 	browser.storage.local.set({ gitlabToken: value });
 }
+function handleGitlabBaseUrlChange() {
+	const value = normalizeGitLabBaseUrl(gitlabBaseUrlElement.value);
+	browser.storage.local.set({ gitlabBaseUrl: value });
+}
 function handleProjectNameChange() {
</code_context>
<issue_to_address>
**nitpick:** Normalize the GitLab base URL input value in the UI as well as in storage.

Currently only the stored value is normalized; the input field still shows whatever the user typed (including trailing slashes/whitespace). Consider also assigning the normalized value back to `gitlabBaseUrlElement.value` so the UI shows the canonical URL after edits.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/scripts/scrumHelper.js Outdated
Comment thread src/scripts/main.js
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for self-hosted GitLab by introducing a configurable GitLab API base URL in the extension settings/popup, persisting it to storage, and requesting optional host permissions only when needed for report generation.

Changes:

  • Added a GitLab-only “GitLab API Base URL” field (UI + storage restore/persist + normalization/validation).
  • Updated GitLab fetching/mapping to use a configurable API base URL (and include it in cache keys).
  • Introduced optional host-permission requesting for custom GitLab hosts, plus dark-mode styling for the new URL input.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/scripts/scrumHelper.js Loads/stores gitlabBaseUrl, instantiates GitLabHelper with it, and maps GitLab report items using the helper’s base URL.
src/scripts/popup.js Adds base URL normalization/validation and requests optional host permissions before generating GitLab reports.
src/scripts/main.js Persists/restores gitlabBaseUrl for non-popup/legacy settings flow.
src/scripts/gitlabHelper.js Accepts configurable API base URL, normalizes it, and namespaces cache keys by base URL.
src/popup.html Adds the GitLab-only base URL input with i18n label/tooltip/placeholder.
src/manifests/chrome.json Declares optional host permissions to enable requesting access to custom GitLab hosts.
src/manifests/firefox.json Declares optional host permissions to enable requesting access to custom GitLab hosts.
src/index.css Extends dark-mode input styling to include type="url".
src/_locales/en/messages.json Adds English i18n strings for the new base URL UI and permission toasts/validation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/scripts/popup.js Outdated
Comment thread src/scripts/popup.js
Comment thread src/scripts/scrumHelper.js
Comment thread src/_locales/en/messages.json
Comment thread src/manifests/chrome.json Outdated
Comment thread src/manifests/firefox.json Outdated
@PhilixTheExplorer PhilixTheExplorer marked this pull request as draft May 11, 2026 10:07
@github-actions github-actions Bot removed the core label May 13, 2026
Copilot AI review requested due to automatic review settings May 13, 2026 14:55
@PhilixTheExplorer PhilixTheExplorer force-pushed the gitlab-base-url-setting branch from 9ffe8be to df2125a Compare May 13, 2026 14:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.

Comment thread src/scripts/popup.js Outdated
Comment thread src/scripts/popup.js Outdated
Comment thread src/scripts/popup.js Outdated
Comment thread src/scripts/main.js
Comment thread src/manifests/chrome.json Outdated
Comment thread src/manifests/firefox.json Outdated
@PhilixTheExplorer PhilixTheExplorer force-pushed the gitlab-base-url-setting branch from df2125a to 1566c8f Compare May 13, 2026 16:45
Copilot AI review requested due to automatic review settings May 13, 2026 17:48
@github-actions github-actions Bot added the core label May 13, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Comment thread src/scripts/main.js
function handleGitlabBaseUrlChange() {
const value = normalizeGitLabBaseUrl(gitlabBaseUrlElement.value);
gitlabBaseUrlElement.value = value;
browser.storage.local.set({ gitlabBaseUrl: value });
Comment thread src/scripts/popup.js
Comment on lines +456 to +463
try {
const url = new URL(baseUrl);
return (
(url.protocol === 'http:' || url.protocol === 'https:') &&
url.pathname.endsWith('/api/v4') &&
url.search === '' &&
url.hash === ''
);
@PhilixTheExplorer PhilixTheExplorer marked this pull request as ready for review May 13, 2026 17:54
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • The normalizeGitLabBaseUrl helper is duplicated in both popup.js and main.js; consider extracting it into a shared utility to avoid divergence and keep behavior consistent.
  • In forceGitlabDataRefresh you use chrome.storage.local while the rest of the code relies on browser.storage.local; aligning on a single storage API (or a common wrapper) will simplify cross‑browser behavior and reduce surprises.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `normalizeGitLabBaseUrl` helper is duplicated in both `popup.js` and `main.js`; consider extracting it into a shared utility to avoid divergence and keep behavior consistent.
- In `forceGitlabDataRefresh` you use `chrome.storage.local` while the rest of the code relies on `browser.storage.local`; aligning on a single storage API (or a common wrapper) will simplify cross‑browser behavior and reduce surprises.

## Individual Comments

### Comment 1
<location path="src/scripts/popup.js" line_range="449-450" />
<code_context>

 	window.updateGenerateButtonState = updateGenerateButtonState;

+	function normalizeGitLabBaseUrl(baseUrl) {
+		return baseUrl.trim().replace(/\/+$/, '');
+	}
+
</code_context>
<issue_to_address>
**suggestion:** Consider de-duplicating normalizeGitLabBaseUrl into a shared utility.

This implementation is duplicated here and in main.js. Centralizing it in a shared helper or namespace will keep behavior consistent and avoid having to update multiple call sites when the normalization logic changes.

Suggested implementation:

```javascript
	function normalizeGitLabBaseUrl(baseUrl) {
		return GitLabUtils.normalizeGitLabBaseUrl(baseUrl);
	}

```

To fully de-duplicate the implementation and centralize the behavior:

1. Introduce a shared utility (for example, `src/scripts/gitlab-utils.js`) with something like:
   ```js
   const GitLabUtils = {
     normalizeGitLabBaseUrl(baseUrl) {
       return baseUrl.trim().replace(/\/+$/, '');
     },
   };

   window.GitLabUtils = GitLabUtils;
   ```
2. Ensure `gitlab-utils.js` is loaded before both `main.js` and `popup.js` (e.g., via `<script>` tag order or bundler configuration).
3. Update `main.js` to replace its inline `normalizeGitLabBaseUrl` implementation with `GitLabUtils.normalizeGitLabBaseUrl(baseUrl)` so both `main.js` and `popup.js` share the same normalization logic.
</issue_to_address>

### Comment 2
<location path="src/scripts/scrumHelper.js" line_range="1954-1955" />
<code_context>
 	hasInjectedContent = false;
 	// Re-instantiate gitlabHelper to ensure a fresh instance for next API call
 	if (window.GitLabHelper) {
+		const items = await chrome.storage.local.get(['gitlabBaseUrl']);
+		gitlabBaseUrl = items.gitlabBaseUrl || '';
 		gitlabHelper = new window.GitLabHelper(gitlabBaseUrl);
 	}
</code_context>
<issue_to_address>
**issue (bug_risk):** Using chrome.storage here may break in environments where only browser.storage is available.

This block calls `chrome.storage.local` while nearby code uses the `browser.*` API. In Firefox or WebExtension-polyfill environments, `chrome` may be undefined and cause `forceGitlabDataRefresh` to throw before `GitLabHelper` is recreated. Please reuse the existing storage abstraction (e.g., `browser.storage.local` or a shared wrapper), or at least fall back to `browser.storage` when `chrome` is unavailable to keep this cross-browser safe.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/scripts/popup.js
Comment on lines +449 to +450
function normalizeGitLabBaseUrl(baseUrl) {
return baseUrl.trim().replace(/\/+$/, '');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider de-duplicating normalizeGitLabBaseUrl into a shared utility.

This implementation is duplicated here and in main.js. Centralizing it in a shared helper or namespace will keep behavior consistent and avoid having to update multiple call sites when the normalization logic changes.

Suggested implementation:

	function normalizeGitLabBaseUrl(baseUrl) {
		return GitLabUtils.normalizeGitLabBaseUrl(baseUrl);
	}

To fully de-duplicate the implementation and centralize the behavior:

  1. Introduce a shared utility (for example, src/scripts/gitlab-utils.js) with something like:
    const GitLabUtils = {
      normalizeGitLabBaseUrl(baseUrl) {
        return baseUrl.trim().replace(/\/+$/, '');
      },
    };
    
    window.GitLabUtils = GitLabUtils;
  2. Ensure gitlab-utils.js is loaded before both main.js and popup.js (e.g., via <script> tag order or bundler configuration).
  3. Update main.js to replace its inline normalizeGitLabBaseUrl implementation with GitLabUtils.normalizeGitLabBaseUrl(baseUrl) so both main.js and popup.js share the same normalization logic.

Comment on lines +1954 to +1955
const items = await chrome.storage.local.get(['gitlabBaseUrl']);
gitlabBaseUrl = items.gitlabBaseUrl || '';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Using chrome.storage here may break in environments where only browser.storage is available.

This block calls chrome.storage.local while nearby code uses the browser.* API. In Firefox or WebExtension-polyfill environments, chrome may be undefined and cause forceGitlabDataRefresh to throw before GitLabHelper is recreated. Please reuse the existing storage abstraction (e.g., browser.storage.local or a shared wrapper), or at least fall back to browser.storage when chrome is unavailable to keep this cross-browser safe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core extension frontend javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants