Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[rubygems/rubygems] Introduce the Gem::CIDetector
This is based on the list in Gem::UpdateSuggestion and Bundler::Fetcher; these have similar purposes (determining whether/what CI we're executing in), and can benefit from being combined and updated (they're both slightly out of date). Noteable changes: * We'll consider ourselves to be on a CI in more cases - if CI_NAME or TASKCLUSTER_ROOT_URL are set specifically, since those represent two cases that were either overlooked or are no longer covered by the existing implementation. (Or possibly TaskCluster still does provide RUN_ID, but failed to document it) * We will notice/track a few additional services in ci_strings (cirrus, dsari, taskcluster), stop tracking 'snap' (they went under in 2017), and update buildbox to buildkite (they've been called that for 8 years, and the BUILDBOX envs have been deprecated for 3). * We'll also sort/uniq/downcase the values (all of which only matter because of the special case of CI_NAME). rubygems/rubygems@60652b942f
- Loading branch information
Showing
3 changed files
with
120 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# frozen_string_literal: true | ||
|
||
module Gem | ||
module CIDetector | ||
# NOTE: Any changes made here will need to be made to both lib/rubygems/ci_detector.rb and | ||
# bundler/lib/bundler/ci_detector.rb (which are enforced duplicates). | ||
# TODO: Drop that duplication once bundler drops support for RubyGems 3.4 | ||
# | ||
# ## Recognized CI providers, their signifiers, and the relevant docs ## | ||
# | ||
# Travis CI - CI, TRAVIS https://docs.travis-ci.com/user/environment-variables/#default-environment-variables | ||
# Cirrus CI - CI, CIRRUS_CI https://cirrus-ci.org/guide/writing-tasks/#environment-variables | ||
# Circle CI - CI, CIRCLECI https://circleci.com/docs/variables/#built-in-environment-variables | ||
# Gitlab CI - CI, GITLAB_CI https://docs.gitlab.com/ee/ci/variables/ | ||
# AppVeyor - CI, APPVEYOR https://www.appveyor.com/docs/environment-variables/ | ||
# CodeShip - CI_NAME https://docs.cloudbees.com/docs/cloudbees-codeship/latest/pro-builds-and-configuration/environment-variables#_default_environment_variables | ||
# dsari - CI, DSARI https://github.com/rfinnie/dsari#running | ||
# Jenkins - BUILD_NUMBER https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables | ||
# TeamCity - TEAMCITY_VERSION https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html#Predefined+Server+Build+Parameters | ||
# Appflow - CI_BUILD_ID https://ionic.io/docs/appflow/automation/environments#predefined-environments | ||
# TaskCluster - TASKCLUSTER_ROOT_URL https://docs.taskcluster.net/docs/manual/design/env-vars | ||
# Semaphore - CI, SEMAPHORE https://docs.semaphoreci.com/ci-cd-environment/environment-variables/ | ||
# BuildKite - CI, BUILDKITE https://buildkite.com/docs/pipelines/environment-variables | ||
# GoCD - GO_SERVER_URL https://docs.gocd.org/current/faq/dev_use_current_revision_in_build.html | ||
# GH Actions - CI, GITHUB_ACTIONS https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables | ||
# | ||
# ### Some "standard" ENVs that multiple providers may set ### | ||
# | ||
# * CI - this is set by _most_ (but not all) CI providers now; it's approaching a standard. | ||
# * CI_NAME - Not as frequently used, but some providers set this to specify their own name | ||
|
||
# Any of these being set is a reasonably reliable indicator that we are | ||
# executing in a CI environment. | ||
ENV_INDICATORS = [ | ||
"CI", | ||
"CI_NAME", | ||
"CONTINUOUS_INTEGRATION", | ||
"BUILD_NUMBER", | ||
"CI_APP_ID", | ||
"CI_BUILD_ID", | ||
"CI_BUILD_NUMBER", | ||
"RUN_ID", | ||
"TASKCLUSTER_ROOT_URL", | ||
].freeze | ||
|
||
# For each CI, this env suffices to indicate that we're on _that_ CI's | ||
# containers. (A few of them only supply a CI_NAME variable, which is also | ||
# nice). And if they set "CI" but we can't tell which one they are, we also | ||
# want to know that - a bare "ci" without another token tells us as much. | ||
ENV_DESCRIPTORS = { | ||
"TRAVIS" => "travis", | ||
"CIRCLECI" => "circle", | ||
"CIRRUS_CI" => "cirrus", | ||
"DSARI" => "dsari", | ||
"SEMAPHORE" => "semaphore", | ||
"JENKINS_URL" => "jenkins", | ||
"BUILDKITE" => "buildkite", | ||
"GO_SERVER_URL" => "go", | ||
"GITLAB_CI" => "gitlab", | ||
"GITHUB_ACTIONS" => "github", | ||
"TASKCLUSTER_ROOT_URL" => "taskcluster", | ||
"CI" => "ci", | ||
}.freeze | ||
|
||
def self.ci? | ||
ENV_INDICATORS.any? {|var| ENV.include?(var) } | ||
end | ||
|
||
def self.ci_strings | ||
matching_names = ENV_DESCRIPTORS.select {|env, _| ENV[env] }.values | ||
matching_names << ENV["CI_NAME"].downcase if ENV["CI_NAME"] | ||
matching_names.reject(&:empty?).sort.uniq | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative "helper" | ||
require "rubygems" | ||
|
||
class TestCiDetector < Test::Unit::TestCase | ||
def test_ci? | ||
with_env("FOO" => "bar") { assert_equal(false, Gem::CIDetector.ci?) } | ||
with_env("CI" => "true") { assert_equal(true, Gem::CIDetector.ci?) } | ||
with_env("CONTINUOUS_INTEGRATION" => "1") { assert_equal(true, Gem::CIDetector.ci?) } | ||
with_env("RUN_ID" => "0", "TASKCLUSTER_ROOT_URL" => "2") do | ||
assert_equal(true, Gem::CIDetector.ci?) | ||
end | ||
end | ||
|
||
def test_ci_strings | ||
with_env("FOO" => "bar") { assert_empty(Gem::CIDetector.ci_strings) } | ||
with_env("TRAVIS" => "true") { assert_equal(["travis"], Gem::CIDetector.ci_strings) } | ||
with_env("CI" => "true", "CIRCLECI" => "true", "GITHUB_ACTIONS" => "true") do | ||
assert_equal(["ci", "circle", "github"], Gem::CIDetector.ci_strings) | ||
end | ||
with_env("CI" => "true", "CI_NAME" => "MYCI") do | ||
assert_equal(["ci", "myci"], Gem::CIDetector.ci_strings) | ||
end | ||
with_env("GITHUB_ACTIONS" => "true", "CI_NAME" => "github") do | ||
assert_equal(["github"], Gem::CIDetector.ci_strings) | ||
end | ||
with_env("TASKCLUSTER_ROOT_URL" => "https://foo.bar", "DSARI" => "1", "CI_NAME" => "") do | ||
assert_equal(["dsari", "taskcluster"], Gem::CIDetector.ci_strings) | ||
end | ||
end | ||
|
||
private | ||
|
||
def with_env(overrides, &block) | ||
@orig_env = ENV.to_h | ||
ENV.replace(overrides) | ||
begin | ||
block.call | ||
ensure | ||
ENV.replace(@orig_env) | ||
end | ||
end | ||
end |