Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for Arch Linux packages #543

Merged
merged 32 commits into from
Oct 15, 2022
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9621f48
feat: add support for Arch Linux packages
Elara6331 Sep 11, 2022
800db22
test: Add initial tests
Elara6331 Sep 12, 2022
e76830c
test: Increase coverage by modifying example info
Elara6331 Sep 12, 2022
b33d642
test: Add test for ArchLinux.ConventionalFileName()
Elara6331 Sep 12, 2022
ff3851c
docs: Return error if package name is invalid
Elara6331 Sep 12, 2022
2068f22
fix: Make empty name invalid
Elara6331 Sep 12, 2022
4219aad
fix: Add replaces field to .PKGINFO generator
Elara6331 Sep 12, 2022
af7817e
test: Add additional tests
Elara6331 Sep 12, 2022
1e1fe66
test: Test for added replaces field
Elara6331 Sep 12, 2022
2170154
docs: Add more comments
Elara6331 Sep 12, 2022
0d9cb16
style: Run gofumpt
Elara6331 Sep 12, 2022
f7e4982
fix: Handle errors as recommended by linter
Elara6331 Sep 12, 2022
c5c7bb4
fix: Allow changing the pkgbase
Elara6331 Sep 12, 2022
549cd4a
style: Resolve semgrep findings
Elara6331 Sep 12, 2022
72b40b8
docs: Change docs to reflect new Arch Linux packager
Elara6331 Sep 12, 2022
c5dfae6
docs: Fix spelling mistake in comment
Elara6331 Sep 13, 2022
19a84eb
docs: use aspell to fix all spelling mistakes
Elara6331 Sep 13, 2022
d091ba9
feat: Handle packaging formats with non-distinct file extensions as d…
Elara6331 Sep 13, 2022
691959e
fix: Add newline to generated .INSTALL file
Elara6331 Sep 14, 2022
08bb186
fix: Take into account provided info for non-symlink files
Elara6331 Sep 14, 2022
05dacae
docs: Fix names for arch-specific scripts in documentation
Elara6331 Sep 14, 2022
c822651
fix: Only consider files with the correct packager field
Elara6331 Sep 14, 2022
aa0519e
fix: Use correct scripts field for post_remove script
Elara6331 Sep 14, 2022
58d9394
test: Implement archlinux acceptance tests
Elara6331 Sep 14, 2022
27cae0b
test: Add archlinux to acceptance_test.go
Elara6331 Sep 14, 2022
dc1f215
test: Add archlinux to github test matrix
Elara6331 Sep 15, 2022
b3e7c15
test: Use updated build.yml from main branch
Elara6331 Sep 15, 2022
079592b
Merge branch 'main' into add-archlinux-pkgs
caarlos0 Sep 15, 2022
19e2c24
Fix ConventionalExtension() for apk
Elara6331 Sep 20, 2022
c0e6c43
fix: Take epoch value into account
Elara6331 Sep 22, 2022
eae88e8
fix: Add arm5 and arm6 architectures
Elara6331 Sep 22, 2022
31ab006
Merge branch 'main' into add-archlinux-pkgs
caarlos0 Oct 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
658 changes: 658 additions & 0 deletions arch/arch.go

Large diffs are not rendered by default.

273 changes: 273 additions & 0 deletions arch/arch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
package arch

import (
"archive/tar"
"bytes"
"fmt"
"io"
"strings"
"testing"

"github.com/goreleaser/nfpm/v2"
"github.com/goreleaser/nfpm/v2/files"
"github.com/klauspost/pgzip"
"github.com/stretchr/testify/require"
)

