diff --git a/.circleci/circleci.txt b/.circleci/circleci.txt new file mode 100644 index 000000000..c7adf9ec1 --- /dev/null +++ b/.circleci/circleci.txt @@ -0,0 +1,78 @@ +# A master build looks like this: + +# BASH_ENV=/tmp/.bash_env-63d018969ca480003a031e62-0-build +# CI=true +# CIRCLECI=true +# CIRCLE_BRANCH=master +# CIRCLE_BUILD_NUM=76545 +# CIRCLE_BUILD_URL=https://circleci.com/gh/tahoe-lafs/tahoe-lafs/76545 +# CIRCLE_JOB=NixOS 21.11 +# CIRCLE_NODE_INDEX=0 +# CIRCLE_NODE_TOTAL=1 +# CIRCLE_PROJECT_REPONAME=tahoe-lafs +# CIRCLE_PROJECT_USERNAME=tahoe-lafs +# CIRCLE_REPOSITORY_URL=git@github.com:tahoe-lafs/tahoe-lafs.git +# CIRCLE_SHA1=ed0bda2d7456f4a2cd60870072e1fe79864a49a1 +# CIRCLE_SHELL_ENV=/tmp/.bash_env-63d018969ca480003a031e62-0-build +# CIRCLE_USERNAME=alice +# CIRCLE_WORKFLOW_ID=6d9bb71c-be3a-4659-bf27-60954180619b +# CIRCLE_WORKFLOW_JOB_ID=0793c975-7b9f-489f-909b-8349b72d2785 +# CIRCLE_WORKFLOW_WORKSPACE_ID=6d9bb71c-be3a-4659-bf27-60954180619b +# CIRCLE_WORKING_DIRECTORY=~/project + +# A build of an in-repo PR looks like this: + +# BASH_ENV=/tmp/.bash_env-63d1971a0298086d8841287e-0-build +# CI=true +# CIRCLECI=true +# CIRCLE_BRANCH=3946-less-chatty-downloads +# CIRCLE_BUILD_NUM=76612 +# CIRCLE_BUILD_URL=https://circleci.com/gh/tahoe-lafs/tahoe-lafs/76612 +# CIRCLE_JOB=NixOS 21.11 +# CIRCLE_NODE_INDEX=0 +# CIRCLE_NODE_TOTAL=1 +# CIRCLE_PROJECT_REPONAME=tahoe-lafs +# CIRCLE_PROJECT_USERNAME=tahoe-lafs +# CIRCLE_PULL_REQUEST=https://github.com/tahoe-lafs/tahoe-lafs/pull/1251 +# CIRCLE_PULL_REQUESTS=https://github.com/tahoe-lafs/tahoe-lafs/pull/1251 +# CIRCLE_REPOSITORY_URL=git@github.com:tahoe-lafs/tahoe-lafs.git +# CIRCLE_SHA1=921a2083dcefdb5f431cdac195fc9ac510605349 +# CIRCLE_SHELL_ENV=/tmp/.bash_env-63d1971a0298086d8841287e-0-build +# CIRCLE_USERNAME=bob +# CIRCLE_WORKFLOW_ID=5e32c12e-be37-4868-9fa8-6a6929fec2f1 +# CIRCLE_WORKFLOW_JOB_ID=316ca408-81b4-4c96-bbdd-644e4c3e01e5 +# CIRCLE_WORKFLOW_WORKSPACE_ID=5e32c12e-be37-4868-9fa8-6a6929fec2f1 +# CIRCLE_WORKING_DIRECTORY=~/project +# CI_PULL_REQUEST=https://github.com/tahoe-lafs/tahoe-lafs/pull/1251 + +# A build of a PR from a fork looks like this: + +# BASH_ENV=/tmp/.bash_env-63d40f7b2e89cd3de10e0db9-0-build +# CI=true +# CIRCLECI=true +# CIRCLE_BRANCH=pull/1252 +# CIRCLE_BUILD_NUM=76678 +# CIRCLE_BUILD_URL=https://circleci.com/gh/tahoe-lafs/tahoe-lafs/76678 +# CIRCLE_JOB=NixOS 21.05 +# CIRCLE_NODE_INDEX=0 +# CIRCLE_NODE_TOTAL=1 +# CIRCLE_PROJECT_REPONAME=tahoe-lafs +# CIRCLE_PROJECT_USERNAME=tahoe-lafs +# CIRCLE_PR_NUMBER=1252 +# CIRCLE_PR_REPONAME=tahoe-lafs +# CIRCLE_PR_USERNAME=carol +# CIRCLE_PULL_REQUEST=https://github.com/tahoe-lafs/tahoe-lafs/pull/1252 +# CIRCLE_PULL_REQUESTS=https://github.com/tahoe-lafs/tahoe-lafs/pull/1252 +# CIRCLE_REPOSITORY_URL=git@github.com:tahoe-lafs/tahoe-lafs.git +# CIRCLE_SHA1=15c7916e0812e6baa2a931cd54b18f3382a8456e +# CIRCLE_SHELL_ENV=/tmp/.bash_env-63d40f7b2e89cd3de10e0db9-0-build +# CIRCLE_USERNAME= +# CIRCLE_WORKFLOW_ID=19c917c8-3a38-4b20-ac10-3265259fa03e +# CIRCLE_WORKFLOW_JOB_ID=58e95215-eccf-4664-a231-1dba7fd2d323 +# CIRCLE_WORKFLOW_WORKSPACE_ID=19c917c8-3a38-4b20-ac10-3265259fa03e +# CIRCLE_WORKING_DIRECTORY=~/project +# CI_PULL_REQUEST=https://github.com/tahoe-lafs/tahoe-lafs/pull/1252 + +# A build of a PR from a fork where the owner has enabled CircleCI looks +# the same as a build of an in-repo PR, except it runs on th owner's +# CircleCI namespace. diff --git a/.circleci/lib.sh b/.circleci/lib.sh index 7717cdb18..c692b5f88 100644 --- a/.circleci/lib.sh +++ b/.circleci/lib.sh @@ -1,21 +1,20 @@ # Run a command, enabling cache writes to cachix if possible. The command is # accepted as a variable number of positional arguments (like argv). function cache_if_able() { - # The `cachix watch-exec ...` does our cache population. When it sees - # something added to the store (I guess) it pushes it to the named cache. - # - # We can only *push* to it if we have a CACHIX_AUTH_TOKEN, though. - # in-repo jobs will get this from CircleCI configuration but jobs from - # forks may not. - echo "Building PR from user/org: ${CIRCLE_PROJECT_USERNAME}" - if [ -v CACHIX_AUTH_TOKEN ]; then + # Dump some info about our build environment. + describe_build + + if is_cache_writeable; then + # If the cache is available we'll use it. This lets fork owners set + # up their own caching if they want. echo "Cachix credentials present; will attempt to write to cache." + + # The `cachix watch-exec ...` does our cache population. When it sees + # something added to the store (I guess) it pushes it to the named + # cache. cachix watch-exec "${CACHIX_NAME}" -- "$@" else - # If we're building a from a forked repository then we're allowed to - # not have the credentials (but it's also fine if the owner of the - # fork supplied their own). - if [ "${CIRCLE_PROJECT_USERNAME}" == "tahoe-lafs" ]; then + if is_cache_required; then echo "Required credentials (CACHIX_AUTH_TOKEN) are missing." return 1 else @@ -24,3 +23,97 @@ function cache_if_able() { fi fi } + +function is_cache_writeable() { + # We can only *push* to the cache if we have a CACHIX_AUTH_TOKEN. in-repo + # jobs will get this from CircleCI configuration but jobs from forks may + # not. + [ -v CACHIX_AUTH_TOKEN ] +} + +function is_cache_required() { + # If we're building in tahoe-lafs/tahoe-lafs then we must use the cache. + # If we're building anything from a fork then we're allowed to not have + # the credentials. + is_upstream +} + +# Return success if the origin of this build is the tahoe-lafs/tahoe-lafs +# repository itself (and so we expect to have cache credentials available), +# failure otherwise. +# +# See circleci.txt for notes about how this determination is made. +function is_upstream() { + # CIRCLE_PROJECT_USERNAME is set to the org the build is happening for. + # If a PR targets a fork of the repo then this is set to something other + # than "tahoe-lafs". + [ "$CIRCLE_PROJECT_USERNAME" == "tahoe-lafs" ] && + + # CIRCLE_BRANCH is set to the real branch name for in-repo PRs and + # "pull/NNNN" for pull requests from forks. + # + # CIRCLE_PULL_REQUESTS is set to a comma-separated list of the full + # URLs of the PR pages which share an underlying branch, with one of + # them ended with that same "pull/NNNN" for PRs from forks. + ! any_element_endswith "/$CIRCLE_BRANCH" "," "$CIRCLE_PULL_REQUESTS" +} + +# Return success if splitting $3 on $2 results in an array with any element +# that ends with $1, failure otherwise. +function any_element_endswith() { + suffix=$1 + shift + + sep=$1 + shift + + haystack=$1 + shift + + IFS="${sep}" read -r -a elements <<< "$haystack" + for elem in "${elements[@]}"; do + if endswith "$suffix" "$elem"; then + return 0 + fi + done + return 1 +} + +# Return success if $2 ends with $1, failure otherwise. +function endswith() { + suffix=$1 + shift + + haystack=$1 + shift + + case "$haystack" in + *${suffix}) + return 0 + ;; + + *) + return 1 + ;; + esac +} + +function describe_build() { + echo "Building PR for user/org: ${CIRCLE_PROJECT_USERNAME}" + echo "Building branch: ${CIRCLE_BRANCH}" + if is_upstream; then + echo "Upstream build." + else + echo "Non-upstream build." + fi + if is_cache_required; then + echo "Cache is required." + else + echo "Cache not required." + fi + if is_cache_writeable; then + echo "Cache is writeable." + else + echo "Cache not writeable." + fi +} diff --git a/newsfragments/3969.minor b/newsfragments/3969.minor new file mode 100644 index 000000000..e69de29bb