diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index afcccc2a20..ed67f710d3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -86,3 +86,9 @@ jobs: julia --project=docs --color=yes docs/make.jl pdf env: DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} + - name: Upload documentation artifacts + uses: actions/upload-artifact@v4 + if: always() + with: + name: pkg-docs + path: docs/build/ diff --git a/docs/src/index.md b/docs/src/index.md index e51ffcec71..da1aa13c4f 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -4,6 +4,23 @@ Welcome to the documentation for Pkg, [Julia](https://julialang.org)'s package m The documentation covers many things, for example managing package installations, developing packages, working with package registries and more. +```@eval +import Markdown +# For Pkg, we need to determine the appropriate Julia version for the PDF +# Since Pkg docs are versioned by Julia version, we'll use a similar approach to Julia docs +julia_patch = if VERSION.prerelease == () + "v$(VERSION.major).$(VERSION.minor).$(VERSION.patch)" +elseif VERSION.prerelease[1] == "DEV" + "dev" +end +file = "Pkg.jl.pdf" +url = "https://raw.githubusercontent.com/JuliaLang/Pkg.jl/gh-pages-pdf/$(julia_patch)/$(file)" +Markdown.parse(""" +!!! note + The documentation is also available in PDF format: [$file]($url). +""") +``` + Throughout the manual the REPL interface to Pkg, the Pkg REPL mode, is used in the examples. There is also a functional API, which is preferred when not working interactively. This API is documented in the [API Reference](@ref) section. diff --git a/src/API.jl b/src/API.jl index f8c720dffa..6a25efa64b 100644 --- a/src/API.jl +++ b/src/API.jl @@ -1414,12 +1414,22 @@ function compat(ctx::Context, pkg::String, compat_str::Union{Nothing,String}; io io = something(io, ctx.io) pkg = pkg == "Julia" ? "julia" : pkg isnothing(compat_str) || (compat_str = string(strip(compat_str, '"'))) + existing_compat = Operations.get_compat_str(ctx.env.project, pkg) + # Double check before deleting a compat entry issue/3567 + if isinteractive() && (isnothing(compat_str) || isempty(compat_str)) + if !isnothing(existing_compat) + ans = Base.prompt(stdin, ctx.io, "No compat string was given. Delete existing compat entry `$pkg = $(repr(existing_compat))`? [y]/n", default = "y") + if lowercase(ans) !== "y" + return + end + end + end if haskey(ctx.env.project.deps, pkg) || pkg == "julia" success = Operations.set_compat(ctx.env.project, pkg, isnothing(compat_str) ? nothing : isempty(compat_str) ? nothing : compat_str) success === false && pkgerror("invalid compat version specifier \"$(compat_str)\"") write_env(ctx.env) if isnothing(compat_str) || isempty(compat_str) - printpkgstyle(io, :Compat, "entry removed for $(pkg)") + printpkgstyle(io, :Compat, "entry removed:\n $pkg = $(repr(existing_compat))") else printpkgstyle(io, :Compat, "entry set:\n $(pkg) = $(repr(compat_str))") end diff --git a/src/Artifacts.jl b/src/Artifacts.jl index f258b2a062..bd96aacaba 100644 --- a/src/Artifacts.jl +++ b/src/Artifacts.jl @@ -218,7 +218,7 @@ function bind_artifact!(artifacts_toml::String, name::String, hash::SHA1; meta = artifact_dict[name] if !isa(meta, Vector) error("Mapping for '$name' within $(artifacts_toml) already exists!") - elseif any(isequal(platform), unpack_platform(x, name, artifacts_toml) for x in meta) + elseif any(p -> platforms_match(platform, p), unpack_platform(x, name, artifacts_toml) for x in meta) error("Mapping for '$name'/$(triplet(platform)) within $(artifacts_toml) already exists!") end end diff --git a/src/GitTools.jl b/src/GitTools.jl index 03cc08adff..562dada8ef 100644 --- a/src/GitTools.jl +++ b/src/GitTools.jl @@ -95,7 +95,7 @@ function clone(io::IO, url, source_path; header=nothing, credentials=nothing, kw @assert !isdir(source_path) || isempty(readdir(source_path)) url = normalize_url(url) printpkgstyle(io, :Cloning, header === nothing ? "git-repo `$url`" : header) - bar = MiniProgressBar(header = "Fetching:", color = Base.info_color()) + bar = MiniProgressBar(header = "Cloning:", color = Base.info_color()) fancyprint = can_fancyprint(io) fancyprint && start_progress(io, bar) if credentials === nothing @@ -122,7 +122,7 @@ function clone(io::IO, url, source_path; header=nothing, credentials=nothing, kw LibGit2.Callbacks() end mkpath(source_path) - return LibGit2.clone(url, source_path; callbacks=callbacks, credentials=credentials, kwargs...) + return LibGit2.clone(url, source_path; callbacks, credentials, kwargs...) end catch err rm(source_path; force=true, recursive=true) @@ -179,7 +179,7 @@ function fetch(io::IO, repo::LibGit2.GitRepo, remoteurl=nothing; header=nothing, end end else - return LibGit2.fetch(repo; remoteurl=remoteurl, callbacks=callbacks, refspecs=refspecs, kwargs...) + return LibGit2.fetch(repo; remoteurl, callbacks, credentials, refspecs, kwargs...) end catch err err isa LibGit2.GitError || rethrow() diff --git a/src/Operations.jl b/src/Operations.jl index 0bb6b0ac4a..00b1469326 100644 --- a/src/Operations.jl +++ b/src/Operations.jl @@ -1298,7 +1298,24 @@ end function check_registered(registries::Vector{Registry.RegistryInstance}, pkgs::Vector{PackageSpec}) pkg = is_all_registered(registries, pkgs) if pkg isa PackageSpec - pkgerror("expected package $(err_rep(pkg)) to be registered") + msg = "expected package $(err_rep(pkg)) to be registered" + # check if the name exists in the registry with a different uuid + if pkg.name !== nothing + reg_uuid = Pair{String, Vector{UUID}}[] + for reg in registries + uuids = Registry.uuids_from_name(reg, pkg.name) + if !isempty(uuids) + push!(reg_uuid, reg.name => uuids) + end + end + if !isempty(reg_uuid) + msg *= "\n You may have provided the wrong UUID for package $(pkg.name).\n Found the following UUIDs for that name:" + for (reg, uuids) in reg_uuid + msg *= "\n - $(join(uuids, ", ")) from registry: $reg" + end + end + end + pkgerror(msg) end return nothing end diff --git a/src/REPLMode/argument_parsers.jl b/src/REPLMode/argument_parsers.jl index c0f284a4b0..12593e217b 100644 --- a/src/REPLMode/argument_parsers.jl +++ b/src/REPLMode/argument_parsers.jl @@ -57,7 +57,7 @@ let url = raw"((git|ssh|http(s)?)|(git@[\w\-\.]+))(:(//)?)([\w\.@\:/\-~]+)(\.git name_uuid = raw"[^@\#\s:]+\s*=\s*[^@\#\s:]+", # Match a `#BRANCH` branch or tag specifier. - branch = raw"\#\s*[^@\#\s]*", + branch = raw"\#\s*[^@^:\s]+", # Match an `@VERSION` version specifier. version = raw"@\s*[^@\#\s]*", @@ -94,19 +94,34 @@ function parse_package_args(args::Vector{PackageToken}; add_or_dev=false)::Vecto # check for and apply PackageSpec modifier (e.g. `#foo` or `@v1.0.2`) function apply_modifier!(pkg::PackageSpec, args::Vector{PackageToken}) (isempty(args) || args[1] isa PackageIdentifier) && return - modifier = popfirst!(args) - if modifier isa Subdir - pkg.subdir = modifier.dir - (isempty(args) || args[1] isa PackageIdentifier) && return + parsed_subdir = false + parsed_version = false + parsed_rev = false + while !isempty(args) modifier = popfirst!(args) - end - - if modifier isa VersionToken - pkg.version = modifier.version - elseif modifier isa Rev - pkg.rev = modifier.rev - else - pkgerror("Package name/uuid must precede subdir specifier `$args`.") + if modifier isa Subdir + if parsed_subdir + pkgerror("Multiple subdir specifiers `$args` found.") + end + pkg.subdir = modifier.dir + (isempty(args) || args[1] isa PackageIdentifier) && return + modifier = popfirst!(args) + parsed_subdir = true + elseif modifier isa VersionToken + if parsed_version + pkgerror("Multiple version specifiers `$args` found.") + end + pkg.version = modifier.version + parsed_version = true + elseif modifier isa Rev + if parsed_rev + pkgerror("Multiple revision specifiers `$args` found.") + end + pkg.rev = modifier.rev + parsed_rev = true + else + pkgerror("Package name/uuid must precede subdir specifier `$args`.") + end end end diff --git a/src/Types.jl b/src/Types.jl index fe51f4015b..cc9ff3061c 100644 --- a/src/Types.jl +++ b/src/Types.jl @@ -525,6 +525,16 @@ function Context!(ctx::Context; kwargs...) for (k, v) in kwargs setfield!(ctx, k, v) end + + # Highlight for logging purposes if julia_version is set to a different version than current VERSION + if haskey(kwargs, :julia_version) && ctx.julia_version !== nothing && ctx.julia_version != VERSION + Pkg.printpkgstyle( + ctx.io, :Context, + "Pkg is operating with julia_version set to `$(ctx.julia_version)`", + color = Base.warn_color() + ) + end + return ctx end diff --git a/src/utils.jl b/src/utils.jl index 16cfb647d7..c67968972f 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -66,7 +66,6 @@ set_readonly(::Nothing) = nothing # try to call realpath on as much as possible function safe_realpath(path) - isempty(path) && return path if ispath(path) try return realpath(path) @@ -75,6 +74,8 @@ function safe_realpath(path) end end a, b = splitdir(path) + # path cannot be reduced at the root or drive, avoid stack overflow + isempty(b) && return path return joinpath(safe_realpath(a), b) end diff --git a/test/artifacts.jl b/test/artifacts.jl index 905cef0daf..00f997c0f8 100644 --- a/test/artifacts.jl +++ b/test/artifacts.jl @@ -256,6 +256,12 @@ end @test ensure_artifact_installed("foo_txt", artifacts_toml; platform=linux64) == artifact_path(hash2) @test ensure_artifact_installed("foo_txt", artifacts_toml; platform=win32) == artifact_path(hash) + # Default HostPlatform() adds a compare_strategy key that doesn't get picked up from + # the Artifacts.toml + testhost = Platform("x86_64", "linux", Dict("libstdcxx_version" => "1.2.3")) + BinaryPlatforms.set_compare_strategy!(testhost, "libstdcxx_version", BinaryPlatforms.compare_version_cap) + @test_throws ErrorException bind_artifact!(artifacts_toml, "foo_txt", hash; download_info=download_info, platform=testhost) + # Next, check that we can get the download_info properly: meta = artifact_meta("foo_txt", artifacts_toml; platform=win32) @test meta["download"][1]["url"] == "http://google.com/hello_world" diff --git a/test/misc.jl b/test/misc.jl index e9b3d00ff6..3a386d161a 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -19,8 +19,9 @@ end end @testset "safe_realpath" begin + realpath(Sys.BINDIR) == Pkg.safe_realpath(Sys.BINDIR) # issue #3085 - for p in ("", "some-non-existing-path") + for p in ("", "some-non-existing-path", "some-non-existing-drive:") @test p == Pkg.safe_realpath(p) end end diff --git a/test/new.jl b/test/new.jl index a539760436..912d0c002f 100644 --- a/test/new.jl +++ b/test/new.jl @@ -345,6 +345,12 @@ end arg = args[1] @test arg.url == "https://github.com/JuliaLang/Pkg.jl" @test arg.rev == "aa/gitlab" + + api, args, opts = first(Pkg.pkg"add https://github.com/TimG1964/XLSX.jl#Bug-fixing-post-#289:subdir") + arg = args[1] + @test arg.url == "https://github.com/TimG1964/XLSX.jl" + @test arg.rev == "Bug-fixing-post-#289" + @test arg.subdir == "subdir" end end @@ -446,7 +452,7 @@ end isolate(loaded_depot=true) do; mktempdir() do tempdir package_path = copy_test_package(tempdir, "UnregisteredUUID") Pkg.activate(package_path) - @test_throws PkgError("expected package `Example [142fd7e7]` to be registered") Pkg.add("JSON") + @test_throws PkgError Pkg.add("JSON") end end # empty git repo (no commits) isolate(loaded_depot=true) do; mktempdir() do tempdir @@ -1402,7 +1408,7 @@ end isolate(loaded_depot=true) do; mktempdir() do tempdir package_path = copy_test_package(tempdir, "UnregisteredUUID") Pkg.activate(package_path) - @test_throws PkgError("expected package `Example [142fd7e7]` to be registered") Pkg.update() + @test_throws PkgError Pkg.update() end end end @@ -1581,7 +1587,7 @@ end isolate(loaded_depot=true) do; mktempdir() do tempdir package_path = copy_test_package(tempdir, "UnregisteredUUID") Pkg.activate(package_path) - @test_throws PkgError("expected package `Example [142fd7e7]` to be registered") Pkg.update() + @test_throws PkgError Pkg.update() end end # package does not exist in the manifest isolate(loaded_depot=true) do diff --git a/test/pkg.jl b/test/pkg.jl index 13e2a0ab96..b4fe5770c8 100644 --- a/test/pkg.jl +++ b/test/pkg.jl @@ -206,6 +206,18 @@ temp_pkg_dir() do project_path @testset "package with wrong UUID" begin @test_throws PkgError Pkg.add(PackageSpec(TEST_PKG.name, UUID(UInt128(1)))) + @testset "package with wrong UUID but correct name" begin + try + Pkg.add(PackageSpec(name="Example", uuid=UUID(UInt128(2)))) + catch e + @test e isa PkgError + errstr = sprint(showerror, e) + @test occursin("expected package `Example [00000000]` to be registered", errstr) + @test occursin("You may have provided the wrong UUID for package Example.", errstr) + @test occursin("Found the following UUIDs for that name:", errstr) + @test occursin("- 7876af07-990d-54b4-ab0e-23690620f79a from registry: General", errstr) + end + end # Missing uuid @test_throws PkgError Pkg.add(PackageSpec(uuid = uuid4())) end