You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
267 lines
10 KiB
267 lines
10 KiB
import { getPrivateKeyFromEnv, getPrivateKeyBytes } from '../../utils/keys.js'; |
|
import { getPublicKey } from 'nostr-tools'; |
|
import { DEFAULT_NOSTR_RELAYS } from '../../config.js'; |
|
import { publishToRelays } from '../../relay/publisher.js'; |
|
import { enhanceRelayList } from '../../relay/relay-fetcher.js'; |
|
import { storeEventInJsonl } from '../../utils/event-storage.js'; |
|
import { addClientTag } from '../../utils/tags.js'; |
|
|
|
// Import publish subcommands |
|
import { publishRepoAnnouncement } from './repo-announcement.js'; |
|
import { publishOwnershipTransfer } from './ownership-transfer.js'; |
|
import { publishPR } from './pr.js'; |
|
import { publishIssue } from './issue.js'; |
|
import { publishStatus } from './status.js'; |
|
import { publishPatch } from './patch.js'; |
|
import { publishRepoState } from './repo-state.js'; |
|
import { publishPRUpdate } from './pr-update.js'; |
|
import { publishEvent } from './event.js'; |
|
|
|
/** |
|
* Main publish command handler |
|
*/ |
|
export async function publish(args, server, json) { |
|
const subcommand = args[0]; |
|
|
|
if (!subcommand || subcommand === '--help' || subcommand === '-h') { |
|
showPublishHelp(); |
|
process.exit(0); |
|
} |
|
|
|
// Get private key |
|
const secretKey = getPrivateKeyFromEnv(); |
|
const privateKeyBytes = getPrivateKeyBytes(secretKey); |
|
const pubkey = getPublicKey(privateKeyBytes); |
|
|
|
// Get relays from environment or use defaults |
|
const relaysEnv = process.env.NOSTR_RELAYS; |
|
const baseRelays = relaysEnv ? relaysEnv.split(',').map(r => r.trim()).filter(r => r.length > 0) : DEFAULT_NOSTR_RELAYS; |
|
|
|
// Enhance relay list with user's relay preferences (outboxes, local relays, blocked relays) |
|
const relays = await enhanceRelayList(baseRelays, pubkey, baseRelays); |
|
|
|
// Route to appropriate subcommand |
|
try { |
|
switch (subcommand) { |
|
case 'repo-announcement': |
|
await publishRepoAnnouncement(args.slice(1), relays, privateKeyBytes, pubkey, json); |
|
break; |
|
case 'ownership-transfer': |
|
await publishOwnershipTransfer(args.slice(1), relays, privateKeyBytes, pubkey, json); |
|
break; |
|
case 'pr': |
|
case 'pull-request': |
|
await publishPR(args.slice(1), relays, privateKeyBytes, pubkey, json); |
|
break; |
|
case 'issue': |
|
await publishIssue(args.slice(1), relays, privateKeyBytes, pubkey, json); |
|
break; |
|
case 'status': |
|
await publishStatus(args.slice(1), relays, privateKeyBytes, pubkey, json); |
|
break; |
|
case 'patch': |
|
await publishPatch(args.slice(1), relays, privateKeyBytes, pubkey, json); |
|
break; |
|
case 'repo-state': |
|
await publishRepoState(args.slice(1), relays, privateKeyBytes, pubkey, json); |
|
break; |
|
case 'pr-update': |
|
case 'pull-request-update': |
|
await publishPRUpdate(args.slice(1), relays, privateKeyBytes, pubkey, json); |
|
break; |
|
case 'event': |
|
await publishEvent(args.slice(1), relays, privateKeyBytes, pubkey, json); |
|
break; |
|
default: |
|
console.error(`Error: Unknown publish subcommand: ${subcommand}`); |
|
console.error('Use: publish repo-announcement|ownership-transfer|pr|pr-update|issue|status|patch|repo-state|event'); |
|
console.error('Run: publish --help for detailed usage'); |
|
process.exit(1); |
|
} |
|
} catch (error) { |
|
const { sanitizeErrorMessage } = await import('../../utils/error-sanitizer.js'); |
|
const errorMessage = error instanceof Error ? error.message : String(error); |
|
const sanitized = sanitizeErrorMessage(errorMessage); |
|
console.error('Error:', sanitized); |
|
process.exit(1); |
|
} |
|
} |
|
|
|
/** |
|
* Common publish function that handles event creation, storage, and publishing |
|
*/ |
|
export async function publishEventCommon(event, relays, privateKeyBytes, pubkey, json, eventType = 'Event') { |
|
// Store event in JSONL file |
|
storeEventInJsonl(event); |
|
|
|
const result = await publishToRelays(event, relays, privateKeyBytes, pubkey); |
|
|
|
if (json) { |
|
console.log(JSON.stringify({ event, published: result }, null, 2)); |
|
} else { |
|
console.log(`${eventType} published!`); |
|
console.log(`Event ID: ${event.id}`); |
|
console.log(`Event stored in nostr/${getEventStorageFile(event.kind)}`); |
|
console.log(`Published to ${result.success.length} relay(s): ${result.success.join(', ')}`); |
|
if (result.failed.length > 0) { |
|
console.log(`Failed on ${result.failed.length} relay(s):`); |
|
result.failed.forEach(f => console.log(` ${f.relay}: ${f.error}`)); |
|
} |
|
} |
|
|
|
return result; |
|
} |
|
|
|
/** |
|
* Get storage file name for event kind |
|
*/ |
|
function getEventStorageFile(kind) { |
|
switch (kind) { |
|
case 30617: return 'repo-announcements.jsonl'; |
|
case 1641: return 'ownership-transfers.jsonl'; |
|
case 1617: return 'patches.jsonl'; |
|
case 1618: return 'pull-requests.jsonl'; |
|
case 1619: return 'pull-request-updates.jsonl'; |
|
case 1621: return 'issues.jsonl'; |
|
case 1630: |
|
case 1631: |
|
case 1632: |
|
case 1633: return 'status-events.jsonl'; |
|
case 30618: return 'repo-states.jsonl'; |
|
default: return `events-kind-${kind}.jsonl`; |
|
} |
|
} |
|
|
|
function showPublishHelp() { |
|
console.log(` |
|
Publish Nostr Git Events |
|
|
|
Usage: gitrep publish <subcommand> [options] |
|
|
|
Subcommands: |
|
repo-announcement <repo-name> [options] |
|
Publish a repository announcement (kind 30617) |
|
Options: |
|
--description <text> Repository description |
|
--clone-url <url> Clone URL (can be specified multiple times) |
|
--web-url <url> Web URL (can be specified multiple times) |
|
--maintainer <npub> Maintainer pubkey (can be specified multiple times) |
|
--relay <url> Custom relay URL (can be specified multiple times) |
|
|
|
Example: |
|
gitrep publish repo-announcement myrepo \\ |
|
--description "My awesome repo" \\ |
|
--clone-url "https://gitrepublic.com/api/git/npub1.../myrepo.git" \\ |
|
--maintainer "npub1..." |
|
|
|
ownership-transfer <repo> <new-owner-npub> [--self-transfer] |
|
Transfer repository ownership (kind 1641) |
|
Note: You must be the current owner (signing with NOSTRGIT_SECRET_KEY) |
|
|
|
Example: |
|
gitrep publish ownership-transfer myrepo npub1... --self-transfer |
|
|
|
pr <owner-npub> <repo> <title> [options] |
|
Create a pull request (kind 1618) |
|
Options: |
|
--content <text> PR description/content |
|
--base <branch> Base branch (default: main) |
|
--head <branch> Head branch (default: main) |
|
|
|
Example: |
|
gitrep publish pr npub1... myrepo "Fix bug" \\ |
|
--content "This PR fixes a critical bug" \\ |
|
--base main --head feature-branch |
|
|
|
issue <owner-npub> <repo> <title> [options] |
|
Create an issue (kind 1621) |
|
Options: |
|
--content <text> Issue description |
|
--label <label> Label (can be specified multiple times) |
|
|
|
Example: |
|
gitrep publish issue npub1... myrepo "Bug report" \\ |
|
--content "Found a bug" --label bug --label critical |
|
|
|
status <event-id> <open|applied|closed|draft> [--content <text>] |
|
Update PR/issue status (kinds 1630-1633) |
|
|
|
Example: |
|
gitrep publish status abc123... closed --content "Fixed in v1.0" |
|
|
|
patch <owner-npub> <repo> <patch-file> [options] |
|
Publish a git patch (kind 1617) |
|
Options: |
|
--earliest-commit <id> Earliest unique commit ID (euc) |
|
--commit <id> Current commit ID |
|
--parent-commit <id> Parent commit ID |
|
--root Mark as root patch |
|
--root-revision Mark as root revision |
|
--reply-to <event-id> Reply to previous patch (NIP-10) |
|
--mention <npub> Mention user (can be specified multiple times) |
|
|
|
Example: |
|
gitrep publish patch npub1... myrepo patch-0001.patch \\ |
|
--earliest-commit abc123 --commit def456 --root |
|
|
|
repo-state <repo> [options] |
|
Publish repository state (kind 30618) |
|
Options: |
|
--ref <ref-path> <commit-id> [parent-commits...] Add ref (can be specified multiple times) |
|
--head <branch> Set HEAD branch |
|
|
|
Example: |
|
gitrep publish repo-state myrepo \\ |
|
--ref refs/heads/main abc123 def456 \\ |
|
--ref refs/tags/v1.0.0 xyz789 \\ |
|
--head main |
|
|
|
pr-update <owner-npub> <repo> <pr-event-id> <commit-id> [options] |
|
Update pull request tip commit (kind 1619) |
|
Options: |
|
--pr-author <npub> PR author pubkey (for NIP-22 tags) |
|
--clone-url <url> Clone URL (required, can be specified multiple times) |
|
--merge-base <commit-id> Most recent common ancestor |
|
--earliest-commit <id> Earliest unique commit ID |
|
--mention <npub> Mention user (can be specified multiple times) |
|
|
|
Example: |
|
gitrep publish pr-update npub1... myrepo pr-event-id new-commit-id \\ |
|
--pr-author npub1... \\ |
|
--clone-url "https://gitrepublic.com/api/git/npub1.../myrepo.git" \\ |
|
--merge-base base-commit-id |
|
|
|
event [options] |
|
Publish a generic Nostr event (defaults to kind 1) |
|
Options: |
|
--kind <number> Event kind (default: 1) |
|
--content <text> Event content (default: '') |
|
--tag <name> <value> Add a tag (can be specified multiple times) |
|
--no-client-tag Don't add client tag (default: adds 'client' tag) |
|
--relay <url> Custom relay URL (can be specified multiple times) |
|
|
|
Examples: |
|
gitrep publish event --kind 1 --content "Hello, Nostr!" |
|
gitrep publish event --kind 1 --content "Hello" --tag "p" "npub1..." |
|
gitrep publish event --kind 42 --content "" --tag "t" "hashtag" --tag "p" "npub1..." |
|
gitrep publish event --kind 1 --content "Test" --no-client-tag |
|
|
|
Event Structure: |
|
All events are automatically signed with NOSTRGIT_SECRET_KEY and published to relays. |
|
Events are stored locally in nostr/ directory (JSONL format) for reference. |
|
|
|
For detailed event structure documentation, see: |
|
- https://github.com/silberengel/gitrepublic-web/tree/main/docs |
|
- docs/NIP_COMPLIANCE.md - NIP compliance and event kinds |
|
- docs/CustomKinds.md - Custom event kinds (1640, 1641, 30620) |
|
|
|
Environment Variables: |
|
NOSTRGIT_SECRET_KEY Required: Nostr private key (nsec or hex) |
|
NOSTR_RELAYS Optional: Comma-separated relay URLs (default: wss://theforest.nostr1.com,wss://relay.damus.io,wss://nostr.land) |
|
|
|
For more information, see: https://github.com/silberengel/gitrepublic-cli |
|
`); |
|
} |
|
|
|
// Export helper functions for subcommands |
|
export { addClientTag };
|
|
|