Solidify logic for picking rspm vs mran

- MRAN doesn't seem to have R 4.1 specific snapshots, so let's
  default to RSPM for anything 4.1+.
- Otherwise, snapshot dates in 2022 will result in using rspm
pull/1104/head
YuviPanda 2022-01-04 18:30:39 +05:30
rodzic 054b602b13
commit fd6314d07d
4 zmienionych plików z 80 dodań i 36 usunięć

Wyświetl plik

@ -185,28 +185,50 @@ class RBuildPack(PythonBuildPack):
return super().get_packages().union(packages) return super().get_packages().union(packages)
def get_cran_mirror_url(self, snapshot_date): def get_rspm_snapshot_url(self, snapshot_date, max_days_prior=7):
# Call the API to find out if we have a snapshot available for the given date. for i in range(max_days_prior):
# If so, use the URL for that snapshot. If not, fall back to MRAN. snapshots = requests.post(
snapshots = requests.post( "https://packagemanager.rstudio.com/__api__/url",
"https://packagemanager.rstudio.com/__api__/url", # Ask for midnight UTC snapshot
# Ask for midnight UTC snapshot json={
json={ "repo": "all",
"repo": "all", "snapshot": (snapshot_date - datetime.timedelta(days=i)).strftime("%Y-%m-%dT00:00:00Z"),
"snapshot": snapshot_date.strftime("%Y-%m-%dT00:00:00Z"), },
}, ).json()
).json() # Construct a snapshot URL that will give us binary packages for Ubuntu Bionic (18.04)
# Construct a snapshot URL that will give us binary packages for Ubuntu Bionic (18.04) if "upsi" in snapshots:
if "upsi" in snapshots: return (
return ( "https://packagemanager.rstudio.com/all/__linux__/bionic/"
"https://packagemanager.rstudio.com/all/__linux__/bionic/" + snapshots["upsi"]
+ snapshots["upsi"] )
) raise ValueError('No snapshot found for {} or {} days prior in packagemanager.rstudio.com'.format(
snapshot_date.strftime("%Y-%m-%d"), max_days_prior
))
def get_mran_snapshot_url(self, snapshot_date, max_days_prior=7):
for i in range(max_days_prior):
try_date = snapshot_date - datetime.timedelta(days=i)
# Fall back to MRAN if packagemanager.rstudio.com doesn't have it
url = "https://mran.microsoft.com/snapshot/{}".format(
try_date.isoformat()
)
r = requests.head(url)
if r.ok:
return url
else:
raise ValueError('No snapshot found for {} or {} days prior in mran.microsoft.com'.format(
snapshot_date.strftime("%Y-%m-%d"), max_days_prior
))
def get_cran_mirror_url(self, snapshot_date):
# Date after which we will use rspm + binary packages instead of MRAN + source packages
rspm_cutoff_date = datetime.date(2022, 1, 1)
if snapshot_date >= rspm_cutoff_date or self.r_version >= V('4.1'):
return self.get_rspm_snapshot_url(snapshot_date)
else:
return self.get_mran_snapshot_url(snapshot_date)
# Fall back to MRAN if packagemanager.rstudio.com doesn't have it
return "https://mran.microsoft.com/snapshot/{}".format(
snapshot_date.isoformat()
)
def get_devtools_snapshot_date(self): def get_devtools_snapshot_date(self):
""" """

Wyświetl plik

@ -1 +1 @@
r-4.1-2021-07-07 r-4.1-2021-10-22

Wyświetl plik

@ -1,8 +1,12 @@
#!/usr/bin/env Rscript #!/usr/bin/env Rscript
library('ggplot2') library('ggplot2')
print(version)
# Fail if version is not 4.1 # Fail if version is not 4.1
if (!(version$major == "4" && as.double(version$minor) >= 1 && as.double(version$minor) < 2)) { if (!(version$major == "4" && as.double(version$minor) >= 1 && as.double(version$minor) < 2)) {
quit("yes", 1) quit("yes", 1)
} }
# R 4.1 always uses rspm snapshots
if (!(startsWith(options()$repos["CRAN"], "https://packagemanager.rstudio.com"))) {
quit("yes", 1)
}

Wyświetl plik

@ -64,28 +64,46 @@ def test_mran_date(tmpdir, runtime, expected):
assert r.checkpoint_date == date(*expected) assert r.checkpoint_date == date(*expected)
@pytest.mark.parametrize("expected", ["2019-12-29", "2019-12-26"]) def test_snapshot_rspm_date():
def test_mran_latestdate(tmpdir, expected): test_dates = {
# Even though there is no snapshot specified in the interface at https://packagemanager.rstudio.com/client/#/repos/1/overview
# For 2021 Oct 22, the API still returns a valid URL that one can install
# packages from - probably some server side magic that repeats our client side logic.
# No snapshot for this date from
date(2021, 10, 22): date(2021, 10, 22),
# Snapshot exists for this date
date(2022, 1, 1): date(2022, 1, 1),
}
r = buildpacks.RBuildPack()
for requested, expected in test_dates.items():
snapshot_url = r.get_rspm_snapshot_url(requested)
print(snapshot_url)
assert snapshot_url.startswith(
"https://packagemanager.rstudio.com/all/__linux__/bionic/" + expected.strftime("%Y-%m-%d")
)
with pytest.raises(ValueError):
r.get_rspm_snapshot_url(date(1691, 9, 5))
@pytest.mark.parametrize("expected", [date(2019, 12, 29), date(2019, 12, 26)])
@pytest.mark.parametrize("requested", [date(2019, 12, 31)])
def test_snapshot_mran_date(requested, expected):
def mock_request_head(url): def mock_request_head(url):
r = Response() r = Response()
if url == "https://mran.microsoft.com/snapshot/" + expected: if url == "https://mran.microsoft.com/snapshot/" + expected.isoformat():
r.status_code = 200 r.status_code = 200
else: else:
r.status_code = 404 r.status_code = 404
r.reason = "Mock MRAN no snapshot" r.reason = "Mock MRAN no snapshot"
return r return r
tmpdir.chdir()
with open("DESCRIPTION", "w") as f:
f.write("")
with patch("requests.head", side_effect=mock_request_head): with patch("requests.head", side_effect=mock_request_head):
with patch("datetime.date") as mockdate: r = buildpacks.RBuildPack()
mockdate.today.return_value = date(2019, 12, 31) assert r.get_mran_snapshot_url(
r = buildpacks.RBuildPack() requested
r.detect() ) == "https://mran.microsoft.com/snapshot/{}".format(expected.isoformat())
assert r.checkpoint_date.isoformat() == expected
def test_install_from_base(tmpdir): def test_install_from_base(tmpdir):