putpaws 0.1.0 → 0.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cbb1de2cdc7c119427ca5dfa073f5e8d07f504ff73f93a464f2912a8ef5168ed
4
- data.tar.gz: 042f6202ae3cb0396b0ab7210b2d299504f7e099e8a0cf43d9c6314f0b6384ba
3
+ metadata.gz: d38b16d5a4fc24915b8bde117f9fda98d0fb6418989eebf98385e86ad724dd32
4
+ data.tar.gz: c3ee3c20989376d82da51e970aa23ab2995af32c39b0e37524c7fa915cf9a8f9
5
5
  SHA512:
6
- metadata.gz: 12b7e736fe78e9a49569627fd296e9f5540cbea849f8b78a7edbb56c8652de73b82d1b2ac92fef279a682f64e4e83e061ba20e2c3afe1b3c99dce7fd35526fe1
7
- data.tar.gz: efad6347afdc46833694e48070afd0330fe42f8bb0bc16638b6b88a7d5b6d1a091ce50f5ce1ddec4f92cc07e54796f42cc9802c2b5aa07ed4af1d96ba5ebd171
6
+ metadata.gz: e2d00b74e706f5c9192a455bd7919845c4afeb06d2c3db9cd17c2030689cdc2858cc439efb6dd360cc8b9d4eb3fe6fceb6e9a62fb361de1ff0b8d30be4be10c2
7
+ data.tar.gz: 49fbf55b6659a02a563792d4ab406bbe1f583e84abf82eee10c5c52b12bbcb3da42d539fb1346dc10dd4a4a6ba70df427b8ddc5f1b5edf7c07e775c4b314fb72
data/README.md CHANGED
@@ -61,6 +61,14 @@ SSM parameters (Ex: `/{service_name}/RAILS_MASTER_KEY`) and putpaws never touche
61
61
  A scheduler role is always created too, and the `target` entry of infra.json carries it,
62
62
  so `scheduler:deploy` works any time by just writing `.putpaws/schedule.json`.
63
63
 
64
+ The CPU architecture is declared once as `cpu_architecture` (`ARM64` / `X86_64`) in the preset
65
+ defaults (overridable per service via `overrides`), and applied consistently to both
66
+ task definitions (`runtimePlatform`) and CodeBuild (environment type and image).
67
+ The bundled preset defaults to `ARM64`; projects whose copied preset predates this setting
68
+ fall back to `X86_64` so nothing changes silently. When switching an existing service to ARM,
69
+ run the CI build right after `up` so an ARM image exists before tasks roll
70
+ (`devops.environment_type` / `environment_image` can override the derivation for staged migration).
71
+
64
72
  CodeBuild is created as CI: builds run inside the VPC with the same subnets and
65
73
  security group as the service, so `db:migrate` against RDS works from the build.
66
74
  Note that the private subnets need a NAT gateway (or VPC endpoints) so that
@@ -147,6 +155,10 @@ bundle exec putpaws awesome-api-staging ecs:run cmd='bundle exec rake db:migrate
147
155
 
148
156
  # Pass wait=true to wait until the task stops and check the exit code
149
157
  bundle exec putpaws awesome-api-staging ecs:run cmd='bundle exec rake db:migrate' wait=true
