Files
coder/docs/admin/integrations/jfrog-artifactory.md
T
Nick Vigilante e458692cb8 refactor(docs): convert absolute coder/coder blob/tree/main links to relative (DOCS-351) (#26341)
Closes [DOCS-351](https://linear.app/codercom/issue/DOCS-351).

> [!WARNING]
> **DO NOT MERGE** until
[DOCS-349](https://linear.app/codercom/issue/DOCS-349)
([coder.com#877](https://github.com/coder/coder.com/pull/877)) has
shipped to production and baked for at least one Vercel cycle.
>
> Without DOCS-349, the relative links in this PR resolve to broken
docs-route URLs (`/docs/helm/coder/values.yaml` -> 404) instead of
GitHub URLs tagged with the displayed docs version. DOCS-349 fixes the
rewriter to classify these as GitHub blob/tree URLs with the page's
resolved ref.

## TL;DR

Converts 121 absolute
`https://github.com/coder/coder/(blob|tree)/main/<path>` links across 39
docs markdown files to relative paths. After this lands AND DOCS-349
deploys, every one of these links will follow the displayed docs version
(mainline tag on bare URLs, explicit tag on `/@vX.Y.Z/`, `main` on
`/@main/`) instead of always pointing to `main`.

## Why

Today a reader on `/docs/@v2.30.0/install/docker` follows a
`compose.yaml` link and arrives at `main`'s `compose.yaml`, which
doesn't necessarily match what the docs page describes. Helm values,
Terraform templates, and source-code references in particular drift
across versions. The fix is to let the coder.com rewriter substitute the
page's resolved ref into the URL; that only works on relative links.

## Example payoff (post-DOCS-349)

| URL | Today (absolute, always `main`) | After (relative + rewriter) |
|---|---|---|
| `/docs/install/docker` |
`https://github.com/coder/coder/blob/main/compose.yaml` |
`https://github.com/coder/coder/blob/v2.34.1/compose.yaml` (today's
mainline) |
| `/docs/@v2.30.0/install/docker` | same as above |
`https://github.com/coder/coder/blob/v2.30.0/compose.yaml` |
| `/docs/@main/install/docker` | same as above |
`https://github.com/coder/coder/blob/main/compose.yaml` |

## Scope

- **121 conversions** across **39 files**.
- Verb breakdown: `tree/main` (directories) and `blob/main` (files),
both flipped to relative paths.
- Line anchors (`#L23-L24`) and query strings preserved verbatim.
- Conversion is mechanical: relative path computed from the doc file's
directory to the target via `os.path.relpath`. Any path starting at the
same directory or below gets a `./` prefix; otherwise `../` chains.

## Rebased on main

The branch was rebased onto `main` after the DOCS-350 hotfix
([#26339](https://github.com/coder/coder/pull/26339)) merged. The hotfix
repointed 3 `docs-backend-contrib-guide` refs in `backend.md` to `main`,
which then needed the same `main` -> relative conversion this PR is
doing for the other 121 links. The conflict was resolved by reapplying
the mechanical conversion to `backend.md` after taking the hotfix's
content. Net result: those 3 links land here as relative, same as
everything else. New HEAD `3f501cb622`.

## Inline fix folded in: dead `nix` link

- `docs/about/contributing/CONTRIBUTING.md:7` -> `../../../nix`

The original absolute URL `https://github.com/coder/coder/tree/main/nix`
already returned 404 today. Repointed to `flake.nix` (modern Nix
entrypoint, what the prose "Nix environment" semantically refers to).
Closes [DOCS-357](https://linear.app/codercom/issue/DOCS-357) here since
the `check-docs` Linkspector job surfaced it during rebase; cheaper to
fix inline than in a separate single-line PR.

## Out of scope (filed separately)

- [DOCS-350](https://linear.app/codercom/issue/DOCS-350): 3 dead
`docs-backend-contrib-guide` branch refs in `backend.md`
([#26339](https://github.com/coder/coder/pull/26339), merged).
- [DOCS-352](https://linear.app/codercom/issue/DOCS-352): 10 SHA-pinned
`(blob|tree)/<sha>` links pending intent review.
- [DOCS-355](https://linear.app/codercom/issue/DOCS-355): code-server
analog (4 absolute `(blob|tree)/main` links in `coder/code-server`).
- [DOCS-356](https://linear.app/codercom/issue/DOCS-356): 2 upstream
content bugs in `coder/code-server/docs/CONTRIBUTING.md` (independent of
this PR).


## Not triggering `/coder-agents-review`

Docs-only edit; per `AGENTS.md` the bot review is reserved for
product/CI changes.

## Pre-mortem

| Concern | Mitigation |
|---|---|
| Merging before DOCS-349 deploys regresses ~120 currently-working links
into 404s on coder.com | Clear DO-NOT-MERGE banner; tracked as blocker
in Linear. |
| Relative path computed incorrectly (off-by-one `..`) | Verified all
114 newly-relative non-md/non-image paths resolve to existing files in
the repo (only exception is the pre-existing dead `nix` link above). |
| Line anchors stripped during conversion | Preserved by the
substitution regex; verified `#L<n>-L<m>` cases in `airgap.md` and
`speed-up-templates.md`. |
| Future code reorgs change file locations | Relative links will start
pointing to nothing. Same failure mode as absolute links pointing to
renamed files; can be caught with a future link-checker job. |

## Validation

```
$ grep -rE 'github\.com/coder/coder/(blob|tree)/main' docs --include="*.md" | wc -l
0
$ git diff --stat origin/main | tail -1
39 files changed, 118 insertions(+), 118 deletions(-)
```

114 newly-relative paths verified to resolve to existing repo files
(Python `os.path.exists` check on each computed target).

<details>
<summary>Decision log + planning context</summary>

**Why relative over `(blob|tree)/{{currentDocsVersion}}/...`
templating**: relative paths require zero markdown-system support and
zero upstream churn beyond this one PR. Templating would require a
preprocessor on `coder.com` side AND a convention upstream authors have
to remember; relative paths just work in a plain editor and
`github.com`'s own renderer too.

**Why `./` prefix on same-directory targets**: makes the conversion
grep-able later (`grep -E '\((\.\./|\./)'`).

**Why preserve `#L<n>-L<m>` anchors verbatim**: the anchor is meaningful
to the linked file's content, not to the URL form; keeping it as-is
preserves authorial intent. If the file later changes such that the line
range drifts, that's a different problem the SHA-pin audit
([DOCS-352](https://linear.app/codercom/issue/DOCS-352)) will surface.

</details>

---

*Generated by Coder Agents on @nickvigilante's behalf.*





## Drive-by external link fix folded in

`docs/about/contributing/CONTRIBUTING.md:296` cited
`https://reflectoring.io/meaningful-commit-messages/` which is returning
HTTP 503 (the host appears to be down site-wide right now). `check-docs`
Linkspector flagged it after the rebase. Replaced with
`https://cbea.ms/git-commit/` (Chris Beams' canonical "If applied, this
commit will..." article, confirmed 200), which is the original source of
the rule the prose recites anyway.
2026-06-22 11:39:12 -04:00

6.0 KiB

JFrog Artifactory Integration

Use Coder and JFrog Artifactory together to secure your development environments without disturbing your developers' existing workflows.

This guide will demonstrate how to use JFrog Artifactory as a package registry within a workspace.

Requirements

  • A JFrog Artifactory instance
  • 1:1 mapping of users in Coder to users in Artifactory by email address or username
  • Repositories configured in Artifactory for each package manager you want to use

Provisioner Authentication

The most straight-forward way to authenticate your template with Artifactory is by using our official Coder modules. We publish two type of modules that automate the JFrog Artifactory and Coder integration.

  1. JFrog-OAuth
  2. JFrog-Token

JFrog-OAuth

This module is usable by JFrog self-hosted (on-premises) Artifactory as it requires configuring a custom integration. This integration benefits from Coder's external-auth feature allows each user to authenticate with Artifactory using an OAuth flow and issues user-scoped tokens to each user.

To set this up, follow these steps:

  1. Add the following to your Helm chart values.yaml for JFrog Artifactory. Replace CODER_URL with your JFrog Artifactory base URL:

    artifactory:
      enabled: true
      frontend:
      extraEnvironmentVariables:
        - name: JF_FRONTEND_FEATURETOGGLER_ACCESSINTEGRATION
          value: "true"
      access:
      accessConfig:
        integrations-enabled: true
        integration-templates:
          - id: "1"
            name: "CODER"
            redirect-uri: "https://CODER_URL/external-auth/jfrog/callback"
            scope: "applied-permissions/user"
    
  2. Create a new Application Integration by going to https://JFROG_URL/ui/admin/configuration/integrations/app-integrations/new and select the Application Type as the integration you created in step 1 or Custom Integration if you are using SaaS instance i.e. example.jfrog.io.

  3. Add a new external authentication to Coder by setting these environment variables in a manner consistent with your Coder deployment. Replace JFROG_URL with your JFrog Artifactory base URL:

    # JFrog Artifactory External Auth
    CODER_EXTERNAL_AUTH_1_ID="jfrog"
    CODER_EXTERNAL_AUTH_1_TYPE="jfrog"
    CODER_EXTERNAL_AUTH_1_CLIENT_ID="YYYYYYYYYYYYYYY"
    CODER_EXTERNAL_AUTH_1_CLIENT_SECRET="XXXXXXXXXXXXXXXXXXX"
    CODER_EXTERNAL_AUTH_1_DISPLAY_NAME="JFrog Artifactory"
    CODER_EXTERNAL_AUTH_1_DISPLAY_ICON="/icon/jfrog.svg"
    CODER_EXTERNAL_AUTH_1_AUTH_URL="https://JFROG_URL/ui/authorization"
    CODER_EXTERNAL_AUTH_1_SCOPES="applied-permissions/user"
    
  4. Create or edit a Coder template and use the JFrog-OAuth module to configure the integration:

    module "jfrog" {
      count          = data.coder_workspace.me.start_count
      source         = "registry.coder.com/modules/jfrog-oauth/coder"
      version        = "1.0.19"
      agent_id       = coder_agent.example.id
      jfrog_url      = "https://example.jfrog.io"
      username_field = "username" # If you are using GitHub to login to both Coder and Artifactory, use username_field = "username"
    
      package_managers = {
        npm    = ["npm", "@scoped:npm-scoped"]
        go     = ["go", "another-go-repo"]
        pypi   = ["pypi", "extra-index-pypi"]
        docker = ["example-docker-staging.jfrog.io", "example-docker-production.jfrog.io"]
      }
    }
    

JFrog-Token

This module makes use of the Artifactory terraform provider and an admin-scoped token to create user-scoped tokens for each user by matching their Coder email or username with Artifactory. This can be used for both SaaS and self-hosted (on-premises) Artifactory instances.

To set this up, follow these steps:

  1. Get a JFrog access token from your Artifactory instance. The token must be an admin token with scope applied-permissions/admin.

  2. Create or edit a Coder template and use the JFrog-Token module to configure the integration and pass the admin token. It is recommended to store the token in a sensitive Terraform variable to prevent it from being displayed in plain text in the terraform state:

    variable "artifactory_access_token" {
      type      = string
      sensitive = true
    }
    
    module "jfrog" {
      source                   = "registry.coder.com/modules/jfrog-token/coder"
      version                  = "1.0.30"
      agent_id                 = coder_agent.example.id
      jfrog_url                = "https://XXXX.jfrog.io"
      artifactory_access_token = var.artifactory_access_token
      package_managers = {
        npm    = ["npm", "@scoped:npm-scoped"]
        go     = ["go", "another-go-repo"]
        pypi   = ["pypi", "extra-index-pypi"]
        docker = ["example-docker-staging.jfrog.io", "example-docker-production.jfrog.io"]
      }
    }
    

Note

The admin-level access token is used to provision user tokens and is never exposed to developers or stored in workspaces.

If you don't want to use the official modules, you can read through the example template, which uses Docker as the underlying compute. The same concepts apply to all compute types.

Air-Gapped Deployments

See the air-gapped deployments section for instructions on how to use Coder modules in an offline environment with Artifactory.

Next Steps