Skip to content

Work trackers: Jira, Linear and Azure Boards ​

CodeLotse links pull requests and issues to tickets in Jira Cloud, Jira Data Center, Linear and Azure Boards. Linked tickets feed their acceptance criteria into the acceptance check, receive the triage labels you map, follow the pull request status and get a link back to the pull request. Tenant admins manage connections under Trackers in the tenant navigation.

How issues are linked ​

  • Keys in pull requests. ABC-123 in a pull request title (upper case) or branch name (any case, for example feature/abc-123-export) links the ticket, but only for the project keys configured on the connection (Linear: team keys). Keys of other projects and look-alikes such as UTF-8 are ignored. For Azure Boards, AB#123 in the title or description links work item 123; mentions inside code blocks and quotes are ignored. At most three tickets are linked per pull request.
  • Keys in repository issues are detected the same way from the issue title.
  • New repository issues can create a ticket (setting "Create tracker issues … in project"). Before creating one, CodeLotse searches the project (JQL, GraphQL search or WIQL) for a ticket with the same title and links that instead.
  • Backfill ("Link existing pull requests and issues") scans the open pull requests and issues of the tenant page by page and links every key it finds.

What is synchronised ​

FieldDirectionNotes
Acceptance criteriaTracker → CodeLotseRead only. From the configured field (Jira custom field such as customfield_10042, Azure Boards field such as Microsoft.VSTS.Common.AcceptanceCriteria) or, when no field is set, from the description. Jira ADF and wiki markup and Azure HTML are converted to Markdown.
LabelsConfigurableOnly mapped labels take part. Each mapping line pairs a repository label with a tracker label, or with a Jira component (area:api = component:API). Labels are synchronised for repository issues, not for pull requests.
StatusRepository → trackerOptional transitions when a pull request opens, is merged, or is closed without merging. A manual status change in the tracker is not undone.
Link commentRepository → trackerOne comment with the pull request or issue URL, posted once per link.

Ownership of the labels decides whose changes travel:

  • The tracker: tracker changes reach the repository; repository changes stay local.
  • The repository: repository changes reach the tracker; tracker changes stay local.
  • Both (default): changes travel both ways. When both sides changed the same set since the last sync, the conflict policy decides: the later change wins (default, by the update time each side reports), the tracker wins, the repository wins, or both changes are kept (set merge).

Loop prevention. CodeLotse stores, per link and field, the value both sides last agreed on and every write it has queued. When its own write comes back as a webhook, the new value equals the agreed value and nothing is written again. A write that is still queued is applied in the comparison before it lands, so stale or partial webhooks do not undo it. Markers live only in CodeLotse's sync state and in the audit log (tracker.* actions with actor publisher), never in text visible on the ticket. A write that fails is retried on the next change instead of reverting the other side. The test suite runs a bidirectional stress test with random concurrent edits on both sides and checks that the sides converge without extra writes.

Writing modes ​

  • Off: tickets are only linked (for the acceptance check and the links list).
  • Trial (default): every sync is planned and logged (tracker sync writes not applied), nothing is written.
  • On: changes are written through the publisher, with a secret scan of comments and created tickets, idempotency per change, retries with back-off and a dead-letter queue. Failures appear as "Last error" on the connection.

Webhooks ​

Every connection has its own webhook URL, shown on the connection: <PUBLIC_URL>/webhooks/jira/<connection id>, /webhooks/linear/<id> or /webhooks/azure-boards/<id>. Deliveries are verified per connection before anything is read from them, stored once per delivery id (X-Atlassian-Webhook-Identifier, Linear-Delivery, Azure notification id; Jira Data Center without the header falls back to a hash of the body) and only trigger a fresh read of the ticket through the tracker API; CodeLotse does not trust ticket content from webhook bodies. An unknown connection and a bad signature both get 401; repeated failures from one address are rate limited (429). Webhook bodies are not stored.

The webhook secret is shown once when the connection is created or rotated ("New webhook secret"). Store it in the tracker:

  • Jira Cloud (Settings → System → WebHooks) and Jira Data Center versions with webhook secrets: URL above, the secret in the "Secret" field, events Issue created/updated/deleted and Comment created. Jira signs with X-Hub-Signature: sha256=….
  • Linear (Settings → API → Webhooks): URL above, resource types Issues and Comments. Linear generates its own signing secret; paste it into the connection settings ("Linear signing secret"). Linear signs with Linear-Signature (HMAC-SHA256); deliveries older than 60 seconds are refused.
  • Azure Boards (Project settings → Service hooks → Web Hooks): events Work item created/updated/deleted/commented on, URL above, basic authentication with user name quavon and the secret as password.

Connecting a tracker &ZeroWidthSpace;

Jira Cloud &ZeroWidthSpace;

  • API token (simplest): an Atlassian account for CodeLotse with access to the projects, an API token from id.atlassian.com/manage-profile/security/api-tokens, site URL https://<site>.atlassian.net.
  • OAuth 2.0 (3LO): each operator registers an own app in the Atlassian developer console (developer.atlassian.com/console/myapps) with the Jira API permissions read:jira-work, write:jira-work, read:jira-user and the callback URL <PUBLIC_URL>/auth/trackers/jira/callback. Enter the client id and secret on the connection, then click Authorise with Atlassian. The authorisation is bound to the admin who started it, expires after 10 minutes and can be used once. Rotating refresh tokens are renewed automatically; the connection stays "Waiting for authorisation" until it succeeds.

Jira Data Center &ZeroWidthSpace;

A personal access token of a service account (Profile → Personal Access Tokens) and the server URL including a context path, for example https://jira.example.com/jira. The self-hosted edition allows http:// for servers on internal networks.

Linear &ZeroWidthSpace;

A personal API key (Settings → API) of a member with access to the teams. An OAuth access token of your own Linear OAuth application can be entered instead ("OAuth access token"); CodeLotse does not run the Linear OAuth flow.

Azure Boards &ZeroWidthSpace;

A personal access token with the scope Work Items (Read & write) and the organisation URL https://dev.azure.com/<organisation> (Azure DevOps Server: the collection URL). Optionally a Microsoft Entra application (directory id, client id, client secret) that was added to the organisation; CodeLotse then uses the client credentials flow.

Field mapping validation &ZeroWidthSpace;

Check mapping in the settings tests the unsaved settings against the tracker: projects (or teams) exist, the acceptance criteria field exists, mapped components exist in the projects, mapped labels exist (Linear requires existing labels; Jira and Azure Boards create labels on first use, so unknown labels are a note, not a problem), transition target statuses exist in the projects, and the project for created tickets is in the project list.

Acceptance check &ZeroWidthSpace;

When a pull request links a ticket, the acceptance check (see the repository policy, feature "Acceptance criteria") reads the ticket's criteria, checks the diff against them and reports them in the review summary and check run as Acceptance criteria (ABC-123): …. If the tracker is unreachable, the last stored copy of the ticket is used.

Rate limits and network &ZeroWidthSpace;

Every tracker request goes through the request budget per tracker host and connection. Retry-After, X-RateLimit-* (Jira, Azure DevOps) and X-RateLimit-Requests-*/X-RateLimit-Complexity-* (Linear) block further requests until the reset and delay the job instead of failing it. Requests time out after 15 seconds, do not follow redirects, and in the enterprise edition only reach public addresses (checked at connect time and on every connection).

Data and security &ZeroWidthSpace;

Credentials and webhook secrets are encrypted with the instance key (context tracker_connections:<id>, included in keys rotate); the API only returns the last four characters of the credential. Stored per linked ticket: key, URL, title, description, acceptance criteria, status, labels, components and the update time, kept until the connection is deleted. Tracker user accounts are not stored. All changes to connections are audited (tracker_connection.*).

Limitations &ZeroWidthSpace;

  • Branch names are only read when the pull request is reviewed with the acceptance check enabled; webhook ingest sees the title.
  • A Jira issue moved to another project keeps its old key on existing links; the old key still resolves at Jira.
  • Status synchronisation is one way (repository → tracker).
  • Pull request URLs in link comments use the repository's code host URL.

The open core is released under the Apache License 2.0.