/**
* pgh group commands
*
* Subcommands: create, list, info, delete, add-repo, remove-repo, add-member, remove-member
*/
import { table, jsonOut, log, error } from '../output.js'
export async function groupCommand (client, sub, args, flags) {
switch (sub) {
case 'create':
return groupCreate(client, args[0], flags)
case 'list':
case 'ls':
return groupList(client, flags)
case 'info':
return groupInfo(client, args[0], flags)
case 'delete':
return groupDelete(client, args[0], flags)
case 'add-repo':
return groupAddRepo(client, args[0], args[1], flags)
case 'remove-repo':
return groupRemoveRepo(client, args[0], args[1], flags)
case 'add-member':
return groupAddMember(client, args[0], args[1], flags)
case 'remove-member':
return groupRemoveMember(client, args[0], args[1], flags)
default:
groupHelp()
}
}
async function groupCreate (client, name, flags) {
if (!name) error('Usage: pgh group create <name> [--description "..."]')
const data = await client.post('/api/namespaces', {
name,
type: 'group',
displayName: flags['display-name'] || flags.displayName || name,
description: flags.description || flags.d || ''
})
if (flags.json) {
jsonOut(data)
return
}
if (data.error) {
error(data.error)
return
}
log(`\u2713 Created group: ${data.name}`)
if (data.description) log(` Description: ${data.description}`)
}
async function groupList (client, flags) {
const namespaces = await client.get('/api/namespaces?type=group')
if (flags.json) {
jsonOut(namespaces)
return
}
if (namespaces.length === 0) {
log('No groups found.')
return
}
table(namespaces, [
{ key: 'name', label: 'NAME', width: 25 },
{ key: 'displayName', label: 'DISPLAY NAME', width: 25 },
{ key: 'repoCount', label: 'REPOS' },
{ key: 'memberCount', label: 'MEMBERS' }
].map(col => {
// Add computed fields
return col
}))
// Since table doesn't support computed keys, format manually
const header = 'NAME REPOS MEMBERS DESCRIPTION'
const sep = '-'.repeat(header.length)
console.log('')
console.log(header)
console.log(sep)
for (const ns of namespaces) {
const repos = ns.repos ? ns.repos.length : 0
const members = ns.members ? ns.members.length : 0
const desc = ns.description ? ns.description.slice(0, 30) : ''
console.log(
`${ns.name.padEnd(26)}${String(repos).padEnd(8)}${String(members).padEnd(9)}${desc}`
)
}
}
async function groupInfo (client, name, flags) {
if (!name) error('Usage: pgh group info <name>')
const ns = await client.get(`/api/namespaces/${encodeURIComponent(name)}`)
if (flags.json) {
jsonOut(ns)
return
}
if (ns.error) {
error(ns.error)
return
}
log(`Name: ${ns.name}`)
log(`Display Name: ${ns.displayName}`)
log(`Type: ${ns.type}`)
log(`Description: ${ns.description || '-'}`)
log(`Visibility: ${ns.visibility}`)
log(`Repos: ${ns.repos.length}`)
if (ns.repos.length > 0) {
log('')
log('Repositories:')
for (const repo of ns.repos) {
log(` - ${repo}`)
}
}
if (ns.members && ns.members.length > 0) {
log('')
log('Members:')
for (const m of ns.members) {
log(` - ${m.userId} (${m.role})`)
}
}
}
async function groupDelete (client, name, flags) {
if (!name) error('Usage: pgh group delete <name>')
const data = await client.del(`/api/namespaces/${encodeURIComponent(name)}`)
if (flags.json) {
jsonOut(data)
return
}
if (data.error) {
error(data.error)
return
}
log(`\u2713 Deleted group: ${name}`)
}
async function groupAddRepo (client, groupName, repoName, flags) {
if (!groupName || !repoName) error('Usage: pgh group add-repo <group> <repo>')
const data = await client.post(`/api/namespaces/${encodeURIComponent(groupName)}/repos`, {
repoName
})
if (flags.json) {
jsonOut(data)
return
}
if (data.error) {
error(data.error)
return
}
log(`\u2713 Added ${repoName} to group ${groupName}`)
}
async function groupRemoveRepo (client, groupName, repoName, flags) {
if (!groupName || !repoName) error('Usage: pgh group remove-repo <group> <repo>')
const data = await client.del(`/api/namespaces/${encodeURIComponent(groupName)}/repos/${encodeURIComponent(repoName)}`)
if (flags.json) {
jsonOut(data)
return
}
if (data.error) {
error(data.error)
return
}
log(`\u2713 Removed ${repoName} from group ${groupName}`)
}
async function groupAddMember (client, groupName, userId, flags) {
if (!groupName || !userId) error('Usage: pgh group add-member <group> <user-id> [--role admin|member]')
const role = flags.role || 'member'
const data = await client.post(`/api/namespaces/${encodeURIComponent(groupName)}/members`, {
userId,
role
})
if (flags.json) {
jsonOut(data)
return
}
if (data.error) {
error(data.error)
return
}
log(`\u2713 Added ${userId} to group ${groupName} as ${role}`)
}
async function groupRemoveMember (client, groupName, userId, flags) {
if (!groupName || !userId) error('Usage: pgh group remove-member <group> <user-id>')
const data = await client.del(`/api/namespaces/${encodeURIComponent(groupName)}/members/${encodeURIComponent(userId)}`)
if (flags.json) {
jsonOut(data)
return
}
if (data.error) {
error(data.error)
return
}
log(`\u2713 Removed ${userId} from group ${groupName}`)
}
export function groupHelp () {
log(`Usage: pgh group <subcommand> [args] [flags]
Subcommands:
create <name> Create a new group
list List all groups
info <name> Show group details
delete <name> Delete empty group
add-repo <group> <repo> Add repo to group
remove-repo <group> <repo> Remove repo from group
add-member <group> <user-id> Add member to group
remove-member <group> <user-id> Remove member from group
Flags:
--description TEXT Group description (create)
--display-name TEXT Display name (create)
--role admin|member Member role (add-member, default: member)
--json Output as JSON
Examples:
pgh group create holepunchto --description "Holepunch core repos"
pgh group add-repo holepunchto hypercore
pgh group add-member holepunchto user123 --role admin
pgh group list
pgh group info holepunchto`)
}