commit ba2959b6833bf0e390c3482f6494ad3e1eb4e81e Author: Vojtěch Mareš Date: Sat May 17 08:43:13 2025 +0200 feat: initial commit Signed-off-by: Vojtěch Mareš diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..87a0020 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf +max_line_length = null + +[Makefile] +indent_style = tab diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a259904 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store + +# app things +repo diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..38a3670 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,15 @@ +defaults: + image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/oven/bun:alpine + +stages: + - copy + +copy: + stage: copy + before_script: + - apk add --no-cache git openssh + - git config --global user.email gitlab@mail.mareshq.com + - git config --global user.name "GitLab CI" + - git config --global url.“https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.mareshq.com”.insteadOf https://gitlab.mareshq.com" + script: + - bun run src/main.ts diff --git a/README.md b/README.md new file mode 100644 index 0000000..5170967 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# copypasta + +A CI based tool to sync remote repositories to my GitLab instance without paying for GitLab Premium to have the pull-based mirroring available to me. diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..d9ce6a0 --- /dev/null +++ b/bun.lock @@ -0,0 +1,25 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "copypasta", + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, + }, + "packages": { + "@types/bun": ["@types/bun@1.2.13", "", { "dependencies": { "bun-types": "1.2.13" } }, "sha512-u6vXep/i9VBxoJl3GjZsl/BFIsvML8DfVDO0RYLEwtSZSp981kEO1V5NwRcO1CPJ7AmvpbnDCiMKo3JvbDEjAg=="], + + "@types/node": ["@types/node@22.15.18", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg=="], + + "bun-types": ["bun-types@1.2.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-rRjA1T6n7wto4gxhAO/ErZEtOXyEZEmnIHQfl0Dt1QQSB4QV0iP6BZ9/YB5fZaHFQ2dwHFrmPaRQ9GGMX01k9Q=="], + + "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8a80993 --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "copypasta", + "module": "src/main.ts", + "type": "module", + "private": true, + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5" + } +} diff --git a/repositories.json b/repositories.json new file mode 100644 index 0000000..7c63ca2 --- /dev/null +++ b/repositories.json @@ -0,0 +1,12 @@ +{ + "repositories": [ + { + "src": "git@gitlab.com:components/opentofu.git", + "dest": "https://gitlab.mareshq.com:gitlab-components/opentofu.git" + }, + { + "src": "git@gitlab.com:renovate-bot/renovate-runner.git", + "dest": "https://gitlab.mareshq.com:gitlab-components/renovate-runner.git" + } + ] +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..24587a9 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,35 @@ +import { $ } from "bun"; +import { repositories } from "../repositories.json" + +async function main() { + const workspace = (await $`pwd`.text()).replaceAll("\n", ""); + + for (const repo of repositories) { + // clone remote repository (source repository) + const repoName = repo.src.split("@")[1]; + if (!repoName) { + continue; + } + + console.log(`Cloning ${repoName}...`); + await $`git clone ${repo.src} repo`; + + await $.cwd("repo"); + await $`git remote rename origin upstream`; + await $`git remote add origin ${repo.dest}`; + + await $`git fetch upstream`; + await $`git fetch origin`; + + const branch = (await $`git branch --show-current`.text()).replaceAll("\n", ""); + await $`git switch -c upstream/${branch}`; + + await $`git push -u origin upstream/${branch}`; + + // clean up + await $.cwd(workspace); + await $`rm -rf repo`; + } +} + +await main(); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9c62f74 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +}