mirror of
https://github.com/denoland/deno.git
synced 2024-12-18 13:22:55 -05:00
fix(outdated): ensure "Latest" version is greater than "Update" version (#27390)
Fixes #27038. Previously, for NPM packages the latest version was the version with the "latest" tag. For JSR packages, the latest version was the greatest version that matched a `*` version requirement. Unfortunately, that doesn't work well with pre-release versions. This PR changes it so that the latest version is always > the currently requested version. For NPM: if "latest" tag > current then "latest" tag; otherwise the greatest version that is >= current For JSR: greatest version >= current This is the most reasonable behavior I could come up with. For example, ``` versions: 2.0.0-beta.2 2.0.0-beta.1 1.0.0 => "latest" tag with a version req `^2.0.0-beta.1` previously: "Update" column => 2.0.0-beta.2 "Latest" column => 1.0.0 now: "Update" column => 2.0.0-beta.2 "Latest" column => 2.0.0-beta.2 ```
This commit is contained in:
parent
2820ba1e22
commit
9d7174e434
22 changed files with 198 additions and 26 deletions
|
@ -3,7 +3,6 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::atomic::AtomicBool;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use deno_ast::ModuleSpecifier;
|
use deno_ast::ModuleSpecifier;
|
||||||
|
@ -28,6 +27,7 @@ use deno_semver::npm::NpmPackageReqReference;
|
||||||
use deno_semver::package::PackageNv;
|
use deno_semver::package::PackageNv;
|
||||||
use deno_semver::package::PackageReq;
|
use deno_semver::package::PackageReq;
|
||||||
use deno_semver::package::PackageReqReference;
|
use deno_semver::package::PackageReqReference;
|
||||||
|
use deno_semver::Version;
|
||||||
use deno_semver::VersionReq;
|
use deno_semver::VersionReq;
|
||||||
use import_map::ImportMap;
|
use import_map::ImportMap;
|
||||||
use import_map::ImportMapWithDiagnostics;
|
use import_map::ImportMapWithDiagnostics;
|
||||||
|
@ -42,6 +42,7 @@ use crate::jsr::JsrFetchResolver;
|
||||||
use crate::module_loader::ModuleLoadPreparer;
|
use crate::module_loader::ModuleLoadPreparer;
|
||||||
use crate::npm::CliNpmResolver;
|
use crate::npm::CliNpmResolver;
|
||||||
use crate::npm::NpmFetchResolver;
|
use crate::npm::NpmFetchResolver;
|
||||||
|
use crate::util::sync::AtomicFlag;
|
||||||
|
|
||||||
use super::ConfigUpdater;
|
use super::ConfigUpdater;
|
||||||
|
|
||||||
|
@ -447,7 +448,7 @@ pub struct DepManager {
|
||||||
|
|
||||||
pending_changes: Vec<Change>,
|
pending_changes: Vec<Change>,
|
||||||
|
|
||||||
dependencies_resolved: AtomicBool,
|
dependencies_resolved: AtomicFlag,
|
||||||
module_load_preparer: Arc<ModuleLoadPreparer>,
|
module_load_preparer: Arc<ModuleLoadPreparer>,
|
||||||
// TODO(nathanwhit): probably shouldn't be pub
|
// TODO(nathanwhit): probably shouldn't be pub
|
||||||
pub(crate) jsr_fetch_resolver: Arc<JsrFetchResolver>,
|
pub(crate) jsr_fetch_resolver: Arc<JsrFetchResolver>,
|
||||||
|
@ -489,7 +490,7 @@ impl DepManager {
|
||||||
resolved_versions: Vec::new(),
|
resolved_versions: Vec::new(),
|
||||||
latest_versions: Vec::new(),
|
latest_versions: Vec::new(),
|
||||||
jsr_fetch_resolver,
|
jsr_fetch_resolver,
|
||||||
dependencies_resolved: AtomicBool::new(false),
|
dependencies_resolved: AtomicFlag::lowered(),
|
||||||
module_load_preparer,
|
module_load_preparer,
|
||||||
npm_fetch_resolver,
|
npm_fetch_resolver,
|
||||||
npm_resolver,
|
npm_resolver,
|
||||||
|
@ -530,10 +531,7 @@ impl DepManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_dependency_resolution(&self) -> Result<(), AnyError> {
|
async fn run_dependency_resolution(&self) -> Result<(), AnyError> {
|
||||||
if self
|
if self.dependencies_resolved.is_raised() {
|
||||||
.dependencies_resolved
|
|
||||||
.load(std::sync::atomic::Ordering::Relaxed)
|
|
||||||
{
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,9 +554,7 @@ impl DepManager {
|
||||||
}
|
}
|
||||||
DepKind::Jsr => graph.packages.mappings().contains_key(&dep.req),
|
DepKind::Jsr => graph.packages.mappings().contains_key(&dep.req),
|
||||||
}) {
|
}) {
|
||||||
self
|
self.dependencies_resolved.raise();
|
||||||
.dependencies_resolved
|
|
||||||
.store(true, std::sync::atomic::Ordering::Relaxed);
|
|
||||||
graph_permit.commit();
|
graph_permit.commit();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -613,6 +609,7 @@ impl DepManager {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
self.dependencies_resolved.raise();
|
||||||
graph_permit.commit();
|
graph_permit.commit();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -655,10 +652,6 @@ impl DepManager {
|
||||||
if self.latest_versions.len() == self.deps.len() {
|
if self.latest_versions.len() == self.deps.len() {
|
||||||
return Ok(self.latest_versions.clone());
|
return Ok(self.latest_versions.clone());
|
||||||
}
|
}
|
||||||
let latest_tag_req = deno_semver::VersionReq::from_raw_text_and_inner(
|
|
||||||
"latest".into(),
|
|
||||||
deno_semver::RangeSetOrTag::Tag("latest".into()),
|
|
||||||
);
|
|
||||||
let mut latest_versions = Vec::with_capacity(self.deps.len());
|
let mut latest_versions = Vec::with_capacity(self.deps.len());
|
||||||
|
|
||||||
let npm_sema = Semaphore::new(32);
|
let npm_sema = Semaphore::new(32);
|
||||||
|
@ -670,14 +663,25 @@ impl DepManager {
|
||||||
DepKind::Npm => futs.push_back(
|
DepKind::Npm => futs.push_back(
|
||||||
async {
|
async {
|
||||||
let semver_req = &dep.req;
|
let semver_req = &dep.req;
|
||||||
let latest_req = PackageReq {
|
|
||||||
name: dep.req.name.clone(),
|
|
||||||
version_req: latest_tag_req.clone(),
|
|
||||||
};
|
|
||||||
let _permit = npm_sema.acquire().await;
|
let _permit = npm_sema.acquire().await;
|
||||||
let semver_compatible =
|
let semver_compatible =
|
||||||
self.npm_fetch_resolver.req_to_nv(semver_req).await;
|
self.npm_fetch_resolver.req_to_nv(semver_req).await;
|
||||||
let latest = self.npm_fetch_resolver.req_to_nv(&latest_req).await;
|
let info =
|
||||||
|
self.npm_fetch_resolver.package_info(&semver_req.name).await;
|
||||||
|
let latest = info
|
||||||
|
.and_then(|info| {
|
||||||
|
let latest_tag = info.dist_tags.get("latest")?;
|
||||||
|
let lower_bound = &semver_compatible.as_ref()?.version;
|
||||||
|
if latest_tag > lower_bound {
|
||||||
|
Some(latest_tag.clone())
|
||||||
|
} else {
|
||||||
|
latest_version(Some(latest_tag), info.versions.keys())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|version| PackageNv {
|
||||||
|
name: semver_req.name.clone(),
|
||||||
|
version,
|
||||||
|
});
|
||||||
PackageLatestVersion {
|
PackageLatestVersion {
|
||||||
latest,
|
latest,
|
||||||
semver_compatible,
|
semver_compatible,
|
||||||
|
@ -688,14 +692,29 @@ impl DepManager {
|
||||||
DepKind::Jsr => futs.push_back(
|
DepKind::Jsr => futs.push_back(
|
||||||
async {
|
async {
|
||||||
let semver_req = &dep.req;
|
let semver_req = &dep.req;
|
||||||
let latest_req = PackageReq {
|
|
||||||
name: dep.req.name.clone(),
|
|
||||||
version_req: deno_semver::WILDCARD_VERSION_REQ.clone(),
|
|
||||||
};
|
|
||||||
let _permit = jsr_sema.acquire().await;
|
let _permit = jsr_sema.acquire().await;
|
||||||
let semver_compatible =
|
let semver_compatible =
|
||||||
self.jsr_fetch_resolver.req_to_nv(semver_req).await;
|
self.jsr_fetch_resolver.req_to_nv(semver_req).await;
|
||||||
let latest = self.jsr_fetch_resolver.req_to_nv(&latest_req).await;
|
let info =
|
||||||
|
self.jsr_fetch_resolver.package_info(&semver_req.name).await;
|
||||||
|
let latest = info
|
||||||
|
.and_then(|info| {
|
||||||
|
let lower_bound = &semver_compatible.as_ref()?.version;
|
||||||
|
latest_version(
|
||||||
|
Some(lower_bound),
|
||||||
|
info.versions.iter().filter_map(|(version, version_info)| {
|
||||||
|
if !version_info.yanked {
|
||||||
|
Some(version)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map(|version| PackageNv {
|
||||||
|
name: semver_req.name.clone(),
|
||||||
|
version,
|
||||||
|
});
|
||||||
PackageLatestVersion {
|
PackageLatestVersion {
|
||||||
latest,
|
latest,
|
||||||
semver_compatible,
|
semver_compatible,
|
||||||
|
@ -893,3 +912,18 @@ fn parse_req_reference(
|
||||||
DepKind::Jsr => JsrPackageReqReference::from_str(input)?.into_inner(),
|
DepKind::Jsr => JsrPackageReqReference::from_str(input)?.into_inner(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn latest_version<'a>(
|
||||||
|
start: Option<&Version>,
|
||||||
|
versions: impl IntoIterator<Item = &'a Version>,
|
||||||
|
) -> Option<Version> {
|
||||||
|
let mut best = start;
|
||||||
|
for version in versions {
|
||||||
|
match best {
|
||||||
|
Some(best_version) if version > best_version => best = Some(version),
|
||||||
|
None => best = Some(version),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
best.cloned()
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
export const foo = 1;
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"exports": {
|
||||||
|
".": "mod.ts"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export const foo = 1;
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"exports": {
|
||||||
|
".": "mod.ts"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"versions": {
|
||||||
|
"2.0.0-beta.1": {},
|
||||||
|
"2.0.0-beta.2": {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export const foo = 1;
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"exports": {}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export const foo = 1;
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"exports": {
|
||||||
|
".": "mod.ts"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export const foo = 1;
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"exports": {
|
||||||
|
".": "mod.ts"
|
||||||
|
}
|
||||||
|
}
|
7
tests/registry/jsr/@denotest/has-pre-release/meta.json
Normal file
7
tests/registry/jsr/@denotest/has-pre-release/meta.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"versions": {
|
||||||
|
"1.0.0": {},
|
||||||
|
"2.0.0-beta.1": {},
|
||||||
|
"2.0.0-beta.2": {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "@denotest/has-pre-release",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"publishConfig": {
|
||||||
|
"tag": "latest"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "@denotest/has-pre-release",
|
||||||
|
"version": "2.0.0-beta.1"
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "@denotest/has-pre-release",
|
||||||
|
"version": "2.0.0-beta.2"
|
||||||
|
}
|
21
tests/specs/update/pre_release/__test__.jsonc
Normal file
21
tests/specs/update/pre_release/__test__.jsonc
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"args": "i",
|
||||||
|
"output": "[WILDCARD]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"args": "outdated",
|
||||||
|
"output": "outdated.out"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"args": "outdated --compatible",
|
||||||
|
"output": "outdated.out"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"args": "outdated --update --latest",
|
||||||
|
"output": "update.out"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
7
tests/specs/update/pre_release/deno.json
Normal file
7
tests/specs/update/pre_release/deno.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"@denotest/npm-has-pre-release": "npm:@denotest/has-pre-release@^2.0.0-beta.1",
|
||||||
|
"@denotest/jsr-has-pre-release": "jsr:@denotest/has-pre-release@^2.0.0-beta.1",
|
||||||
|
"@denotest/has-only-pre-release": "jsr:@denotest/has-only-pre-release@^2.0.0-beta.1"
|
||||||
|
}
|
||||||
|
}
|
28
tests/specs/update/pre_release/deno.lock
Normal file
28
tests/specs/update/pre_release/deno.lock
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"version": "4",
|
||||||
|
"specifiers": {
|
||||||
|
"jsr:@denotest/has-only-pre-release@^2.0.0-beta.1": "2.0.0-beta.1",
|
||||||
|
"jsr:@denotest/has-pre-release@^2.0.0-beta.1": "2.0.0-beta.1",
|
||||||
|
"npm:@denotest/has-pre-release@^2.0.0-beta.1": "2.0.0-beta.1"
|
||||||
|
},
|
||||||
|
"jsr": {
|
||||||
|
"@denotest/has-only-pre-release@2.0.0-beta.1": {
|
||||||
|
"integrity": "43fd680ea94bb5db5fe1a2d86101c47d0e2cc77323b881755cea9a0372e49537"
|
||||||
|
},
|
||||||
|
"@denotest/has-pre-release@2.0.0-beta.1": {
|
||||||
|
"integrity": "43fd680ea94bb5db5fe1a2d86101c47d0e2cc77323b881755cea9a0372e49537"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"npm": {
|
||||||
|
"@denotest/has-pre-release@2.0.0-beta.1": {
|
||||||
|
"integrity": "sha512-K1fHe1L2EUSLgijtzzALNpkkIO0SrX3z+IXvVjjOIE8HKd4T7lkpzDdoUp+WllwS3KXmuJh+9vIfY5lFp38pew=="
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@denotest/has-only-pre-release@^2.0.0-beta.1",
|
||||||
|
"jsr:@denotest/has-pre-release@^2.0.0-beta.1",
|
||||||
|
"npm:@denotest/has-pre-release@^2.0.0-beta.1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
11
tests/specs/update/pre_release/outdated.out
Normal file
11
tests/specs/update/pre_release/outdated.out
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
┌────────────────────────────────────┬──────────────┬──────────────┬──────────────┐
|
||||||
|
│ Package │ Current │ Update │ Latest │
|
||||||
|
├────────────────────────────────────┼──────────────┼──────────────┼──────────────┤
|
||||||
|
│ jsr:@denotest/has-only-pre-release │ 2.0.0-beta.1 │ 2.0.0-beta.2 │ 2.0.0-beta.2 │
|
||||||
|
├────────────────────────────────────┼──────────────┼──────────────┼──────────────┤
|
||||||
|
│ jsr:@denotest/has-pre-release │ 2.0.0-beta.1 │ 2.0.0-beta.2 │ 2.0.0-beta.2 │
|
||||||
|
├────────────────────────────────────┼──────────────┼──────────────┼──────────────┤
|
||||||
|
│ npm:@denotest/has-pre-release │ 2.0.0-beta.1 │ 2.0.0-beta.2 │ 2.0.0-beta.2 │
|
||||||
|
└────────────────────────────────────┴──────────────┴──────────────┴──────────────┘
|
||||||
|
|
||||||
|
[WILDCARD]
|
5
tests/specs/update/pre_release/update.out
Normal file
5
tests/specs/update/pre_release/update.out
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[WILDCARD]
|
||||||
|
Updated 3 dependencies:
|
||||||
|
- jsr:@denotest/has-only-pre-release 2.0.0-beta.1 -> 2.0.0-beta.2
|
||||||
|
- jsr:@denotest/has-pre-release 2.0.0-beta.1 -> 2.0.0-beta.2
|
||||||
|
- npm:@denotest/has-pre-release 2.0.0-beta.1 -> 2.0.0-beta.2
|
|
@ -267,6 +267,7 @@ fn get_npm_package(
|
||||||
let mut tarballs = HashMap::new();
|
let mut tarballs = HashMap::new();
|
||||||
let mut versions = serde_json::Map::new();
|
let mut versions = serde_json::Map::new();
|
||||||
let mut latest_version = semver::Version::parse("0.0.0").unwrap();
|
let mut latest_version = semver::Version::parse("0.0.0").unwrap();
|
||||||
|
let mut dist_tags = serde_json::Map::new();
|
||||||
for entry in fs::read_dir(&package_folder)? {
|
for entry in fs::read_dir(&package_folder)? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let file_type = entry.file_type()?;
|
let file_type = entry.file_type()?;
|
||||||
|
@ -345,6 +346,14 @@ fn get_npm_package(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(publish_config) = version_info.get("publishConfig") {
|
||||||
|
if let Some(tag) = publish_config.get("tag") {
|
||||||
|
if let Some(tag) = tag.as_str() {
|
||||||
|
dist_tags.insert(tag.to_string(), version.clone().into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
versions.insert(version.clone(), version_info.into());
|
versions.insert(version.clone(), version_info.into());
|
||||||
let version = semver::Version::parse(&version)?;
|
let version = semver::Version::parse(&version)?;
|
||||||
if version.cmp(&latest_version).is_gt() {
|
if version.cmp(&latest_version).is_gt() {
|
||||||
|
@ -352,8 +361,9 @@ fn get_npm_package(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dist_tags = serde_json::Map::new();
|
if !dist_tags.contains_key("latest") {
|
||||||
dist_tags.insert("latest".to_string(), latest_version.to_string().into());
|
dist_tags.insert("latest".to_string(), latest_version.to_string().into());
|
||||||
|
}
|
||||||
|
|
||||||
// create the registry file for this package
|
// create the registry file for this package
|
||||||
let mut registry_file = serde_json::Map::new();
|
let mut registry_file = serde_json::Map::new();
|
||||||
|
|
Loading…
Reference in a new issue