aircon 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a82194933fb47bffb6daacf310c4f244ab1d9a25db2f9b32ec25edd578c4a06
4
- data.tar.gz: 0222cec49d295996c4066d56518ee84fff8747c8189713f4581a17e103ac8c61
3
+ metadata.gz: 9d51871327109dc3145685c8ff1b808658592ebc527baaab88fc7d54d3a2b5d1
4
+ data.tar.gz: 60ddec11a21faf68d0c8d43beae7a424d935e82df72c0be5e09ff90a2102829b
5
5
  SHA512:
6
- metadata.gz: 937ab4b996d225743372ed6d5c25fe319c2305dfd84bca24b95671465ae1846ab20043f2ad7e442096e09cb060840e2a2bd948b5b723932469bb99f744c85dd4
7
- data.tar.gz: 7623f3fd28dd5adffbd85908447c1d5e640a952a9eb2c27d0c31f8de7f2407d76a22762f34f50b0f3f4aab3fe750b5e34e33cc69fdb29575804bee222aa02629
6
+ metadata.gz: 23e48ab217b5c812dffac335ff9dbebb72233379dbf601fe86369e2fa38960b798d1bd325ddbde3164951927a798a60ca9c129aa87b49f2e2333cdafae5a9934
7
+ data.tar.gz: 8d93936b25ce81e580a5741706cd3e7a6cd4ae1bc46776e6db1a25864a51ca2c08b42f33d8e583b84c05bd040c2e42fa646e3c7d03d486ef7723ca4ecfbae8b8
data/README.md CHANGED
@@ -97,7 +97,7 @@ aircon up my-feature -d # start detached (no interactive shell)
97
97
  5. Installs Claude Code inside the container if not already present (`curl -fsSL https://claude.ai/install.sh | bash`).
98
98
  6. Adds `~/.local/bin` to `PATH` in `/etc/bash.bashrc` so `claude` is available in all sessions.
99
99
  7. Writes `GH_TOKEN` and `GITHUB_PERSONAL_ACCESS_TOKEN` to `/etc/bash.bashrc` (if `gh_token` is set).
100
- 8. Writes `CLAUDE_CODE_OAUTH_TOKEN` to `/etc/bash.bashrc` (if configured).
100
+ 8. Injects Claude Code credentials based on `credentials_source`: copies from macOS Keychain (`keychain`), from `~/.claude/.credentials.json` (`file`), or sets `CLAUDE_CODE_OAUTH_TOKEN` in `/etc/bash.bashrc` (`oauth_token`).
101
101
  9. Sets `git config user.email` and `git config user.name` globally inside the container.
102
102
  10. Configures `git` to authenticate GitHub URLs with your token (covers both `https://github.com/` and `git@github.com:`).
103
103
  11. Checks out the branch:
@@ -177,7 +177,11 @@ app_name: my-app
177
177
  # GitHub personal access token — authenticates git and gh inside the container
178
178
  gh_token: <%= ENV['GITHUB_TOKEN'] %>
179
179
 
180
- # Claude Code OAuth token set as CLAUDE_CODE_OAUTH_TOKEN inside the container
180
+ # How to obtain Claude Code credentials: "keychain" (macOS), "file", or "oauth_token"
181
+ credentials_source: keychain
182
+
183
+ # Claude Code OAuth token — used when credentials_source is "oauth_token"
184
+ # Falls back to CLAUDE_CODE_OAUTH_TOKEN env var if not set here.
181
185
  claude_code_oauth_token: <%= ENV['CLAUDE_CODE_OAUTH_TOKEN'] %>
182
186
 
183
187
  # Workspace folder path inside the container
@@ -210,7 +214,8 @@ init_script: .aircon/aircon_init.sh
210
214
  | `compose_file` | `.aircon/docker-compose.yml` | Docker Compose file to use |
211
215
  | `app_name` | basename of cwd | App name passed to Compose as `AIRCON_APP_NAME` |
212
216
  | `gh_token` | `nil` | GitHub token; sets `GH_TOKEN` and `GITHUB_PERSONAL_ACCESS_TOKEN` in the container |
213
- | `claude_code_oauth_token` | `nil` | Claude Code OAuth token; sets `CLAUDE_CODE_OAUTH_TOKEN` in the container |
217
+ | `credentials_source` | `keychain` | `keychain` (macOS), `file` (~/.claude/.credentials.json), or `oauth_token` |
218
+ | `claude_code_oauth_token` | `nil` | OAuth token for `oauth_token` mode; falls back to `CLAUDE_CODE_OAUTH_TOKEN` env var |
214
219
  | `workspace_path` | `/workspace` | Workspace folder path inside the container |
215
220
  | `claude_config_path` | `~/.claude.json` | Host path to `claude.json` |
216
221
  | `claude_dir_path` | `~/.claude` | Host path to `.claude/` directory |
@@ -233,12 +238,7 @@ init_script: .aircon/aircon_init.sh
233
238
  ## Releasing to RubyGems
234
239
 
235
240
  1. Bump the version in `lib/aircon/version.rb`.
236
- 2. Build and push:
237
-
238
- ```bash
239
- gem build aircon.gemspec
240
- gem push aircon-<version>.gem
241
- ```
241
+ 2. run `bundle exec rake release`.
242
242
 
243
243
  You'll be prompted for your RubyGems credentials on first push. Subsequent pushes use the stored API key at `~/.gem/credentials`.
244
244
 
data/lib/aircon/cli.rb CHANGED
@@ -70,7 +70,11 @@ module Aircon
70
70
  # GitHub personal access token (supports ERB)
71
71
  # gh_token: <%= ENV['GITHUB_TOKEN'] %>
72
72
 
73
- # Claude Code OAuth token (supports ERB)
73
+ # How to obtain Claude Code credentials: "keychain" (macOS), "file", or "oauth_token"
74
+ # credentials_source: keychain
75
+
76
+ # Claude Code OAuth token — used when credentials_source is "oauth_token" (supports ERB)
77
+ # Falls back to CLAUDE_CODE_OAUTH_TOKEN env var if not set here.
74
78
  # claude_code_oauth_token: <%= ENV['CLAUDE_CODE_OAUTH_TOKEN'] %>
75
79
 
76
80
  # Workspace folder path inside the container
@@ -185,6 +189,7 @@ module Aircon
185
189
  COMPOSE_TEMPLATE = <<~'YAML'
186
190
  services:
187
191
  app:
192
+ image: ${AIRCON_APP_NAME:-aircon}-app:latest
188
193
  build:
189
194
  context: ..
190
195
  dockerfile: .aircon/Dockerfile
@@ -80,6 +80,8 @@ module Aircon
80
80
  FileUtils.mkdir_p(File.join(staging, ".claude"))
81
81
  end
82
82
 
83
+ write_credentials(File.join(staging, ".claude", ".credentials.json"))
84
+
83
85
  home = @config.container_home
84
86
  rewrite_paths(staging, home)
85
87
  user = @config.container_user
@@ -106,6 +108,29 @@ module Aircon
106
108
  end
107
109
  end
108
110
 
111
+ def write_credentials(dest)
112
+ case @config.credentials_source
113
+ when "keychain"
114
+ out, status = Open3.capture2(
115
+ "security", "find-generic-password",
116
+ "-a", ENV.fetch("USER", "unknown"),
117
+ "-w", "-s", "Claude Code-credentials"
118
+ )
119
+ if status.success?
120
+ File.write(dest, out)
121
+ else
122
+ warn "Warning: Could not read credentials from keychain."
123
+ end
124
+ when "file"
125
+ src = File.expand_path("~/.claude/.credentials.json")
126
+ if File.exist?(src)
127
+ FileUtils.cp(src, dest)
128
+ else
129
+ warn "Warning: Credentials file not found at #{src}"
130
+ end
131
+ end
132
+ end
133
+
109
134
  def setup_container(container, branch)
110
135
  home = @config.container_home
111
136
 
@@ -125,10 +150,13 @@ module Aircon
125
150
  "echo 'export GITHUB_PERSONAL_ACCESS_TOKEN=\"#{@config.gh_token}\"' >> /etc/bash.bashrc")
126
151
  end
127
152
 
