Files
Runner/act/filecollector/file_collector.go
T
silverwind 546eca312e refactor: remove unreachable runner code (#1179)
Remove inherited act APIs, configuration branches, and test seams that neither the daemon nor exec can reach. Constant-fold settings both entry points already enforce and consolidate duplicate runner paths.

Major removals:

- Unwired custom action-cache and local-repository-cache implementations.
- Legacy matrix, platform, input, container-reuse, logging, Git remote, and action-replacement configuration paths.
- Unused Docker socket, container network, tar-copy, and platform PTY wrappers.
- Single-implementation filesystem, environment, runner, and expression abstractions.
- Duplicated step-container, command-logging, credential, reusable-workflow, and execution paths.
- Generated client mock boilerplate, obsolete fixtures, test-only seams, stale wrappers, and commented-out code.

This removes 2844 net Go lines while retaining Gitea RPC, event, matrix, input, cache, artifact, action, reusable workflow, Docker, host, exec, and release behavior.

Assisted by Codex (GPT-5).

Reviewed-on: https://gitea.com/gitea/runner/pulls/1179
Reviewed-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-22 16:54:41 +00:00

182 lines
4.6 KiB
Go

// Copyright 2024 The Gitea Authors. All rights reserved.
// Copyright 2024 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package filecollector
import (
"archive/tar"
"context"
"errors"
"fmt"
"io"
"io/fs"
"os"
"path"
"path/filepath"
"strings"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/filemode"
"github.com/go-git/go-git/v5/plumbing/format/gitignore"
"github.com/go-git/go-git/v5/plumbing/format/index"
)
type Handler interface {
WriteFile(path string, fi fs.FileInfo, linkName string, f io.Reader) error
}
type TarCollector struct {
TarWriter *tar.Writer
UID int
GID int
DstDir string
}
func (tc TarCollector) WriteFile(fpath string, fi fs.FileInfo, linkName string, f io.Reader) error {
// create a new dir/file header
header, err := tar.FileInfoHeader(fi, linkName)
if err != nil {
return err
}
// update the name to correctly reflect the desired destination when untaring
header.Name = path.Join(tc.DstDir, fpath)
header.Mode = int64(fi.Mode())
header.ModTime = fi.ModTime()
header.Uid = tc.UID
header.Gid = tc.GID
// write the header
if err := tc.TarWriter.WriteHeader(header); err != nil {
return err
}
// this is a symlink no reader provided
if f == nil {
return nil
}
// copy file data into tar writer
if _, err := io.Copy(tc.TarWriter, f); err != nil {
return err
}
return nil
}
type CopyCollector struct {
DstDir string
}
func (cc *CopyCollector) WriteFile(fpath string, fi fs.FileInfo, linkName string, f io.Reader) error {
fdestpath := filepath.Join(cc.DstDir, fpath)
if err := os.MkdirAll(filepath.Dir(fdestpath), 0o777); err != nil {
return err
}
// Remove any existing destination so we can overwrite read-only files
// (e.g. git pack files at mode 0444 trip EACCES on macOS and "Access is
// denied" on Windows when reopened with O_WRONLY) and so os.Symlink does
// not fail with EEXIST. os.Remove clears the Windows read-only attribute
// internally; on Unix unlink only needs write permission on the parent.
_ = os.Remove(fdestpath)
if f == nil {
return os.Symlink(linkName, fdestpath)
}
df, err := os.OpenFile(fdestpath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, fi.Mode())
if err != nil {
return err
}
defer df.Close()
if _, err := io.Copy(df, f); err != nil {
return err
}
return nil
}
type FileCollector struct {
Ignorer gitignore.Matcher
SrcPath string
SrcPrefix string
Handler Handler
}
func openGitIndex(path string) (*index.Index, error) {
repo, err := git.PlainOpen(path)
if err != nil {
return nil, err
}
return repo.Storer.Index()
}
func (fc *FileCollector) CollectFiles(ctx context.Context, submodulePath []string) filepath.WalkFunc {
i, _ := openGitIndex(path.Join(fc.SrcPath, path.Join(submodulePath...)))
return func(file string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
if ctx != nil && ctx.Err() != nil {
return errors.New("copy cancelled")
}
sansPrefix := strings.TrimPrefix(file, fc.SrcPrefix)
split := strings.Split(sansPrefix, string(filepath.Separator))
// The root folders should be skipped, submodules only have the last path component set to "." by filepath.Walk
if fi.IsDir() && len(split) > 0 && split[len(split)-1] == "." {
return nil
}
var entry *index.Entry
if i != nil {
entry, err = i.Entry(strings.Join(split[len(submodulePath):], "/"))
} else {
err = index.ErrEntryNotFound
}
if err != nil && fc.Ignorer != nil && fc.Ignorer.Match(split, fi.IsDir()) {
if fi.IsDir() {
if i != nil {
ms, err := i.Glob(strings.Join(append(split[len(submodulePath):], "**"), "/"))
if err != nil || len(ms) == 0 {
return filepath.SkipDir
}
} else {
return filepath.SkipDir
}
} else {
return nil
}
}
if err == nil && entry.Mode == filemode.Submodule {
err = filepath.Walk(file, fc.CollectFiles(ctx, split))
if err != nil {
return err
}
return filepath.SkipDir
}
path := filepath.ToSlash(sansPrefix)
// return on non-regular files (thanks to [kumo](https://medium.com/@komuw/just-like-you-did-fbdd7df829d3) for this suggested update)
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
linkName, err := os.Readlink(file)
if err != nil {
return fmt.Errorf("unable to readlink '%s': %w", file, err)
}
return fc.Handler.WriteFile(path, fi, linkName, nil)
} else if !fi.Mode().IsRegular() {
return nil
}
// open file
f, err := os.Open(file)
if err != nil {
return err
}
defer f.Close()
if ctx != nil {
stop := context.AfterFunc(ctx, func() { _ = f.Close() })
defer stop()
}
return fc.Handler.WriteFile(path, fi, "", f)
}
}