-
Notifications
You must be signed in to change notification settings - Fork 501
Support self-hosted github installations #2755
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
ef38eb7
407d4b9
de1721f
438901a
be69eef
bc84016
5fb85ef
285c286
a1fa597
c992c64
ed5eeba
77ab55a
dc9337f
0e268b4
47fecbe
24f4f9c
a12fcfc
6b0f8a7
dcef3dd
d293e82
055111e
4e0c5e6
5133dd5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -297,6 +297,9 @@ end | |
|
|
||
| Implementation of `DeployConfig` for deploying from GitHub Actions. | ||
|
|
||
| For self-hosted GitHub installation use the `GitHubActions(pages_url)` constructor | ||
| to specify the URL to the GitHub pages location. | ||
|
|
||
| The following environment variables influences the build | ||
| when using the `GitHubActions` configuration: | ||
|
|
||
|
|
@@ -311,19 +314,61 @@ when using the `GitHubActions` configuration: | |
| - `GITHUB_TOKEN` or `DOCUMENTER_KEY`: used for authentication with GitHub, | ||
| see the manual section for [GitHub Actions](@ref) for more information. | ||
|
|
||
| - `GITHUB_API_URL`: specifies the GitHub API URL, which generally is `https://api.github.com`, | ||
| but may be different for self-hosted GitHub instances. | ||
|
|
||
| - `GITHUB_ACTOR`: name of the person or app that initiated the workflow. For | ||
| example, `octocat`. This is used to construct API calls. | ||
|
|
||
| The `GITHUB_*` variables are set automatically on GitHub Actions, see the | ||
| [documentation](https://docs.github.com/en/actions/reference/workflows-and-actions/variables#default-environment-variables). | ||
| """ | ||
| struct GitHubActions <: DeployConfig | ||
| github_repository::String | ||
| github_event_name::String | ||
| github_ref::String | ||
| github_host::String | ||
| github_api::String | ||
| github_pages_url::String | ||
| end | ||
|
|
||
| function GitHubActions() | ||
| # Try to deduce GitHub Pages URL using GitHub API | ||
| github_repository = get(ENV, "GITHUB_REPOSITORY", "") # "JuliaDocs/Documenter.jl" | ||
| github_api = get(ENV, "GITHUB_API_URL", "") # https://api.github.com | ||
| github_token = get(ENV, "GITHUB_TOKEN", "") | ||
|
|
||
| try | ||
| # Construct the curl call | ||
| cmd = `curl -s` | ||
| push!(cmd.exec, "-H", "Authorization: token $(github_token)") | ||
| push!(cmd.exec, "-H", "User-Agent: Documenter.jl") | ||
| push!(cmd.exec, "--fail") | ||
| push!(cmd.exec, "$(github_api)/repos/$(github_repository)/pages") | ||
|
|
||
| # Run the command (silently) | ||
| response = run_and_capture(cmd) | ||
| response = JSON.parse(response.stdout) | ||
|
|
||
| return GitHubActions(response["html_url"]) | ||
| catch | ||
| @warn "Unable to deduce GitHub Pages URL using GitHub API; falling back guess from the repository name." | ||
| parts = split(github_repository, "/") | ||
| if length(parts) == 2 | ||
| owner, repo = parts | ||
| return GitHubActions("https://$(owner).github.io/$(repo)/") | ||
| else | ||
| return GitHubActions("") | ||
| end | ||
| end | ||
| end | ||
|
|
||
| function GitHubActions(pages_url) | ||
| github_repository = get(ENV, "GITHUB_REPOSITORY", "") # "JuliaDocs/Documenter.jl" | ||
| github_event_name = get(ENV, "GITHUB_EVENT_NAME", "") # "push", "pull_request" or "cron" (?) | ||
| github_ref = get(ENV, "GITHUB_REF", "") # "refs/heads/$(branchname)" for branch, "refs/tags/$(tagname)" for tags | ||
| return GitHubActions(github_repository, github_event_name, github_ref) | ||
| github_api = get(ENV, "GITHUB_API_URL", "") # https://api.github.com | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is There's also
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we can assume anything about it, I don't know exactly if it is configurable but I would expect that it is as some orgs have strict rules about domain names I need to check GITHUB_SERVER_URL , for some reason I haven't used that, don't know if it is because we don't have it or because I've missed it if something like that exists I would prefer to used it indeed instead of specifying it manually
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that And in the corresponding enterprise docs at https://docs.github.com/en/[email protected]/actions/reference/workflows-and-actions/variables the example value given is |
||
| return GitHubActions(github_repository, github_event_name, github_ref, Remotes.github_host(), github_api, pages_url) | ||
| end | ||
|
|
||
| # Check criteria for deployment | ||
|
|
@@ -393,7 +438,7 @@ function deploy_folder( | |
| all_ok &= pr_ok | ||
| println(io, "- $(marker(pr_ok)) ENV[\"GITHUB_REF\"] corresponds to a PR number") | ||
| if pr_ok | ||
| pr_origin_matches_repo = verify_github_pull_repository(cfg.github_repository, pr_number) | ||
| pr_origin_matches_repo = verify_github_pull_repository(cfg, pr_number) | ||
| all_ok &= pr_origin_matches_repo | ||
| println(io, "- $(marker(pr_origin_matches_repo)) PR originates from the same repository") | ||
| end | ||
|
|
@@ -452,7 +497,7 @@ end | |
|
|
||
| authentication_method(::GitHubActions) = env_nonempty("DOCUMENTER_KEY") ? SSH : HTTPS | ||
| function authenticated_repo_url(cfg::GitHubActions) | ||
| return "https://$(ENV["GITHUB_ACTOR"]):$(ENV["GITHUB_TOKEN"])@github.com/$(cfg.github_repository).git" | ||
| return "https://$(ENV["GITHUB_ACTOR"]):$(ENV["GITHUB_TOKEN"])@$(cfg.github_host)/$(cfg.github_repository).git" | ||
| end | ||
|
|
||
| function version_tag_strip_build(tag; tag_prefix = "") | ||
|
|
@@ -469,7 +514,7 @@ function version_tag_strip_build(tag; tag_prefix = "") | |
| return "$s0$s1$s2$s3$s4" | ||
| end | ||
|
|
||
| function post_status(gha::GitHubActions; type, repo::String, subfolder = nothing, kwargs...) | ||
| function post_status(gha::GitHubActions; type, subfolder = nothing, kwargs...) | ||
| try # make this non-fatal and silent | ||
| # If we got this far it usually means everything is in | ||
| # order so no need to check everything again. | ||
|
|
@@ -489,21 +534,20 @@ function post_status(gha::GitHubActions; type, repo::String, subfolder = nothing | |
| sha = get(ENV, "GITHUB_SHA", nothing) | ||
| end | ||
| sha === nothing && return | ||
| return post_github_status(type, gha.github_repository, repo, sha, subfolder) | ||
| catch | ||
| @debug "Failed to post status" | ||
| return post_github_status(gha, type, sha, subfolder) | ||
| catch e | ||
| @debug "Failed to post status" exception = e | ||
| end | ||
| end | ||
|
|
||
| function post_github_status(type::S, source::S, deploydocs_repo::S, sha::S, subfolder = nothing) where {S <: String} | ||
| function post_github_status(gha::GitHubActions, type::S, sha::S, subfolder = nothing) where {S <: String} | ||
| try | ||
| Sys.which("curl") === nothing && return | ||
| if Sys.which("curl") === nothing | ||
| @warn "curl not found in PATH, cannot post status" | ||
| return | ||
| end | ||
| ## Extract owner and repository names | ||
| source_owner, source_repo = split(source, '/') | ||
| m = match(r"^github.com\/(.+?)\/(.+?)(.git)?$", deploydocs_repo) | ||
| m === nothing && return | ||
| deploy_owner = String(m.captures[1]) | ||
| deploy_repo = String(m.captures[2]) | ||
| source_owner, source_repo = split(gha.github_repository, '/') | ||
|
|
||
| ## Need an access token for this | ||
| auth = get(ENV, "GITHUB_TOKEN", nothing) | ||
|
|
@@ -518,9 +562,9 @@ function post_github_status(type::S, source::S, deploydocs_repo::S, sha::S, subf | |
| json["description"] = "Documentation build in progress" | ||
| elseif type == "success" | ||
| json["description"] = "Documentation build succeeded" | ||
| target_url = "https://$(deploy_owner).github.io/$(deploy_repo)/" | ||
| if subfolder !== nothing | ||
| target_url *= "$(subfolder)/" | ||
| target_url = gha.github_pages_url | ||
| if !isempty(target_url) && subfolder !== nothing | ||
| target_url = rstrip(target_url, '/') * "/$(subfolder)/" | ||
| end | ||
| json["target_url"] = target_url | ||
| elseif type == "error" | ||
|
|
@@ -531,18 +575,19 @@ function post_github_status(type::S, source::S, deploydocs_repo::S, sha::S, subf | |
| error("unsupported type: $type") | ||
| end | ||
| push!(cmd.exec, "-d", JSON.json(json)) | ||
| push!(cmd.exec, "https://api.github.com/repos/$(source_owner)/$(source_repo)/statuses/$(sha)") | ||
| push!(cmd.exec, "$(gha.github_api)/repos/$(source_owner)/$(source_repo)/statuses/$(sha)") | ||
| # Run the command (silently) | ||
| io = IOBuffer() | ||
| res = run(pipeline(cmd; stdout = io, stderr = devnull)) | ||
| @debug "Response of curl POST request" response = String(take!(io)) | ||
| catch | ||
| @debug "Failed to post status" | ||
| catch e | ||
| @debug "Failed to post status" exception = e | ||
| end | ||
| return nothing | ||
| end | ||
|
|
||
| function verify_github_pull_repository(repo, prnr) | ||
| function verify_github_pull_repository(cfg::GitHubActions, prnr) | ||
| repo = cfg.github_repository | ||
| github_token = get(ENV, "GITHUB_TOKEN", nothing) | ||
| if github_token === nothing | ||
| @warn "GITHUB_TOKEN is missing, unable to verify if PR comes from destination repository -- assuming it doesn't." | ||
|
|
@@ -553,7 +598,7 @@ function verify_github_pull_repository(repo, prnr) | |
| push!(cmd.exec, "-H", "Authorization: token $(github_token)") | ||
| push!(cmd.exec, "-H", "User-Agent: Documenter.jl") | ||
| push!(cmd.exec, "--fail") | ||
| push!(cmd.exec, "https://api.github.com/repos/$(repo)/pulls/$(prnr)") | ||
| push!(cmd.exec, "$(cfg.github_api)/repos/$(repo)/pulls/$(prnr)") | ||
| try | ||
| # Run the command (silently) | ||
| response = run_and_capture(cmd) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ Types and functions for handling repository remotes. | |
| """ | ||
| module Remotes | ||
|
|
||
| using URIs: URI | ||
| """ | ||
| abstract type Remote | ||
|
|
||
|
|
@@ -103,7 +104,44 @@ function repofile(remote::Remote, ref, filename, linerange = nothing) | |
| end | ||
|
|
||
| """ | ||
| GitHub(user :: AbstractString, repo :: AbstractString) | ||
| parse_url(url) | ||
|
|
||
| Return tuple with values `authority` and `path` fragments of the given URL string. | ||
| This function is not strictly following URI specification as it will parse "github.com/user/repo" into `("github.com", "/user/repo")` | ||
| returning authority even if scheme is missing. | ||
| """ | ||
| function parse_url(url)::Tuple{String, String} | ||
| if contains(url, "://") == false | ||
| url = "https://$(url)" | ||
| end | ||
| u = URI(url) | ||
|
|
||
| authority = u.host | ||
| if u.userinfo != "" | ||
| authority = "$(u.userinfo)@$(authority)" | ||
| end | ||
|
|
||
| if u.port != "" | ||
| authority = "$(authority):$(u.port)" | ||
| end | ||
| return (authority, u.path) | ||
| end | ||
|
|
||
| """ | ||
| github_host() | ||
|
|
||
| Returns hostname of the GitHub installation where this code is running on at the moment. | ||
| This is derived from the `ENV[GITHUB_SERVER_URL]` variable which is set in every GitHub Actions workflow. | ||
| If this variable is not set, return "github.com". | ||
| """ | ||
| function github_host() | ||
| haskey(ENV, "GITHUB_SERVER_URL") || return "github.com" | ||
| url = ENV["GITHUB_SERVER_URL"] | ||
| return parse_url(url)[1] | ||
| end | ||
|
|
||
| """ | ||
| GitHub(user :: AbstractString, repo :: AbstractString, [host :: AbstractString]) | ||
| GitHub(remote :: AbstractString) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I took the liberty of removing |
||
|
|
||
| Represents a remote Git repository hosted on GitHub. The repository is identified by the | ||
|
|
@@ -117,16 +155,28 @@ makedocs( | |
|
|
||
| The single-argument constructor assumes that the user and repository parts are separated by | ||
| a slash (e.g. `JuliaDocs/Documenter.jl`). | ||
|
|
||
| A `host` can be provided to point to the location of the self-hosted GitHub installation. | ||
| """ | ||
| struct GitHub <: Remote | ||
| user::String | ||
| repo::String | ||
| host::String | ||
|
|
||
| GitHub(user::AbstractString, repo::AbstractString, host::AbstractString = github_host()) = new(user, repo, host) | ||
| end | ||
| function GitHub(remote::AbstractString) | ||
| user, repo = split(remote, '/') | ||
| return GitHub(user, repo) | ||
| url_authority, url_path = parse_url(remote) | ||
| path = url_path[1] == '/' ? url_path[2:end] : url_path | ||
|
|
||
| if occursin("/", path) | ||
| user, repo = split(path, "/") | ||
| return GitHub(user, repo, url_authority) | ||
| else | ||
| return GitHub(url_authority, path) | ||
| end | ||
| end | ||
| repourl(remote::GitHub) = "https://github.com/$(remote.user)/$(remote.repo)" | ||
| repourl(remote::GitHub) = "https://$(remote.host)/$(remote.user)/$(remote.repo)" | ||
| function fileurl(remote::GitHub, ref::AbstractString, filename::AbstractString, linerange) | ||
| url = "$(repourl(remote))/blob/$(ref)/$(filename)" | ||
| isnothing(linerange) && return url | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This now needs to be moved up (it currently is in the 1.16.0 section but that version has already been released)