├── .github └── CODEOWNERS ├── .circleci └── config.yml ├── manifest ├── variants ├── node.Dockerfile.template └── browsers.Dockerfile.template ├── README.md ├── LICENSE ├── release.sh └── gen-dockerfiles.sh /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Automatically request PR reviews from the CPE Team as a whole and Ricardo specifically 2 | * @CircleCI-Public/cpeng @felicianotech 3 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | shell: circleci/shellcheck@3.1.1 5 | 6 | workflows: 7 | lint-scripts: 8 | jobs: 9 | - shell/check: 10 | exclude: "SC1091,SC2128,SC2145,SC2154" 11 | -------------------------------------------------------------------------------- /manifest: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This file exists solely to get `cimg-shared` to pass shellcheck. 4 | # It's not actually used for real image builds. 5 | 6 | repository=nothing 7 | parent=nothing 8 | variants=(nothing) 9 | namespace=nothing 10 | parentTags=(nothing) 11 | defaultParentTag=nothing 12 | parentSlug=nothing -------------------------------------------------------------------------------- /variants/node.Dockerfile.template: -------------------------------------------------------------------------------- 1 | # vim:set ft=dockerfile: 2 | 3 | FROM %%NAMESPACE%%/%%PARENT%%:%%PARENT_TAG%% 4 | 5 | LABEL maintainer="Community & Partner Engineering Team " 6 | 7 | # Dockerfile will pull the latest LTS release from cimg-node. 8 | RUN curl -sSL "https://raw.githubusercontent.com/CircleCI-Public/cimg-node/main/ALIASES" -o nodeAliases.txt && \ 9 | NODE_VERSION=$(grep "lts" ./nodeAliases.txt | cut -d "=" -f 2-) && \ 10 | curl -L -o node.tar.xz "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz" && \ 11 | sudo tar -xJf node.tar.xz -C /usr/local --strip-components=1 && \ 12 | rm node.tar.xz nodeAliases.txt && \ 13 | sudo ln -s /usr/local/bin/node /usr/local/bin/nodejs 14 | 15 | ENV YARN_VERSION 1.22.18 16 | RUN curl -L -o yarn.tar.gz "https://yarnpkg.com/downloads/${YARN_VERSION}/yarn-v${YARN_VERSION}.tar.gz" && \ 17 | sudo tar -xzf yarn.tar.gz -C /opt/ && \ 18 | rm yarn.tar.gz && \ 19 | sudo ln -s /opt/yarn-v${YARN_VERSION}/bin/yarn /usr/local/bin/yarn && \ 20 | sudo ln -s /opt/yarn-v${YARN_VERSION}/bin/yarnpkg /usr/local/bin/yarnpkg 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `cimg` Shared Scripts [![GitHub License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/CircleCI-Public/cimg-shared/main/LICENSE)[![CircleCI Build Status](https://circleci.com/gh/CircleCI-Public/cimg-shared.svg?style=shield "CircleCI Build Status")](https://circleci.com/gh/CircleCI-Public/cimg-shared) [![GitHub License](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://raw.githubusercontent.com/CircleCI-Public/cimg-base/main/LICENSE) [![CircleCI Community](https://img.shields.io/badge/community-CircleCI%20Discuss-343434.svg)](https://discuss.circleci.com/c/ecosystem/images) 2 | 3 | This repository contains shared resources used by CircleCI's new deterministic convenience images pilot project. 4 | 5 | ## Overview 6 | 7 | See [the `circleci-cimg` topic tag](https://github.com/search?q=topic%3Acircleci-cimg) for all repositories associated with new convenience images work. 8 | 9 | ## Contributing 10 | 11 | We welcome [issues](https://github.com/CircleCI-Public/cimg-shared/issues) submitted to and [pull requests](https://github.com/CircleCI-Public/cimg-shared/pulls) opened against this repository! 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Circle Internet Services, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Much of the version processing logic is repeated from gen-dockerfiles.sh 4 | # This should be de-duped sometime in the future when this logic is solidified 5 | versions=() 6 | 7 | for versionGroup in "$@"; do 8 | 9 | # Process the version group(s) that were passed to this script. 10 | if [[ "$versionGroup" == *"#"* ]]; then 11 | vgParam1=$(cut -d "#" -f2- <<< "$versionGroup") 12 | versionGroup="${versionGroup//$vgParam1}" 13 | versionGroup="${versionGroup//\#}" 14 | fi 15 | 16 | if [[ "$versionGroup" == *"="* ]]; then 17 | vgAlias1=$(cut -d "=" -f2- <<< "$versionGroup") 18 | versionGroup="${versionGroup//$vgAlias1}" 19 | versionGroup="${versionGroup//=}" 20 | fi 21 | 22 | vgVersion=$(cut -d "v" -f2- <<< "$versionGroup") 23 | versions+=( "$vgVersion" ) 24 | done 25 | 26 | branchName="" 27 | commitMSG="" 28 | 29 | if [[ ${#versions[@]} == 0 ]]; then 30 | echo "Error, no versions detected." 31 | exit 1 32 | elif [[ ${#versions[@]} == 1 ]]; then 33 | branchName="release-v${versions[0]}" 34 | commitMSG="Publish v${versions[0]}. [release]" 35 | elif [[ ${#versions[@]} == 2 ]]; then 36 | branchName="release-v${versions[0]}-and-v${versions[1]}" 37 | commitMSG="Publish v${versions[0]} and v${versions[1]}. [release]" 38 | elif [[ ${#versions[@]} == 3 ]]; then 39 | branchName="release-v${versions[0]}-v${versions[1]}-and-more" 40 | commitMSG="Publish v${versions[0]}, v${versions[1]}, and v${versions[2]}. [release]" 41 | elif [[ ${#versions[@]} == 4 ]]; then 42 | branchName="release-v${versions[0]}-v${versions[1]}-and-more" 43 | commitMSG="Pub: v${versions[0]}, v${versions[1]}, v${versions[2]}, and more. [release]" 44 | elif [[ ${#versions[@]} -gt 4 ]]; then 45 | branchName="release-v${versions[0]}-v${versions[1]}-and-more" 46 | commitMSG="Pub: ${versions[0]},${versions[1]},${versions[2]},${versions[3]}, and more. [release]" 47 | fi 48 | 49 | defaultBranch=$(git remote show origin | grep 'HEAD branch' | cut -d' ' -f5) 50 | 51 | git checkout -b "${branchName}" "${defaultBranch}" 52 | shared/gen-dockerfiles.sh "$@" 53 | git add . 54 | git commit -m "${commitMSG}" 55 | git push -u origin "${branchName}" 56 | -------------------------------------------------------------------------------- /variants/browsers.Dockerfile.template: -------------------------------------------------------------------------------- 1 | # vim:set ft=dockerfile: 2 | 3 | FROM %%NAMESPACE%%/%%PARENT%%:%%PARENT_TAG%%-node 4 | 5 | LABEL maintainer="CircleCI Community & Partner Engineering Team " 6 | 7 | # Install Selenium 8 | ENV SELENIUM_VER=3.141.59 9 | RUN curl -sSL -o selenium-server-standalone-${SELENIUM_VER}.jar "https://selenium-release.storage.googleapis.com/${SELENIUM_VER%.*}/selenium-server-standalone-${SELENIUM_VER}.jar" && \ 10 | sudo cp selenium-server-standalone-${SELENIUM_VER}.jar /usr/local/bin/selenium.jar && \ 11 | rm selenium-server-standalone-${SELENIUM_VER}.jar 12 | 13 | RUN sudo apt-get update && \ 14 | 15 | # Install Java only if it's not already available 16 | # Java is installed for Selenium 17 | if ! command -v java > /dev/null; then \ 18 | echo "Java not found in parent image, installing..." && \ 19 | sudo apt-get install -y --no-install-recommends --no-upgrade openjdk-11-jre; \ 20 | fi && \ 21 | sudo rm -rf /var/lib/apt/lists/* 22 | 23 | # Below is setup to allow xvfb to start when the container starts up. 24 | # The label in particular allows this image to override what CircleCI does 25 | # when booting the image. 26 | LABEL com.circleci.preserve-entrypoint=true 27 | ENV DISPLAY=":99" 28 | #RUN printf '#!/bin/sh\nXvfb :99 -screen 0 1280x1024x24 &\nexec "$@"\n' > /tmp/entrypoint && \ 29 | # chmod +x /tmp/entrypoint && \ 30 | # sudo mv /tmp/entrypoint /docker-entrypoint.sh 31 | RUN printf '#!/bin/sh\nXvfb :99 -screen 0 1280x1024x24 &\nexec "$@"\n' | sudo tee /docker-entrypoint.sh && \ 32 | sudo chmod +x /docker-entrypoint.sh 33 | 34 | # Install a single version of Firefox. This isn't intended to be a regularly 35 | # updated thing. Instead, if this version of Firefox isn't want the end user 36 | # wants they should install a different version via the Browser Tools Orb. 37 | # 38 | # Canonical made a major technology change in how Firefox is installed from 39 | # Ubuntu 21.10 and up. The general CI space doesn't seem to be ready for a snap 40 | # based Firefox right now so we are installing the original deb based version 41 | # from Ubuntu Focal, even if the current Ubuntu version is newer. 42 | RUN echo 'deb http://us.archive.ubuntu.com/ubuntu/ focal-updates main' | sudo tee /etc/apt/sources.list.d/firefox.list && \ 43 | echo 'Package: firefox' | sudo tee -a /etc/apt/preferences.d/firefox.pref && \ 44 | echo 'Pin: release n=focal' | sudo tee -a /etc/apt/preferences.d/firefox.pref && \ 45 | echo 'Pin-Priority: 500' | sudo tee -a /etc/apt/preferences.d/firefox.pref && \ 46 | 47 | sudo apt-get update && \ 48 | sudo apt-get install --no-install-recommends --yes firefox && \ 49 | sudo rm -rf /var/lib/apt/lists/* && \ 50 | firefox --version 51 | 52 | # Install a single version of Google Chrome Stable. This isn't intended to be a 53 | # regularly updated thing. Instead, if this version of Chrome isn't want the 54 | # end user wants they should install a different version via the Browser Tools 55 | # Orb. 56 | RUN wget -q -O - "https://dl.google.com/linux/linux_signing_key.pub" | sudo apt-key add - && \ 57 | echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee /etc/apt/sources.list.d/google-chrome.list && \ 58 | sudo apt-get update && \ 59 | sudo apt-get install google-chrome-stable && \ 60 | sudo rm -rf /var/lib/apt/lists/* 61 | 62 | ENTRYPOINT ["/docker-entrypoint.sh"] 63 | CMD ["/bin/sh"] 64 | -------------------------------------------------------------------------------- /gen-dockerfiles.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # A Docker image is a combination of REGISTRY/NAMESPACE/REPOSITORY[:TAG]. 4 | # Registry will be ignored for now unless we move off Docker Hub. 5 | # Import repo-specific image information 6 | source ./manifest 7 | tagless_image=${namespace}/${repository} 8 | 9 | # Prepare the build and push files. Originally we only needed a build file but 10 | # with modern versions of Docker, a push file became neccesary as well. 11 | echo "#!/usr/bin/env bash" > ./build-images.sh 12 | echo "# Do not edit by hand; please use build scripts/templates to make changes" >> ./build-images.sh 13 | chmod +x ./build-images.sh 14 | echo "" >> ./build-images.sh 15 | 16 | echo "#!/usr/bin/env bash" > ./push-images.sh 17 | echo "# Do not edit by hand; please use build scripts/templates to make changes" >> ./push-images.sh 18 | chmod +x ./push-images.sh 19 | 20 | export CREATE_VERSIONS=("$@") 21 | 22 | # A version can be a major.minor or major.minor.patch version string. 23 | # An alias can be passed right after the version with an equal sign (=). 24 | # An additional parameter can be passed with a hash (#) sign. 25 | # Additionally versions/version groups are separated by spaces. 26 | # 27 | # Examples: 28 | # 29 | # 1.13.1 v1.14.2 30 | # v1.13.1#sha256abcfabdbc674bcg 31 | # v13.0.1=lts 32 | # v20.04 33 | # v8.0.252=lts=https://example.com/download/item.tar-gz 34 | # 35 | # Template variables exists in the `Dockerfile.template` files. The start and 36 | # end with two percent symbles `%%`. During Dockerfile generation, they get 37 | # replaced with actual valuables. Here's what's available to use: 38 | # 39 | # %%VERSION_FULL%% - the complete version passed to the script such as `1.2.3` 40 | # %%MAIN_VERSION%% - deprecated, please use %%VERSION_FULL%% instead 41 | # %%VERSION_MAJOR%% - just the major integer of the version such as `1` 42 | # %%VERSION_MINOR%% - the major and minor integers of the version with a decimal in the middle such as `1.2` 43 | # %%ALIAS1%% - what's passed as the alias when passing version strings to the build script (see above) 44 | # %%PARAM1%% - what's passed as the paramater when passing version strings to the build script (see above) 45 | # %%MAIN_SHA%% - deprecated, please use %%PARAM1%% 46 | 47 | ##### 48 | # Helper functions. 49 | ##### 50 | 51 | # Parses all template variables, regardless of if it's a main or variant image 52 | parse_template_variables () { 53 | 54 | local variantPath=${1} 55 | local parent=${2} 56 | local fileTemplate=${3} 57 | local parentTag=${4} 58 | local directory=${5} 59 | 60 | [[ -d "$directory" ]] || mkdir "$directory" 61 | 62 | sed -e 's!%%PARENT%%!'"${parent}"'!g' "${fileTemplate}" > "./${versionShort}/${variantPath}Dockerfile" 63 | sed -i.bak 's/%%PARENT_TAG%%/'"${parentTag}"'/g' "./${versionShort}/${variantPath}Dockerfile" 64 | sed -i.bak 's/%%NAMESPACE%%/'"${namespace}"'/g' "./${versionShort}/${variantPath}Dockerfile" 65 | sed -i.bak 's/%%MAIN_VERSION%%/'"${vgVersion}"'/g' "./${versionShort}/${variantPath}Dockerfile" # will be deprecated in the future 66 | sed -i.bak 's/%%VERSION_FULL%%/'"${vgVersion}"'/g' "./${versionShort}/${variantPath}Dockerfile" 67 | sed -i.bak 's/%%VERSION_MINOR%%/'"${versionShort}"'/g' "./${versionShort}/${variantPath}Dockerfile" 68 | sed -i.bak 's/%%VERSION_MAJOR%%/'"${vgVersionMajor}"'/g' "./${vgVersionMinor}/${variantPath}Dockerfile" 69 | sed -i.bak 's!%%MAIN_SHA%%!'"$vgParam1"'!g' "./$versionShort/${variantPath}Dockerfile" # will be deprecated in the future 70 | sed -i.bak 's!%%PARAM1%%!'"${vgParam1}"'!g' "./${versionShort}/${variantPath}Dockerfile" 71 | sed -i.bak 's!%%ALIAS1%%!'"${vgAlias1}"'!g' "./${versionShort}/${variantPath}Dockerfile" 72 | } 73 | 74 | build_and_push() { 75 | local pathing=${1} 76 | local versionString=${2} 77 | local versionShortString=${3} 78 | local defaultString=${4} 79 | local defaultShortString=${5} 80 | 81 | # every version loop will generate these basic docker tags 82 | # if parentTags are enabled, then additional tags will be generated in the parentTag loop 83 | # the defaultString is referenced as the tag that should be given by default for either a parent Tag or an alias 84 | 85 | echo "docker push $tagless_image:$versionShortString" >> ./push-images-temp.sh 86 | echo "docker push $tagless_image:$versionString" >> ./push-images-temp.sh 87 | echo "docker build --file $pathing/Dockerfile -t $tagless_image:$versionString -t $tagless_image:$versionShortString ." >> ./build-images-temp.sh 88 | 89 | if [[ -n $defaultParentTag ]] && [[ "$defaultParentTag" == "$parentTag" ]]; then 90 | { 91 | echo "docker tag $tagless_image:$versionString $tagless_image:$defaultString" 92 | echo "docker tag $tagless_image:$versionShortString $tagless_image:$defaultShortString" 93 | echo "docker push $tagless_image:$defaultShortString" 94 | echo "docker push $tagless_image:$defaultString" 95 | } >> ./push-images-temp.sh 96 | fi 97 | 98 | if [[ -n $vgAlias1 ]] && [[ "$vgVersion" = "$aliasGroup" ]]; then 99 | { 100 | echo "docker tag $tagless_image:$versionString $tagless_image:$defaultString" 101 | echo "docker push $tagless_image:$defaultString" 102 | } >> ./push-images-temp.sh 103 | fi 104 | } 105 | 106 | filepath_templating () { 107 | if [[ -f "./variants/${variant}.Dockerfile.template" ]]; then 108 | fileTemplate="./variants/${variant}.Dockerfile.template" 109 | elif [[ -f "./shared/variants/${variant}.Dockerfile.template" ]]; then 110 | fileTemplate="./shared/variants/${variant}.Dockerfile.template" 111 | else 112 | echo "Error: Variant ${variant} doesn't exist. Exiting." 113 | exit 2 114 | fi 115 | } 116 | 117 | ##### 118 | # Starting version loop. 119 | ##### 120 | for versionGroup in "$@"; do 121 | 122 | # Process the version group(s) that were passed to this script. 123 | if [[ "$versionGroup" == *"#"* ]]; then 124 | vgParam1=$(cut -d "#" -f2- <<< "$versionGroup") 125 | versionGroup="${versionGroup//$vgParam1}" 126 | versionGroup="${versionGroup//\#}" 127 | fi 128 | 129 | if [[ "$versionGroup" == *"="* ]]; then 130 | vgAlias1=$(cut -d "=" -f2- <<< "$versionGroup") 131 | versionGroup="${versionGroup//$vgAlias1}" 132 | versionGroup="${versionGroup//=}" 133 | aliasGroup="${versionGroup}" 134 | fi 135 | 136 | vgVersionFull=$(cut -d "v" -f2- <<< "$versionGroup") 137 | vgVersion=$vgVersionFull # will be deprecated in the future 138 | 139 | if [[ $vgVersionFull =~ ^[0-9]+\.[0-9]+ ]]; then 140 | vgVersionMinor=${BASH_REMATCH[0]} 141 | versionShort=$vgVersionMinor # will be deprecated in the future 142 | else 143 | echo "Version matching (minor) failed." >&2 144 | exit 1 145 | fi 146 | 147 | if [[ $vgVersionFull =~ ^[0-9]+ ]]; then 148 | vgVersionMajor=${BASH_REMATCH[0]} 149 | else 150 | echo "Version matching (major) failed." >&2 151 | exit 1 152 | fi 153 | 154 | [[ -d "$versionShort" ]] || mkdir "$versionShort" 155 | 156 | # no parentTag loop; creates Dockerfiles and variants 157 | if [[ -z "${parentTags[0]}" ]]; then 158 | parse_template_variables "" "$parent" "./Dockerfile.template" "$vgVersion" "$versionShort" 159 | build_and_push "$versionShort" "$vgVersion" "$versionShort" "$vgAlias1" 160 | 161 | for variant in "${variants[@]}"; do 162 | filepath_templating 163 | parse_template_variables "$variant/" "$repository" "$fileTemplate" "$vgVersion" "$versionShort/$variant" 164 | build_and_push "$versionShort/$variant" "$vgVersion-$variant" "$versionShort-$variant" "$vgAlias1-$variant" 165 | done 166 | else 167 | 168 | # parentTag loop; one Dockerfile will be created along with however many variants there are for each parentTag 169 | for parentTag in "${parentTags[@]}"; do 170 | if [[ -n $parentTag ]]; then 171 | parse_template_variables "$parentTag/" "$parent" "./Dockerfile.template" "$parentTag" "$versionShort/$parentTag" 172 | build_and_push "$versionShort/$parentTag" "$vgVersion-$parentSlug-$parentTag" "$versionShort-$parentSlug-$parentTag" "$vgVersion" "$versionShort" 173 | 174 | for variant in "${variants[@]}"; do 175 | filepath_templating 176 | parse_template_variables "$parentTag/$variant/" "$repository" "$fileTemplate" "$vgVersion-$parentSlug-$parentTag" "$versionShort/$parentTag/$variant" 177 | build_and_push "$versionShort/$parentTag/$variant" "$vgVersion-$parentSlug-$parentTag-$variant" "$versionShort-$parentSlug-$parentTag-$variant" "$vgVersion-$variant" "$versionShort-$variant" 178 | done 179 | fi 180 | done 181 | fi 182 | # Build out the ALIASES file. Keeps track of aliases that have been set 183 | # without losing old versions. 184 | if [[ -n $vgAlias1 ]] && [[ $aliasGroup = "$versionGroup" ]]; then 185 | if [[ -f ALIASES ]]; then 186 | # Make sure the current alias isn't in the file. 187 | grep -v "${vgAlias1}" ./ALIASES > ./TEMP && mv ./TEMP ./ALIASES 188 | fi 189 | 190 | echo "${vgAlias1}=${vgVersion}" >> ALIASES 191 | fi 192 | 193 | # This .bak thing fixes a Linux/macOS compatibility issue, but the files are cleaned up 194 | find . -name \*.bak -type f -delete 195 | done 196 | 197 | if [[ -n "${CREATE_VERSIONS}" ]]; then 198 | # Make sure the current alias isn't in the file. 199 | if [[ -f GEN-CHECK ]]; then 200 | grep -v "${CREATE_VERSIONS}" ./GEN-CHECK > ./TEMP2 && mv ./TEMP2 ./GEN-CHECK 201 | fi 202 | 203 | echo "GEN_CHECK=($@)" > GEN-CHECK 204 | if [[ -f TEMP2 ]]; then 205 | rm ./TEMP2 206 | fi 207 | fi 208 | 209 | cat -n push-images-temp.sh | sort -uk2 | sort -nk1 | cut -f2- >> push-images.sh 210 | cat -n build-images-temp.sh | sort -uk2 | sort -nk1 | cut -f2- >> build-images.sh 211 | rm push-images-temp.sh build-images-temp.sh 212 | --------------------------------------------------------------------------------