158
+
159
+ # Override the task size for this launch only (must be a valid Fargate combination).
160
+ # Also available on ecs:shell.
161
+ bundle exec putpaws awesome-api-staging ecs:run cmd='bundle exec rake heavy:job' cpu=1024 memory=2048 wait=true
150
162
  ```
151
163
 
152
164
  Launch a temporary task for operation and attach to it (like SSH-ing into the environment).
@@ -170,6 +182,15 @@ so you can identify them on the AWS console and on the task list of `ecs:attach`
170
182
  `ecs:run` and `ecs:shell` require `network` and `target` in `.putpaws/application.json`,
171
183
  which refer to the `network` / `target` sections of `.putpaws/infra.json` (the same ones used by scheduler).
172
184
 
185
+ ### CodeBuild
186
+
187
+ Start the CI build. By default it is fire-and-forget; pass `wait=true` to wait
188
+ until the build completes (phases are printed) and exit non-zero on failure.
189
+
190
+ ```
191
+ bundle exec putpaws awesome-api-staging code_build:build branch=main wait=true
192
+ ```
193
+
173
194
  ### CloudWatch Logs
174
195
 
175
196
  `tail -f`
@@ -41,6 +41,23 @@ module Putpaws::CodeBuild
41
41
  codebuild_client.start_build(**params)
42
42
  end
43
43
 
44
+ def wait_until_build_complete(build_id, timeout: 3600, interval: 10)
45
+ deadline = Time.now + timeout
46
+ last_phase = nil
47
+ loop do
48
+ res = codebuild_client.batch_get_builds(ids: [build_id])
49
+ build = res.builds.first
50
+ raise "Build not found: #{build_id}" unless build
51
+ if build.current_phase != last_phase
52
+ last_phase = build.current_phase
53
+ puts " phase: #{last_phase}"
54
+ end
55
+ return build if build.build_status != 'IN_PROGRESS'
56
+ raise "Timed out waiting for build to complete: #{build_id}" if Time.now > deadline
57
+ sleep interval
58
+ end
59
+ end
60
+
44
61
  def build_source_version(type:, version:)
45
62
  type = type.to_sym
46
63
  if type == :branch
@@ -15,7 +15,7 @@ namespace :code_build do
15
15
  set :codebuild_project, codebuild_project
16
16
  end
17
17
 
18
- desc "Start build."
18
+ desc "Start build. Pass wait=true to wait until the build completes and fail on error. (Ex) branch=main wait=true"
19
19
  task build: :set_project do
20
20
  aws = Putpaws::CodeBuild::ProjectCommand.config(fetch(:app))
21
21
 
@@ -30,10 +30,22 @@ namespace :code_build do
30
30
  end
31
31
  codebuild_project = fetch(:codebuild_project)
32
32
  aws.codebuild_project = codebuild_project
33
- aws.start_build_with_specific_version(
34
- source_version: source_version,
35
- environment_variables: ENV['env'] ? JSON.parse(ENV['env']) : {},
33
+ res = aws.start_build_with_specific_version(
34
+ source_version: source_version,
35
+ environment_variables: ENV['env'] ? JSON.parse(ENV['env']) : {},
36
36
  buildspec_override: ENV['buildspec']
37
37
  )
38
+ build = res.build
39
+ puts "Build started: #{build.id}"
40
+
41
+ if ENV['wait']
42
+ puts "Waiting for the build to complete..."
43
+ build = aws.wait_until_build_complete(build.id)
44
+ if build.build_status == 'SUCCEEDED'
45
+ puts "Build succeeded: #{build.id}"
46
+ else
47
+ raise "Build #{build.build_status.downcase}: #{build.id}"
48
+ end
49
+ end
38
50
  end
39
51
  end
@@ -50,7 +50,20 @@ module Putpaws::Ecs
50
50
  # Launch a temporary task whose main process is overridden with the given
51
51
  # command, so that the task terminates itself when the command finishes.
52
52
  # started_by and group are set so that temporary tasks are identifiable.
53
- def run_ecs_task(command:, started_by:, group:, container: nil)
53
+ # cpu/memory override the task-level size for this launch only
54
+ # (must be a valid Fargate combination).
55
+ def run_ecs_task(command:, started_by:, group:, container: nil, cpu: nil, memory: nil)
56
+ overrides = {
57
+ container_overrides: [
58
+ {
59
+ name: container || container_name,
60
+ command: ['/bin/sh', '-c', command],
61
+ }
62
+ ]
63
+ }
64
+ overrides[:cpu] = cpu.to_s unless cpu.to_s.strip.empty?
65
+ overrides[:memory] = memory.to_s unless memory.to_s.strip.empty?
66
+
54
67
  res = ecs_client.run_task({
55
68
  cluster: target.cluster,
56
69
  task_definition: target.task_definition,
@@ -66,14 +79,7 @@ module Putpaws::Ecs
66
79
  assign_public_ip: network.assign_public_ip,
67
80
  }
68
81
  },
69
- overrides: {
70
- container_overrides: [
71
- {
72
- name: container || container_name,
73
- command: ['/bin/sh', '-c', command],
74
- }
75
- ]
76
- },
82
+ overrides: overrides,
77
83
  })
78
84
  failure = res.failures.first
79
85
  raise "Failed to run task: #{failure.reason} #{failure.detail}" if failure
@@ -56,7 +56,7 @@ namespace :ecs do
56
56
  system(*cmd)
57
57
  end
58
58
 
59
- desc "Run a command on a temporary ECS task. The task terminates itself when the command finishes. (Ex) cmd='bundle exec rake db:migrate' wait=true"
59
+ desc "Run a command on a temporary ECS task. The task terminates itself when the command finishes. (Ex) cmd='bundle exec rake db:migrate' wait=true cpu=1024 memory=2048"
60
60
  task :run do
61
61
  runner = Putpaws::Ecs::RunCommand.config(fetch(:app))
62
62
  command = ENV['cmd']
@@ -68,7 +68,9 @@ namespace :ecs do
68
68
  command: command,
69
69
  started_by: 'putpaws-run',
70
70
  group: 'putpaws-run',
71
- container: ENV['container']
71
+ container: ENV['container'],
72
+ cpu: ENV['cpu'],
73
+ memory: ENV['memory']
72
74
  )
73
75
  task_id = ecs_task.task_arn.split('/').last
74
76
  puts "Task started: #{task_id} [putpaws-run]"
@@ -119,7 +121,7 @@ namespace :ecs do
119
121
  end
120
122
  end
121
123
 
122
- desc "Launch a temporary ECS task for operation and attach to it. The task terminates itself when ttl passes (default 30m). (Ex) ttl=45m keep=true"
124
+ desc "Launch a temporary ECS task for operation and attach to it. The task terminates itself when ttl passes (default 30m). (Ex) ttl=45m keep=true cpu=256 memory=512"
123
125
  task :shell do
124
126
  runner = Putpaws::Ecs::RunCommand.config(fetch(:app))
125
127
  ttl = Putpaws::Ecs::RunCommand.parse_ttl(ENV['ttl'])
@@ -128,7 +130,9 @@ namespace :ecs do
128
130
  ecs_task = runner.run_ecs_task(
129
131
  command: "sleep #{ttl}",
130
132
  started_by: 'putpaws-shell',
131
- group: 'putpaws-shell'
133
+ group: 'putpaws-shell',
134
+ cpu: ENV['cpu'],
135
+ memory: ENV['memory']
132
136
  )
133
137
  task_id = ecs_task.task_arn.split('/').last
134
138
  puts "Task started: #{task_id} [putpaws-shell]"
@@ -65,7 +65,7 @@ module Putpaws
65
65
  version_id: policy.default_version_id
66
66
  )
67
67
  JSON.parse(URI.decode_www_form_component(ver.policy_version.document))
68
- rescue Aws::IAM::Errors::NoSuchEntityException
68
+ rescue Aws::IAM::Errors::NoSuchEntity, Aws::IAM::Errors::NoSuchEntityException
69
69
  nil
70
70
  end
71
71
 
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "defaults": {
3
+ "cpu_architecture": "ARM64",
3
4
  "cpu": "256",
4
5
  "memory": "512",
5
6
  "desired_count": 1,
@@ -24,7 +25,10 @@
24
25
  "service_task_definition": "app",
25
26
  "devops_defaults": {
26
27
  "source_type": "GITHUB",
27
- "environment_image": "aws/codebuild/standard:7.0",
28
+ "environment_images": {
29
+ "ARM64": "aws/codebuild/amazonlinux-aarch64-standard:3.0",
30
+ "X86_64": "aws/codebuild/standard:7.0"
31
+ },
28
32
  "compute_type": "BUILD_GENERAL1_SMALL",
29
33
  "privileged_mode": true,
30
34
  "buildspec": "buildspec.yml"
@@ -2,6 +2,10 @@
2
2
  "family": "<%= service_name %>-app",
3
3
  "requiresCompatibilities": ["FARGATE"],
4
4
  "networkMode": "awsvpc",
5
+ "runtimePlatform": {
6
+ "cpuArchitecture": "<%= cpu_architecture %>",
7
+ "operatingSystemFamily": "LINUX"
8
+ },
5
9
  "cpu": "<%= cpu %>",
6
10
  "memory": "<%= memory %>",
7
11
  "executionRoleArn": "<%= execution_role_arn %>",
@@ -2,6 +2,10 @@
2
2
  "family": "<%= service_name %>-web",
3
3
  "requiresCompatibilities": ["FARGATE"],
4
4
  "networkMode": "awsvpc",
5
+ "runtimePlatform": {
6
+ "cpuArchitecture": "<%= cpu_architecture %>",
7
+ "operatingSystemFamily": "LINUX"
8
+ },
5
9
  "cpu": "<%= cpu %>",
6
10
  "memory": "<%= memory %>",
7
11
  "executionRoleArn": "<%= execution_role_arn %>",
@@ -92,6 +92,20 @@ module Putpaws
92
92
  preset.devops_defaults.merge(devops.reject{|_, v| v.nil?})
93
93
  end
94
94
 
95
+ CPU_ARCHITECTURES = %w[X86_64 ARM64]
96
+
97
+ # Single source of truth for the CPU architecture, applied to both
98
+ # task definitions (runtimePlatform) and CodeBuild (environment type/image).
99
+ # Falls back to X86_64 when unspecified so that existing projects
100
+ # (whose copied presets/templates predate this setting) keep working.
101
+ def cpu_architecture
102
+ arch = (settings[:cpu_architecture] || 'X86_64').to_s
103
+ unless CPU_ARCHITECTURES.include?(arch)
104
+ raise "Invalid cpu_architecture: #{arch} (available: #{CPU_ARCHITECTURES.join(', ')})"
105
+ end
106
+ arch
107
+ end
108
+
95
109
  def account_id
96
110
  Util.arn_account_id(base[:ecr_repository_arn])
97
111
  end
@@ -5,6 +5,17 @@ module Putpaws
5
5
  module Provision
6
6
  module Resources
7
7
  class CodebuildProject < Base
8
+ # Derived from cpu_architecture; devops.environment_type / environment_image
9
+ # override the derivation (useful for staged arch migration).
10
+ ENVIRONMENT_TYPES = {
11
+ 'ARM64' => 'ARM_CONTAINER',
12
+ 'X86_64' => 'LINUX_CONTAINER',
13
+ }
14
+ DEFAULT_IMAGES = {
15
+ 'ARM64' => 'aws/codebuild/amazonlinux-aarch64-standard:3.0',
16
+ 'X86_64' => 'aws/codebuild/standard:7.0',
17
+ }
18
+
8
19
  def name
9
20
  config.build_project_name
10
21
  end
@@ -13,6 +24,16 @@ module Putpaws
13
24
  config.devops_settings
14
25
  end
15
26
 
27
+ def environment_type
28
+ devops[:environment_type] || ENVIRONMENT_TYPES.fetch(config.cpu_architecture)
29
+ end
30
+
31
+ def environment_image
32
+ devops[:environment_image] ||
33
+ (devops[:environment_images] || {})[config.cpu_architecture.to_sym] ||
34
+ DEFAULT_IMAGES.fetch(config.cpu_architecture)
35
+ end
36
+
16
37
  def security_group_ids
17
38
  ids = state.resources[:security_group_ids]
18
39
  raise "security_group_ids not found in state. Run the service step first." if ids.nil? || ids.empty?
@@ -30,8 +51,8 @@ module Putpaws
30
51
  },
31
52
  artifacts: {type: 'NO_ARTIFACTS'},
32
53
  environment: {
33
- type: 'LINUX_CONTAINER',
34
- image: devops[:environment_image] || 'aws/codebuild/standard:7.0',
54
+ type: environment_type,
55
+ image: environment_image,
35
56
  compute_type: devops[:compute_type] || 'BUILD_GENERAL1_SMALL',
36
57
  privileged_mode: devops.fetch(:privileged_mode, true),
37
58
  },
@@ -60,6 +81,7 @@ module Putpaws
60
81
  current.source&.location != desired_params[:source][:location] ||
61
82
  current.source&.buildspec != desired_params[:source][:buildspec] ||
62
83
  current.service_role != desired_params[:service_role] ||
84
+ current.environment&.type != desired_params[:environment][:type] ||
63
85
  current.environment&.image != desired_params[:environment][:image] ||
64
86
  current.environment&.compute_type != desired_params[:environment][:compute_type] ||
65
87
  current.vpc_config&.subnets.to_a.sort != desired_params[:vpc_config][:subnets].sort ||
@@ -43,7 +43,7 @@ module Putpaws
43
43
 
44
44
  def current
45
45
  clients.iam.get_role(role_name: name).role
46
- rescue Aws::IAM::Errors::NoSuchEntityException
46
+ rescue Aws::IAM::Errors::NoSuchEntity, Aws::IAM::Errors::NoSuchEntityException
47
47
  nil
48
48
  end
49
49
 
@@ -25,6 +25,9 @@ module Putpaws
25
25
  def current
26
26
  res = clients.ecs.describe_services(cluster: config.cluster_name, services: [name])
27
27
  res.services.detect{|s| s.status == 'ACTIVE'}
28
+ rescue Aws::ECS::Errors::ClusterNotFoundException
29
+ # cluster not created yet (Ex: `ahead` on a fresh setup) -> no service either
30
+ nil
28
31
  end
29
32
 
30
33
  def changed?(current)
@@ -11,7 +11,8 @@ module Putpaws
11
11
  # Variables exposed to taskdef ERB templates.
12
12
  class TemplateContext
13
13
  attr_reader :service_name, :region, :account_id,
14
- :cpu, :memory, :container_port, :image, :nginx_image,
14
+ :cpu, :memory, :container_port, :cpu_architecture,
15
+ :image, :nginx_image,
15
16
  :log_group, :execution_role_arn, :task_role_arn,
16
17
  :env_json, :secrets_json
17
18
 
@@ -23,6 +24,7 @@ module Putpaws
23
24
  @cpu = settings[:cpu].to_s
24
25
  @memory = settings[:memory].to_s
25
26
  @container_port = (settings[:container_port] || 3000).to_i
27
+ @cpu_architecture = config.cpu_architecture
26
28
  @image = config.ecr_image_uri
27
29
  @nginx_image = settings[:nginx_image] || 'public.ecr.aws/nginx/nginx:stable'
28
30
  @log_group = config.log_group
@@ -1,3 +1,3 @@
1
1
  module Putpaws
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.4"
3
3
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: putpaws
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - metheglin
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-09-01 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rake
@@ -209,7 +208,6 @@ homepage: https://rubygems.org/gems/putpaws
209
208
  licenses:
210
209
  - MIT
211
210
  metadata: {}
212
- post_install_message:
213
211
  rdoc_options: []
214
212
  require_paths:
215
213
  - lib
@@ -224,8 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
224
222
  - !ruby/object:Gem::Version
225
223
  version: '0'
226
224
  requirements: []
227
- rubygems_version: 3.4.10
228
- signing_key:
225
+ rubygems_version: 4.0.16
229
226
  specification_version: 4
230
227
  summary: Put your paws up!!
231
228
  test_files: []