-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathgithub2forgejo
More file actions
executable file
·160 lines (137 loc) · 6.34 KB
/
Copy pathgithub2forgejo
File metadata and controls
executable file
·160 lines (137 loc) · 6.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/usr/bin/env nu
def or-default [default: closure] {
if ($in == null) {
do $default
} else {
$in
}
}
# Migrates a GitHub users or organizations repositories to a Forgejo instance.
#
# Accepted environment variables:
#
# DRY:
# Only print the actions that will be taken, and don't execute.
# Defaults to false, and a lack of the variable will be interpreted as false.
#
# GITHUB_USER:
# The user or organization to fetch the repositories from.
# Case sensitive.
# GITHUB_TOKEN:
# An access token for fetching private repositories.
# Optional. Set to empty string to not ask interactively.
#
# FORGEJO_URL:
# The URL to the Forgejo instance.
# Must include the protocol (http(s)://) as it is not just a domain.
# FORGEJO_USER:
# The user or organization 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 dry = $env | get --optional DRY | into bool --relaxed
let github_user = $env | get --optional GITHUB_USER | or-default { input $"(ansi red)GitHub username: (ansi reset)" }
let github_token = $env | get --optional 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 --optional FORGEJO_URL | or-default { input $"(ansi green)Forgejo instance URL \(with https://): (ansi reset)" } | str trim --right --char "/"
let forgejo_user = $env | get --optional FORGEJO_USER | or-default { input $"(ansi green)Forgejo username or organization to migrate to: (ansi reset)" }
let forgejo_token = $env | get --optional FORGEJO_TOKEN | or-default { input $"(ansi green)Forgejo access token: (ansi reset)" }
let strategy = $env | get --optional 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 --optional FORCE_SYNC | try { into bool } | 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" }
if $strategy not-in [ "mirrored", "cloned" ] {
print --stderr $"(ansi red)Invalid strategy! Must be either: (ansi green)mirrored(ansi red) or (ansi cyan)cloned(ansi red).(ansi reset)"
}
let github_repos = do {
def get-repos-at [page_nr: number] {
if $github_token == "" {
http get $"https://api.github.com/users/($github_user)/repos?per_page=100?page=($page_nr)"
} else {
# This fetchs all repos you have access to and have interacted with. Not just your own.
# We filter that later.
(http get $"https://api.github.com/user/repos?per_page=100&page=($page_nr)"
-H [ Authorization $"token ($github_token)" ])
}
}
mut repos = []
mut page_nr = 1
loop {
let page = get-repos-at $page_nr
$repos ++= $page
if ($page | length) < 100 {
# Less than the per_page means it's the end.
break;
}
$page_nr += 1
}
$repos | where { get owner.login | $in == $github_user }
}
# Delete mirrored repos that do not exist on GitHub.
if $force_sync {
let github_repo_names = ($github_repos | get name)
let forgejo_mirrored_repos = (
http get $"($forgejo_url)/api/v1/user/repos"
-H [ Authorization $"token ($forgejo_token)" ]
| where { get mirror }
| where { if $github_token == "" { not $in.private } else { true } }
)
let forgejo_not_on_github = ($forgejo_mirrored_repos | where { $in.name not-in $github_repo_names })
$forgejo_not_on_github | each {|forgejo_repo|
print --no-newline $"(ansi red)Deleting (ansi yellow)($forgejo_url)/($forgejo_repo.full_name)(ansi red) because the mirror source doesn't exist on GitHub anymore...(ansi reset)"
if $dry {
print $" (ansi yellow)Dry run, so can't know result as I'm not actually doing it.(ansi reset)"
return
}
(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 Forgejo.
$github_repos | each {|github_repo|
print --no-newline $"(ansi blue)(match $strategy {
"mirrored" => "Mirroring"
"cloned" => "Cloning"
}) (if $github_repo.private {
$"(ansi red)private(ansi blue)"
} else {
$"(ansi green)public(ansi blue)(char space)"
}) repository (ansi purple)($github_repo.html_url)(ansi blue) to (ansi white_bold)($forgejo_url)/($forgejo_user)/($github_repo.name)(ansi blue)...(ansi reset)"
if $dry {
print $" (ansi yellow)Dry run, so can't know result as I'm not actually doing it.(ansi reset)"
return
}
let response = (
http post $"($forgejo_url)/api/v1/repos/migrate"
--allow-errors
-t application/json
-H [ Authorization $"token ($forgejo_token)" ]
({
clone_addr: $github_repo.html_url
mirror: ($strategy == "mirrored")
private: $github_repo.private
repo_owner: $forgejo_user
repo_name: $github_repo.name
} | merge (if $github_token != "" { { auth_token: $github_token } } else { {} }))
)
let error_message = ($response | get --optional message)
if ($error_message != null and $error_message =~ "already exists") {
print $" (ansi yellow)Already mirrored!(ansi reset)"
} else if ($error_message != null) {
print $" (ansi red)Unknown error: ($error_message)(ansi reset)"
} else {
print $" (ansi green_bold)Success!(ansi reset)"
}
}
null
}