Something went wrong. Try again.
Maps Linux distribution source packages to their upstream repositories, and through them to each other
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330package opensuse
import ( "os" "path/filepath" "testing")
func loadSpec(t *testing.T, rel string) *Spec { t.Helper() b, err := os.ReadFile(filepath.Join("testdata", rel)) if err != nil { t.Fatal(err) } return ParseSpec(string(b))}
func patchByFile(s *Spec, file string) *Patch { for i := range s.Patches { if s.Patches[i].File == file { return &s.Patches[i] } } return nil}
func sourceByTag(s *Spec, tag string) *Source { for i := range s.Sources { if s.Sources[i].Tag == tag { return &s.Sources[i] } } return nil}
func TestParseSpecCurl(t *testing.T) { s := loadSpec(t, "curl/curl.spec") // Name is "curl%{?psuffix}"; psuffix is "-mini" only for the mini flavor, which the // evaluator sees as "%{target}" == "" != "mini". if s.Name != "curl" || s.Version != "8.22.0" || s.Release != "0" || s.URL != "https://curl.se" { t.Errorf("preamble: %+v", s) } if src := sourceByTag(s, "Source"); src == nil || src.Value != "https://curl.se/download/curl-8.22.0.tar.xz" || !src.IsURL { t.Errorf("Source: %+v", src) } if src := sourceByTag(s, "Source4"); src == nil || src.Value != "https://daniel.haxx.se/mykey.asc" || src.Rename != "curl.keyring" { t.Errorf("Source4: %+v", src) } if src := sourceByTag(s, "Source3"); src == nil || src.Value != "baselibs.conf" || src.IsURL { t.Errorf("Source3: %+v", src) } if len(s.Patches) != 3 || s.Patches[2].File != "curl-secure-getenv.patch" || s.Patches[2].Comment != nil { t.Errorf("patches: %+v", s.Patches) } if got := s.SourceURLs(); len(got) != 3 { t.Errorf("source urls: %v", got) } if s.Macros["with_openssl"] != "1" || s.Macros["with_quic"] != "1" { t.Errorf("bconds: %v", s.Macros) }}
func TestParseSpecApr(t *testing.T) { s := loadSpec(t, "apr/apr.spec") if s.Name != "apr" || s.Version != "1.7.6" { t.Fatalf("preamble: %+v", s) } if src := sourceByTag(s, "Source2"); src == nil || src.Value != "https://downloads.apache.org/apr/KEYS" || src.Rename != "apr.keyring" { t.Errorf("Source2: %+v", src) } if got := s.Expand("%{libname}"); got != "libapr1-0" { t.Errorf("nested macro: %q", got) } p := patchByFile(s, "reproducible.patch") if p == nil || p.Tag != "Patch11" || p.Comment != nil || len(p.Comments) != 0 { t.Errorf("Patch11: %+v", p) } if p := patchByFile(s, "apr-test-sendfile-timeout.patch"); p == nil || len(p.Comments) != 1 || p.Comments[0] != "prevent random failures of the testsuite (sendfile test)" { t.Errorf("Patch10 comments: %+v", p) }}
func TestParseSpecCeph(t *testing.T) { s := loadSpec(t, "ceph/ceph.spec") if s.Name != "ceph" || s.Version != "18.2.7" || s.Release != "0" { t.Errorf("preamble: name=%q version=%q release=%q", s.Name, s.Version, s.Release) } // _remote_tarball_prefix is only defined under %if 0%{?fedora} || 0%{?rhel} || ... if src := sourceByTag(s, "Source0"); src == nil || src.Value != "ceph-18.2.7.tar.bz2" || src.IsURL { t.Errorf("Source0: %+v", src) } if len(s.Patches) != 19 { t.Fatalf("patches: %d", len(s.Patches)) } p := patchByFile(s, "cephadm-reproducible.patch") if p == nil || p.Comment == nil { t.Fatalf("Patch15: %+v", p) } if c := p.Comment; c.Kind != "FIX" || c.Scope != "OPENSUSE" || c.File != "cephadm-reproducible.patch" || c.Text != "Ensure cephadm executable is reproducible" { t.Errorf("Patch15 tag: %+v", c) } if p := patchByFile(s, "ceph-mgr-python-avoid-pyo3-errors.patch"); p == nil || p.Comment == nil || p.Comment.Text != "PR #62951" || !p.Comment.IsUpstream() { t.Errorf("Patch9 tag: %+v", p) } if p := patchByFile(s, "ceph-replace-CryptoPP-calls-with-GnuTLS.patch"); p == nil || p.Comment == nil || p.Comment.File != "" || len(p.Comment.Refs) != 1 || p.Comment.Refs[0] != "jsc#PED-13011" || p.Comment.Text != "replace CryptoPP calls with GnuTLS" { t.Errorf("Patch14 tag: %+v", p.Comment) } if p := patchByFile(s, "ceph-mgr-stop-using-deprecated-api-to-initialize-python.patch"); p == nil || p.Comment == nil || len(p.Comment.Refs) != 1 || p.Comment.Refs[0] != "4cf9b36c66fd64f2dc50f4e1acca3fe93e29b3f2" { t.Errorf("Patch0 sha ref: %+v", p.Comment) }}
func TestParseSpecRivet(t *testing.T) { s := loadSpec(t, "specs/Rivet.spec") if s.Name != "Rivet" || s.URL != "https://gitlab.com/hepcedar/rivet/" { t.Errorf("preamble: %+v", s) } if src := sourceByTag(s, "Source"); src == nil || src.Value != "https://www.hepforge.org/archive/rivet/Rivet-4.1.2.tar.bz2" { t.Errorf("Source: %+v", src) } p := patchByFile(s, "Rivet-reproducible-plugin-build.patch") if p == nil || p.Comment == nil { t.Fatalf("Patch2: %+v", p) } c := p.Comment if !c.IsUpstream() || c.File != "Rivet-reproducible-plugin-build.patch" || c.Email != "bwiedemann@suse.de" || c.Text != "Make plugin .so builds independent of CPU count and tmp file names" { t.Errorf("Patch2 tag: %+v", c) } if p := patchByFile(s, "Rivet-disable-testCmdLine.patch"); p == nil || p.Comment == nil || p.Comment.Email != "badshah400@gmail.com" || p.Comment.Scope != "OPENSUSE" { t.Errorf("Patch1 tag: %+v", p) } if p := patchByFile(s, "sover.diff"); p == nil || p.Comment != nil { t.Errorf("Patch0 should be untagged: %+v", p) }}
func TestParseSpecAnt(t *testing.T) { s := loadSpec(t, "specs/ant.spec") // Name depends on "%{flavor}" (from @BUILD_FLAVOR@, empty for the main build). if s.Name != "ant" || s.Version != "1.10.18" { t.Errorf("preamble: name=%q version=%q", s.Name, s.Version) } if src := sourceByTag(s, "Source0"); src == nil || src.Value != "https://www.apache.org/dist/ant/source/apache-ant-1.10.18-src.tar.xz" { t.Errorf("Source0: %+v", src) } p := patchByFile(s, "reproducible-build-manifest.patch") if p == nil || p.Comment == nil || p.Comment.Kind != "FEATURE" || p.Comment.Scope != "OPENSUSE" || p.Comment.Text != `have fixed "Created-by" in manifest` { t.Errorf("Patch3: %+v", p) } p = patchByFile(s, "reproducible-jar-mtime.patch") if p == nil || p.Comment == nil || p.Comment.File != "" || p.Comment.Text != "debian patch to use SOURCE_DATE_EPOCH for timestamp in property files" { t.Errorf("Patch5: %+v", p) } if p := patchByFile(s, "reproducible-javadoc.patch"); p == nil || p.Comment != nil { t.Errorf("Patch6 should be untagged: %+v", p) } if s.Macros["with_bootstrap"] != "1" { t.Errorf("main flavor should take the %%else branch: %v", s.Macros["with_bootstrap"]) }}
func TestParseSpecArj(t *testing.T) { s := loadSpec(t, "specs/arj.spec") if src := sourceByTag(s, "Source0"); src == nil || src.Value != "http://downloads.sourceforge.net/arj/arj-3.10.22.tar.gz" { t.Errorf("Source0: %+v", src) } p := patchByFile(s, "arj-3.10.22-reproducible.patch") if p == nil || p.Comment == nil || p.Comment.Scope != "OPENSUSE" || p.Comment.File != "" || p.Comment.Text != "make build reproducible" { t.Errorf("Patch4: %+v", p) } p = patchByFile(s, "arj-3.10.22-fixstrcpy.patch") if p == nil || p.Comment == nil || !p.Comment.IsUpstream() || len(p.Comment.Refs) != 1 || p.Comment.Refs[0] != "https://sourceforge.net/p/arj/git/merge-requests/1/" { t.Errorf("Patch5: %+v", p) } if p := patchByFile(s, "arj_3.10.22-6.diff.gz"); p == nil { t.Errorf("compressed patch missing") }}
func TestParseSpecWindowMaker(t *testing.T) { s := loadSpec(t, "specs/WindowMaker.spec") if p := patchByFile(s, "WindowMaker-config.patch"); p == nil || p.Tag != "Patch1" { t.Errorf("%%{name} in Patch value: %+v", s.Patches) } p := patchByFile(s, "WindowMaker-reproducible.patch") if p == nil || p.Comment == nil || p.Comment.Kind != "FEATURE" || p.Comment.Scope != "OPENSUSE" || p.Comment.Text != "" { t.Errorf("bare tag: %+v", p) } // The bare "#" line above Patch101 is not a tag. if p := patchByFile(s, "wm-giflib.patch"); p == nil || p.Comment != nil { t.Errorf("Patch101: %+v", p) }}
func TestParseSpecAmazonECSInit(t *testing.T) { s := loadSpec(t, "specs/amazon-ecs-init.spec") src := sourceByTag(s, "Source0") if src == nil || src.Value != "https://github.com/aws/amazon-ecs-agent/archive/refs/tags/v1.106.1.tar.gz" || src.Rename != "amazon-ecs-init-1.106.1.tar.gz" { t.Errorf("Source0: %+v", src) } if src := sourceByTag(s, "Source1"); src == nil || src.Value != "amazon-ecs.service" { t.Errorf("Source1 (%%{short_name}): %+v", src) } p := patchByFile(s, "CVE-2026-56852.patch") if p == nil || p.Comment == nil || !p.Comment.IsUpstream() || p.Comment.File != "CVE-2026-56852.patch" || len(p.Comment.Refs) != 1 || p.Comment.Refs[0] != "bsc#1272128" { t.Errorf("Patch1: %+v", p) } if p := patchByFile(s, "reproducible.patch"); p == nil || p.Comment != nil { t.Errorf("Patch0 must not steal Patch1's tag: %+v", p) }}
func TestParseSpecArandr(t *testing.T) { s := loadSpec(t, "specs/arandr.spec") if s.Name != "arandr" || s.URL != "https://christian.amsuess.com/tools/arandr/" { t.Errorf("preamble: %+v", s) } if src := sourceByTag(s, "Source"); src == nil || src.Value != "http://christian.amsuess.com/tools/arandr/files/arandr-0.1.11.tar.gz" { t.Errorf("Source: %+v", src) }}
func TestParseSpecGitCloneAndURLPatch(t *testing.T) { s := ParseSpec(`#Git-Clone: https://github.com/example/project.git%define project github.com/containers/podman%global vertag v%{version}Name: podmanVersion: 6.0.2Release: 0URL: https://%{project}Source0: https://%{project}/archive/%{vertag}.tar.gz#/%{name}-%{version}.tar.gz# PATCH-FIX-UPSTREAM fix-wayland-compatibility.patch -- based on PR 123Patch7: https://github.com/example/project/pull/132.patch# PATCH-FIX-UPSTREAM fix-thing.patch bsc#123456 -- based on commit abc123Patch8: https://github.com/example/project/commit/abc1234567.patch`) if s.GitClone != "https://github.com/example/project.git" { t.Errorf("Git-Clone: %q", s.GitClone) } if s.URL != "https://github.com/containers/podman" { t.Errorf("URL: %q", s.URL) } if src := sourceByTag(s, "Source0"); src == nil || src.Value != "https://github.com/containers/podman/archive/v6.0.2.tar.gz" || src.Rename != "podman-6.0.2.tar.gz" { t.Errorf("Source0: %+v", src) } if len(s.Patches) != 2 { t.Fatalf("patches: %+v", s.Patches) } p := s.Patches[0] if !p.IsURL || p.File != "132.patch" || p.Comment == nil || p.Comment.File != "fix-wayland-compatibility.patch" || p.Comment.Text != "based on PR 123" { t.Errorf("Patch7: %+v", p) } p = s.Patches[1] if p.File != "abc1234567.patch" || p.Comment == nil || len(p.Comment.Refs) != 1 || p.Comment.Refs[0] != "bsc#123456" { t.Errorf("Patch8: %+v", p) }}
func TestMacroExpansion(t *testing.T) { cases := []struct { spec string want map[string]string }{ {"%define foo 1\n%define bar %{foo}2\nName: x%{bar}\nVersion: %{?undefined_thing}%{!?undefined_thing:3}%{?foo:4}", map[string]string{"name": "x12", "version": "34"}}, {"%global v 2\n%if %{v} >= 2\nName: new\n%else\nName: old\n%endif", map[string]string{"name": "new"}}, {"%if 0%{?fedora} || 0%{?rhel} >= 9\nName: fedora\n%else\nName: suse\n%endif", map[string]string{"name": "suse"}}, {"%if 0%{?suse_version} >= 1699\nName: tw\n%else\nName: leap\n%endif", map[string]string{"name": "tw"}}, {"%bcond_without x\n%if %{with x}\nName: withx\n%else\nName: withoutx\n%endif", map[string]string{"name": "withx"}}, {"%bcond_with x\n%if %{with x}\nName: withx\n%elif ! %{with x}\nName: withoutx\n%else\nName: never\n%endif", map[string]string{"name": "withoutx"}}, {"%ifarch x86_64\nName: a\n%else\nName: b\n%endif", map[string]string{"name": "a"}}, // unknown branch: first wins {"%define flavor @BUILD_FLAVOR@%{nil}\n%if \"%{flavor}\" == \"\"\nName: main\n%else\nName: flavor\n%endif", map[string]string{"name": "main"}}, {"Name: a\nVersion: 1.0\nRelease: 0%{?dist}\nURL: https://example.org/%{name}-%name-%{version}", map[string]string{"release": "0", "url": "https://example.org/a-a-1.0"}}, {"%define a %(echo hi)\nName: x%{a}y%{_libdir}z", map[string]string{"name": "x%(echo hi)y%{_libdir}z"}}, {"%define a b \\\n c\nName: %{a}", map[string]string{"name": "b c"}}, {"%if 0\n%define x no\n%endif\n%define x yes\nName: %{x}", map[string]string{"name": "yes"}}, } for _, c := range cases { s := ParseSpec(c.spec) for k, want := range c.want { got := s.Macros[k] if got != want { t.Errorf("spec %q: %s = %q, want %q", c.spec, k, got, want) } } }}
func TestParsePatchTag(t *testing.T) { cases := []struct { in string want PatchTag }{ {"PATCH-FIX-OPENSUSE -- make build reproducible", PatchTag{Kind: "FIX", Scope: "OPENSUSE", Text: "make build reproducible"}}, {"PATCH-FIX-UPSTREAM https://sourceforge.net/p/arj/git/merge-requests/1/", PatchTag{Kind: "FIX", Scope: "UPSTREAM", Refs: []string{"https://sourceforge.net/p/arj/git/merge-requests/1/"}}}, {"PATCH-FIX-UPSTREAM -- bmwiedemann https://sourceforge.net/p/dvdstyler/DVDStyler/merge-requests/1/", PatchTag{Kind: "FIX", Scope: "UPSTREAM", Refs: []string{"https://sourceforge.net/p/dvdstyler/DVDStyler/merge-requests/1/"}, Text: "bmwiedemann https://sourceforge.net/p/dvdstyler/DVDStyler/merge-requests/1/"}}, {"PATCH-FIX-UPSTREAM x.patch -- based on commit 4cf9b36c66fd64f2dc50f4e1acca3fe93e29b3f2", PatchTag{Kind: "FIX", Scope: "UPSTREAM", File: "x.patch", Refs: []string{"4cf9b36c66fd64f2dc50f4e1acca3fe93e29b3f2"}, Text: "based on commit 4cf9b36c66fd64f2dc50f4e1acca3fe93e29b3f2"}}, {"PATCH-FEATURE-OPENSUSE", PatchTag{Kind: "FEATURE", Scope: "OPENSUSE"}}, {"PATCH-FIX-SLE foo.patch bsc#1 bsc#2 jsc#SLE-3 me@example.com -- text here", PatchTag{Kind: "FIX", Scope: "SLE", File: "foo.patch", Refs: []string{"bsc#1", "bsc#2", "jsc#SLE-3"}, Email: "me@example.com", Text: "text here"}}, {"PATCH-FIX-UPSTREAM ceph-librbd-fix-atomic-shared-pointer.patch - 7e3f243eae129634359dac1943f85155f6ff3e61", PatchTag{Kind: "FIX", Scope: "UPSTREAM", File: "ceph-librbd-fix-atomic-shared-pointer.patch", Refs: []string{"7e3f243eae129634359dac1943f85155f6ff3e61"}, Text: "-"}}, {"not a tag", PatchTag{}}, } for _, c := range cases { got := parsePatchTag(c.in) if c.want.Kind == "" { if got != nil { t.Errorf("%q: expected nil, got %+v", c.in, got) } continue } if got == nil { t.Errorf("%q: expected tag", c.in) continue } got.Raw = "" if got.Kind != c.want.Kind || got.Scope != c.want.Scope || got.File != c.want.File || got.Email != c.want.Email || got.Text != c.want.Text || len(got.Refs) != len(c.want.Refs) { t.Errorf("%q: got %+v want %+v", c.in, got, c.want) continue } for i := range got.Refs { if got.Refs[i] != c.want.Refs[i] { t.Errorf("%q: refs %v want %v", c.in, got.Refs, c.want.Refs) } } }}