feat(arch/src): convert PKGBUILD/.SRCINFO to .recipe + build.sh
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <[email protected]>
This commit is contained in:
@@ -0,0 +1,218 @@
|
|||||||
|
// Package src converts Arch Linux source packages (PKGBUILD/.SRCINFO) into
|
||||||
|
// Zeta recipes.
|
||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.spectoria.dev/huntedbytheirs/zeta-reconstruct/internal/depmap"
|
||||||
|
"git.spectoria.dev/huntedbytheirs/zeta-reconstruct/internal/registry"
|
||||||
|
"git.spectoria.dev/huntedbytheirs/zeta-reconstruct/internal/zeta"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Convert converts an Arch source package (a PKGBUILD, a .SRCINFO, or a
|
||||||
|
// directory containing either) into a Zeta .recipe with build_system =
|
||||||
|
// "custom" plus a generated build.sh, both written into opts.Output:
|
||||||
|
//
|
||||||
|
// <out>/<name>.recipe
|
||||||
|
// <out>/build.sh
|
||||||
|
//
|
||||||
|
// v1 fidelity guardrails error out on shapes Zeta cannot represent yet:
|
||||||
|
// split packages, VCS sources (git+/svn+/hg+/bzr+ protocols, <vcs>:: URL
|
||||||
|
// fragments, or a dynamic pkgver() function), zero sources and multiple
|
||||||
|
// sources. A nonzero epoch and the dropped metadata fields (makedepends,
|
||||||
|
// optdepends, provides, conflicts, replaces) are warned about on stderr.
|
||||||
|
func Convert(input string, opts registry.Options) error {
|
||||||
|
warn := func(msg string) { fmt.Fprintf(os.Stderr, "warning: %s\n", msg) }
|
||||||
|
|
||||||
|
info, err := Load(input, opts.Arch)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("convert: %w", err)
|
||||||
|
}
|
||||||
|
pkgbuildPath, err := resolvePKGBUILD(input)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("convert: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fidelity guardrails: v1 covers exactly one package built from exactly
|
||||||
|
// one static tarball source.
|
||||||
|
if len(info.PkgNames) != 1 {
|
||||||
|
return errors.New("convert: split packages not yet supported in v1")
|
||||||
|
}
|
||||||
|
if hasVCS(info, pkgbuildPath) {
|
||||||
|
return errors.New("convert: VCS source not yet supported in v1")
|
||||||
|
}
|
||||||
|
if len(info.Sources) == 0 {
|
||||||
|
return errors.New("convert: no sources not yet supported in v1")
|
||||||
|
}
|
||||||
|
if len(info.Sources) > 1 {
|
||||||
|
return errors.New("convert: multiple sources not yet supported in v1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn-only: an epoch cannot be folded into Zeta versioning, and the
|
||||||
|
// recipe format has no fields for these relations.
|
||||||
|
if info.Epoch != "" && info.Epoch != "0" {
|
||||||
|
warn(fmt.Sprintf("epoch %s is not representable in Zeta versioning", info.Epoch))
|
||||||
|
}
|
||||||
|
for _, f := range []struct {
|
||||||
|
field string
|
||||||
|
vals []string
|
||||||
|
}{
|
||||||
|
{"makedepends", info.Makedepends},
|
||||||
|
{"optdepends", info.Optdepends},
|
||||||
|
{"provides", info.Provides},
|
||||||
|
{"conflicts", info.Conflicts},
|
||||||
|
{"replaces", info.Replaces},
|
||||||
|
} {
|
||||||
|
if len(f.vals) > 0 {
|
||||||
|
warn(fmt.Sprintf("%s dropped (not representable in Zeta)", f.field))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deps: map each declared dependency through the Arch→Zeta name map.
|
||||||
|
// Dropped deps are skipped (MapDep already warned).
|
||||||
|
var deps []string
|
||||||
|
for _, d := range info.Depends {
|
||||||
|
if mapped, ok := depmap.MapDep(d, warn); ok {
|
||||||
|
deps = append(deps, mapped)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
summary := info.Desc
|
||||||
|
if summary == "" {
|
||||||
|
// recipe.lua requires a non-empty summary.
|
||||||
|
summary = info.PkgNames[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
recipe := &zeta.Recipe{
|
||||||
|
Name: info.PkgNames[0],
|
||||||
|
Version: info.Version,
|
||||||
|
Summary: summary,
|
||||||
|
URL: sourceURL(info.Sources[0]),
|
||||||
|
SHA256: pickSHA256(info.SHA256Sums, warn),
|
||||||
|
Deps: deps,
|
||||||
|
BuildScript: "build.sh",
|
||||||
|
}
|
||||||
|
|
||||||
|
src, err := recipe.Emit()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("convert: emit recipe: %w", err)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(opts.Output, 0o755); err != nil {
|
||||||
|
return fmt.Errorf("convert: mkdir %s: %w", opts.Output, err)
|
||||||
|
}
|
||||||
|
recipePath := filepath.Join(opts.Output, recipe.Name+".recipe")
|
||||||
|
if err := os.WriteFile(recipePath, []byte(src), 0o644); err != nil {
|
||||||
|
return fmt.Errorf("convert: write %s: %w", recipePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
script, err := Generate(info, pkgbuildPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("convert: generate build.sh: %w", err)
|
||||||
|
}
|
||||||
|
scriptPath := filepath.Join(opts.Output, "build.sh")
|
||||||
|
if err := os.WriteFile(scriptPath, []byte(script), 0o755); err != nil {
|
||||||
|
return fmt.Errorf("convert: write %s: %w", scriptPath, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolvePKGBUILD returns the file whose text feeds Generate and the
|
||||||
|
// pkgver() scan: the PKGBUILD when one exists, else the .SRCINFO path (whose
|
||||||
|
// content yields no function bodies — stub functions result).
|
||||||
|
func resolvePKGBUILD(input string) (string, error) {
|
||||||
|
st, err := os.Stat(input)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !st.IsDir() {
|
||||||
|
return input, nil
|
||||||
|
}
|
||||||
|
pkgbuild := filepath.Join(input, "PKGBUILD")
|
||||||
|
if fileExists(pkgbuild) {
|
||||||
|
return pkgbuild, nil
|
||||||
|
}
|
||||||
|
srcinfo := filepath.Join(input, ".SRCINFO")
|
||||||
|
if fileExists(srcinfo) {
|
||||||
|
return srcinfo, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("src: %s: neither .SRCINFO nor PKGBUILD found", input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasVCS reports whether the source package relies on a VCS source or a
|
||||||
|
// dynamic pkgver() function and therefore cannot be represented in v1.
|
||||||
|
func hasVCS(info *SrcInfo, pkgbuildPath string) bool {
|
||||||
|
for _, s := range info.Sources {
|
||||||
|
if isVCSSource(s) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasPkgverFunc(pkgbuildPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isVCSSource reports whether a makepkg source entry names a VCS checkout:
|
||||||
|
// the git+/svn+/hg+/bzr+ protocol forms or the `<vcs>::<url>` fragment form
|
||||||
|
// (e.g. git::https://…). A plain https:// tarball download is a normal
|
||||||
|
// source, as is a `name::<url>` entry (a download-name prefix, not a VCS).
|
||||||
|
func isVCSSource(s string) bool {
|
||||||
|
for _, prefix := range []string{"git+", "svn+", "hg+", "bzr+"} {
|
||||||
|
if strings.HasPrefix(s, prefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if frag, _, ok := strings.Cut(s, "::"); ok {
|
||||||
|
switch frag {
|
||||||
|
case "git", "svn", "hg", "bzr", "fossil", "cvs", "darcs", "mtn":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// pkgverFuncRE matches a top-level pkgver() function definition in bash
|
||||||
|
// (column 0), the makepkg marker for a dynamically computed version.
|
||||||
|
var pkgverFuncRE = regexp.MustCompile(`(?m)^pkgver\(`)
|
||||||
|
|
||||||
|
func hasPkgverFunc(path string) bool {
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return pkgverFuncRE.Match(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sourceURL derives the recipe url from a makepkg source entry: the text
|
||||||
|
// after a "name::" prefix when present, otherwise the whole entry verbatim.
|
||||||
|
// The URL scheme is never stripped.
|
||||||
|
func sourceURL(s string) string {
|
||||||
|
if _, rest, ok := strings.Cut(s, "::"); ok {
|
||||||
|
return rest
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// sha256Re matches exactly 64 hex characters, the recipe.lua sha256 shape.
|
||||||
|
var sha256Re = regexp.MustCompile(`^[0-9a-fA-F]{64}$`)
|
||||||
|
|
||||||
|
// pickSHA256 returns the source sha256 for the recipe, or "" when absent or
|
||||||
|
// unusable (an empty value, makepkg's "SKIP" marker, or a non-hex64 string —
|
||||||
|
// the latter is warned about because the checksum is dropped).
|
||||||
|
func pickSHA256(sums []string, warn func(string)) string {
|
||||||
|
if len(sums) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
s := strings.TrimSpace(sums[0])
|
||||||
|
if s == "" || strings.EqualFold(s, "SKIP") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if !sha256Re.MatchString(s) {
|
||||||
|
warn(fmt.Sprintf("sha256sum %q is not 64 hex characters; checksum dropped", s))
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
package src
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.spectoria.dev/huntedbytheirs/zeta-reconstruct/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
srcinfoDir = "../../../testdata/srcinfo"
|
||||||
|
recipeLua = "/home/specter/Public/zeta-toolchain/toolchain/lib/?.lua"
|
||||||
|
)
|
||||||
|
|
||||||
|
// convertMytool runs the full conversion on the mytool fixture directory
|
||||||
|
// into a fresh temp dir and returns that dir plus the emitted recipe and
|
||||||
|
// build.sh contents.
|
||||||
|
func convertMytool(t *testing.T) (out, recipe, script string) {
|
||||||
|
t.Helper()
|
||||||
|
out = t.TempDir()
|
||||||
|
if err := Convert(filepath.Join(srcinfoDir, "mytool"), registry.Options{Output: out}); err != nil {
|
||||||
|
t.Fatalf("Convert(mytool): %v", err)
|
||||||
|
}
|
||||||
|
rb, err := os.ReadFile(filepath.Join(out, "mytool.recipe"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("mytool.recipe not written: %v", err)
|
||||||
|
}
|
||||||
|
sb, err := os.ReadFile(filepath.Join(out, "build.sh"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("build.sh not written: %v", err)
|
||||||
|
}
|
||||||
|
return out, string(rb), string(sb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertStderr runs Convert with os.Stderr redirected into a pipe and
|
||||||
|
// returns the captured output along with the error. Tests using this helper
|
||||||
|
// must not run in parallel.
|
||||||
|
func convertStderr(t *testing.T, input string, opts registry.Options) (string, error) {
|
||||||
|
t.Helper()
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("pipe: %v", err)
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
old := os.Stderr
|
||||||
|
os.Stderr = w
|
||||||
|
err = Convert(input, opts)
|
||||||
|
if cerr := w.Close(); cerr != nil && err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
os.Stderr = old
|
||||||
|
out, rerr := io.ReadAll(r)
|
||||||
|
if rerr != nil {
|
||||||
|
t.Fatalf("read stderr: %v", rerr)
|
||||||
|
}
|
||||||
|
return string(out), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// quoteLua renders a path as a safe single-quoted Lua string literal.
|
||||||
|
func quoteLua(s string) string {
|
||||||
|
return `'` + strings.ReplaceAll(s, `\`, `\\`) + `'`
|
||||||
|
}
|
||||||
|
|
||||||
|
// writePKGBUILD writes a PKGBUILD fixture into a fresh temp dir and returns
|
||||||
|
// the dir (passed as input to Convert).
|
||||||
|
func writePKGBUILD(t *testing.T, body string) string {
|
||||||
|
t.Helper()
|
||||||
|
dir := t.TempDir()
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "PKGBUILD"), []byte(body), 0o644); err != nil {
|
||||||
|
t.Fatalf("write PKGBUILD fixture: %v", err)
|
||||||
|
}
|
||||||
|
return dir
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvert(t *testing.T) {
|
||||||
|
out, recipe, script := convertMytool(t)
|
||||||
|
|
||||||
|
for _, want := range []string{
|
||||||
|
`build_system = "custom"`,
|
||||||
|
`build_script = "build.sh"`,
|
||||||
|
`name = "mytool"`,
|
||||||
|
} {
|
||||||
|
if !strings.Contains(recipe, want) {
|
||||||
|
t.Errorf("mytool.recipe does not contain %q:\n%s", want, recipe)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !strings.Contains(script, `pkgdir="$DESTDIR"`) {
|
||||||
|
t.Errorf("build.sh does not contain pkgdir=\"$DESTDIR\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(filepath.Join(out, "mytool.recipe")); err != nil {
|
||||||
|
t.Errorf("mytool.recipe missing: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(filepath.Join(out, "build.sh")); err != nil {
|
||||||
|
t.Errorf("build.sh missing: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertRecipeLoadsInZeta(t *testing.T) {
|
||||||
|
lua, err := exec.LookPath("lua")
|
||||||
|
if err != nil {
|
||||||
|
t.Skip("lua not available; skipping cross-tool validation")
|
||||||
|
}
|
||||||
|
|
||||||
|
out := t.TempDir()
|
||||||
|
if err := Convert(filepath.Join(srcinfoDir, "mytool"), registry.Options{Output: out}); err != nil {
|
||||||
|
t.Fatalf("Convert(mytool): %v", err)
|
||||||
|
}
|
||||||
|
recipePath := filepath.Join(out, "mytool.recipe")
|
||||||
|
|
||||||
|
probe := `package.path="` + recipeLua + `;"..package.path; ` +
|
||||||
|
`local r,e=require("recipe").load(` + quoteLua(recipePath) + `); assert(r,e)`
|
||||||
|
cmd := exec.Command(lua, "-e", probe)
|
||||||
|
if b, cerr := cmd.CombinedOutput(); cerr != nil {
|
||||||
|
t.Fatalf("real zeta-makepkg recipe.load rejected the emitted recipe: %v\n%s", cerr, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertSplitPackage(t *testing.T) {
|
||||||
|
err := Convert(filepath.Join(srcinfoDir, "split"), registry.Options{Output: t.TempDir()})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Convert(split) error = nil, want guardrail error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "not yet supported") {
|
||||||
|
t.Errorf("Convert(split) error = %q, want it to contain %q", err, "not yet supported")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "split packages not yet supported in v1") {
|
||||||
|
t.Errorf("Convert(split) error = %q, want the split guardrail message", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertVCS(t *testing.T) {
|
||||||
|
err := Convert(filepath.Join(srcinfoDir, "vcs"), registry.Options{Output: t.TempDir()})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Convert(vcs) error = nil, want guardrail error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "not yet supported") {
|
||||||
|
t.Errorf("Convert(vcs) error = %q, want it to contain %q", err, "not yet supported")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "VCS source not yet supported in v1") {
|
||||||
|
t.Errorf("Convert(vcs) error = %q, want the VCS guardrail message", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertEpochWarns(t *testing.T) {
|
||||||
|
dir := writePKGBUILD(t, `pkgname=epo
|
||||||
|
pkgver=1.0
|
||||||
|
pkgrel=1
|
||||||
|
epoch=1
|
||||||
|
pkgdesc="Epoch fixture"
|
||||||
|
arch=(x86_64)
|
||||||
|
source=("epo-1.0.tar.gz")
|
||||||
|
`)
|
||||||
|
stderr, err := convertStderr(t, dir, registry.Options{Output: t.TempDir()})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Convert(epoch) error = %v, want nil (epoch warns only)", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(stderr, "epoch") {
|
||||||
|
t.Errorf("stderr = %q, want a warning mentioning epoch", stderr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertNamedSourceURL(t *testing.T) {
|
||||||
|
dir := writePKGBUILD(t, `pkgname=namedsrc
|
||||||
|
pkgver=1.0
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="Named source fixture"
|
||||||
|
arch=(x86_64)
|
||||||
|
source=("name::https://example.com/x.tar.gz")
|
||||||
|
sha256sums=('SKIP')
|
||||||
|
`)
|
||||||
|
out := t.TempDir()
|
||||||
|
if err := Convert(dir, registry.Options{Output: out}); err != nil {
|
||||||
|
t.Fatalf("Convert(named source) error = %v, want nil", err)
|
||||||
|
}
|
||||||
|
content, err := os.ReadFile(filepath.Join(out, "namedsrc.recipe"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("namedsrc.recipe not written: %v", err)
|
||||||
|
}
|
||||||
|
const wantLine = `url = "https://example.com/x.tar.gz",`
|
||||||
|
if !strings.Contains(string(content), wantLine) {
|
||||||
|
t.Errorf("recipe url = %q, want the line %q (scheme preserved, name:: stripped):\n%s",
|
||||||
|
string(content), wantLine, content)
|
||||||
|
}
|
||||||
|
if strings.Contains(string(content), "name::") {
|
||||||
|
t.Errorf("recipe still contains the name:: prefix:\n%s", content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertMissingInput(t *testing.T) {
|
||||||
|
err := Convert(filepath.Join(t.TempDir(), "nope"), registry.Options{Output: t.TempDir()})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Convert(missing) error = nil, want error for nonexistent input")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertZeroSources(t *testing.T) {
|
||||||
|
dir := writePKGBUILD(t, `pkgname=nosrc
|
||||||
|
pkgver=1.0
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="No sources fixture"
|
||||||
|
arch=(x86_64)
|
||||||
|
`)
|
||||||
|
err := Convert(dir, registry.Options{Output: t.TempDir()})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Convert(no sources) error = nil, want guardrail error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "no sources not yet supported in v1") {
|
||||||
|
t.Errorf("Convert(no sources) error = %q, want the zero-source guardrail message", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertMultiSource(t *testing.T) {
|
||||||
|
dir := writePKGBUILD(t, `pkgname=multisrc
|
||||||
|
pkgver=1.0
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="Multi source fixture"
|
||||||
|
arch=(x86_64)
|
||||||
|
source=("a-1.0.tar.gz" "b-1.0.tar.gz")
|
||||||
|
sha256sums=('SKIP' 'SKIP')
|
||||||
|
`)
|
||||||
|
err := Convert(dir, registry.Options{Output: t.TempDir()})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Convert(multi source) error = nil, want guardrail error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "multiple sources not yet supported in v1") {
|
||||||
|
t.Errorf("Convert(multi source) error = %q, want the multi-source guardrail message", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertDroppedMetadataWarns(t *testing.T) {
|
||||||
|
dir := writePKGBUILD(t, `pkgname=meta
|
||||||
|
pkgver=1.0
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="Dropped metadata fixture"
|
||||||
|
arch=(x86_64)
|
||||||
|
source=("meta-1.0.tar.gz")
|
||||||
|
makedepends=('make')
|
||||||
|
optdepends=('bonus: optional extra')
|
||||||
|
provides=('virt')
|
||||||
|
conflicts=('other')
|
||||||
|
replaces=('old')
|
||||||
|
`)
|
||||||
|
stderr, err := convertStderr(t, dir, registry.Options{Output: t.TempDir()})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Convert(dropped metadata) error = %v, want nil (warns only)", err)
|
||||||
|
}
|
||||||
|
for _, field := range []string{"makedepends", "optdepends", "provides", "conflicts", "replaces"} {
|
||||||
|
want := "warning: " + field + " dropped (not representable in Zeta)"
|
||||||
|
if !strings.Contains(stderr, want) {
|
||||||
|
t.Errorf("stderr = %q, want warning %q", stderr, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,6 @@
|
|||||||
package src
|
package src
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
|
|
||||||
"git.spectoria.dev/huntedbytheirs/zeta-reconstruct/internal/registry"
|
"git.spectoria.dev/huntedbytheirs/zeta-reconstruct/internal/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,9 +11,9 @@ type frontend struct{}
|
|||||||
|
|
||||||
func (frontend) Name() string { return "arch-src" }
|
func (frontend) Name() string { return "arch-src" }
|
||||||
|
|
||||||
|
// Convert delegates to the package-level converter (convert.go).
|
||||||
func (frontend) Convert(input string, opts registry.Options) error {
|
func (frontend) Convert(input string, opts registry.Options) error {
|
||||||
// Implemented in a later todo (PKGBUILD/.SRCINFO -> .recipe + build.sh).
|
return Convert(input, opts)
|
||||||
return errors.New("not implemented")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
Reference in New Issue
Block a user