1+ import type * as CommandExecutor from "@effect/platform/CommandExecutor"
12import type { PlatformError } from "@effect/platform/Error"
23import * as FileSystem from "@effect/platform/FileSystem"
34import * as Path from "@effect/platform/Path"
45import { Effect , pipe } from "effect"
56
67import type { ProjectConfig , TemplateConfig } from "../core/domain.js"
78import { deriveRepoPathParts } from "../core/domain.js"
9+ import { runDockerInspectContainerIp } from "../shell/docker.js"
810import { readProjectConfig } from "../shell/config.js"
911import type { ConfigDecodeError , ConfigNotFoundError } from "../shell/errors.js"
1012import { resolveBaseDir } from "../shell/paths.js"
@@ -20,16 +22,21 @@ export type ProjectLoadError = PlatformError | ConfigNotFoundError | ConfigDecod
2022
2123export const buildSshCommand = (
2224 config : TemplateConfig ,
23- sshKey : string | null
24- ) : string =>
25- sshKey === null
26- ? `ssh ${ sshOptions } -p ${ config . sshPort } ${ config . sshUser } @localhost`
27- : `ssh -i ${ sshKey } ${ sshOptions } -p ${ config . sshPort } ${ config . sshUser } @localhost`
25+ sshKey : string | null ,
26+ ipAddress ?: string
27+ ) : string => {
28+ const host = ipAddress ?? "localhost"
29+ const port = ipAddress ? 22 : config . sshPort
30+ return sshKey === null
31+ ? `ssh ${ sshOptions } -p ${ port } ${ config . sshUser } @${ host } `
32+ : `ssh -i ${ sshKey } ${ sshOptions } -p ${ port } ${ config . sshUser } @${ host } `
33+ }
2834
2935export type ProjectSummary = {
3036 readonly projectDir : string
3137 readonly config : ProjectConfig
3238 readonly sshCommand : string
39+ readonly ipAddress ?: string | undefined
3340 readonly authorizedKeysPath : string
3441 readonly authorizedKeysExists : boolean
3542}
@@ -45,6 +52,7 @@ export type ProjectItem = {
4552 readonly sshPort : number
4653 readonly targetDir : string
4754 readonly sshCommand : string
55+ readonly ipAddress ?: string | undefined
4856 readonly sshKeyPath : string | null
4957 readonly authorizedKeysPath : string
5058 readonly authorizedKeysExists : boolean
@@ -73,6 +81,24 @@ type ProjectBase = {
7381 readonly config : ProjectConfig
7482}
7583
84+ export const getContainerIpIfInsideContainer = (
85+ fs : FileSystem . FileSystem ,
86+ projectDir : string ,
87+ containerName : string
88+ ) : Effect . Effect < string | undefined , PlatformError , CommandExecutor . CommandExecutor > =>
89+ Effect . gen ( function * ( _ ) {
90+ const isInsideContainer = yield * _ ( fs . exists ( "/.dockerenv" ) )
91+ if ( ! isInsideContainer ) {
92+ return undefined
93+ }
94+ return yield * _ (
95+ runDockerInspectContainerIp ( projectDir , containerName ) . pipe (
96+ Effect . map ( ( ip ) => ( ip . length > 0 ? ip : undefined ) ) ,
97+ Effect . catchAll ( ( ) => Effect . succeed ( undefined ) )
98+ )
99+ )
100+ } )
101+
76102const loadProjectBase = (
77103 configPath : string
78104) : Effect . Effect < ProjectBase , ProjectLoadError , FileSystem . FileSystem | Path . Path > =>
@@ -91,21 +117,25 @@ const findProjectConfigPaths = (
91117export const loadProjectSummary = (
92118 configPath : string ,
93119 sshKey : string | null
94- ) : Effect . Effect < ProjectSummary , ProjectLoadError , FileSystem . FileSystem | Path . Path > =>
120+ ) : Effect . Effect < ProjectSummary , ProjectLoadError , FileSystem . FileSystem | Path . Path | CommandExecutor . CommandExecutor > =>
95121 Effect . gen ( function * ( _ ) {
96122 const { config, fs, path, projectDir } = yield * _ ( loadProjectBase ( configPath ) )
123+
124+ const ipAddress = yield * _ ( getContainerIpIfInsideContainer ( fs , projectDir , config . template . containerName ) )
125+
97126 const resolvedAuthorizedKeys = resolveAuthorizedKeysPath (
98127 path ,
99128 projectDir ,
100129 config . template . authorizedKeysPath
101130 )
102131 const authExists = yield * _ ( fs . exists ( resolvedAuthorizedKeys ) )
103- const sshCommand = buildSshCommand ( config . template , sshKey )
132+ const sshCommand = buildSshCommand ( config . template , sshKey , ipAddress )
104133
105134 return {
106135 projectDir,
107136 config,
108137 sshCommand,
138+ ipAddress,
109139 authorizedKeysPath : resolvedAuthorizedKeys ,
110140 authorizedKeysExists : authExists
111141 }
@@ -139,13 +169,16 @@ const formatDisplayName = (repoUrl: string): string => {
139169export const loadProjectItem = (
140170 configPath : string ,
141171 sshKey : string | null
142- ) : Effect . Effect < ProjectItem , ProjectLoadError , FileSystem . FileSystem | Path . Path > =>
172+ ) : Effect . Effect < ProjectItem , ProjectLoadError , FileSystem . FileSystem | Path . Path | CommandExecutor . CommandExecutor > =>
143173 Effect . gen ( function * ( _ ) {
144174 const { config, fs, path, projectDir } = yield * _ ( loadProjectBase ( configPath ) )
145175 const template = config . template
176+
177+ const ipAddress = yield * _ ( getContainerIpIfInsideContainer ( fs , projectDir , template . containerName ) )
178+
146179 const resolvedAuthorizedKeys = resolveAuthorizedKeysPath ( path , projectDir , template . authorizedKeysPath )
147180 const authExists = yield * _ ( fs . exists ( resolvedAuthorizedKeys ) )
148- const sshCommand = buildSshCommand ( template , sshKey )
181+ const sshCommand = buildSshCommand ( template , sshKey , ipAddress )
149182 const displayName = formatDisplayName ( template . repoUrl )
150183
151184 return {
@@ -159,6 +192,7 @@ export const loadProjectItem = (
159192 sshPort : template . sshPort ,
160193 targetDir : template . targetDir ,
161194 sshCommand,
195+ ipAddress,
162196 sshKeyPath : sshKey ,
163197 authorizedKeysPath : resolvedAuthorizedKeys ,
164198 authorizedKeysExists : authExists ,
0 commit comments