2018-12-13 11:08:53 +00:00
# frozen_string_literal: true
module API
2020-10-15 00:08:42 +00:00
class Releases < :: API :: Base
2018-12-13 11:08:53 +00:00
include PaginationParams
2022-11-03 15:11:31 +00:00
releases_tags = %w[ releases ]
2019-09-02 07:03:07 +00:00
RELEASE_ENDPOINT_REQUIREMENTS = API :: NAMESPACE_OR_PROJECT_REQUIREMENTS
2018-12-25 09:48:26 +00:00
. merge ( tag_name : API :: NO_SLASH_URL_PART_REGEX )
2021-04-30 06:10:26 +00:00
RELEASE_CLI_USER_AGENT = 'GitLab-release-cli'
2018-12-13 11:08:53 +00:00
2022-04-08 12:08:48 +00:00
feature_category :release_orchestration
2022-04-29 12:10:13 +00:00
urgency :low
2018-12-13 11:08:53 +00:00
2022-04-08 12:08:48 +00:00
params do
2022-11-03 15:11:31 +00:00
requires :id , types : [ String , Integer ] , desc : 'The ID or URL-encoded path of the group'
2022-04-08 12:08:48 +00:00
end
resource :groups , requirements : API :: NAMESPACE_OR_PROJECT_REQUIREMENTS do
before { authorize_read_group_releases! }
2021-04-30 06:10:26 +00:00
2022-11-03 15:11:31 +00:00
desc 'List group releases' do
detail 'Returns a list of group releases.'
2022-04-08 12:08:48 +00:00
success Entities :: Release
2022-11-03 15:11:31 +00:00
failure [
{ code : 400 , message : 'Bad request' } ,
{ code : 403 , message : 'Forbidden' } ,
{ code : 404 , message : 'Not found' }
]
is_array true
tags releases_tags
2022-04-08 12:08:48 +00:00
end
params do
2022-11-03 15:11:31 +00:00
requires :id ,
types : [ String , Integer ] ,
desc : 'The ID or URL-encoded path of the group owned by the authenticated user'
optional :sort ,
type : String ,
values : %w[ asc desc ] ,
default : 'desc' ,
desc : 'The direction of the order. Either `desc` (default) for descending order or `asc` for ascending order'
optional :simple ,
type : Boolean ,
default : false ,
desc : 'Return only limited fields for each release'
2022-04-08 12:08:48 +00:00
use :pagination
end
get " :id/releases " do
finder_options = {
sort : params [ :sort ]
}
strict_params = declared_params ( include_missing : false )
releases = find_group_releases ( finder_options )
present_group_releases ( strict_params , releases )
end
end
2020-10-30 18:08:56 +00:00
2018-12-13 11:08:53 +00:00
params do
2022-11-03 15:11:31 +00:00
requires :id , types : [ String , Integer ] , desc : 'The ID or URL-encoded path of the project'
2018-12-13 11:08:53 +00:00
end
resource :projects , requirements : API :: NAMESPACE_OR_PROJECT_REQUIREMENTS do
2022-04-08 12:08:48 +00:00
before { authorize_read_releases! }
after { track_release_event }
2022-11-03 15:11:31 +00:00
desc 'List Releases' do
detail 'Returns a paginated list of releases. This feature was introduced in GitLab 11.7.'
2021-04-30 06:10:26 +00:00
named 'get_releases'
2022-11-03 15:11:31 +00:00
is_array true
2018-12-13 11:08:53 +00:00
success Entities :: Release
2022-11-03 15:11:31 +00:00
tags releases_tags
2018-12-13 11:08:53 +00:00
end
params do
use :pagination
2022-11-03 15:11:31 +00:00
optional :order_by ,
type : String ,
values : %w[ released_at created_at ] ,
default : 'released_at' ,
desc : 'The field to use as order. Either `released_at` (default) or `created_at`'
optional :sort ,
type : String ,
values : %w[ asc desc ] ,
default : 'desc' ,
desc : 'The direction of the order. Either `desc` (default) for descending order or `asc` for ascending order'
optional :include_html_description ,
type : Boolean ,
desc : 'If `true`, a response includes HTML rendered markdown of the release description'
2018-12-13 11:08:53 +00:00
end
2021-10-27 15:13:41 +00:00
route_setting :authentication , job_token_allowed : true
2018-12-13 11:08:53 +00:00
get ':id/releases' do
2020-10-12 18:08:31 +00:00
releases = :: ReleasesFinder . new ( user_project , current_user , declared_params . slice ( :order_by , :sort ) ) . execute
2018-12-13 11:08:53 +00:00
2021-05-11 03:10:35 +00:00
# We cache the serialized payload per user in order to avoid repeated renderings.
# Since the cached result could contain sensitive information,
# it will expire in a short interval.
present_cached paginate ( releases ) ,
with : Entities :: Release ,
# `current_user` could be absent if the releases are publicly accesible.
# We should not use `cache_key` for the user because the version/updated_at
# context is unnecessary here.
cache_context : - > ( _ ) { " user:{ #{ current_user & . id } } " } ,
expires_in : 5 . minutes ,
2021-06-03 12:10:18 +00:00
current_user : current_user ,
include_html_description : params [ :include_html_description ]
2018-12-13 11:08:53 +00:00
end
2022-11-03 15:11:31 +00:00
desc 'Get a release by a tag name' do
detail 'Gets a release for the given tag. This feature was introduced in GitLab 11.7.'
2021-04-30 06:10:26 +00:00
named 'get_release'
2018-12-13 11:08:53 +00:00
success Entities :: Release
2022-11-03 15:11:31 +00:00
failure [
{ code : 401 , message : 'Unauthorized' } ,
{ code : 404 , message : 'Not found' }
]
tags releases_tags
2018-12-13 11:08:53 +00:00
end
params do
2022-11-03 15:11:31 +00:00
requires :tag_name , type : String , desc : 'The Git tag the release is associated with' , as : :tag
optional :include_html_description ,
type : Boolean ,
desc : 'If `true`, a response includes HTML rendered markdown of the release description'
2018-12-13 11:08:53 +00:00
end
2021-10-27 15:13:41 +00:00
route_setting :authentication , job_token_allowed : true
2019-09-02 07:03:07 +00:00
get ':id/releases/:tag_name' , requirements : RELEASE_ENDPOINT_REQUIREMENTS do
2019-05-03 13:29:20 +00:00
authorize_download_code!
2018-12-13 11:08:53 +00:00
2021-06-30 03:07:30 +00:00
not_found! unless release
2021-06-03 12:10:18 +00:00
present release , with : Entities :: Release , current_user : current_user , include_html_description : params [ :include_html_description ]
2018-12-13 11:08:53 +00:00
end
2022-09-16 15:14:12 +00:00
desc 'Download a project release asset file' do
detail 'This feature was introduced in GitLab 15.4.'
named 'download_release_asset_file'
2022-11-03 15:11:31 +00:00
failure [
{ code : 401 , message : 'Unauthorized' } ,
{ code : 404 , message : 'Not found' }
]
tags releases_tags
2022-09-16 15:14:12 +00:00
end
params do
2022-11-03 15:11:31 +00:00
requires :tag_name , type : String , desc : 'The Git tag the release is associated with' , as : :tag
requires :file_path ,
type : String ,
file_path : true ,
desc : 'The path to the file to download, as specified when creating the release asset'
2022-09-16 15:14:12 +00:00
end
route_setting :authentication , job_token_allowed : true
get ':id/releases/:tag_name/downloads/*file_path' , format : false , requirements : RELEASE_ENDPOINT_REQUIREMENTS do
authorize_download_code!
not_found! unless release
link = release . links . find_by_filepath! ( " / #{ params [ :file_path ] } " )
not_found! unless link
redirect link . url
end
desc 'Get the latest project release' do
detail 'This feature was introduced in GitLab 15.4.'
named 'get_latest_release'
2022-11-03 15:11:31 +00:00
failure [
{ code : 401 , message : 'Unauthorized' } ,
{ code : 404 , message : 'Not found' }
]
tags releases_tags
2022-09-16 15:14:12 +00:00
end
params do
2022-11-03 15:11:31 +00:00
requires :suffix_path ,
type : String ,
file_path : true ,
desc : 'The path to be suffixed to the latest release'
2022-09-16 15:14:12 +00:00
end
route_setting :authentication , job_token_allowed : true
get ':id/releases/permalink/latest(/)(*suffix_path)' , format : false , requirements : RELEASE_ENDPOINT_REQUIREMENTS do
authorize_download_code!
# Try to find the latest release
latest_release = find_latest_release
not_found! unless latest_release
# Build the full API URL with the tag of the latest release
redirect_url = api_v4_projects_releases_path ( id : user_project . id , tag_name : latest_release . tag )
# Include the additional suffix_path if present
redirect_url += " / #{ params [ :suffix_path ] } " if params [ :suffix_path ] . present?
# Include any query parameter except `order_by` since we have plans to extend it in the future.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/352945 for reference.
query_parameters_except_order_by = get_query_params . except ( 'order_by' )
if query_parameters_except_order_by . present?
redirect_url += " ? #{ query_parameters_except_order_by . compact . to_param } "
end
redirect redirect_url
end
2022-11-03 15:11:31 +00:00
desc 'Create a release' do
detail 'Creates a release. Developer level access to the project is required to create a release. This feature was introduced in GitLab 11.7.'
2021-04-30 06:10:26 +00:00
named 'create_release'
2018-12-13 11:08:53 +00:00
success Entities :: Release
2022-11-03 15:11:31 +00:00
failure [
{ code : 400 , message : 'Bad request' } ,
{ code : 401 , message : 'Unauthorized' } ,
{ code : 403 , message : 'Forbidden' } ,
{ code : 404 , message : 'Not found' } ,
{ code : 409 , message : 'Conflict' } ,
{ code : 422 , message : 'Unprocessable entity' }
]
tags releases_tags
2018-12-13 11:08:53 +00:00
end
params do
2022-11-03 15:11:31 +00:00
requires :tag_name , type : String , desc : 'The tag where the release is created from' , as : :tag
2022-05-31 18:08:16 +00:00
optional :tag_message , type : String , desc : 'Message to use if creating a new annotated tag'
2022-11-03 15:11:31 +00:00
optional :name , type : String , desc : 'The release name'
optional :description , type : String , desc : 'The description of the release. You can use Markdown'
optional :ref ,
type : String ,
desc : " If a tag specified in `tag_name` doesn't exist, the release is created from `ref` and tagged " \
" with `tag_name`. It can be a commit SHA, another tag name, or a branch name. "
2019-01-02 10:56:03 +00:00
optional :assets , type : Hash do
optional :links , type : Array do
2022-11-03 15:11:31 +00:00
requires :name , type : String , desc : 'The name of the link. Link names must be unique within the release'
requires :url , type : String , desc : 'The URL of the link. Link URLs must be unique within the release'
optional :filepath , type : String , desc : 'Optional path for a direct asset link'
optional :link_type , type : String , desc : 'The type of the link: `other`, `runbook`, `image`, `package`. Defaults to `other`'
2019-01-02 10:56:03 +00:00
end
2019-01-02 01:40:33 +00:00
end
2022-11-03 15:11:31 +00:00
optional :milestones ,
type : Array [ String ] ,
coerce_with : :: API :: Validations :: Types :: CommaSeparatedToArray . coerce ,
desc : 'The title of each milestone the release is associated with. GitLab Premium customers can specify group milestones' ,
default : [ ]
optional :released_at ,
type : DateTime ,
desc : 'Date and time for the release. Defaults to the current time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). ' \
'Only provide this field if creating an upcoming or historical release.'
2018-12-13 11:08:53 +00:00
end
2019-12-11 15:07:38 +00:00
route_setting :authentication , job_token_allowed : true
2018-12-13 11:08:53 +00:00
post ':id/releases' do
authorize_create_release!
2018-12-25 09:48:26 +00:00
result = :: Releases :: CreateService
. new ( user_project , current_user , declared_params ( include_missing : false ) )
. execute
2018-12-13 11:08:53 +00:00
if result [ :status ] == :success
2020-01-10 18:07:43 +00:00
log_release_created_audit_event ( result [ :release ] )
2019-05-03 13:29:20 +00:00
present result [ :release ] , with : Entities :: Release , current_user : current_user
2018-12-13 11:08:53 +00:00
else
2018-12-25 09:48:26 +00:00
render_api_error! ( result [ :message ] , result [ :http_status ] )
2018-12-13 11:08:53 +00:00
end
end
desc 'Update a release' do
2022-11-03 15:11:31 +00:00
detail 'Updates a release. Developer level access to the project is required to update a release. This feature was introduced in GitLab 11.7.'
2021-04-30 06:10:26 +00:00
named 'update_release'
2018-12-13 11:08:53 +00:00
success Entities :: Release
2022-11-03 15:11:31 +00:00
failure [
{ code : 400 , message : 'Bad request' } ,
{ code : 401 , message : 'Unauthorized' } ,
{ code : 403 , message : 'Forbidden' } ,
{ code : 404 , message : 'Not found' }
]
tags releases_tags
2018-12-13 11:08:53 +00:00
end
params do
2022-11-03 15:11:31 +00:00
requires :tag_name , type : String , desc : 'The Git tag the release is associated with' , as : :tag
optional :name , type : String , desc : 'The release name'
optional :description , type : String , desc : 'The description of the release. You can use Markdown'
optional :released_at , type : DateTime , desc : 'The date when the release is/was ready. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`)'
optional :milestones ,
type : Array [ String ] ,
coerce_with : :: API :: Validations :: Types :: CommaSeparatedToArray . coerce ,
desc : 'The title of each milestone to associate with the release. GitLab Premium customers can specify group milestones. To remove all milestones from the release, specify `[]`'
2018-12-13 11:08:53 +00:00
end
2021-10-27 15:13:41 +00:00
route_setting :authentication , job_token_allowed : true
2019-09-02 07:03:07 +00:00
put ':id/releases/:tag_name' , requirements : RELEASE_ENDPOINT_REQUIREMENTS do
2018-12-13 11:08:53 +00:00
authorize_update_release!
2018-12-25 09:48:26 +00:00
result = :: Releases :: UpdateService
. new ( user_project , current_user , declared_params ( include_missing : false ) )
. execute
2018-12-13 11:08:53 +00:00
if result [ :status ] == :success
2020-01-10 18:07:43 +00:00
log_release_updated_audit_event
log_release_milestones_updated_audit_event if result [ :milestones_updated ]
2019-05-03 13:29:20 +00:00
present result [ :release ] , with : Entities :: Release , current_user : current_user
2018-12-13 11:08:53 +00:00
else
render_api_error! ( result [ :message ] , result [ :http_status ] )
end
end
2018-12-25 07:36:25 +00:00
desc 'Delete a release' do
2022-11-03 15:11:31 +00:00
detail " Delete a release. Deleting a release doesn't delete the associated tag. Maintainer level access to the project is required to delete a release. This feature was introduced in GitLab 11.7. "
2021-04-30 06:10:26 +00:00
named 'delete_release'
2018-12-25 07:36:25 +00:00
success Entities :: Release
2022-11-03 15:11:31 +00:00
failure [
{ code : 400 , message : 'Bad request' } ,
{ code : 401 , message : 'Unauthorized' } ,
{ code : 403 , message : 'Forbidden' } ,
{ code : 404 , message : 'Not found' }
]
tags releases_tags
2018-12-25 07:36:25 +00:00
end
params do
2022-11-03 15:11:31 +00:00
requires :tag_name , type : String , desc : 'The Git tag the release is associated with' , as : :tag
2018-12-25 07:36:25 +00:00
end
2021-10-27 15:13:41 +00:00
route_setting :authentication , job_token_allowed : true
2019-09-02 07:03:07 +00:00
delete ':id/releases/:tag_name' , requirements : RELEASE_ENDPOINT_REQUIREMENTS do
2018-12-25 09:48:26 +00:00
authorize_destroy_release!
2018-12-25 07:36:25 +00:00
2018-12-25 09:48:26 +00:00
result = :: Releases :: DestroyService
. new ( user_project , current_user , declared_params ( include_missing : false ) )
. execute
2018-12-25 07:36:25 +00:00
if result [ :status ] == :success
2022-08-17 18:11:29 +00:00
log_release_deleted_audit_event
2019-05-03 13:29:20 +00:00
present result [ :release ] , with : Entities :: Release , current_user : current_user
2018-12-25 07:36:25 +00:00
else
render_api_error! ( result [ :message ] , result [ :http_status ] )
end
end
2018-12-13 11:08:53 +00:00
end
2018-12-25 09:48:26 +00:00
helpers do
2022-04-08 12:08:48 +00:00
def authorize_read_group_releases!
authorize! :read_release , user_group
end
2018-12-25 09:48:26 +00:00
def authorize_create_release!
authorize! :create_release , user_project
end
def authorize_read_releases!
authorize! :read_release , user_project
end
def authorize_read_release!
authorize! :read_release , release
end
def authorize_update_release!
authorize! :update_release , release
end
def authorize_destroy_release!
authorize! :destroy_release , release
end
2019-05-03 13:29:20 +00:00
def authorize_download_code!
2021-06-30 03:07:30 +00:00
authorize! :download_code , user_project
2019-05-03 13:29:20 +00:00
end
2020-03-25 18:08:10 +00:00
def authorize_create_evidence!
2020-10-07 15:08:40 +00:00
# extended in EE
2020-03-25 18:08:10 +00:00
end
2018-12-25 09:48:26 +00:00
def release
@release || = user_project . releases . find_by_tag ( params [ :tag ] )
end
2020-01-10 18:07:43 +00:00
2022-09-16 15:14:12 +00:00
def find_latest_release
ReleasesFinder . new ( user_project , current_user , { order_by : 'released_at' , sort : 'desc' } ) . execute . first
end
def get_query_params
return { } unless @request . query_string . present?
Rack :: Utils . parse_nested_query ( @request . query_string )
end
2020-01-10 18:07:43 +00:00
def log_release_created_audit_event ( release )
2020-10-07 15:08:40 +00:00
# extended in EE
2020-01-10 18:07:43 +00:00
end
def log_release_updated_audit_event
2020-10-07 15:08:40 +00:00
# extended in EE
2020-01-10 18:07:43 +00:00
end
2022-08-17 18:11:29 +00:00
def log_release_deleted_audit_event
# extended in EE
end
2020-01-10 18:07:43 +00:00
def log_release_milestones_updated_audit_event
2020-10-07 15:08:40 +00:00
# extended in EE
2020-01-10 18:07:43 +00:00
end
2021-04-30 06:10:26 +00:00
def release_cli?
request . env [ 'HTTP_USER_AGENT' ] & . include? ( RELEASE_CLI_USER_AGENT ) == true
end
def event_context
{
release_cli : release_cli?
}
end
def track_release_event
Gitlab :: Tracking . event ( options [ :for ] . name , options [ :route_options ] [ :named ] ,
project : user_project , user : current_user , ** event_context )
end
2022-04-08 12:08:48 +00:00
def find_group_releases ( finder_options )
:: Releases :: GroupReleasesFinder
. new ( user_group , current_user , finder_options )
. execute ( preload : true )
end
def present_group_releases ( params , releases )
options = {
with : params [ :simple ] ? Entities :: BasicReleaseDetails : Entities :: Release ,
current_user : current_user
}
# GroupReleasesFinder has already ordered the data for us
present paginate ( releases , skip_default_order : true ) , options
end
2018-12-25 09:48:26 +00:00
end
2018-12-13 11:08:53 +00:00
end
end
2020-01-10 18:07:43 +00:00
2021-05-11 21:10:21 +00:00
API :: Releases . prepend_mod_with ( 'API::Releases' )