diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 7d671fc..1e6afb2 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -59,6 +59,12 @@ if [ "$(id -u)" = "0" ]; then echo "This may indicate a UID conflict. Consider using a different GITREPUBLIC_UID." fi + # Configure git user.name and user.email for gitrepublic user + # This is required for git commits to work properly + echo "Configuring git identity for $ACTUAL_USER..." + su-exec $ACTUAL_USER git config --global user.name "GitRepublic" || true + su-exec $ACTUAL_USER git config --global user.email "gitrepublic@gitrepublic.web" || true + echo "Switching to user: $ACTUAL_USER (UID: $GITREPUBLIC_UID)..." exec su-exec $ACTUAL_USER "$@" else diff --git a/nostr/commit-signatures.jsonl b/nostr/commit-signatures.jsonl index d1b5452..cf4b32b 100644 --- a/nostr/commit-signatures.jsonl +++ b/nostr/commit-signatures.jsonl @@ -113,3 +113,4 @@ {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772220851,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"d98d2d6a6eb27ba36f19015f7d6969fe3925c40b23187d70ccc9b61141c6b4b7","sig":"8727e3015e38a78d7a6105c26e5b1469dc4d6d701e58d5d6c522ab529b4daa2d39d4353eb6d091f3c1fd28ad0289eae808494c9e2722bf9065dd2b2e9001664f"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772223624,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"99cb543f1e821f1b7df4bbde2b3da3ab3a09cda7a1e9a537fe1b8df79b19e8e8","sig":"762a7ea92457ce81cc5aae9bc644fb9d80f90c7500035fbb506f2f76a5942333b828cc8a59f7656b0e714b15a59158be0a671f51476be2e8eabe9731ced74bcb"} {"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772226191,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"20be97351d2b05fa7ad9e161b2619e9babaaffc6a8090057c1a3ac50a0f08d6a","sig":"a174c7dd39f613dd88260ef5c111b943df381b0acae20d048596e11ef1a6b0e3c1bfb9a8858af3df0f8858c4c79d1e2d03ad248a0608ac5d5cded6a81e99af77"} +{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1772227102,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fix"]],"content":"Signed commit: bug-fix","id":"0f366a0cc7c003f74e375f40e7c322781746d12829943df1287bf67f36e1330a","sig":"167177ccfeb053cd645e50e7d00450b847ecd65c305165777bcfbe39fd3f48ccc86b57fdd183d2a4b138d94d27d11e4f1c121d702b295d94b9aee0a8dc81a744"} diff --git a/src/lib/services/git/announcement-manager.ts b/src/lib/services/git/announcement-manager.ts index c734d7e..dbdce37 100644 --- a/src/lib/services/git/announcement-manager.ts +++ b/src/lib/services/git/announcement-manager.ts @@ -266,6 +266,17 @@ export class AnnouncementManager { logger.info({ repoPath, filesToAdd, isEmpty }, 'Adding files and committing announcement'); await workGit.add(filesToAdd); + // Configure git user.name and user.email for this repository + // This is required for git commits to work (committer identity) + // We use a generic identity since the server is making the commit on behalf of the system + try { + await workGit.addConfig('user.name', 'GitRepublic', false, 'local'); + await workGit.addConfig('user.email', 'gitrepublic@gitrepublic.web', false, 'local'); + logger.debug({ repoPath }, 'Configured git user.name and user.email for repository'); + } catch (configError) { + logger.warn({ repoPath, error: configError }, 'Failed to set git config, commit may fail'); + } + // Use the event timestamp for commit date const commitDate = new Date(event.created_at * 1000).toISOString(); const commitMessage = selfTransferEvent @@ -451,13 +462,9 @@ export class AnnouncementManager { isEmpty }, 'Failed to ensure announcement in repo'); - // For empty repos, this is critical - we need the initial commit - // For non-empty repos, it's less critical but still important - if (isEmpty) { - // Re-throw for empty repos so caller knows it failed - throw new Error(`Failed to commit announcement to empty repo: ${errorMessage}`); - } - // For non-empty repos, don't throw - announcement file creation is important but shouldn't block provisioning + // Don't throw - this is now non-blocking + // The announcement is available from relays, so this is just for offline papertrail + // Even for empty repos, we don't throw since provisioning should succeed regardless } } diff --git a/src/lib/services/git/repo-manager.ts b/src/lib/services/git/repo-manager.ts index 4024e46..aaa2dff 100644 --- a/src/lib/services/git/repo-manager.ts +++ b/src/lib/services/git/repo-manager.ts @@ -165,27 +165,34 @@ export class RepoManager { } else { // Branches exist (from sync) - ensure announcement is committed to the default branch // This must happen after syncing so we can commit it to the existing default branch - await this.announcementManager.ensureAnnouncementInRepo(repoPath.fullPath, event, selfTransferEvent); + // Non-blocking: fire and forget - we have the announcement from relays, so this is just for offline papertrail + this.announcementManager.ensureAnnouncementInRepo(repoPath.fullPath, event, selfTransferEvent) + .catch((err) => { + logger.warn({ error: err, repoPath: repoPath.fullPath, eventId: event.id }, + 'Failed to save announcement to repo (non-blocking, announcement available from relays)'); + }); } } else { // For existing repos, check if announcement exists in repo // If not, try to fetch from relays and save it + // Note: We have the announcement from polling (event parameter), so we can use that + // Non-blocking: fire and forget - we have the announcement from relays, so this is just for offline papertrail const hasAnnouncement = await this.announcementManager.hasAnnouncementInRepoFile(repoPath.fullPath); if (!hasAnnouncement) { - // Try to fetch from relays - const fetchedEvent = await this.announcementManager.fetchAnnouncementFromRelays(event.pubkey, repoPath.repoName); - if (fetchedEvent) { - // Save fetched announcement to repo - await this.announcementManager.ensureAnnouncementInRepo(repoPath.fullPath, fetchedEvent, selfTransferEvent); - } else { - // Announcement not found in repo or relays - this is a problem - logger.warn({ repoPath: repoPath.fullPath }, 'Existing repo has no announcement in repo or on relays'); - } - } - - if (selfTransferEvent) { - // Ensure self-transfer event is also saved - await this.announcementManager.ensureAnnouncementInRepo(repoPath.fullPath, event, selfTransferEvent); + // We have the event from polling, so use it directly (no need to fetch from relays again) + // Save announcement to repo asynchronously + this.announcementManager.ensureAnnouncementInRepo(repoPath.fullPath, event, selfTransferEvent) + .catch((err) => { + logger.warn({ error: err, repoPath: repoPath.fullPath, eventId: event.id }, + 'Failed to save announcement to repo (non-blocking, announcement available from relays)'); + }); + } else if (selfTransferEvent) { + // Announcement exists but self-transfer might not - save it asynchronously + this.announcementManager.ensureAnnouncementInRepo(repoPath.fullPath, event, selfTransferEvent) + .catch((err) => { + logger.warn({ error: err, repoPath: repoPath.fullPath, eventId: event.id }, + 'Failed to save self-transfer to repo (non-blocking)'); + }); } } } @@ -302,6 +309,18 @@ Your commits will all be signed by your Nostr keys and saved to the event files } await workGit.add(filesToAdd); + // Configure git user.name and user.email for this repository + // This is required for git commits to work (committer identity) + // We use a generic identity since the server is making the commit on behalf of the system + // The --author flag sets the author, but we still need committer identity configured + try { + await workGit.addConfig('user.name', 'GitRepublic', false, 'local'); + await workGit.addConfig('user.email', 'gitrepublic@gitrepublic.web', false, 'local'); + logger.debug({ repoPath, npub, repoName }, 'Configured git user.name and user.email for repository'); + } catch (configError) { + logger.warn({ repoPath, npub, repoName, error: configError }, 'Failed to set git config, commit may fail'); + } + // Commit files together await workGit.commit('Initial commit', filesToAdd, { '--author': `${authorName} <${authorEmail}>` @@ -371,8 +390,13 @@ Your commits will all be signed by your Nostr keys and saved to the event files } if (announcementToUse) { - // Save announcement to repo (this will create initial commit if repo is empty) - await this.announcementManager.ensureAnnouncementInRepo(repoPath, announcementToUse); + // Save announcement to repo asynchronously (non-blocking) + // We have the announcement from relays, so this is just for offline papertrail + this.announcementManager.ensureAnnouncementInRepo(repoPath, announcementToUse) + .catch((err) => { + logger.warn({ error: err, repoPath, eventId: announcementToUse?.id }, + 'Failed to save announcement to repo (non-blocking, announcement available from relays)'); + }); return { success: true, announcement: announcementToUse }; } @@ -616,12 +640,12 @@ Your commits will all be signed by your Nostr keys and saved to the event files } // Ensure announcement is saved to nostr/repo-events.jsonl (non-blocking - repo is usable without it) - try { - await this.announcementManager.ensureAnnouncementInRepo(repoPath, announcementEvent); - } catch (verifyError) { - // Announcement file creation is optional - log but don't fail - logger.warn({ error: verifyError, npub, repoName }, 'Failed to ensure announcement in repo, but repository is usable'); - } + // Fire and forget - we have the announcement from relays, so this is just for offline papertrail + this.announcementManager.ensureAnnouncementInRepo(repoPath, announcementEvent) + .catch((verifyError) => { + // Announcement file creation is optional - log but don't fail + logger.warn({ error: verifyError, npub, repoName }, 'Failed to ensure announcement in repo, but repository is usable'); + }); logger.info({ npub, repoName }, 'Successfully fetched repository on-demand'); return { success: true, announcement: announcementEvent };