diff --git a/full-sync.js b/full-sync.js index 8ba1c7353..930f191a0 100644 --- a/full-sync.js +++ b/full-sync.js @@ -1,16 +1,32 @@ const appFn = require('./') const { FULL_SYNC_NOP } = require('./lib/env') const { createProbot } = require('probot') +const pino = require('pino') async function performFullSync (appFn, nop) { - const probot = createProbot() + const logLevel = process.env.LOG_LEVEL || 'info' + const logger = pino({ + level: logLevel, + transport: { + target: 'pino-pretty', + options: { + colorize: true, + ignore: 'pid,hostname', + messageFormat: '{msg}', + customColors: 'info:blue,warn:yellow,error:red', + levelFirst: true + } + } + }) + + const probot = createProbot({ overrides: { log: logger } }) probot.log.info(`Starting full sync with NOP=${nop}`) try { const app = appFn(probot, {}) const settings = await app.syncInstallation(nop) - if (settings.errors && settings.errors.length > 0) { + if (settings && settings.errors && settings.errors.length > 0) { probot.log.error('Errors occurred during full sync.') process.exit(1) } @@ -22,7 +38,13 @@ async function performFullSync (appFn, nop) { } } -performFullSync(appFn, FULL_SYNC_NOP).catch((error) => { - console.error('Fatal error during full sync:', error) - process.exit(1) -}) +// Only run if executed directly (not when imported for testing) +if (require.main === module) { + performFullSync(appFn, FULL_SYNC_NOP).catch((error) => { + console.error('Fatal error during full sync:', error) + process.exit(1) + }) +} + +// Export for testing +module.exports = { performFullSync } diff --git a/package-lock.json b/package-lock.json index cdb557108..19d3a6dd1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,8 @@ "minimatch": "^10.2.1", "node-cron": "^4.2.1", "octokit": "^5.0.2", + "pino": "^10.3.1", + "pino-pretty": "^13.1.3", "probot": "^14.2.4", "proxy-from-env": "^2.1.0", "undici": "^7.7.0" diff --git a/package.json b/package.json index 302f9f205..705a909b3 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,8 @@ "minimatch": "^10.2.1", "node-cron": "^4.2.1", "octokit": "^5.0.2", + "pino": "^10.3.1", + "pino-pretty": "^13.1.3", "probot": "^14.2.4", "proxy-from-env": "^2.1.0", "undici": "^7.7.0" diff --git a/test/unit/full-sync.test.js b/test/unit/full-sync.test.js new file mode 100644 index 000000000..584c07df4 --- /dev/null +++ b/test/unit/full-sync.test.js @@ -0,0 +1,41 @@ +const { performFullSync } = require('../../full-sync') + +jest.mock('probot', () => ({ + createProbot: jest.fn() +})) +jest.mock('pino', () => jest.fn(() => ({ info: jest.fn() }))) + +describe('full-sync.js', () => { + let mockApp + + beforeEach(() => { + jest.clearAllMocks() + require('probot').createProbot.mockImplementation(({ overrides }) => ({ + log: overrides.log + })) + mockApp = { syncInstallation: jest.fn() } + }) + + it('should pass logger to createProbot via overrides (v14 fix)', async () => { + mockApp.syncInstallation.mockResolvedValue({ errors: [] }) + await performFullSync(jest.fn().mockReturnValue(mockApp), true) + + expect(require('probot').createProbot).toHaveBeenCalledWith( + expect.objectContaining({ + overrides: expect.objectContaining({ log: expect.any(Object) }) + }) + ) + }) + + it('should handle null settings without crashing (null safety)', async () => { + mockApp.syncInstallation.mockResolvedValue(null) + const mockLogger = { info: jest.fn(), error: jest.fn() } + require('pino').mockReturnValueOnce(mockLogger) + require('probot').createProbot.mockImplementationOnce(({ overrides }) => ({ + log: overrides.log + })) + await performFullSync(jest.fn().mockReturnValue(mockApp), true) + + expect(mockLogger.info).toHaveBeenCalledWith('Full sync completed successfully.') + }) +})