@@ -160,6 +160,11 @@ func New(opts *Options) *Handler {
160
160
handler .buildInfoJSON = html .EscapeString (string (buildInfoResponse ))
161
161
handler .handler = mux .ServeHTTP
162
162
163
+ handler .installScript , err = parseInstallScript (opts .SiteFS , opts .BuildInfo )
164
+ if err != nil {
165
+ panic ("failed to parse install script" )
166
+ }
167
+
163
168
return handler
164
169
}
165
170
@@ -169,8 +174,8 @@ type Handler struct {
169
174
secureHeaders * secure.Secure
170
175
handler http.HandlerFunc
171
176
htmlTemplates * template.Template
172
-
173
177
buildInfoJSON string
178
+ installScript []byte
174
179
175
180
// RegionsFetcher will attempt to fetch the more detailed WorkspaceProxy data, but will fall back to the
176
181
// regions if the user does not have the correct permissions.
@@ -217,6 +222,11 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
217
222
// If the asset does not exist, this will return a 404.
218
223
h .handler .ServeHTTP (rw , r )
219
224
return
225
+ // If requesting the install.sh script, fill in the template with the
226
+ // appropriate hostname and version info.
227
+ case reqFile == "install.sh" :
228
+ h .serveInstallScript (rw , r )
229
+ return
220
230
// If the original file path exists we serve it.
221
231
case h .exists (reqFile ):
222
232
if ShouldCacheFile (reqFile ) {
@@ -306,6 +316,11 @@ func ShouldCacheFile(reqFile string) bool {
306
316
return true
307
317
}
308
318
319
+ func (h * Handler ) serveInstallScript (rw http.ResponseWriter , r * http.Request ) {
320
+ rw .Header ().Add ("content-type" , "text/plain; charset=utf-8" )
321
+ http .ServeContent (rw , r , "install.sh" , time.Time {}, bytes .NewReader (h .installScript ))
322
+ }
323
+
309
324
func (h * Handler ) serveHTML (resp http.ResponseWriter , request * http.Request , reqPath string , state htmlState ) bool {
310
325
if data , err := h .renderHTMLWithState (request , reqPath , state ); err == nil {
311
326
if reqPath == "" {
@@ -533,6 +548,32 @@ func findAndParseHTMLFiles(files fs.FS) (*template.Template, error) {
533
548
return root , nil
534
549
}
535
550
551
+ type installScriptState struct {
552
+ Origin string
553
+ Version string
554
+ }
555
+
556
+ func parseInstallScript (files fs.FS , buildInfo codersdk.BuildInfoResponse ) ([]byte , error ) {
557
+ scriptFile , err := fs .ReadFile (files , "install.sh" )
558
+ if err != nil {
559
+ return []byte {}, err
560
+ }
561
+
562
+ script , err := template .New ("install.sh" ).Parse (string (scriptFile ))
563
+ if err != nil {
564
+ return []byte {}, err
565
+ }
566
+
567
+ var buf bytes.Buffer
568
+ state := installScriptState {Origin : buildInfo .DashboardURL , Version : buildInfo .Version }
569
+ err = script .Execute (& buf , state )
570
+ if err != nil {
571
+ return []byte {}, err
572
+ }
573
+
574
+ return buf .Bytes (), err
575
+ }
576
+
536
577
// ExtractOrReadBinFS checks the provided fs for compressed coder binaries and
537
578
// extracts them into dest/bin if found. As a fallback, the provided FS is
538
579
// checked for a /bin directory, if it is non-empty it is returned. Finally
0 commit comments