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

Allow marking projects as "archived" #17005

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

facutuesca
Copy link
Contributor

@facutuesca facutuesca commented Oct 30, 2024

What is this?

Following the discussion here, this PR shows what the implementation could look like for adding "status markers" to projects.

Specifically, this PR allows changing a project's status to "Archived".

Details

The implementation is straightforward, since a Project.lifecycle_status field already exists (to mark a project as quarantined). This change adds a new "archived" value for that field, and adds the UI so that users can archive and unarchive their projects.

Even though the issue mentioned above proposes more statuses ("deprecated", "finished", etc), those can be ambiguous and overlapping, and would require further discussion to define clearly. "Archived", however, is already widely used on GitHub repos and its meaning should be easy for users to understand:

You can archive a repository to make it read-only for all users and indicate that it's no longer actively maintained. You can also unarchive repositories that have been archived

TODO

  • Actually restrict file uploads when project is Archived
  • Add admin UI to archive/unarchive projects

Screenshots

Project settings

image image image

Project landing page

image

cc @woodruffw @miketheman

@facutuesca facutuesca requested a review from a team as a code owner October 30, 2024 18:46
@woodruffw woodruffw added the UX/UI design, user experience, user interface label Nov 19, 2024
@facutuesca facutuesca force-pushed the add-archived-project-status branch from 78ef39e to f22daf0 Compare November 19, 2024 16:40
Copy link
Member

@miketheman miketheman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, looking good, thanks for scoping it down to a single lifecycle status.

I think something good to add to the owner-facing management page would be a suggestion to publish a new release with a readme update or something to point folks where to go next.
Prior ideas have been to allow user-submitted text in the form, but I like that less, since then we have to figure out where to store that.

I think a warning modal popup confirmation might also be in order when archiving would be a good idea to prevent misclicks.

This also doesn't appear to block users from making further releases to an archived project - is that intentionally excluded? In my head, once Archived, it's in read-only mode - similar to GitHub's Archived.

warehouse/templates/manage/project/settings.html Outdated Show resolved Hide resolved
@miketheman
Copy link
Member

I also just realized - this probably needs some added notation in the Admin UI/ability to change there as well. I didn't see anything in scope here yet.

@facutuesca facutuesca force-pushed the add-archived-project-status branch 2 times, most recently from 365bced to 0a59e39 Compare November 25, 2024 22:50
@facutuesca facutuesca force-pushed the add-archived-project-status branch from 0a59e39 to 2c2853b Compare November 25, 2024 22:56
@facutuesca
Copy link
Contributor Author

@miketheman

I think something good to add to the owner-facing management page would be a suggestion to publish a new release with a readme update or something to point folks where to go next.
Prior ideas have been to allow user-submitted text in the form, but I like that less, since then we have to figure out where to store that.

I think a warning modal popup confirmation might also be in order when archiving would be a good idea to prevent misclicks.>

I changed the UI to add the suggestion and the modal popup. Since these are specific to archiving a project, I changed the UI so that instead of a dropdown with the backend statuses, the user sees two buttons to archive or unarchive their project (see the updated screenshots)

This also doesn't appear to block users from making further releases to an archived project - is that intentionally excluded? In my head, once Archived, it's in read-only mode - similar to GitHub's Archived.

I'll add the restrictions once the rest is ready. I was thinking to only disallow uploads for now, WDYT?

this probably needs some added notation in the Admin UI/ability to change there as well. I didn't see anything in scope here yet.

What should the UI for this look?

@facutuesca
Copy link
Contributor Author

Pushed a commit to disallow uploads on archived projects

Signed-off-by: Facundo Tuesca <[email protected]>
Signed-off-by: Facundo Tuesca <[email protected]>
@facutuesca
Copy link
Contributor Author

Added admin UI to archive/unarchive projects:

image

Copy link
Member

@miketheman miketheman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coming along nicely, thanks for pushing forward on this. I had some notes inline, mostly structural in nature, but some questions as well - nothing too "heavy".

Let me know if anything isn't clear!

owner2 = DBRoleFactory.create(project=project)

# Maintainers should not appear in the ACLs, since they only have
# upload permissions, and anchived projects don't allow upload
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo:

Suggested change
# upload permissions, and anchived projects don't allow upload
# upload permissions, and archived projects don't allow upload

Comment on lines +378 to +379
DBRoleFactory.create(project=project, role_name="Maintainer")
DBRoleFactory.create(project=project, role_name="Maintainer")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor optimization, useful if same params and return value not used

Suggested change
DBRoleFactory.create(project=project, role_name="Maintainer")
DBRoleFactory.create(project=project, role_name="Maintainer")
DBRoleFactory.create_batch(2, project=project, role_name="Maintainer")

Comment on lines +195 to +212
if (
project.lifecycle_status is None
or project.lifecycle_status == LifecycleStatus.QuarantineExit
):
project.lifecycle_status = LifecycleStatus.Archived
project.record_event(
tag=EventTag.Project.ProjectArchiveEnter,
request=request,
additional={
"submitted_by": request.user.username,
},
)
request.session.flash("Project archived", queue="success")
else:
request.session.flash(
f"Cannot archive project with status {project.lifecycle_status}",
queue="error",
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider early-exit inversion of the logic, like so (pseudocode):

if not <conditions that must be met>:
    flash "cannot archive", error
    return

do the work
flash "done", success

thus communicate early on the exit clause, continue normally otherwise.
Removes one layer of indentation for "the work", and makes it simpler to create another if/else when necessary with fewer logical jumps to make.

Similar applies to unarchive_project

Comment on lines 333 to +338
for publisher in self.oidc_publishers:
acls.append((Allow, f"oidc:{publisher.id}", [Permissions.ProjectsUpload]))
if self.lifecycle_status != LifecycleStatus.Archived:
# Only allow uploads in non-archived projects
acls.append(
(Allow, f"oidc:{publisher.id}", [Permissions.ProjectsUpload])
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optimize: Adding the if will now happen on each iteration of the loop per publisher, but the project's lifecycle happens once, probably can shortcut the loop instead by placing the if outside.
Most projects only have a single publisher, but just in case, save the iteration if unnecessary.

Comment on lines +387 to +389
current_permissions = [
p for p in current_permissions if p != Permissions.ProjectsUpload
]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optimize: could this be instead?

Suggested change
current_permissions = [
p for p in current_permissions if p != Permissions.ProjectsUpload
]
current_permissions.remove(Permissions.ProjectsUpload)

I think each branch of current_permissions ends up with ProjectsUpload, and this might be faster than a list comprehension?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
HTML requires change to HTML files UX/UI design, user experience, user interface
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants