diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index f1ac244c2..211e28835 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -9,10 +9,9 @@ jobs:
steps:
- uses: actions/checkout@v1
- - name: Set up Ruby 2.7
+ - name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
- ruby-version: '2.7'
bundler-cache: true
- name: rubocop
run: |
@@ -25,7 +24,7 @@ jobs:
fail-fast: false
matrix:
ruby_version:
- - '2.7'
+ - '3.0'
services:
db:
@@ -68,7 +67,7 @@ jobs:
fail-fast: false
matrix:
ruby_version:
- - '2.7'
+ - '3.0'
services:
db:
@@ -107,9 +106,10 @@ jobs:
fail-fast: false
matrix:
ruby_version:
- - '2.7'
- '3.0'
- '3.1'
+ - '3.2'
+ - '3.3'
services:
redis:
@@ -139,8 +139,6 @@ jobs:
- uses: actions/checkout@v1
- name: Set up Ruby
uses: ruby/setup-ruby@v1
- with:
- ruby-version: '3.0'
- name: Run setup script
run: |
git config --global user.email "you@example.com"
@@ -149,7 +147,7 @@ jobs:
export SHIPIT_GEM_PATH="${PWD}"
mkdir /tmp/new-app
cd /tmp/new-app
- gem install rails -v '~> 7.0.2' --no-document
+ gem install rails -v '~> 7.1.1' --no-document
rails new shipit --skip-action-cable --skip-turbolinks --skip-action-mailer --skip-active-storage --skip-webpack-install --skip-action-mailbox --skip-action-text -m "${SHIPIT_GEM_PATH}/template.rb"
env:
SHIPIT_EDGE: "1"
diff --git a/.rubocop.yml b/.rubocop.yml
index 979b3f24a..da85c1c48 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,5 +1,5 @@
AllCops:
- TargetRubyVersion: 2.7
+ TargetRubyVersion: 3.0
Exclude:
- tmp/*
- bin/*
diff --git a/.ruby-version b/.ruby-version
new file mode 100644
index 000000000..75a22a26a
--- /dev/null
+++ b/.ruby-version
@@ -0,0 +1 @@
+3.0.3
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 61bbaf0c0..679df9436 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,26 @@
# Unreleased
+# 0.39.0
+
+* Minimum Ruby version is now Ruby 3.0
+* Upgraded to Rails 7.1.1
+* Upgraded Octokit to 5.6.1 (#1327)
+* Migrate from legacy Rails secrets to credentials (#1326)
+ * Rails secrets were [deprecated in Rails 7.1](https://github.com/rails/rails/pull/48472)
+ * [Guide on credentials](https://guides.rubyonrails.org/security.html#custom-credentials)
+* For deployments, `allow_concurrency` defaults to the same value as `force`. If wanted, it can be set separately by passing the intended value for `allow_concurrency` to `build_deploy` method
+
+# 0.38.0
+
+* Convert `commit_deployment_statuses.github_id` to bigint (#1312)
+* Allow to lock and archive stacks from the API (#1282)
+* Hide API tokens from the user interface after the initial creation (#1298)
+
+# 0.37.0
+
+* Suppress progress output for git checkout (#1278)
+* Make API refresh action match non-API refresh action for stacks (#1277)
+
# 0.36.1
* Fix compatibility with Rails 7.0.3.1 (YAML serialized fields). (#1273)
diff --git a/Gemfile b/Gemfile
index e6c52fb4c..b12bdc854 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,6 +3,7 @@ source 'https://rubygems.org'
gemspec
gem 'sqlite3'
+gem 'ejson-rails', require: 'ejson/rails/skip_secrets'
group :ci do
gem 'mysql2'
@@ -25,5 +26,4 @@ end
group :development, :test do
gem 'byebug'
gem 'pry'
- gem 'mini_racer'
end
diff --git a/Gemfile.lock b/Gemfile.lock
index 50b6deeed..f1021ac8a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
- shipit-engine (0.36.1)
+ shipit-engine (0.39.0)
active_model_serializers (~> 0.9.3)
ansi_stream (~> 0.0.6)
autoprefixer-rails (~> 6.4.1)
@@ -12,11 +12,11 @@ PATH
gemoji (~> 2.1)
jquery-rails (~> 4.4)
lodash-rails (~> 4.17)
- octokit (~> 4.20)
+ octokit (~> 5.6.0)
omniauth-github (~> 1.4)
paquito
pubsubstub (~> 0.2.0)
- rails (~> 7.0.0)
+ rails (~> 7.1.1)
rails-timeago (~> 2.13.0)
rails_autolink (~> 1.1.6)
rake
@@ -33,73 +33,82 @@ PATH
GEM
remote: https://rubygems.org/
specs:
- actioncable (7.0.3.1)
- actionpack (= 7.0.3.1)
- activesupport (= 7.0.3.1)
+ actioncable (7.1.3.4)
+ actionpack (= 7.1.3.4)
+ activesupport (= 7.1.3.4)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
- actionmailbox (7.0.3.1)
- actionpack (= 7.0.3.1)
- activejob (= 7.0.3.1)
- activerecord (= 7.0.3.1)
- activestorage (= 7.0.3.1)
- activesupport (= 7.0.3.1)
+ zeitwerk (~> 2.6)
+ actionmailbox (7.1.3.4)
+ actionpack (= 7.1.3.4)
+ activejob (= 7.1.3.4)
+ activerecord (= 7.1.3.4)
+ activestorage (= 7.1.3.4)
+ activesupport (= 7.1.3.4)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
- actionmailer (7.0.3.1)
- actionpack (= 7.0.3.1)
- actionview (= 7.0.3.1)
- activejob (= 7.0.3.1)
- activesupport (= 7.0.3.1)
+ actionmailer (7.1.3.4)
+ actionpack (= 7.1.3.4)
+ actionview (= 7.1.3.4)
+ activejob (= 7.1.3.4)
+ activesupport (= 7.1.3.4)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
- rails-dom-testing (~> 2.0)
- actionpack (7.0.3.1)
- actionview (= 7.0.3.1)
- activesupport (= 7.0.3.1)
- rack (~> 2.0, >= 2.2.0)
+ rails-dom-testing (~> 2.2)
+ actionpack (7.1.3.4)
+ actionview (= 7.1.3.4)
+ activesupport (= 7.1.3.4)
+ nokogiri (>= 1.8.5)
+ racc
+ rack (>= 2.2.4)
+ rack-session (>= 1.0.1)
rack-test (>= 0.6.3)
- rails-dom-testing (~> 2.0)
- rails-html-sanitizer (~> 1.0, >= 1.2.0)
- actiontext (7.0.3.1)
- actionpack (= 7.0.3.1)
- activerecord (= 7.0.3.1)
- activestorage (= 7.0.3.1)
- activesupport (= 7.0.3.1)
+ rails-dom-testing (~> 2.2)
+ rails-html-sanitizer (~> 1.6)
+ actiontext (7.1.3.4)
+ actionpack (= 7.1.3.4)
+ activerecord (= 7.1.3.4)
+ activestorage (= 7.1.3.4)
+ activesupport (= 7.1.3.4)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
- actionview (7.0.3.1)
- activesupport (= 7.0.3.1)
+ actionview (7.1.3.4)
+ activesupport (= 7.1.3.4)
builder (~> 3.1)
- erubi (~> 1.4)
- rails-dom-testing (~> 2.0)
- rails-html-sanitizer (~> 1.1, >= 1.2.0)
- active_model_serializers (0.9.8)
+ erubi (~> 1.11)
+ rails-dom-testing (~> 2.2)
+ rails-html-sanitizer (~> 1.6)
+ active_model_serializers (0.9.9)
activemodel (>= 3.2)
concurrent-ruby (~> 1.0)
- activejob (7.0.3.1)
- activesupport (= 7.0.3.1)
+ activejob (7.1.3.4)
+ activesupport (= 7.1.3.4)
globalid (>= 0.3.6)
- activemodel (7.0.3.1)
- activesupport (= 7.0.3.1)
- activerecord (7.0.3.1)
- activemodel (= 7.0.3.1)
- activesupport (= 7.0.3.1)
- activestorage (7.0.3.1)
- actionpack (= 7.0.3.1)
- activejob (= 7.0.3.1)
- activerecord (= 7.0.3.1)
- activesupport (= 7.0.3.1)
+ activemodel (7.1.3.4)
+ activesupport (= 7.1.3.4)
+ activerecord (7.1.3.4)
+ activemodel (= 7.1.3.4)
+ activesupport (= 7.1.3.4)
+ timeout (>= 0.4.0)
+ activestorage (7.1.3.4)
+ actionpack (= 7.1.3.4)
+ activejob (= 7.1.3.4)
+ activerecord (= 7.1.3.4)
+ activesupport (= 7.1.3.4)
marcel (~> 1.0)
- mini_mime (>= 1.1.0)
- activesupport (7.0.3.1)
+ activesupport (7.1.3.4)
+ base64
+ bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
+ connection_pool (>= 2.2.5)
+ drb
i18n (>= 1.6, < 2)
minitest (>= 5.1)
+ mutex_m
tzinfo (~> 2.0)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
@@ -111,6 +120,8 @@ GEM
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
+ base64 (0.2.0)
+ bigdecimal (3.1.8)
builder (3.2.4)
byebug (11.1.3)
coderay (1.1.3)
@@ -123,16 +134,22 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.12.2)
- concurrent-ruby (1.1.10)
+ concurrent-ruby (1.3.1)
+ connection_pool (2.4.1)
crack (0.4.5)
rexml
crass (1.0.6)
+ date (3.3.4)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
- digest (3.1.0)
docile (1.4.0)
+ drb (2.2.1)
+ ejson (1.4.1)
+ ejson-rails (0.2.1)
+ ejson
+ railties (>= 5.2)
equalizer (0.0.11)
- erubi (1.10.0)
+ erubi (1.12.0)
execjs (2.8.1)
explicit-parameters (0.4.1)
actionpack (>= 6.0)
@@ -140,7 +157,7 @@ GEM
virtus (~> 1.0)
faker (2.20.0)
i18n (>= 1.8.11, < 2)
- faraday (1.10.0)
+ faraday (1.10.3)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
@@ -155,7 +172,7 @@ GEM
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
- faraday-http-cache (2.4.0)
+ faraday-http-cache (2.5.0)
faraday (>= 0.8)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.4)
@@ -167,71 +184,68 @@ GEM
faraday-retry (1.0.3)
ffi (1.15.5)
gemoji (2.1.0)
- globalid (1.0.0)
- activesupport (>= 5.0)
+ globalid (1.2.1)
+ activesupport (>= 6.1)
hashdiff (1.0.1)
hashie (5.0.0)
- i18n (1.11.0)
+ i18n (1.14.5)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
- jquery-rails (4.5.0)
+ io-console (0.7.2)
+ irb (1.13.1)
+ rdoc (>= 4.0.0)
+ reline (>= 0.4.2)
+ jquery-rails (4.6.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
- jwt (2.4.1)
- libv8-node (16.10.0.0-arm64-darwin)
- libv8-node (16.10.0.0-x86_64-darwin)
- libv8-node (16.10.0.0-x86_64-linux)
+ jwt (2.7.1)
lodash-rails (4.17.21)
railties (>= 3.1)
- loofah (2.18.0)
+ loofah (2.22.0)
crass (~> 1.0.2)
- nokogiri (>= 1.5.9)
- mail (2.7.1)
+ nokogiri (>= 1.12.0)
+ mail (2.8.1)
mini_mime (>= 0.1.1)
- marcel (1.0.2)
+ net-imap
+ net-pop
+ net-smtp
+ marcel (1.0.4)
method_source (1.0.0)
- mini_mime (1.1.2)
- mini_racer (0.6.2)
- libv8-node (~> 16.10.0.0)
- minitest (5.16.2)
- mocha (1.13.0)
- msgpack (1.5.3)
+ mini_mime (1.1.5)
+ minitest (5.23.1)
+ mocha (2.1.0)
+ ruby2_keywords (>= 0.0.5)
+ msgpack (1.7.1)
multi_xml (0.6.0)
- multipart-post (2.2.3)
+ multipart-post (2.3.0)
+ mutex_m (0.2.0)
mysql2 (0.5.3)
- net-imap (0.2.3)
- digest
+ net-imap (0.4.12)
+ date
net-protocol
- strscan
- net-pop (0.1.1)
- digest
+ net-pop (0.1.2)
net-protocol
+ net-protocol (0.2.2)
timeout
- net-protocol (0.1.3)
- timeout
- net-smtp (0.3.1)
- digest
+ net-smtp (0.5.0)
net-protocol
- timeout
- nio4r (2.5.8)
- nokogiri (1.13.7-arm64-darwin)
+ nio4r (2.7.3)
+ nokogiri (1.16.5-arm64-darwin)
racc (~> 1.4)
- nokogiri (1.13.7-x86_64-darwin)
+ nokogiri (1.16.5-x86_64-linux)
racc (~> 1.4)
- nokogiri (1.13.7-x86_64-linux)
- racc (~> 1.4)
- oauth2 (2.0.6)
+ oauth2 (2.0.9)
faraday (>= 0.17.3, < 3.0)
jwt (>= 1.0, < 3.0)
multi_xml (~> 0.5)
- rack (>= 1.2, < 3)
- rash_alt (>= 0.4, < 1)
+ rack (>= 1.2, < 4)
+ snaky_hash (~> 2.0)
version_gem (~> 1.1)
- octokit (4.25.1)
+ octokit (5.6.1)
faraday (>= 1, < 3)
sawyer (~> 0.9)
- omniauth (1.9.1)
+ omniauth (1.9.2)
hashie (>= 3.4.6)
rack (>= 1.6.2, < 3)
omniauth-github (1.4.0)
@@ -240,7 +254,7 @@ GEM
omniauth-oauth2 (1.7.3)
oauth2 (>= 1.4, < 3)
omniauth (>= 1.9, < 3)
- paquito (0.6.1)
+ paquito (0.10.0)
msgpack (>= 1.5.2)
parallel (1.21.0)
parser (3.1.1.0)
@@ -249,57 +263,72 @@ GEM
pry (0.14.1)
coderay (~> 1.1)
method_source (~> 1.0)
+ psych (5.1.2)
+ stringio
public_suffix (4.0.6)
pubsubstub (0.2.2)
rack
redis (~> 4.0)
- racc (1.6.0)
- rack (2.2.4)
- rack-test (2.0.2)
+ racc (1.8.0)
+ rack (2.2.9)
+ rack-session (1.0.2)
+ rack (< 3)
+ rack-test (2.1.0)
rack (>= 1.3)
- rails (7.0.3.1)
- actioncable (= 7.0.3.1)
- actionmailbox (= 7.0.3.1)
- actionmailer (= 7.0.3.1)
- actionpack (= 7.0.3.1)
- actiontext (= 7.0.3.1)
- actionview (= 7.0.3.1)
- activejob (= 7.0.3.1)
- activemodel (= 7.0.3.1)
- activerecord (= 7.0.3.1)
- activestorage (= 7.0.3.1)
- activesupport (= 7.0.3.1)
+ rackup (1.0.0)
+ rack (< 3)
+ webrick
+ rails (7.1.3.4)
+ actioncable (= 7.1.3.4)
+ actionmailbox (= 7.1.3.4)
+ actionmailer (= 7.1.3.4)
+ actionpack (= 7.1.3.4)
+ actiontext (= 7.1.3.4)
+ actionview (= 7.1.3.4)
+ activejob (= 7.1.3.4)
+ activemodel (= 7.1.3.4)
+ activerecord (= 7.1.3.4)
+ activestorage (= 7.1.3.4)
+ activesupport (= 7.1.3.4)
bundler (>= 1.15.0)
- railties (= 7.0.3.1)
- rails-dom-testing (2.0.3)
- activesupport (>= 4.2.0)
+ railties (= 7.1.3.4)
+ rails-dom-testing (2.2.0)
+ activesupport (>= 5.0.0)
+ minitest
nokogiri (>= 1.6)
- rails-html-sanitizer (1.4.3)
- loofah (~> 2.3)
+ rails-html-sanitizer (1.6.0)
+ loofah (~> 2.21)
+ nokogiri (~> 1.14)
rails-timeago (2.13.0)
actionpack (>= 3.1)
activesupport (>= 3.1)
- rails_autolink (1.1.6)
- rails (> 3.1)
- railties (7.0.3.1)
- actionpack (= 7.0.3.1)
- activesupport (= 7.0.3.1)
- method_source
+ rails_autolink (1.1.8)
+ actionview (> 3.1)
+ activesupport (> 3.1)
+ railties (> 3.1)
+ railties (7.1.3.4)
+ actionpack (= 7.1.3.4)
+ activesupport (= 7.1.3.4)
+ irb
+ rackup (>= 1.0.0)
rake (>= 12.2)
- thor (~> 1.0)
- zeitwerk (~> 2.5)
+ thor (~> 1.0, >= 1.2.2)
+ zeitwerk (~> 2.6)
rainbow (3.1.1)
- rake (13.0.6)
- rash_alt (0.4.12)
- hashie (>= 3.4)
- redis (4.7.1)
+ rake (13.2.1)
+ rdoc (6.7.0)
+ psych (>= 4.0.0)
+ redis (4.8.1)
redis-objects (1.7.0)
redis
regexp_parser (2.2.1)
- responders (3.0.1)
- actionpack (>= 5.0)
- railties (>= 5.0)
- rexml (3.2.5)
+ reline (0.5.8)
+ io-console (~> 0.5)
+ responders (3.1.0)
+ actionpack (>= 5.2)
+ railties (>= 5.2)
+ rexml (3.2.8)
+ strscan (>= 3.0.9)
rubocop (1.18.3)
parallel (~> 1.10)
parser (>= 3.0.0.0)
@@ -336,6 +365,9 @@ GEM
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.4)
+ snaky_hash (2.0.1)
+ hashie
+ version_gem (~> 1.1, >= 1.1.1)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
@@ -344,7 +376,8 @@ GEM
activesupport (>= 5.2)
sprockets (>= 3.0.0)
spy (1.0.2)
- sqlite3 (1.4.2)
+ sqlite3 (1.7.3-arm64-darwin)
+ sqlite3 (1.7.3-x86_64-linux)
state_machines (0.5.0)
state_machines-activemodel (0.8.0)
activemodel (>= 5.1)
@@ -352,18 +385,19 @@ GEM
state_machines-activerecord (0.8.0)
activerecord (>= 5.1)
state_machines-activemodel (>= 0.8.0)
- strscan (3.0.3)
- thor (1.2.1)
+ stringio (3.1.0)
+ strscan (3.1.0)
+ thor (1.3.1)
thread_safe (0.3.6)
- tilt (2.0.10)
- timeout (0.3.0)
- tzinfo (2.0.4)
+ tilt (2.2.0)
+ timeout (0.4.1)
+ tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.1.0)
validate_url (1.0.15)
activemodel (>= 3.0.0)
public_suffix
- version_gem (1.1.0)
+ version_gem (1.1.3)
virtus (1.0.5)
axiom-types (~> 0.1)
coercible (~> 1.0)
@@ -373,20 +407,20 @@ GEM
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
- websocket-driver (0.7.5)
+ webrick (1.8.1)
+ websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
- zeitwerk (2.6.0)
+ zeitwerk (2.6.15)
PLATFORMS
- arm64-darwin-21
- x86_64-darwin-20
+ arm64-darwin
x86_64-linux
DEPENDENCIES
byebug
+ ejson-rails
faker
- mini_racer
mocha
mysql2
pg
@@ -400,4 +434,4 @@ DEPENDENCIES
webmock
BUNDLED WITH
- 2.3.8
+ 2.4.18
diff --git a/README.md b/README.md
index 8bde27a95..872aee5b7 100644
--- a/README.md
+++ b/README.md
@@ -25,8 +25,8 @@ This guide aims to help you [set up](#installation-and-setup), [use](#using-ship
**II. USING SHIPIT**
* [Adding stacks](#adding-stacks)
-* [Working on stacks](#working-on-stacks),
-* [Configuring stacks](#configuring-stacks).
+* [Working on stacks](#working-on-stacks)
+* [Configuring stacks](#configuring-stacks)
**III. REFERENCE**
@@ -357,6 +357,11 @@ For example:
fetch:
curl --silent https://app.example.com/services/ping/version
```
+
+**Note:** Currently, deployments in emergency mode are configured to occur concurrently via [the `build_deploy` method](https://github.com/Shopify/shipit-engine/blob/main/app/models/shipit/stack.rb),
+whose `allow_concurrency` keyword argument defaults to `force`, where `force` is true when emergency mode is enabled.
+If you'd like to separate these two from one another, override this method as desired in your service.
+
Kubernetes
**kubernetes
** allows to specify a Kubernetes namespace and context to deploy to.
@@ -610,7 +615,7 @@ review:
Shell commands timeout
-All the shell commands can take an optional `timeout` parameter to limit their duration:
+All the shell commands can take an optional `timeout` parameter. This is the value in seconds that a command can be inactive before Shipit will terminate the task.
```yml
deploy:
@@ -641,7 +646,7 @@ Your deploy scripts have access to the following environment variables:
* `GITHUB_REPO_OWNER`: The GitHub username of the repository owner for the current deploy/task.
* `EMAIL`: Email of the user that triggered the deploy/task (if available)
* `ENVIRONMENT`: The stack environment (e.g `production` / `staging`)
-* `BRANCH`: The stack branch (e.g `master`)
+* `BRANCH`: The stack branch (e.g `main`)
* `LAST_DEPLOYED_SHA`: The git SHA of the last deployed commit
* `DIFF_LINK`: URL to the diff on GitHub.
* `TASK_ID`: ID of the task that is running
diff --git a/app/controllers/shipit/api/base_controller.rb b/app/controllers/shipit/api/base_controller.rb
index b294e1374..d7df8157a 100644
--- a/app/controllers/shipit/api/base_controller.rb
+++ b/app/controllers/shipit/api/base_controller.rb
@@ -93,8 +93,8 @@ def not_found(_error)
render(status: :not_found, json: { status: '404', error: 'Not Found' })
end
- def conflict(_error)
- render(status: :conflict, json: { status: '409', error: 'Conflict' })
+ def conflict(error)
+ render(status: :conflict, json: { status: '409', error: error.message })
end
end
end
diff --git a/app/controllers/shipit/api/deploys_controller.rb b/app/controllers/shipit/api/deploys_controller.rb
index d80dedbfb..3ccb3f077 100644
--- a/app/controllers/shipit/api/deploys_controller.rb
+++ b/app/controllers/shipit/api/deploys_controller.rb
@@ -11,6 +11,7 @@ def index
params do
requires :sha, String, length: { in: 6..40 }
accepts :force, Boolean, default: false
+ accepts :allow_concurrency, Boolean
accepts :require_ci, Boolean, default: false
accepts :env, Hash, default: {}
end
@@ -18,7 +19,10 @@ def create
commit = stack.commits.by_sha(params.sha) || param_error!(:sha, 'Unknown revision')
param_error!(:force, "Can't deploy a locked stack") if !params.force && stack.locked?
param_error!(:require_ci, "Commit is not deployable") if params.require_ci && !commit.deployable?
- deploy = stack.trigger_deploy(commit, current_user, env: params.env, force: params.force)
+
+ allow_concurrency = params.allow_concurrency.nil? ? params.force : params.allow_concurrency
+ deploy = stack.trigger_deploy(commit, current_user, env: params.env, force: params.force,
+ allow_concurrency: allow_concurrency)
render_resource(deploy, status: :accepted)
end
end
diff --git a/app/controllers/shipit/api/stacks_controller.rb b/app/controllers/shipit/api/stacks_controller.rb
index 9d5b1ab14..ffcec1215 100644
--- a/app/controllers/shipit/api/stacks_controller.rb
+++ b/app/controllers/shipit/api/stacks_controller.rb
@@ -46,9 +46,13 @@ def create
accepts :ignore_ci, Boolean
accepts :merge_queue_enabled, Boolean
accepts :continuous_deployment, Boolean
+ accepts :archived, Boolean
end
def update
- stack.update(params)
+ stack.update(update_params)
+
+ update_archived
+
render_resource(stack)
end
@@ -62,6 +66,8 @@ def destroy
end
def refresh
+ RefreshStatusesJob.perform_later(stack_id: stack.id)
+ RefreshCheckRunsJob.perform_later(stack_id: stack.id)
GithubSyncJob.perform_later(stack_id: stack.id)
render_resource(stack, status: :accepted)
end
@@ -76,6 +82,26 @@ def stack
@stack ||= stacks.from_param!(params[:id])
end
+ def update_archived
+ if key?(:archived)
+ if params[:archived]
+ stack.archive!(nil)
+ elsif stack.archived?
+ stack.unarchive!
+ end
+ end
+ end
+
+ def key?(key)
+ params.to_h.key?(key)
+ end
+
+ def update_params
+ params.select do |key, _|
+ %i(environment branch deploy_url ignore_ci merge_queue_enabled continuous_deployment).include?(key)
+ end
+ end
+
def repository
@repository ||= Repository.find_or_create_by(owner: repo_owner, name: repo_name)
end
diff --git a/app/helpers/shipit/api_clients_helper.rb b/app/helpers/shipit/api_clients_helper.rb
new file mode 100644
index 000000000..1b55fc169
--- /dev/null
+++ b/app/helpers/shipit/api_clients_helper.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+module Shipit
+ module ApiClientsHelper
+ def api_client_token(api_client)
+ if api_client.created_at >= 5.minutes.ago && current_user == api_client.creator
+ api_client.authentication_token
+ else
+ "#{api_client.authentication_token[0..5]}************************"
+ end
+ end
+ end
+end
diff --git a/app/jobs/shipit/background_job.rb b/app/jobs/shipit/background_job.rb
index ee7d69e91..0426792c5 100644
--- a/app/jobs/shipit/background_job.rb
+++ b/app/jobs/shipit/background_job.rb
@@ -5,8 +5,14 @@ class << self
attr_accessor :timeout
end
+ DEFAULT_RETRY_TIME_IN_SECONDS = 30
+
# Write actions can sometimes fail intermittently, particulary for large and/or busy repositories
- retry_on(Octokit::BadGateway, Octokit::InternalServerError)
+ retry_on(Octokit::ServerError)
+
+ rescue_from(Octokit::TooManyRequests, Octokit::AbuseDetected) do |exception|
+ retry_job wait: exception.response_headers.fetch("Retry-After", DEFAULT_RETRY_TIME_IN_SECONDS).to_i.seconds
+ end
def perform(*)
with_timeout do
diff --git a/app/jobs/shipit/continuous_delivery_job.rb b/app/jobs/shipit/continuous_delivery_job.rb
index de5c1b4b3..46a292f01 100644
--- a/app/jobs/shipit/continuous_delivery_job.rb
+++ b/app/jobs/shipit/continuous_delivery_job.rb
@@ -8,7 +8,10 @@ class ContinuousDeliveryJob < BackgroundJob
def perform(stack)
return unless stack.continuous_deployment?
- return if stack.active_task?
+
+ # checks if there are any tasks running, including concurrent tasks
+ return if stack.occupied?
+
stack.trigger_continuous_delivery
end
end
diff --git a/app/jobs/shipit/create_release_statuses_job.rb b/app/jobs/shipit/create_release_statuses_job.rb
index dbd3a681f..d632eb4ef 100644
--- a/app/jobs/shipit/create_release_statuses_job.rb
+++ b/app/jobs/shipit/create_release_statuses_job.rb
@@ -4,6 +4,7 @@ class CreateReleaseStatusesJob < BackgroundJob
include BackgroundJob::Unique
queue_as :default
+ on_duplicate :drop
def perform(commit)
commit.release_statuses.to_be_created.each(&:create_status_on_github!)
diff --git a/app/models/shipit/api_client.rb b/app/models/shipit/api_client.rb
index 27459c3ec..18f5b73c7 100644
--- a/app/models/shipit/api_client.rb
+++ b/app/models/shipit/api_client.rb
@@ -8,7 +8,7 @@ class ApiClient < Record
validates :creator, :name, presence: true
- serialize :permissions, Shipit.serialized_column(:permissions, type: Array)
+ serialize :permissions, coder: Shipit.serialized_column(:permissions, type: Array)
PERMISSIONS = %w(
read:stack
write:stack
diff --git a/app/models/shipit/commit.rb b/app/models/shipit/commit.rb
index a1417cc5b..9e152b2ed 100644
--- a/app/models/shipit/commit.rb
+++ b/app/models/shipit/commit.rb
@@ -168,10 +168,26 @@ def create_status_from_github!(github_status)
end
end
- def refresh_check_runs!
+ def paginated_check_runs
response = stack.handle_github_redirections do
- stack.github_api.check_runs(github_repo_name, sha)
+ stack.github_api.check_runs(github_repo_name, sha, per_page: 100)
+ end
+
+ return response if stack.github_api.last_response.rels[:next].nil?
+
+ loop do
+ page = stack.handle_github_redirections do
+ stack.github_api.get(stack.github_api.last_response.rels[:next].href)
+ end
+ response.check_runs.concat(page.check_runs)
+ break if stack.github_api.last_response.rels[:next].nil?
end
+
+ response
+ end
+
+ def refresh_check_runs!
+ response = paginated_check_runs
response.check_runs.each do |check_run|
create_or_update_check_run_from_github!(check_run)
end
diff --git a/app/models/shipit/commit_deployment_status.rb b/app/models/shipit/commit_deployment_status.rb
index 85bde2b35..6be2693f5 100644
--- a/app/models/shipit/commit_deployment_status.rb
+++ b/app/models/shipit/commit_deployment_status.rb
@@ -48,7 +48,6 @@ def create_status_on_github(client)
client.create_deployment_status(
commit_deployment.api_url,
status,
- accept: 'application/vnd.github.flash-preview+json',
target_url: url_helpers.stack_deploy_url(stack, task),
description: description.truncate(DESCRIPTION_CHARACTER_LIMIT_ON_GITHUB),
environment_url: stack.deploy_url,
diff --git a/app/models/shipit/delivery.rb b/app/models/shipit/delivery.rb
index 141c87f8b..9f1fef9ae 100644
--- a/app/models/shipit/delivery.rb
+++ b/app/models/shipit/delivery.rb
@@ -9,7 +9,7 @@ class Delivery < Record
validates :url, presence: true, url: { no_local: true, allow_blank: true }
validates :content_type, presence: true
- serialize :response_headers, SafeJSON
+ serialize :response_headers, coder: SafeJSON
after_commit :purge_old_deliveries, on: :create
diff --git a/app/models/shipit/deploy_spec/file_system.rb b/app/models/shipit/deploy_spec/file_system.rb
index 8e3d0f84a..3708b9df3 100644
--- a/app/models/shipit/deploy_spec/file_system.rb
+++ b/app/models/shipit/deploy_spec/file_system.rb
@@ -101,11 +101,23 @@ def load_config
end
def shipit_file_names_in_priority_order
- ["#{app_name}.#{@env}.yml", "#{app_name}.yml", "shipit.#{@env}.yml", "shipit.yml"].uniq
+ [
+ "#{app_name}.#{@env}.yml",
+ ".shipit/#{app_name}.#{@env}.yml",
+
+ "#{app_name}.yml",
+ ".shipit/#{app_name}.yml",
+
+ "shipit.#{@env}.yml",
+ ".shipit/#{@env}.yml",
+
+ "shipit.yml",
+ ".shipit/shipit.yml",
+ ].uniq
end
def bare_shipit_filenames
- ["#{app_name}.yml", "shipit.yml"].uniq
+ ["#{app_name}.yml", "shipit.yml", ".shipit/#{app_name}.yml", ".shipit/shipit.yml"].uniq
end
def config_file_path
diff --git a/app/models/shipit/hook.rb b/app/models/shipit/hook.rb
index 9aa6a4e1f..5b20ddeda 100644
--- a/app/models/shipit/hook.rb
+++ b/app/models/shipit/hook.rb
@@ -87,7 +87,7 @@ def signature
validates :content_type, presence: true, inclusion: { in: CONTENT_TYPES.keys }
validates :events, presence: true, subset: { of: EVENTS }
- serialize :events, Shipit::CSVSerializer
+ serialize :events, coder: Shipit::CSVSerializer
scope :global, -> { where(stack_id: nil) }
scope :scoped_to, ->(stack) { where(stack_id: stack.id) }
diff --git a/app/models/shipit/pull_request.rb b/app/models/shipit/pull_request.rb
index 0083ad9c0..8e993af82 100644
--- a/app/models/shipit/pull_request.rb
+++ b/app/models/shipit/pull_request.rb
@@ -11,7 +11,7 @@ class PullRequest < Record
has_many :pull_request_assignments
has_many :assignees, class_name: :User, through: :pull_request_assignments, source: :user
- serialize :labels, Shipit.serialized_column(:labels, type: Array)
+ serialize :labels, coder: Shipit.serialized_column(:labels, type: Array)
after_create_commit :emit_create_hooks
after_update_commit :emit_update_hooks
diff --git a/app/models/shipit/stack.rb b/app/models/shipit/stack.rb
index 8e93a8e6b..23767f254 100644
--- a/app/models/shipit/stack.rb
+++ b/app/models/shipit/stack.rb
@@ -101,7 +101,7 @@ def sync_github_if_necessary
validates :lock_reason, length: { maximum: 4096 }
- serialize :cached_deploy_spec, DeploySpec
+ serialize :cached_deploy_spec, coder: DeploySpec
delegate(
:provisioning_handler_name,
:find_task_definition,
@@ -150,14 +150,14 @@ def trigger_task(definition_id, user, env: nil, force: false)
task
end
- def build_deploy(until_commit, user, env: nil, force: false)
+ def build_deploy(until_commit, user, env: nil, force: false, allow_concurrency: force)
since_commit = last_deployed_commit.presence || commits.first
deploys.build(
user_id: user.id,
until_commit: until_commit,
since_commit: since_commit,
env: filter_deploy_envs(env&.to_h || {}),
- allow_concurrency: force,
+ allow_concurrency: allow_concurrency,
ignored_safeties: force || !until_commit.deployable?,
max_retries: retries_on_deploy,
)
@@ -226,8 +226,12 @@ def schedule_merges
def next_commit_to_deploy
commits_to_deploy = commits.order(id: :asc).newer_than(last_deployed_commit).reachable.preload(:statuses)
- commits_to_deploy = commits_to_deploy.limit(maximum_commits_per_deploy) if maximum_commits_per_deploy
- commits_to_deploy.to_a.reverse.find(&:deployable?)
+ if maximum_commits_per_deploy
+ commits_with_max_applied = commits_to_deploy.limit(maximum_commits_per_deploy)
+ deployable_commits(commits_with_max_applied) || deployable_commits(commits_to_deploy)
+ else
+ deployable_commits(commits_to_deploy)
+ end
end
def deployed_too_recently?
@@ -453,6 +457,14 @@ def active_task
@active_task ||= tasks.current
end
+ def occupied?
+ !!occupied
+ end
+
+ def occupied
+ @occupied ||= tasks.active.last
+ end
+
def locked?
lock_reason.present?
end
@@ -620,6 +632,10 @@ def emit_lock_hooks
private
+ def deployable_commits(commits)
+ commits.to_a.reverse.find(&:deployable?)
+ end
+
def clear_cache
remove_instance_variable(:@active_task) if defined?(@active_task)
end
diff --git a/app/models/shipit/task.rb b/app/models/shipit/task.rb
index f2b6f0f05..c0baa4688 100644
--- a/app/models/shipit/task.rb
+++ b/app/models/shipit/task.rb
@@ -3,7 +3,11 @@ module Shipit
class Task < Record
include DeferredTouch
- ConcurrentTaskRunning = Class.new(StandardError)
+ class ConcurrentTaskRunning < StandardError
+ def message
+ "A task is already running."
+ end
+ end
PRESENCE_CHECK_TIMEOUT = 30
ACTIVE_STATUSES = %w(pending running aborting).freeze
@@ -54,8 +58,8 @@ def new
end
end
- serialize :definition, TaskDefinition
- serialize :env, Shipit.serialized_column(:env, coder: EnvHash)
+ serialize :definition, coder: TaskDefinition
+ serialize :env, coder: Shipit.serialized_column(:env, coder: EnvHash)
scope :success, -> { where(status: 'success') }
scope :completed, -> { where(status: COMPLETED_STATUSES) }
diff --git a/app/models/shipit/task_execution_strategy/default.rb b/app/models/shipit/task_execution_strategy/default.rb
index 72e52554d..3cd2998b9 100644
--- a/app/models/shipit/task_execution_strategy/default.rb
+++ b/app/models/shipit/task_execution_strategy/default.rb
@@ -70,7 +70,7 @@ def checkout_repository
@task.acquire_git_cache_lock do
@task.ping
unless @commands.fetched?(@task.until_commit).tap(&:run).success?
- capture!(@commands.fetch)
+ capture!(@commands.fetch_commit(@task.until_commit))
end
end
end
@@ -94,7 +94,7 @@ def capture!(command)
@task.write(line)
end
finished_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
- @task.write("pid: #{command.pid} finished in: #{finished_at - started_at} seconds\n")
+ @task.write("pid: #{command.pid} finished in: #{(finished_at - started_at).round(3)} seconds\n")
command.success?
end
diff --git a/app/models/shipit/user.rb b/app/models/shipit/user.rb
index 0e283e143..c94c29dfa 100644
--- a/app/models/shipit/user.rb
+++ b/app/models/shipit/user.rb
@@ -95,6 +95,8 @@ def refresh_from_github!
update!(github_user: Shipit.github.api.user(github_id))
rescue Octokit::NotFound
identify_renamed_user!
+ rescue Octokit::Forbidden
+ Rails.logger.info("User #{name}, github_id #{github_id} has forbidden access to their GitHub, likely deleted.")
end
def github_user=(github_user)
diff --git a/app/views/shipit/_variables.html.erb b/app/views/shipit/_variables.html.erb
index c87397154..a43dd6c93 100644
--- a/app/views/shipit/_variables.html.erb
+++ b/app/views/shipit/_variables.html.erb
@@ -9,7 +9,7 @@
<% if variable.select %>
<%= field.select variable.name, options_for_select([["Please select...", { disabled: "disabled" }]] + variable.select, variable.default || "Please select...") %>
<% else %>
- <%= field.text_field variable.name, value: variable.default %>
+ <%= field.text_field variable.name, value: params[variable.name].presence || variable.default %>
<% end %>
<%= field.label variable.name, variable.title || variable.name %>
diff --git a/app/views/shipit/api_clients/show.html.erb b/app/views/shipit/api_clients/show.html.erb
index 0bcfe40f5..3c515c90d 100644
--- a/app/views/shipit/api_clients/show.html.erb
+++ b/app/views/shipit/api_clients/show.html.erb
@@ -10,7 +10,7 @@
Authentication token:
- <%= @api_client.authentication_token %>
+ <%= api_client_token(@api_client) %>
diff --git a/app/views/shipit/merge_status/failure.html.erb b/app/views/shipit/merge_status/failure.html.erb
index dcd0a258e..2fe738881 100644
--- a/app/views/shipit/merge_status/failure.html.erb
+++ b/app/views/shipit/merge_status/failure.html.erb
@@ -15,7 +15,7 @@
<%= link_to @stack.to_param, stack_url(@stack), target: '_blank', rel: 'noopener' %>
- master branch is failing!
+ main branch is failing!
<%= render 'commit_count_warning' if display_commit_count_warning?(params[:commits].to_i) %>
diff --git a/app/views/shipit/missing_settings.html.erb b/app/views/shipit/missing_settings.html.erb
index 8c874f0c5..59acfb32f 100644
--- a/app/views/shipit/missing_settings.html.erb
+++ b/app/views/shipit/missing_settings.html.erb
@@ -22,7 +22,7 @@
Config:
- <% if Rails.application.secrets.github.present? %>
+ <% if Rails.application.credentials.github.present? %>
Success!
<% else %>
diff --git a/config/secrets.development.example.yml b/config/secrets.development.example.yml
index 90829c9f8..ef996717d 100644
--- a/config/secrets.development.example.yml
+++ b/config/secrets.development.example.yml
@@ -1,7 +1,7 @@
host: 'localhost:3000'
redis_url: 'redis://127.0.0.1:6379/0'
-# For creating an app see: https://github.com/Shopify/shipit-engine/blob/master/docs/setup.md#creating-the-github-app
+# For creating an app see: https://github.com/Shopify/shipit-engine/blob/main/docs/setup.md#creating-the-github-app
# Can be obtained there: https://github.com/settings/apps
# Set the "Authorization callback URL" as `/github/auth/github/callback`
diff --git a/config/secrets.development.shopify.yml b/config/secrets.development.shopify.yml
index f13fa971c..52e9ff6c5 100644
--- a/config/secrets.development.shopify.yml
+++ b/config/secrets.development.shopify.yml
@@ -1,7 +1,7 @@
host: 'shipit-engine.myshopify.io'
redis_url: 'redis://shipit-engine.railgun:6379'
-# For creating an app see: https://github.com/Shopify/shipit-engine/blob/master/docs/setup.md#creating-the-github-app
+# For creating an app see: https://github.com/Shopify/shipit-engine/blob/main/docs/setup.md#creating-the-github-app
github:
somegithuborg:
diff --git a/contrib/browser-extension/README.md b/contrib/browser-extension/README.md
index 642015dbe..5a4a5452f 100644
--- a/contrib/browser-extension/README.md
+++ b/contrib/browser-extension/README.md
@@ -1,8 +1,8 @@
# Shipit Browser Extension
-If you have ever pushed to a broken `master` because you forgot to check CI/Slack then we have the extension for you.
+If you have ever pushed to a broken `main` because you forgot to check CI/Slack then we have the extension for you.
-This extension will alert you on GitHub's pull request page if `master` is broken, so that you can avoid the embarassment of having merged on red master.
+This extension will alert you on GitHub's pull request page if `main` is broken, so that you can avoid the embarassment of having merged on red master.
## Installation
diff --git a/db/migrate/20230703181143_change_commit_deployment_statuses_github_id_to_big_int.rb b/db/migrate/20230703181143_change_commit_deployment_statuses_github_id_to_big_int.rb
new file mode 100644
index 000000000..a450890b3
--- /dev/null
+++ b/db/migrate/20230703181143_change_commit_deployment_statuses_github_id_to_big_int.rb
@@ -0,0 +1,5 @@
+class ChangeCommitDeploymentStatusesGithubIdToBigInt < ActiveRecord::Migration[7.0]
+ def change
+ change_column :commit_deployment_statuses, :github_id, :bigint
+ end
+end
diff --git a/dev.yml b/dev.yml
index 3327f5df7..2dfd802fa 100644
--- a/dev.yml
+++ b/dev.yml
@@ -5,10 +5,9 @@ name: shipit-engine
type: rails
up:
- - homebrew:
+ - packages:
- sqlite
- - ruby:
- version: 2.7.5
+ - ruby
- isogun
- bundler:
without: ci
diff --git a/docs/setup.md b/docs/setup.md
index 0a907a9aa..ffbfc1727 100644
--- a/docs/setup.md
+++ b/docs/setup.md
@@ -14,8 +14,8 @@ In the future we'd like to provide it fully packaged inside a Docker container,
Shipit provides you with a Rails template. To bootstrap your Shipit installation:
-1. If you don't have Rails installed, run this command: `gem install rails -v 7.0`
-2. Run this command: `rails _7.0_ new shipit --skip-action-cable --skip-turbolinks --skip-action-mailer --skip-active-storage --skip-webpack-install --skip-action-mailbox --skip-action-text -m https://raw.githubusercontent.com/Shopify/shipit-engine/master/template.rb`
+1. If you don't have Rails installed, run this command: `gem install rails -v 7.1`
+2. Run this command: `rails _7.1_ new shipit --skip-action-cable --skip-turbolinks --skip-action-mailer --skip-active-storage --skip-webpack-install --skip-action-mailbox --skip-action-text -m https://raw.githubusercontent.com/Shopify/shipit-engine/main/template.rb`
## Creating the GitHub App
diff --git a/lib/shipit.rb b/lib/shipit.rb
index 885f6b53f..8360dbb0a 100644
--- a/lib/shipit.rb
+++ b/lib/shipit.rb
@@ -291,7 +291,7 @@ def revision_file
end
def secrets
- Rails.application.secrets
+ Rails.application.credentials
end
end
diff --git a/lib/shipit/engine.rb b/lib/shipit/engine.rb
index 0d90d124c..b1279137f 100644
--- a/lib/shipit/engine.rb
+++ b/lib/shipit/engine.rb
@@ -21,7 +21,7 @@ class Engine < ::Rails::Engine
Shipit::Engine.routes.default_url_options[:host] = Shipit.host
Pubsubstub.redis_url = Shipit.redis_url.to_s
- Rails.application.secrets.deep_symbolize_keys!
+ Rails.application.credentials.deep_symbolize_keys!
app.config.assets.paths << Emoji.images_path
app.config.assets.precompile += %w(
diff --git a/lib/shipit/github_app.rb b/lib/shipit/github_app.rb
index a8ee5bfba..ae27297e0 100644
--- a/lib/shipit/github_app.rb
+++ b/lib/shipit/github_app.rb
@@ -103,7 +103,6 @@ def fetch_new_token
) do
response = new_client(bearer_token: authentication_payload).create_app_installation_access_token(
installation_id,
- accept: 'application/vnd.github.machine-man-preview+json',
)
token = Token.from_github(response)
raise AuthenticationFailed if token.blank?
diff --git a/lib/shipit/octokit_check_runs.rb b/lib/shipit/octokit_check_runs.rb
index 09d75a016..4b13172b1 100644
--- a/lib/shipit/octokit_check_runs.rb
+++ b/lib/shipit/octokit_check_runs.rb
@@ -1,9 +1,7 @@
# frozen_string_literal: true
module OctokitCheckRuns
def check_runs(repo, sha, options = {})
- paginate("#{Octokit::Repository.path(repo)}/commits/#{sha}/check-runs", options.reverse_merge(
- accept: 'application/vnd.github.antiope-preview+json',
- ))
+ paginate("#{Octokit::Repository.path(repo)}/commits/#{sha}/check-runs", options)
end
end
diff --git a/lib/shipit/octokit_iterator.rb b/lib/shipit/octokit_iterator.rb
index c6c96f0b6..3a113cb1b 100644
--- a/lib/shipit/octokit_iterator.rb
+++ b/lib/shipit/octokit_iterator.rb
@@ -16,6 +16,8 @@ def each(&block)
response = @response
loop do
+ return unless response.present?
+
response.data.each(&block)
return unless response.rels[:next]
response = response.rels[:next].get
diff --git a/lib/shipit/stack_commands.rb b/lib/shipit/stack_commands.rb
index 0e37e3315..252e93956 100644
--- a/lib/shipit/stack_commands.rb
+++ b/lib/shipit/stack_commands.rb
@@ -13,6 +13,16 @@ def env
super.merge(@stack.env)
end
+ def fetch_commit(commit)
+ create_directories
+ if valid_git_repository?(@stack.git_path)
+ git('fetch', 'origin', '--quiet', '--tags', commit.sha, env: env, chdir: @stack.git_path)
+ else
+ @stack.clear_git_cache!
+ git_clone(@stack.repo_git_url, @stack.git_path, branch: @stack.branch, env: env, chdir: @stack.deploys_path)
+ end
+ end
+
def fetch
create_directories
if valid_git_repository?(@stack.git_path)
@@ -48,12 +58,12 @@ def fetch_deployed_revision
end
def build_cacheable_deploy_spec
- with_temporary_working_directory do |dir|
+ with_temporary_working_directory(recursive: false) do |dir|
DeploySpec::FileSystem.new(dir, @stack.environment).cacheable
end
end
- def with_temporary_working_directory(commit: nil)
+ def with_temporary_working_directory(commit: nil, recursive: true)
commit ||= @stack.last_deployed_commit.presence || @stack.commits.reachable.last
if !commit || !fetched?(commit).tap(&:run).success?
@@ -64,15 +74,24 @@ def with_temporary_working_directory(commit: nil)
end
end
+ git_args = []
+ git_args << '--recursive' if recursive
Dir.mktmpdir do |dir|
git(
'clone', @stack.git_path, @stack.repo_name,
- '--recursive', '--origin', 'cache',
+ *git_args, '--origin', 'cache',
chdir: dir
).run!
git_dir = File.join(dir, @stack.repo_name)
- git('-c', 'advice.detachedHead=false', 'checkout', commit.sha, chdir: git_dir).run! if commit
+ git(
+ '-c',
+ 'advice.detachedHead=false',
+ 'checkout',
+ '--quiet',
+ commit.sha,
+ chdir: git_dir
+ ).run! if commit
yield Pathname.new(git_dir)
end
end
diff --git a/lib/shipit/task_commands.rb b/lib/shipit/task_commands.rb
index 522efe2cf..c7a011bc8 100644
--- a/lib/shipit/task_commands.rb
+++ b/lib/shipit/task_commands.rb
@@ -2,7 +2,7 @@
# rubocop:disable Lint/MissingSuper
module Shipit
class TaskCommands < Commands
- delegate :fetch, :fetched?, to: :stack_commands
+ delegate :fetch_commit, :fetch, :fetched?, to: :stack_commands
def initialize(task)
@task = task
@@ -47,7 +47,14 @@ def env
end
def checkout(commit)
- git('-c', 'advice.detachedHead=false', 'checkout', commit.sha, chdir: @task.working_directory)
+ git(
+ '-c',
+ 'advice.detachedHead=false',
+ 'checkout',
+ '--quiet',
+ commit.sha,
+ chdir: @task.working_directory
+ )
end
def clone
diff --git a/lib/shipit/version.rb b/lib/shipit/version.rb
index c3d2009a2..528579f6b 100644
--- a/lib/shipit/version.rb
+++ b/lib/shipit/version.rb
@@ -1,4 +1,4 @@
# frozen_string_literal: true
module Shipit
- VERSION = '0.36.1'
+ VERSION = '0.39.0'
end
diff --git a/script/cibuild b/script/cibuild
index 3ed1e1a50..12d7377ac 100755
--- a/script/cibuild
+++ b/script/cibuild
@@ -38,8 +38,8 @@ case $SUITE in
git config --global user.email "anonymous@example.com"
git config --global user.name "Anonymous"
- gem install rails -v 7.0
- rails _7.0_ new shipit -m "${TEMPLATE}" \
+ gem install rails -v 7.1.1
+ rails _7.1.1_ new shipit -m "${TEMPLATE}" \
--skip-action-cable \
--skip-turbolinks \
--skip-action-mailer \
diff --git a/shipit-engine.gemspec b/shipit-engine.gemspec
index 3ede910da..9eed3b91d 100644
--- a/shipit-engine.gemspec
+++ b/shipit-engine.gemspec
@@ -17,6 +17,8 @@ Gem::Specification.new do |s|
s.files = Dir["{app,config,db,lib,vendor}/**/*", "LICENSE", "Rakefile", "README.md"]
s.test_files = Dir["test/**/*"] - Dir["test/dummy/tmp/**/*"] - Dir["test/dummy/log/**/*"]
+ s.required_ruby_version = '>= 3.0.0'
+
s.add_dependency('active_model_serializers', '~> 0.9.3')
s.add_dependency('ansi_stream', '~> 0.0.6')
s.add_dependency('autoprefixer-rails', '~> 6.4.1')
@@ -27,10 +29,10 @@ Gem::Specification.new do |s|
s.add_dependency('gemoji', '~> 2.1')
s.add_dependency('jquery-rails', '~> 4.4')
s.add_dependency('lodash-rails', '~> 4.17')
- s.add_dependency('octokit', '~> 4.20')
+ s.add_dependency('octokit', '~> 5.6.0')
s.add_dependency('omniauth-github', '~> 1.4')
s.add_dependency('pubsubstub', '~> 0.2.0')
- s.add_dependency('rails', '~> 7.0.0')
+ s.add_dependency('rails', '~> 7.1.1')
s.add_dependency('rails-timeago', '~> 2.13.0')
s.add_dependency('rails_autolink', '~> 1.1.6')
s.add_dependency('rake')
diff --git a/template.rb b/template.rb
index 607b043e0..2f80764ff 100644
--- a/template.rb
+++ b/template.rb
@@ -1,10 +1,10 @@
# Template for rails new app
# Run this like `rails new shipit -m template.rb`
-if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7')
- raise Thor::Error, "You need at least Ruby 2.7 to install shipit"
+if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.0')
+ raise Thor::Error, "You need at least Ruby 3.0 to install shipit"
end
-if Gem::Version.new(Rails::VERSION::STRING) < Gem::Version.new('7.0')
- raise Thor::Error, "You need Rails 7.0 to install shipit"
+if Gem::Version.new(Rails::VERSION::STRING) < Gem::Version.new('7.1')
+ raise Thor::Error, "You need Rails 7.1 to install shipit"
end
route %(mount Shipit::Engine, at: '/')
@@ -124,7 +124,7 @@
end
CODE
-inject_into_file 'config/application.rb', after: "load_defaults 7.0\n" do
+inject_into_file 'config/application.rb', after: "load_defaults 7.1\n" do
"\n config.active_job.queue_adapter = :sidekiq\n"
end
@@ -151,5 +151,5 @@
)
end
- say("Read https://github.com/Shopify/shipit-engine/blob/master/docs/setup.md for the details on how to create the App and update config/secrets.yml", Thor::Shell::Color::GREEN, true)
+ say("Read https://github.com/Shopify/shipit-engine/blob/main/docs/setup.md for the details on how to create the App and update config/secrets.yml", Thor::Shell::Color::GREEN, true)
end
diff --git a/test/controllers/api/base_controller_test.rb b/test/controllers/api/base_controller_test.rb
index a37d0211a..348514a46 100644
--- a/test/controllers/api/base_controller_test.rb
+++ b/test/controllers/api/base_controller_test.rb
@@ -3,7 +3,7 @@
module Shipit
module Api
- class BaseControllerTest < ActionController::TestCase
+ class BaseControllerTest < ApiControllerTestCase
test "authentication is required" do
get :index
assert_response :unauthorized
diff --git a/test/controllers/api/ccmenu_controller_test.rb b/test/controllers/api/ccmenu_controller_test.rb
index 76c695c72..ba3dbdf2a 100644
--- a/test/controllers/api/ccmenu_controller_test.rb
+++ b/test/controllers/api/ccmenu_controller_test.rb
@@ -3,7 +3,7 @@
module Shipit
module Api
- class CCMenuControllerTest < ActionController::TestCase
+ class CCMenuControllerTest < ApiControllerTestCase
setup do
authenticate!
@stack = shipit_stacks(:shipit)
diff --git a/test/controllers/api/commits_controller_test.rb b/test/controllers/api/commits_controller_test.rb
index 607deea37..793216eae 100644
--- a/test/controllers/api/commits_controller_test.rb
+++ b/test/controllers/api/commits_controller_test.rb
@@ -3,7 +3,7 @@
module Shipit
module Api
- class CommitsControllerTest < ActionController::TestCase
+ class CommitsControllerTest < ApiControllerTestCase
setup do
@stack = shipit_stacks(:shipit)
authenticate!
diff --git a/test/controllers/api/deploys_controller_test.rb b/test/controllers/api/deploys_controller_test.rb
index 770281582..c7ebbf02a 100644
--- a/test/controllers/api/deploys_controller_test.rb
+++ b/test/controllers/api/deploys_controller_test.rb
@@ -3,7 +3,7 @@
module Shipit
module Api
- class DeploysControllerTest < ActionController::TestCase
+ class DeploysControllerTest < ApiControllerTestCase
setup do
authenticate!
@user = shipit_users(:walrus)
@@ -81,6 +81,7 @@ class DeploysControllerTest < ActionController::TestCase
end
assert_response :conflict
+ assert_json 'error', 'A task is already running.'
end
test "#create refuses to deploy unsuccessful commits if the require_ci flag is passed" do
@@ -119,6 +120,29 @@ class DeploysControllerTest < ActionController::TestCase
assert_response :accepted
assert_json 'status', 'pending'
end
+
+ test "#create uses allow_concurrency param when provided" do
+ @stack.update!(lock_reason: 'Something broken')
+
+ assert_difference -> { @stack.deploys.count }, 1 do
+ post :create, params: { stack_id: @stack.to_param, sha: @commit.sha, force: 'true', allow_concurrency: 'false' }
+ end
+ assert_response :accepted
+ assert_json 'status', 'pending'
+ refute @stack.deploys.last.allow_concurrency
+ end
+
+ test "#create defaults allow_concurrency to force param when not provided" do
+ @stack.update!(lock_reason: 'Something broken')
+ expected_force = true
+
+ assert_difference -> { @stack.deploys.count }, 1 do
+ post :create, params: { stack_id: @stack.to_param, sha: @commit.sha, force: expected_force }
+ end
+ assert_response :accepted
+ assert_json 'status', 'pending'
+ assert_equal expected_force, @stack.deploys.last.allow_concurrency
+ end
end
end
end
diff --git a/test/controllers/api/hooks_controller_test.rb b/test/controllers/api/hooks_controller_test.rb
index 2d19780c8..a577f6a05 100644
--- a/test/controllers/api/hooks_controller_test.rb
+++ b/test/controllers/api/hooks_controller_test.rb
@@ -3,7 +3,7 @@
module Shipit
module Api
- class HooksControllerTest < ActionController::TestCase
+ class HooksControllerTest < ApiControllerTestCase
setup do
authenticate!
@stack = shipit_stacks(:shipit)
diff --git a/test/controllers/api/locks_controller_test.rb b/test/controllers/api/locks_controller_test.rb
index 3ba27867f..faf47aeb1 100644
--- a/test/controllers/api/locks_controller_test.rb
+++ b/test/controllers/api/locks_controller_test.rb
@@ -3,7 +3,7 @@
module Shipit
module Api
- class LocksControllerTest < ActionController::TestCase
+ class LocksControllerTest < ApiControllerTestCase
setup do
authenticate!
@stack = shipit_stacks(:shipit)
diff --git a/test/controllers/api/merge_requests_controller_test.rb b/test/controllers/api/merge_requests_controller_test.rb
index 59f7a8d59..7ed252cdd 100644
--- a/test/controllers/api/merge_requests_controller_test.rb
+++ b/test/controllers/api/merge_requests_controller_test.rb
@@ -3,7 +3,7 @@
module Shipit
module Api
- class MergeRequestsControllerTest < ActionController::TestCase
+ class MergeRequestsControllerTest < ApiControllerTestCase
setup do
@stack = shipit_stacks(:shipit)
@merge_request = shipit_merge_requests(:shipit_pending)
diff --git a/test/controllers/api/outputs_controller_test.rb b/test/controllers/api/outputs_controller_test.rb
index a4a730e40..a2c158e93 100644
--- a/test/controllers/api/outputs_controller_test.rb
+++ b/test/controllers/api/outputs_controller_test.rb
@@ -3,7 +3,7 @@
module Shipit
module Api
- class OutputsControllerTest < ActionController::TestCase
+ class OutputsControllerTest < ApiControllerTestCase
setup do
@stack = shipit_stacks(:shipit)
authenticate!
diff --git a/test/controllers/api/release_statuses_controller_test.rb b/test/controllers/api/release_statuses_controller_test.rb
index bccd076c0..7af530290 100644
--- a/test/controllers/api/release_statuses_controller_test.rb
+++ b/test/controllers/api/release_statuses_controller_test.rb
@@ -3,7 +3,7 @@
module Shipit
module Api
- class ReleaseStatusesControllerTest < ActionController::TestCase
+ class ReleaseStatusesControllerTest < ApiControllerTestCase
setup do
authenticate!
@stack = shipit_stacks(:shipit_canaries)
diff --git a/test/controllers/api/rollback_controller_test.rb b/test/controllers/api/rollback_controller_test.rb
index 60988db22..0f9af7be8 100644
--- a/test/controllers/api/rollback_controller_test.rb
+++ b/test/controllers/api/rollback_controller_test.rb
@@ -3,7 +3,7 @@
module Shipit
module Api
- class RollbacksControllerTest < ActionController::TestCase
+ class RollbacksControllerTest < ApiControllerTestCase
setup do
authenticate!
@user = shipit_users(:walrus)
diff --git a/test/controllers/api/stacks_controller_test.rb b/test/controllers/api/stacks_controller_test.rb
index 4148f5316..0586d221a 100644
--- a/test/controllers/api/stacks_controller_test.rb
+++ b/test/controllers/api/stacks_controller_test.rb
@@ -3,7 +3,7 @@
module Shipit
module Api
- class StacksControllerTest < ActionController::TestCase
+ class StacksControllerTest < ApiControllerTestCase
setup do
authenticate!
@stack = shipit_stacks(:shipit)
@@ -114,6 +114,57 @@ class StacksControllerTest < ActionController::TestCase
refute @stack.continuous_deployment
end
+ test "#update does not perform archive when key is not provided" do
+ refute_predicate @stack, :archived?
+ refute_predicate @stack, :locked?
+
+ patch :update, params: { id: @stack.to_param }
+
+ @stack.reload
+ refute_predicate @stack, :archived?
+ refute_predicate @stack, :locked?
+ end
+
+ test "#update does not perform unarchive when key is not provided" do
+ @stack.archive!(shipit_users(:walrus))
+ assert_predicate @stack, :locked?
+ assert_predicate @stack, :archived?
+
+ patch :update, params: { id: @stack.to_param }
+
+ @stack.reload
+ assert_predicate @stack, :locked?
+ assert_predicate @stack, :archived?
+ end
+
+ test "#update allows to archive the stack" do
+ refute_predicate @stack, :archived?
+ refute_predicate @stack, :locked?
+
+ patch :update, params: { id: @stack.to_param, archived: true }
+
+ @stack.reload
+ assert_predicate @stack, :locked?
+ assert_predicate @stack, :archived?
+ assert_instance_of AnonymousUser, @stack.lock_author
+ assert_equal "Archived", @stack.lock_reason
+ end
+
+ test "#update allows to unarchive the stack" do
+ @stack.archive!(shipit_users(:walrus))
+ assert_predicate @stack, :locked?
+ assert_predicate @stack, :archived?
+
+ patch :update, params: { id: @stack.to_param, archived: false }
+
+ @stack.reload
+ refute_predicate @stack, :archived?
+ refute_predicate @stack, :locked?
+ assert_nil @stack.locked_since
+ assert_nil @stack.lock_reason
+ assert_instance_of AnonymousUser, @stack.lock_author
+ end
+
test "#index returns a list of stacks" do
stack = Stack.last
get :index
@@ -214,6 +265,15 @@ class StacksControllerTest < ActionController::TestCase
end
assert_response :accepted
end
+
+ test "#refresh queues a RefreshStatusesJob and RefreshCheckRunsJob" do
+ assert_enqueued_with(job: RefreshStatusesJob, args: [stack_id: @stack.id]) do
+ assert_enqueued_with(job: RefreshCheckRunsJob, args: [stack_id: @stack.id]) do
+ post :refresh, params: { id: @stack.to_param }
+ end
+ end
+ assert_response :accepted
+ end
end
end
end
diff --git a/test/controllers/api/tasks_controller_test.rb b/test/controllers/api/tasks_controller_test.rb
index 4c0701378..4649e413d 100644
--- a/test/controllers/api/tasks_controller_test.rb
+++ b/test/controllers/api/tasks_controller_test.rb
@@ -3,7 +3,7 @@
module Shipit
module Api
- class TasksControllerTest < ActionController::TestCase
+ class TasksControllerTest < ApiControllerTestCase
setup do
@stack = shipit_stacks(:shipit)
@user = shipit_users(:walrus)
diff --git a/test/controllers/stacks_controller_test.rb b/test/controllers/stacks_controller_test.rb
index 2a7c2fd15..7b8912937 100644
--- a/test/controllers/stacks_controller_test.rb
+++ b/test/controllers/stacks_controller_test.rb
@@ -10,7 +10,7 @@ class StacksControllerTest < ActionController::TestCase
end
test "validates that Shipit.github is present" do
- Rails.application.secrets.stubs(:github).returns(nil)
+ Rails.application.credentials.stubs(:github).returns(nil)
get :index
assert_select "#github_app .missing"
assert_select ".missing", count: 1
diff --git a/test/controllers/tasks_controller_test.rb b/test/controllers/tasks_controller_test.rb
index adbd9355c..ff5fc08c2 100644
--- a/test/controllers/tasks_controller_test.rb
+++ b/test/controllers/tasks_controller_test.rb
@@ -11,9 +11,16 @@ class TasksControllerTest < ActionController::TestCase
session[:user_id] = shipit_users(:walrus).id
end
- test "tasks defined in the shipit.yml can be displayed" do
+ test "tasks defined in the shipit.yml can be displayed with default variable values" do
get :new, params: { stack_id: @stack, definition_id: @definition.id }
assert_response :ok
+ assert_select 'input[name="task[env][FOO]"][value="1"]'
+ end
+
+ test "it is possible to provide a default value override for a task" do
+ get :new, params: { stack_id: @stack, definition_id: @definition.id, FOO: '42' }
+ assert_response :ok
+ assert_select 'input[name="task[env][FOO]"][value="42"]'
end
test "tasks defined in the shipit.yml can't be triggered if the stack is being deployed" do
diff --git a/test/dummy/config/application.rb b/test/dummy/config/application.rb
index 99c43aac8..9a9a61c14 100644
--- a/test/dummy/config/application.rb
+++ b/test/dummy/config/application.rb
@@ -17,6 +17,7 @@
module Shipit
class Application < Rails::Application
- config.load_defaults 7.0
+ config.load_defaults 7.1
+ config.active_record.encryption.support_sha1_for_non_deterministic_encryption = true
end
end
diff --git a/test/dummy/config/initializers/0_load_development_secrets.rb b/test/dummy/config/initializers/0_load_development_secrets.rb
index 863be78d5..f3b83652b 100644
--- a/test/dummy/config/initializers/0_load_development_secrets.rb
+++ b/test/dummy/config/initializers/0_load_development_secrets.rb
@@ -2,8 +2,8 @@
if local_secrets.exist?
secrets = YAML.load(local_secrets.read).deep_symbolize_keys
if Rails.env.development?
- Rails.application.secrets.deep_merge!(secrets)
+ Rails.application.credentials.deep_merge!(secrets)
elsif Rails.env.test?
- Rails.application.secrets.merge!(redis_url: secrets[:redis_url])
+ Rails.application.credentials.merge!(redis_url: secrets[:redis_url])
end
end
diff --git a/test/dummy/config/secrets.development.json b/test/dummy/config/secrets.development.json
new file mode 100644
index 000000000..2c22e2b66
--- /dev/null
+++ b/test/dummy/config/secrets.development.json
@@ -0,0 +1,3 @@
+{
+ "secret_key_base": "s3cr3ts3cr3ts3cr3ts3cr3ts3cr3ts3cr3t"
+}
\ No newline at end of file
diff --git a/test/dummy/config/secrets.test.json b/test/dummy/config/secrets.test.json
new file mode 100644
index 000000000..a08afdab4
--- /dev/null
+++ b/test/dummy/config/secrets.test.json
@@ -0,0 +1,21 @@
+{
+ "host": "shipit.com",
+ "secret_key_base": "s3cr3ts3cr3ts3cr3ts3cr3ts3cr3ts3cr3t",
+ "github_api": {
+ "token": "t0k3n"
+ },
+ "github": {
+ "domain": null,
+ "app_id": 42,
+ "installation_id": 43,
+ "bot_login": "shipit[bot]",
+ "webhook_secret": null,
+ "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA7iUQC2uUq/gtQg0gxtyaccuicYgmq1LUr1mOWbmwM1Cv63+S\n73qo8h87FX+YyclY5fZF6SMXIys02JOkImGgbnvEOLcHnImCYrWs03msOzEIO/pG\nM0YedAPtQ2MEiLIu4y8htosVxeqfEOPiq9kQgFxNKyETzjdIA9q1md8sofuJUmPv\nibacW1PecuAMnn+P8qf0XIDp7uh6noB751KvhCaCNTAPtVE9NZ18OmNG9GOyX/pu\npQHIrPgTpTG6KlAe3r6LWvemzwsMtuRGU+K+KhK9dFIlSE+v9rA32KScO8efOh6s\nGu3rWorV4iDu14U62rzEfdzzc63YL94sUbZxbwIDAQABAoIBADLJ8r8MxZtbhYN1\nu0zOFZ45WL6v09dsBfITvnlCUeLPzYUDIzoxxcBFittN6C744x3ARS6wjimw+EdM\nTZALlCSb/sA9wMDQzt7wchhz9Zh2H5RzDu+2f54sjDh38KqancdT8PO2fAFGxX/b\nqicOVyeZB9gv6MJtJc20olBbuXAeBNfcDABF9oxF+0i+Ssg7B4VXiqgcjtGbr/Og\nqRll7AqyTArVx2xEcVfZxeZ4zGnigzcJq4te7yYpxzwk+RxblkPh54Yt4WxZ+8DI\nRsn3r6ajlpwzpwvsJFU2Txq7xBTzGQMFmy/Pnjk83kP2cogxB2+tRyjITGqTwD8b\ngg9PFCkCgYEA+7u8A0l0Cz6p0SI6c7ftVePVRiIhpawWN7og/wEmI6zUjm/3rA+R\nhrhaVKuOD8QF/HdDsqTck5gjGAjTmJz6r33/cl1Tz+pr62znsrB4r0yMKvQbKN81\nWGaWOsi2+ZXqLNv5h5wpUF0MTKlXHeKnwP5kuEvGwVn6WURFCh6PhLMCgYEA8i5e\nJjulJVGyd5HuoY3xyO7E6DjidsqRnVRq+hYpORjnHvTmSwe4+tH4ha2p9Kv2Y6k3\nC1NYY/fSMQoYCCRaYyJleI+la/9tsZqAmtms4ZB8KhFmPHf9fW75i6G0xKWyZ8K+\nE2Ft/UaEiM282593cguV6+Kt5uExnyPxLLK4FlUCgYEAwRJ/JGI8/7bjFkTTYheq\nj5q75BufhOrU6471acAe2XPgXxLfefdC3Xodxh0CS3NESBvNL4Ikr4sbN37lk4Kq\n/th7iOKtuqUIeru/hZy2I3VpeDRbdGCmEJQ2GwYA2LKztg5Nd0Y9paaIHXAwIfrK\nQUqcQ4HTAk8ZpUeoUBeaaeMCgYANLmbjb9WiPVsYVPIHCwHA7PX8qbPxwT7BsGmO\nKQyfVfKmZa/vH4F67Vi4deZNMdrcO8aKMEQcVM2065a5QrlEsgeR00eupB1lUEJ1\nqylUsZeAdqf43JMIc7TTW77KATa/nQLZbTEeWus1wvTngztuEqFbUGAks9cOkVc8\nFpIcbQKBgQDVIL8gPLmn0f+4oLF8MBC+oxtKpz14X5iJ1saGFkzW5I+nIEskpS0S\nqtirnTCnJFGdCrFwctnxiuiCmyGwpBYdjIfHyvYAHnqAtMnESzCUyeSFZiquVW5W\nMvbMmDPoV27XOHU9kIq6NXtfrkpufiyo6/VEYWozXalxKLNuqLYfPQ==\n-----END RSA PRIVATE KEY-----\n",
+ "oauth": {
+ "id": "Iv1.bf2c2c45b449bfd9",
+ "secret": "ef694cd6e45223075d78d138ef014049052665f1",
+ "teams": null
+ }
+ },
+ "redis_url": "redis://127.0.0.1:6379/7"
+}
\ No newline at end of file
diff --git a/test/dummy/db/schema.rb b/test/dummy/db/schema.rb
index 2cea45691..22bbfaffb 100644
--- a/test/dummy/db/schema.rb
+++ b/test/dummy/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2021_11_03_154121) do
+ActiveRecord::Schema.define(version: 2023_07_03_181143) do
create_table "api_clients", force: :cascade do |t|
t.text "permissions", limit: 65535
@@ -42,7 +42,7 @@
create_table "commit_deployment_statuses", force: :cascade do |t|
t.integer "commit_deployment_id"
t.string "status"
- t.integer "github_id"
+ t.bigint "github_id"
t.string "api_url"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
diff --git a/test/helpers/api_helper.rb b/test/helpers/api_helper.rb
index 909969dfa..45665a007 100644
--- a/test/helpers/api_helper.rb
+++ b/test/helpers/api_helper.rb
@@ -8,3 +8,16 @@ def authenticate!(client = @client || :spy)
request.headers['Authorization'] = "Basic #{Base64.encode64(client.authentication_token)}"
end
end
+
+module Shipit
+ class ApiControllerTestCase < ActionController::TestCase
+ private
+
+ def process(_action, **kwargs)
+ if kwargs[:method] != "GET"
+ kwargs[:as] ||= :json
+ end
+ super
+ end
+ end
+end
diff --git a/test/jobs/shipit/background_job_test.rb b/test/jobs/shipit/background_job_test.rb
new file mode 100644
index 000000000..3c8be33b6
--- /dev/null
+++ b/test/jobs/shipit/background_job_test.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+require 'test_helper'
+
+module Shipit
+ class BackgroundJobTest < ActiveSupport::TestCase
+ setup do
+ @stack = shipit_stacks(:shipit)
+ @last_commit = @stack.commits.last
+ @job = CacheDeploySpecJob.new
+ @user = shipit_users(:walrus)
+ end
+
+ test "#perform retries on Octokit secondary rate limit exceptions" do
+ freeze_time do
+ Octokit::Forbidden.any_instance.expects(:response_headers)
+ .returns({ "Retry-After" => 45 })
+
+ Shipit.github.api.expects(:user).with(@user.github_id).raises(Octokit::TooManyRequests)
+
+ assert_enqueued_with(job: BackgroundStubJob, at: Time.now + 45.seconds) do
+ BackgroundStubJob.perform_now(@user)
+ end
+ end
+ end
+
+ class BackgroundStubJob < BackgroundJob
+ queue_as :default
+
+ def perform(user)
+ Shipit.github.api.user(user.github_id)
+ end
+ end
+ end
+end
diff --git a/test/models/commit_deployment_status_test.rb b/test/models/commit_deployment_status_test.rb
index 8bcbc5ab7..9d75fce59 100644
--- a/test/models/commit_deployment_status_test.rb
+++ b/test/models/commit_deployment_status_test.rb
@@ -15,7 +15,6 @@ class CommitDeploymentStatusTest < ActiveSupport::TestCase
@author.github_api.class.any_instance.expects(:create_deployment_status).with(
@deployment.api_url,
'in_progress',
- accept: "application/vnd.github.flash-preview+json",
target_url: "http://shipit.com/shopify/shipit-engine/production/deploys/#{@task.id}",
description: "walrus triggered the deploy of shopify/shipit-engine/production to #{@deployment.short_sha}",
environment_url: "https://shipit.shopify.com",
diff --git a/test/models/commits_test.rb b/test/models/commits_test.rb
index 0965167e5..55449bdc5 100644
--- a/test/models/commits_test.rb
+++ b/test/models/commits_test.rb
@@ -353,10 +353,11 @@ class CommitsTest < ActiveSupport::TestCase
completed_at: Time.now,
started_at: Time.now - 1.minute,
)
- response = mock(
+ response = stub(rels: {}, data: mock(
check_runs: [check_run],
- )
- Shipit.github.api.expects(:check_runs).with(@stack.github_repo_name, @commit.sha).returns(response)
+ ))
+ Shipit.github.api.expects(:check_runs).with(@stack.github_repo_name, @commit.sha, per_page: 100).returns(response.data)
+ Shipit.github.api.expects(:last_response).returns(response)
assert_difference -> { @commit.check_runs.count }, 1 do
@commit.refresh_check_runs!
diff --git a/test/models/merge_request_test.rb b/test/models/merge_request_test.rb
index 249d6a5c6..a0140d4a6 100644
--- a/test/models/merge_request_test.rb
+++ b/test/models/merge_request_test.rb
@@ -125,7 +125,7 @@ class MergeRequestTest < ActiveSupport::TestCase
created_at: 1.day.ago,
)])
- Shipit.github.api.expects(:check_runs).with(@stack.github_repo_name, head_sha).returns(stub(
+ response = stub(rels: {}, data: stub(
check_runs: [stub(
id: 123456,
name: 'check run',
@@ -140,6 +140,8 @@ class MergeRequestTest < ActiveSupport::TestCase
)]
))
+ Shipit.github.api.expects(:last_response).returns(response)
+ Shipit.github.api.expects(:check_runs).with(@stack.github_repo_name, head_sha, per_page: 100).returns(response.data)
merge_request.refresh!
assert_predicate merge_request, :mergeable?
@@ -243,7 +245,10 @@ class MergeRequestTest < ActiveSupport::TestCase
end
test "status transitions emit hooks" do
- job = assert_enqueued_with(job: EmitEventJob) do
+ expected_args = ->(job_args) do
+ job_args.first[:event] == 'merge'
+ end
+ job = assert_enqueued_with(job: EmitEventJob, args: expected_args) do
@pr.reject!('merge_conflict')
end
params = job.arguments.first
diff --git a/test/models/shipit/stacks_test.rb b/test/models/shipit/stack_test.rb
similarity index 94%
rename from test/models/shipit/stacks_test.rb
rename to test/models/shipit/stack_test.rb
index dbfe5bb9a..a65e82efe 100644
--- a/test/models/shipit/stacks_test.rb
+++ b/test/models/shipit/stack_test.rb
@@ -3,7 +3,7 @@
require 'securerandom'
module Shipit
- class StacksTest < ActiveSupport::TestCase
+ class StackTest < ActiveSupport::TestCase
def setup
@stack = shipit_stacks(:shipit)
@expected_base_path = Rails.root.join('data', 'stacks', @stack.to_param).to_s
@@ -278,6 +278,36 @@ def self.deliver(event, stack, payload)
end
end
+ test "#active_task? is false if stack has a concurrent deploy in active state" do
+ @stack.trigger_deploy(shipit_commits(:third), AnonymousUser.new, force: true)
+ refute @stack.active_task?
+ end
+
+ test "#occupied? is false if stack has no deploy in either pending or running state" do
+ @stack.deploys.active.destroy_all
+ refute @stack.occupied?
+ end
+
+ test "#occupied? is false if stack has no deploy at all" do
+ @stack.deploys.destroy_all
+ refute @stack.occupied?
+ end
+
+ test "occupied? is true if stack has a concurrent deploy in active state" do
+ @stack.trigger_deploy(shipit_commits(:third), AnonymousUser.new, force: true)
+ assert @stack.occupied?
+ end
+
+ test "occupied? is true if stack has a deploy in pending state" do
+ @stack.trigger_deploy(shipit_commits(:third), AnonymousUser.new)
+ assert @stack.occupied?
+ end
+
+ test "#occupied? is true if a rollback is ongoing" do
+ shipit_deploys(:shipit_complete).trigger_rollback(AnonymousUser.new)
+ assert @stack.occupied?
+ end
+
test "#deployable? returns true if the stack is not locked, not awaiting provision, and is not deploying" do
@stack.deploys.destroy_all
@stack.update!(lock_reason: nil, awaiting_provision: false)
@@ -674,12 +704,12 @@ def self.deliver(event, stack, payload)
assert_equal shipit_commits(:fifth), @stack.next_commit_to_deploy
end
- test "#next_commit_to_deploy respects the deploy.max_commits directive" do
+ test "#next_commit_to_deploy respects the deploy.max_commits directive given the commit is deployable" do
@stack.tasks.destroy_all
- fifth_commit = shipit_commits(:third)
- fifth_commit.statuses.create!(stack_id: @stack.id, state: 'success', context: 'ci/travis')
- assert_predicate fifth_commit, :deployable?
+ third_commit = shipit_commits(:third)
+ third_commit.statuses.create!(stack_id: @stack.id, state: 'success', context: 'ci/travis')
+ assert_predicate third_commit, :deployable?
assert_equal shipit_commits(:third), @stack.next_commit_to_deploy
@@ -687,6 +717,19 @@ def self.deliver(event, stack, payload)
assert_equal shipit_commits(:third), @stack.next_commit_to_deploy
end
+ test "#next_commit_to_deploy deploys the first deployable commit when deploy.max_commits directive fails to find a deployable commit" do
+ @stack.tasks.destroy_all
+
+ third_commit = shipit_commits(:third)
+ third_commit.statuses.create!(stack_id: @stack.id, state: 'success', context: 'ci/travis')
+ assert_predicate third_commit, :deployable?
+
+ assert_equal shipit_commits(:third), @stack.next_commit_to_deploy
+
+ @stack.expects(:maximum_commits_per_deploy).returns(1).at_least_once
+ assert_equal shipit_commits(:third), @stack.next_commit_to_deploy
+ end
+
test "setting #lock_reason also sets #locked_since" do
assert_predicate @stack.locked_since, :nil?
diff --git a/test/models/users_test.rb b/test/models/users_test.rb
index 1cbe57296..b62083c05 100644
--- a/test/models/users_test.rb
+++ b/test/models/users_test.rb
@@ -203,6 +203,14 @@ class UsersTest < ActiveSupport::TestCase
assert_equal 'george@cyclim.se', user.email
end
+ test "#refresh_from_github! logs deleted users" do
+ Shipit.github.api.expects(:user).with(@user.github_id).raises(Octokit::Forbidden)
+
+ Rails.logger.expects(:info).with("User #{@user.name}, github_id #{@user.github_id} has forbidden access to their GitHub, likely deleted.")
+
+ @user.refresh_from_github!
+ end
+
test "#github_api uses the user's access token" do
assert_equal @user.github_access_token, @user.github_api.access_token
end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index a5ba4068e..6c75d7f97 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -23,7 +23,7 @@
# Load fixtures from the engine
if ActiveSupport::TestCase.respond_to?(:fixture_path=)
- ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
+ ActiveSupport::TestCase.fixture_paths << File.expand_path("../fixtures", __FILE__)
ActiveSupport::TestCase.fixtures(:all)
end
@@ -71,7 +71,7 @@ class TestCase
end
end
- ActiveRecord::Migration.check_pending!
+ ActiveRecord::Migration.check_all_pending!
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
#
diff --git a/test/unit/deploy_commands_test.rb b/test/unit/deploy_commands_test.rb
index 5113706d1..d839ea501 100644
--- a/test/unit/deploy_commands_test.rb
+++ b/test/unit/deploy_commands_test.rb
@@ -21,6 +21,100 @@ def setup
StackCommands.stubs(git_version: Gem::Version.new('1.8.4.3'))
end
+ test "#fetch_commit calls git fetch if repository cache already exist" do
+ @stack.git_path.stubs(:exist?).returns(true)
+ @stack.git_path.stubs(:empty?).returns(false)
+
+ command = @commands.fetch_commit(@deploy.until_commit)
+
+ assert_equal %W(git fetch origin --quiet --tags #{@deploy.until_commit.sha}), command.args
+ end
+
+ test "#fetch_commit calls git fetch in git_path directory if repository cache already exist" do
+ @stack.git_path.stubs(:exist?).returns(true)
+ @stack.git_path.stubs(:empty?).returns(false)
+
+ command = @commands.fetch_commit(@deploy.until_commit)
+
+ assert_equal @stack.git_path.to_s, command.chdir
+ end
+
+ test "#fetch_commit calls git clone if repository cache do not exist" do
+ @stack.git_path.stubs(:exist?).returns(false)
+
+ command = @commands.fetch_commit(@deploy.until_commit)
+
+ expected = %W(git clone --quiet --single-branch --recursive --branch master #{@stack.repo_git_url} #{@stack.git_path})
+ assert_equal expected, command.args.map(&:to_s)
+ end
+
+ test "#fetch_commit calls git clone if repository cache is empty" do
+ @stack.git_path.stubs(:exist?).returns(true)
+ @stack.git_path.stubs(:empty?).returns(true)
+
+ command = @commands.fetch_commit(@deploy.until_commit)
+
+ expected = %W(git clone --quiet --single-branch --recursive --branch master #{@stack.repo_git_url} #{@stack.git_path})
+ assert_equal expected, command.args
+ end
+
+ test "#fetch_commit calls git clone if repository cache corrupt" do
+ @stack.git_path.stubs(:exist?).returns(true)
+ @stack.git_path.stubs(:empty?).returns(false)
+ StackCommands.any_instance.expects(:git_cmd_succeeds?)
+ .with(@stack.git_path)
+ .returns(false)
+
+ command = @commands.fetch_commit(@deploy.until_commit)
+
+ expected = %W(git clone --quiet --single-branch --recursive --branch master #{@stack.repo_git_url} #{@stack.git_path})
+ assert_equal expected, command.args
+ end
+
+ test "#fetch_commit clears a corrupted git stash before cloning" do
+ @stack.expects(:clear_git_cache!)
+ @stack.git_path.stubs(:exist?).returns(true)
+ @stack.git_path.stubs(:empty?).returns(false)
+ StackCommands.any_instance.expects(:git_cmd_succeeds?)
+ .with(@stack.git_path)
+ .returns(false)
+
+ command = @commands.fetch_commit(@deploy.until_commit)
+
+ expected = %W(git clone --quiet --single-branch --recursive --branch master #{@stack.repo_git_url} #{@stack.git_path})
+ assert_equal expected, command.args
+ end
+
+ test "#fetch_commit does not use --single-branch if git is outdated" do
+ @stack.git_path.stubs(:exist?).returns(false)
+ StackCommands.stubs(git_version: Gem::Version.new('1.7.2.30'))
+
+ command = @commands.fetch_commit(@deploy.until_commit)
+
+ expected = %W(git clone --quiet --recursive --branch master #{@stack.repo_git_url} #{@stack.git_path})
+ assert_equal expected, command.args.map(&:to_s)
+ end
+
+ test "#fetch_commit calls git fetch in base_path directory if repository cache do not exist" do
+ @stack.git_path.stubs(:exist?).returns(false)
+
+ command = @commands.fetch_commit(@deploy.until_commit)
+
+ assert_equal @stack.deploys_path.to_s, command.chdir
+ end
+
+ test "#fetch_commit merges Shipit.env in ENVIRONMENT" do
+ Shipit.stubs(:env).returns("SPECIFIC_CONFIG" => 5)
+ command = @commands.fetch_commit(@deploy.until_commit)
+ assert_equal '5', command.env["SPECIFIC_CONFIG"]
+ end
+
+ test "#env uses the correct Github token for a stack" do
+ Shipit.github(organization: 'shopify').stubs(:token).returns('aS3cr3Tt0kEn')
+ command = @commands.fetch_commit(@deploy.until_commit)
+ assert_equal 'aS3cr3Tt0kEn', command.env["GITHUB_TOKEN"]
+ end
+
test "#fetch calls git fetch if repository cache already exist" do
@stack.git_path.stubs(:exist?).returns(true)
@stack.git_path.stubs(:empty?).returns(false)
@@ -109,12 +203,6 @@ def setup
assert_equal '5', command.env["SPECIFIC_CONFIG"]
end
- test "#env uses the correct Github token for a stack" do
- Shipit.github(organization: 'shopify').stubs(:token).returns('aS3cr3Tt0kEn')
- command = @commands.fetch
- assert_equal 'aS3cr3Tt0kEn', command.env["GITHUB_TOKEN"]
- end
-
test "#clone clones the repository cache into the working directory" do
commands = @commands.clone
assert_equal 2, commands.size
@@ -134,7 +222,11 @@ def setup
test "#checkout checks out the deployed commit" do
command = @commands.checkout(@deploy.until_commit)
- assert_equal ['git', '-c', 'advice.detachedHead=false', 'checkout', @deploy.until_commit.sha], command.args
+ checkout_args = [
+ 'git', '-c', 'advice.detachedHead=false', 'checkout', '--quiet',
+ @deploy.until_commit.sha,
+ ]
+ assert_equal checkout_args, command.args
end
test "#checkout checks out the deployed commit from the working directory" do
diff --git a/test/unit/github_app_test.rb b/test/unit/github_app_test.rb
index 5beda14d5..4e2aab2eb 100644
--- a/test/unit/github_app_test.rb
+++ b/test/unit/github_app_test.rb
@@ -202,7 +202,7 @@ def app(extra_config = {})
end
def default_config
- Rails.application.secrets.github.deep_dup
+ Rails.application.credentials.github.deep_dup
end
end
end