Skip to content

Transition the coverage-linux64 pipeline to Buildkite #41238

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

Merged
merged 4 commits into from
Jun 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .buildkite/coverage-linux64/0_webui.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# This file represents what is put into the webUI.
# It is purely for keeping track of the changes we make to the webUI configuration; modifying this file has no effect.
# We use the `cryptic` buildkite plugin to provide secrets management, which requires some integration into the WebUI's steps.
agents:
queue: "julia"
sandbox.jl: "true"

steps:
- label: ":unlock: Unlock secrets, launch pipelines"
plugins:
- staticfloat/cryptic:
# Our signed pipelines must have a `signature` or `signature_file` parameter that
# verifies the treehash of the pipeline itself and the inputs listed in `inputs`
signed_pipelines:
- pipeline: .buildkite/coverage-linux64/pipeline.yml
signature: "U2FsdGVkX18eQWpd3hMYLO5Kd+6K+oBoLk1I6J3qIw7lc6g5/jaeWyq/wralosZCfTzyjS4NstNKFvhQf3KDPEBVElipNvTxoWOjVLRVOrfBqqvTkQN4xVosY/r026Gy"
6 changes: 6 additions & 0 deletions .buildkite/coverage-linux64/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Coverage pipeline

We run coverage on a separate pipeline, that uses a scheduled build rather than webhooks.
The pipeline is here: https://buildkite.com/julialang/julia-coverage-linux64

It contains [its own webui steps](0_webuiy.ml) (listed here in this repository for clarity) and its own [pipeline.yml](pipeline.yml).
44 changes: 44 additions & 0 deletions .buildkite/coverage-linux64/pipeline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# These steps should only run on `sandbox.jl` machines, not `docker`-isolated ones
# since we need nestable sandboxing. The rootfs images being used here are built from
# the `.buildkite/rootfs_images/llvm-passes.jl` file.
agents:
queue: "julia"
# Only run on `sandbox.jl` machines (not `docker`-isolated ones) since we need nestable sandboxing
sandbox.jl: "true"
os: "linux"

steps:
- label: ":unlock: :coverage: Run coverage test"
plugins:
- staticfloat/cryptic:
variables:
- CODECOV_TOKEN="U2FsdGVkX19l0fhdBabbuiEdysyEabkJLRHfxm7CNRkuGbnwPV365sxxC7Czs/CVcws0N1oB4pVwALRRMe36oA=="
- COVERALLS_TOKEN="U2FsdGVkX19zopI0hMNzzi2UUOvNVFD8Y0iisFnO/ryVxU7Tit8ZEaeN+gxodRx4CosUUh192F1+q3dTMWRIvw=="
- JuliaCI/julia#v1:
version: 1.6
- staticfloat/sandbox#v1:
rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v1/llvm-passes.tar.gz
rootfs_treehash: "f3ed53f159e8f13edfba8b20ebdb8ece73c1b8a8"
uid: 1000
gid: 1000
commands: |
echo "--- Build Julia from source"
make -j 6

echo "--- Print Julia version info"
./julia -e 'using InteractiveUtils; InteractiveUtils.versioninfo()'
./julia -e '@info "" Sys.CPU_THREADS'
# this is necessary to make sure that the LibGit2 tests passes
git config --global init.defaultBranch master

echo "--- Run Julia tests with code coverage enabled"
# Run the actual tests
./julia --code-coverage=all --sysimage-native-code=no .buildkite/coverage-linux64/run_tests_base.jl

echo "--- Process and upload coverage information"
./julia .buildkite/coverage-linux64/upload_coverage.jl
timeout_in_minutes: 600 # 600 minutes = 10 hours

# We must accept the signed job id secret in order to propagate secrets
env:
BUILDKITE_PLUGIN_CRYPTIC_BASE64_SIGNED_JOB_ID_SECRET: ${BUILDKITE_PLUGIN_CRYPTIC_BASE64_SIGNED_JOB_ID_SECRET?}
36 changes: 36 additions & 0 deletions .buildkite/coverage-linux64/run_tests_base.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# When running this file, make sure to set the `--code-coverage=all` command-line flag.

# Important note: even if one or more tests fail, we will still exit with status code 0.

# The reason for this is that we always want to upload code coverage, even if some of the
# tests fail. Therefore, even if the `coverage-linux64` pipeline passes, you should not
# assume that all of the tests passed. If you want to know if all of the tests are passing,
# please look at the status of the `tester_linux64` pipeline.

const include_tests = String[]

const exclude_tests = String[]

empty!(Base.DEPOT_PATH)
push!(Base.DEPOT_PATH, mktempdir(; cleanup = true))

module ChooseTests
include(joinpath(dirname(dirname(@__DIR__)), "test", "choosetests.jl"))
end

const tests = ChooseTests.choosetests() |>
first |>
x -> setdiff(x, exclude_tests) |>
x -> vcat(x, include_tests) |>
unique |>
sort

