2024-02-18 17:52:09 -08:00
|
|
|
// Copyright (C) 2023 Jared Allard <jared@rgst.io>
|
|
|
|
// Copyright (C) 2023 Outreach <https://outreach.io>
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU General Public License version
|
|
|
|
// 2 as published by the Free Software Foundation.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
package ebuild
|
|
|
|
|
|
|
|
import (
|
2025-03-09 16:35:45 -07:00
|
|
|
"context"
|
2024-02-18 17:52:09 -08:00
|
|
|
_ "embed"
|
2025-03-09 16:35:45 -07:00
|
|
|
"fmt"
|
2024-02-18 17:52:09 -08:00
|
|
|
"io"
|
2025-03-09 16:35:45 -07:00
|
|
|
"io/fs"
|
2024-02-18 17:52:09 -08:00
|
|
|
"os/exec"
|
2025-03-09 16:35:45 -07:00
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
2024-02-18 17:52:09 -08:00
|
|
|
|
2025-03-09 16:35:45 -07:00
|
|
|
"github.com/jaredallard/overlay/.tools/internal/steps/stepshelpers"
|
2024-02-18 17:52:09 -08:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
// manifestValidationScript contains the script used to validate
|
|
|
|
// Manifest files.
|
|
|
|
//
|
|
|
|
//go:embed embed/verify-manifest.sh
|
2025-03-09 16:35:45 -07:00
|
|
|
var manifestValidationScript []byte
|
2024-02-18 17:52:09 -08:00
|
|
|
|
|
|
|
// gentooImage is the docker image used for validating Manifest files.
|
2025-03-09 16:58:05 -07:00
|
|
|
var gentooImage = "git.rgst.io/jaredallard/overlay:updater"
|
2024-02-18 17:52:09 -08:00
|
|
|
|
|
|
|
// Common errors.
|
|
|
|
var (
|
|
|
|
// ErrManifestInvalid is returned when the manifest is out of date or
|
|
|
|
// otherwise invalid in a semi-expected way.
|
|
|
|
ErrManifestInvalid = errors.New("manifest is out of date or invalid")
|
|
|
|
)
|
|
|
|
|
2025-03-09 16:35:45 -07:00
|
|
|
// copyDirectoryIntoContainer copies all files in the srcPath/ into
|
|
|
|
// destPath/ in the container.
|
|
|
|
func copyDirectoryIntoContainer(ctx context.Context, containerID, srcPath, destPath string) error {
|
|
|
|
directories := make(map[string]struct{})
|
|
|
|
|
|
|
|
if err := filepath.WalkDir(srcPath, func(path string, d fs.DirEntry, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// We skip directories.
|
|
|
|
if d.IsDir() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
relPath, err := filepath.Rel(srcPath, path)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not create relative path for %q: %w", path, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
dir := filepath.Dir(relPath)
|
|
|
|
if _, ok := directories[dir]; !ok {
|
|
|
|
if err := stepshelpers.RunCommandInContainer(ctx, containerID, "mkdir", "-p", filepath.Join(destPath, dir)); err != nil {
|
|
|
|
return fmt.Errorf("failed to ensure directory %q: %w", dir, err)
|
|
|
|
}
|
|
|
|
directories[dir] = struct{}{}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := stepshelpers.CopyFileToContainer(ctx, containerID, filepath.Join(srcPath, relPath), filepath.Join(destPath, relPath)); err != nil {
|
|
|
|
return fmt.Errorf("failed to copy %q into container: %w", relPath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}); err != nil {
|
|
|
|
return fmt.Errorf("failed to walk srcDir: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-02-18 17:52:09 -08:00
|
|
|
// ValidateManifest ensures that the manifest at the provided path is
|
|
|
|
// valid for the given ebuild. This requires docker to be installed on
|
|
|
|
// the host and running.
|
2025-03-09 16:35:45 -07:00
|
|
|
func ValidateManifest(stdout, stderr io.Writer, overlayDir, packageName string) error {
|
|
|
|
ctx := context.TODO()
|
|
|
|
|
|
|
|
bid, err := exec.Command(
|
2025-03-09 16:43:01 -07:00
|
|
|
"docker", "run", "--init", "-d", "--rm", "--entrypoint", "sleep", gentooImage, "infinity",
|
2025-03-09 16:35:45 -07:00
|
|
|
).Output()
|
|
|
|
if err != nil {
|
|
|
|
var execErr *exec.ExitError
|
|
|
|
if errors.As(err, &execErr) {
|
|
|
|
return fmt.Errorf("failed to run container: %s", string(execErr.Stderr))
|
2024-02-18 17:52:09 -08:00
|
|
|
}
|
|
|
|
|
2025-03-09 16:35:45 -07:00
|
|
|
return fmt.Errorf("failed to run container: %w", err)
|
|
|
|
}
|
|
|
|
containerID := strings.TrimSpace(string(bid))
|
2025-03-09 16:43:01 -07:00
|
|
|
defer exec.Command("docker", "stop", containerID).Run() //nolint:errcheck // Why: best effort
|
2025-03-09 16:35:45 -07:00
|
|
|
|
|
|
|
lclPkgDir := filepath.Join(overlayDir, packageName)
|
|
|
|
|
|
|
|
containerOverlayDir := "/ebuild/src"
|
|
|
|
containerPkgDir := path.Join(containerOverlayDir, packageName)
|
|
|
|
|
|
|
|
if err := copyDirectoryIntoContainer(ctx, containerID, lclPkgDir, containerPkgDir); err != nil {
|
|
|
|
return fmt.Errorf("failed to copy ebuild contents into container: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := copyDirectoryIntoContainer(ctx, containerID, filepath.Join(overlayDir, "metadata"), "/ebuild/src/metadata"); err != nil {
|
|
|
|
return fmt.Errorf("failed to copy metadata directory into container: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := stepshelpers.CopyFileBytesToContainer(ctx, containerID, manifestValidationScript, "/verify-manifest.sh"); err != nil {
|
|
|
|
return fmt.Errorf("failed to copy validation script into container: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := stepshelpers.RunCommandInContainer(ctx, containerID, "chmod", "+x", "/verify-manifest.sh"); err != nil {
|
|
|
|
return fmt.Errorf("failed to mark validation script as executable: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := stepshelpers.RunCommandInContainer(ctx, containerID, "/verify-manifest.sh", packageName); err != nil {
|
|
|
|
return fmt.Errorf("ebuild failed to lint: %w", err)
|
2024-02-18 17:52:09 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|