func exampleInfo() *nfpm.Info {
return nfpm.WithDefaults(&nfpm.Info{
Name: "foo-test",
Arch: "amd64",
Description: "Foo does things",
Priority: "extra",
Maintainer: "Carlos A Becker <pkg@carlosbecker.com>",
Version: "1.0.0",
Section: "default",
Homepage: "http://carlosbecker.com",
Vendor: "nope",
License: "MIT",
Overridables: nfpm.Overridables{
Depends: []string{
"bash",
},
Replaces: []string{
"svn",
},
Provides: []string{
"bzr",
},
Conflicts: []string{
"zsh",
},
Contents: []*files.Content{
{
Source: "../testdata/fake",
Destination: "/usr/local/bin/fake",
},
{
Source: "../testdata/whatever.conf",
Destination: "/etc/fake/fake.conf",
Type: "config",
},
{
Destination: "/var/log/whatever",
Type: "dir",
},
{
Destination: "/usr/share/whatever",
Type: "dir",
},
{
Source: "/etc/fake/fake.conf",
Destination: "/etc/fake/fake-link.conf",
Type: "symlink",
},
},
Scripts: nfpm.Scripts{
PreInstall: "../testdata/scripts/preinstall.sh",
PostInstall: "../testdata/scripts/postinstall.sh",
PreRemove: "../testdata/scripts/preremove.sh",
PostRemove: "../testdata/scripts/postremove.sh",
},
ArchLinux: nfpm.ArchLinux{
Scripts: nfpm.ArchLinuxScripts{
PreUpgrade: "../testdata/scripts/preupgrade.sh",
PostUpgrade: "../testdata/scripts/postupgrade.sh",
},
},
},
})
}

func TestArch(t *testing.T) {
for _, arch := range []string{"386", "amd64", "arm64"} {
arch := arch
t.Run(arch, func(t *testing.T) {
info := exampleInfo()
info.Arch = arch
err := Default.Package(info, io.Discard)
require.NoError(t, err)
})
}
}

func TestArchNoFiles(t *testing.T) {
info := exampleInfo()
info.Contents = nil
info.Scripts = nfpm.Scripts{}
info.ArchLinux = nfpm.ArchLinux{}
err := Default.Package(info, io.Discard)
require.NoError(t, err)
}

func TestArchNoInfo(t *testing.T) {
err := Default.Package(nfpm.WithDefaults(&nfpm.Info{}), io.Discard)
require.Error(t, err)
}

func TestArchConventionalFileName(t *testing.T) {
for _, arch := range []string{"386", "amd64", "arm64"} {
arch := arch
t.Run(arch, func(t *testing.T) {
info := exampleInfo()
info.Arch = arch
name := Default.ConventionalFileName(info)
require.Equal(t,
"foo-test-1.0.0-1-"+archToArchLinux[arch]+".pkg.tar.zst",
name,
)
})
}
}

func TestArchPkginfo(t *testing.T) {
pkginfoData, err := makeTestPkginfo(t, exampleInfo())
require.NoError(t, err)
fields := extractPkginfoFields(pkginfoData)
require.Equal(t, "foo-test", fields["pkgname"])
require.Equal(t, "foo-test", fields["pkgbase"])
require.Equal(t, "1.0.0-1", fields["pkgver"])
require.Equal(t, "Foo does things", fields["pkgdesc"])
require.Equal(t, "http://carlosbecker.com", fields["url"])
require.Equal(t, "Unknown Packager", fields["packager"])
require.Equal(t, "x86_64", fields["arch"])
require.Equal(t, "MIT", fields["license"])
require.Equal(t, "1234", fields["size"])
require.Equal(t, "svn", fields["replaces"])
require.Equal(t, "zsh", fields["conflict"])
require.Equal(t, "bzr", fields["provides"])
require.Equal(t, "bash", fields["depend"])
require.Equal(t, "etc/fake/fake.conf", fields["backup"])
}

func TestArchPkgbase(t *testing.T) {
info := exampleInfo()
info.ArchLinux.Pkgbase = "foo"
pkginfoData, err := makeTestPkginfo(t, info)
require.NoError(t, err)
fields := extractPkginfoFields(pkginfoData)
require.Equal(t, "foo", fields["pkgbase"])
}

