Repository: goreleaser/nfpm
Pull request #323 (2021-04-20 19:12:22)

feat: add support for alpine pre and post upgrade scripts
* [x] Added upgrade test for apk
* [x] Added upgrade test for deb
* [x] Converted the config-noreplace test for rpm to be an upgrade test.

vercel[bot]: This pull request is being automatically deployed with Vercel ([learn more](https://vercel.link/github-learn-more)).  
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: [https://vercel.com/goreleaser/nfpm/B8qVZASg3m3BVMPijQLJH1v9nMKB](https://vercel.com/goreleaser/nfpm/B8qVZASg3m3BVMPijQLJH1v9nMKB)  
✅ Preview: [https://nfpm-git-support-apk-upgrade-scripts-goreleaser.vercel.app](https://nfpm-git-support-apk-upgrade-scripts-goreleaser.vercel.app)


codecov[bot]: # [Codecov](https://codecov.io/gh/goreleaser/nfpm/pull/323?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) Report
> Merging [#323](https://codecov.io/gh/goreleaser/nfpm/pull/323?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) (77ec6dd) into [master](https://codecov.io/gh/goreleaser/nfpm/commit/1fb26910a00fb1e7625f2763db819521f87f1134?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) (1fb2691) will **increase** coverage by `0.10%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/goreleaser/nfpm/pull/323/graphs/tree.svg?width=650&height=150&src=pr&token=9QKGIxn7wg&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser)](https://codecov.io/gh/goreleaser/nfpm/pull/323?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser)

```diff
@@            Coverage Diff             @@
##           master     #323      +/-   ##
==========================================
+ Coverage   69.37%   69.47%   +0.10%     
==========================================
  Files           9        9              
  Lines        1159     1163       +4     
==========================================
+ Hits          804      808       +4     
  Misses        223      223              
  Partials      132      132              
```


| [Impacted Files](https://codecov.io/gh/goreleaser/nfpm/pull/323?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) | Coverage Δ | |
|---|---|---|
| [nfpm.go](https://codecov.io/gh/goreleaser/nfpm/pull/323/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-bmZwbS5nbw==) | `85.32% <ø> (ø)` | |
| [apk/apk.go](https://codecov.io/gh/goreleaser/nfpm/pull/323/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-YXBrL2Fway5nbw==) | `70.53% <100.00%> (+0.49%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/goreleaser/nfpm/pull/323?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/goreleaser/nfpm/pull/323?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser). Last update [fb9b6f4...77ec6dd](https://codecov.io/gh/goreleaser/nfpm/pull/323?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser).


Patch:
diff --git a/apk/apk.go b/apk/apk.go
index 0a986f3..3c53eb6 100644
--- a/apk/apk.go
+++ b/apk/apk.go
@@ -340,10 +340,12 @@ func createBuilderControl(info *nfpm.Info, size int64, dataDigest []byte) func(t
 		//
 		// exit 0
 		for script, dest := range map[string]string{
-			info.Scripts.PreInstall:  ".pre-install",
-			info.Scripts.PostInstall: ".post-install",
-			info.Scripts.PreRemove:   ".pre-deinstall",
-			info.Scripts.PostRemove:  ".post-deinstall",
+			info.Scripts.PreInstall:      ".pre-install",
+			info.APK.Scripts.PreUpgrade:  ".pre-upgrade",
+			info.Scripts.PostInstall:     ".post-install",
+			info.APK.Scripts.PostUpgrade: ".post-upgrade",
+			info.Scripts.PreRemove:       ".pre-deinstall",
+			info.Scripts.PostRemove:      ".post-deinstall",
 		} {
 			if script != "" {
 				if err := newScriptInsideTarGz(tw, script, dest); err != nil {
diff --git a/nfpm.go b/nfpm.go
index cd7e190..7da6283 100644
--- a/nfpm.go
+++ b/nfpm.go
@@ -267,6 +267,7 @@ type RPMSignature struct {
 
 type APK struct {
 	Signature APKSignature `yaml:"signature,omitempty"`
+	Scripts   APKScripts   `yaml:"scripts,omitempty"`
 }
 
 type APKSignature struct {
@@ -275,6 +276,11 @@ type APKSignature struct {
 	KeyName string `yaml:"key_name,omitempty"`
 }
 
+type APKScripts struct {
+	PreUpgrade  string `yaml:"preupgrade,omitempty"`
+	PostUpgrade string `yaml:"postupgrade,omitempty"`
+}
+
 // Deb is custom configs that are only available on deb packages.
 type Deb struct {
 	Scripts   DebScripts   `yaml:"scripts,omitempty"`
diff --git a/www/docs/tips.md b/www/docs/tips.md
index a32d07e..09757f3 100644
--- a/www/docs/tips.md
+++ b/www/docs/tips.md
@@ -15,7 +15,7 @@
 
 ### Example Multi platform post-install script
 ```bash
-#!/bin/bash
+#!/bin/sh
 
 # Step 1, decide if we should use systemd or init/upstart
 use_systemctl="True"
@@ -36,11 +36,7 @@ cleanup() {
     fi
 }
 
-# Step 2, check if this is a clean install or an upgrade
-# [ -z "$1" ] == alpine linux - Note alpine uses a separate post-upgrade script
-# [ "$1" = "1" ] == RPM clean install
-# { [ "$1" = "configure" ] && [ -z "$2" ] ;} == deb clean install
-if [ -z "$1" ] || [ "$1" = "1" ] || { [ "$1" = "configure" ] && [ -z "$2" ] ;}; then
+cleanInstall() {
     printf "\033[32m Post Install of an clean install\033[0m\n"
     # Step 3 (clean install), enable the service in the proper way for this platform
     if [ "${use_systemctl}" = "False" ]; then
@@ -66,37 +62,79 @@ if [ -z "$1" ] || [ "$1" = "1" ] || { [ "$1" = "configure" ] && [ -z "$2" ] ;};
         systemctl enable <SERVICE NAME> ||:
         systemctl restart <SERVICE NAME> ||:
     fi
-else
+}
+
+upgrade() {
     printf "\033[32m Post Install of an upgrade\033[0m\n"
     # Step 3(upgrade), do what you need
     ...
+}
+
+# Step 2, check if this is a clean install or an upgrade
+action="$1"
+if  [ "$1" = "configure" ] && [ -z "$2" ]; then
+  # Alpine linux does not pass args, and deb passes $1=configure
+  action="install"
+elif [ "$1" = "configure" ] && [ -n "$2" ]; then
+    # deb passes $1=configure $2=<current version>
+    action="upgrade"
 fi
 
+case "$action" in
+  "1" | "install")
+    cleanInstall
+    ;;
+  "2" | "upgrade")
+    printf "\033[32m Post Install of an upgrade\033[0m\n"
+    upgrade
+    ;;
+  *)
+    # $1 == version being installed  
+    printf "\033[32m Alpine\033[0m"
+    cleanInstall
+    ;;
+esac
+
 # Step 4, clean up unused files, yes you get a warning when you remove the package, but that is ok. 
 cleanup
 ```
 ### Example Multi platform (RPM & Deb) post-remove script
 ```bash
-#!/bin/bash
+#!/bin/sh
+
+cleanInstall() {
+    printf "\033[32m Post Install of a clean install\033[0m\n"
+}
+
+upgrade() {
+    printf "\033[32m Post Install of an upgrade\033[0m\n"
+}
 
 action="$1"
-if [ -z "$1" ]; then
-  # Alpine linux does not pass args, so set what you want the post-remove to be
-  # Also it uses a separate pre/post-upgrade script
-  action=purge
+if  [ "$1" = "configure" ] && [ -z "$2" ]; then
+  # Alpine linux does not pass args, and deb passes $1=configure
+  action="install"
+elif [ "$1" = "configure" ] && [ -n "$2" ]; then
+    # deb passes $1=configure $2=<current version>
+    action="upgrade"
 fi
 
 case "$action" in
-  "0", "remove")
-    printf "\033[32m Post Remove of a normal remove\033[0m\n"
+  "1" | "install")
+    printf "\033[32m Post Install of a clean install\033[0m\n"
+    cleanInstall
     ;;
-  "1", "upgrade")
-    printf "\033[32m Post Remove of an upgrade\033[0m\n"
+  "2" | "upgrade")
+    printf "\033[32m Post Install of an upgrade\033[0m\n"
+    upgrade
     ;;
-  "purge")
-    printf "\033[32m Post Remove purge, deb only\033[0m\n"
+  *)
+    # $1 == version being installed  
+    printf "\033[32m Alpine\033[0m"
+    cleanInstall
     ;;
 esac
+
 ```
 
 ### Deb & RPM


Test patch:
diff --git a/acceptance_test.go b/acceptance_test.go
index ea54094..8992b63 100644
--- a/acceptance_test.go
+++ b/acceptance_test.go
@@ -64,37 +64,67 @@ func TestCore(t *testing.T) {
 	}
 }
 
-func TestConfigNoReplace(t *testing.T) {
+func TestUpgrade(t *testing.T) {
 	t.Parallel()
-	target := "./testdata/acceptance/tmp/noreplace_old_rpm.rpm"
-	require.NoError(t, os.MkdirAll("./testdata/acceptance/tmp", 0o700))
+	testNames := []string{
+		"upgrade",
+	}
+	for _, name := range testNames {
+		for format, architecture := range formatArchs {
+			for _, arch := range architecture {
+				func(tt *testing.T, testName, testFormat, testArch string) {
+					tt.Run(fmt.Sprintf("%s/%s/%s", testFormat, testArch, testName), func(ttt *testing.T) {
+						ttt.Parallel()
+						if testArch == "ppc64le" && os.Getenv("NO_TEST_PPC64LE") == "true" {
+							ttt.Skip("ppc64le arch not supported in pipeline")
+						}
+						oldpkg := fmt.Sprintf("tmp/%s_%s.v1.%s", testName, testArch, testFormat)
+						target := fmt.Sprintf("./testdata/acceptance/%s", oldpkg)
+						require.NoError(t, os.MkdirAll("./testdata/acceptance/tmp", 0o700))
 
-	config, err := nfpm.ParseFile("./testdata/acceptance/rpm.config-noreplace-old.yaml")
-	require.NoError(t, err)
+						config, err := nfpm.ParseFileWithEnvMapping(fmt.Sprintf("./testdata/acceptance/%s.v1.yaml", testName),
+							func(s string) string {
+								switch s {
+								case "BUILD_ARCH":
+									return testArch
+								case "SEMVER":
+									return "v1.0.0-0.1.b1+git.abcdefgh"
+								default:
+									return os.Getenv(s)
+								}
+							},
+						)
+						require.NoError(t, err)
 
-	info, err := config.Get("rpm")
-	require.NoError(t, err)
-	require.NoError(t, nfpm.Validate(info))
+						info, err := config.Get(testFormat)
+						require.NoError(t, err)
+						require.NoError(t, nfpm.Validate(info))
 
-	pkg, err := nfpm.Get("rpm")
-	require.NoError(t, err)
+						pkg, err := nfpm.Get(testFormat)
+						require.NoError(t, err)
 
-	f, err := os.Create(target)
-	require.NoError(t, err)
-	info.Target = target
-	require.NoError(t, pkg.Package(nfpm.WithDefaults(info), f))
-	t.Run("rpm/config-noreplace", func(t *testing.T) {
-		accept(t, acceptParms{
-			Name:   "noreplace_rpm",
-			Conf:   "rpm.config-noreplace.yaml",
-			Format: "rpm",
-			Docker: dockerParams{
-				File:   "rpm.dockerfile",
-				Target: "config-noreplace",
-				Arch:   "amd64",
-			},
-		})
-	})
+						f, err := os.Create(target)
+						require.NoError(t, err)
+						defer f.Close()
+						info.Target = target
+						require.NoError(t, pkg.Package(nfpm.WithDefaults(info), f))
+
+						accept(ttt, acceptParms{
+							Name:   fmt.Sprintf("%s_%s.v2", testName, testArch),
+							Conf:   fmt.Sprintf("%s.v2.yaml", testName),
+							Format: testFormat,
+							Docker: dockerParams{
+								File:      fmt.Sprintf("%s.dockerfile", testFormat),
+								Target:    testName,
+								Arch:      testArch,
+								BuildArgs: []string{fmt.Sprintf("oldpackage=%s", oldpkg)},
+							},
+						})
+					})
+				}(t, name, format, arch)
+			}
+		}
+	}
 }
 
 func TestCompression(t *testing.T) {
diff --git a/apk/apk_test.go b/apk/apk_test.go
index 46d8b65..4454e4e 100644
--- a/apk/apk_test.go
+++ b/apk/apk_test.go
@@ -14,7 +14,6 @@ import (
 	"path/filepath"
 	"testing"
 
-	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
 	"github.com/goreleaser/nfpm/v2"
@@ -101,8 +100,8 @@ func verifyArch(t *testing.T, nfpmArch, expectedArch string) {
 	info := exampleInfo()
 	info.Arch = nfpmArch
 
-	assert.NoError(t, Default.Package(info, ioutil.Discard))
-	assert.Equal(t, expectedArch, info.Arch)
+	require.NoError(t, Default.Package(info, ioutil.Discard))
+	require.Equal(t, expectedArch, info.Arch)
 }
 
 func TestCreateBuilderData(t *testing.T) {
@@ -115,9 +114,9 @@ func TestCreateBuilderData(t *testing.T) {
 	var buf bytes.Buffer
 	tw := tar.NewWriter(&buf)
 
-	assert.NoError(t, builderData(tw))
+	require.NoError(t, builderData(tw))
 
-	assert.Equal(t, 11784, buf.Len())
+	require.Equal(t, 11784, buf.Len())
 }
 
 func TestCombineToApk(t *testing.T) {
@@ -129,8 +128,8 @@ func TestCombineToApk(t *testing.T) {
 
 	var bufTarget bytes.Buffer
 
-	assert.NoError(t, combineToApk(&bufTarget, &bufData, &bufControl))
-	assert.Equal(t, 2, bufTarget.Len())
+	require.NoError(t, combineToApk(&bufTarget, &bufData, &bufControl))
+	require.Equal(t, 2, bufTarget.Len())
 }
 
 func TestPathsToCreate(t *testing.T) {
@@ -142,7 +141,7 @@ func TestPathsToCreate(t *testing.T) {
 		parts := parts
 		pathToTest := pathToTest
 		t.Run(fmt.Sprintf("pathToTest: '%s'", pathToTest), func(t *testing.T) {
-			assert.Equal(t, parts, pathsToCreate(pathToTest))
+			require.Equal(t, parts, pathsToCreate(pathToTest))
 		})
 	}
 }
@@ -182,7 +181,7 @@ func TestDefaultWithArch(t *testing.T) {
 
 func TestNoInfo(t *testing.T) {
 	err := Default.Package(nfpm.WithDefaults(&nfpm.Info{}), ioutil.Discard)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestFileDoesNotExist(t *testing.T) {
@@ -218,7 +217,7 @@ func TestFileDoesNotExist(t *testing.T) {
 		}),
 		ioutil.Discard,
 	)
-	assert.EqualError(t, err, fmt.Sprintf("matching \"%s\": file does not exist", filepath.ToSlash(abs)))
+	require.EqualError(t, err, fmt.Sprintf("matching \"%s\": file does not exist", filepath.ToSlash(abs)))
 }
 
 func TestNoFiles(t *testing.T) {
@@ -241,7 +240,7 @@ func TestNoFiles(t *testing.T) {
 		}),
 		ioutil.Discard,
 	)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 }
 
 func TestCreateBuilderControl(t *testing.T) {
@@ -253,7 +252,7 @@ func TestCreateBuilderControl(t *testing.T) {
 
 	var w bytes.Buffer
 	tw := tar.NewWriter(&w)
-	assert.NoError(t, builderControl(tw))
+	require.NoError(t, builderControl(tw))
 
 	control := string(extractFromTar(t, w.Bytes(), ".PKGINFO"))
 	golden := "testdata/TestCreateBuilderControl.golden"
@@ -261,8 +260,8 @@ func TestCreateBuilderControl(t *testing.T) {
 		require.NoError(t, ioutil.WriteFile(golden, []byte(control), 0o655)) // nolint: gosec
 	}
 	bts, err := ioutil.ReadFile(golden) //nolint:gosec
-	assert.NoError(t, err)
-	assert.Equal(t, string(bts), control)
+	require.NoError(t, err)
+	require.Equal(t, string(bts), control)
 }
 
 func TestCreateBuilderControlScripts(t *testing.T) {
@@ -273,6 +272,10 @@ func TestCreateBuilderControlScripts(t *testing.T) {
 		PreRemove:   "../testdata/scripts/preremove.sh",
 		PostRemove:  "../testdata/scripts/postremove.sh",
 	}
+	info.APK.Scripts = nfpm.APKScripts{
+		PreUpgrade:  "../testdata/scripts/preupgrade.sh",
+		PostUpgrade: "../testdata/scripts/postupgrade.sh",
+	}
 	err := info.Validate()
 	require.NoError(t, err)
 
@@ -281,7 +284,7 @@ func TestCreateBuilderControlScripts(t *testing.T) {
 
 	var w bytes.Buffer
 	tw := tar.NewWriter(&w)
-	assert.NoError(t, builderControl(tw))
+	require.NoError(t, builderControl(tw))
 
 	control := string(extractFromTar(t, w.Bytes(), ".PKGINFO"))
 	golden := "testdata/TestCreateBuilderControlScripts.golden"
@@ -289,13 +292,27 @@ func TestCreateBuilderControlScripts(t *testing.T) {
 		require.NoError(t, ioutil.WriteFile(golden, []byte(control), 0o655)) // nolint: gosec
 	}
 	bts, err := ioutil.ReadFile(golden) //nolint:gosec
-	assert.NoError(t, err)
-	assert.Equal(t, string(bts), control)
+	require.NoError(t, err)
+	require.Equal(t, string(bts), control)
+
+	// Validate scripts are correct
+	script := string(extractFromTar(t, w.Bytes(), ".pre-install"))
+	require.Contains(t, script, `echo "Preinstall" > /dev/null`)
+	script = string(extractFromTar(t, w.Bytes(), ".post-install"))
+	require.Contains(t, script, `echo "Postinstall" > /dev/null`)
+	script = string(extractFromTar(t, w.Bytes(), ".pre-upgrade"))
+	require.Contains(t, script, `echo "PreUpgrade" > /dev/null`)
+	script = string(extractFromTar(t, w.Bytes(), ".post-upgrade"))
+	require.Contains(t, script, `echo "PostUpgrade" > /dev/null`)
+	script = string(extractFromTar(t, w.Bytes(), ".pre-deinstall"))
+	require.Contains(t, script, `echo "Preremove" > /dev/null`)
+	script = string(extractFromTar(t, w.Bytes(), ".post-deinstall"))
+	require.Contains(t, script, `echo "Postremove" > /dev/null`)
 }
 
 func TestControl(t *testing.T) {
 	var w bytes.Buffer
-	assert.NoError(t, writeControl(&w, controlData{
+	require.NoError(t, writeControl(&w, controlData{
 		Info:          exampleInfo(),
 		InstalledSize: 10,
 	}))
@@ -304,8 +321,8 @@ func TestControl(t *testing.T) {
 		require.NoError(t, ioutil.WriteFile(golden, w.Bytes(), 0o655)) // nolint: gosec
 	}
 	bts, err := ioutil.ReadFile(golden) //nolint:gosec
-	assert.NoError(t, err)
-	assert.Equal(t, string(bts), w.String())
+	require.NoError(t, err)
+	require.Equal(t, string(bts), w.String())
 }
 
 func TestSignature(t *testing.T) {
@@ -450,7 +467,7 @@ func TestAPKConventionalFileName(t *testing.T) {
 			Release:         testCase.Release,
 			Prerelease:      testCase.Prerelease,
 		}
-		assert.Equal(t, testCase.Expect, Default.ConventionalFileName(info))
+		require.Equal(t, testCase.Expect, Default.ConventionalFileName(info))
 	}
 }
 
@@ -463,5 +480,5 @@ func TestPackageSymlinks(t *testing.T) {
 			Type:        "symlink",
 		},
 	}
-	assert.NoError(t, Default.Package(info, ioutil.Discard))
+	require.NoError(t, Default.Package(info, ioutil.Discard))
 }
diff --git a/testdata/acceptance/apk.dockerfile b/testdata/acceptance/apk.dockerfile
index 7f8044a..63a4ad9 100644
--- a/testdata/acceptance/apk.dockerfile
+++ b/testdata/acceptance/apk.dockerfile
@@ -102,3 +102,28 @@ RUN export FOUND_VER="$(cat found)" && \
 # ---- changelog test ----
 FROM min AS withchangelog
 RUN echo "No Changelog support for apk?"
+
+
+# ---- upgrade test ----
+FROM test_base AS upgrade
+ARG oldpackage
+RUN echo "${oldpackage}"
+COPY ${oldpackage} /tmp/old_foo.apk
+RUN apk add --allow-untrusted /tmp/old_foo.apk
+
+RUN test -f /tmp/preinstall-proof
+RUN cat /tmp/preinstall-proof | grep "Install"
+
+RUN test -f /tmp/postinstall-proof
+RUN cat /tmp/postinstall-proof | grep "Install"
+
+RUN test ! -f /tmp/preupgrade-proof
+RUN test ! -f /tmp/postupgrade-proof
+
+RUN echo modified > /etc/regular.conf
+RUN echo modified > /etc/noreplace.conf
+
+RUN apk add --allow-untrusted /tmp/foo.apk
+
+RUN test -f /tmp/preupgrade-proof
+RUN test -f /tmp/postupgrade-proof
diff --git a/testdata/acceptance/core.complex.yaml b/testdata/acceptance/core.complex.yaml
index eef8016..f76405e 100644
--- a/testdata/acceptance/core.complex.yaml
+++ b/testdata/acceptance/core.complex.yaml
@@ -35,3 +35,7 @@ scripts:
   postinstall: ./testdata/acceptance/scripts/postinstall.sh
   preremove: ./testdata/acceptance/scripts/preremove.sh
   postremove: ./testdata/acceptance/scripts/postremove.sh
+apk:
+  scripts:
+    preupgrade: ./testdata/acceptance/scripts/preupgrade.sh
+    postupgrade: ./testdata/acceptance/scripts/postupgrade.sh
\ No newline at end of file
diff --git a/testdata/acceptance/deb.dockerfile b/testdata/acceptance/deb.dockerfile
index 73d7670..7e6d4e4 100644
--- a/testdata/acceptance/deb.dockerfile
+++ b/testdata/acceptance/deb.dockerfile
@@ -144,4 +144,32 @@ RUN dpkg -i /tmp/dummy.deb
 RUN dpkg -i /tmp/foo.deb 2>&1 | grep "foo breaks dummy"
 # make sure foo can be installed if dummy is not installed
 RUN dpkg -r dummy
-RUN dpkg -i /tmp/foo.deb
\ No newline at end of file
+RUN dpkg -i /tmp/foo.deb
+
+
+# ---- upgrade test ----
+FROM test_base AS upgrade
+ARG oldpackage
+RUN echo "${oldpackage}"
+COPY ${oldpackage} /tmp/old_foo.deb
+RUN dpkg -i /tmp/old_foo.deb
+
+RUN test -f /tmp/preinstall-proof
+RUN cat /tmp/preinstall-proof | grep "Install"
+
+RUN test -f /tmp/postinstall-proof
+RUN cat /tmp/postinstall-proof | grep "Install"
+
+RUN dpkg -i /tmp/foo.deb
+
+RUN test -f /tmp/preremove-proof
+RUN cat /tmp/preremove-proof | grep "Upgrade"
+
+RUN test -f /tmp/postremove-proof
+RUN cat /tmp/postremove-proof | grep "Upgrade"
+
+RUN test -f /tmp/preinstall-proof
+RUN cat /tmp/preinstall-proof | grep "Upgrade"
+
+RUN test -f /tmp/postinstall-proof
+RUN cat /tmp/postinstall-proof | grep "Upgrade"
\ No newline at end of file
diff --git a/testdata/acceptance/rpm.config-noreplace-old.yaml b/testdata/acceptance/rpm.config-noreplace-old.yaml
deleted file mode 100644
index 31aaf25..0000000
--- a/testdata/acceptance/rpm.config-noreplace-old.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: "foo"
-arch: "all"
-platform: "linux"
-version: "v1.0.0"
-maintainer: "Foo Bar"
-vendor: "foobar"
-homepage: "https://foobar.org"
-license: "MIT"
-contents:
-- src: ./testdata/whatever.conf
-  dst: /etc/regular.conf
-  type: config
-- src: ./testdata/whatever.conf
-  dst: /etc/noreplace.conf
-  type: config|noreplace
diff --git a/testdata/acceptance/rpm.config-noreplace.yaml b/testdata/acceptance/rpm.config-noreplace.yaml
deleted file mode 100644
index df21c8c..0000000
--- a/testdata/acceptance/rpm.config-noreplace.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: "foo"
-arch: "all"
-platform: "linux"
-version: "v2.0.0"
-maintainer: "Foo Bar"
-vendor: "foobar"
-homepage: "https://foobar.org"
-license: "MIT"
-contents:
-- src: ./testdata/whatever2.conf
-  dst: /etc/regular.conf
-  type: config
-- src: ./testdata/whatever2.conf
-  dst: /etc/noreplace.conf
-  type: config|noreplace
diff --git a/testdata/acceptance/rpm.dockerfile b/testdata/acceptance/rpm.dockerfile
index b26d1bc..102a2ee 100644
--- a/testdata/acceptance/rpm.dockerfile
+++ b/testdata/acceptance/rpm.dockerfile
@@ -147,16 +147,36 @@ RUN rpm -e foo
 RUN test -f /etc/foo/whatever.conf.rpmsave
 RUN test ! -f /usr/local/bin/fake
 
-# ---- config-noreplace test ----
-FROM test_base AS config-noreplace
-COPY tmp/noreplace_old_rpm.rpm /tmp/old_foo.rpm
+# ---- upgrade test ----
+FROM test_base AS upgrade
+ARG oldpackage
+RUN echo "${oldpackage}"
+COPY ${oldpackage} /tmp/old_foo.rpm
 RUN rpm -ivh /tmp/old_foo.rpm
 
+RUN test -f /tmp/preinstall-proof
+RUN cat /tmp/preinstall-proof | grep "Install"
+
+RUN test -f /tmp/postinstall-proof
+RUN cat /tmp/postinstall-proof | grep "Install"
+
 RUN echo modified > /etc/regular.conf
 RUN echo modified > /etc/noreplace.conf
 
 RUN rpm -ivh /tmp/foo.rpm --upgrade
 
+RUN test -f /tmp/preremove-proof
+RUN cat /tmp/preremove-proof | grep "Upgrade"
+
+RUN test -f /tmp/postremove-proof
+RUN cat /tmp/postremove-proof | grep "Upgrade"
+
+RUN test -f /tmp/preinstall-proof
+RUN cat /tmp/preinstall-proof | grep "Upgrade"
+
+RUN test -f /tmp/postinstall-proof
+RUN cat /tmp/postinstall-proof | grep "Upgrade"
+
 RUN cat /etc/regular.conf | grep foo=baz
 RUN test -f /etc/regular.conf.rpmsave
 RUN cat /etc/regular.conf.rpmsave | grep modified
diff --git a/testdata/acceptance/scripts/postinstall.sh b/testdata/acceptance/scripts/postinstall.sh
index 8e9264e..557a8a4 100644
--- a/testdata/acceptance/scripts/postinstall.sh
+++ b/testdata/acceptance/scripts/postinstall.sh
@@ -1,3 +1,33 @@
-#!/bin/bash
+#!/bin/sh
 
-echo "Ok" > /tmp/postinstall-proof
+cleanInstall() {
+    printf "\033[32m Post Install of a clean install\033[0m\n"
+    echo "Install" > /tmp/postinstall-proof
+}
+
+upgrade() {
+    printf "\033[32m Post Install of an upgrade\033[0m\n"
+    echo "Upgrade" > /tmp/postinstall-proof
+}
+
+action="$1"
+if  [ "$1" = "configure" ] && [ -z "$2" ]; then
+  # Alpine linux does not pass args, and deb passes $1=configure
+  action="install"
+elif [ "$1" = "configure" ] && [ -n "$2" ]; then
+    # deb passes $1=configure $2=<current version>
+    action="upgrade"
+fi
+
+case "$action" in
+  "1" | "install")
+    cleanInstall
+    ;;
+  "2" | "upgrade")
+    upgrade
+    ;;
+  *)
+    printf "\033[32m Alpine\033[0m"
+    cleanInstall
+    ;;
+esac
diff --git a/testdata/acceptance/scripts/postremove.sh b/testdata/acceptance/scripts/postremove.sh
index ef8f893..5192634 100644
--- a/testdata/acceptance/scripts/postremove.sh
+++ b/testdata/acceptance/scripts/postremove.sh
@@ -1,3 +1,36 @@
-#!/bin/bash
+#!/bin/sh
 
-echo "Ok" > /tmp/postremove-proof
+remove() {
+    printf "\033[32m Post Remove of a normal remove\033[0m\n"
+    echo "Remove" > /tmp/postremove-proof
+}
+
+purge() {
+    printf "\033[32m Post Remove purge, deb only\033[0m\n"
+    echo "Purge" > /tmp/postremove-proof
+}
+
+upgrade() {
+    printf "\033[32m Post Remove of an upgrade\033[0m\n"
+    echo "Upgrade" > /tmp/postremove-proof
+}
+
+echo "$@"
+
+action="$1"
+
+case "$action" in
+  "0" | "remove")
+    remove
+    ;;
+  "1" | "upgrade")
+    upgrade
+    ;;
+  "purge")
+    purge
+    ;;
+  *)
+    printf "\033[32m Alpine\033[0m"
+    remove
+    ;;
+esac
\ No newline at end of file
diff --git a/testdata/acceptance/scripts/postupgrade.sh b/testdata/acceptance/scripts/postupgrade.sh
new file mode 100644
index 0000000..08de270
--- /dev/null
+++ b/testdata/acceptance/scripts/postupgrade.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo "Ok" > /tmp/postupgrade-proof
diff --git a/testdata/acceptance/scripts/preinstall.sh b/testdata/acceptance/scripts/preinstall.sh
index 37ca45f..5180d83 100644
--- a/testdata/acceptance/scripts/preinstall.sh
+++ b/testdata/acceptance/scripts/preinstall.sh
@@ -1,3 +1,26 @@
-#!/bin/bash
+#!/bin/sh
 
-echo "Ok" > /tmp/preinstall-proof
+cleanInstall() {
+    printf "\033[32m Post Install of a clean install\033[0m\n"
+    echo "Install" > /tmp/preinstall-proof
+}
+
+upgrade() {
+    printf "\033[32m Post Install of an upgrade\033[0m\n"
+    echo "Upgrade" > /tmp/preinstall-proof
+}
+
+action="$1"
+
+case "$action" in
+  "1" | "install")
+    cleanInstall
+    ;;
+  "2" | "upgrade")
+    upgrade
+    ;;
+  *)
+    printf "\033[32m Alpine\033[0m"
+    cleanInstall
+    ;;
+esac
diff --git a/testdata/acceptance/scripts/preremove.sh b/testdata/acceptance/scripts/preremove.sh
index 2fd3b62..d3bba5e 100644
--- a/testdata/acceptance/scripts/preremove.sh
+++ b/testdata/acceptance/scripts/preremove.sh
@@ -1,3 +1,26 @@
-#!/bin/bash
+#!/bin/sh
 
-echo "Ok" > /tmp/preremove-proof
+remove() {
+    printf "\033[32m Pre Remove of a normal remove\033[0m\n"
+    echo "Remove" > /tmp/preremove-proof
+}
+
+upgrade() {
+    printf "\033[32m Pre Remove of an upgrade\033[0m\n"
+    echo "Upgrade" > /tmp/preremove-proof
+}
+
+action="$1"
+
+case "$action" in
+  "0" | "remove")
+    remove
+    ;;
+  "1" | "upgrade")
+    upgrade
+    ;;
+  *)
+    printf "\033[32m Alpine\033[0m"
+    remove
+    ;;
+esac
\ No newline at end of file
diff --git a/testdata/acceptance/scripts/preupgrade.sh b/testdata/acceptance/scripts/preupgrade.sh
new file mode 100644
index 0000000..a0b76f2
--- /dev/null
+++ b/testdata/acceptance/scripts/preupgrade.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo "Ok" > /tmp/preupgrade-proof
diff --git a/testdata/acceptance/upgrade.v1.yaml b/testdata/acceptance/upgrade.v1.yaml
new file mode 100644
index 0000000..8019403
--- /dev/null
+++ b/testdata/acceptance/upgrade.v1.yaml
@@ -0,0 +1,40 @@
+name: "foo"
+arch: "${BUILD_ARCH}"
+platform: "linux"
+version: "v1.2.3"
+maintainer: "Foo Bar"
+depends:
+  - bash
+provides:
+  - fake
+recommends:
+  - fish
+replaces:
+  - foo
+suggests:
+  - zsh
+description: |
+  Foo bar
+    Multiple lines
+vendor: "foobar"
+homepage: "https://foobar.org"
+license: "MIT"
+contents:
+  - src: ./testdata/whatever.conf
+    dst: /etc/regular.conf
+    type: config
+  - src: ./testdata/whatever.conf
+    dst: /etc/noreplace.conf
+    type: config|noreplace
+empty_folders:
+  - /var/log/whatever
+  - /usr/share/foo
+scripts:
+  preinstall: ./testdata/acceptance/scripts/preinstall.sh
+  postinstall: ./testdata/acceptance/scripts/postinstall.sh
+  preremove: ./testdata/acceptance/scripts/preremove.sh
+  postremove: ./testdata/acceptance/scripts/postremove.sh
+apk:
+  scripts:
+    preupgrade: ./testdata/acceptance/scripts/preupgrade.sh
+    postupgrade: ./testdata/acceptance/scripts/postupgrade.sh
\ No newline at end of file
diff --git a/testdata/acceptance/upgrade.v2.yaml b/testdata/acceptance/upgrade.v2.yaml
new file mode 100644
index 0000000..3bd830b
--- /dev/null
+++ b/testdata/acceptance/upgrade.v2.yaml
@@ -0,0 +1,40 @@
+name: "foo"
+arch: "${BUILD_ARCH}"
+platform: "linux"
+version: "v2.0.0"
+maintainer: "Foo Bar"
+depends:
+  - bash
+provides:
+  - fake
+recommends:
+  - fish
+replaces:
+  - foo
+suggests:
+  - zsh
+description: |
+  Foo bar
+    Multiple lines
+vendor: "foobar"
+homepage: "https://foobar.org"
+license: "MIT"
+contents:
+  - src: ./testdata/whatever2.conf
+    dst: /etc/regular.conf
+    type: config
+  - src: ./testdata/whatever2.conf
+    dst: /etc/noreplace.conf
+    type: config|noreplace
+empty_folders:
+  - /var/log/whatever
+  - /usr/share/foo
+scripts:
+  preinstall: ./testdata/acceptance/scripts/preinstall.sh
+  postinstall: ./testdata/acceptance/scripts/postinstall.sh
+  preremove: ./testdata/acceptance/scripts/preremove.sh
+  postremove: ./testdata/acceptance/scripts/postremove.sh
+apk:
+  scripts:
+    preupgrade: ./testdata/acceptance/scripts/preupgrade.sh
+    postupgrade: ./testdata/acceptance/scripts/postupgrade.sh
\ No newline at end of file
diff --git a/testdata/scripts/postupgrade.sh b/testdata/scripts/postupgrade.sh
new file mode 100644
index 0000000..08df33c
--- /dev/null
+++ b/testdata/scripts/postupgrade.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+echo "$@"
+
+echo "PostUpgrade" > /dev/null
diff --git a/testdata/scripts/preupgrade.sh b/testdata/scripts/preupgrade.sh
new file mode 100644
index 0000000..6600803
--- /dev/null
+++ b/testdata/scripts/preupgrade.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+echo "$@"
+
+echo "PreUpgrade" > /dev/null