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::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
|
@ -28,6 +27,7 @@ use deno_semver::npm::NpmPackageReqReference;
|
|||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use deno_semver::package::PackageReqReference;
|
||||
use deno_semver::Version;
|
||||
use deno_semver::VersionReq;
|
||||
use import_map::ImportMap;
|
||||
use import_map::ImportMapWithDiagnostics;
|
||||
|
@ -42,6 +42,7 @@ use crate::jsr::JsrFetchResolver;
|
|||
use crate::module_loader::ModuleLoadPreparer;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::NpmFetchResolver;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
|
||||
use super::ConfigUpdater;
|
||||
|
||||
|
@ -447,7 +448,7 @@ pub struct DepManager {
|
|||
|
||||
pending_changes: Vec<Change>,
|
||||
|
||||
dependencies_resolved: AtomicBool,
|
||||
dependencies_resolved: AtomicFlag,
|
||||
module_load_preparer: Arc<ModuleLoadPreparer>,
|
||||
// TODO(nathanwhit): probably shouldn't be pub
|
||||
pub(crate) jsr_fetch_resolver: Arc<JsrFetchResolver>,
|
||||
|
@ -489,7 +490,7 @@ impl DepManager {
|
|||
resolved_versions: Vec::new(),
|
||||
latest_versions: Vec::new(),
|
||||
jsr_fetch_resolver,
|
||||
dependencies_resolved: AtomicBool::new(false),
|
||||
dependencies_resolved: AtomicFlag::lowered(),
|
||||
module_load_preparer,
|
||||
npm_fetch_resolver,
|
||||
npm_resolver,
|
||||
|
@ -530,10 +531,7 @@ impl DepManager {
|
|||
}
|
||||
|
||||
async fn run_dependency_resolution(&self) -> Result<(), AnyError> {
|
||||
if self
|
||||
.dependencies_resolved
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
{
|
||||
if self.dependencies_resolved.is_raised() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -556,9 +554,7 @@ impl DepManager {
|
|||
}
|
||||
DepKind::Jsr => graph.packages.mappings().contains_key(&dep.req),
|
||||
}) {
|
||||
self
|
||||
.dependencies_resolved
|
||||
.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
self.dependencies_resolved.raise();
|
||||
graph_permit.commit();
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -613,6 +609,7 @@ impl DepManager {
|
|||
)
|
||||
.await?;
|
||||
|
||||
self.dependencies_resolved.raise();
|
||||
graph_permit.commit();
|
||||
|
||||
Ok(())
|
||||
|
@ -655,10 +652,6 @@ impl DepManager {
|
|||
if self.latest_versions.len() == self.deps.len() {
|
||||
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 npm_sema = Semaphore::new(32);
|
||||
|
@ -670,14 +663,25 @@ impl DepManager {
|
|||
DepKind::Npm => futs.push_back(
|
||||
async {
|
||||
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 semver_compatible =
|
||||
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 {
|
||||
latest,
|
||||
semver_compatible,
|
||||
|
@ -688,14 +692,29 @@ impl DepManager {
|
|||
DepKind::Jsr => futs.push_back(
|
||||
async {
|
||||
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 semver_compatible =
|
||||
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 {
|
||||
latest,
|
||||
semver_compatible,
|
||||
|
@ -893,3 +912,18 @@ fn parse_req_reference(
|
|||
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 versions = serde_json::Map::new();
|
||||
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)? {
|
||||
let entry = entry?;
|
||||
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());
|
||||
let version = semver::Version::parse(&version)?;
|
||||
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());
|
||||
}
|
||||
|
||||
// create the registry file for this package
|
||||
let mut registry_file = serde_json::Map::new();
|
||||
|
|
Loading…
Reference in a new issue