const ncores = min(Sys.CPU_THREADS, Threads.nthreads())

@info "" ncores Sys.CPU_THREADS Threads.nthreads()

try
Base.runtests(tests; ncores)
catch ex
@error "" exception=(ex, catch_backtrace())
end
115 changes: 115 additions & 0 deletions .buildkite/coverage-linux64/upload_coverage.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
empty!(Base.DEPOT_PATH)
push!(Base.DEPOT_PATH, mktempdir(; cleanup = true))

import Pkg
import Logging
import TOML

Pkg.add(; name = "Coverage", uuid = "a2441757-f6aa-5fb2-8edb-039e3f45d037", version = "1")
Pkg.precompile()

import Coverage

function get_external_stdlib_names(stdlib_dir::AbstractString)
filename_list = filter(x -> isfile(joinpath(stdlib_dir, x)), readdir(stdlib_dir))
# find all of the files like `Pkg.version`, `Statistics.version`, etc.
regex_matches_or_nothing = match.(Ref(r"^([\w].*?)\.version$"), filename_list)
regex_matches = filter(x -> x !== nothing, regex_matches_or_nothing)
# get the names of the external stdlibs, like `Pkg`, `Statistics`, etc.
external_stdlib_names = only.(regex_matches)
unique!(external_stdlib_names)
sort!(external_stdlib_names)
@info "# Begin list of external stdlibs"
for (i, x) in enumerate(external_stdlib_names)
@info "$(i). $(x)"
end
@info "# End list of external stdlibs"
return external_stdlib_names
end

function get_external_stdlib_prefixes(stdlib_dir::AbstractString)
external_stdlib_names = get_external_stdlib_names(stdlib_dir)
prefixes_1 = joinpath.(Ref(stdlib_dir), external_stdlib_names, Ref(""))
prefixes_2 = joinpath.(Ref(stdlib_dir), string.(external_stdlib_names, Ref("-")))
prefixes = vcat(prefixes_1, prefixes_2)
unique!(prefixes)
sort!(prefixes)
# example of what `prefixes` might look like:
# 4-element Vector{String}:
# "stdlib/Pkg-"
# "stdlib/Pkg/"
# "stdlib/Statistics-"
# "stdlib/Statistics/"
return prefixes
end

function print_coverage_summary(fc::Coverage.FileCoverage)
cov_lines, tot_lines = Coverage.get_summary(fc)
if cov_lines == tot_lines == 0
cov_pct = 0
else
cov_pct = floor(Int, cov_lines/tot_lines * 100)
end
pad_1 = 71
pad_2 = 15
pad_3 = 15
col_1 = rpad(fc.filename, pad_1)
col_2 = rpad(string(cov_pct, " %"), pad_2)
col_3 = string(
rpad(string(cov_lines), pad_3),
string(tot_lines),
)
@info "$(col_1) $(col_2) $(col_3)"
return nothing
end

function print_coverage_summary(
fcs::Vector{Coverage.FileCoverage}, description::AbstractString,
)
cov_lines, tot_lines = Coverage.get_summary(fcs)
if cov_lines == tot_lines == 0
cov_pct = 0
else
cov_pct = floor(Int, cov_lines/tot_lines * 100)
end
@info "$(description): $(cov_pct)% ($(cov_lines)/$(tot_lines))"
return nothing
end

# `Coverage.process_folder` will have a LOT of `@info` statements that will make the log
# way too long. So before we run `Coverage.process_folder`, we disable logging for `@info`
# statements. After we run `Coverage.process_folder`, we re-enable logging for `@info`
# statements.
Logging.disable_logging(Logging.Info)
const fcs = Coverage.merge_coverage_counts(
Coverage.process_folder("base"),
Coverage.process_folder("stdlib"),
);
Logging.disable_logging(Logging.Debug)

# Only include source code files. Exclude test files, benchmarking files, etc.
filter!(fcs) do fc
occursin(r"^base\/", fc.filename) || occursin("/src/", fc.filename)
end;

# Exclude all external stdlibs (stdlibs that live in external repos).
const external_stdlib_prefixes = get_external_stdlib_prefixes("stdlib")
filter!(fcs) do fc
all(x -> !startswith(fc.filename, x), external_stdlib_prefixes)
end;

# Exclude all stdlib JLLs (stdlibs of the form `stdlib/*_jll/`).
filter!(fcs) do fc
!occursin(r"^stdlib\/[A-Za-z0-9]*?_jll\/", fc.filename)
end

sort!(fcs; by = fc -> fc.filename)

print_coverage_summary.(fcs);
print_coverage_summary(fcs, "Total")

# In order to upload to Codecov, you need to have the `CODECOV_TOKEN` environment variable defined.
Coverage.Codecov.submit_local(fcs)

# In order to upload to Coveralls, you need to have the `COVERALLS_TOKEN` environment variable defined.
Coverage.Coveralls.submit_local(fcs)