go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cipkg/base/generators/embed.go (about)

     1  // Copyright 2023 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //	http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package generators
    16  
    17  import (
    18  	"context"
    19  	"crypto"
    20  	"embed"
    21  	"fmt"
    22  	"io/fs"
    23  	"path"
    24  	"path/filepath"
    25  
    26  	"go.chromium.org/luci/cipkg/base/actions"
    27  	"go.chromium.org/luci/cipkg/core"
    28  )
    29  
    30  type EmbeddedFiles struct {
    31  	name string
    32  
    33  	ref string
    34  	dir string
    35  	efs embed.FS
    36  
    37  	modeOverride func(info fs.FileInfo) (fs.FileMode, error)
    38  }
    39  
    40  // InitEmbeddedFS registers the embed.FS to copy executor and returns the
    41  // corresponding generator.
    42  // It should only be used in init() or on global variables e.g.
    43  // //go:embed something
    44  // var somethingEmbed embed.FS
    45  // var somethingGen = InitEmbeddedFS("derivationName", somethingEmbed)
    46  func InitEmbeddedFS(name string, e embed.FS) *EmbeddedFiles {
    47  	h := crypto.SHA256.New()
    48  	if err := getHashFromFS(e, h); err != nil {
    49  		// Embedded files are frozen after build. Panic since this is more like a
    50  		// build failure.
    51  		panic(err)
    52  	}
    53  	ref := fmt.Sprintf("%x", h.Sum([]byte(name)))
    54  	actions.RegisterEmbed(ref, e)
    55  	return &EmbeddedFiles{
    56  		name: name,
    57  
    58  		ref: ref,
    59  		dir: ".",
    60  		efs: e,
    61  	}
    62  }
    63  
    64  func (e *EmbeddedFiles) Generate(ctx context.Context, plats Platforms) (*core.Action, error) {
    65  	efs, err := fs.Sub(e.efs, e.dir)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	files := make(map[string]*core.ActionFilesCopy_Source)
    71  	if err := fs.WalkDir(efs, ".", func(name string, d fs.DirEntry, err error) error {
    72  		if err != nil {
    73  			return err
    74  		}
    75  		if d.IsDir() {
    76  			return nil
    77  		}
    78  		if name == "." {
    79  			return fmt.Errorf("root dir can't be file")
    80  		}
    81  
    82  		mode := fs.FileMode(0o666)
    83  		if e.modeOverride != nil {
    84  			info, err := d.Info()
    85  			if err != nil {
    86  				return err
    87  			}
    88  			if mode, err = e.modeOverride(info); err != nil {
    89  				return err
    90  			}
    91  		}
    92  
    93  		files[filepath.FromSlash(name)] = &core.ActionFilesCopy_Source{
    94  			Content: &core.ActionFilesCopy_Source_Embed_{
    95  				Embed: &core.ActionFilesCopy_Source_Embed{Ref: e.ref, Path: path.Join(e.dir, name)},
    96  			},
    97  			Mode: uint32(mode),
    98  		}
    99  		return nil
   100  	}); err != nil {
   101  		return nil, err
   102  	}
   103  
   104  	return &core.Action{
   105  		Name: e.name,
   106  		Spec: &core.Action_Copy{
   107  			Copy: &core.ActionFilesCopy{Files: files},
   108  		},
   109  	}, nil
   110  }
   111  
   112  // SubDir returns a generator copies files in the sub directory of the source.
   113  func (e *EmbeddedFiles) SubDir(dir string) *EmbeddedFiles {
   114  	ret := *e
   115  	ret.dir = path.Join(e.dir, dir)
   116  	return &ret
   117  }
   118  
   119  // WithModeOverride overrides file modes while copying.
   120  func (e *EmbeddedFiles) WithModeOverride(f func(info fs.FileInfo) (fs.FileMode, error)) *EmbeddedFiles {
   121  	ret := *e
   122  	ret.modeOverride = f
   123  	return &ret
   124  }