github.com/abemedia/appcast@v0.4.0/integrations/apt/build.go (about)

     1  package apt
     2  
     3  import (
     4  	"context"
     5  	"crypto/md5"
     6  	"crypto/sha1"
     7  	"crypto/sha256"
     8  	"fmt"
     9  	"io/fs"
    10  	"os"
    11  	"path"
    12  	"strings"
    13  	"unsafe"
    14  
    15  	"github.com/abemedia/appcast/integrations/apt/deb"
    16  	"github.com/abemedia/appcast/pkg/crypto/pgp"
    17  	"github.com/abemedia/appcast/source"
    18  	"github.com/abemedia/appcast/target"
    19  )
    20  
    21  type Config struct {
    22  	Source     *source.Source
    23  	Version    string
    24  	Prerelease bool
    25  	Target     target.Target
    26  	PGPKey     *pgp.PrivateKey
    27  	Compress   CompressionAlgo
    28  }
    29  
    30  func Build(ctx context.Context, c *Config) error {
    31  	pkgs := read(ctx, c)
    32  
    33  	version := c.Version
    34  	if v := getVersionConstraint(pkgs); v != "" {
    35  		version += "," + v
    36  	}
    37  
    38  	releases, err := c.Source.ListReleases(ctx, &source.ListOptions{
    39  		Version:    version,
    40  		Prerelease: c.Prerelease,
    41  	})
    42  	if err == source.ErrNoReleaseFound {
    43  		return nil
    44  	}
    45  	if err != nil {
    46  		return err
    47  	}
    48  
    49  	p, err := getPackages(ctx, c, releases)
    50  	if err != nil || p == nil {
    51  		return err
    52  	}
    53  	pkgs = append(p, pkgs...)
    54  
    55  	dir, err := release(c.PGPKey, c.Compress, pkgs)
    56  	if err != nil {
    57  		return err
    58  	}
    59  	defer os.RemoveAll(dir)
    60  
    61  	files := os.DirFS(dir)
    62  	return fs.WalkDir(files, ".", func(path string, d fs.DirEntry, err error) error {
    63  		if err != nil || d.IsDir() {
    64  			return err
    65  		}
    66  		b, err := fs.ReadFile(files, path)
    67  		if err != nil {
    68  			return err
    69  		}
    70  		w, err := c.Target.NewWriter(ctx, path)
    71  		if err != nil {
    72  			return err
    73  		}
    74  		if _, err = w.Write(b); err != nil {
    75  			return err
    76  		}
    77  		return w.Close()
    78  	})
    79  }
    80  
    81  func read(ctx context.Context, c *Config) []*Package {
    82  	dist := "edge"
    83  	rd, err := c.Target.NewReader(ctx, "dists/edge/Release")
    84  	if err != nil {
    85  		dist = "stable"
    86  		rd, err = c.Target.NewReader(ctx, "dists/stable/Release")
    87  		if err != nil {
    88  			return nil
    89  		}
    90  	}
    91  	defer rd.Close()
    92  
    93  	r := &Releases{}
    94  	if err = deb.NewDecoder(rd).Decode(r); err != nil {
    95  		return nil
    96  	}
    97  
    98  	var pkgs []*Package
    99  	for _, arch := range strings.Split(r.Architectures, " ") {
   100  		path := fmt.Sprintf("dists/%s/main/binary-%s/Packages", dist, arch)
   101  		rd, err = c.Target.NewReader(ctx, path)
   102  		if err != nil {
   103  			return nil
   104  		}
   105  		defer rd.Close()
   106  
   107  		var p []*Package
   108  		if err = deb.NewDecoder(rd).Decode(&p); err != nil {
   109  			return nil
   110  		}
   111  		pkgs = append(pkgs, p...)
   112  	}
   113  
   114  	return pkgs
   115  }
   116  
   117  func getVersionConstraint(pkgs []*Package) string {
   118  	if len(pkgs) == 0 {
   119  		return ""
   120  	}
   121  
   122  	v := make([]byte, 0, len(pkgs)*len("!=0.0.0,"))
   123  	for _, p := range pkgs {
   124  		v = append(v, '!', '=')
   125  		v = append(v, strings.Replace(p.Version, "~", "-", 1)...)
   126  		v = append(v, ',')
   127  	}
   128  
   129  	return unsafe.String(unsafe.SliceData(v), len(v)-1)
   130  }
   131  
   132  func getPackages(ctx context.Context, c *Config, releases []*source.Release) ([]*Package, error) {
   133  	var pkgs []*Package
   134  	for _, release := range releases {
   135  		for _, asset := range release.Assets {
   136  			if path.Ext(asset.Name) != ".deb" {
   137  				continue
   138  			}
   139  			p, err := getPackage(ctx, c, release.Version, asset.Name)
   140  			if err != nil {
   141  				return nil, err
   142  			}
   143  			pkgs = append(pkgs, p)
   144  		}
   145  	}
   146  	return pkgs, nil
   147  }
   148  
   149  func getPackage(ctx context.Context, c *Config, version, name string) (*Package, error) {
   150  	b, err := c.Source.DownloadAsset(ctx, version, name)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	p, err := getControl(b)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  	p.Size = len(b)
   160  	p.Filename = "pool/main/" + p.Package[0:1] + "/" + p.Package + "/" +
   161  		p.Package + "_" + p.Version + "_" + p.Architecture + ".deb"
   162  	p.MD5sum = md5.Sum(b)
   163  	p.SHA1 = sha1.Sum(b)
   164  	p.SHA256 = sha256.Sum256(b)
   165  
   166  	w, err := c.Target.NewWriter(ctx, p.Filename)
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  	if _, err = w.Write(b); err != nil {
   171  		return nil, err
   172  	}
   173  	if err = w.Close(); err != nil {
   174  		return nil, err
   175  	}
   176  
   177  	return p, nil
   178  }