• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37456

pending completion
#37456

push

local

web-flow
Cover binomial cases for large K (BigInt) (#48073)

* Cover binomial cases for large K (BigInt)

Co-authored-by: Sebastian Stock <42280794+sostock@users.noreply.github.com>

9 of 9 new or added lines in 1 file covered. (100.0%)

70916 of 81824 relevant lines covered (86.67%)

35598279.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/stdlib/LibGit2/src/gitcredential.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
const GIT_CRED_ATTRIBUTES = ("protocol", "host", "path", "username", "password", "url")
4

5
"""
6
    GitCredential
7

8
Git credential information used in communication with git credential helpers. The field are
9
named using the [input/output key specification](https://git-scm.com/docs/git-credential#IOFMT).
10
"""
11
mutable struct GitCredential
12
    protocol::Union{String, Nothing}
13
    host::Union{String, Nothing}
14
    path::Union{String, Nothing}
15
    username::Union{String, Nothing}
16
    password::Union{Base.SecretBuffer, Nothing}
17
    use_http_path::Bool
18

19
    function GitCredential(
×
20
            protocol::Union{AbstractString, Nothing}=nothing,
21
            host::Union{AbstractString, Nothing}=nothing,
22
            path::Union{AbstractString, Nothing}=nothing,
23
            username::Union{AbstractString, Nothing}=nothing,
24
            password::Union{AbstractString, Nothing}=nothing)
25
        new(protocol, host, path, username, password, true)
×
26
    end
27
end
28

29
function GitCredential(cfg::GitConfig, url::AbstractString)
×
30
    fill!(cfg, parse(GitCredential, url))
×
31
end
32

33
function GitCredential(user_pass_cred::UserPasswordCredential, url::AbstractString)
×
34
    cred = parse(GitCredential, url)
×
35
    cred.username = user_pass_cred.user
×
36
    cred.password = deepcopy(user_pass_cred.pass)
×
37
    return cred
×
38
end
39

40
Base.:(==)(c1::GitCredential, c2::GitCredential) = (c1.protocol, c1.host, c1.path, c1.username, c1.password, c1.use_http_path) ==
×
41
                                                   (c2.protocol, c2.host, c2.path, c2.username, c2.password, c2.use_http_path)
42
Base.hash(cred::GitCredential, h::UInt) = hash(GitCredential, hash((cred.protocol, cred.host, cred.path, cred.username, cred.password, cred.use_http_path), h))
×
43

44
function Base.shred!(cred::GitCredential)
×
45
    cred.protocol = nothing
×
46
    cred.host = nothing
×
47
    cred.path = nothing
×
48
    cred.username = nothing
×
49
    pwd = cred.password
×
50
    pwd !== nothing && Base.shred!(pwd)
×
51
    cred.password = nothing
×
52
    return cred
×
53
end
54

55

56
"""
57
    ismatch(url, git_cred) -> Bool
58

59
Checks if the `git_cred` is valid for the given `url`.
60
"""
61
function ismatch(url::AbstractString, git_cred::GitCredential)
×
62
    isempty(url) && return true
×
63

64
    m = match(URL_REGEX, url)
×
65
    m === nothing && error("Unable to parse URL")
×
66

67
    # Note: missing URL groups match anything
68
    (m[:scheme] === nothing ? true : m[:scheme] == git_cred.protocol) &&
×
69
    (m[:host] === nothing ? true : m[:host] == git_cred.host) &&
70
    (m[:path] === nothing ? true : m[:path] == git_cred.path) &&
71
    (m[:user] === nothing ? true : m[:user] == git_cred.username)
72
end
73

74
function isfilled(cred::GitCredential)
×
75
    cred.username !== nothing && cred.password !== nothing
×
76
end
77

78
function Base.parse(::Type{GitCredential}, url::AbstractString)
×
79
    m = match(URL_REGEX, url)
×
80
    m === nothing && error("Unable to parse URL")
×
81
    return GitCredential(
×
82
        m[:scheme],
83
        m[:host],
84
        m[:path],
85
        m[:user],
86
        m[:password],
87
    )
88
end
89

90
function Base.copy!(a::GitCredential, b::GitCredential)
×
91
    Base.shred!(a)
×
92
    a.protocol = b.protocol
×
93
    a.host = b.host
×
94
    a.path = b.path
×
95
    a.username = b.username
×
96
    a.password = b.password === nothing ? nothing : copy(b.password)
×
97
    return a
×
98
end
99

100
function Base.write(io::IO, cred::GitCredential)
×
101
    cred.protocol !== nothing && write(io, "protocol=", cred.protocol, '\n')
×
102
    cred.host !== nothing && write(io, "host=", cred.host, '\n')
×
103
    cred.path !== nothing && cred.use_http_path && write(io, "path=", cred.path, '\n')
×
104
    cred.username !== nothing && write(io, "username=", cred.username, '\n')
×
105
    cred.password !== nothing && write(io, "password=", cred.password, '\n')
×
106
    nothing
×
107
end
108

109
function Base.read!(io::IO, cred::GitCredential)
×
110
    # https://git-scm.com/docs/git-credential#IOFMT
111
    while !(eof(io)::Bool)
×
112
        key::AbstractString = readuntil(io, '=')
×
113
        if key == "password"
×
114
            value = Base.SecretBuffer()
×
115
            while !(eof(io)::Bool) && (c = read(io, UInt8)) != UInt8('\n')
×
116
                write(value, c)
×
117
            end
×
118
            seekstart(value)
×
119
        else
120
            value = readuntil(io, '\n')
×
121
        end
122

123
        if key == "url"
×
124
            # Any components which are missing from the URL will be set to empty
125
            # https://git-scm.com/docs/git-credential#git-credential-codeurlcode
126
            Base.shred!(parse(GitCredential, value::AbstractString)) do urlcred
×
127
                copy!(cred, urlcred)
×
128
            end
129
        elseif key in GIT_CRED_ATTRIBUTES
×
130
            field = getproperty(cred, Symbol(key))
×
131
            field !== nothing && Symbol(key) === :password && Base.shred!(field)
×
132
            setproperty!(cred, Symbol(key), value)
×
133
        elseif !all(isspace, key)
×
134
            @warn "Unknown git credential attribute found: $(repr(key))"
×
135
        end
136
    end
×
137

138
    return cred
×
139
end
140

141
function fill!(cfg::GitConfig, cred::GitCredential)
×
142
    cred.use_http_path = use_http_path(cfg, cred)
×
143

144
    # When the username is missing default to using the username set in the configuration
145
    if cred.username === nothing
×
146
        cred.username = default_username(cfg, cred)
×
147
    end
148

149
    for helper in credential_helpers(cfg, cred)
×
150
        fill!(helper, cred)
×
151

152
        # "Once Git has acquired both a username and a password, no more helpers will be
153
        # tried." – https://git-scm.com/docs/gitcredentials#gitcredentials-helper
154
        !isfilled(cred) && break
×
155
    end
×
156

157
    return cred
×
158
end
159

160
struct GitCredentialHelper
161
    cmd::Cmd
×
162
end
163

164
function Base.parse(::Type{GitCredentialHelper}, helper::AbstractString)
×
165
    # The helper string can take on different behaviors depending on the value:
166
    # - "Code after `!` evaluated in shell" – https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage
167
    # - "If the helper name is not an absolute path, then the string `git credential-` is
168
    #   prepended." – https://git-scm.com/docs/gitcredentials#gitcredentials-helper
169
    if startswith(helper, '!')
×
170
        cmd_str = helper[2:end]
×
171
    elseif isabspath(first(Base.shell_split(helper)))
×
172
        cmd_str = helper
×
173
    else
174
        cmd_str = "git credential-$helper"
×
175
    end
176

177
    GitCredentialHelper(`$(Base.shell_split(cmd_str))`)
×
178
end
179

180
function Base.:(==)(a::GitCredentialHelper, b::GitCredentialHelper)
×
181
    a.cmd == b.cmd
×
182
end
183

184
function run!(helper::GitCredentialHelper, operation::AbstractString, cred::GitCredential)
×
185
    cmd = `$(helper.cmd) $operation`
×
186
    p = open(cmd, "r+")
×
187

188
    # Provide the helper with the credential information we know
189
    write(p, cred)
×
190
    write(p, "\n")
×
191
    t = @async close(p.in)
×
192

193
    # Process the response from the helper
194
    Base.read!(p, cred)
×
195
    wait(p)
×
196

197
    return cred
×
198
end
199

200
function run(helper::GitCredentialHelper, operation::AbstractString, cred::GitCredential)
×
201
    run!(helper, operation, deepcopy(cred))
×
202
end
203

204
# The available actions between using `git credential` and helpers are slightly different.
205
# We will directly interact with the helpers as that way we can request credential
206
# information without a prompt (helper `get` vs. git credential `fill`).
207
# https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage
208

209
fill!(helper::GitCredentialHelper, cred::GitCredential) = run!(helper, "get", cred)
×
210
approve(helper::GitCredentialHelper, cred::GitCredential) = run(helper, "store", cred)
×
211
reject(helper::GitCredentialHelper, cred::GitCredential) = run(helper, "erase", cred)
×
212

213
"""
214
    credential_helpers(config, git_cred) -> Vector{GitCredentialHelper}
215

216
Return all of the `GitCredentialHelper`s found within the provided `config` which are valid
217
for the specified `git_cred`.
218
"""
219
function credential_helpers(cfg::GitConfig, cred::GitCredential)
×
220
    helpers = GitCredentialHelper[]
×
221

222
    # https://git-scm.com/docs/gitcredentials#gitcredentials-helper
223
    for entry in GitConfigIter(cfg, r"credential.*\.helper$")
×
224
        section, url, name, value = split_cfg_entry(entry)
×
225
        @assert name == "helper"
×
226

227
        # Only use configuration settings where the URL applies to the git credential
228
        ismatch(url, cred) || continue
×
229

230
        # An empty credential.helper resets the list to empty
231
        if isempty(value)
×
232
            empty!(helpers)
×
233
        else
234
            Base.push!(helpers, parse(GitCredentialHelper, value))
×
235
        end
236
    end
×
237

238
    return helpers
×
239
end
240

241
"""
242
    default_username(config, git_cred) -> Union{String, Nothing}
243

244
Return the default username, if any, provided by the `config` which is valid for the
245
specified `git_cred`.
246
"""
247
function default_username(cfg::GitConfig, cred::GitCredential)
×
248
    # https://git-scm.com/docs/gitcredentials#gitcredentials-username
249
    for entry in GitConfigIter(cfg, r"credential.*\.username")
×
250
        section, url, name, value = split_cfg_entry(entry)
×
251
        @assert name == "username"
×
252

253
        # Only use configuration settings where the URL applies to the git credential
254
        ismatch(url, cred) || continue
×
255
        return value
×
256
    end
×
257

258
    return nothing
×
259
end
260

261
function use_http_path(cfg::GitConfig, cred::GitCredential)
×
262
    seen_specific = false
×
263
    use_path = false  # Default is to ignore the path
×
264

265
    # https://git-scm.com/docs/gitcredentials#gitcredentials-useHttpPath
266
    #
267
    # Note: Ideally the regular expression should use "useHttpPath"
268
    # https://github.com/libgit2/libgit2/issues/4390
269
    for entry in GitConfigIter(cfg, r"credential.*\.usehttppath")
×
270
        section, url, name, value = split_cfg_entry(entry)
×
271

272
        # Ignore global configuration if we have already encountered more specific entry
273
        if ismatch(url, cred) && (!isempty(url) || !seen_specific)
×
274
            seen_specific = !isempty(url)
×
275
            use_path = value == "true"
×
276
        end
277
    end
×
278

279
    return use_path
×
280
end
281

282
approve(cfg::GitConfig, cred::AbstractCredential, url::AbstractString) = nothing
×
283
reject(cfg::GitConfig, cred::AbstractCredential, url::AbstractString) = nothing
×
284

285
function approve(cfg::GitConfig, cred::UserPasswordCredential, url::AbstractString)
×
286
    git_cred = GitCredential(cred, url)
×
287
    git_cred.use_http_path = use_http_path(cfg, git_cred)
×
288

289
    for helper in credential_helpers(cfg, git_cred)
×
290
        approve(helper, git_cred)
×
291
    end
×
292

293
    Base.shred!(git_cred)
×
294
    nothing
×
295
end
296

297
function reject(cfg::GitConfig, cred::UserPasswordCredential, url::AbstractString)
×
298
    git_cred = GitCredential(cred, url)
×
299
    git_cred.use_http_path = use_http_path(cfg, git_cred)
×
300

301
    for helper in credential_helpers(cfg, git_cred)
×
302
        reject(helper, git_cred)
×
303
    end
×
304

305
    Base.shred!(git_cred)
×
306
    nothing
×
307
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc