itamae 1.14.2 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +2 -0
- data/.github/workflows/docker-daemon.json +3 -0
- data/.github/workflows/release.yml +4 -4
- data/.github/workflows/test.yml +30 -15
- data/.github/workflows/test_main.yml +37 -31
- data/CHANGELOG.md +15 -1
- data/Gemfile +9 -0
- data/Rakefile +12 -2
- data/itamae.gemspec +2 -2
- data/lib/itamae/backend.rb +3 -1
- data/lib/itamae/node.rb +1 -1
- data/lib/itamae/notification.rb +1 -2
- data/lib/itamae/recipe.rb +1 -1
- data/lib/itamae/resource/group.rb +3 -1
- data/lib/itamae/runner.rb +2 -2
- data/lib/itamae/version.rb +1 -1
- data/spec/integration/default_spec.rb +23 -13
- data/spec/integration/ordinary_user_spec.rb +6 -6
- data/spec/integration/recipes/default.rb +39 -26
- data/spec/integration/recipes/ordinary_user.rb +25 -25
- data/tasks/httpbin_server.rb +63 -0
- data/tasks/integration_local_spec.rb +42 -31
- metadata +8 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 12b8fcd57c70cc4c440ccd46a34786f1ac43a6bc7f4b66b68598ffeecfe9daaf
|
|
4
|
+
data.tar.gz: c1e77510842948b7cc4222e566939e8e32c1540d4b09178dc6492449b38d98c2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4d033e29941f7cc56de7626d1b3bdee2e00d1adca58e13c2927c4f3a1202046d80427eb614111424c8e21cc43c2bf480009069782fa5faebfdbf2f9dcd7fdbc5
|
|
7
|
+
data.tar.gz: bd55aa58068d1ec98b5bc74e4e6829d7e0927f575e352289b74801916ebab2f9e511a4b197a947da538c033b0d054af39dc91209facc0e1e74b797c0da07b599
|
data/.github/dependabot.yml
CHANGED
|
@@ -18,20 +18,20 @@ jobs:
|
|
|
18
18
|
|
|
19
19
|
steps:
|
|
20
20
|
- name: Harden Runner
|
|
21
|
-
uses: step-security/harden-runner@
|
|
21
|
+
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
|
22
22
|
with:
|
|
23
23
|
egress-policy: audit
|
|
24
24
|
|
|
25
|
-
- uses: actions/checkout@
|
|
25
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
26
26
|
|
|
27
27
|
- name: Set up Ruby
|
|
28
|
-
uses: ruby/setup-ruby@
|
|
28
|
+
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
|
29
29
|
with:
|
|
30
30
|
bundler-cache: true
|
|
31
31
|
ruby-version: ruby
|
|
32
32
|
|
|
33
33
|
- name: Publish to RubyGems
|
|
34
|
-
uses: rubygems/release-gem@
|
|
34
|
+
uses: rubygems/release-gem@052cc82692552de3ef2b81fd670e41d13cba8092 # v1.4.0
|
|
35
35
|
|
|
36
36
|
- name: Create GitHub release
|
|
37
37
|
run: |
|
data/.github/workflows/test.yml
CHANGED
|
@@ -11,6 +11,7 @@ on:
|
|
|
11
11
|
- reopened
|
|
12
12
|
schedule:
|
|
13
13
|
- cron: "0 0 * * 5" # JST 9:00 (Fri)
|
|
14
|
+
workflow_dispatch:
|
|
14
15
|
|
|
15
16
|
jobs:
|
|
16
17
|
test_main:
|
|
@@ -29,10 +30,12 @@ jobs:
|
|
|
29
30
|
- "3.2"
|
|
30
31
|
- "3.3"
|
|
31
32
|
- "3.4"
|
|
33
|
+
- "4.0"
|
|
32
34
|
rubyopt:
|
|
33
35
|
- ""
|
|
34
36
|
- "--jit"
|
|
35
37
|
- "--yjit"
|
|
38
|
+
- "--zjit"
|
|
36
39
|
exclude:
|
|
37
40
|
# --jit is available since MRI 2.6
|
|
38
41
|
- ruby: "2.3"
|
|
@@ -62,20 +65,35 @@ jobs:
|
|
|
62
65
|
rubyopt: "--yjit"
|
|
63
66
|
- ruby: "3.4"
|
|
64
67
|
rubyopt: "--yjit"
|
|
68
|
+
- ruby: "4.0"
|
|
69
|
+
rubyopt: "--yjit"
|
|
70
|
+
|
|
71
|
+
# --zjit is available since MRI 4.0
|
|
72
|
+
- ruby: "2.3"
|
|
73
|
+
rubyopt: "--zjit"
|
|
74
|
+
- ruby: "2.4"
|
|
75
|
+
rubyopt: "--zjit"
|
|
76
|
+
- ruby: "2.5"
|
|
77
|
+
rubyopt: "--zjit"
|
|
78
|
+
- ruby: "2.6"
|
|
79
|
+
rubyopt: "--zjit"
|
|
80
|
+
- ruby: "2.7"
|
|
81
|
+
rubyopt: "--zjit"
|
|
82
|
+
- ruby: "3.0"
|
|
83
|
+
rubyopt: "--zjit"
|
|
84
|
+
- ruby: "3.1"
|
|
85
|
+
rubyopt: "--zjit"
|
|
86
|
+
- ruby: "3.2"
|
|
87
|
+
rubyopt: "--zjit"
|
|
88
|
+
- ruby: "3.3"
|
|
89
|
+
rubyopt: "--zjit"
|
|
90
|
+
- ruby: "3.4"
|
|
91
|
+
rubyopt: "--zjit"
|
|
65
92
|
|
|
66
93
|
uses: ./.github/workflows/test_main.yml
|
|
67
94
|
with:
|
|
68
95
|
ruby: ${{ matrix.ruby }}
|
|
69
96
|
rubyopt: ${{ matrix.rubyopt }}
|
|
70
|
-
|
|
71
|
-
# This repository uses matrix to run 50+ builds in a pipeline.
|
|
72
|
-
# This results in simultaneous requests being sent to httpbin.org,
|
|
73
|
-
# similar to a DDoS attack, often causing 500 errors and test failures.
|
|
74
|
-
#
|
|
75
|
-
# e.g. https://github.com/itamae-kitchen/itamae/actions/runs/17045210601/job/48319099479?pr=379
|
|
76
|
-
#
|
|
77
|
-
# Therefore, I suppressed simultaneous requests to httpbin.org.
|
|
78
|
-
skip_http_request_test: ${{ (matrix.ruby == '2.3' && matrix.rubyopt == '') && 'false' || 'true' }}
|
|
79
97
|
secrets:
|
|
80
98
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
|
81
99
|
|
|
@@ -87,12 +105,9 @@ jobs:
|
|
|
87
105
|
|
|
88
106
|
steps:
|
|
89
107
|
- name: Slack Notification (success)
|
|
90
|
-
uses:
|
|
108
|
+
uses: act10ns/slack@d96404edccc6d6467fc7f8134a420c851b1e9054 # v2.2.0
|
|
91
109
|
if: always()
|
|
92
110
|
continue-on-error: true
|
|
93
111
|
with:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
icon_emoji: ":octocat:"
|
|
97
|
-
url: ${{ secrets.SLACK_WEBHOOK }}
|
|
98
|
-
token: ${{ secrets.GITHUB_TOKEN }}
|
|
112
|
+
status: ${{ job.status }}
|
|
113
|
+
webhook-url: ${{ secrets.SLACK_WEBHOOK }}
|
|
@@ -10,9 +10,6 @@ on:
|
|
|
10
10
|
required: false
|
|
11
11
|
type: string
|
|
12
12
|
default: ""
|
|
13
|
-
skip_http_request_test:
|
|
14
|
-
required: true
|
|
15
|
-
type: string
|
|
16
13
|
secrets:
|
|
17
14
|
SLACK_WEBHOOK:
|
|
18
15
|
required: true
|
|
@@ -22,9 +19,9 @@ jobs:
|
|
|
22
19
|
runs-on: ubuntu-latest
|
|
23
20
|
|
|
24
21
|
steps:
|
|
25
|
-
- uses: actions/checkout@
|
|
22
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
26
23
|
|
|
27
|
-
- uses: ruby/setup-ruby@v1
|
|
24
|
+
- uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
|
28
25
|
with:
|
|
29
26
|
ruby-version: ${{ inputs.ruby }}
|
|
30
27
|
bundler-cache: true
|
|
@@ -37,15 +34,13 @@ jobs:
|
|
|
37
34
|
RUBYOPT: ${{ inputs.rubyopt }}
|
|
38
35
|
|
|
39
36
|
- name: Slack Notification (not success)
|
|
40
|
-
uses:
|
|
37
|
+
uses: act10ns/slack@d96404edccc6d6467fc7f8134a420c851b1e9054 # v2.2.0
|
|
41
38
|
if: "! success()"
|
|
42
39
|
continue-on-error: true
|
|
43
40
|
with:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
url: ${{ secrets.SLACK_WEBHOOK }}
|
|
48
|
-
token: ${{ secrets.GITHUB_TOKEN }}
|
|
41
|
+
status: ${{ job.status }}
|
|
42
|
+
webhook-url: ${{ secrets.SLACK_WEBHOOK }}
|
|
43
|
+
inputs: ${{ toJson(inputs) }}
|
|
49
44
|
|
|
50
45
|
integration-docker:
|
|
51
46
|
runs-on: ubuntu-latest
|
|
@@ -59,12 +54,16 @@ jobs:
|
|
|
59
54
|
|
|
60
55
|
env:
|
|
61
56
|
TEST_IMAGE: ${{ matrix.image }}
|
|
62
|
-
SKIP_HTTP_REQUEST_TEST: ${{ inputs.skip_http_request_test }}
|
|
63
57
|
|
|
64
58
|
steps:
|
|
65
|
-
- uses: actions/checkout@
|
|
59
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
60
|
+
|
|
61
|
+
- name: Pull Docker Hub images through mirror.gcr.io
|
|
62
|
+
run: |
|
|
63
|
+
sudo cp .github/workflows/docker-daemon.json /etc/docker/daemon.json
|
|
64
|
+
sudo systemctl restart docker
|
|
66
65
|
|
|
67
|
-
- uses: ruby/setup-ruby@v1
|
|
66
|
+
- uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
|
68
67
|
with:
|
|
69
68
|
ruby-version: ${{ inputs.ruby }}
|
|
70
69
|
bundler-cache: true
|
|
@@ -72,6 +71,10 @@ jobs:
|
|
|
72
71
|
|
|
73
72
|
- run: bundle update
|
|
74
73
|
|
|
74
|
+
- run: bundle exec rake spec:integration:docker:httpbin_start
|
|
75
|
+
env:
|
|
76
|
+
RUBYOPT: ${{ inputs.rubyopt }}
|
|
77
|
+
|
|
75
78
|
- run: bundle exec rake spec:integration:docker:boot
|
|
76
79
|
env:
|
|
77
80
|
RUBYOPT: ${{ inputs.rubyopt }}
|
|
@@ -91,16 +94,19 @@ jobs:
|
|
|
91
94
|
env:
|
|
92
95
|
RUBYOPT: ${{ inputs.rubyopt }}
|
|
93
96
|
|
|
97
|
+
- run: bundle exec rake spec:integration:docker:httpbin_stop
|
|
98
|
+
if: always()
|
|
99
|
+
env:
|
|
100
|
+
RUBYOPT: ${{ inputs.rubyopt }}
|
|
101
|
+
|
|
94
102
|
- name: Slack Notification (not success)
|
|
95
|
-
uses:
|
|
103
|
+
uses: act10ns/slack@d96404edccc6d6467fc7f8134a420c851b1e9054 # v2.2.0
|
|
96
104
|
if: "! success()"
|
|
97
105
|
continue-on-error: true
|
|
98
106
|
with:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
url: ${{ secrets.SLACK_WEBHOOK }}
|
|
103
|
-
token: ${{ secrets.GITHUB_TOKEN }}
|
|
107
|
+
status: ${{ job.status }}
|
|
108
|
+
webhook-url: ${{ secrets.SLACK_WEBHOOK }}
|
|
109
|
+
matrix: ${{ toJson(matrix) }}
|
|
104
110
|
|
|
105
111
|
integration-local:
|
|
106
112
|
runs-on: ubuntu-latest
|
|
@@ -108,13 +114,15 @@ jobs:
|
|
|
108
114
|
# NOTE: When ruby is 2.5, tests run on buster. But buster is already EOL and didn't work apt-get
|
|
109
115
|
if: ${{ inputs.ruby >= '2.6' }}
|
|
110
116
|
|
|
111
|
-
env:
|
|
112
|
-
SKIP_HTTP_REQUEST_TEST: ${{ inputs.skip_http_request_test }}
|
|
113
|
-
|
|
114
117
|
steps:
|
|
115
|
-
- uses: actions/checkout@
|
|
118
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
119
|
+
|
|
120
|
+
- name: Pull Docker Hub images through mirror.gcr.io
|
|
121
|
+
run: |
|
|
122
|
+
sudo cp .github/workflows/docker-daemon.json /etc/docker/daemon.json
|
|
123
|
+
sudo systemctl restart docker
|
|
116
124
|
|
|
117
|
-
- uses: ruby/setup-ruby@v1
|
|
125
|
+
- uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
|
|
118
126
|
with:
|
|
119
127
|
ruby-version: ${{ inputs.ruby }}
|
|
120
128
|
bundler-cache: true
|
|
@@ -131,12 +139,10 @@ jobs:
|
|
|
131
139
|
RUBYOPT: ${{ inputs.rubyopt }}
|
|
132
140
|
|
|
133
141
|
- name: Slack Notification (not success)
|
|
134
|
-
uses:
|
|
142
|
+
uses: act10ns/slack@d96404edccc6d6467fc7f8134a420c851b1e9054 # v2.2.0
|
|
135
143
|
if: "! success()"
|
|
136
144
|
continue-on-error: true
|
|
137
145
|
with:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
url: ${{ secrets.SLACK_WEBHOOK }}
|
|
142
|
-
token: ${{ secrets.GITHUB_TOKEN }}
|
|
146
|
+
status: ${{ job.status }}
|
|
147
|
+
webhook-url: ${{ secrets.SLACK_WEBHOOK }}
|
|
148
|
+
inputs: ${{ toJson(inputs) }}
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
## Unreleased
|
|
2
|
-
[full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.
|
|
2
|
+
[full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.15.0...master)
|
|
3
|
+
|
|
4
|
+
## v1.15.0
|
|
5
|
+
[full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.14.2...v1.15.0)
|
|
6
|
+
|
|
7
|
+
Improvements
|
|
8
|
+
|
|
9
|
+
- [Add `system_group` option to `group` resource by takumin](https://github.com/itamae-kitchen/itamae/pull/297)
|
|
10
|
+
- NOTE: This bumps the minimum required specinfra version to 2.81.0.
|
|
11
|
+
- [Add 'etc' library requirement in backend.rb by neidiom](https://github.com/itamae-kitchen/itamae/pull/389)
|
|
12
|
+
- [Prefer `Bundler.with_unbundled_env` over deprecated `with_clean_env` by neidiom](https://github.com/itamae-kitchen/itamae/pull/390)
|
|
13
|
+
- [Convert ohai commands to array form in runner by neidiom](https://github.com/itamae-kitchen/itamae/pull/395)
|
|
14
|
+
- [Use `respond_to_missing?` instead of `respond_to?` in Node by neidiom](https://github.com/itamae-kitchen/itamae/pull/409)
|
|
15
|
+
- [Replace abort with raise in notification validation by neidiom](https://github.com/itamae-kitchen/itamae/pull/410)
|
|
16
|
+
- [Fix shadowed variable in `include_recipe` find block by neidiom](https://github.com/itamae-kitchen/itamae/pull/408)
|
|
3
17
|
|
|
4
18
|
## v1.14.2
|
|
5
19
|
[full changelog](https://github.com/itamae-kitchen/itamae/compare/v1.14.1...v1.14.2)
|
data/Gemfile
CHANGED
|
@@ -11,3 +11,12 @@ group :test do
|
|
|
11
11
|
gem 'growl'
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
|
+
|
|
15
|
+
# FIXME: Delete the following after https://github.com/fakefs/fakefs/pull/494 is merged and released
|
|
16
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("4.0.0.preview2")
|
|
17
|
+
gem "english"
|
|
18
|
+
gem "fileutils"
|
|
19
|
+
gem "find"
|
|
20
|
+
gem "irb"
|
|
21
|
+
gem "stringio"
|
|
22
|
+
end
|
data/Rakefile
CHANGED
|
@@ -24,12 +24,22 @@ namespace :spec do
|
|
|
24
24
|
task :all => ['spec:integration:docker', 'spec:integration:local']
|
|
25
25
|
|
|
26
26
|
desc "Run provision and specs"
|
|
27
|
-
task :docker => ["docker:boot", "docker:provision", "docker:serverspec", 'docker:clean_docker_container']
|
|
27
|
+
task :docker => ["docker:httpbin_start", "docker:boot", "docker:provision", "docker:serverspec", 'docker:clean_docker_container', "docker:httpbin_stop"]
|
|
28
28
|
|
|
29
29
|
namespace :docker do
|
|
30
|
+
desc "Start a local go-httpbin server for http_request tests"
|
|
31
|
+
task :httpbin_start do
|
|
32
|
+
HttpbinServer.start
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
desc "Stop the local go-httpbin server"
|
|
36
|
+
task :httpbin_stop do
|
|
37
|
+
HttpbinServer.stop
|
|
38
|
+
end
|
|
39
|
+
|
|
30
40
|
desc "Run docker"
|
|
31
41
|
task :boot do
|
|
32
|
-
sh "docker run --
|
|
42
|
+
sh "docker run --privileged -d --name #{container_name} #{TEST_IMAGE} /sbin/init"
|
|
33
43
|
end
|
|
34
44
|
|
|
35
45
|
desc "Run itamae"
|
data/itamae.gemspec
CHANGED
|
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
|
|
|
28
28
|
spec.require_paths = ["lib"]
|
|
29
29
|
|
|
30
30
|
spec.add_runtime_dependency "thor", ">= 1.0.0"
|
|
31
|
-
spec.add_runtime_dependency "specinfra", [">= 2.
|
|
31
|
+
spec.add_runtime_dependency "specinfra", [">= 2.81.0", "< 3.0.0"]
|
|
32
32
|
spec.add_runtime_dependency "hashie"
|
|
33
33
|
spec.add_runtime_dependency "ansi"
|
|
34
34
|
spec.add_runtime_dependency "schash", "~> 0.1.0"
|
|
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
|
|
|
36
36
|
spec.add_development_dependency "bundler", ">= 1.3"
|
|
37
37
|
spec.add_development_dependency "rake"
|
|
38
38
|
spec.add_development_dependency "rspec", "~> 3.0"
|
|
39
|
-
spec.add_development_dependency "serverspec", "~> 2.
|
|
39
|
+
spec.add_development_dependency "serverspec", "~> 2.43"
|
|
40
40
|
spec.add_development_dependency "pry-byebug"
|
|
41
41
|
spec.add_development_dependency "docker-api", "~> 2"
|
|
42
42
|
spec.add_development_dependency "fakefs"
|
data/lib/itamae/backend.rb
CHANGED
|
@@ -2,6 +2,7 @@ require 'specinfra/core'
|
|
|
2
2
|
require 'singleton'
|
|
3
3
|
require 'io/console'
|
|
4
4
|
require 'net/ssh'
|
|
5
|
+
require 'etc'
|
|
5
6
|
|
|
6
7
|
Specinfra::Configuration.error_on_missing_backend_type = true
|
|
7
8
|
|
|
@@ -267,7 +268,8 @@ module Itamae
|
|
|
267
268
|
hostname = opts[:host_name] || 'default'
|
|
268
269
|
vagrant_cmd = "vagrant ssh-config #{hostname} > #{config.path}"
|
|
269
270
|
if defined?(Bundler)
|
|
270
|
-
Bundler.with_clean_env
|
|
271
|
+
bundler_method = Bundler.respond_to?(:with_unbundled_env) ? :with_unbundled_env : :with_clean_env
|
|
272
|
+
Bundler.public_send(bundler_method) do
|
|
271
273
|
`#{vagrant_cmd}`
|
|
272
274
|
end
|
|
273
275
|
else
|
data/lib/itamae/node.rb
CHANGED
data/lib/itamae/notification.rb
CHANGED
|
@@ -30,8 +30,7 @@ module Itamae
|
|
|
30
30
|
|
|
31
31
|
def validate!
|
|
32
32
|
unless [:delay, :delayed, :immediately].include?(timing)
|
|
33
|
-
|
|
34
|
-
abort
|
|
33
|
+
raise ArgumentError, "'#{timing}' is not valid notification timing. (Valid option is delayed or immediately)"
|
|
35
34
|
end
|
|
36
35
|
end
|
|
37
36
|
end
|
data/lib/itamae/recipe.rb
CHANGED
|
@@ -122,7 +122,7 @@ module Itamae
|
|
|
122
122
|
expanded_path = ::File.join(expanded_path, 'default.rb') if ::Dir.exist?(expanded_path)
|
|
123
123
|
expanded_path.concat('.rb') unless expanded_path.end_with?('.rb')
|
|
124
124
|
candidate_paths = [expanded_path, Recipe.find_recipe_in_gem(target)].compact
|
|
125
|
-
path = candidate_paths.find {|
|
|
125
|
+
path = candidate_paths.find {|candidate_path| File.exist?(candidate_path) }
|
|
126
126
|
|
|
127
127
|
unless path
|
|
128
128
|
raise NotFoundError, "Recipe not found. (#{target})"
|
|
@@ -4,6 +4,7 @@ module Itamae
|
|
|
4
4
|
define_attribute :action, default: :create
|
|
5
5
|
define_attribute :groupname, type: String, default_name: true
|
|
6
6
|
define_attribute :gid, type: Integer
|
|
7
|
+
define_attribute :system_group, type: [TrueClass, FalseClass]
|
|
7
8
|
|
|
8
9
|
def set_current_attributes
|
|
9
10
|
current.exist = exist?
|
|
@@ -21,7 +22,8 @@ module Itamae
|
|
|
21
22
|
end
|
|
22
23
|
else
|
|
23
24
|
options = {
|
|
24
|
-
gid:
|
|
25
|
+
gid: attributes.gid,
|
|
26
|
+
system_group: attributes.system_group,
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
run_specinfra(:add_group, attributes.groupname, options)
|
data/lib/itamae/runner.rb
CHANGED
|
@@ -101,14 +101,14 @@ module Itamae
|
|
|
101
101
|
hash.extend(Hashie::Extensions::DeepMerge)
|
|
102
102
|
|
|
103
103
|
if @options[:ohai]
|
|
104
|
-
unless @backend.run_command("which ohai", error: false).exit_status == 0
|
|
104
|
+
unless @backend.run_command(["which", "ohai"], error: false).exit_status == 0
|
|
105
105
|
# install Ohai
|
|
106
106
|
Itamae.logger.info "Installing Chef package... (to use Ohai)"
|
|
107
107
|
@backend.run_command("curl -L https://omnitruck.chef.io/install.sh | bash")
|
|
108
108
|
end
|
|
109
109
|
|
|
110
110
|
Itamae.logger.info "Loading node data via ohai..."
|
|
111
|
-
hash.merge!(JSON.parse(@backend.run_command("ohai
|
|
111
|
+
hash.merge!(JSON.parse(@backend.run_command(["ohai"], error: false).stdout))
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
@options.fetch(:node_json, []).each do |name|
|
data/lib/itamae/version.rb
CHANGED
|
@@ -7,6 +7,16 @@ describe user("itamae") do
|
|
|
7
7
|
it { should have_login_shell '/bin/dash' }
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
+
describe user("itamae_system") do
|
|
11
|
+
it { should exist }
|
|
12
|
+
it { should be_is_system_user }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe group("itamae_system") do
|
|
16
|
+
it { should exist }
|
|
17
|
+
it { should be_is_system_group }
|
|
18
|
+
end
|
|
19
|
+
|
|
10
20
|
describe file('/tmp/included_recipe') do
|
|
11
21
|
it { should be_file }
|
|
12
22
|
end
|
|
@@ -77,34 +87,34 @@ describe file('/tmp/never_exist2') do
|
|
|
77
87
|
it { should_not be_file }
|
|
78
88
|
end
|
|
79
89
|
|
|
80
|
-
describe file('/tmp/http_request.html')
|
|
90
|
+
describe file('/tmp/http_request.html') do
|
|
81
91
|
it { should be_file }
|
|
82
|
-
its(:content) { should match(/"from":\s*"itamae"/) }
|
|
92
|
+
its(:content) { should match(/"from":\s*\[?\s*"itamae"/) }
|
|
83
93
|
end
|
|
84
94
|
|
|
85
|
-
describe file('/tmp/http_request_delete.html')
|
|
95
|
+
describe file('/tmp/http_request_delete.html') do
|
|
86
96
|
it { should be_file }
|
|
87
|
-
its(:content) { should match(/"from":\s*"itamae"/) }
|
|
97
|
+
its(:content) { should match(/"from":\s*\[?\s*"itamae"/) }
|
|
88
98
|
end
|
|
89
99
|
|
|
90
|
-
describe file('/tmp/http_request_post.html')
|
|
100
|
+
describe file('/tmp/http_request_post.html') do
|
|
91
101
|
it { should be_file }
|
|
92
|
-
its(:content) { should match(/"from":\s*"itamae"/) }
|
|
93
|
-
its(:content) { should match(/"love":\s*"sushi"/) }
|
|
102
|
+
its(:content) { should match(/"from":\s*\[?\s*"itamae"/) }
|
|
103
|
+
its(:content) { should match(/"love":\s*\[?\s*"sushi"/) }
|
|
94
104
|
end
|
|
95
105
|
|
|
96
|
-
describe file('/tmp/http_request_put.html')
|
|
106
|
+
describe file('/tmp/http_request_put.html') do
|
|
97
107
|
it { should be_file }
|
|
98
|
-
its(:content) { should match(/"from":\s*"itamae"/) }
|
|
99
|
-
its(:content) { should match(/"love":\s*"sushi"/) }
|
|
108
|
+
its(:content) { should match(/"from":\s*\[?\s*"itamae"/) }
|
|
109
|
+
its(:content) { should match(/"love":\s*\[?\s*"sushi"/) }
|
|
100
110
|
end
|
|
101
111
|
|
|
102
|
-
describe file('/tmp/http_request_headers.html')
|
|
112
|
+
describe file('/tmp/http_request_headers.html') do
|
|
103
113
|
it { should be_file }
|
|
104
|
-
its(:content) { should match(/"User-Agent":\s*"Itamae"/) }
|
|
114
|
+
its(:content) { should match(/"User-Agent":\s*\[?\s*"Itamae"/) }
|
|
105
115
|
end
|
|
106
116
|
|
|
107
|
-
xdescribe file('/tmp/http_request_redirect.html')
|
|
117
|
+
xdescribe file('/tmp/http_request_redirect.html') do
|
|
108
118
|
it { should be_file }
|
|
109
119
|
its(:content) { should match(/"from":\s*"itamae"/) }
|
|
110
120
|
end
|
|
@@ -84,25 +84,25 @@ end
|
|
|
84
84
|
|
|
85
85
|
###
|
|
86
86
|
|
|
87
|
-
describe file('/tmp/http_request.html')
|
|
87
|
+
describe file('/tmp/http_request.html') do
|
|
88
88
|
it { should be_file }
|
|
89
89
|
it { should be_owned_by "ordinary_san" }
|
|
90
90
|
it { should be_grouped_into "ordinary_san" }
|
|
91
|
-
its(:content) { should match(/"from":\s*"itamae"/) }
|
|
91
|
+
its(:content) { should match(/"from":\s*\[?\s*"itamae"/) }
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
describe file('/tmp/http_request_root.html')
|
|
94
|
+
describe file('/tmp/http_request_root.html') do
|
|
95
95
|
it { should be_file }
|
|
96
96
|
it { should be_owned_by "root" }
|
|
97
97
|
it { should be_grouped_into "root" }
|
|
98
|
-
its(:content) { should match(/"from":\s*"itamae"/) }
|
|
98
|
+
its(:content) { should match(/"from":\s*\[?\s*"itamae"/) }
|
|
99
99
|
end
|
|
100
100
|
|
|
101
101
|
%w[/tmp/http_request_another_ordinary.html /tmp/http_request_another_ordinary_with_root.html].each do |path|
|
|
102
|
-
describe file(path)
|
|
102
|
+
describe file(path) do
|
|
103
103
|
it { should be_file }
|
|
104
104
|
it { should be_owned_by "itamae" }
|
|
105
105
|
it { should be_grouped_into "itamae" }
|
|
106
|
-
its(:content) { should match(/"from":\s*"itamae"/) }
|
|
106
|
+
its(:content) { should match(/"from":\s*\[?\s*"itamae"/) }
|
|
107
107
|
end
|
|
108
108
|
end
|
|
@@ -39,6 +39,17 @@ user "create itamae2 user with create home directory" do
|
|
|
39
39
|
shell "/bin/sh"
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
group "create itamae_system group" do
|
|
43
|
+
groupname "itamae_system"
|
|
44
|
+
system_group true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
user "create itamae_system user" do
|
|
48
|
+
gid "itamae_system"
|
|
49
|
+
username "itamae_system"
|
|
50
|
+
system_user true
|
|
51
|
+
end
|
|
52
|
+
|
|
42
53
|
######
|
|
43
54
|
|
|
44
55
|
package 'jq' do
|
|
@@ -192,39 +203,41 @@ end
|
|
|
192
203
|
|
|
193
204
|
######
|
|
194
205
|
|
|
195
|
-
|
|
196
|
-
http_request "/tmp/http_request.html" do
|
|
197
|
-
url "https://httpbin.org/get?from=itamae"
|
|
198
|
-
end
|
|
206
|
+
httpbin_url = ENV.fetch("HTTPBIN_URL") { "http://127.0.0.1:#{ENV.fetch('HTTPBIN_HOST_PORT', '8080')}" }
|
|
199
207
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
end
|
|
208
|
+
http_request "/tmp/http_request.html" do
|
|
209
|
+
url "#{httpbin_url}/get?from=itamae"
|
|
210
|
+
end
|
|
204
211
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
end
|
|
212
|
+
http_request "/tmp/http_request_delete.html" do
|
|
213
|
+
action :delete
|
|
214
|
+
url "#{httpbin_url}/delete?from=itamae"
|
|
215
|
+
end
|
|
210
216
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
217
|
+
http_request "/tmp/http_request_post.html" do
|
|
218
|
+
action :post
|
|
219
|
+
headers "Content-Type" => "application/x-www-form-urlencoded"
|
|
220
|
+
message "love=sushi"
|
|
221
|
+
url "#{httpbin_url}/post?from=itamae"
|
|
222
|
+
end
|
|
216
223
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
224
|
+
http_request "/tmp/http_request_put.html" do
|
|
225
|
+
action :put
|
|
226
|
+
headers "Content-Type" => "application/x-www-form-urlencoded"
|
|
227
|
+
message "love=sushi"
|
|
228
|
+
url "#{httpbin_url}/put?from=itamae"
|
|
229
|
+
end
|
|
221
230
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
# end
|
|
231
|
+
http_request "/tmp/http_request_headers.html" do
|
|
232
|
+
headers "User-Agent" => "Itamae"
|
|
233
|
+
url "#{httpbin_url}/get"
|
|
226
234
|
end
|
|
227
235
|
|
|
236
|
+
# http_request "/tmp/http_request_redirect.html" do
|
|
237
|
+
# redirect_limit 1
|
|
238
|
+
# url "#{httpbin_url}/redirect-to?url=#{httpbin_url}/get%3Ffrom%3Ditamae"
|
|
239
|
+
# end
|
|
240
|
+
|
|
228
241
|
link "/tmp-link" do
|
|
229
242
|
to "/tmp"
|
|
230
243
|
end
|
|
@@ -83,29 +83,29 @@ end
|
|
|
83
83
|
|
|
84
84
|
###
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
86
|
+
httpbin_url = ENV.fetch("HTTPBIN_URL") { "http://127.0.0.1:#{ENV.fetch('HTTPBIN_HOST_PORT', '8080')}" }
|
|
87
|
+
|
|
88
|
+
http_request "/tmp/http_request.html" do
|
|
89
|
+
url "#{httpbin_url}/get?from=itamae"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
http_request "/tmp/http_request_root.html" do
|
|
93
|
+
user 'root'
|
|
94
|
+
owner 'root'
|
|
95
|
+
group 'root'
|
|
96
|
+
url "#{httpbin_url}/get?from=itamae"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
http_request "/tmp/http_request_another_ordinary.html" do
|
|
100
|
+
user 'itamae'
|
|
101
|
+
owner 'itamae'
|
|
102
|
+
group 'itamae'
|
|
103
|
+
url "#{httpbin_url}/get?from=itamae"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
http_request "/tmp/http_request_another_ordinary_with_root.html" do
|
|
107
|
+
user 'root'
|
|
108
|
+
owner 'itamae'
|
|
109
|
+
group 'itamae'
|
|
110
|
+
url "#{httpbin_url}/get?from=itamae"
|
|
111
111
|
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Manage a local mccutchen/go-httpbin container for the integration tests.
|
|
2
|
+
#
|
|
3
|
+
# The public httpbin.org service is flaky and, under the CI matrix, receives
|
|
4
|
+
# so many simultaneous requests that it returns 500s. Running an httpbin-compatible
|
|
5
|
+
# server locally removes that external dependency.
|
|
6
|
+
#
|
|
7
|
+
# The `http_request` resource issues the HTTP request from the itamae process
|
|
8
|
+
# itself (`Net::HTTP`), so the reachable URL differs per backend:
|
|
9
|
+
# - docker backend: itamae runs on the host -> host_url (published port)
|
|
10
|
+
# - local backend: itamae runs in a container -> NETWORK_URL (shared network)
|
|
11
|
+
# The container therefore exposes both a published loopback port and a shared network.
|
|
12
|
+
module HttpbinServer
|
|
13
|
+
IMAGE = 'ghcr.io/mccutchen/go-httpbin:2.24.0'
|
|
14
|
+
NETWORK = 'itamae-test'
|
|
15
|
+
CONTAINER = 'httpbin'
|
|
16
|
+
PORT = 8080 # container port; also the port in NETWORK_URL
|
|
17
|
+
NETWORK_URL = "http://#{CONTAINER}:#{PORT}".freeze # reachable from a container on NETWORK
|
|
18
|
+
|
|
19
|
+
module_function
|
|
20
|
+
|
|
21
|
+
# Host port to publish go-httpbin on. Overridable so local runs can avoid a
|
|
22
|
+
# port already in use on the host (CI runners always have the default free).
|
|
23
|
+
def host_port
|
|
24
|
+
ENV.fetch('HTTPBIN_HOST_PORT', PORT.to_s)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# URL reachable from the itamae host (docker backend). Kept in sync with the
|
|
28
|
+
# recipe default in spec/integration/recipes/*.rb.
|
|
29
|
+
def host_url
|
|
30
|
+
"http://127.0.0.1:#{host_port}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def start
|
|
34
|
+
create_network
|
|
35
|
+
remove_container # drop any stale container from a previous run
|
|
36
|
+
run! 'docker', 'run', '-d', '--name', CONTAINER,
|
|
37
|
+
'--network', NETWORK,
|
|
38
|
+
'-p', "127.0.0.1:#{host_port}:#{PORT}",
|
|
39
|
+
IMAGE
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def stop
|
|
43
|
+
remove_container
|
|
44
|
+
quiet 'docker', 'network', 'rm', NETWORK
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def create_network
|
|
48
|
+
return if system('docker', 'network', 'inspect', NETWORK, out: File::NULL, err: File::NULL)
|
|
49
|
+
run! 'docker', 'network', 'create', NETWORK
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def remove_container
|
|
53
|
+
quiet 'docker', 'rm', '-f', CONTAINER
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def run!(*cmd)
|
|
57
|
+
system(*cmd) or raise "command failed: #{cmd.join(' ')}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def quiet(*cmd)
|
|
61
|
+
system(*cmd, out: File::NULL, err: File::NULL)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -10,22 +10,27 @@ namespace 'spec:integration:local' do
|
|
|
10
10
|
next
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
HttpbinServer.start
|
|
14
|
+
ENV['HTTPBIN_URL'] = HttpbinServer::NETWORK_URL
|
|
15
|
+
begin
|
|
16
|
+
IntegrationLocalSpecRunner.new(
|
|
15
17
|
[
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
[
|
|
19
|
+
"spec/integration/recipes/default.rb",
|
|
20
|
+
"spec/integration/recipes/default2.rb",
|
|
21
|
+
"spec/integration/recipes/redefine.rb",
|
|
22
|
+
"spec/integration/recipes/local.rb",
|
|
23
|
+
],
|
|
24
|
+
[
|
|
25
|
+
"--dry-run",
|
|
26
|
+
"spec/integration/recipes/dry_run.rb",
|
|
27
|
+
],
|
|
20
28
|
],
|
|
21
|
-
[
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
['spec/integration/default_spec.rb']
|
|
27
|
-
).run
|
|
28
|
-
|
|
29
|
+
['spec/integration/default_spec.rb']
|
|
30
|
+
).run
|
|
31
|
+
ensure
|
|
32
|
+
HttpbinServer.stop
|
|
33
|
+
end
|
|
29
34
|
end
|
|
30
35
|
|
|
31
36
|
desc 'Run integration test for ordinary user with `itamae local`'
|
|
@@ -36,23 +41,29 @@ namespace 'spec:integration:local' do
|
|
|
36
41
|
next
|
|
37
42
|
end
|
|
38
43
|
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
HttpbinServer.start
|
|
45
|
+
ENV['HTTPBIN_URL'] = HttpbinServer::NETWORK_URL
|
|
46
|
+
begin
|
|
47
|
+
runner = IntegrationLocalSpecRunner.new(
|
|
41
48
|
[
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
[
|
|
50
|
+
"--dry-run",
|
|
51
|
+
"spec/integration/recipes/ordinary_user.rb",
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
"spec/integration/recipes/ordinary_user.rb"
|
|
55
|
+
],
|
|
44
56
|
],
|
|
45
|
-
[
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
runner.run
|
|
57
|
+
['spec/integration/ordinary_user_spec.rb'],
|
|
58
|
+
user: 'ordinary_san'
|
|
59
|
+
)
|
|
60
|
+
runner.docker_exec 'useradd', 'ordinary_san', '-p', '*'
|
|
61
|
+
runner.docker_exec 'useradd', 'itamae', '-p', '*', '--create-home'
|
|
62
|
+
runner.docker_exec 'sh', '-c', 'echo "ordinary_san ALL=(ALL:ALL) NOPASSWD: ALL" >> /etc/sudoers'
|
|
63
|
+
runner.run
|
|
64
|
+
ensure
|
|
65
|
+
HttpbinServer.stop
|
|
66
|
+
end
|
|
56
67
|
end
|
|
57
68
|
end
|
|
58
69
|
|
|
@@ -78,7 +89,7 @@ class IntegrationLocalSpecRunner
|
|
|
78
89
|
|
|
79
90
|
def docker_run
|
|
80
91
|
mount_dir = Pathname(__dir__).join('../').to_s
|
|
81
|
-
sh 'docker', 'run', '--env', '
|
|
92
|
+
sh 'docker', 'run', '--env', 'HTTPBIN_URL', '--network', HttpbinServer::NETWORK, '--privileged', '-d', '--name', CONTAINER_NAME, '-v', "#{mount_dir}:/itamae", "ruby:#{@ruby_version}", 'sleep', '1d'
|
|
82
93
|
end
|
|
83
94
|
|
|
84
95
|
def prepare
|
|
@@ -115,6 +126,6 @@ class IntegrationLocalSpecRunner
|
|
|
115
126
|
end
|
|
116
127
|
|
|
117
128
|
def docker_exec(*cmd, options: [])
|
|
118
|
-
sh 'docker', 'exec', '--env', 'LANG=en_US.utf8', '--env', '
|
|
129
|
+
sh 'docker', 'exec', '--env', 'LANG=en_US.utf8', '--env', 'HTTPBIN_URL', *options, CONTAINER_NAME, *cmd
|
|
119
130
|
end
|
|
120
131
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: itamae
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.15.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ryota Arai
|
|
@@ -31,7 +31,7 @@ dependencies:
|
|
|
31
31
|
requirements:
|
|
32
32
|
- - ">="
|
|
33
33
|
- !ruby/object:Gem::Version
|
|
34
|
-
version: 2.
|
|
34
|
+
version: 2.81.0
|
|
35
35
|
- - "<"
|
|
36
36
|
- !ruby/object:Gem::Version
|
|
37
37
|
version: 3.0.0
|
|
@@ -41,7 +41,7 @@ dependencies:
|
|
|
41
41
|
requirements:
|
|
42
42
|
- - ">="
|
|
43
43
|
- !ruby/object:Gem::Version
|
|
44
|
-
version: 2.
|
|
44
|
+
version: 2.81.0
|
|
45
45
|
- - "<"
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
47
|
version: 3.0.0
|
|
@@ -135,14 +135,14 @@ dependencies:
|
|
|
135
135
|
requirements:
|
|
136
136
|
- - "~>"
|
|
137
137
|
- !ruby/object:Gem::Version
|
|
138
|
-
version: '2.
|
|
138
|
+
version: '2.43'
|
|
139
139
|
type: :development
|
|
140
140
|
prerelease: false
|
|
141
141
|
version_requirements: !ruby/object:Gem::Requirement
|
|
142
142
|
requirements:
|
|
143
143
|
- - "~>"
|
|
144
144
|
- !ruby/object:Gem::Version
|
|
145
|
-
version: '2.
|
|
145
|
+
version: '2.43'
|
|
146
146
|
- !ruby/object:Gem::Dependency
|
|
147
147
|
name: pry-byebug
|
|
148
148
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -209,6 +209,7 @@ extensions: []
|
|
|
209
209
|
extra_rdoc_files: []
|
|
210
210
|
files:
|
|
211
211
|
- ".github/dependabot.yml"
|
|
212
|
+
- ".github/workflows/docker-daemon.json"
|
|
212
213
|
- ".github/workflows/release.yml"
|
|
213
214
|
- ".github/workflows/test.yml"
|
|
214
215
|
- ".github/workflows/test_main.yml"
|
|
@@ -309,6 +310,7 @@ files:
|
|
|
309
310
|
- spec/unit/lib/itamae/resource_spec.rb
|
|
310
311
|
- spec/unit/lib/itamae/runner_spec.rb
|
|
311
312
|
- spec/unit/spec_helper.rb
|
|
313
|
+
- tasks/httpbin_server.rb
|
|
312
314
|
- tasks/integration_local_spec.rb
|
|
313
315
|
homepage: https://itamae.kitchen/
|
|
314
316
|
licenses:
|
|
@@ -332,7 +334,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
332
334
|
- !ruby/object:Gem::Version
|
|
333
335
|
version: '0'
|
|
334
336
|
requirements: []
|
|
335
|
-
rubygems_version:
|
|
337
|
+
rubygems_version: 4.0.16
|
|
336
338
|
specification_version: 4
|
|
337
339
|
summary: Simple Configuration Management Tool
|
|
338
340
|
test_files:
|