blob: fc32127ca5af4bddf412c8db815e69c368958372 [file] [log] [blame]
// Copyright 2023 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package generators
import (
"context"
"crypto"
"embed"
"fmt"
"io/fs"
"path"
"path/filepath"
"go.chromium.org/luci/cipkg/base/actions"
"go.chromium.org/luci/cipkg/core"
)
type EmbeddedFiles struct {
name string
ref string
dir string
efs embed.FS
// modeOverride lets you customize file permissions within embed.FS, which
// otherwise always set to 0o444.
modeOverride func(name string) (fs.FileMode, error)
}
// InitEmbeddedFS registers the embed.FS to copy executor and returns the
// corresponding generator.
// It should only be used in init() or on global variables e.g.
// //go:embed something
// var somethingEmbed embed.FS
// var somethingGen = InitEmbeddedFS("derivationName", somethingEmbed)
func InitEmbeddedFS(name string, e embed.FS) *EmbeddedFiles {
h := crypto.SHA256.New()
if err := getHashFromFS(e, h); err != nil {
// Embedded files are frozen after build. Panic since this is more like a
// build failure.
panic(err)
}
ref := fmt.Sprintf("%x", h.Sum([]byte(name)))
actions.RegisterEmbed(ref, e)
return &EmbeddedFiles{
name: name,
ref: ref,
dir: ".",
efs: e,
}
}
func (e *EmbeddedFiles) Generate(ctx context.Context, plats Platforms) (*core.Action, error) {
efs, err := fs.Sub(e.efs, e.dir)
if err != nil {
return nil, err
}
files := make(map[string]*core.ActionFilesCopy_Source)
if err := fs.WalkDir(efs, ".", func(name string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
if name == "." {
return fmt.Errorf("root dir can't be file")
}
mode := fs.FileMode(0o444)
if e.modeOverride != nil {
if mode, err = e.modeOverride(name); err != nil {
return err
}
}
files[filepath.FromSlash(name)] = &core.ActionFilesCopy_Source{
Content: &core.ActionFilesCopy_Source_Embed_{
Embed: &core.ActionFilesCopy_Source_Embed{Ref: e.ref, Path: path.Join(e.dir, name)},
},
Mode: uint32(mode),
}
return nil
}); err != nil {
return nil, err
}
return &core.Action{
Name: e.name,
Spec: &core.Action_Copy{
Copy: &core.ActionFilesCopy{Files: files},
},
}, nil
}
// SubDir returns a generator copies files in the sub directory of the source.
func (e *EmbeddedFiles) SubDir(dir string) *EmbeddedFiles {
ret := *e
ret.dir = path.Join(e.dir, dir)
return &ret
}
// WithModeOverride overrides file modes while copying.
func (e *EmbeddedFiles) WithModeOverride(f func(name string) (fs.FileMode, error)) *EmbeddedFiles {
ret := *e
ret.modeOverride = f
return &ret
}