Skip to content

Commit 8059fd0

Browse files
committed
Port script to package
0 parents  commit 8059fd0

11 files changed

+439
-0
lines changed

.Rbuildignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
^.*\.Rproj$
2+
^\.Rproj\.user$
3+
^action.yml$
4+
^Dockerfile$
5+
^entrypoint.sh$
6+

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.Rproj.user
2+
.Rhistory
3+
.RData
4+
.Ruserdata

DESCRIPTION

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Package: rforgemirror
2+
Type: Package
3+
Title: R-forge Mirror Tools
4+
Version: 0.1.0
5+
Authors@R: person("Jeroen", "Ooms", ,"jeroen@berkeley.edu",
6+
role = c("aut", "cre"), comment = c(ORCID = "0000-0002-4035-0289"))
7+
Description: Mirrors SVN repositories to https://github.com/rforge.
8+
License: Apache License (>= 2)
9+
Encoding: UTF-8
10+
LazyData: true
11+
RoxygenNote: 7.1.1
12+
Imports:
13+
curl,
14+
gert,
15+
gh,
16+
xml2,
17+
rvest,
18+
sys

Dockerfile

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
FROM ubuntu:focal
2+
3+
ENV DEBIAN_FRONTEND noninteractive
4+
5+
COPY . /pkg
6+
COPY entrypoint.sh /entrypoint.sh
7+
8+
RUN \
9+
apt-get update && \
10+
apt-get -y dist-upgrade && \
11+
apt-get install -y r-base-core git gcc libcurl4-openssl-dev libssl-dev libgit2-dev libxml2-dev subversion git-svn curl
12+
13+
RUN \
14+
R -e 'install.packages("remotes"); remotes::install_local("/pkg")'
15+
16+
ENTRYPOINT ["/entrypoint.sh"]
17+

LICENSE.md

