From ea91c90af7a8393af92072bdd58da27d70606694 Mon Sep 17 00:00:00 2001 From: Fedor Korotkov Date: Mon, 23 Aug 2021 07:58:24 -0400 Subject: [PATCH] Document Arm Containers (#905) * Document Arm Containers * Don't forget the limits * Added new words to spelling dict * Make all the code samples to have arm64 alternative * announcement * latest * Announcing --- .spelling | 3 + docs/examples.md | 966 +++++++++++++++++++++++--------- docs/faq.md | 1 + docs/guide/docker-builder-vm.md | 2 +- docs/guide/linux.md | 71 ++- docs/guide/quick-start.md | 38 +- docs/guide/writing-tasks.md | 508 ++++++++++++----- docs/pricing.md | 23 +- mkdocs.yml | 2 + theme/overrides/home.html | 4 +- 10 files changed, 1152 insertions(+), 466 deletions(-) diff --git a/.spelling b/.spelling index d260be10..4b1c0980 100644 --- a/.spelling +++ b/.spelling @@ -74,6 +74,7 @@ Golang GolangCI Google GraphQL +Graviton2 Heroku Homebrew Hypervisor @@ -115,6 +116,8 @@ Windows XCLogParser Xcode Zero-config +amd64 +arm64 autoscaler autoscaling backend diff --git a/docs/examples.md b/docs/examples.md index 014519bf..d4c955d9 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -69,12 +69,23 @@ task: Bazel Team provides a [set of official Docker images with Bazel pre-installed](https://l.gcr.io/google/bazel). Here is an example of how `.cirrus.yml` can look like for Bazel: -```yaml -container: - image: l.gcr.io/google/bazel:latest -task: - build_script: bazel build //... -``` +=== "amd64" + + ```yaml + container: + image: l.gcr.io/google/bazel:latest + task: + build_script: bazel build //... + ``` + +=== "arm64" + + ```yaml + arm_container: + image: l.gcr.io/google/bazel:latest + task: + build_script: bazel build //... + ``` If these images are not the right fit for your project you can always use any custom Docker image with Cirrus CI. @@ -84,29 +95,57 @@ Cirrus CI has [built-in HTTP Cache](guide/writing-tasks.md#http-cache) which is Here is an example of how Cirrus CI HTTP Cache can be used with Bazel: -```yaml -container: - image: l.gcr.io/google/bazel:latest -task: - build_script: - bazel build - --spawn_strategy=sandboxed - --strategy=Javac=sandboxed - --genrule_strategy=sandboxed - --remote_http_cache=http://$CIRRUS_HTTP_CACHE_HOST - //... -``` +=== "amd64" + + ```yaml + container: + image: l.gcr.io/google/bazel:latest + task: + build_script: + bazel build + --spawn_strategy=sandboxed + --strategy=Javac=sandboxed + --genrule_strategy=sandboxed + --remote_http_cache=http://$CIRRUS_HTTP_CACHE_HOST + //... + ``` + +=== "arm64" + + ```yaml + arm_container: + image: l.gcr.io/google/bazel:latest + task: + build_script: + bazel build + --spawn_strategy=sandboxed + --strategy=Javac=sandboxed + --genrule_strategy=sandboxed + --remote_http_cache=http://$CIRRUS_HTTP_CACHE_HOST + //... + ``` ## C++ Official [GCC Docker images](https://hub.docker.com/_/gcc/) can be used for builds. Here is an example of a `.cirrus.yml` that runs tests: -```yaml -container: - image: gcc:latest -task: - tests_script: make tests -``` +=== "amd64" + + ```yaml + container: + image: gcc:latest + task: + tests_script: make tests + ``` + +=== "arm64" + + ```yaml + arm_container: + image: gcc:latest + task: + tests_script: make tests + ``` ## Crystal @@ -129,33 +168,65 @@ spec_task: Official [Elixir Docker images](https://hub.docker.com/_/elixir/) can be used for builds. Here is an example of a `.cirrus.yml` that runs tests: -```yaml -test_task: - container: - image: elixir:latest - mix_cache: - folder: deps - fingerprint_script: cat mix.lock - populate_script: mix deps.get - compile_script: mix compile - test_script: mix test -``` +=== "amd64" + + ```yaml + test_task: + container: + image: elixir:latest + mix_cache: + folder: deps + fingerprint_script: cat mix.lock + populate_script: mix deps.get + compile_script: mix compile + test_script: mix test + ``` + +=== "arm64" + + ```yaml + test_task: + arm_container: + image: elixir:latest + mix_cache: + folder: deps + fingerprint_script: cat mix.lock + populate_script: mix deps.get + compile_script: mix compile + test_script: mix test + ``` ## Erlang Official [Erlang Docker images](https://hub.docker.com/_/erlang/) can be used for builds. Here is an example of a `.cirrus.yml` that runs tests: -```yaml -test_task: - container: - image: erlang:latest - rebar3_cache: - folder: _build - fingerprint_script: cat rebar.lock - populate_script: rebar3 compile --deps_only - compile_script: rebar3 compile - test_script: rebar3 ct -``` +=== "amd64" + + ```yaml + test_task: + container: + image: erlang:latest + rebar3_cache: + folder: _build + fingerprint_script: cat rebar.lock + populate_script: rebar3 compile --deps_only + compile_script: rebar3 compile + test_script: rebar3 ct + ``` + +=== "arm64" + + ```yaml + test_task: + arm_container: + image: erlang:latest + rebar3_cache: + folder: _build + fingerprint_script: cat rebar.lock + populate_script: rebar3 compile --deps_only + compile_script: rebar3 compile + test_script: rebar3 ct + ``` ## Flutter @@ -203,18 +274,35 @@ Now you'll be able to run tests targeting web via `pub run test test -p chromium The best way to test Go projects is by using [official Go Docker images](https://hub.docker.com/_/golang/). Here is an example of how `.cirrus.yml` can look like for a project using Go Modules: -```yaml -container: - image: golang:latest +=== "amd64" -test_task: - modules_cache: - fingerprint_script: cat go.sum - folder: $GOPATH/pkg/mod - get_script: go get ./... - build_script: go build ./... - test_script: go test ./... -``` + ```yaml + container: + image: golang:latest + + test_task: + modules_cache: + fingerprint_script: cat go.sum + folder: $GOPATH/pkg/mod + get_script: go get ./... + build_script: go build ./... + test_script: go test ./... + ``` + +=== "arm64" + + ```yaml + arm_container: + image: golang:latest + + test_task: + modules_cache: + fingerprint_script: cat go.sum + folder: $GOPATH/pkg/mod + get_script: go get ./... + build_script: go build ./... + test_script: go test ./... + ``` ## GolangCI Lint @@ -222,18 +310,35 @@ We highly recommend to configure some sort of linting for your Go project. One o The Cirrus CI annotator supports providing inline reports on PRs and can parse GolangCI Lint reports. Here is an example of a *GolangCI Lint* task that you can add to your `.cirrus.yml`: -```yaml -task: - name: GolangCI Lint - container: - image: golangci/golangci-lint:latest - run_script: golangci-lint run -v --out-format json > lint-report.json - always: - golangci_artifacts: - path: lint-report.json - type: text/json - format: golangci -``` +=== "amd64" + + ```yaml + task: + name: GolangCI Lint + container: + image: golangci/golangci-lint:latest + run_script: golangci-lint run -v --out-format json > lint-report.json + always: + golangci_artifacts: + path: lint-report.json + type: text/json + format: golangci + ``` + +=== "arm64" + + ```yaml + task: + name: GolangCI Lint + arm_container: + image: golangci/golangci-lint:latest + run_script: golangci-lint run -v --out-format json > lint-report.json + always: + golangci_artifacts: + path: lint-report.json + type: text/json + format: golangci + ``` ## Gradle @@ -246,21 +351,41 @@ To preserve caches between Gradle runs, add a [cache instruction](guide/writing- The trick here is to clean up `~/.gradle/caches` folder in the very end of a build. Gradle creates some unique nondeterministic files in `~/.gradle/caches` folder on every run which makes Cirrus CI re-upload the cache *every time*. This way, you get faster builds! -```yaml -container: - image: gradle:jdk8 - -check_task: - gradle_cache: - folder: ~/.gradle/caches - check_script: gradle check - cleanup_before_cache_script: - - rm -rf ~/.gradle/caches/$GRADLE_VERSION/ - - rm -rf ~/.gradle/caches/transforms-1 - - rm -rf ~/.gradle/caches/journal-1 - - rm -rf ~/.gradle/caches/jars-3/*/buildSrc.jar - - find ~/.gradle/caches/ -name "*.lock" -type f -delete -``` +=== "amd64" + + ```yaml + container: + image: gradle:jdk11 + + check_task: + gradle_cache: + folder: ~/.gradle/caches + check_script: gradle check + cleanup_before_cache_script: + - rm -rf ~/.gradle/caches/$GRADLE_VERSION/ + - rm -rf ~/.gradle/caches/transforms-1 + - rm -rf ~/.gradle/caches/journal-1 + - rm -rf ~/.gradle/caches/jars-3/*/buildSrc.jar + - find ~/.gradle/caches/ -name "*.lock" -type f -delete + ``` + +=== "arm64" + + ```yaml + arm_container: + image: gradle:jdk11 + + check_task: + gradle_cache: + folder: ~/.gradle/caches + check_script: gradle check + cleanup_before_cache_script: + - rm -rf ~/.gradle/caches/$GRADLE_VERSION/ + - rm -rf ~/.gradle/caches/transforms-1 + - rm -rf ~/.gradle/caches/journal-1 + - rm -rf ~/.gradle/caches/jars-3/*/buildSrc.jar + - find ~/.gradle/caches/ -name "*.lock" -type f -delete + ``` ### Build Cache @@ -330,15 +455,31 @@ If it is running on a pull request, annotations will also be displayed in-line. Official [Maven Docker images](https://hub.docker.com/_/maven/) can be used for building and testing Maven projects: -```yaml -task: - name: Cirrus CI - container: - image: maven:latest - maven_cache: - folder: ~/.m2 - test_script: mvn test -B -``` +=== "amd64" + + ```yaml + container: + image: maven:latest + + task: + name: Cirrus CI + maven_cache: + folder: ~/.m2 + test_script: mvn test -B + ``` + +=== "arm64" + + ```yaml + arm_container: + image: maven:latest + + task: + name: Cirrus CI + maven_cache: + folder: ~/.m2 + test_script: mvn test -B + ``` ## MySQL @@ -346,16 +487,31 @@ The [Additional Containers feature](guide/writing-tasks.md#additional-containers MySQL image as you might be running in production for your application. Getting a running instance of the latest GA version of MySQL can used with the following six lines in your `.cirrus.yml`: -```yaml hl_lines="3 4 5 6 7 8" -container: - image: golang:latest - additional_containers: - - name: mysql - image: mysql:latest - port: 3306 - env: - MYSQL_ROOT_PASSWORD: "" -``` +=== "amd64" + + ```yaml hl_lines="3 4 5 6 7 8" + container: + image: golang:latest + additional_containers: + - name: mysql + image: mysql:latest + port: 3306 + env: + MYSQL_ROOT_PASSWORD: "" + ``` + +=== "arm64" + + ```yaml hl_lines="3 4 5 6 7 8" + arm_container: + image: golang:latest + additional_containers: + - name: mysql + image: mysql:latest + port: 3306 + env: + MYSQL_ROOT_PASSWORD: "" + ``` With the configuration above MySQL will be available on `localhost:3306`. Use empty password to login as `root` user. @@ -367,95 +523,189 @@ Official [NodeJS Docker images](https://hub.docker.com/_/node/) can be used for Here is an example of a `.cirrus.yml` that caches `node_modules` based on contents of `package-lock.json` file and runs tests: -```yaml -container: - image: node:latest +=== "amd64" -test_task: - node_modules_cache: - folder: node_modules - fingerprint_script: cat package-lock.json - populate_script: npm ci - test_script: npm test -``` + ```yaml + container: + image: node:latest + + test_task: + node_modules_cache: + folder: node_modules + fingerprint_script: cat package-lock.json + populate_script: npm ci + test_script: npm test + ``` + +=== "arm64" + + ```yaml + arm_container: + image: node:latest + + test_task: + node_modules_cache: + folder: node_modules + fingerprint_script: cat package-lock.json + populate_script: npm ci + test_script: npm test + ``` ### Yarn Here is an example of a `.cirrus.yml` that caches `node_modules` based on the contents of a `yarn.lock` file and runs tests: -```yaml -container: - image: node:latest +=== "amd64" -test_task: - node_modules_cache: - folder: node_modules - fingerprint_script: cat yarn.lock - populate_script: yarn install - test_script: yarn run test -``` + ```yaml + container: + image: node:latest + + test_task: + node_modules_cache: + folder: node_modules + fingerprint_script: cat yarn.lock + populate_script: yarn install + test_script: yarn run test + ``` + +=== "arm64" + + ```yaml + arm_container: + image: node:latest + + test_task: + node_modules_cache: + folder: node_modules + fingerprint_script: cat yarn.lock + populate_script: yarn install + test_script: yarn run test + ``` ### Yarn 2 Yarn 2 (also known as Yarn Berry), has a different package cache location (`.yarn/cache`). To run tests, it would look like this: -```yaml -container: - image: node:latest -test_task: - yarn_cache: - folder: .yarn/cache - fingerprint_script: cat yarn.lock - install_script: - - yarn set version berry - - yarn install - test_script: yarn run test -``` +=== "amd64" + + ```yaml + container: + image: node:latest + + test_task: + yarn_cache: + folder: .yarn/cache + fingerprint_script: cat yarn.lock + install_script: + - yarn set version berry + - yarn install + test_script: yarn run test + ``` + +=== "arm64" + + ```yaml + arm_container: + image: node:latest + + test_task: + yarn_cache: + folder: .yarn/cache + fingerprint_script: cat yarn.lock + install_script: + - yarn set version berry + - yarn install + test_script: yarn run test + ``` ## Python Official [Python Docker images](https://hub.docker.com/_/python/) can be used for builds. Here is an example of a `.cirrus.yml` that caches installed packages based on contents of `requirements.txt` and runs `pytest`: -```yaml -container: - image: python:slim +=== "amd64" -test_task: - pip_cache: - folder: ~/.cache/pip - fingerprint_script: echo $PYTHON_VERSION && cat requirements.txt - populate_script: pip install -r requirements.txt - test_script: pytest -``` + ```yaml + container: + image: python:slim + + test_task: + pip_cache: + folder: ~/.cache/pip + fingerprint_script: echo $PYTHON_VERSION && cat requirements.txt + populate_script: pip install -r requirements.txt + test_script: pytest + ``` + +=== "arm64" + + ```yaml + arm_container: + image: python:slim + + test_task: + pip_cache: + folder: ~/.cache/pip + fingerprint_script: echo $PYTHON_VERSION && cat requirements.txt + populate_script: pip install -r requirements.txt + test_script: pytest + ``` ### Building PyPI Packages Also using the Python Docker images, you can run tests if you are making packages for [PyPI](https://pypi.org). Here is an example `.cirrus.yml` for doing so: -```yaml -container: - image: python:slim - -build_package_test_task: - pip_cache: - folder: ~/.cache/pip - fingerprint_script: echo $PYTHON_VERSION - populate_script: python3 -m pip install --upgrade setuptools wheel - build_package_test_script: python3 setup.py sdist bdist_wheel -``` +=== "amd64" + + ```yaml + container: + image: python:slim + + build_package_test_task: + pip_cache: + folder: ~/.cache/pip + fingerprint_script: echo $PYTHON_VERSION + populate_script: python3 -m pip install --upgrade setuptools wheel + build_package_test_script: python3 setup.py sdist bdist_wheel + ``` + +=== "arm64" + + ```yaml + arm_container: + image: python:slim + + build_package_test_task: + pip_cache: + folder: ~/.cache/pip + fingerprint_script: echo $PYTHON_VERSION + populate_script: python3 -m pip install --upgrade setuptools wheel + build_package_test_script: python3 setup.py sdist bdist_wheel + ``` ### Linting You can easily set up linting with Cirrus CI and flake8, here is an example `.cirrus.yml`: -```yaml -lint_task: - container: - image: alpine/flake8:latest - script: flake8 *.py -``` +=== "amd64" + + ```yaml + lint_task: + container: + image: alpine/flake8:latest + script: flake8 *.py + ``` + +=== "arm64" + + ```yaml + lint_task: + arm_container: + image: alpine/flake8:latest + script: flake8 *.py + ``` ### `Unittest` Annotations @@ -463,22 +713,43 @@ Python Unittest reports are supported by [Cirrus CI Annotations](https://medium. This way you can see what tests are failing without leaving the pull request you are reviewing! Here is an example of a `.cirrus.yml` that produces and stores `Unittest` reports: -```yaml -unittest_task: - container: - image: python:slim - install_dependencies_script: | - pip3 install unittest_xml_reporting - run_tests_script: python3 -m xmlrunner tests - # replace 'tests' with the module, - # unittest.TestCase, or unittest.TestSuite - # that the tests are in - always: - upload_results_artifacts: - path: ./*.xml - format: junit - type: text/xml -``` +=== "amd64" + + ```yaml + unittest_task: + container: + image: python:slim + install_dependencies_script: | + pip3 install unittest_xml_reporting + run_tests_script: python3 -m xmlrunner tests + # replace 'tests' with the module, + # unittest.TestCase, or unittest.TestSuite + # that the tests are in + always: + upload_results_artifacts: + path: ./*.xml + format: junit + type: text/xml + ``` + +=== "arm64" + + ```yaml + unittest_task: + arm_container: + image: python:slim + install_dependencies_script: | + pip3 install unittest_xml_reporting + run_tests_script: python3 -m xmlrunner tests + # replace 'tests' with the module, + # unittest.TestCase, or unittest.TestSuite + # that the tests are in + always: + upload_results_artifacts: + path: ./*.xml + format: junit + type: text/xml + ``` Now you should get annotations for your test results. @@ -560,32 +831,8 @@ Official [Ruby Docker images](https://hub.docker.com/_/ruby/) can be used for bu Here is an example of a `.cirrus.yml` that caches installed gems based on Ruby version, contents of `Gemfile.lock`, and runs `rspec`: -```yaml -container: - image: ruby:latest - -rspec_task: - bundle_cache: - folder: /usr/local/bundle - fingerprint_script: - - echo $RUBY_VERSION - - cat Gemfile.lock - populate_script: bundle install - rspec_script: bundle exec rspec --format json --out rspec.json - always: - rspec_report_artifacts: - path: rspec.json - type: text/json - format: rspec -``` +=== "amd64" -??? tip "Repositories without `Gemfile.lock`" - When you are not committing `Gemfile.lock` (in Ruby gems repositories, for example) - you can run `bundle install` (or `bundle update`) in `install_script` - instead of `populate_script` in `bundle_cache`. Cirrus Agent is clever enough to re-upload - cache entry only if cached folder has been changed during task execution. - Here is an example of a `.cirrus.yml` that always runs `bundle install`: - ```yaml container: image: ruby:latest @@ -595,12 +842,78 @@ rspec_task: folder: /usr/local/bundle fingerprint_script: - echo $RUBY_VERSION - - cat Gemfile - - cat *.gemspec - install_script: bundle install # or `update` for the freshest bundle - rspec_script: bundle exec rspec + - cat Gemfile.lock + populate_script: bundle install + rspec_script: bundle exec rspec --format json --out rspec.json + always: + rspec_report_artifacts: + path: rspec.json + type: text/json + format: rspec ``` +=== "arm64" + + ```yaml + arm_container: + image: ruby:latest + + rspec_task: + bundle_cache: + folder: /usr/local/bundle + fingerprint_script: + - echo $RUBY_VERSION + - cat Gemfile.lock + populate_script: bundle install + rspec_script: bundle exec rspec --format json --out rspec.json + always: + rspec_report_artifacts: + path: rspec.json + type: text/json + format: rspec + ``` + +??? tip "Repositories without `Gemfile.lock`" + When you are not committing `Gemfile.lock` (in Ruby gems repositories, for example) + you can run `bundle install` (or `bundle update`) in `install_script` + instead of `populate_script` in `bundle_cache`. Cirrus Agent is clever enough to re-upload + cache entry only if cached folder has been changed during task execution. + Here is an example of a `.cirrus.yml` that always runs `bundle install`: + + === "amd64" + + ```yaml + container: + image: ruby:latest + + rspec_task: + bundle_cache: + folder: /usr/local/bundle + fingerprint_script: + - echo $RUBY_VERSION + - cat Gemfile + - cat *.gemspec + install_script: bundle install # or `update` for the freshest bundle + rspec_script: bundle exec rspec + ``` + + === "arm64" + + ```yaml + arm_container: + image: ruby:latest + + rspec_task: + bundle_cache: + folder: /usr/local/bundle + fingerprint_script: + - echo $RUBY_VERSION + - cat Gemfile + - cat *.gemspec + install_script: bundle install # or `update` for the freshest bundle + rspec_script: bundle exec rspec + ``` + !!! tip "Test Parallelization" It's super easy to add intelligent test splitting by using [Knapsack Pro](https://knapsackpro.com/) and [matrix modification](guide/writing-tasks.md#matrix-modification). After [setting up Knapsack Pro gem](https://docs.knapsackpro.com/knapsack_pro-ruby/guide/), you can add sharding like this: @@ -628,70 +941,140 @@ Cirrus CI natively supports [RSpec](https://rspec.info/) and [RuboCop](https://r To get behavior-driven test annotations, generate and upload a `rspec` artifact from your lint task: -```yaml -container: - image: ruby:latest +=== "amd64" -task: - name: RSpec - bundle_cache: - folder: /usr/local/bundle - fingerprint_script: - - echo $RUBY_VERSION - - cat Gemfile.lock - populate_script: bundle install - script: bundle exec rspec --format json --out rspec.json - always: - rspec_artifacts: - path: rspec.json - type: text/json - format: rspec -``` + ```yaml + container: + image: ruby:latest + + task: + name: RSpec + bundle_cache: + folder: /usr/local/bundle + fingerprint_script: + - echo $RUBY_VERSION + - cat Gemfile.lock + populate_script: bundle install + script: bundle exec rspec --format json --out rspec.json + always: + rspec_artifacts: + path: rspec.json + type: text/json + format: rspec + ``` + +=== "arm64" + + ```yaml + arm_container: + image: ruby:latest + + task: + name: RSpec + bundle_cache: + folder: /usr/local/bundle + fingerprint_script: + - echo $RUBY_VERSION + - cat Gemfile.lock + populate_script: bundle install + script: bundle exec rspec --format json --out rspec.json + always: + rspec_artifacts: + path: rspec.json + type: text/json + format: rspec + ``` Generate a `rubocop` artifact to quickly gain context for linter/formatter annotations: -```yaml -container: - image: ruby:latest +=== "amd64" -task: - name: RuboCop - bundle_cache: - folder: /usr/local/bundle - fingerprint_script: - - echo $RUBY_VERSION - - cat Gemfile.lock - populate_script: bundle install - script: bundle exec rubocop --format json --out rubocop.json - always: - rubocop_artifacts: - path: rubocop.json - type: text/json - format: rubocop -``` + ```yaml + container: + image: ruby:latest + + task: + name: RuboCop + bundle_cache: + folder: /usr/local/bundle + fingerprint_script: + - echo $RUBY_VERSION + - cat Gemfile.lock + populate_script: bundle install + script: bundle exec rubocop --format json --out rubocop.json + always: + rubocop_artifacts: + path: rubocop.json + type: text/json + format: rubocop + ``` + +=== "arm64" + + ```yaml + arm_container: + image: ruby:latest + + task: + name: RuboCop + bundle_cache: + folder: /usr/local/bundle + fingerprint_script: + - echo $RUBY_VERSION + - cat Gemfile.lock + populate_script: bundle install + script: bundle exec rubocop --format json --out rubocop.json + always: + rubocop_artifacts: + path: rubocop.json + type: text/json + format: rubocop + ``` ## Rust Official [Rust Docker images](https://hub.docker.com/_/rust/) can be used for builds. Here is a basic example of `.cirrus.yml` that caches crates in `$CARGO_HOME` based on contents of `Cargo.lock`: -```yaml -container: - image: rust:latest +=== "amd64" -test_task: - registry_cache: - folder: $CARGO_HOME/registry - fingerprint_script: cat Cargo.lock - target_cache: - folder: target - fingerprint_script: - - rustc --version - - cat Cargo.lock - build_script: cargo build - test_script: cargo test - before_cache_script: rm -rf $CARGO_HOME/registry/index -``` + ```yaml + container: + image: rust:latest + + test_task: + registry_cache: + folder: $CARGO_HOME/registry + fingerprint_script: cat Cargo.lock + target_cache: + folder: target + fingerprint_script: + - rustc --version + - cat Cargo.lock + build_script: cargo build + test_script: cargo test + before_cache_script: rm -rf $CARGO_HOME/registry/index + ``` + +=== "arm64" + + ```yaml + arm_container: + image: rust:latest + + test_task: + registry_cache: + folder: $CARGO_HOME/registry + fingerprint_script: cat Cargo.lock + target_cache: + folder: target + fingerprint_script: + - rustc --version + - cat Cargo.lock + build_script: cargo build + test_script: cargo test + before_cache_script: rm -rf $CARGO_HOME/registry/index + ``` !!! tip "Caching Cleanup" @@ -704,26 +1087,51 @@ test_task: It is possible to use nightly builds of Rust via an [official `rustlang/rust:nightly` container](https://hub.docker.com/r/rustlang/rust/). Here is an example of a `.cirrus.yml` to run tests against the latest stable and nightly versions of Rust: -```yaml -test_task: - matrix: - - container: - image: rust:latest - - allow_failures: true - container: - image: rustlang/rust:nightly - registry_cache: - folder: $CARGO_HOME/registry - fingerprint_script: cat Cargo.lock - target_cache: - folder: target - fingerprint_script: - - rustc --version - - cat Cargo.lock - build_script: cargo build - test_script: cargo test - before_cache_script: rm -rf $CARGO_HOME/registry/index -``` +=== "amd64" + + ```yaml + test_task: + matrix: + - container: + image: rust:latest + - allow_failures: true + container: + image: rustlang/rust:nightly + registry_cache: + folder: $CARGO_HOME/registry + fingerprint_script: cat Cargo.lock + target_cache: + folder: target + fingerprint_script: + - rustc --version + - cat Cargo.lock + build_script: cargo build + test_script: cargo test + before_cache_script: rm -rf $CARGO_HOME/registry/index + ``` + +=== "arm64" + + ```yaml + test_task: + matrix: + - arm_container: + image: rust:latest + - allow_failures: true + arm_container: + image: rustlang/rust:nightly + registry_cache: + folder: $CARGO_HOME/registry + fingerprint_script: cat Cargo.lock + target_cache: + folder: target + fingerprint_script: + - rustc --version + - cat Cargo.lock + build_script: cargo build + test_script: cargo test + before_cache_script: rm -rf $CARGO_HOME/registry/index + ``` ??? tip "FreeBSD Caveats" diff --git a/docs/faq.md b/docs/faq.md index 740b2f5b..b4103aae 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -28,6 +28,7 @@ Cirrus CI has the following limitations on how many CPUs for different platforms for public repositories for free: * 16.0 CPUs for Linux platform (Containers or VMs). +* 16.0 CPUs for Arm Linux platform (Containers). * 8.0 CPUs for Windows platform (Containers or VMs) * 8.0 CPUs for FreeBSD VMs. * 12.0 CPUs macOS VM (1 VM). diff --git a/docs/guide/docker-builder-vm.md b/docs/guide/docker-builder-vm.md index 804dc819..7c37b8d0 100644 --- a/docs/guide/docker-builder-vm.md +++ b/docs/guide/docker-builder-vm.md @@ -95,7 +95,7 @@ docker build --cache-from myrepo/foo:latest \ ### Dockerfile as a CI environment With Docker Builder there is no need to build and push custom containers so they can be used as an environment to run CI tasks in. -Cirrus CI can do it for you! Just declare a path to a `Dockerfile` with the `dockerfile` field for you container +Cirrus CI can do it for you! Just declare a path to a `Dockerfile` with the `dockerfile` field for your `container` (`arm_container`s are not supported yet) declaration in your `.cirrus.yml` like this: ```yaml diff --git a/docs/guide/linux.md b/docs/guide/linux.md index d366be48..f50f6a85 100644 --- a/docs/guide/linux.md +++ b/docs/guide/linux.md @@ -1,22 +1,41 @@ ## Linux Containers -Linux Community Cluster is a [Kubernetes](https://kubernetes.io/) cluster running on [Google Kubernetes Engine](supported-computing-services.md#google-kubernetes-engine) -that is available free of charge for Open Source community. Paying customers can also use Community Cluster for -personal private repositories or buy CPU time with [compute credits](../pricing.md#compute-credits) for their private organization repositories. +Cirrus CI supports `container` and `arm_container` instances in order to run your CI workloads on `amd64` and `arm64` +platforms respectively. Cirrus CI uses Kubernetes clusters running in different clouds that are the most suitable for +running each platform: -Community Cluster is configured the same way as anyone can configure a personal GKE cluster as [described here](supported-computing-services.md#google-kubernetes-engine). +* For `container` instances Cirrus CI uses a GKE cluster of compute-optimized instances running in Google Cloud. +* For `arm_container` instances Cirrus CI uses a EKS cluster of Graviton2 instances running in AWS. -By default, a container is given 2 CPUs and 4 GB of memory but it can be configured in `.cirrus.yml`: +Community Clusters are configured the same way as anyone can configure a private Kubernetes cluster for their own +repository. Cirrus CI supports connecting managed Kubernetes clusters from most of the cloud providers. Please check out +all the [supported computing services](supported-computing-services.md) Cirrus CI can integrate with. -```yaml -container: - image: openjdk:8-jdk - cpu: 4 - memory: 12G +By default, a container is given 2 CPUs and 4 GB of memory, but it can be configured in `.cirrus.yml`: -task: - script: ... -``` +=== "amd64" + + ```yaml + container: + image: openjdk:latest + cpu: 4 + memory: 12G + + task: + script: ... + ``` + +=== "arm64" + + ```yaml + arm_container: + image: openjdk:latest + cpu: 4 + memory: 12G + + task: + script: ... + ``` Containers on Community Cluster can use maximum 8.0 CPUs and up to 32 GB of memory. Memory limit is tight to the amount of CPUs requested. For each CPU you can't get more than 4G of memory. @@ -33,14 +52,26 @@ Tasks using [Compute Credits](../pricing.md#compute-credits) has higher limits a ??? info "Using in-memory disks" Some I/O intensive tasks may benefit from using a `tmpfs` disk mounted as a working directory. Set `use_in_memory_disk` flag to enable in-memory disk for a container: + + === "amd64" - ```yaml - task: - name: Much I/O - container: - image: alpine:latest - use_in_memory_disk: true - ``` + ```yaml + task: + name: Much I/O + container: + image: alpine:latest + use_in_memory_disk: true + ``` + + === "arm64" + + ```yaml + task: + name: Much I/O + arm_container: + image: alpine:latest + use_in_memory_disk: true + ``` **Note**: any files you write including cloned repository will count against your task's memory limit. diff --git a/docs/guide/quick-start.md b/docs/guide/quick-start.md index 6caf4e16..01f03119 100644 --- a/docs/guide/quick-start.md +++ b/docs/guide/quick-start.md @@ -26,17 +26,33 @@ The `.cirrus.yml` defines tasks that will be executed for every build for the re For a Node.js project, your `.cirrus.yml` could look like: -```yaml -container: - image: node:latest - -check_task: - node_modules_cache: - folder: node_modules - fingerprint_script: cat yarn.lock - populate_script: yarn install - test_script: yarn test -``` +=== "amd64" + + ```yaml + container: + image: node:latest + + check_task: + node_modules_cache: + folder: node_modules + fingerprint_script: cat yarn.lock + populate_script: yarn install + test_script: yarn test + ``` + +=== "arm64" + + ```yaml + arm_container: + image: node:latest + + check_task: + node_modules_cache: + folder: node_modules + fingerprint_script: cat yarn.lock + populate_script: yarn install + test_script: yarn test + ``` That's all! After pushing a `.cirrus.yml` a build with all the tasks defined in the `.cirrus.yml` file will be created. diff --git a/docs/guide/writing-tasks.md b/docs/guide/writing-tasks.md index 94c5cd77..e2b56041 100644 --- a/docs/guide/writing-tasks.md +++ b/docs/guide/writing-tasks.md @@ -1,53 +1,104 @@ A `task` defines a sequence of [instructions](#supported-instructions) to execute and an [execution environment](#execution-environment) to execute these instructions in. Let's see a line-by-line example of a `.cirrus.yml` configuration file first: -```yaml -test_task: - container: - image: gradle:jdk11 - test_script: gradle test -``` +=== "amd64" + + ```yaml + test_task: + container: + image: openjdk:latest + test_script: ./gradlew test + ``` + +=== "arm64" + + ```yaml + test_task: + arm_container: + image: openjdk:latest + test_script: ./gradlew test + ``` The example above defines a single task that will be scheduled and executed on the [Linux Community Cluster](linux.md) using the `gradle:jdk11` Docker image. Only one user-defined [script instruction](#script-instruction) to run `gradle test` will be executed. Not that complex, right? Please read the topics below if you want better understand what's doing on in a more complex `.cirrus.yml` configuration file, such as this: -``` {: .yaml .annotate } -task: - container: - image: node:latest # (1) - - node_modules_cache: # (2) - folder: node_modules - fingerprint_script: cat yarn.lock - populate_script: yarn install +=== "amd64" - matrix: # (3) - - name: Lint - skip: !changesInclude('.cirrus.yml', '**.{js,ts}') # (4) - lint_script: yarn run lint - - name: Test + ``` {: .yaml .annotate } + task: container: - matrix: # (5) - - image: node:latest - - image: node:lts - test_script: yarn run test - - name: Publish - depends_on: - - Lint - - Test - only_if: $BRANCH == "master" # (6) - publish_script: yarn run publish -``` + image: node:latest # (1) + + node_modules_cache: # (2) + folder: node_modules + fingerprint_script: cat yarn.lock + populate_script: yarn install + + matrix: # (3) + - name: Lint + skip: !changesInclude('.cirrus.yml', '**.{js,ts}') # (4) + lint_script: yarn run lint + - name: Test + container: + matrix: # (5) + - image: node:latest + - image: node:lts + test_script: yarn run test + - name: Publish + depends_on: + - Lint + - Test + only_if: $BRANCH == "master" # (6) + publish_script: yarn run publish + ``` + + 1. Use any Docker image from public or [private](linux.md#working-with-private-registries) registries + 2. Use [cache instruction](#cache-instruction) to persist folders based on an arbitrary `fingerprint_script`. + 3. Use [`matrix` modification](#matrix-modification) to produce many similar tasks. + 4. See what kind of files were changes and skip tasks that are not applicable. + See [`changesInclude`](#supported-functions) and [`changesIncludeOnly`](#supported-functions) documentation for details. + 5. Use nested [`matrix` modification](#matrix-modification) to produce even more tasks. + 6. Completely exclude tasks from execution graph by [any custom condition](#conditional-task-execution). + +=== "arm64" + + ``` {: .yaml .annotate } + task: + arm_container: + image: node:latest # (1) + + node_modules_cache: # (2) + folder: node_modules + fingerprint_script: cat yarn.lock + populate_script: yarn install + + matrix: # (3) + - name: Lint + skip: !changesInclude('.cirrus.yml', '**.{js,ts}') # (4) + lint_script: yarn run lint + - name: Test + arm_container: + matrix: # (5) + - image: node:latest + - image: node:lts + test_script: yarn run test + - name: Publish + depends_on: + - Lint + - Test + only_if: $BRANCH == "master" # (6) + publish_script: yarn run publish + ``` -1. Use any Docker image from public or [private](linux.md#working-with-private-registries) registries -2. Use [cache instruction](#cache-instruction) to persist folders based on an arbitrary `fingerprint_script`. -3. Use [`matrix` modification](#matrix-modification) to produce many similar tasks. -4. See what kind of files were changes and skip tasks that are not applicable. - See [`changesInclude`](#supported-functions) and [`changesIncludeOnly`](#supported-functions) documentation for details. -5. Use nested [`matrix` modification](#matrix-modification) to produce even more tasks. -6. Completely exclude tasks from execution graph by [any custom condition](#conditional-task-execution). + 1. Use any Docker image from public or [private](linux.md#working-with-private-registries) registries + 2. Use [cache instruction](#cache-instruction) to persist folders based on an arbitrary `fingerprint_script`. + 3. Use [`matrix` modification](#matrix-modification) to produce many similar tasks. + 4. See what kind of files were changes and skip tasks that are not applicable. + See [`changesInclude`](#supported-functions) and [`changesIncludeOnly`](#supported-functions) documentation for details. + 5. Use nested [`matrix` modification](#matrix-modification) to produce even more tasks. + 6. Completely exclude tasks from execution graph by [any custom condition](#conditional-task-execution). !!! tip "Task Naming" To name a task one can use the `name` field. `foo_task` syntax is a syntactic sugar. Separate name @@ -73,6 +124,7 @@ following fields for a `task`: Field Name | Managed by | Description -------------------------- | ---------- | ----------------------- `container` | **us** | [Linux Docker Container][container] +`arm_container` | **us** | [Linux Arm Docker Container][container] `windows_container` | **us** | [Windows Docker Container][windows_container] `docker_builder` | **us** | [Full-fledged VM pre-configured for running Docker][docker_builder] `macos_instance` | **us** | [macOS Virtual Machines][macos_instance] @@ -156,21 +208,41 @@ A `cache` instruction allows to persist a folder and reuse it during the next ex Here is an example: -```yaml -test_task: - container: - image: node:latest - node_modules_cache: - folder: node_modules - reupload_on_changes: false # since there is a fingerprint script - fingerprint_script: - - echo $CIRRUS_OS - - node --version - - cat package-lock.json - populate_script: - - npm install - test_script: npm run test -``` +=== "amd64" + + ```yaml + test_task: + container: + image: node:latest + node_modules_cache: + folder: node_modules + reupload_on_changes: false # since there is a fingerprint script + fingerprint_script: + - echo $CIRRUS_OS + - node --version + - cat package-lock.json + populate_script: + - npm install + test_script: npm run test + ``` + +=== "arm64" + + ```yaml + test_task: + arm_container: + image: node:latest + node_modules_cache: + folder: node_modules + reupload_on_changes: false # since there is a fingerprint script + fingerprint_script: + - echo $CIRRUS_OS + - node --version + - cat package-lock.json + populate_script: + - npm install + test_script: npm run test + ``` Either `folder` or a `folders` field (with a list of folder paths) is *required* and they tell the agent which folder paths to cache. @@ -214,16 +286,31 @@ logged under `Upload '$CACHE_NAME' cache` instructions for easier debugging of c That means the only difference between the example above and below is that `yarn install` will always be executed in the example below where in the example above only when `yarn.lock` has changes. -```yaml -test_task: - container: - image: node:latest - node_modules_cache: - folder: node_modules - fingerprint_script: cat yarn.lock - install_script: yarn install - test_script: yarn run test -``` +=== "amd64" + + ```yaml + test_task: + container: + image: node:latest + node_modules_cache: + folder: node_modules + fingerprint_script: cat yarn.lock + install_script: yarn install + test_script: yarn run test + ``` + +=== "arm64" + + ```yaml + test_task: + arm_container: + image: node:latest + node_modules_cache: + folder: node_modules + fingerprint_script: cat yarn.lock + install_script: yarn install + test_script: yarn run test + ``` !!! warning "Caching for Pull Requests" Tasks for PRs upload caches to a separate caching namespace to not interfere with caches used by other tasks. @@ -239,19 +326,37 @@ Normally caches are uploaded at the end of the task execution. However, you can To do this, use the `upload_caches` instruction, which uploads a list of caches passed to it once executed: -```yaml -test_task: - container: - image: node:latest - node_modules_cache: - folder: node_modules - upload_caches: - - node_modules - install_script: yarn install - test_script: yarn run test - pip_cache: - folder: ~/.cache/pip -``` +=== "amd64" + + ```yaml + test_task: + container: + image: node:latest + node_modules_cache: + folder: node_modules + upload_caches: + - node_modules + install_script: yarn install + test_script: yarn run test + pip_cache: + folder: ~/.cache/pip + ``` + +=== "arm64" + + ```yaml + test_task: + arm_container: + image: node:latest + node_modules_cache: + folder: node_modules + upload_caches: + - node_modules + install_script: yarn install + test_script: yarn run test + pip_cache: + folder: ~/.cache/pip + ``` Note that `pip` cache won't be uploaded in this example: using `upload_caches` disables the default behavior where all caches are automatically uploaded at the end of the task, so if you want to upload `pip` cache too, you'll have to either: @@ -557,71 +662,147 @@ from the original task by replacing the whole `matrix` YAML node with each `matr Let check an example of a `.cirrus.yml`: -```yaml -test_task: - container: - matrix: - - image: node:latest - - image: node:lts - test_script: yarn run test -``` +=== "amd64" + + ```yaml + test_task: + container: + matrix: + - image: node:latest + - image: node:lts + test_script: yarn run test + ``` + +=== "arm64" + + ```yaml + test_task: + arm_container: + matrix: + - image: node:latest + - image: node:lts + test_script: yarn run test + ``` Which will be expanded into: -```yaml -test_task: - container: - image: node:latest - test_script: yarn run test +=== "amd64" -test_task: - container: - image: node:lts - test_script: yarn run test -``` + ```yaml + test_task: + container: + image: node:latest + test_script: yarn run test + + test_task: + container: + image: node:lts + test_script: yarn run test + ``` + +=== "arm64" + + ```yaml + test_task: + arm_container: + image: node:latest + test_script: yarn run test + + test_task: + arm_container: + image: node:lts + test_script: yarn run test + ``` !!! tip The `matrix` modifier can be used multiple times within a task. The `matrix` modification makes it easy to create some pretty complex testing scenarios like this: -```yaml -task: - container: - matrix: - - image: node:latest - - image: node:lts - node_modules_cache: - folder: node_modules - fingerprint_script: - - node --version - - cat yarn.lock - populate_script: yarn install - matrix: - - name: Build - build_script: yarn build - - name: Test - test_script: yarn run test -``` +=== "amd64" + + ```yaml + task: + container: + matrix: + - image: node:latest + - image: node:lts + node_modules_cache: + folder: node_modules + fingerprint_script: + - node --version + - cat yarn.lock + populate_script: yarn install + matrix: + - name: Build + build_script: yarn build + - name: Test + test_script: yarn run test + ``` + +=== "arm64" + + ```yaml + task: + arm_container: + matrix: + - image: node:latest + - image: node:lts + node_modules_cache: + folder: node_modules + fingerprint_script: + - node --version + - cat yarn.lock + populate_script: yarn install + matrix: + - name: Build + build_script: yarn build + - name: Test + test_script: yarn run test + ``` ## Task Execution Dependencies Sometimes it might be very handy to execute some tasks only after successful execution of other tasks. For such cases it is possible to specify task names that a particular task depends. Use `depends_on` keyword to define dependencies: -```yaml -lint_task: - script: yarn run lint +=== "amd64" -test_task: - script: yarn run test + ```yaml + container: + image: node:latest -publish_task: - depends_on: - - test - - lint - script: yarn run publish -``` + lint_task: + script: yarn run lint + + test_task: + script: yarn run test + + publish_task: + depends_on: + - test + - lint + script: yarn run publish + ``` + +=== "arm64" + + ```yaml + arm_container: + image: node:latest + + lint_task: + script: yarn run lint + + test_task: + script: yarn run test + + publish_task: + depends_on: + - test + - lint + script: yarn run publish + ``` ??? tip "Task Names and Aliases" It is possible to specify the task's name via the `name` field. `lint_task` syntax is a syntactic sugar that will be @@ -916,18 +1097,35 @@ should have a unique `name` and specify at least Docker `image` and `port` that In the example below we use an [official MySQL Docker image](https://hub.docker.com/_/mysql/) that exposes the standard MySQL port (3306). Tests will be able to access MySQL instance via `localhost:3306`. -```yaml -container: - image: golang:latest - additional_containers: - - name: mysql - image: mysql:latest - port: 3306 - cpu: 1.0 - memory: 512Mi - env: - MYSQL_ROOT_PASSWORD: "" -``` +=== "amd64" + + ```yaml + container: + image: golang:latest + additional_containers: + - name: mysql + image: mysql:latest + port: 3306 + cpu: 1.0 + memory: 512Mi + env: + MYSQL_ROOT_PASSWORD: "" + ``` + +=== "arm64" + + ```yaml + arm_container: + image: golang:latest + additional_containers: + - name: mysql + image: mysql:latest + port: 3306 + cpu: 1.0 + memory: 512Mi + env: + MYSQL_ROOT_PASSWORD: "" + ``` Additional container can be very handy in many scenarios. Please check [Cirrus CI catalog of examples](../examples.md) for more details. @@ -952,18 +1150,34 @@ Additional container can be very handy in many scenarios. Please check [Cirrus C ??? tip "Overriding Default Command" It's also possible to override the default `CMD` of an additional container via `command` field: - - ```yaml - container: - image: golang:latest - additional_containers: - - name: mysql - image: mysql:latest - port: 7777 - command: mysqld --port 7777 - env: - MYSQL_ROOT_PASSWORD: "" - ``` + + === "amd64" + + ```yaml + container: + image: golang:latest + additional_containers: + - name: mysql + image: mysql:latest + port: 7777 + command: mysqld --port 7777 + env: + MYSQL_ROOT_PASSWORD: "" + ``` + + === "arm64" + + ```yaml + arm_container: + image: golang:latest + additional_containers: + - name: mysql + image: mysql:latest + port: 7777 + command: mysqld --port 7777 + env: + MYSQL_ROOT_PASSWORD: "" + ``` ??? warning **Note** that `additional_containers` can be used only with [Community Cluster](supported-computing-services.md#community-cluster) diff --git a/docs/pricing.md b/docs/pricing.md index 729e3e7b..5d5d63e5 100644 --- a/docs/pricing.md +++ b/docs/pricing.md @@ -66,12 +66,23 @@ https://cirrus-ci.com/settings/github/MY-ORGANIZATION Compute credits can be used with any of the following instance types: `container`, `windows_container` and `macos_instance`. No additional configuration needed. -```yaml -task: - container: - image: node:latest - ... -``` +=== "amd64" + + ```yaml + task: + container: + image: node:latest + ... + ``` + +=== "arm64" + + ```yaml + task: + arm_container: + image: node:latest + ... + ``` !!! tip "Using compute credits for public or personal private repositories" If you willing to boost Cirrus CI for public or your personal private repositories you need to explicitly mark a task to use compute credits diff --git a/mkdocs.yml b/mkdocs.yml index 9b7c6692..191a4d8d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -37,6 +37,7 @@ theme: text: Roboto code: Roboto Mono features: + - content.tabs.link - navigation.tabs - navigation.tabs.sticky - navigation.top @@ -73,6 +74,7 @@ markdown_extensions: - pymdownx.mark - pymdownx.smartsymbols - pymdownx.superfences + - pymdownx.tabbed - pymdownx.tasklist: custom_checkbox: true - pymdownx.tilde diff --git a/theme/overrides/home.html b/theme/overrides/home.html index b304895e..780bf20d 100644 --- a/theme/overrides/home.html +++ b/theme/overrides/home.html @@ -1,8 +1,8 @@ {% extends "base.html" %} {% block announce %} - - 🎉🎉🎉  Introducing Cirrus Terminal: a simple way to get SSH-like access to your tasks  ðŸŽ‰ðŸŽ‰ðŸŽ‰ + + 💪💪💪  Announcing Arm Linux Containers powered by Graviton2 processors on AWS  ðŸ’ªðŸ’ªðŸ’ª {% endblock %}