func TestArchInvalidName(t *testing.T) {
info := exampleInfo()
info.Name = "#"
_, err := makeTestPkginfo(t, info)
require.ErrorIs(t, err, ErrInvalidPkgName)
}

func TestArchVersionWithRelease(t *testing.T) {
info := exampleInfo()
info.Version = "0.0.1"
info.Release = "4"
pkginfoData, err := makeTestPkginfo(t, info)
require.NoError(t, err)
fields := extractPkginfoFields(pkginfoData)
require.Equal(t, "0.0.1-4", fields["pkgver"])
}

func TestArchOverrideArchitecture(t *testing.T) {
info := exampleInfo()
info.ArchLinux.Arch = "randomarch"
pkginfoData, err := makeTestPkginfo(t, info)
require.NoError(t, err)
fields := extractPkginfoFields(pkginfoData)
require.Equal(t, "randomarch", fields["arch"])
}

func makeTestPkginfo(t *testing.T, info *nfpm.Info) ([]byte, error) {
t.Helper()

buf := &bytes.Buffer{}
tw := tar.NewWriter(buf)

entry, err := createPkginfo(info, tw, 1234)
if err != nil {
return nil, err
}

tw.Close()

tr := tar.NewReader(buf)
_, err = tr.Next()
require.NoError(t, err)

pkginfoData := make([]byte, entry.Size)
_, err = io.ReadFull(tr, pkginfoData)
if err != nil {
return nil, err
}

return pkginfoData, nil
}

func extractPkginfoFields(data []byte) map[string]string {
strData := string(data)
strData = strings.TrimPrefix(strData, "# Generated by nfpm\n")
strData = strings.TrimSpace(strData)

splitData := strings.Split(strData, "\n")
out := map[string]string{}

for _, kvPair := range splitData {
fmt.Println(kvPair)
splitPair := strings.Split(kvPair, " = ")
fmt.Println(splitPair, len(splitPair), splitPair[0], splitPair[1])
out[splitPair[0]] = splitPair[1]
}

return out
}

const correctMtree = `#mtree
./foo time=1234.0 type=dir
./3 time=12345.0 mode=644 size=100 type=file md5digest=abcd sha256digest=ef12
./sh time=123456.0 mode=777 type=link link=/bin/bash
`

