diff --git a/.dockerignore b/.dockerignore index e3ef112..3509af5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,4 @@ * -!package.json -!bun.lockb +!deno.jsonc +!deno.lock !src diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..cbac569 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "deno.enable": true +} diff --git a/Dockerfile b/Dockerfile index eda8b4c..75911ef 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,11 @@ -FROM oven/bun:1 as base +FROM denoland/deno AS builder WORKDIR /app -FROM base as builder COPY . . -RUN bun install --production --frozen-lockfile -RUN bun build --target bun --minify src/index.ts --outfile app.js +RUN deno install --frozen +RUN deno compile --allow-net --allow-env --no-prompt --output /app/sync ./src/index.ts -FROM base -COPY --from=builder /app/app.js . -ENTRYPOINT [ "bun", "run", "app.js" ] +FROM debian:stable-slim +WORKDIR /app +COPY --from=builder /app/sync . +ENTRYPOINT [ "/app/sync" ] diff --git a/bin b/bin new file mode 100755 index 0000000..a521546 Binary files /dev/null and b/bin differ diff --git a/bun.lockb b/bun.lockb deleted file mode 100755 index 7005452..0000000 Binary files a/bun.lockb and /dev/null differ diff --git a/deno.jsonc b/deno.jsonc new file mode 100644 index 0000000..16f1bba --- /dev/null +++ b/deno.jsonc @@ -0,0 +1,8 @@ +{ + "version": "1.3.0", + "imports": { + "axios": "npm:axios@^1.7.9", + "node-cron": "npm:node-cron@^3.0.3", + "winston": "npm:winston@^3.17.0" + } +} diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..d6e8a13 --- /dev/null +++ b/deno.lock @@ -0,0 +1,228 @@ +{ + "version": "4", + "specifiers": { + "jsr:@std/dotenv@*": "0.225.3", + "npm:axios@*": "1.7.9", + "npm:axios@^1.7.9": "1.7.9", + "npm:node-cron@*": "3.0.3", + "npm:node-cron@^3.0.3": "3.0.3", + "npm:winston@*": "3.17.0", + "npm:winston@^3.17.0": "3.17.0" + }, + "jsr": { + "@std/dotenv@0.225.3": { + "integrity": "a95e5b812c27b0854c52acbae215856d9cce9d4bbf774d938c51d212711e8d4a" + } + }, + "npm": { + "@colors/colors@1.6.0": { + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==" + }, + "@dabh/diagnostics@2.0.3": { + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": [ + "colorspace", + "enabled", + "kuler" + ] + }, + "@types/triple-beam@1.3.5": { + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, + "async@3.2.6": { + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + }, + "asynckit@0.4.0": { + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "axios@1.7.9": { + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dependencies": [ + "follow-redirects", + "form-data", + "proxy-from-env" + ] + }, + "color-convert@1.9.3": { + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": [ + "color-name" + ] + }, + "color-name@1.1.3": { + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "color-string@1.9.1": { + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": [ + "color-name", + "simple-swizzle" + ] + }, + "color@3.2.1": { + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": [ + "color-convert", + "color-string" + ] + }, + "colorspace@1.1.4": { + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": [ + "color", + "text-hex" + ] + }, + "combined-stream@1.0.8": { + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": [ + "delayed-stream" + ] + }, + "delayed-stream@1.0.0": { + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "enabled@2.0.0": { + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "fecha@4.2.3": { + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "fn.name@1.1.0": { + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "follow-redirects@1.15.9": { + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==" + }, + "form-data@4.0.1": { + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dependencies": [ + "asynckit", + "combined-stream", + "mime-types" + ] + }, + "inherits@2.0.4": { + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-arrayish@0.3.2": { + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "is-stream@2.0.1": { + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "kuler@2.0.0": { + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "logform@2.7.0": { + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "dependencies": [ + "@colors/colors", + "@types/triple-beam", + "fecha", + "ms", + "safe-stable-stringify", + "triple-beam" + ] + }, + "mime-db@1.52.0": { + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types@2.1.35": { + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": [ + "mime-db" + ] + }, + "ms@2.1.3": { + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node-cron@3.0.3": { + "integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==", + "dependencies": [ + "uuid" + ] + }, + "one-time@1.0.0": { + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": [ + "fn.name" + ] + }, + "proxy-from-env@1.1.0": { + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "readable-stream@3.6.2": { + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": [ + "inherits", + "string_decoder", + "util-deprecate" + ] + }, + "safe-buffer@5.2.1": { + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-stable-stringify@2.5.0": { + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==" + }, + "simple-swizzle@0.2.2": { + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": [ + "is-arrayish" + ] + }, + "stack-trace@0.0.10": { + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==" + }, + "string_decoder@1.3.0": { + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": [ + "safe-buffer" + ] + }, + "text-hex@1.0.0": { + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "triple-beam@1.4.1": { + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==" + }, + "util-deprecate@1.0.2": { + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "uuid@8.3.2": { + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "winston-transport@4.9.0": { + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dependencies": [ + "logform", + "readable-stream", + "triple-beam" + ] + }, + "winston@3.17.0": { + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "dependencies": [ + "@colors/colors", + "@dabh/diagnostics", + "async", + "is-stream", + "logform", + "one-time", + "readable-stream", + "safe-stable-stringify", + "stack-trace", + "triple-beam", + "winston-transport" + ] + } + }, + "workspace": { + "dependencies": [ + "npm:axios@^1.7.9", + "npm:node-cron@^3.0.3", + "npm:winston@^3.17.0" + ] + } +} diff --git a/package.json b/package.json deleted file mode 100644 index b767c10..0000000 --- a/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "version": "1.2.0", - "type": "module", - "scripts": { - "start": "node .", - "build": "tsc", - "dev": "tsc -w" - }, - "main": "./dist/index.js", - "dependencies": { - "axios": "^1.6.7", - "node-cron": "^3.0.3", - "winston": "^3.11.0" - }, - "devDependencies": { - "@types/node": "^20.11.7", - "@types/node-cron": "^3.0.11", - "typescript": "^5.3.3" - } -} diff --git a/src/api/gitea.ts b/src/api/gitea.ts index 640521f..989ecbc 100644 --- a/src/api/gitea.ts +++ b/src/api/gitea.ts @@ -1,9 +1,9 @@ import axios, { AxiosError } from 'axios' -import { Config } from '../config.js' -import { logger } from '../logger.js' -import { ListRepositoriesResponse } from './gitea.types.js' -import { Repository } from './github.types.js' +import { Config } from '../config.ts' +import { logger } from '../logger.ts' +import { ListRepositoriesResponse } from './gitea.types.ts' +import { Repository } from './github.types.ts' const Base = axios.create({ baseURL: new URL('/api/v1', Config.gitea.host).href, diff --git a/src/api/github.ts b/src/api/github.ts index 74eddc0..6a0888b 100644 --- a/src/api/github.ts +++ b/src/api/github.ts @@ -1,8 +1,8 @@ import axios from 'axios' -import { Config } from '../config.js' -import type { ListRepositoriesResponse } from './github.types.js' -import { logger } from '../logger.js' +import { Config } from '../config.ts' +import type { ListRepositoriesResponse } from './github.types.ts' +import { logger } from '../logger.ts' const Base = axios.create({ baseURL: 'https://api.github.com', diff --git a/src/config.ts b/src/config.ts index 8b5a127..3642334 100644 --- a/src/config.ts +++ b/src/config.ts @@ -6,11 +6,11 @@ function getEnv( parse?: (value: string) => T, validator?: (s: string | T) => boolean ): T | string { - const value = process.env[key] + const value = Deno.env.get(key) const parsed = value === undefined ? fallback : parse ? parse(value) : value if (validator && !validator(parsed)) { console.error(`Invalid or missing value for ${key}: ${value}`) - process.exit(1) + Deno.exit(1) } return parsed } diff --git a/src/core.ts b/src/core.ts index 8e84463..619061f 100644 --- a/src/core.ts +++ b/src/core.ts @@ -1,7 +1,7 @@ -import { listAllRepositories as giteaRepos, mirror, MirrorOptions, updateRepository } from './api/gitea.js' -import { listAllRepositories as githubRepos } from './api/github.js' -import { Config } from './config.js' -import { logger } from './logger.js' +import { listAllRepositories as giteaRepos, mirror, MirrorOptions, updateRepository } from './api/gitea.ts' +import { listAllRepositories as githubRepos } from './api/github.ts' +import { Config } from './config.ts' +import { logger } from './logger.ts' let running = false @@ -27,6 +27,10 @@ export async function sync() { else { lr.info('visibility changed, updating') const [owner, repository] = sameName.full_name.split('/') + if (!owner || !repository) { + lr.error('invalid repository name', { full_name: sameName.full_name }) + continue + } await updateRepository(owner, repository, { private: repo.private }) } } else { diff --git a/src/index.ts b/src/index.ts index 20c8b46..4793f83 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ import cron from 'node-cron' -import { Config } from './config.js' -import { sync } from './core.js' -import { logger } from './logger.js' +import { Config } from './config.ts' +import { sync } from './core.ts' +import { logger } from './logger.ts' logger.info(`Mirror manager - ${Config.version}`, { version: Config.version }) diff --git a/src/logger.ts b/src/logger.ts index 5e4b3b2..f934842 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,6 +1,6 @@ import winston from 'winston' -import { Config } from './config.js' +import { Config } from './config.ts' export const logger = winston.createLogger({ level: Config.logging.level, diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 0d109f8..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "compilerOptions": { - "target": "es2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - "module": "ES2022" /* Specify what module code is generated. */, - "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, - "outDir": "./dist" /* Specify an output folder for all emitted files. */, - "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, - "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, - "strict": true /* Enable all strict type-checking options. */, - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -}