Repository: GoogleContainerTools/kpt
Pull request #3999 (2023-07-05 21:13:37)

build(deps): bump google.golang.org/grpc from 1.51.0 to 1.53.0 in /porch
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.51.0 to 1.53.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/grpc/grpc-go/releases">google.golang.org/grpc's releases</a>.</em></p>
<blockquote>
<h2>Release 1.53.0</h2>
<h1>API Changes</h1>
<ul>
<li>balancer: support injection of per-call metadata from LB policies (<a href="https://redirect.github.com/grpc/grpc-go/issues/5853">#5853</a>)</li>
<li>resolver: remove deprecated field <code>resolver.Target.Endpoint</code> and replace with <code>resolver.Target.Endpoint()</code> (<a href="https://redirect.github.com/grpc/grpc-go/issues/5852">#5852</a>)
<ul>
<li>Special Thanks: <a href="https://github.com/kylejb"><code>@​kylejb</code></a></li>
</ul>
</li>
</ul>
<h1>New Features</h1>
<ul>
<li>xds/ringhash: introduce <code>GRPC_RING_HASH_CAP</code> environment variable to override the maximum ring size. (<a href="https://redirect.github.com/grpc/grpc-go/issues/5884">#5884</a>)</li>
<li>rls: propagate headers received in RLS response to backends (<a href="https://redirect.github.com/grpc/grpc-go/issues/5883">#5883</a>)</li>
</ul>
<h1>Bug Fixes</h1>
<ul>
<li>transport: drain client transport when streamID approaches MaxStreamID (<a href="https://redirect.github.com/grpc/grpc-go/issues/5889">#5889</a>)</li>
<li>server: after GracefulStop, ensure connections are closed when final RPC completes (<a href="https://redirect.github.com/grpc/grpc-go/issues/5968">#5968</a>)</li>
<li>server: fix a few issues where grpc server uses RST_STREAM for non-HTTP/2 errors (<a href="https://redirect.github.com/grpc/grpc-go/issues/5893">#5893</a>)</li>
<li>xdsclient: fix race which can happen when multiple load reporting calls are made at the same time. (<a href="https://redirect.github.com/grpc/grpc-go/issues/5927">#5927</a>)</li>
<li>rls: fix a data race involving the LRU cache (<a href="https://redirect.github.com/grpc/grpc-go/issues/5925">#5925</a>)</li>
<li>xds: fix panic involving double close of channel in xDS transport (<a href="https://redirect.github.com/grpc/grpc-go/issues/5959">#5959</a>)</li>
<li>gcp/observability: update method name validation (<a href="https://redirect.github.com/grpc/grpc-go/issues/5951">#5951</a>)</li>
</ul>
<h1>Documentation</h1>
<ul>
<li>credentials/oauth: mark <code>NewOauthAccess</code> as deprecated (<a href="https://redirect.github.com/grpc/grpc-go/issues/5882">#5882</a>)
<ul>
<li>Special Thanks: <a href="https://github.com/buzzsurfr"><code>@​buzzsurfr</code></a></li>
</ul>
</li>
</ul>
<h2>Release 1.52.3</h2>
<h1>Bug Fixes</h1>
<ul>
<li>Fix user-agent version</li>
</ul>
<h2>Release 1.52.2</h2>
<h1>Bug Fixes</h1>
<ul>
<li>xds: fix panic involving double close of channel in xDS transport (<a href="https://redirect.github.com/grpc/grpc-go/issues/5959">#5959</a>)</li>
</ul>
<h2>Release 1.52.1</h2>
<h1>Bug Fixes</h1>
<ul>
<li>grpclb: rename grpclbstate package back to state (<a href="https://redirect.github.com/grpc/grpc-go/issues/5963">#5963</a>)</li>
</ul>
<h2>Release 1.52.0</h2>
<h1>New Features</h1>
<ul>
<li>xdsclient: log node ID with verbosity INFO (<a href="https://redirect.github.com/grpc/grpc-go/issues/5860">#5860</a>)</li>
<li>ringhash: impose cap on <code>max_ring_size</code> to reduce possibility of OOMs (<a href="https://redirect.github.com/grpc/grpc-go/issues/5801">#5801</a>)</li>
</ul>
<h1>Behavior Changes</h1>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/grpc/grpc-go/commit/dba26e15a07f43875ccf806a2dd6cbcbc1c12eab"><code>dba26e1</code></a> Change version to 1.53.0 (<a href="https://redirect.github.com/grpc/grpc-go/issues/5983">#5983</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/2a1e9348ff7b5d9f4b5039e84e6c9873b5b3e26e"><code>2a1e934</code></a> server: after GracefulStop, ensure connections are closed when final RPC comp...</li>
<li><a href="https://github.com/grpc/grpc-go/commit/e2d69aa076dd070e3668784c4dc8bcf7131b3f67"><code>e2d69aa</code></a> tests: fix spelling of variable (<a href="https://redirect.github.com/grpc/grpc-go/issues/5966">#5966</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/a6376c9893f56fc3819bee9ef5d71f55cc2d38dd"><code>a6376c9</code></a> xds/resolver: cleanup tests to use real xDS client 3/n (<a href="https://redirect.github.com/grpc/grpc-go/issues/5953">#5953</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/bf8fc46fa6eb913e4ed0f6dee6c6a7b75e85fbf0"><code>bf8fc46</code></a> xds/resolver: cleanup tests to use real xDS client 5/n (<a href="https://redirect.github.com/grpc/grpc-go/issues/5955">#5955</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/3930549b38c0fc4cd94a95efccf7cef5f90515fd"><code>3930549</code></a> resolver: replace resolver.Target.Endpoint field with Endpoint() method (<a href="https://redirect.github.com/grpc/grpc-go/issues/5852">#5852</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/894816c487f8dd48fc971c45a7c5baa4b86ef7de"><code>894816c</code></a> grpclb: rename <code>grpclbstate</code> package back to <code>state</code> (<a href="https://redirect.github.com/grpc/grpc-go/issues/5962">#5962</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/e5a0237a46a5f95fa571624929be10c7afebb180"><code>e5a0237</code></a> encoding: fix duplicate compressor names (<a href="https://redirect.github.com/grpc/grpc-go/issues/5958">#5958</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/4adb2a7a00d8b62df5ea34d520fe3ca13bffd31a"><code>4adb2a7</code></a> xds/resolver: cleanup tests to use real xDS client 2/n (<a href="https://redirect.github.com/grpc/grpc-go/issues/5952">#5952</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/52a8392f374b8cd60e176b67925a7f8c1605d014"><code>52a8392</code></a> gcp/observability: update method name validation (<a href="https://redirect.github.com/grpc/grpc-go/issues/5951">#5951</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/grpc/grpc-go/compare/v1.51.0...v1.53.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/grpc&package-manager=go_modules&previous-version=1.51.0&new-version=1.53.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/GoogleContainerTools/kpt/network/alerts).

</details>


Patch:
diff --git a/commands/alpha/rpkg/pull/command.go b/commands/alpha/rpkg/pull/command.go
index 7515cf6e..e022f80e 100644
--- a/commands/alpha/rpkg/pull/command.go
+++ b/commands/alpha/rpkg/pull/command.go
@@ -22,6 +22,7 @@ import (
 	"sort"
 	"strings"
 
+	"github.com/GoogleContainerTools/kpt/commands/alpha/rpkg/util"
 	"github.com/GoogleContainerTools/kpt/internal/docs/generated/rpkgdocs"
 	"github.com/GoogleContainerTools/kpt/internal/errors"
 	"github.com/GoogleContainerTools/kpt/internal/util/cmdutil"
@@ -112,6 +113,10 @@ func (r *runner) runE(_ *cobra.Command, args []string) error {
 		return errors.E(op, err)
 	}
 
+	if err := util.AddResourceVersionAnnotation(&resources); err != nil {
+		return errors.E(op, err)
+	}
+
 	if len(args) > 1 {
 		if err := writeToDir(resources.Spec.Resources, args[1]); err != nil {
 			return errors.E(op, err)
diff --git a/commands/alpha/rpkg/push/command.go b/commands/alpha/rpkg/push/command.go
index 3e83cf45..e96f7132 100644
--- a/commands/alpha/rpkg/push/command.go
+++ b/commands/alpha/rpkg/push/command.go
@@ -25,6 +25,7 @@ import (
 	"path/filepath"
 	"strings"
 
+	"github.com/GoogleContainerTools/kpt/commands/alpha/rpkg/util"
 	"github.com/GoogleContainerTools/kpt/internal/docs/generated/rpkgdocs"
 	"github.com/GoogleContainerTools/kpt/internal/errors"
 	"github.com/GoogleContainerTools/kpt/internal/fnruntime"
@@ -132,6 +133,16 @@ func (r *runner) runE(cmd *cobra.Command, args []string) error {
 			Resources: resources,
 		},
 	}
+
+	rv, err := util.GetResourceVersionAnnotation(&pkgResources)
+	if err != nil {
+		return errors.E(op, err)
+	}
+	pkgResources.ResourceVersion = rv
+	if err = util.RemoveResourceVersionAnnotation(&pkgResources); err != nil {
+		return errors.E(op, err)
+	}
+
 	if err := r.client.Update(r.ctx, &pkgResources); err != nil {
 		return errors.E(op, err)
 	}
diff --git a/commands/alpha/rpkg/util/common.go b/commands/alpha/rpkg/util/common.go
index e4cd3a61..7dafe979 100644
--- a/commands/alpha/rpkg/util/common.go
+++ b/commands/alpha/rpkg/util/common.go
@@ -1,4 +1,4 @@
-// Copyright 2022 The kpt Authors
+// Copyright 2023 The kpt Authors
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -16,15 +16,21 @@ package util
 
 import (
 	"context"
+	"fmt"
 
-	porchapi "github.com/GoogleContainerTools/kpt/porch/api/porch/v1alpha1"
+	fnsdk "github.com/GoogleContainerTools/kpt-functions-sdk/go/fn"
+	api "github.com/GoogleContainerTools/kpt/porch/api/porch/v1alpha1"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
+const (
+	ResourceVersionAnnotation = "internal.kpt.dev/resource-version"
+)
+
 func PackageAlreadyExists(ctx context.Context, c client.Client, repository, packageName, namespace string) (bool, error) {
 	// only the first package revision can be created from init or clone, so
 	// we need to check that the package doesn't already exist.
-	packageRevisionList := porchapi.PackageRevisionList{}
+	packageRevisionList := api.PackageRevisionList{}
 	if err := c.List(ctx, &packageRevisionList, &client.ListOptions{
 		Namespace: namespace,
 	}); err != nil {
@@ -37,3 +43,67 @@ func PackageAlreadyExists(ctx context.Context, c client.Client, repository, pack
 	}
 	return false, nil
 }
+
+func GetResourceFileKubeObject(prr *api.PackageRevisionResources, file, kind, name string) (*fnsdk.KubeObject, error) {
+	if prr.Spec.Resources == nil {
+		return nil, fmt.Errorf("nil resources found for PackageRevisionResources '%s/%s'", prr.Namespace, prr.Name)
+	}
+
+	if _, ok := prr.Spec.Resources[file]; !ok {
+		return nil, fmt.Errorf("%q not found in PackageRevisionResources '%s/%s'", file, prr.Namespace, prr.Name)
+	}
+
+	ko, err := fnsdk.ParseKubeObject([]byte(prr.Spec.Resources[file]))
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse %q of PackageRevisionResources %s/%s: %w", file, prr.Namespace, prr.Name, err)
+	}
+	if kind != "" && ko.GetKind() != kind {
+		return nil, fmt.Errorf("%q does not contain kind %q in PackageRevisionResources '%s/%s'", file, kind, prr.Namespace, prr.Name)
+	}
+	if name != "" && ko.GetName() != name {
+		return nil, fmt.Errorf("%q does not contain resource named %q in PackageRevisionResources '%s/%s'", file, name, prr.Namespace, prr.Name)
+	}
+
+	return ko, nil
+}
+
+func GetResourceVersionAnnotation(prr *api.PackageRevisionResources) (string, error) {
+	ko, err := GetResourceFileKubeObject(prr, "Kptfile", "Kptfile", "")
+
+	if err != nil {
+		return "", err
+	}
+	annotations := ko.GetAnnotations()
+	rv, ok := annotations[ResourceVersionAnnotation]
+	if !ok {
+		rv = ""
+	}
+	return rv, nil
+}
+
+func AddResourceVersionAnnotation(prr *api.PackageRevisionResources) error {
+	ko, err := GetResourceFileKubeObject(prr, "Kptfile", "Kptfile", "")
+	if err != nil {
+		return err
+	}
+
+	ko.SetAnnotation(ResourceVersionAnnotation, prr.GetResourceVersion())
+	prr.Spec.Resources["Kptfile"] = ko.String()
+
+	return nil
+}
+
+func RemoveResourceVersionAnnotation(prr *api.PackageRevisionResources) error {
+	ko, err := GetResourceFileKubeObject(prr, "Kptfile", "Kptfile", "")
+	if err != nil {
+		return err
+	}
+
+	_, err = ko.RemoveNestedField("metadata", "annotations", ResourceVersionAnnotation)
+	if err != nil {
+		return err
+	}
+	prr.Spec.Resources["Kptfile"] = ko.String()
+
+	return nil
+}
diff --git a/go.mod b/go.mod
index 81565b43..8b66c6bc 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/GoogleContainerTools/kpt
 go 1.20
 
 require (
+	github.com/GoogleContainerTools/kpt-functions-sdk/go/fn v0.0.0-20220506190241-f85503febd54
 	github.com/GoogleContainerTools/kpt/porch/api v0.0.0-20230121152246-dc44dbd18a33
 	github.com/GoogleContainerTools/kpt/rollouts v0.0.0-20230209223911-c6c49d0a0636
 	github.com/bytecodealliance/wasmtime-go v0.39.0
diff --git a/go.sum b/go.sum
index d623e098..e3698c6d 100644
--- a/go.sum
+++ b/go.sum
@@ -23,6 +23,8 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
 github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
 github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/GoogleContainerTools/kpt-functions-sdk/go/fn v0.0.0-20220506190241-f85503febd54 h1:z5iYiugZJiTzQ6ggU0Cae/T+LkrDwcqZyebh8SbmQ0E=
+github.com/GoogleContainerTools/kpt-functions-sdk/go/fn v0.0.0-20220506190241-f85503febd54/go.mod h1:vl3iiwgrqdDgvGi5ckt3O9IoyaHUgFkfxE4RjQIqgwk=
 github.com/GoogleContainerTools/kpt/porch/api v0.0.0-20230121152246-dc44dbd18a33 h1:9M1bvq7hU/JTY4VVcqhCQT0eAa5HznrFaLAm2ldfe70=
 github.com/GoogleContainerTools/kpt/porch/api v0.0.0-20230121152246-dc44dbd18a33/go.mod h1:ASrhnLAL4ahTuiUJyepqcpVRXIoRMJyDs8/eSxwhgZM=
 github.com/GoogleContainerTools/kpt/rollouts v0.0.0-20230209223911-c6c49d0a0636 h1:9oUTINyozQvQ78QvKM3jTLI54oKMrq9V1VYpb4m8Asc=
diff --git a/porch/controllers/packagevariants/pkg/controllers/packagevariant/injection.go b/porch/controllers/packagevariants/pkg/controllers/packagevariant/injection.go
index 4fa4f77c..1a9938f2 100644
--- a/porch/controllers/packagevariants/pkg/controllers/packagevariant/injection.go
+++ b/porch/controllers/packagevariants/pkg/controllers/packagevariant/injection.go
@@ -336,7 +336,7 @@ func setInjectionPointConditionsAndGates(kptfileKubeObject *fn.KubeObject, injec
 	for k := range gateMap {
 		gates = append(gates, kptfilev1.ReadinessGate{ConditionType: k})
 	}
-	sort.Slice(gates, func(i, j int) bool { return gates[i].ConditionType < gates[j].ConditionType })
+	sort.SliceStable(gates, func(i, j int) bool { return gates[i].ConditionType < gates[j].ConditionType })
 
 	if gates != nil {
 		info.ReadinessGates = gates
@@ -348,7 +348,7 @@ func setInjectionPointConditionsAndGates(kptfileKubeObject *fn.KubeObject, injec
 
 	// update the status conditions
 	if conditions != nil {
-		sort.Slice(conditions, func(i, j int) bool { return conditions[i].Type < conditions[j].Type })
+		sort.SliceStable(conditions, func(i, j int) bool { return conditions[i].Type < conditions[j].Type })
 		status.Conditions = convertConditionsFromMetaToKptfile(conditions)
 		err = kptfileKubeObject.SetNestedField(status, "status")
 		if err != nil {
diff --git a/porch/controllers/packagevariants/pkg/controllers/packagevariant/packagevariant_controller.go b/porch/controllers/packagevariants/pkg/controllers/packagevariant/packagevariant_controller.go
index 83c02744..3798afa5 100644
--- a/porch/controllers/packagevariants/pkg/controllers/packagevariant/packagevariant_controller.go
+++ b/porch/controllers/packagevariants/pkg/controllers/packagevariant/packagevariant_controller.go
@@ -16,8 +16,6 @@ package packagevariant
 
 import (
 	"context"
-	"crypto/sha1"
-	"encoding/hex"
 	"flag"
 	"fmt"
 	"strconv"
@@ -29,6 +27,7 @@ import (
 
 	"github.com/GoogleContainerTools/kpt-functions-sdk/go/fn"
 	kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1"
+	"github.com/GoogleContainerTools/kpt/pkg/kptfile/kptfileutil"
 
 	"golang.org/x/mod/semver"
 	"k8s.io/apimachinery/pkg/api/meta"
@@ -42,7 +41,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/handler"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 	"sigs.k8s.io/controller-runtime/pkg/source"
-	"sigs.k8s.io/kustomize/kyaml/yaml"
 )
 
 type Options struct{}
@@ -370,10 +368,13 @@ func (r *PackageVariantReconciler) findAndUpdateExistingRevisions(ctx context.Co
 			// we need to copy a published package to a new draft before updating
 			if porchapi.LifecycleIsPublished(downstream.Spec.Lifecycle) {
 				klog.Infoln(fmt.Sprintf("package variant %q needs to update package revision %q for new upstream revision, creating new draft", pv.Name, downstream.Name))
+				oldDS := downstream
 				downstream, err = r.copyPublished(ctx, downstream, pv, prList)
 				if err != nil {
+					klog.Errorf("package variant %q failed to copy %q: %s", pv.Name, oldDS.Name, err.Error())
 					return nil, err
 				}
+				klog.Infoln(fmt.Sprintf("package variant %q created %q based on %q", pv.Name, downstream.Name, oldDS.Name))
 			}
 			downstreams[i], err = r.updateDraft(ctx, downstream, upstream)
 			if err != nil {
@@ -394,16 +395,20 @@ func (r *PackageVariantReconciler) findAndUpdateExistingRevisions(ctx context.Co
 			// so, clone to a new Draft if that's the case
 			if porchapi.LifecycleIsPublished(downstream.Spec.Lifecycle) {
 				klog.Infoln(fmt.Sprintf("package variant %q needs to mutate to package revision %q, creating new draft", pv.Name, downstream.Name))
+				oldDS := downstream
 				downstream, err = r.copyPublished(ctx, downstream, pv, prList)
 				if err != nil {
+					klog.Errorf("package variant %q failed to copy %q: %s", pv.Name, oldDS.Name, err.Error())
 					return nil, err
 				}
+				klog.Infoln(fmt.Sprintf("package variant %q created %q based on %q", pv.Name, downstream.Name, oldDS.Name))
 				downstreams[i] = downstream
-			}
-			// recalculate from the new Draft
-			prr, _, err = r.calculateDraftResources(ctx, pv, downstreams[i])
-			if err != nil {
-				return nil, err
+				// recalculate from the new Draft
+				prr, _, err = r.calculateDraftResources(ctx, pv, downstreams[i])
+				if err != nil {
+					return nil, err
+				}
+
 			}
 			// Save the updated PackageRevisionResources
 			if err := r.Update(ctx, prr); err != nil {
@@ -411,7 +416,6 @@ func (r *PackageVariantReconciler) findAndUpdateExistingRevisions(ctx context.Co
 			}
 			klog.Infoln(fmt.Sprintf("package variant %q updated package revision %q for new mutations", pv.Name, downstream.Name))
 		}
-
 	}
 	return downstreams, nil
 }
@@ -763,10 +767,9 @@ func (r *PackageVariantReconciler) calculateDraftResources(ctx context.Context,
 		return nil, false, fmt.Errorf("nil resources found for PackageRevisionResources '%s/%s'", prr.Namespace, prr.Name)
 	}
 
-	// Calculate a hash of the resources
-	beforeHash, err := hashFromPackageRevisionResources(&prr)
-	if err != nil {
-		return nil, false, err
+	origResources := make(map[string]string, len(prr.Spec.Resources))
+	for k, v := range prr.Spec.Resources {
+		origResources[k] = v
 	}
 
 	// Apply our mutations
@@ -782,12 +785,73 @@ func (r *PackageVariantReconciler) calculateDraftResources(ctx context.Context,
 		return nil, false, err
 	}
 
-	afterHash, err := hashFromPackageRevisionResources(&prr)
+	if len(prr.Spec.Resources) != len(origResources) {
+		// files were added or deleted
+		klog.Infoln(fmt.Sprintf("PackageVariant %q, PackageRevision %q, resources changed: %d original files, %d new files", pv.Name, prr.Name, len(origResources), len(prr.Spec.Resources)))
+		return &prr, true, nil
+	}
+
+	for k, v := range origResources {
+		newValue, ok := prr.Spec.Resources[k]
+		if !ok {
+			// a file was deleted
+			klog.Infoln(fmt.Sprintf("PackageVariant %q, PackageRevision %q, resources changed: %q in original files, not in new files", pv.Name, prr.Name, k))
+			return &prr, true, nil
+		}
+
+		if newValue != v {
+			// HACK ALERT - TODO(jbelamaric): Fix this
+			// Currently nephio controllers and package variant controller are rendering Kptfiles slightly differently in YAML
+			// not sure why, need to investigate more. It may be due to different versions of kyaml. So, here, just for Kptfiles,
+			// we will parse and compare semantically.
+			//
+			if k == "Kptfile" && kptfilesEqual(v, newValue) {
+				klog.Infoln(fmt.Sprintf("PackageVariant %q, PackageRevision %q, resources changed: Kptfiles differ, but not semantically", pv.Name, prr.Name))
+				continue
+			}
+
+			// a file was changed
+			klog.Infoln(fmt.Sprintf("PackageVariant %q, PackageRevision %q, resources changed: %q different", pv.Name, prr.Name, k))
+			return &prr, true, nil
+		}
+	}
+
+	// all files in orig are in new, no new files, and all contents match
+	// so no change
+	klog.Infoln(fmt.Sprintf("PackageVariant %q, PackageRevision %q, resources unchanged", pv.Name, prr.Name))
+	return &prr, false, nil
+}
+
+func parseKptfile(kf string) (*kptfilev1.KptFile, error) {
+	ko, err := fn.ParseKubeObject([]byte(kf))
 	if err != nil {
-		return nil, false, err
+		return nil, err
+	}
+	var kptfile kptfilev1.KptFile
+	err = ko.As(&kptfile)
+	if err != nil {
+		return nil, err
 	}
 
-	return &prr, beforeHash != afterHash, nil
+	return &kptfile, nil
+}
+
+func kptfilesEqual(a, b string) bool {
+	akf, err := parseKptfile(a)
+	if err != nil {
+		return false
+	}
+
+	bkf, err := parseKptfile(b)
+	if err != nil {
+		return false
+	}
+
+	equal, err := kptfileutil.Equal(akf, bkf)
+	if err != nil {
+		return false
+	}
+	return equal
 }
 
 func ensurePackageContext(pv *api.PackageVariant,
@@ -980,12 +1044,3 @@ func isPackageVariantFunc(fn *fn.SubObject, pvName string) (bool, error) {
 func generatePVFuncName(funcName, pvName string, pos int) string {
 	return fmt.Sprintf("%s.%s.%s.%d", PackageVariantFuncPrefix, pvName, funcName, pos)
 }
-
-func hashFromPackageRevisionResources(prr *porchapi.PackageRevisionResources) (string, error) {
-	b, err := yaml.Marshal(prr.Spec.Resources)
-	if err != nil {
-		return "", err
-	}
-	hash := sha1.Sum(b)
-	return hex.EncodeToString(hash[:]), nil
-}
diff --git a/porch/go.mod b/porch/go.mod
index 82d58e48..7bbc81c5 100644
--- a/porch/go.mod
+++ b/porch/go.mod
@@ -40,7 +40,7 @@ require (
 	golang.org/x/tools v0.7.0
 	google.golang.org/api v0.108.0
 	google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2
-	google.golang.org/grpc v1.51.0
+	google.golang.org/grpc v1.53.0
 	google.golang.org/protobuf v1.29.1
 	gopkg.in/yaml.v2 v2.4.0
 	k8s.io/api v0.25.3
@@ -72,7 +72,7 @@ require (
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/blang/semver/v4 v4.0.0 // indirect
 	github.com/bytecodealliance/wasmtime-go v0.39.0 // indirect
-	github.com/cespare/xxhash/v2 v2.1.2 // indirect
+	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 	github.com/chai2010/gettext-go v1.0.2 // indirect
 	github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
 	github.com/coreos/go-semver v0.3.0 // indirect
diff --git a/porch/go.sum b/porch/go.sum
index 785cf547..ee0087d3 100644
--- a/porch/go.sum
+++ b/porch/go.sum
@@ -126,8 +126,9 @@ github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6
 github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
 github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
+github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
 github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -1125,8 +1126,8 @@ google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
 google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
 google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
 google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
-google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
+google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
+google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
diff --git a/porch/pkg/engine/engine.go b/porch/pkg/engine/engine.go
index ec3699dc..b1a8944d 100644
--- a/porch/pkg/engine/engine.go
+++ b/porch/pkg/engine/engine.go
@@ -53,6 +53,10 @@ import (
 
 var tracer = otel.Tracer("engine")
 
+const (
+	OptimisticLockErrorMsg = "the object has been modified; please apply your changes to the latest version and try again"
+)
+
 type CaDEngine interface {
 	// ObjectCache() is a cache of all our objects.
 	ObjectCache() WatcherManager
@@ -626,6 +630,15 @@ func (cad *cadEngine) UpdatePackageRevision(ctx context.Context, repositoryObj *
 	ctx, span := tracer.Start(ctx, "cadEngine::UpdatePackageRevision", trace.WithAttributes())
 	defer span.End()
 
+	newRV := newObj.GetResourceVersion()
+	if len(newRV) == 0 {
+		return nil, fmt.Errorf("resourceVersion must be specified for an update")
+	}
+
+	if newRV != oldObj.GetResourceVersion() {
+		return nil, apierrors.NewConflict(api.Resource("packagerevisions"), oldObj.GetName(), fmt.Errorf(OptimisticLockErrorMsg))
+	}
+
 	repo, err := cad.cache.OpenRepository(ctx, repositoryObj)
 	if err != nil {
 		return nil, err
@@ -1049,6 +1062,15 @@ func (cad *cadEngine) UpdatePackageResources(ctx context.Context, repositoryObj
 		return nil, nil, err
 	}
 
+	newRV := new.GetResourceVersion()
+	if len(newRV) == 0 {
+		return nil, nil, fmt.Errorf("resourceVersion must be specified for an update")
+	}
+
+	if newRV != old.GetResourceVersion() {
+		return nil, nil, apierrors.NewConflict(api.Resource("packagerevisionresources"), old.GetName(), fmt.Errorf(OptimisticLockErrorMsg))
+	}
+
 	// Validate package lifecycle. Can only update a draft.
 	switch lifecycle := rev.Spec.Lifecycle; lifecycle {
 	default:


Test patch:
diff --git a/commands/alpha/rpkg/pull/command_test.go b/commands/alpha/rpkg/pull/command_test.go
index eca7212b..2adc9b0b 100644
--- a/commands/alpha/rpkg/pull/command_test.go
+++ b/commands/alpha/rpkg/pull/command_test.go
@@ -74,6 +74,7 @@ items:
     name: bar
     annotations:
       config.kubernetes.io/local-config: "true"
+      internal.kpt.dev/resource-version: "999"
       config.kubernetes.io/index: '0'
       internal.config.kubernetes.io/index: '0'
       internal.config.kubernetes.io/path: 'Kptfile'
@@ -126,6 +127,7 @@ items:
     name: bar
     annotations:
       config.kubernetes.io/local-config: "true"
+      internal.kpt.dev/resource-version: "999"
       config.kubernetes.io/index: '0'
       internal.config.kubernetes.io/index: '0'
       internal.config.kubernetes.io/path: 'Kptfile'
diff --git a/e2e/porch_test.go b/e2e/porch_test.go
index 3d18b77d..2f9b277e 100644
--- a/e2e/porch_test.go
+++ b/e2e/porch_test.go
@@ -17,6 +17,7 @@
 package e2e
 
 import (
+	"bufio"
 	"bytes"
 	"errors"
 	"io/fs"
@@ -45,10 +46,26 @@ func TestPorch(t *testing.T) {
 	runTests(t, abs)
 }
 
+func runUtilityCommand(t *testing.T, command string, args ...string) error {
+	cmd := exec.Command(command, args...)
+	t.Logf("running utility command %s %s", command, strings.Join(args, " "))
+	return cmd.Run()
+}
+
 func runTests(t *testing.T, path string) {
 	gitServerURL := startGitServer(t, path)
 	testCases := scanTestCases(t, path)
 
+	// remove any tmp files from previous test runs
+	err := runUtilityCommand(t, "rm", "-rf", "/tmp/porch-e2e")
+	if err != nil {
+		t.Fatalf("Failed to clean up older run: %v", err)
+	}
+	err = runUtilityCommand(t, "mkdir", "/tmp/porch-e2e")
+	if err != nil {
+		t.Fatalf("Failed to create temp directory: %v", err)
+	}
+
 	for _, tc := range testCases {
 		t.Run(tc.TestCase, func(t *testing.T) {
 			if tc.Skip != "" {
@@ -71,7 +88,7 @@ func runTestCase(t *testing.T, repoURL string, tc porch.TestCaseConfig) {
 	}
 
 	for i := range tc.Commands {
-		time.Sleep(5 * time.Second)
+		time.Sleep(1 * time.Second)
 		command := &tc.Commands[i]
 		cmd := exec.Command("kpt", command.Args...)
 
@@ -89,6 +106,8 @@ func runTestCase(t *testing.T, repoURL string, tc porch.TestCaseConfig) {
 			reorderYamlStdout(t, &stdout)
 		}
 
+		cleanupStderr(t, &stderr)
+
 		if os.Getenv(updateGoldenFiles) != "" {
 			updateCommand(command, err, stdout.String(), stderr.String())
 		}
@@ -102,6 +121,13 @@ func runTestCase(t *testing.T, repoURL string, tc porch.TestCaseConfig) {
 		if got, want := stderr.String(), command.Stderr; got != want {
 			t.Errorf("unexpected stderr content from 'kpt %s'; (-want, +got) %s", strings.Join(command.Args, " "), cmp.Diff(want, got))
 		}
+
+		// hack here; but if the command registered a repo, give a few extra seconds for the repo to reach readiness
+		for _, arg := range command.Args {
+			if arg == "register" {
+				time.Sleep(5 * time.Second)
+			}
+		}
 	}
 
 	if os.Getenv(updateGoldenFiles) != "" {
@@ -109,9 +135,43 @@ func runTestCase(t *testing.T, repoURL string, tc porch.TestCaseConfig) {
 	}
 }
 
+// remove PASS lines from kpt fn eval, which includes a duration and will vary
+func cleanupStderr(t *testing.T, buf *bytes.Buffer) {
+	scanner := bufio.NewScanner(buf)
+	var newBuf bytes.Buffer
+	for scanner.Scan() {
+		line := scanner.Text()
+		if !strings.Contains(line, "[PASS]") {
+			newBuf.Write([]byte(line))
+			newBuf.Write([]byte("\n"))
+		}
+	}
+
+	buf.Reset()
+	if _, err := buf.Write(newBuf.Bytes()); err != nil {
+		t.Fatalf("Failed to update cleaned up stderr: %v", err)
+	}
+}
+
 func reorderYamlStdout(t *testing.T, buf *bytes.Buffer) {
+	if buf.Len() == 0 {
+		return
+	}
+
+	// strip out the internal.kpt.dev/resource-version
+	// annotation, because that will change with every run
+	scanner := bufio.NewScanner(buf)
+	var newBuf bytes.Buffer
+	for scanner.Scan() {
+		line := scanner.Text()
+		if !strings.Contains(line, "internal.kpt.dev/resource-version:") {
+			newBuf.Write([]byte(line))
+			newBuf.Write([]byte("\n"))
+		}
+	}
+
 	var data interface{}
-	if err := yaml.Unmarshal(buf.Bytes(), &data); err != nil {
+	if err := yaml.Unmarshal(newBuf.Bytes(), &data); err != nil {
 		// not yaml.
 		return
 	}
diff --git a/e2e/testdata/porch/rpkg-push/config.yaml b/e2e/testdata/porch/rpkg-push/config.yaml
index e9ba0c44..378ab6e7 100644
--- a/e2e/testdata/porch/rpkg-push/config.yaml
+++ b/e2e/testdata/porch/rpkg-push/config.yaml
@@ -58,30 +58,19 @@ commands:
       - git-efe3d01c68dfdcdd69114c9a7c65cce0d662a46f
     stdin: |
       apiVersion: config.kubernetes.io/v1
-      kind: ResourceList
       items:
       - apiVersion: kpt.dev/v1
+        info:
+          description: sample description
         kind: Kptfile
         metadata:
-          name: test-package
           annotations:
-            config.kubernetes.io/index: '0'
+            config.kubernetes.io/index: "0"
             config.kubernetes.io/local-config: "true"
             config.kubernetes.io/path: Kptfile
-            internal.config.kubernetes.io/index: '0'
-            internal.config.kubernetes.io/path: 'Kptfile'
-        info:
-          site: http://kpt.dev/test-package
-          description: Updated Test Package Description
-      - apiVersion: v1
-        kind: ConfigMap
-        metadata:
-          name: new-config-map
-          annotations:
-            internal.config.kubernetes.io/path: 'config-map.yaml'
-            config.kubernetes.io/path: 'config-map.yaml'
-        data:
-          key: value
+            internal.config.kubernetes.io/index: "0"
+            internal.config.kubernetes.io/path: Kptfile
+          name: test-package
       - apiVersion: v1
         data:
           name: example
@@ -94,6 +83,36 @@ commands:
             internal.config.kubernetes.io/index: "0"
             internal.config.kubernetes.io/path: package-context.yaml
           name: kptfile.kpt.dev
+      kind: ResourceList
+    yaml: true
+    exitCode: 1
+    stderr: "Error: Internal error occurred: resourceVersion must be specified for an update \n"
+  - args:
+      - alpha
+      - rpkg
+      - pull
+      - --namespace=rpkg-push
+      - git-efe3d01c68dfdcdd69114c9a7c65cce0d662a46f
+      - /tmp/porch-e2e/rpkg-push-git-efe3d01c68dfdcdd69114c9a7c65cce0d662a46f
+  - args:
+      - fn
+      - eval
+      - --image
+      - gcr.io/kpt-fn/search-replace:v0.2.0
+      - --match-kind
+      - Kptfile
+      - /tmp/porch-e2e/rpkg-push-git-efe3d01c68dfdcdd69114c9a7c65cce0d662a46f
+      - --
+      - by-path=info.description
+      - put-value=Updated Test Package Description
+    stderr: "[RUNNING] \"gcr.io/kpt-fn/search-replace:v0.2.0\" on 1 resource(s)\n  Results:\n    [info] info.description: Mutated field value to \"Updated Test Package Description\"\n"
+  - args:
+      - alpha
+      - rpkg
+      - push
+      - --namespace=rpkg-push
+      - git-efe3d01c68dfdcdd69114c9a7c65cce0d662a46f
+      - /tmp/porch-e2e/rpkg-push-git-efe3d01c68dfdcdd69114c9a7c65cce0d662a46f
   - args:
       - alpha
       - rpkg
@@ -106,7 +125,6 @@ commands:
       - apiVersion: kpt.dev/v1
         info:
           description: Updated Test Package Description
-          site: http://kpt.dev/test-package
         kind: Kptfile
         metadata:
           annotations:
@@ -116,17 +134,6 @@ commands:
             internal.config.kubernetes.io/index: "0"
             internal.config.kubernetes.io/path: Kptfile
           name: test-package
-      - apiVersion: v1
-        data:
-          key: value
-        kind: ConfigMap
-        metadata:
-          annotations:
-            config.kubernetes.io/index: "0"
-            config.kubernetes.io/path: config-map.yaml
-            internal.config.kubernetes.io/index: "0"
-            internal.config.kubernetes.io/path: config-map.yaml
-          name: new-config-map
       - apiVersion: v1
         data:
           name: example
diff --git a/e2e/testdata/porch/rpkg-update/config.yaml b/e2e/testdata/porch/rpkg-update/config.yaml
index 1a7139a0..e813f1b7 100644
--- a/e2e/testdata/porch/rpkg-update/config.yaml
+++ b/e2e/testdata/porch/rpkg-update/config.yaml
@@ -111,51 +111,32 @@ commands:
       git-3f036055f7ba68706372cbe0c4b14d553794f7c4                         No update available
       git-7fcdd499f0790ac3bd8f805e3e5e00825641eb60                         No update available
       git-7ab0219ace10c1081a8b40a6b97d5da58bdb62e0   git                   No update available
+  - args:
+      - alpha
+      - rpkg
+      - pull
+      - --namespace=rpkg-update
+      - git-7ab0219ace10c1081a8b40a6b97d5da58bdb62e0
+      - /tmp/porch-e2e/pkg-update-git-7ab0219ace10c1081a8b40a6b97d5da58bdb62e0
+  - args:
+      - fn
+      - eval
+      - --image
+      - gcr.io/kpt-fn/search-replace:v0.2.0
+      - --match-kind
+      - Kptfile
+      - /tmp/porch-e2e/pkg-update-git-7ab0219ace10c1081a8b40a6b97d5da58bdb62e0
+      - --
+      - by-path=upstreamLock.git.ref
+      - put-value=invalid
+    stderr: "[RUNNING] \"gcr.io/kpt-fn/search-replace:v0.2.0\" on 1 resource(s)\n  Results:\n    [info] upstreamLock.git.ref: Mutated field value to \"invalid\"\n"
   - args:
       - alpha
       - rpkg
       - push
       - --namespace=rpkg-update
       - git-7ab0219ace10c1081a8b40a6b97d5da58bdb62e0
-    stdin: |
-      apiVersion: config.kubernetes.io/v1
-      kind: ResourceList
-      items:
-      - apiVersion: kpt.dev/v1
-        kind: Kptfile
-        metadata:
-          name: basens-edit-clone
-          annotations:
-            config.kubernetes.io/index: '0'
-            config.kubernetes.io/local-config: "true"
-            config.kubernetes.io/path: Kptfile
-            internal.config.kubernetes.io/index: '0'
-            internal.config.kubernetes.io/path: 'Kptfile'
-        upstream:
-          type: git
-          git:
-            repo: http://git-server.test-git-namespace.svc.cluster.local:8080/rpkg-update
-            directory: base-ns
-            ref: basens/v1
-        upstreamLock:
-          type: git
-          git:
-            repo: http://git-server.test-git-namespace.svc.cluster.local:8080/rpkg-update
-            directory: base-ns
-            ref: invalid
-            commit: 8f3ee9e0d5724fbd0fbdcf81bef0d47d6d29cc64
-      - apiVersion: v1
-        data:
-          name: example
-        kind: ConfigMap
-        metadata:
-          annotations:
-            config.kubernetes.io/index: "0"
-            config.kubernetes.io/local-config: "true"
-            config.kubernetes.io/path: package-context.yaml
-            internal.config.kubernetes.io/index: "0"
-            internal.config.kubernetes.io/path: package-context.yaml
-          name: kptfile.kpt.dev
+      - /tmp/porch-e2e/pkg-update-git-7ab0219ace10c1081a8b40a6b97d5da58bdb62e0
   - args:
       - alpha
       - rpkg
@@ -163,4 +144,4 @@ commands:
       - --namespace=rpkg-update
       - --discover=upstream
     stderr: "Error: could not parse upstreamLock in Kptfile of package \"git-7ab0219ace10c1081a8b40a6b97d5da58bdb62e0\": malformed upstreamLock.Git.Ref \"invalid\" \n"
-    exitCode: 1
\ No newline at end of file
+    exitCode: 1
diff --git a/porch/test/e2e/e2e_test.go b/porch/test/e2e/e2e_test.go
index 0c0fec48..d3c55220 100644
--- a/porch/test/e2e/e2e_test.go
+++ b/porch/test/e2e/e2e_test.go
@@ -1835,6 +1835,13 @@ func (t *PorchSuite) TestNewPackageRevisionLabels(ctx context.Context) {
 	// Propose the package.
 	pr.Spec.Lifecycle = porchapi.PackageRevisionLifecycleProposed
 	t.UpdateF(ctx, &pr)
+
+	// retrieve the updated object
+	t.GetF(ctx, client.ObjectKey{
+		Namespace: pr.Namespace,
+		Name:      pr.Name,
+	}, &pr)
+
 	t.validateLabelsAndAnnos(ctx, pr.Name,
 		map[string]string{
 			labelKey1: labelVal1,
@@ -1859,6 +1866,12 @@ func (t *PorchSuite) TestNewPackageRevisionLabels(ctx context.Context) {
 		},
 	)
 
+	// retrieve the updated object
+	t.GetF(ctx, client.ObjectKey{
+		Namespace: pr.Namespace,
+		Name:      pr.Name,
+	}, &pr)
+
 	// Update the labels and annotations on the approved package.
 	delete(pr.ObjectMeta.Labels, labelKey1)
 	pr.ObjectMeta.Labels[labelKey2] = labelVal2
@@ -2508,7 +2521,7 @@ func (t *PorchSuite) waitUntilRepositoryDeleted(ctx context.Context, name, names
 }
 
 func (t *PorchSuite) waitUntilAllPackagesDeleted(ctx context.Context, repoName string) {
-	err := wait.PollImmediateWithContext(ctx, time.Second, 20*time.Second, func(ctx context.Context) (done bool, err error) {
+	err := wait.PollImmediateWithContext(ctx, time.Second, 60*time.Second, func(ctx context.Context) (done bool, err error) {
 		var pkgRevList porchapi.PackageRevisionList
 		if err := t.client.List(ctx, &pkgRevList); err != nil {
 			t.Logf("error listing packages: %v", err)