func TestArchMtree(t *testing.T) {
info := exampleInfo()

buf := &bytes.Buffer{}
tw := tar.NewWriter(buf)

err := createMtree(info, tw, []MtreeEntry{
{
Destination: "/foo",
Time: 1234,
Type: "dir",
},
{
Destination: "/3",
Time: 12345,
Mode: 0o644,
Size: 100,
Type: "file",
MD5: []byte{0xAB, 0xCD},
SHA256: []byte{0xEF, 0x12},
},
{
LinkSource: "/bin/bash",
Destination: "/sh",
Time: 123456,
Mode: 0o777,
Type: "symlink",
},
})
require.NoError(t, err)

tw.Close()

tr := tar.NewReader(buf)
_, err = tr.Next()
require.NoError(t, err)

gr, err := pgzip.NewReader(tr)
require.NoError(t, err)
defer gr.Close()

mtree, err := io.ReadAll(gr)
require.NoError(t, err)

require.InDeltaSlice(t, []byte(correctMtree), mtree, 0)
}
2 changes: 1 addition & 1 deletion internal/cmd/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func newPackageCmd() *packageCmd {

cmd.Flags().StringVarP(&root.config, "config", "f", "nfpm.yaml", "config file to be used")
cmd.Flags().StringVarP(&root.target, "target", "t", "", "where to save the generated package (filename, folder or empty for current folder)")
cmd.Flags().StringVarP(&root.packager, "packager", "p", "", "which packager implementation to use [apk|deb|rpm]")
cmd.Flags().StringVarP(&root.packager, "packager", "p", "", "which packager implementation to use [apk|deb|rpm|archlinux]")

root.cmd = cmd
return root
Expand Down
1 change: 1 addition & 0 deletions internal/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

_ "github.com/goreleaser/nfpm/v2/apk"
_ "github.com/goreleaser/nfpm/v2/arch"
_ "github.com/goreleaser/nfpm/v2/deb"
_ "github.com/goreleaser/nfpm/v2/rpm"
"github.com/spf13/cobra"
Expand Down
13 changes: 13 additions & 0 deletions nfpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,19 @@ type Overridables struct {
RPM RPM `yaml:"rpm,omitempty" json:"rpm,omitempty" jsonschema:"title=rpm-specific settings"`
Deb Deb `yaml:"deb,omitempty" json:"deb,omitempty" jsonschema:"title=deb-specific settings"`
APK APK `yaml:"apk,omitempty" json:"apk,omitempty" jsonschema:"title=apk-specific settings"`
ArchLinux ArchLinux `yaml:"archlinux,omitempty" json:"archlinux,omitempty" jsonschema:"title=archlinux-specific settings"`
}

type ArchLinux struct {
Pkgbase string `yaml:"pkgbase,omitempty" json:"pkgbase,omitempty" jsonschema:"title=explicitly specify the name used to refer to a split package, defaults to name"`
Arch string `yaml:"arch,omitempty" json:"arch,omitempty" jsonschema:"title=architecture in archlinux nomenclature"`
Packager string `yaml:"packager,omitempty" json:"packager,omitempty" jsonschema:"title=organization that packaged the software"`
Scripts ArchLinuxScripts `yaml:"scripts,omitempty" json:"scripts,omitempty" jsonschema:"title=archlinux-specific scripts"`
}

type ArchLinuxScripts struct {
PreUpgrade string `yaml:"preupgrade,omitempty" json:"preupgrade,omitempty" jsonschema:"title=preupgrade script"`
PostUpgrade string `yaml:"postupgrade,omitempty" json:"postupgrade,omitempty" jsonschema:"title=postupgrade script"`
}

// RPM is custom configs that are only available on RPM packages.
Expand Down
2 changes: 1 addition & 1 deletion www/docs/cmd/nfpm_package.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ nfpm package [flags]
```
-f, --config string config file to be used (default "nfpm.yaml")
-h, --help help for package
-p, --packager string which packager implementation to use [apk|deb|rpm]
-p, --packager string which packager implementation to use [apk|deb|rpm|archlinux]
-t, --target string where to save the generated package (filename, folder or empty for current folder)
```

Expand Down
22 changes: 22 additions & 0 deletions www/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ overrides:
# ...
apk:
# ...
archlinux:
depends:
- baz
- some-lib

# Custom configuration applied only to the RPM packager.
rpm:
Expand Down Expand Up @@ -329,6 +333,24 @@ apk:
key_name: origin
# APK does not use pgp keys, so the key_id field is ignored.
key_id: ignored

archlinux:
# This value is used to specify the name used to refer to a group
# of packages when building a split package. Defaults to name
# See: https://wiki.archlinux.org/title/PKGBUILD#pkgbase
pkgbase: bar
# The packager identifies the organization packaging the software
# rather than the developer. Defaults to "Unknown Packager".
packager: GoReleaser <staff@goreleaser.com>

# Arch Linux specific scripts.
scripts:
# The preupdate script runs before pacman updates the package
preupdate: ./scripts/preupdate.sh
# The postupdate script runs after pacman updates the package
postupdate: ./scripts/postupdate.sh


```

## Templating
Expand Down
2 changes: 1 addition & 1 deletion www/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

![](/static/banner.svg)

nFPM is a simple, 0-dependencies, `deb`, `rpm` and `apk` packager.
nFPM is a simple, 0-dependencies, `deb`, `rpm`, `apk`, and Arch Linux packager.

## Why

Expand Down