github.com/abemedia/appcast@v0.4.0/integrations/apk/repo.go (about)

     1  package apk
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"io/fs"
    10  	"os"
    11  	"path/filepath"
    12  
    13  	"github.com/abemedia/appcast/pkg/crypto/rsa"
    14  	"github.com/abemedia/appcast/target"
    15  	"gitlab.alpinelinux.org/alpine/go/repository"
    16  )
    17  
    18  type repo struct {
    19  	repos map[string]*repository.ApkIndex
    20  	dir   string
    21  }
    22  
    23  func openRepo(ctx context.Context, t target.Target) (*repo, error) {
    24  	dir, err := os.MkdirTemp("", "")
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  
    29  	res := &repo{
    30  		repos: make(map[string]*repository.ApkIndex),
    31  		dir:   dir,
    32  	}
    33  
    34  	// See https://wiki.alpinelinux.org/wiki/Architecture
    35  	archs := []string{"x86", "x86_64", "armhf", "armv7", "aarch64", "ppc64le", "s390x"}
    36  
    37  	for _, arch := range archs {
    38  		r, err := t.NewReader(ctx, arch+"/APKINDEX.tar.gz")
    39  		if err != nil {
    40  			if errors.Is(err, fs.ErrNotExist) {
    41  				continue
    42  			}
    43  			return nil, err
    44  		}
    45  		index, err := repository.IndexFromArchive(r)
    46  		if err != nil {
    47  			return nil, err
    48  		}
    49  		res.repos[arch] = index
    50  	}
    51  
    52  	return res, nil
    53  }
    54  
    55  func (r *repo) Add(b []byte) error {
    56  	p, err := repository.ParsePackage(bytes.NewReader(b))
    57  	if err != nil {
    58  		return err
    59  	}
    60  
    61  	index, ok := r.repos[p.Arch]
    62  	if !ok {
    63  		index = &repository.ApkIndex{}
    64  		r.repos[p.Arch] = index
    65  	}
    66  	index.Packages = append(index.Packages, p)
    67  
    68  	dirname := filepath.Join(r.dir, p.Arch)
    69  	if err = os.MkdirAll(dirname, fs.ModePerm); err != nil {
    70  		return err
    71  	}
    72  
    73  	filename := fmt.Sprintf("%s-%s.apk", p.Name, p.Version)
    74  	return os.WriteFile(filepath.Join(dirname, filename), b, 0o600)
    75  }
    76  
    77  func (r *repo) Write(rsaKey *rsa.PrivateKey, publicKeyName string) error {
    78  	for arch, index := range r.repos {
    79  		rd, err := repository.ArchiveFromIndex(index)
    80  		if err != nil {
    81  			return err
    82  		}
    83  
    84  		if rsaKey != nil {
    85  			rd, err = repository.SignArchive(rd, rsaKey, publicKeyName)
    86  			if err != nil {
    87  				return err
    88  			}
    89  		}
    90  
    91  		path := filepath.Join(r.dir, arch, "APKINDEX.tar.gz")
    92  		f, err := os.Create(path)
    93  		if err != nil {
    94  			return err
    95  		}
    96  		if _, err = io.Copy(f, rd); err != nil {
    97  			return err
    98  		}
    99  		if err = f.Close(); err != nil {
   100  			return err
   101  		}
   102  	}
   103  
   104  	if rsaKey != nil {
   105  		pub, err := rsa.MarshalPublicKey(rsa.Public(rsaKey))
   106  		if err != nil {
   107  			return err
   108  		}
   109  		return os.WriteFile(filepath.Join(r.dir, publicKeyName), pub, 0o600)
   110  	}
   111  
   112  	return nil
   113  }