diff --git a/.dockerignore b/.dockerignore index 31e17c1..e3ef112 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,4 @@ * !package.json -!pnpm-lock.yaml +!bun.lockb !src -!tsconfig.json diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index 932b2b0..0000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -18.15 diff --git a/Dockerfile b/Dockerfile index dfa6950..eda8b4c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,11 @@ -FROM node:18.15-alpine as base - -RUN npm -g install pnpm@7 +FROM oven/bun:1 as base WORKDIR /app -COPY package.json pnpm-lock.yaml ./ -FROM base as build -RUN pnpm install --frozen-lockfile +FROM base as builder COPY . . -RUN pnpm run build - -FROM base as runner -RUN pnpm install --frozen-lockfile --prod -COPY --from=build /app/dist /app/dist -CMD ["pnpm", "run", "start"] - - - +RUN bun install --production --frozen-lockfile +RUN bun build --target bun --minify src/index.ts --outfile app.js +FROM base +COPY --from=builder /app/app.js . +ENTRYPOINT [ "bun", "run", "app.js" ] diff --git a/README.md b/README.md index ec6c3fb..621e631 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ GITHUB_TOKEN= # Host of the Gitea server GITEA_HOST= -# Gitea token (scopes: repo) +# Gitea token (scopes: repos: read and write; user: read) GITEA_TOKEN= # OPTIONAL diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..7005452 Binary files /dev/null and b/bun.lockb differ diff --git a/docker-compose.yaml b/docker-compose.yaml index 056033f..bc674e8 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,10 +1,10 @@ # FOR DEVELOPMENT ONLY # See README.md for an example -version: "3.8" +version: '3.8' services: - job: + sync: image: cupcakearmy/gitea-sync build: . env_file: .env @@ -18,4 +18,4 @@ services: - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro ports: - - "3000:3000" + - '3000:3000' diff --git a/package.json b/package.json index 705524a..b767c10 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "1.1.0", + "version": "1.2.0", "type": "module", "scripts": { "start": "node .", @@ -8,14 +8,13 @@ }, "main": "./dist/index.js", "dependencies": { - "axios": "^1.6.0", - "dotenv": "^16.0.3", - "node-cron": "^3.0.2", - "winston": "^3.8.2" + "axios": "^1.6.7", + "node-cron": "^3.0.3", + "winston": "^3.11.0" }, "devDependencies": { - "@types/node": "18", - "@types/node-cron": "^3.0.7", - "typescript": "^4.9.5" + "@types/node": "^20.11.7", + "@types/node-cron": "^3.0.11", + "typescript": "^5.3.3" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml deleted file mode 100644 index 86cffb3..0000000 --- a/pnpm-lock.yaml +++ /dev/null @@ -1,302 +0,0 @@ -lockfileVersion: '6.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -dependencies: - axios: - specifier: ^1.6.0 - version: 1.6.0 - dotenv: - specifier: ^16.0.3 - version: 16.0.3 - node-cron: - specifier: ^3.0.2 - version: 3.0.2 - winston: - specifier: ^3.8.2 - version: 3.8.2 - -devDependencies: - '@types/node': - specifier: '18' - version: 18.14.6 - '@types/node-cron': - specifier: ^3.0.7 - version: 3.0.7 - typescript: - specifier: ^4.9.5 - version: 4.9.5 - -packages: - - /@colors/colors@1.5.0: - resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} - engines: {node: '>=0.1.90'} - dev: false - - /@dabh/diagnostics@2.0.3: - resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} - dependencies: - colorspace: 1.1.4 - enabled: 2.0.0 - kuler: 2.0.0 - dev: false - - /@types/node-cron@3.0.7: - resolution: {integrity: sha512-9PuLtBboc/+JJ7FshmJWv769gDonTpItN0Ol5TMwclpSQNjVyB2SRxSKBcTtbSysSL5R7Oea06kTTFNciCoYwA==} - dev: true - - /@types/node@18.14.6: - resolution: {integrity: sha512-93+VvleD3mXwlLI/xASjw0FzKcwzl3OdTCzm1LaRfqgS21gfFtK3zDXM5Op9TeeMsJVOaJ2VRDpT9q4Y3d0AvA==} - dev: true - - /@types/triple-beam@1.3.2: - resolution: {integrity: sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==} - dev: false - - /async@3.2.4: - resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} - dev: false - - /asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: false - - /axios@1.6.0: - resolution: {integrity: sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==} - dependencies: - follow-redirects: 1.15.2 - form-data: 4.0.0 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - dev: false - - /color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - dependencies: - color-name: 1.1.3 - dev: false - - /color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - dev: false - - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: false - - /color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} - dependencies: - color-name: 1.1.4 - simple-swizzle: 0.2.2 - dev: false - - /color@3.2.1: - resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} - dependencies: - color-convert: 1.9.3 - color-string: 1.9.1 - dev: false - - /colorspace@1.1.4: - resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} - dependencies: - color: 3.2.1 - text-hex: 1.0.0 - dev: false - - /combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - dependencies: - delayed-stream: 1.0.0 - dev: false - - /delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - dev: false - - /dotenv@16.0.3: - resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} - engines: {node: '>=12'} - dev: false - - /enabled@2.0.0: - resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} - dev: false - - /fecha@4.2.3: - resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} - dev: false - - /fn.name@1.1.0: - resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - dev: false - - /follow-redirects@1.15.2: - resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - dev: false - - /form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - dev: false - - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: false - - /is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - dev: false - - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: false - - /kuler@2.0.0: - resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} - dev: false - - /logform@2.5.1: - resolution: {integrity: sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==} - dependencies: - '@colors/colors': 1.5.0 - '@types/triple-beam': 1.3.2 - fecha: 4.2.3 - ms: 2.1.3 - safe-stable-stringify: 2.4.2 - triple-beam: 1.3.0 - dev: false - - /mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - dev: false - - /mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - dependencies: - mime-db: 1.52.0 - dev: false - - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - dev: false - - /node-cron@3.0.2: - resolution: {integrity: sha512-iP8l0yGlNpE0e6q1o185yOApANRe47UPbLf4YxfbiNHt/RU5eBcGB/e0oudruheSf+LQeDMezqC5BVAb5wwRcQ==} - engines: {node: '>=6.0.0'} - dependencies: - uuid: 8.3.2 - dev: false - - /one-time@1.0.0: - resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} - dependencies: - fn.name: 1.1.0 - dev: false - - /proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - dev: false - - /readable-stream@3.6.1: - resolution: {integrity: sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==} - engines: {node: '>= 6'} - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - dev: false - - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: false - - /safe-stable-stringify@2.4.2: - resolution: {integrity: sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==} - engines: {node: '>=10'} - dev: false - - /simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - dependencies: - is-arrayish: 0.3.2 - dev: false - - /stack-trace@0.0.10: - resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} - dev: false - - /string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - dependencies: - safe-buffer: 5.2.1 - dev: false - - /text-hex@1.0.0: - resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} - dev: false - - /triple-beam@1.3.0: - resolution: {integrity: sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==} - dev: false - - /typescript@4.9.5: - resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} - engines: {node: '>=4.2.0'} - hasBin: true - dev: true - - /util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - dev: false - - /uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true - dev: false - - /winston-transport@4.5.0: - resolution: {integrity: sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==} - engines: {node: '>= 6.4.0'} - dependencies: - logform: 2.5.1 - readable-stream: 3.6.1 - triple-beam: 1.3.0 - dev: false - - /winston@3.8.2: - resolution: {integrity: sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==} - engines: {node: '>= 12.0.0'} - dependencies: - '@colors/colors': 1.5.0 - '@dabh/diagnostics': 2.0.3 - async: 3.2.4 - is-stream: 2.0.1 - logform: 2.5.1 - one-time: 1.0.0 - readable-stream: 3.6.1 - safe-stable-stringify: 2.4.2 - stack-trace: 0.0.10 - triple-beam: 1.3.0 - winston-transport: 4.5.0 - dev: false diff --git a/src/api/gitea.ts b/src/api/gitea.ts index 6430a3f..640521f 100644 --- a/src/api/gitea.ts +++ b/src/api/gitea.ts @@ -12,6 +12,8 @@ const Base = axios.create({ }, }) +const l = logger.child({ api: 'gitea' }) + export type MirrorOptions = { private: boolean auth_token: string @@ -20,7 +22,7 @@ export type MirrorOptions = { } export async function mirror(options: MirrorOptions) { try { - logger.debug('Mirroring repository', options) + l.debug('mirroring repository', options) const response = await Base({ url: '/repos/migrate', method: 'POST', @@ -41,18 +43,19 @@ export async function mirror(options: MirrorOptions) { releases: true, }, }) - logger.debug('Mirrored repository', { data: response.data }) + l.debug('mirrored repository', { data: response.data }) return response.data } catch (e) { if (e instanceof AxiosError) { - logger.error('Error mirroring repository', e.response?.data) + l.error('Error mirroring repository', e.response?.data) } else { - logger.error('Unknown error', e) + l.error('Unknown error', e) } } } export async function listRepositories(page: number, limit: number) { + l.debug(`listing repos`, { page, limit }) const response = await Base({ url: '/user/repos', method: 'GET', @@ -65,22 +68,23 @@ export async function listRepositories(page: number, limit: number) { } export async function listAllRepositories() { - logger.debug('Listing all repositories in Gitea') + l.debug('listing all repositories') const limit = 50 const repos: ListRepositoriesResponse = [] let page = 1 while (true) { + l.debug('listing page', { page }) const response = await listRepositories(page, limit) repos.push(...response) if (response.length < limit) break page++ } - logger.debug('Listed all repositories in Gitea', { data: repos }) + l.debug('listed all repositories', { repos: repos.map((repo) => repo.name) }) return repos } export async function updateRepository(owner: string, repo: string, body: Partial) { - logger.debug('Updating repository', { owner, repo, body }) + l.debug('updating repository', { owner, repo, body }) await Base({ url: `/repos/${owner}/${repo}`, method: 'PATCH', diff --git a/src/api/github.ts b/src/api/github.ts index 2fb6812..74eddc0 100644 --- a/src/api/github.ts +++ b/src/api/github.ts @@ -2,6 +2,7 @@ import axios from 'axios' import { Config } from '../config.js' import type { ListRepositoriesResponse } from './github.types.js' +import { logger } from '../logger.js' const Base = axios.create({ baseURL: 'https://api.github.com', @@ -12,7 +13,10 @@ const Base = axios.create({ }, }) +const l = logger.child({ api: 'github' }) + async function listRepos(page: number, limit: number) { + l.debug('listing repos', { page, limit }) const response = await Base({ url: `user/repos`, method: 'GET', @@ -26,14 +30,17 @@ async function listRepos(page: number, limit: number) { } export async function listAllRepositories() { + l.debug('listing all repos') const limit = 100 const repos: ListRepositoriesResponse = [] let page = 1 while (true) { + l.debug('listing page', { page }) const response = await listRepos(page, limit) repos.push(...response) if (response.length < limit) break page++ } + l.debug('listed all repos') return repos } diff --git a/src/config.ts b/src/config.ts index 02b97b1..8b5a127 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,7 +1,3 @@ -import { config } from 'dotenv' - -config() - function getEnv(key: string, fallback: string, parse?: undefined, validator?: (s: string) => boolean): string function getEnv(key: string, fallback: T, parse: (value: string) => T, validator?: (T: string) => boolean): T function getEnv( @@ -19,12 +15,6 @@ function getEnv( return parsed } -function parseBoolean(value: string): boolean { - value = value.toLowerCase() - const truthy = ['true', 'yes', '1', 'y'] - return truthy.includes(value) -} - function isPresent(s: string): boolean { return s.length > 0 } diff --git a/src/core.ts b/src/core.ts index 14f49cd..8e84463 100644 --- a/src/core.ts +++ b/src/core.ts @@ -5,30 +5,32 @@ import { logger } from './logger.js' let running = false +const l = logger.child({ context: 'runner' }) + export async function sync() { if (running) { - logger.info('Already running, skipping') + l.info('already running, skipping') return } try { - logger.info('Starting sync') + l.info('starting sync') const syncedRepos = await giteaRepos() const toSync = await githubRepos() - logger.debug('Loaded repos', { remote: toSync.length, local: syncedRepos.length }) + l.debug('loaded repos', { remote: toSync.length, local: syncedRepos.length }) for (const repo of toSync) { + const lr = l.child({ repo: repo.name }) const sameName = syncedRepos.find((r) => r.name === repo.name || r.original_url === repo.clone_url) if (sameName) { if (sameName.original_url === repo.clone_url) { if (sameName.private === repo.private) logger.info('Already synced, skipping', { name: repo.name }) else { - logger.info('Visibility changed, updating', { name: repo.name }) + lr.info('visibility changed, updating') const [owner, repository] = sameName.full_name.split('/') await updateRepository(owner, repository, { private: repo.private }) } } else { - logger.error('Repo with same name but different url', { - name: repo.name, + lr.error('repo with same name but different url', { url: repo.clone_url, original_url: sameName.original_url, }) @@ -42,14 +44,14 @@ export async function sync() { private: repo.private, auth_token: Config.github.token, } - logger.info('Mirroring repository', options) + lr.info('mirroring repository', options) await mirror(options) - logger.info('Mirrored repository', { name: repo.name }) + lr.info('mirrored repository') } - logger.info('Finished sync') + l.info('Finished sync') } catch (error) { - logger.debug(error) - logger.error('Failed to sync', { error: error instanceof Error ? error.message : 'Unknown error' }) + l.debug(error) + l.error('Failed to sync', { error: error instanceof Error ? error.message : 'Unknown error' }) } finally { running = false }