Merge pull request 'Delete packages, arch-repos & repos' (#265) from Chewing_Bever/vieter:repo-delete-routes into dev
	
		
			
	
		
	
	
		
			
				
	
				ci/woodpecker/push/lint Pipeline was successful
				
					Details
				
			
		
			
				
	
				ci/woodpecker/push/docs Pipeline was successful
				
					Details
				
			
		
			
				
	
				ci/woodpecker/push/arch Pipeline was successful
				
					Details
				
			
		
			
				
	
				ci/woodpecker/push/build Pipeline was successful
				
					Details
				
			
		
			
				
	
				ci/woodpecker/push/man Pipeline was successful
				
					Details
				
			
		
			
				
	
				ci/woodpecker/push/test Pipeline was successful
				
					Details
				
			
		
			
				
	
				ci/woodpecker/push/docker Pipeline was successful
				
					Details
				
			
		
			
				
	
				ci/woodpecker/push/deploy Pipeline was successful
				
					Details
				
			
		
	
				
					
				
			
				
	
				ci/woodpecker/push/lint Pipeline was successful
				
					Details
				
			
		
			
				
	
				ci/woodpecker/push/docs Pipeline was successful
				
					Details
				
			
		
			
				
	
				ci/woodpecker/push/arch Pipeline was successful
				
					Details
				
			
		
			
				
	
				ci/woodpecker/push/build Pipeline was successful
				
					Details
				
			
		
			
				
	
				ci/woodpecker/push/man Pipeline was successful
				
					Details
				
			
		
			
				
	
				ci/woodpecker/push/test Pipeline was successful
				
					Details
				
			
		
			
				
	
				ci/woodpecker/push/docker Pipeline was successful
				
					Details
				
			
		
			
				
	
				ci/woodpecker/push/deploy Pipeline was successful
				
					Details
				
			
		
	Reviewed-on: #265remotes/1749439125376657365/tmp_refs/heads/v-0.3.1
						commit
						78b0918df7
					
				|  | @ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | ||||||
| * Targets with kind 'url' can provide a direct URL to a PKGBUILD instead of | * Targets with kind 'url' can provide a direct URL to a PKGBUILD instead of | ||||||
|   providing a Git repository |   providing a Git repository | ||||||
| * CLI commands for searching the AUR & directly adding packages | * CLI commands for searching the AUR & directly adding packages | ||||||
|  | * HTTP routes for removing packages, arch-repos & repos | ||||||
| 
 | 
 | ||||||
| ### Changed | ### Changed | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -93,3 +93,87 @@ other already present arch-repos. | ||||||
| Parameter | Description | Parameter | Description | ||||||
| --------- | ----------- | --------- | ----------- | ||||||
| repo | Repository to publish package to | repo | Repository to publish package to | ||||||
|  | 
 | ||||||
|  | ## Remove package from arch-repo | ||||||
|  | 
 | ||||||
|  | <aside class="notice"> | ||||||
|  | 
 | ||||||
|  | This endpoint requests authentication. | ||||||
|  | 
 | ||||||
|  | </aside> | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | curl \ | ||||||
|  |   -H 'X-Api-Key: secret' \ | ||||||
|  |   -XDELETE \ | ||||||
|  |   https://example.com/vieter/x86_64/mike | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | This endpoint allows you to remove a package from a given arch-repo. | ||||||
|  | 
 | ||||||
|  | ### HTTP Request | ||||||
|  | 
 | ||||||
|  | `DELETE /:repo/:arch/:pkg` | ||||||
|  | 
 | ||||||
|  | ### URL Parameters | ||||||
|  | 
 | ||||||
|  | Parameter | Description | ||||||
|  | --------- | ----------- | ||||||
|  | repo | Repository to delete package from | ||||||
|  | arch | Specific arch-repo to remove package from | ||||||
|  | pkg | Name of package to remove (without any version information) | ||||||
|  | 
 | ||||||
|  | ## Remove arch-repo | ||||||
|  | 
 | ||||||
|  | <aside class="notice"> | ||||||
|  | 
 | ||||||
|  | This endpoint requests authentication. | ||||||
|  | 
 | ||||||
|  | </aside> | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | curl \ | ||||||
|  |   -H 'X-Api-Key: secret' \ | ||||||
|  |   -XDELETE \ | ||||||
|  |   https://example.com/vieter/x86_64 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | This endpoint allows removing an entire arch-repo. | ||||||
|  | 
 | ||||||
|  | ### HTTP Request | ||||||
|  | 
 | ||||||
|  | `DELETE /:repo/:arch` | ||||||
|  | 
 | ||||||
|  | ### URL Parameters | ||||||
|  | 
 | ||||||
|  | Parameter | Description | ||||||
|  | --------- | ----------- | ||||||
|  | repo | Repository to delete arch-repo from | ||||||
|  | arch | Specific architecture to remove | ||||||
|  | 
 | ||||||
|  | ## Remove repo | ||||||
|  | 
 | ||||||
|  | <aside class="notice"> | ||||||
|  | 
 | ||||||
|  | This endpoint requests authentication. | ||||||
|  | 
 | ||||||
|  | </aside> | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | curl \ | ||||||
|  |   -H 'X-Api-Key: secret' \ | ||||||
|  |   -XDELETE \ | ||||||
|  |   https://example.com/vieter | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | This endpoint allows removing an entire repo. | ||||||
|  | 
 | ||||||
|  | ### HTTP Request | ||||||
|  | 
 | ||||||
|  | `DELETE /:repo` | ||||||
|  | 
 | ||||||
|  | ### URL Parameters | ||||||
|  | 
 | ||||||
|  | Parameter | Description | ||||||
|  | --------- | ----------- | ||||||
|  | repo | Repository to delete | ||||||
|  |  | ||||||
|  | @ -154,51 +154,3 @@ fn (r &RepoGroupManager) add_pkg_in_arch_repo(repo string, arch string, pkg &pac | ||||||
| 
 | 
 | ||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
| 
 |  | ||||||
| // remove_pkg_from_arch_repo removes a package from an arch-repo's database. It |  | ||||||
| // returns false if the package wasn't present in the database. It also |  | ||||||
| // optionally re-syncs the repo archives. |  | ||||||
| fn (r &RepoGroupManager) remove_pkg_from_arch_repo(repo string, arch string, pkg_name string, sync bool) ?bool { |  | ||||||
| 	repo_dir := os.join_path(r.repos_dir, repo, arch) |  | ||||||
| 
 |  | ||||||
| 	// If the repository doesn't exist yet, the result is automatically false |  | ||||||
| 	if !os.exists(repo_dir) { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// We iterate over every directory in the repo dir |  | ||||||
| 	// TODO filter so we only check directories |  | ||||||
| 	for d in os.ls(repo_dir)? { |  | ||||||
| 		// Because a repository only allows a single version of each package, |  | ||||||
| 		// we need only compare whether the name of the package is the same, |  | ||||||
| 		// not the version. |  | ||||||
| 		name := d.split('-')#[..-2].join('-') |  | ||||||
| 
 |  | ||||||
| 		if name == pkg_name { |  | ||||||
| 			// We lock the mutex here to prevent other routines from creating a |  | ||||||
| 			// new archive while we remove an entry |  | ||||||
| 			lock r.mutex { |  | ||||||
| 				os.rmdir_all(os.join_path_single(repo_dir, d))? |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// Also remove the package archive |  | ||||||
| 			repo_pkg_dir := os.join_path(r.pkg_dir, repo, arch) |  | ||||||
| 
 |  | ||||||
| 			archives := os.ls(repo_pkg_dir)?.filter(it.split('-')#[..-3].join('-') == name) |  | ||||||
| 
 |  | ||||||
| 			for archive_name in archives { |  | ||||||
| 				full_path := os.join_path_single(repo_pkg_dir, archive_name) |  | ||||||
| 				os.rm(full_path)? |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// Sync the db archives if requested |  | ||||||
| 			if sync { |  | ||||||
| 				r.sync(repo, arch)? |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			return true |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,85 @@ | ||||||
|  | module repo | ||||||
|  | 
 | ||||||
|  | import os | ||||||
|  | 
 | ||||||
|  | // remove_pkg_from_arch_repo removes a package from an arch-repo's database. It | ||||||
|  | // returns false if the package wasn't present in the database. It also | ||||||
|  | // optionally re-syncs the repo archives. | ||||||
|  | pub fn (r &RepoGroupManager) remove_pkg_from_arch_repo(repo string, arch string, pkg_name string, sync bool) ?bool { | ||||||
|  | 	repo_dir := os.join_path(r.repos_dir, repo, arch) | ||||||
|  | 
 | ||||||
|  | 	// If the repository doesn't exist yet, the result is automatically false | ||||||
|  | 	if !os.exists(repo_dir) { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// We iterate over every directory in the repo dir | ||||||
|  | 	// TODO filter so we only check directories | ||||||
|  | 	for d in os.ls(repo_dir)? { | ||||||
|  | 		// Because a repository only allows a single version of each package, | ||||||
|  | 		// we need only compare whether the name of the package is the same, | ||||||
|  | 		// not the version. | ||||||
|  | 		name := d.split('-')#[..-2].join('-') | ||||||
|  | 
 | ||||||
|  | 		if name == pkg_name { | ||||||
|  | 			// We lock the mutex here to prevent other routines from creating a | ||||||
|  | 			// new archive while we remove an entry | ||||||
|  | 			lock r.mutex { | ||||||
|  | 				os.rmdir_all(os.join_path_single(repo_dir, d))? | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// Also remove the package archive | ||||||
|  | 			repo_pkg_dir := os.join_path(r.pkg_dir, repo, arch) | ||||||
|  | 
 | ||||||
|  | 			archives := os.ls(repo_pkg_dir)?.filter(it.split('-')#[..-3].join('-') == name) | ||||||
|  | 
 | ||||||
|  | 			for archive_name in archives { | ||||||
|  | 				full_path := os.join_path_single(repo_pkg_dir, archive_name) | ||||||
|  | 				os.rm(full_path)? | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// Sync the db archives if requested | ||||||
|  | 			if sync { | ||||||
|  | 				r.sync(repo, arch)? | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // remove_arch_repo removes an arch-repo & its packages. | ||||||
|  | pub fn (r &RepoGroupManager) remove_arch_repo(repo string, arch string) ?bool { | ||||||
|  | 	repo_dir := os.join_path(r.repos_dir, repo, arch) | ||||||
|  | 
 | ||||||
|  | 	// If the repository doesn't exist yet, the result is automatically false | ||||||
|  | 	if !os.exists(repo_dir) { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	os.rmdir_all(repo_dir)? | ||||||
|  | 
 | ||||||
|  | 	pkg_dir := os.join_path(r.pkg_dir, repo, arch) | ||||||
|  | 	os.rmdir_all(pkg_dir)? | ||||||
|  | 
 | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // remove_repo removes a repo & its packages. | ||||||
|  | pub fn (r &RepoGroupManager) remove_repo(repo string) ?bool { | ||||||
|  | 	repo_dir := os.join_path_single(r.repos_dir, repo) | ||||||
|  | 
 | ||||||
|  | 	// If the repository doesn't exist yet, the result is automatically false | ||||||
|  | 	if !os.exists(repo_dir) { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	os.rmdir_all(repo_dir)? | ||||||
|  | 
 | ||||||
|  | 	pkg_dir := os.join_path_single(r.pkg_dir, repo) | ||||||
|  | 	os.rmdir_all(pkg_dir)? | ||||||
|  | 
 | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | @ -0,0 +1,77 @@ | ||||||
|  | module server | ||||||
|  | 
 | ||||||
|  | import web | ||||||
|  | import net.http | ||||||
|  | import response { new_response } | ||||||
|  | 
 | ||||||
|  | // delete_package tries to remove the given package. | ||||||
|  | ['/:repo/:arch/:pkg'; delete] | ||||||
|  | fn (mut app App) delete_package(repo string, arch string, pkg string) web.Result { | ||||||
|  | 	if !app.is_authorized() { | ||||||
|  | 		return app.json(http.Status.unauthorized, new_response('Unauthorized.')) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	res := app.repo.remove_pkg_from_arch_repo(repo, arch, pkg, true) or { | ||||||
|  | 		app.lerror('Error while deleting package: $err.msg()') | ||||||
|  | 
 | ||||||
|  | 		return app.json(http.Status.internal_server_error, new_response('Failed to delete package.')) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if res { | ||||||
|  | 		app.linfo("Removed package '$pkg' from '$repo/$arch'") | ||||||
|  | 
 | ||||||
|  | 		return app.json(http.Status.ok, new_response('Package removed.')) | ||||||
|  | 	} else { | ||||||
|  | 		app.linfo("Tried removing package '$pkg' from '$repo/$arch', but it doesn't exist.") | ||||||
|  | 
 | ||||||
|  | 		return app.json(http.Status.not_found, new_response('Package not found.')) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // delete_arch_repo tries to remove the given arch-repo. | ||||||
|  | ['/:repo/:arch'; delete] | ||||||
|  | fn (mut app App) delete_arch_repo(repo string, arch string) web.Result { | ||||||
|  | 	if !app.is_authorized() { | ||||||
|  | 		return app.json(http.Status.unauthorized, new_response('Unauthorized.')) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	res := app.repo.remove_arch_repo(repo, arch) or { | ||||||
|  | 		app.lerror('Error while deleting arch-repo: $err.msg()') | ||||||
|  | 
 | ||||||
|  | 		return app.json(http.Status.internal_server_error, new_response('Failed to delete arch-repo.')) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if res { | ||||||
|  | 		app.linfo("Removed '$repo/$arch'") | ||||||
|  | 
 | ||||||
|  | 		return app.json(http.Status.ok, new_response('Arch-repo removed.')) | ||||||
|  | 	} else { | ||||||
|  | 		app.linfo("Tried removing '$repo/$arch', but it doesn't exist.") | ||||||
|  | 
 | ||||||
|  | 		return app.json(http.Status.not_found, new_response('Arch-repo not found.')) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // delete_repo tries to remove the given repo. | ||||||
|  | ['/:repo'; delete] | ||||||
|  | fn (mut app App) delete_repo(repo string) web.Result { | ||||||
|  | 	if !app.is_authorized() { | ||||||
|  | 		return app.json(http.Status.unauthorized, new_response('Unauthorized.')) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	res := app.repo.remove_repo(repo) or { | ||||||
|  | 		app.lerror('Error while deleting repo: $err.msg()') | ||||||
|  | 
 | ||||||
|  | 		return app.json(http.Status.internal_server_error, new_response('Failed to delete repo.')) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if res { | ||||||
|  | 		app.linfo("Removed '$repo'") | ||||||
|  | 
 | ||||||
|  | 		return app.json(http.Status.ok, new_response('Repo removed.')) | ||||||
|  | 	} else { | ||||||
|  | 		app.linfo("Tried removing '$repo', but it doesn't exist.") | ||||||
|  | 
 | ||||||
|  | 		return app.json(http.Status.not_found, new_response('Repo not found.')) | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue