aircon 0.1.1 → 0.2.1
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/README.md +8 -3
- data/lib/aircon/cli.rb +5 -1
- data/lib/aircon/commands/up.rb +46 -12
- data/lib/aircon/configuration.rb +18 -3
- data/lib/aircon/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b5b3b124b494117995209a018929128c6f19659813c182d8cf204a29b695bc97
|
|
4
|
+
data.tar.gz: ffd8e8323e460a77a007abe18f43cee07080b78156da9759fd3f22773310860c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 749d093f93485cbbc99b3280be49d8921049b5ad107ccc17170f6522d47eefe358bde6ba17afa74c270d289133feb47673486a4dc28e39f8f42fa614e9f9f5b4
|
|
7
|
+
data.tar.gz: '091c7ce2565eea7113f4c3d8263a0c31a72349e5c05ff681f373bd0c9e40b82feda3f031f507a5ec74304e40161891d98034f1a6b8167616f2dc031a03b41676'
|
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.
|
|
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
|
-
#
|
|
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
|
-
| `
|
|
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 |
|
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
|
|
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
|
data/lib/aircon/commands/up.rb
CHANGED
|
@@ -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.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
@@ -142,14 +170,20 @@ module Aircon
|
|
|
142
170
|
system("docker", "exec", container, "git", "config", "--global",
|
|
143
171
|
"url.#{authed}.insteadOf", "git@github.com:")
|
|
144
172
|
end
|
|
145
|
-
#
|
|
146
|
-
|
|
147
|
-
if
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
173
|
+
# Skip checkout if already on the target branch (host was on this branch)
|
|
174
|
+
current_branch, = Open3.capture2("docker", "exec", container, "git", "rev-parse", "--abbrev-ref", "HEAD")
|
|
175
|
+
if current_branch.strip != branch
|
|
176
|
+
# Check if branch exists on remote; if so, check it out, otherwise create new
|
|
177
|
+
_, status = Open3.capture2("docker", "exec", container, "git", "ls-remote", "--heads", "origin", branch)
|
|
178
|
+
if status.success? && !_.strip.empty?
|
|
179
|
+
system("docker", "exec", container, "git", "fetch", "origin", branch)
|
|
180
|
+
system("docker", "exec", container, "git", "checkout", "-f", "-b", branch, "origin/#{branch}")
|
|
181
|
+
system("docker", "exec", container, "git", "clean", "-fd")
|
|
182
|
+
else
|
|
183
|
+
system("docker", "exec", container, "git", "fetch", "origin", "main")
|
|
184
|
+
system("docker", "exec", container, "git", "checkout", "-f", "--no-track", "-b", branch, "origin/main")
|
|
185
|
+
system("docker", "exec", container, "git", "clean", "-fd")
|
|
186
|
+
end
|
|
153
187
|
end
|
|
154
188
|
|
|
155
189
|
# If you have the official anthropic marketplace plugin installed, it will always make a call to the anthropic github repo on claude startup. It uses SSH, but it should be https for universal compatibility since its a public repository.
|
data/lib/aircon/configuration.rb
CHANGED
|
@@ -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, :
|
|
26
|
-
:claude_config_path, :claude_dir_path, :service, :git_email,
|
|
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
|
data/lib/aircon/version.rb
CHANGED