From 721dbb1eaa0b18f9f2e61122c83b1033e777c131 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Fri, 20 Sep 2024 15:46:49 -0700 Subject: [PATCH 1/6] Try out a Julia deployment strategy --- .github/workflows/main.yaml | 4 +- helpers/deploy_with_preview.jl | 189 +++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 helpers/deploy_with_preview.jl diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 92ed342..df70553 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -39,9 +39,7 @@ jobs: QUARTO_JULIA_PROJECT: "@quarto" - name: Render and Publish - uses: quarto-dev/quarto-actions/publish@v2 - with: - target: gh-pages + run: julia helpers/deploy_with_preview.jl env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} QUARTO_JULIA_PROJECT: "@quarto" diff --git a/helpers/deploy_with_preview.jl b/helpers/deploy_with_preview.jl new file mode 100644 index 0000000..aa63598 --- /dev/null +++ b/helpers/deploy_with_preview.jl @@ -0,0 +1,189 @@ +# This file was adapted from Documenter.jl's deployconfig.jl function for Github Actions. + +function authenticated_repo_url() + return "https://$(ENV["GITHUB_ACTOR"]):$(ENV["GITHUB_TOKEN"])@github.com/$(ENV["GITHUB_REPOSITORY"]).git" +end + +marker(x) = x ? "✔" : "✘" + +git() = `git` + +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 + +cfg = (; github_repository, github_event_name, github_ref) + +if cfg.github_event_name == "pull_request" + build_type = :preview +elseif occursin(r"^refs\/tags\/(.*)$", cfg.github_ref) + build_type = :release +else + build_type = :devbranch +end + +println(io, "Deployment criteria for deploying $(build_type) build from GitHub Actions:") + +all_ok = true +is_preview = false + +if build_type in (:release, :devbranch) + event_ok = in(cfg.github_event_name, ["push", "workflow_dispatch", "schedule"]) + all_ok &= event_ok + println(io, "- $(marker(event_ok)) ENV[\"GITHUB_EVENT_NAME\"]=\"$(cfg.github_event_name)\" is \"push\", \"workflow_dispatch\" or \"schedule\"") + m = match(r"^refs\/heads\/(.*)$", cfg.github_ref) + branch_ok = m === nothing ? false : String(m.captures[1]) == devbranch + all_ok &= branch_ok + println(io, "- $(marker(branch_ok)) ENV[\"GITHUB_REF\"] matches devbranch=\"$(devbranch)\"") + is_preview = false + subfolder = "." +elseif build_type == :preview + m = match(r"refs\/pull\/(\d+)\/merge", cfg.github_ref) + pr_number = tryparse(Int, m === nothing ? "" : m.captures[1]) + pr_ok = pr_number !== nothing + all_ok &= pr_ok + println(io, "- $(marker(pr_ok)) ENV[\"GITHUB_REF\"] corresponds to a PR number") + btype_ok = true + all_ok &= btype_ok + is_preview = true + ## deploydocs to previews/PR + subfolder = "previews/PR$(something(pr_number, 0))" +end + +quarto_rendered = success(`quarto render`) + +println("$(marker(all_ok)) All environment variables are set correctly.") +println("$(marker(quarto_rendered)) Quarto rendered successfully.") + +if !quarto_rendered + println("$(marker(false)) Quarto rendering failed.") + exit(1) +end +if !all_ok + println("$(marker(false)) Deployment failed.") + exit(0) +end + +# Here we know that the site builds to `docs/`, +# but you could customize this directory in the future. +builddir = "docs" +upstream = "https://github.com/geocompx/geocompjl.git" +branch = "gh-pages" + + +sha = cd(dirname(@__DIR__)) do + # Find the commit sha. + # We'll make sure we run the git commands in the source directory (root), in case + # the working directory has been changed (e.g. if the makedocs' build argument is + # outside root). + try + readchomp(`$(git()) rev-parse --short HEAD`) + catch + # git rev-parse will throw an error and return code 128 if it is not being + # run in a git repository, which will make run/readchomp throw an exception. + # We'll assume that if readchomp fails it is due to this and set the sha + # variable accordingly. + "(not-git-repo)" + end +end + + +""" + gitrm_copy(src, dst) + +Uses `git rm -r` to remove `dst` and then copies `src` to `dst`. Assumes that the working +directory is within the git repository of `dst` is when the function is called. + +This is to get around [#507](https://github.com/JuliaDocs/Documenter.jl/issues/507) on +filesystems that are case-insensitive (e.g. on OS X, Windows). Without doing a `git rm` +first, `git add -A` will not detect case changes in filenames. +""" +function gitrm_copy(src, dst) + # Remove individual entries since with versions=nothing the root + # would be removed and we want to preserve previews + if isdir(dst) + for x in filter!(!in((".git", "previews", "CNAME")), readdir(dst)) + # --ignore-unmatch so that we wouldn't get errors if dst does not exist + run(`$(git()) rm -rf --ignore-unmatch $(joinpath(dst, x))`) + end + end + # git rm also remove parent directories + # if they are empty so need to mkpath after + mkpath(dst) + # Copy individual entries rather then the full folder since with + # versions=nothing it would replace the root including e.g. the .git folder + for x in readdir(src) + cp(joinpath(src, x), joinpath(dst, x); force=true) + end +end +# Generate a closure with common commands for ssh and https +function git_commands(sshconfig=nothing) + # Setup git. + run(`$(git()) init`) + run(`$(git()) config user.name "Julia Quarto deployment script"`) + run(`$(git()) config user.email "documenter@juliadocs.github.io"`) + + # Fetch from remote and checkout the branch. + run(`$(git()) remote add upstream $upstream`) + try + run(`$(git()) fetch upstream`) + catch e + @error """ + Git failed to fetch $upstream + This can be caused by a DOCUMENTER_KEY variable that is not correctly set up. + Make sure that the environment variable is properly set up as a Base64-encoded string + of the SSH private key. You may need to re-generate the keys with DocumenterTools. + """ + rethrow(e) + end + + try + run(`$(git()) checkout -b $branch upstream/$branch`) + catch e + @info """ + Checking out $branch failed, creating a new orphaned branch. + This usually happens when deploying to a repository for the first time and + the $branch branch does not exist yet. The fatal error above is expected output + from Git in this situation. + """ + @debug "checking out $branch failed with error: $e" + run(`$(git()) checkout --orphan $branch`) + run(`$(git()) commit --allow-empty -m "Initial empty commit for docs"`) + end + + # Copy docs to `subfolder` directory. + deploy_dir = subfolder === nothing ? "." : joinpath(dirname, subfolder) + gitrm_copy(target_dir, deploy_dir) + + # Add, commit, and push the docs to the remote. + run(`$(git()) add -A -- ':!.documenter-identity-file.tmp' ':!**/.documenter-identity-file.tmp'`) + if !success(`$(git()) diff --cached --exit-code`) + # if !isnothing(archive) + # run(`$(git()) commit -m "build based on $sha"`) + # @info "Skipping push and writing repository to an archive" archive + # run(`$(git()) archive -o $(archive) HEAD`) + # elseif forcepush + # run(`$(git()) commit --amend --date=now -m "build based on $sha"`) + # run(`$(git()) push -fq upstream HEAD:$branch`) + # else + run(`$(git()) commit -m "build based on $sha"`) + run(`$(git()) push -q upstream HEAD:$branch`) + # end + else + @debug "new docs identical to the old -- not committing nor pushing." + end +end + + +# authentication_method(deploy_config) === HTTPS +# The upstream URL to which we push new content authenticated with token +upstream = authenticated_repo_url() +try + cd(git_commands, temp) + # post_status(deploy_config; repo=repo, type="success", subfolder=subfolder) +catch e + @error "Failed to push:" exception=(e, catch_backtrace()) + # post_status(deploy_config; repo=repo, type="error") + rethrow(e) +end + From 053a721088b9885fd68da91ac04e5da69906eeec Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Fri, 20 Sep 2024 15:57:55 -0700 Subject: [PATCH 2/6] set io to stdout --- helpers/deploy_with_preview.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/helpers/deploy_with_preview.jl b/helpers/deploy_with_preview.jl index aa63598..4c04966 100644 --- a/helpers/deploy_with_preview.jl +++ b/helpers/deploy_with_preview.jl @@ -14,6 +14,8 @@ github_ref = get(ENV, "GITHUB_REF", "") # "refs/heads/$(branchname cfg = (; github_repository, github_event_name, github_ref) +io = stdout + if cfg.github_event_name == "pull_request" build_type = :preview elseif occursin(r"^refs\/tags\/(.*)$", cfg.github_ref) From 7f287660d81f9702eb307e12be0d7a932e9b1105 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Fri, 20 Sep 2024 16:04:00 -0700 Subject: [PATCH 3/6] emit Quarto output to stdout as well --- helpers/deploy_with_preview.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpers/deploy_with_preview.jl b/helpers/deploy_with_preview.jl index 4c04966..eb343ac 100644 --- a/helpers/deploy_with_preview.jl +++ b/helpers/deploy_with_preview.jl @@ -52,7 +52,7 @@ elseif build_type == :preview subfolder = "previews/PR$(something(pr_number, 0))" end -quarto_rendered = success(`quarto render`) +quarto_rendered = success(pipeline(`quarto render`; stdout=stdout, stderr=stderr)) println("$(marker(all_ok)) All environment variables are set correctly.") println("$(marker(quarto_rendered)) Quarto rendered successfully.") @@ -181,7 +181,7 @@ end # The upstream URL to which we push new content authenticated with token upstream = authenticated_repo_url() try - cd(git_commands, temp) + cd(git_commands, mktempdir()) # post_status(deploy_config; repo=repo, type="success", subfolder=subfolder) catch e @error "Failed to push:" exception=(e, catch_backtrace()) From b55feaaa55f97aa6dbb1b237fa5961881436b35d Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Fri, 20 Sep 2024 16:15:04 -0700 Subject: [PATCH 4/6] fix joinpath invocation --- helpers/deploy_with_preview.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/deploy_with_preview.jl b/helpers/deploy_with_preview.jl index eb343ac..92f09e4 100644 --- a/helpers/deploy_with_preview.jl +++ b/helpers/deploy_with_preview.jl @@ -154,7 +154,7 @@ function git_commands(sshconfig=nothing) end # Copy docs to `subfolder` directory. - deploy_dir = subfolder === nothing ? "." : joinpath(dirname, subfolder) + deploy_dir = subfolder === nothing ? "." : joinpath(".", subfolder) gitrm_copy(target_dir, deploy_dir) # Add, commit, and push the docs to the remote. From 1568b3ef12315099c4633ae14a4887b104f8c4bf Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Fri, 20 Sep 2024 16:23:08 -0700 Subject: [PATCH 5/6] change builddir to target_dir --- helpers/deploy_with_preview.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/deploy_with_preview.jl b/helpers/deploy_with_preview.jl index 92f09e4..368d7f5 100644 --- a/helpers/deploy_with_preview.jl +++ b/helpers/deploy_with_preview.jl @@ -68,7 +68,7 @@ end # Here we know that the site builds to `docs/`, # but you could customize this directory in the future. -builddir = "docs" +target_dir = "docs" # builddir upstream = "https://github.com/geocompx/geocompjl.git" branch = "gh-pages" From d95577ac4b2a4f0f01a6417526389711abc07822 Mon Sep 17 00:00:00 2001 From: Anshul Singhvi Date: Fri, 20 Sep 2024 16:31:30 -0700 Subject: [PATCH 6/6] use abspath in target_dir --- helpers/deploy_with_preview.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/deploy_with_preview.jl b/helpers/deploy_with_preview.jl index 368d7f5..3e3ff2b 100644 --- a/helpers/deploy_with_preview.jl +++ b/helpers/deploy_with_preview.jl @@ -68,7 +68,7 @@ end # Here we know that the site builds to `docs/`, # but you could customize this directory in the future. -target_dir = "docs" # builddir +target_dir = abspath("./docs") # builddir upstream = "https://github.com/geocompx/geocompjl.git" branch = "gh-pages"