commit f9e8456cf95607da3b7a61872fed27e8643ee321 Author: Morten Olsen Date: Wed Oct 1 23:00:03 2025 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fde981c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.env +/node_modules diff --git a/migrate.ts b/migrate.ts new file mode 100644 index 0000000..f381097 --- /dev/null +++ b/migrate.ts @@ -0,0 +1,62 @@ +import 'dotenv/config'; + +import { Octokit } from 'octokit'; +import { giteaApi } from 'gitea-js'; + +const mapping = { + 'morten-olsen': 'morten', + 'morten-olsen-archive': 'archive', + 'morten-olsen-env': 'env', +}; +const giteaOrgs = ['morten', 'archive'] +const github = new Octokit({ + auth: process.env.GITHUB_TOKEN, +}); + +const headers = { + Authorization: `Bearer ${process.env.GITEA_TOKEN}`, + 'Content-Type': 'application/json' +} +const giteaBaseUrl = 'https://gitea.olsen.cloud/api/v1/'; + +const iterator = github.paginate.iterator(github.rest.repos.listForAuthenticatedUser, { + per_page: 10, +}); + +for await (const { data: repos } of iterator) { + for (const repo of repos) { + const mappedOwner = mapping[repo.owner.login] || repo.owner.login; + + const url = new URL('repos/migrate', giteaBaseUrl); + console.log('start', mappedOwner, repo.name) + // url.searchParams.set('access_token', process.env.GITEA_TOKEN); + const payload = { + 'clone_addr': repo.clone_url, + 'repo_name': repo.name, + 'repo_owner': mappedOwner, + 'service': 'github', + 'auth_token': process.env.GITHUB_TOKEN, + 'private': repo.private, + 'description': repo.description || '', + 'issues': true, + 'labels': true, + 'milestones': true, + 'pull_requests': true, + 'releases': true, + 'wiki': true, + 'mirror': true, + }; + const response = await fetch(url, { + method: 'post', + headers, + body: JSON.stringify(payload) + }); + + console.log(response.status, mappedOwner, repo.name) + if (response.status !== 201) { + console.log('a', await response.text()); + } else { + await new Promise((resolve) => setTimeout(resolve, 3000)); + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..4c939b7 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "git-exodus", + "type": "module", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "packageManager": "pnpm@10.15.0", + "dependencies": { + "dotenv": "^17.2.2", + "gitea-js": "^1.23.0", + "octokit": "^5.0.3" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..2dc64e4 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,342 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + dotenv: + specifier: ^17.2.2 + version: 17.2.2 + gitea-js: + specifier: ^1.23.0 + version: 1.23.0 + octokit: + specifier: ^5.0.3 + version: 5.0.3 + +packages: + + '@octokit/app@16.1.0': + resolution: {integrity: sha512-OdKHnm0CYLk8Setr47CATT4YnRTvWkpTYvE+B/l2B0mjszlfOIit3wqPHVslD2jfc1bD4UbO7Mzh6gjCuMZKsA==} + engines: {node: '>= 20'} + + '@octokit/auth-app@8.1.0': + resolution: {integrity: sha512-6bWhyvLXqCSfHiqlwzn9pScLZ+Qnvh/681GR/UEEPCMIVwfpRDBw0cCzy3/t2Dq8B7W2X/8pBgmw6MOiyE0DXQ==} + engines: {node: '>= 20'} + + '@octokit/auth-oauth-app@9.0.1': + resolution: {integrity: sha512-TthWzYxuHKLAbmxdFZwFlmwVyvynpyPmjwc+2/cI3cvbT7mHtsAW9b1LvQaNnAuWL+pFnqtxdmrU8QpF633i1g==} + engines: {node: '>= 20'} + + '@octokit/auth-oauth-device@8.0.1': + resolution: {integrity: sha512-TOqId/+am5yk9zor0RGibmlqn4V0h8vzjxlw/wYr3qzkQxl8aBPur384D1EyHtqvfz0syeXji4OUvKkHvxk/Gw==} + engines: {node: '>= 20'} + + '@octokit/auth-oauth-user@6.0.0': + resolution: {integrity: sha512-GV9IW134PHsLhtUad21WIeP9mlJ+QNpFd6V9vuPWmaiN25HEJeEQUcS4y5oRuqCm9iWDLtfIs+9K8uczBXKr6A==} + engines: {node: '>= 20'} + + '@octokit/auth-token@6.0.0': + resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} + engines: {node: '>= 20'} + + '@octokit/auth-unauthenticated@7.0.1': + resolution: {integrity: sha512-qVq1vdjLLZdE8kH2vDycNNjuJRCD1q2oet1nA/GXWaYlpDxlR7rdVhX/K/oszXslXiQIiqrQf+rdhDlA99JdTQ==} + engines: {node: '>= 20'} + + '@octokit/core@7.0.3': + resolution: {integrity: sha512-oNXsh2ywth5aowwIa7RKtawnkdH6LgU1ztfP9AIUCQCvzysB+WeU8o2kyyosDPwBZutPpjZDKPQGIzzrfTWweQ==} + engines: {node: '>= 20'} + + '@octokit/endpoint@11.0.0': + resolution: {integrity: sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ==} + engines: {node: '>= 20'} + + '@octokit/graphql@9.0.1': + resolution: {integrity: sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg==} + engines: {node: '>= 20'} + + '@octokit/oauth-app@8.0.1': + resolution: {integrity: sha512-QnhMYEQpnYbEPn9cae+wXL2LuPMFglmfeuDJXXsyxIXdoORwkLK8y0cHhd/5du9MbO/zdG/BXixzB7EEwU63eQ==} + engines: {node: '>= 20'} + + '@octokit/oauth-authorization-url@8.0.0': + resolution: {integrity: sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ==} + engines: {node: '>= 20'} + + '@octokit/oauth-methods@6.0.0': + resolution: {integrity: sha512-Q8nFIagNLIZgM2odAraelMcDssapc+lF+y3OlcIPxyAU+knefO8KmozGqfnma1xegRDP4z5M73ABsamn72bOcA==} + engines: {node: '>= 20'} + + '@octokit/openapi-types@25.1.0': + resolution: {integrity: sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==} + + '@octokit/openapi-webhooks-types@12.0.3': + resolution: {integrity: sha512-90MF5LVHjBedwoHyJsgmaFhEN1uzXyBDRLEBe7jlTYx/fEhPAk3P3DAJsfZwC54m8hAIryosJOL+UuZHB3K3yA==} + + '@octokit/plugin-paginate-graphql@6.0.0': + resolution: {integrity: sha512-crfpnIoFiBtRkvPqOyLOsw12XsveYuY2ieP6uYDosoUegBJpSVxGwut9sxUgFFcll3VTOTqpUf8yGd8x1OmAkQ==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-paginate-rest@13.1.1': + resolution: {integrity: sha512-q9iQGlZlxAVNRN2jDNskJW/Cafy7/XE52wjZ5TTvyhyOD904Cvx//DNyoO3J/MXJ0ve3rPoNWKEg5iZrisQSuw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-rest-endpoint-methods@16.0.0': + resolution: {integrity: sha512-kJVUQk6/dx/gRNLWUnAWKFs1kVPn5O5CYZyssyEoNYaFedqZxsfYs7DwI3d67hGz4qOwaJ1dpm07hOAD1BXx6g==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-retry@8.0.1': + resolution: {integrity: sha512-KUoYR77BjF5O3zcwDQHRRZsUvJwepobeqiSSdCJ8lWt27FZExzb0GgVxrhhfuyF6z2B2zpO0hN5pteni1sqWiw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=7' + + '@octokit/plugin-throttling@11.0.1': + resolution: {integrity: sha512-S+EVhy52D/272L7up58dr3FNSMXWuNZolkL4zMJBNIfIxyZuUcczsQAU4b5w6dewJXnKYVgSHSV5wxitMSW1kw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': ^7.0.0 + + '@octokit/request-error@7.0.0': + resolution: {integrity: sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg==} + engines: {node: '>= 20'} + + '@octokit/request@10.0.3': + resolution: {integrity: sha512-V6jhKokg35vk098iBqp2FBKunk3kMTXlmq+PtbV9Gl3TfskWlebSofU9uunVKhUN7xl+0+i5vt0TGTG8/p/7HA==} + engines: {node: '>= 20'} + + '@octokit/types@14.1.0': + resolution: {integrity: sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==} + + '@octokit/webhooks-methods@6.0.0': + resolution: {integrity: sha512-MFlzzoDJVw/GcbfzVC1RLR36QqkTLUf79vLVO3D+xn7r0QgxnFoLZgtrzxiQErAjFUOdH6fas2KeQJ1yr/qaXQ==} + engines: {node: '>= 20'} + + '@octokit/webhooks@14.1.3': + resolution: {integrity: sha512-gcK4FNaROM9NjA0mvyfXl0KPusk7a1BeA8ITlYEZVQCXF5gcETTd4yhAU0Kjzd8mXwYHppzJBWgdBVpIR9wUcQ==} + engines: {node: '>= 20'} + + '@types/aws-lambda@8.10.152': + resolution: {integrity: sha512-soT/c2gYBnT5ygwiHPmd9a1bftj462NWVk2tKCc1PYHSIacB2UwbTS2zYG4jzag1mRDuzg/OjtxQjQ2NKRB6Rw==} + + before-after-hook@4.0.0: + resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} + + bottleneck@2.19.5: + resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} + + dotenv@17.2.2: + resolution: {integrity: sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==} + engines: {node: '>=12'} + + fast-content-type-parse@3.0.0: + resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==} + + gitea-js@1.23.0: + resolution: {integrity: sha512-f4+UPoWgDetZeZ+Awo5iI1nVdO5bjxA8+2QCeLo3oYWUYxKyzLfXgbW1EPD635wb8hLgS0DRBu5XhtiuYKEeUA==} + + octokit@5.0.3: + resolution: {integrity: sha512-+bwYsAIRmYv30NTmBysPIlgH23ekVDriB07oRxlPIAH5PI0yTMSxg5i5Xy0OetcnZw+nk/caD4szD7a9YZ3QyQ==} + engines: {node: '>= 20'} + + toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} + + universal-github-app-jwt@2.2.2: + resolution: {integrity: sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==} + + universal-user-agent@7.0.3: + resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} + +snapshots: + + '@octokit/app@16.1.0': + dependencies: + '@octokit/auth-app': 8.1.0 + '@octokit/auth-unauthenticated': 7.0.1 + '@octokit/core': 7.0.3 + '@octokit/oauth-app': 8.0.1 + '@octokit/plugin-paginate-rest': 13.1.1(@octokit/core@7.0.3) + '@octokit/types': 14.1.0 + '@octokit/webhooks': 14.1.3 + + '@octokit/auth-app@8.1.0': + dependencies: + '@octokit/auth-oauth-app': 9.0.1 + '@octokit/auth-oauth-user': 6.0.0 + '@octokit/request': 10.0.3 + '@octokit/request-error': 7.0.0 + '@octokit/types': 14.1.0 + toad-cache: 3.7.0 + universal-github-app-jwt: 2.2.2 + universal-user-agent: 7.0.3 + + '@octokit/auth-oauth-app@9.0.1': + dependencies: + '@octokit/auth-oauth-device': 8.0.1 + '@octokit/auth-oauth-user': 6.0.0 + '@octokit/request': 10.0.3 + '@octokit/types': 14.1.0 + universal-user-agent: 7.0.3 + + '@octokit/auth-oauth-device@8.0.1': + dependencies: + '@octokit/oauth-methods': 6.0.0 + '@octokit/request': 10.0.3 + '@octokit/types': 14.1.0 + universal-user-agent: 7.0.3 + + '@octokit/auth-oauth-user@6.0.0': + dependencies: + '@octokit/auth-oauth-device': 8.0.1 + '@octokit/oauth-methods': 6.0.0 + '@octokit/request': 10.0.3 + '@octokit/types': 14.1.0 + universal-user-agent: 7.0.3 + + '@octokit/auth-token@6.0.0': {} + + '@octokit/auth-unauthenticated@7.0.1': + dependencies: + '@octokit/request-error': 7.0.0 + '@octokit/types': 14.1.0 + + '@octokit/core@7.0.3': + dependencies: + '@octokit/auth-token': 6.0.0 + '@octokit/graphql': 9.0.1 + '@octokit/request': 10.0.3 + '@octokit/request-error': 7.0.0 + '@octokit/types': 14.1.0 + before-after-hook: 4.0.0 + universal-user-agent: 7.0.3 + + '@octokit/endpoint@11.0.0': + dependencies: + '@octokit/types': 14.1.0 + universal-user-agent: 7.0.3 + + '@octokit/graphql@9.0.1': + dependencies: + '@octokit/request': 10.0.3 + '@octokit/types': 14.1.0 + universal-user-agent: 7.0.3 + + '@octokit/oauth-app@8.0.1': + dependencies: + '@octokit/auth-oauth-app': 9.0.1 + '@octokit/auth-oauth-user': 6.0.0 + '@octokit/auth-unauthenticated': 7.0.1 + '@octokit/core': 7.0.3 + '@octokit/oauth-authorization-url': 8.0.0 + '@octokit/oauth-methods': 6.0.0 + '@types/aws-lambda': 8.10.152 + universal-user-agent: 7.0.3 + + '@octokit/oauth-authorization-url@8.0.0': {} + + '@octokit/oauth-methods@6.0.0': + dependencies: + '@octokit/oauth-authorization-url': 8.0.0 + '@octokit/request': 10.0.3 + '@octokit/request-error': 7.0.0 + '@octokit/types': 14.1.0 + + '@octokit/openapi-types@25.1.0': {} + + '@octokit/openapi-webhooks-types@12.0.3': {} + + '@octokit/plugin-paginate-graphql@6.0.0(@octokit/core@7.0.3)': + dependencies: + '@octokit/core': 7.0.3 + + '@octokit/plugin-paginate-rest@13.1.1(@octokit/core@7.0.3)': + dependencies: + '@octokit/core': 7.0.3 + '@octokit/types': 14.1.0 + + '@octokit/plugin-rest-endpoint-methods@16.0.0(@octokit/core@7.0.3)': + dependencies: + '@octokit/core': 7.0.3 + '@octokit/types': 14.1.0 + + '@octokit/plugin-retry@8.0.1(@octokit/core@7.0.3)': + dependencies: + '@octokit/core': 7.0.3 + '@octokit/request-error': 7.0.0 + '@octokit/types': 14.1.0 + bottleneck: 2.19.5 + + '@octokit/plugin-throttling@11.0.1(@octokit/core@7.0.3)': + dependencies: + '@octokit/core': 7.0.3 + '@octokit/types': 14.1.0 + bottleneck: 2.19.5 + + '@octokit/request-error@7.0.0': + dependencies: + '@octokit/types': 14.1.0 + + '@octokit/request@10.0.3': + dependencies: + '@octokit/endpoint': 11.0.0 + '@octokit/request-error': 7.0.0 + '@octokit/types': 14.1.0 + fast-content-type-parse: 3.0.0 + universal-user-agent: 7.0.3 + + '@octokit/types@14.1.0': + dependencies: + '@octokit/openapi-types': 25.1.0 + + '@octokit/webhooks-methods@6.0.0': {} + + '@octokit/webhooks@14.1.3': + dependencies: + '@octokit/openapi-webhooks-types': 12.0.3 + '@octokit/request-error': 7.0.0 + '@octokit/webhooks-methods': 6.0.0 + + '@types/aws-lambda@8.10.152': {} + + before-after-hook@4.0.0: {} + + bottleneck@2.19.5: {} + + dotenv@17.2.2: {} + + fast-content-type-parse@3.0.0: {} + + gitea-js@1.23.0: {} + + octokit@5.0.3: + dependencies: + '@octokit/app': 16.1.0 + '@octokit/core': 7.0.3 + '@octokit/oauth-app': 8.0.1 + '@octokit/plugin-paginate-graphql': 6.0.0(@octokit/core@7.0.3) + '@octokit/plugin-paginate-rest': 13.1.1(@octokit/core@7.0.3) + '@octokit/plugin-rest-endpoint-methods': 16.0.0(@octokit/core@7.0.3) + '@octokit/plugin-retry': 8.0.1(@octokit/core@7.0.3) + '@octokit/plugin-throttling': 11.0.1(@octokit/core@7.0.3) + '@octokit/request-error': 7.0.0 + '@octokit/types': 14.1.0 + '@octokit/webhooks': 14.1.3 + + toad-cache@3.7.0: {} + + universal-github-app-jwt@2.2.2: {} + + universal-user-agent@7.0.3: {} diff --git a/remove-all.ts b/remove-all.ts new file mode 100644 index 0000000..c07b63c --- /dev/null +++ b/remove-all.ts @@ -0,0 +1,26 @@ +import 'dotenv/config'; + +import { Octokit } from 'octokit'; +import { giteaApi } from 'gitea-js'; + +const github = new Octokit({ + auth: process.env.GITHUB_TOKEN, +}); + +const response = await fetch('https://gitea.olsen.cloud/api/v1/user/repos', { + headers: { + authorization: `Bearer ${process.env.GITEA_TOKEN}`, + }, +}) +const repos = await response.json(); +for (const repo of repos) { + const response = await fetch(`https://gitea.olsen.cloud/api/v1/repos/${repo.owner.login}/${repo.name}`, { + method: 'delete', + headers: { + authorization: `Bearer ${process.env.GITEA_TOKEN}`, + }, + }); + + console.log(response.status, repo.owner.login, repo.name) +} +