@@ -31,10 +31,54 @@ const defaultFileSystem: FileSystem = {
3131 writeFile,
3232}
3333
34- export const defaultSSHConfigResponse : SSHConfigResponse = {
35- ssh_config_options : { } ,
36- // The prefix is not used by the vscode-extension
37- hostname_prefix : "coder." ,
34+ export const defaultSSHConfigResponse : Record < string , string > = { }
35+
36+ // mergeSSHConfigValues will take a given ssh config and merge it with the overrides
37+ // provided. The merge handles key case insensitivity, so casing in the "key" does
38+ // not matter.
39+ export function mergeSSHConfigValues (
40+ config : Record < string , string > ,
41+ overrides : Record < string , string > ,
42+ ) : Record < string , string > {
43+ const merged : Record < string , string > = { }
44+
45+ // We need to do a case insensitive match for the overrides as ssh config keys are case insensitive.
46+ // To get the correct key:value, use:
47+ // key = caseInsensitiveOverrides[key.toLowerCase()]
48+ // value = overrides[key]
49+ const caseInsensitiveOverrides : Record < string , string > = { }
50+ Object . keys ( overrides ) . forEach ( ( key ) => {
51+ caseInsensitiveOverrides [ key . toLowerCase ( ) ] = key
52+ } )
53+
54+ Object . keys ( config ) . forEach ( ( key ) => {
55+ const lower = key . toLowerCase ( )
56+ // If the key is in overrides, use the override value.
57+ if ( caseInsensitiveOverrides [ lower ] ) {
58+ const correctCaseKey = caseInsensitiveOverrides [ lower ]
59+ const value = overrides [ correctCaseKey ]
60+ delete caseInsensitiveOverrides [ lower ]
61+
62+ // If the value is empty, do not add the key. It is being removed.
63+ if ( value === "" ) {
64+ return
65+ }
66+ merged [ correctCaseKey ] = value
67+ return
68+ }
69+ // If no override, take the original value.
70+ if ( config [ key ] !== "" ) {
71+ merged [ key ] = config [ key ]
72+ }
73+ } )
74+
75+ // Add remaining overrides.
76+ Object . keys ( caseInsensitiveOverrides ) . forEach ( ( lower ) => {
77+ const correctCaseKey = caseInsensitiveOverrides [ lower ]
78+ merged [ correctCaseKey ] = overrides [ correctCaseKey ]
79+ } )
80+
81+ return merged
3882}
3983
4084export class SSHConfig {
@@ -58,15 +102,15 @@ export class SSHConfig {
58102 }
59103 }
60104
61- async update ( values : SSHValues , overrides : SSHConfigResponse = defaultSSHConfigResponse ) {
105+ async update ( values : SSHValues , overrides : Record < string , string > = defaultSSHConfigResponse ) {
62106 // We should remove this in March 2023 because there is not going to have
63107 // old configs
64108 this . cleanUpOldConfig ( )
65109 const block = this . getBlock ( )
66110 if ( block ) {
67111 this . eraseBlock ( block )
68112 }
69- this . appendBlock ( values , overrides . ssh_config_options )
113+ this . appendBlock ( values , overrides )
70114 await this . save ( )
71115 }
72116
@@ -122,43 +166,16 @@ export class SSHConfig {
122166 */
123167 private appendBlock ( { Host, ...otherValues } : SSHValues , overrides : Record < string , string > ) {
124168 const lines = [ this . startBlockComment , `Host ${ Host } ` ]
125- // We need to do a case insensitive match for the overrides as ssh config keys are case insensitive.
126- // To get the correct key:value, use:
127- // key = caseInsensitiveOverrides[key.toLowerCase()]
128- // value = overrides[key]
129- const caseInsensitiveOverrides : Record < string , string > = { }
130- Object . keys ( overrides ) . forEach ( ( key ) => {
131- caseInsensitiveOverrides [ key . toLowerCase ( ) ] = key
132- } )
133169
134- const keys = Object . keys ( otherValues ) as Array < keyof typeof otherValues >
170+ // configValues is the merged values of the defaults and the overrides.
171+ const configValues = mergeSSHConfigValues ( otherValues , overrides )
172+
173+ // keys is the sorted keys of the merged values.
174+ const keys = ( Object . keys ( configValues ) as Array < keyof typeof configValues > ) . sort ( )
135175 keys . forEach ( ( key ) => {
136- const lower = key . toLowerCase ( )
137- if ( caseInsensitiveOverrides [ lower ] ) {
138- const correctCaseKey = caseInsensitiveOverrides [ lower ]
139- const value = overrides [ correctCaseKey ]
140- // Remove the key from the overrides so we don't write it again.
141- delete caseInsensitiveOverrides [ lower ]
142- if ( value === "" ) {
143- // If the value is empty, don't write it. Prevent writing the default
144- // value as well.
145- return
146- }
147- // If the key is in overrides, use the override value.
148- // Doing it this way maintains the default order of the keys.
149- lines . push ( this . withIndentation ( `${ key } ${ value } ` ) )
150- return
151- }
152- lines . push ( this . withIndentation ( `${ key } ${ otherValues [ key ] } ` ) )
153- } )
154- // Write remaining overrides that have not been written yet. Sort to maintain deterministic order.
155- const remainingKeys = ( Object . keys ( caseInsensitiveOverrides ) as Array < keyof typeof caseInsensitiveOverrides > ) . sort ( )
156- remainingKeys . forEach ( ( key ) => {
157- const correctKey = caseInsensitiveOverrides [ key ]
158- const value = overrides [ correctKey ]
159- // Only write the value if it is not empty.
176+ const value = configValues [ key ]
160177 if ( value !== "" ) {
161- lines . push ( this . withIndentation ( `${ correctKey } ${ value } ` ) )
178+ lines . push ( this . withIndentation ( `${ key } ${ value } ` ) )
162179 }
163180 } )
164181
0 commit comments