+194
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
Apache License
2+
==============
3+
4+
_Version 2.0, January 2004_
5+
_&lt;<http://www.apache.org/licenses/>&gt;_
6+
7+
### Terms and Conditions for use, reproduction, and distribution
8+
9+
#### 1. Definitions
10+
11+
“License” shall mean the terms and conditions for use, reproduction, and
12+
distribution as defined by Sections 1 through 9 of this document.
13+
14+
“Licensor” shall mean the copyright owner or entity authorized by the copyright
15+
owner that is granting the License.
16+
17+
“Legal Entity” shall mean the union of the acting entity and all other entities
18+
that control, are controlled by, or are under common control with that entity.
19+
For the purposes of this definition, “control” means **(i)** the power, direct or
20+
indirect, to cause the direction or management of such entity, whether by
21+
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
22+
outstanding shares, or **(iii)** beneficial ownership of such entity.
23+
24+
“You” (or “Your”) shall mean an individual or Legal Entity exercising
25+
permissions granted by this License.
26+
27+
“Source” form shall mean the preferred form for making modifications, including
28+
but not limited to software source code, documentation source, and configuration
29+
files.
30+
31+
“Object” form shall mean any form resulting from mechanical transformation or
32+
translation of a Source form, including but not limited to compiled object code,
33+
generated documentation, and conversions to other media types.
34+
35+
“Work” shall mean the work of authorship, whether in Source or Object form, made
36+
available under the License, as indicated by a copyright notice that is included
37+
in or attached to the work (an example is provided in the Appendix below).
38+
39+
“Derivative Works” shall mean any work, whether in Source or Object form, that
40+
is based on (or derived from) the Work and for which the editorial revisions,
41+
annotations, elaborations, or other modifications represent, as a whole, an
42+
original work of authorship. For the purposes of this License, Derivative Works
43+
shall not include works that remain separable from, or merely link (or bind by
44+
name) to the interfaces of, the Work and Derivative Works thereof.
45+
46+
“Contribution” shall mean any work of authorship, including the original version
47+
of the Work and any modifications or additions to that Work or Derivative Works
48+
thereof, that is intentionally submitted to Licensor for inclusion in the Work
49+
by the copyright owner or by an individual or Legal Entity authorized to submit
50+
on behalf of the copyright owner. For the purposes of this definition,
51+
“submitted” means any form of electronic, verbal, or written communication sent
52+
to the Licensor or its representatives, including but not limited to
53+
communication on electronic mailing lists, source code control systems, and
54+
issue tracking systems that are managed by, or on behalf of, the Licensor for
55+
the purpose of discussing and improving the Work, but excluding communication
56+
that is conspicuously marked or otherwise designated in writing by the copyright
57+
owner as “Not a Contribution.”
58+
59+
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
60+
of whom a Contribution has been received by Licensor and subsequently
61+
incorporated within the Work.
62+
63+
#### 2. Grant of Copyright License
64+
65+
Subject to the terms and conditions of this License, each Contributor hereby
66+
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
67+
irrevocable copyright license to reproduce, prepare Derivative Works of,
68+
publicly display, publicly perform, sublicense, and distribute the Work and such
69+
Derivative Works in Source or Object form.
70+
71+
#### 3. Grant of Patent License
72+
73+
Subject to the terms and conditions of this License, each Contributor hereby
74+
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
75+
irrevocable (except as stated in this section) patent license to make, have
76+
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
77+
such license applies only to those patent claims licensable by such Contributor
78+
that are necessarily infringed by their Contribution(s) alone or by combination
79+
of their Contribution(s) with the Work to which such Contribution(s) was
80+
submitted. If You institute patent litigation against any entity (including a
81+
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
82+
Contribution incorporated within the Work constitutes direct or contributory
83+
patent infringement, then any patent licenses granted to You under this License
84+
for that Work shall terminate as of the date such litigation is filed.
85+
86+
#### 4. Redistribution
87+
88+
You may reproduce and distribute copies of the Work or Derivative Works thereof
89+
in any medium, with or without modifications, and in Source or Object form,
90+
provided that You meet the following conditions:
91+
92+
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
93+
this License; and
94+
* **(b)** You must cause any modified files to carry prominent notices stating that You
95+
changed the files; and
96+
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
97+
all copyright, patent, trademark, and attribution notices from the Source form
98+
of the Work, excluding those notices that do not pertain to any part of the
99+
Derivative Works; and
100+
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
101+
Derivative Works that You distribute must include a readable copy of the
102+
attribution notices contained within such NOTICE file, excluding those notices
103+
that do not pertain to any part of the Derivative Works, in at least one of the
104+
following places: within a NOTICE text file distributed as part of the
105+
Derivative Works; within the Source form or documentation, if provided along
106+
with the Derivative Works; or, within a display generated by the Derivative
107+
Works, if and wherever such third-party notices normally appear. The contents of
108+
the NOTICE file are for informational purposes only and do not modify the
109+
License. You may add Your own attribution notices within Derivative Works that
110+
You distribute, alongside or as an addendum to the NOTICE text from the Work,
111+
provided that such additional attribution notices cannot be construed as
112+
modifying the License.
113+
114+
You may add Your own copyright statement to Your modifications and may provide
115+
additional or different license terms and conditions for use, reproduction, or
116+
distribution of Your modifications, or for any such Derivative Works as a whole,
117+
provided Your use, reproduction, and distribution of the Work otherwise complies
118+
with the conditions stated in this License.
119+
120+
#### 5. Submission of Contributions
121+
122+
Unless You explicitly state otherwise, any Contribution intentionally submitted
123+
for inclusion in the Work by You to the Licensor shall be under the terms and
124+
conditions of this License, without any additional terms or conditions.
125+
Notwithstanding the above, nothing herein shall supersede or modify the terms of
126+
any separate license agreement you may have executed with Licensor regarding
127+
such Contributions.
128+
129+
#### 6. Trademarks
130+
131+
This License does not grant permission to use the trade names, trademarks,
132+
service marks, or product names of the Licensor, except as required for
133+
reasonable and customary use in describing the origin of the Work and
134+
reproducing the content of the NOTICE file.
135+
136+
#### 7. Disclaimer of Warranty
137+
138+
Unless required by applicable law or agreed to in writing, Licensor provides the
139+
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
140+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
141+
including, without limitation, any warranties or conditions of TITLE,
142+
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
143+
solely responsible for determining the appropriateness of using or
144+
redistributing the Work and assume any risks associated with Your exercise of
145+
permissions under this License.
146+
147+
#### 8. Limitation of Liability
148+
149+
In no event and under no legal theory, whether in tort (including negligence),
150+
contract, or otherwise, unless required by applicable law (such as deliberate
151+
and grossly negligent acts) or agreed to in writing, shall any Contributor be
152+
liable to You for damages, including any direct, indirect, special, incidental,
153+
or consequential damages of any character arising as a result of this License or
154+
out of the use or inability to use the Work (including but not limited to
155+
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
156+
any and all other commercial damages or losses), even if such Contributor has
157+
been advised of the possibility of such damages.
158+
159+
#### 9. Accepting Warranty or Additional Liability
160+
161+
While redistributing the Work or Derivative Works thereof, You may choose to
162+
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
163+
other liability obligations and/or rights consistent with this License. However,
164+
in accepting such obligations, You may act only on Your own behalf and on Your
165+
sole responsibility, not on behalf of any other Contributor, and only if You
166+
agree to indemnify, defend, and hold each Contributor harmless for any liability
167+
incurred by, or claims asserted against, such Contributor by reason of your
168+
accepting any such warranty or additional liability.
169+
170+
_END OF TERMS AND CONDITIONS_
171+
172+
### APPENDIX: How to apply the Apache License to your work
173+
174+
To apply the Apache License to your work, attach the following boilerplate
175+
notice, with the fields enclosed by brackets `[]` replaced with your own
176+
identifying information. (Don't include the brackets!) The text should be
177+
enclosed in the appropriate comment syntax for the file format. We also
178+
recommend that a file or class name and description of purpose be included on
179+
the same “printed page” as the copyright notice for easier identification within
180+
third-party archives.
181+
182+
Copyright [yyyy] [name of copyright owner]
183+
184+
Licensed under the Apache License, Version 2.0 (the "License");
185+
you may not use this file except in compliance with the License.
186+
You may obtain a copy of the License at
187+
188+
http://www.apache.org/licenses/LICENSE-2.0
189+
190+
Unless required by applicable law or agreed to in writing, software
191+
distributed under the License is distributed on an "AS IS" BASIS,
192+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
193+
See the License for the specific language governing permissions and
194+
limitations under the License.

NAMESPACE

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Generated by roxygen2: do not edit by hand
2+
3+
export(rforge_cleanup_repos)
4+
export(rforge_find_projects)
5+
export(rforge_list_repos)
6+
export(rforge_mirror)

R/rforge.R

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#' R-forge mirror tools
2+
#'
3+
#' Mirrors SVN repositories to \url{https://github.com/rforge}
4+
#'
5+
#' @export
6+
#' @name rforge-mirror
7+
#' @rdname rforge
8+
#' @param this_week by default we only mirror repositories with activity in
9+
#' the past week. Set this to FALSE to mirror all repos we can find.
10+
rforge_mirror <- function(this_week = TRUE){
11+
user <- gh::gh_whoami()
12+
if(!(user$login %in% c('rforge', 'r-forge')))
13+
stop("No valid PAT found for r-forge user")
14+
projects <- rforge_find_projects(this_week = this_week)
15+
projects <- setdiff(projects, skiplist)
16+
lapply(projects, mirror_one_project)
17+
invisible()
18+
}
19+
20+
#' @export
21+
#' @rdname rforge
22+
rforge_find_projects <- function(this_week = TRUE){
23+
url <- 'http://r-forge.r-project.org/top/mostactive.php'
24+
if(isTRUE(this_week))
25+
url <- paste0(url, '?type=week')
26+
projects <- find_projects(url)
27+
sort(basename(unlist(projects)))
28+
}
29+
30+
find_projects <- function(page){
31+
main <- xml2::read_html(page)
32+
links <- rvest::html_nodes(main, 'a[href^="https://r-forge.r-project.org/projects/"]')
33+
cat(sprintf("Found %d projects in %s\n", length(links), basename(page)))
34+
rvest::html_attr(links, "href")
35+
}
36+
37+
mirror_one_project <- function(project){
38+
create_gh_repo(project)
39+
clone_and_push(project)
40+
}
41+
42+
create_gh_repo <- function(project){
43+
endpoint <- paste0('/repos/rforge/', project)
44+
tryCatch({
45+
res <- gh::gh(endpoint)
46+
cat(sprintf('Found existing repo "%s"\n', res$full_name))
47+
}, http_error_404 = function(e){
48+
cat(sprintf('Did not find repo "%s". Creating new repo...\n', project))
49+
res <- gh::gh('/user/repos', .method = 'POST',
50+
name = project,
51+
has_issues = FALSE,
52+
has_wiki = FALSE,
53+
has_downloads = FALSE,
54+
homepage = paste0("https://r-forge.r-project.org/projects/", project),
55+
description = sprintf("Read-only mirror of \"%s\" from r-forge SVN.", project))
56+
cat(sprintf('Created new repo "%s"\n', res$full_name))
57+
})
58+
}
59+
60+
clone_and_push <- function(project){
61+
pwd <- getwd()
62+
on.exit(setwd(pwd))
63+
sys::exec_wait("git", c("svn", "clone", sprintf("svn://svn.r-forge.r-project.org/svnroot/%s", project), project))
64+
setwd(project)
65+
gert::git_remote_add('origin', sprintf('https://github.com/rforge/%s.git', project))
66+
gert::git_push('origin', force = TRUE, mirror = TRUE)
67+
setwd("..")
68+
unlink(project, recursive = TRUE)
69+
}
70+
71+
#' @rdname rforge
72+
#' @export
73+
rforge_list_repos <- function(){
74+
repos <- gh::gh('/users/rforge/repos', .limit = 1e5)
75+
vapply(repos, function(x){x$name}, character(1))
76+
}
77+
78+
#' @rdname rforge
79+
#' @export
80+
rforge_cleanup_repos <- function(){
81+
repos <- rforge_list_repos()
82+
status <- rforge_project_status(repos)
83+
dead <- repos[which(status == 404)]
84+
cat("Found dead repositories:", dead)
85+
lapply(dead, function(project){
86+
endpoint <- paste0("/repos/rforge/", project)
87+
gh::gh(endpoint, .method = 'DELETE')
88+
})
89+
}
90+
91+
rforge_project_status <- function(projects){
92+
out <- rep(0, length(projects))
93+
pool <- curl::new_pool()
94+
lapply(seq_along(projects), function(i){
95+
name <- projects[i]
96+
url <- paste0("https://r-forge.r-project.org/projects/", name)
97+
curl::curl_fetch_multi(url, done = function(res){
98+
out[i] <<- res$status_code
99+
cat(sprintf("Project '%s' is %d\n", name, res$status_code), file = stderr())
100+
}, pool = pool)
101+
})
102+
curl::multi_run(pool = pool)
103+
structure(out, names = projects)
104+
}
105+
106+
# These fail for whatever reason.
107+
# Usually either disabled on r-forge or has files bigger than 100MB
108+
skiplist <-
109+
c("adrminer", "arules", "bayessdeevol", "bbmm-analysis", "biocep-distrib",
110+
"casper", "dcgor", "dnet", "dtrees2", "dtw", "estimators4nfi",
111+
"flr", "fpt", "fxregime", "gamboostlss", "genabel", "gsdesign",
112+
"gsif", "gsmoothr", "gwidgets", "hmmr", "htda", "ihelp", "ipeglim",
113+
"jmr", "laicpms", "metasem", "mmsa", "morgan-rtools", "mpdir",
114+
"msgl", "multivarseg", "nem", "neuroim", "oem", "omearalab",
115+
"open-tfdea", "optbiomarker", "patchwork", "pmoments", "polyploid",
116+
"ptauxpc", "rftestproject12", "r-survey", "seqinr", "sprint",
117+
"teatime", "toxcast", "trajectory-sim", "treevo", "tuner", "waveslim",
118+
"wavetiling", "hyperspec")

action.yml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: 'Mirroring R-Forge to GitHub'
2+
description: 'Mirror R-Forge repos to GitHub'
3+
inputs:
4+
full:
5+
description: Perform a full mirror of all repos
6+
default: false
7+
cleanup:
8+
description: Delete dead r-forge repos
9+
default: false
10+
runs:
11+
using: 'docker'
12+
image: 'docker://runiverse/rforge-mirror'
13+
args:
14+
- ${{ inputs.full }}
15+
- ${{ inputs.cleanup }}

0 commit comments

Comments
 (0)