NAME

CPAN::InGit::Server - A Mojolicious::Controller that serves the ArchiveTrees from a git repo

DESCRIPTION

This controller serves paths that can be used directly as a CPAN Mirror by tools like 'cpanm'. By default, it serves all branches, but you can also configure it to serve only a single branch. It serves content directly from the Git repo and does not need a working directory checked out.

ATTRIBUTES

cpan_repo

Reference to the CPAN::InGit object from which files will be served

archive_tree

Reference to an ArchvieTree object. If set, the controller will serve only the tree of this branch from whatever path it was rooted at. If not set, this attribute will be lazy-built from attribute branch_name and cached in branch_cache. The lazy-building of this attribute may return undef, and should be checked before dispatching to an action.

branch_name

Name of branch to be lazy-loaded by the archive_tree accessor.

branch_cache

A hashref or Tree::RB::XS object storing a cache of ArchiveTree objects. This needs to be supplied in the stash if you are loading branches by name at runtime, or else it will be creating an ArchiveTree object on every request.

branch_head_only

If true, this prevents serving files from a Git::Raw tree which is not the HEAD of a branch. By default, any Git sha-1, tag, or branch name may be used so long as it references a tree that meets the requirements for an ArchiveTree.

index_branches

If true, list the available local CPAN archive branches on the repository index. Disabled by default; only applies when serving multiple trees.

module_index_max_rows

Set this to the maximum number of rows to return from the JSON endpoint that searches modules, used by the per-tree index page.

METHODS

serve_repo_index

Serve the repository landing page. Local branches containing CPAN configuration and a package index are listed only when index_branches is enabled in the stash.

serve_index

Serve an HTML index for a CPAN tree, including its commit date, package index date, and the first 5000 matching modules.

serve_module_list

Serve modules/ as JSON. filter is a case-insensitive literal substring matched against the entire original 02packages row (module, version, and distribution path). after and before are exclusive module-name boundaries; supply at most one. Rows are sorted by module name, with at most 500 returned per request.

The response contains modules (objects with name, version, path, and text), the applied filter, the Git tree ID, and next_after and previous_before cursors (null at either end). complete is true only when this response includes every row matching the filter, including rows outside the requested boundary. A client may then narrow that filter locally.

check_branch_exists

This checks that the "branch_name" could be successfully resolved to a "archive_tree". It returns true, or sets a 404 error and returns undef, and can be used as ->under(\&check_branch_exists).

serve_package_details

Serve modules/02packages.details.txt[.gz].

serve_author_file

Serve all files under authors/id/.... (All files in that path of the git branch are considered public) This performs automatic creation of .tar.gz files if a directory and matching .meta file exist, allowing distributions to be unpacked within the git repo, but served as .tar.gz files.

render_gzipped

Serve content after gzipping it and setting the content type to application/gzip.

ROUTES

Cpan Mirror Paths

A functioning CPAN mirror requires a file /modules/02packages.details.txt.gz which lists the full contents of the latest version of every module. That file also lists the author paths for each module, which are relative to /authors/id/

In CPAN::InGit, /modules/02packages.details.txt is stored uncompressed, and gzipped as it is served. Each author dist is stored as a .tar.gz file and a metadata file with extension .meta and may also optionally be unpacked at a directory of the same name minus the .tar.gz extension. A special author "LOCAL" is used for custom overrides of upstream packages.

Summary:

/                                                       # repository index
/~static/...                                            # static files served publicly
/:branch/                                               # browsable tree index
/:branch/modules/?filter=TEXT&after=MODULE              # filtered module list
/:branch/modules/02packages.details.txt                 # index of the mirror
/:branch/authors/id/D/DP/DPARIS/Crypt-DES-2.07.tar.gz   # copy of upstream
/:branch/authors/id/D/DP/DPARIS/Crypt-DES-2.07.meta     # extracted metadata
/:branch/authors/id/L/LO/LOCAL/Crypt-DES-2.07_01/...    # untarred customized dist
/:branch/authors/id/L/LO/LOCAL/Crypt-DES-2.07_01.meta   # untarred customized dist's metadata

mount

my $route= app->routes->any('/pan');
my $repo= CPAN::InGit->new(git_repo => '/path/to/.git');
CPAN::InGit::Server->mount($route, $repo, %options);
# %options:
#   archive_tree => $ArchiveTree_obj,
#   branch_cache => $branch_cache,
#   index_branches => 1, # optionally list local CPAN archive branches

This class method sets up the routes for serving the mirror under a provided route. $route is the route under which you want to serve the 'PAN.

If you specify 'archive_tree', that tree will be directly mounted at the route without the '/:branch' selector portion of the paths. Otherwise, the route's root serves a repository index; index_branches enables branch links on that page and defaults to false. Files in share/public are served verbatim under /~static/ using Mojo's static file handler, regardless of the mount path.

VERSION

version 0.005

AUTHOR

Michael Conrad <mike@nrdvana.net>

COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by Michael Conrad, and IntelliTree Solutions.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.