128
- if @config.claude_code_oauth_token && !@config.claude_code_oauth_token.to_s.empty?
129
- system("docker", "exec", "-u", "root", container, "bash", "-c",
130
- "grep -qF 'export CLAUDE_CODE_OAUTH_TOKEN=' /etc/bash.bashrc 2>/dev/null || " \
131
- "echo 'export CLAUDE_CODE_OAUTH_TOKEN=\"#{@config.claude_code_oauth_token}\"' >> /etc/bash.bashrc")
153
+ if @config.credentials_source == "oauth_token"
154
+ token = @config.claude_code_oauth_token || ENV["CLAUDE_CODE_OAUTH_TOKEN"]
155
+ if token && !token.to_s.empty?
156
+ system("docker", "exec", "-u", "root", container, "bash", "-c",
157
+ "grep -qF 'export CLAUDE_CODE_OAUTH_TOKEN=' /etc/bash.bashrc 2>/dev/null || " \
158
+ "echo 'export CLAUDE_CODE_OAUTH_TOKEN=\"#{token}\"' >> /etc/bash.bashrc")
159
+ end
132
160
  end
133
161
 
134
162
  # Configure git and create branch
@@ -6,11 +6,13 @@ require "erb"
6
6
  module Aircon
7
7
  class Configuration
8
8
  CONFIG_FILE = ".aircon/aircon.yml"
9
+ VALID_CREDENTIALS_SOURCES = %w[keychain file oauth_token].freeze
9
10
 
10
11
  DEFAULTS = {
11
12
  "compose_file" => ".aircon/docker-compose.yml",
12
13
  "app_name" => nil,
13
14
  "gh_token" => nil,
15
+ "credentials_source" => "keychain",
14
16
  "claude_code_oauth_token" => nil,
15
17
  "workspace_path" => nil,
16
18
  "claude_config_path" => "~/.claude.json",
@@ -22,9 +24,9 @@ module Aircon
22
24
  "init_script" => ".aircon/aircon_init.sh"
23
25
  }.freeze
24
26
 
25
- attr_reader :compose_file, :app_name, :gh_token, :claude_code_oauth_token, :workspace_path,
26
- :claude_config_path, :claude_dir_path, :service, :git_email, :git_name,
27
- :container_user, :init_script
27
+ attr_reader :compose_file, :app_name, :gh_token, :credentials_source, :claude_code_oauth_token,
28
+ :workspace_path, :claude_config_path, :claude_dir_path, :service, :git_email,
29
+ :git_name, :container_user, :init_script
28
30
 
29
31
  def initialize(dir: Dir.pwd)
30
32
  attrs = DEFAULTS.dup
@@ -40,6 +42,7 @@ module Aircon
40
42
  @compose_file = attrs["compose_file"]
41
43
  @app_name = attrs["app_name"] || File.basename(dir)
42
44
  @gh_token = attrs["gh_token"]
45
+ @credentials_source = attrs["credentials_source"]
43
46
  @claude_code_oauth_token = attrs["claude_code_oauth_token"]
44
47
  @workspace_path = attrs["workspace_path"] || "/workspace"
45
48
  @claude_config_path = attrs["claude_config_path"]
@@ -49,10 +52,22 @@ module Aircon
49
52
  @git_name = attrs["git_name"]
50
53
  @container_user = attrs["container_user"]
51
54
  @init_script = attrs["init_script"]
55
+
56
+ validate!
52
57
  end
53
58
 
54
59
  def container_home
55
60
  @container_user == "root" ? "/root" : "/home/#{@container_user}"
56
61
  end
62
+
63
+ private
64
+
65
+ def validate!
66
+ return if VALID_CREDENTIALS_SOURCES.include?(@credentials_source)
67
+
68
+ raise ArgumentError,
69
+ "Invalid credentials_source: #{@credentials_source.inspect}. " \
70
+ "Must be one of: #{VALID_CREDENTIALS_SOURCES.join(', ')}"
71
+ end
57
72
  end
58
73
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Aircon
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aircon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philip Nguyen
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '1.3'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rake
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: rspec
28
42
  requirement: !ruby/object:Gem::Requirement