diff --git a/README.md b/README.md index 8969b89..b709668 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# GitHub to Gitea/Forgejo Migration Script +# GitHub to Forgejo Migration Script Plain simple, just install [Nushell](https://nushell.sh) and run the script: @@ -15,7 +15,7 @@ for a uninteractive run, or run the script like so: And get a nice interactive experience. -This works on any Gitea or Forgejo instance. +This works on any Forgejo instance. ## FAQ @@ -31,7 +31,7 @@ This works on any Gitea or Forgejo instance. ### Can I migrate specific repositories? -Nope. Just use the Gitea/Forgejo web UI for that. +Nope. Just use the Forgejo web UI for that. ## License diff --git a/migrate.nu b/migrate.nu index c2385c9..0d49e7a 100755 --- a/migrate.nu +++ b/migrate.nu @@ -1,33 +1,43 @@ #!/usr/bin/env nu def or-default [default: closure] { - if ($in | is-empty) { + if ($in == null) { do $default } else { $in } } -# Migrates a GitHub users repositories to a Gitea or Forgejo instance. +# Migrates a GitHub users repositories to a Forgejo instance. # -# Here is the accepted environment variables, if one isn't set, it will -# be prompted for: +# Accepted environment variables: # -# GITHUB_USER: The user to migrate from. -# GITHUB_TOKEN: An access token for fetching private repositories. Optional. -# GITEA_URL: The URL to the Gitea or Forgejo instance -# GITEA_USER: The user to migrate the repositories to. -# GITEA_TOKEN: An access token for the user to actually insert repositories to. -# STRATEGY: The strategy. Valid options are "mirrored" or "cloned" (case insensitive). -# FORCE_SYNC: Whether to delete a mirrored repo from Gitea or Forgejo if the source on GitHub doesn't exist anymore. Must be either "true" or "false". +# GITHUB_USER: The user to fetch the repositories from. +# GITHUB_TOKEN: An access token for fetching private repositories. Optional. +# +# FORGEJO_URL: The URL to the Forgejo instance. Must include the protocol (https://). +# FORGEJO_USER: The user to migrate the repositories to. +# FORGEJO_TOKEN: An access token for the specified user. +# +# STRATEGY: +# The strategy. Valid options are "mirrored" or "cloned" (case insensitive). +# "mirrored" will mirror the repository and tell the Forgejo instance to +# periodically update it, "cloned" will only clone once. "cloned" is +# useful if you are never going to use GitHub again. +# +# FORCE_SYNC: +# Whether to delete a mirrored repo from the Forgejo instance if the +# source on GitHub doesn't exist anymore. Must be either "true" or "false". +# +# To leave an environment variable unspecified, set it to an empty string. def main [] { - let github_user = $env | get -i GITHUB_USER | or-default { input $"(ansi red)GitHub username: (ansi reset)" } - let github_token = $env | get -i GITHUB_TOKEN | or-default { input $"(ansi red)GitHub access token (ansi yellow)\((ansi blue)optional, only used for private repositories(ansi yellow))(ansi red): (ansi reset)" } - let gitea_url = $env | get -i GITEA_URL | or-default { input $"(ansi green)Gitea instance URL \(with https://): (ansi reset)" } | str trim --right --char "/" - let gitea_user = $env | get -i GITEA_USER | or-default { input $"(ansi green)Gitea username or organization to migrate to: (ansi reset)" } - let gitea_token = $env | get -i GITEA_TOKEN | or-default { input $"(ansi green)Gitea access token: (ansi reset)" } - let strategy = $env | get -i STRATEGY | or-default { [ Mirrored Cloned ] | input list $"(ansi cyan)Should the repos be mirrored, or just cloned once?(ansi reset)" } | str downcase - let force_sync = $env | get -i FORCE_SYNC | or-default { [ "Yup, delete them" Nope ] | input list $"(ansi yellow)Should mirrored repos that don't have a GitHub source anymore be deleted?(ansi reset)" } | $in != "Nope" + let github_user = $env | get -i GITHUB_USER | or-default { input $"(ansi red)GitHub username: (ansi reset)" } + let github_token = $env | get -i GITHUB_TOKEN | or-default { input $"(ansi red)GitHub access token (ansi yellow)\((ansi blue)optional, only used for private repositories(ansi yellow))(ansi red): (ansi reset)" } + let forgejo_url = $env | get -i FORGEJO_URL | or-default { input $"(ansi green)Forgejo instance URL \(with https://): (ansi reset)" } | str trim --right --char "/" + let forgejo_user = $env | get -i FORGEJO_USER | or-default { input $"(ansi green)Forgejo username or organization to migrate to: (ansi reset)" } + let forgejo_token = $env | get -i FORGEJO_TOKEN | or-default { input $"(ansi green)Forgejo access token: (ansi reset)" } + let strategy = $env | get -i STRATEGY | or-default { [ Mirrored Cloned ] | input list $"(ansi cyan)Should the repos be mirrored, or just cloned once?(ansi reset)" } | str downcase + let force_sync = $env | get -i FORCE_SYNC | or-default { [ "Yup, delete them" Nope ] | input list $"(ansi yellow)Should mirrored repos that don't have a GitHub source anymore be deleted?(ansi reset)" } | $in != "Nope" let github_repos = do { def get-repos-at [page_nr: number] { @@ -60,32 +70,32 @@ def main [] { if $force_sync { let github_repo_names = ($github_repos | get name) - let gitea_mirrored_repos = ( - http get $"($gitea_url)/api/v1/user/repos" - -H [ Authorization $"token ($gitea_token)" ] + let forgejo_mirrored_repos = ( + http get $"($forgejo_url)/api/v1/user/repos" + -H [ Authorization $"token ($forgejo_token)" ] | filter { get mirror } | filter { ($github_token != "") and not $in.private } ) - let gitea_not_on_github = ($gitea_mirrored_repos | filter { not ($in.name in $github_repo_names) }) + let forgejo_not_on_github = ($forgejo_mirrored_repos | filter { not ($in.name in $github_repo_names) }) - $gitea_not_on_github | each {|gitea_repo| - print --no-newline $"(ansi red)Deleting ($gitea_url)/($gitea_repo.full_name) because the mirror source doesn't exist on GitHub anymore...(ansi reset)" + $forgejo_not_on_github | each {|forgejo_repo| + print --no-newline $"(ansi red)Deleting ($forgejo_url)/($forgejo_repo.full_name) because the mirror source doesn't exist on GitHub anymore...(ansi reset)" - (http delete $"($gitea_url)/api/v1/repos/($gitea_repo.full_name)" - -H [ Authorization $"token ($gitea_token)" ]) + (http delete $"($forgejo_url)/api/v1/repos/($forgejo_repo.full_name)" + -H [ Authorization $"token ($forgejo_token)" ]) print $" (ansi green_bold)Success!(ansi reset)" } } - # Mirror repos that do exist on GitHub to Gitea. + # Mirror repos that do exist on GitHub to Forgejo. $github_repos | each {|github_repo| print --no-newline $"(ansi blue)( $strategy | str capitalize | str replace "ed" "ing" ) ( [ $"(ansi green)public(ansi blue)(char space)" $"(ansi red)private(ansi blue)" ] | get ($github_repo.private | into int) - ) repository (ansi purple)($github_repo.html_url)(ansi blue) to (ansi white_bold)($gitea_url)/($gitea_user)/($github_repo.name)(ansi blue)...(ansi reset)" + ) repository (ansi purple)($github_repo.html_url)(ansi blue) to (ansi white_bold)($forgejo_url)/($forgejo_user)/($github_repo.name)(ansi blue)...(ansi reset)" let github_repo_url = if not $github_repo.private { $github_repo.html_url @@ -94,16 +104,16 @@ def main [] { } let response = ( - http post $"($gitea_url)/api/v1/repos/migrate" + http post $"($forgejo_url)/api/v1/repos/migrate" --allow-errors -t application/json - -H [ Authorization $"token ($gitea_token)" ] + -H [ Authorization $"token ($forgejo_token)" ] { clone_addr: $github_repo_url mirror: ($strategy != "cloned") private: $github_repo.private - repo_owner: $gitea_user + repo_owner: $forgejo_user repo_name: $github_repo.name } )