refactor(forge): extract ForgeBackend class and registry

Problem: adding a new forge required touching 5 lookup tables
(`FORGE_HOSTS`, `FORGE_CLI`, `FORGE_AUTH_CMD`, `SHORTHAND_PREFIX`,
`_warned_forges`) and every branching site in `_api_args`,
`fetch_metadata`, and `parse_ref`.

Solution: introduce a `ForgeBackend` class with `parse_url`,
`api_args`, and `parse_state` methods, plus a `register()` /
`backends()` registry. New forges (Gitea, Forgejo) are a single
`register()` call via the `gitea_backend()` convenience constructor.
This commit is contained in:
Barrett Ruth 2026-03-10 22:11:37 -04:00
parent ecacb62674
commit 0033b26e38
4 changed files with 380 additions and 186 deletions

View file

@ -316,6 +316,81 @@ describe('forge parse.body integration', function()
end)
end)
describe('forge registry', function()
it('backends() returns all registered backends', function()
local backends = forge.backends()
assert.is_true(#backends >= 3)
local names = {}
for _, b in ipairs(backends) do
names[b.name] = true
end
assert.is_true(names['github'])
assert.is_true(names['gitlab'])
assert.is_true(names['codeberg'])
end)
it('register() with custom backend resolves URLs', function()
local custom = forge.gitea_backend({
name = 'mygitea',
shorthand = 'mg',
default_host = 'gitea.example.com',
})
forge.register(custom)
local ref = forge.parse_ref('https://gitea.example.com/alice/proj/issues/7')
assert.is_not_nil(ref)
assert.equals('mygitea', ref.forge)
assert.equals('alice', ref.owner)
assert.equals('proj', ref.repo)
assert.equals('issue', ref.type)
assert.equals(7, ref.number)
end)
it('register() with custom shorthand resolves', function()
local ref = forge._parse_shorthand('mg:alice/proj#7')
assert.is_not_nil(ref)
assert.equals('mygitea', ref.forge)
assert.equals('alice', ref.owner)
assert.equals('proj', ref.repo)
assert.equals(7, ref.number)
end)
it('_api_args dispatches to custom backend', function()
local args = forge._api_args({
forge = 'mygitea',
owner = 'alice',
repo = 'proj',
type = 'issue',
number = 7,
url = '',
})
assert.same({ 'tea', 'api', '/repos/alice/proj/issues/7' }, args)
end)
it('gitea_backend() creates a working backend', function()
local b = forge.gitea_backend({
name = 'forgejo',
shorthand = 'fj',
default_host = 'forgejo.example.com',
cli = 'forgejo-cli',
auth_cmd = 'forgejo-cli login',
})
assert.equals('forgejo', b.name)
assert.equals('fj', b.shorthand)
assert.equals('forgejo-cli', b.cli)
local ref = b:parse_url('https://forgejo.example.com/bob/repo/pulls/3')
assert.is_nil(ref)
forge.register(b)
ref = b:parse_url('https://forgejo.example.com/bob/repo/pulls/3')
assert.is_not_nil(ref)
assert.equals('forgejo', ref.forge)
assert.equals('pull_request', ref.type)
assert.equals(3, ref.number)
end)
end)
describe('forge diff integration', function()
local store = require('pending.store')
local diff = require('pending.diff')