code.gitea.io/gitea@v1.22.3/routers/api/v1/repo/action.go (about)

     1  // Copyright 2023 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package repo
     5  
     6  import (
     7  	"errors"
     8  	"net/http"
     9  
    10  	actions_model "code.gitea.io/gitea/models/actions"
    11  	"code.gitea.io/gitea/models/db"
    12  	secret_model "code.gitea.io/gitea/models/secret"
    13  	api "code.gitea.io/gitea/modules/structs"
    14  	"code.gitea.io/gitea/modules/util"
    15  	"code.gitea.io/gitea/modules/web"
    16  	"code.gitea.io/gitea/routers/api/v1/shared"
    17  	"code.gitea.io/gitea/routers/api/v1/utils"
    18  	actions_service "code.gitea.io/gitea/services/actions"
    19  	"code.gitea.io/gitea/services/context"
    20  	secret_service "code.gitea.io/gitea/services/secrets"
    21  )
    22  
    23  // ListActionsSecrets list an repo's actions secrets
    24  func (Action) ListActionsSecrets(ctx *context.APIContext) {
    25  	// swagger:operation GET /repos/{owner}/{repo}/actions/secrets repository repoListActionsSecrets
    26  	// ---
    27  	// summary: List an repo's actions secrets
    28  	// produces:
    29  	// - application/json
    30  	// parameters:
    31  	// - name: owner
    32  	//   in: path
    33  	//   description: owner of the repository
    34  	//   type: string
    35  	//   required: true
    36  	// - name: repo
    37  	//   in: path
    38  	//   description: name of the repository
    39  	//   type: string
    40  	//   required: true
    41  	// - name: page
    42  	//   in: query
    43  	//   description: page number of results to return (1-based)
    44  	//   type: integer
    45  	// - name: limit
    46  	//   in: query
    47  	//   description: page size of results
    48  	//   type: integer
    49  	// responses:
    50  	//   "200":
    51  	//     "$ref": "#/responses/SecretList"
    52  	//   "404":
    53  	//     "$ref": "#/responses/notFound"
    54  
    55  	repo := ctx.Repo.Repository
    56  
    57  	opts := &secret_model.FindSecretsOptions{
    58  		RepoID:      repo.ID,
    59  		ListOptions: utils.GetListOptions(ctx),
    60  	}
    61  
    62  	secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts)
    63  	if err != nil {
    64  		ctx.InternalServerError(err)
    65  		return
    66  	}
    67  
    68  	apiSecrets := make([]*api.Secret, len(secrets))
    69  	for k, v := range secrets {
    70  		apiSecrets[k] = &api.Secret{
    71  			Name:    v.Name,
    72  			Created: v.CreatedUnix.AsTime(),
    73  		}
    74  	}
    75  
    76  	ctx.SetTotalCountHeader(count)
    77  	ctx.JSON(http.StatusOK, apiSecrets)
    78  }
    79  
    80  // create or update one secret of the repository
    81  func (Action) CreateOrUpdateSecret(ctx *context.APIContext) {
    82  	// swagger:operation PUT /repos/{owner}/{repo}/actions/secrets/{secretname} repository updateRepoSecret
    83  	// ---
    84  	// summary: Create or Update a secret value in a repository
    85  	// consumes:
    86  	// - application/json
    87  	// produces:
    88  	// - application/json
    89  	// parameters:
    90  	// - name: owner
    91  	//   in: path
    92  	//   description: owner of the repository
    93  	//   type: string
    94  	//   required: true
    95  	// - name: repo
    96  	//   in: path
    97  	//   description: name of the repository
    98  	//   type: string
    99  	//   required: true
   100  	// - name: secretname
   101  	//   in: path
   102  	//   description: name of the secret
   103  	//   type: string
   104  	//   required: true
   105  	// - name: body
   106  	//   in: body
   107  	//   schema:
   108  	//     "$ref": "#/definitions/CreateOrUpdateSecretOption"
   109  	// responses:
   110  	//   "201":
   111  	//     description: response when creating a secret
   112  	//   "204":
   113  	//     description: response when updating a secret
   114  	//   "400":
   115  	//     "$ref": "#/responses/error"
   116  	//   "404":
   117  	//     "$ref": "#/responses/notFound"
   118  
   119  	repo := ctx.Repo.Repository
   120  
   121  	opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
   122  
   123  	_, created, err := secret_service.CreateOrUpdateSecret(ctx, 0, repo.ID, ctx.Params("secretname"), opt.Data)
   124  	if err != nil {
   125  		if errors.Is(err, util.ErrInvalidArgument) {
   126  			ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err)
   127  		} else if errors.Is(err, util.ErrNotExist) {
   128  			ctx.Error(http.StatusNotFound, "CreateOrUpdateSecret", err)
   129  		} else {
   130  			ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err)
   131  		}
   132  		return
   133  	}
   134  
   135  	if created {
   136  		ctx.Status(http.StatusCreated)
   137  	} else {
   138  		ctx.Status(http.StatusNoContent)
   139  	}
   140  }
   141  
   142  // DeleteSecret delete one secret of the repository
   143  func (Action) DeleteSecret(ctx *context.APIContext) {
   144  	// swagger:operation DELETE /repos/{owner}/{repo}/actions/secrets/{secretname} repository deleteRepoSecret
   145  	// ---
   146  	// summary: Delete a secret in a repository
   147  	// consumes:
   148  	// - application/json
   149  	// produces:
   150  	// - application/json
   151  	// parameters:
   152  	// - name: owner
   153  	//   in: path
   154  	//   description: owner of the repository
   155  	//   type: string
   156  	//   required: true
   157  	// - name: repo
   158  	//   in: path
   159  	//   description: name of the repository
   160  	//   type: string
   161  	//   required: true
   162  	// - name: secretname
   163  	//   in: path
   164  	//   description: name of the secret
   165  	//   type: string
   166  	//   required: true
   167  	// responses:
   168  	//   "204":
   169  	//     description: delete one secret of the organization
   170  	//   "400":
   171  	//     "$ref": "#/responses/error"
   172  	//   "404":
   173  	//     "$ref": "#/responses/notFound"
   174  
   175  	repo := ctx.Repo.Repository
   176  
   177  	err := secret_service.DeleteSecretByName(ctx, 0, repo.ID, ctx.Params("secretname"))
   178  	if err != nil {
   179  		if errors.Is(err, util.ErrInvalidArgument) {
   180  			ctx.Error(http.StatusBadRequest, "DeleteSecret", err)
   181  		} else if errors.Is(err, util.ErrNotExist) {
   182  			ctx.Error(http.StatusNotFound, "DeleteSecret", err)
   183  		} else {
   184  			ctx.Error(http.StatusInternalServerError, "DeleteSecret", err)
   185  		}
   186  		return
   187  	}
   188  
   189  	ctx.Status(http.StatusNoContent)
   190  }
   191  
   192  // GetVariable get a repo-level variable
   193  func (Action) GetVariable(ctx *context.APIContext) {
   194  	// swagger:operation GET /repos/{owner}/{repo}/actions/variables/{variablename} repository getRepoVariable
   195  	// ---
   196  	// summary: Get a repo-level variable
   197  	// produces:
   198  	// - application/json
   199  	// parameters:
   200  	// - name: owner
   201  	//   in: path
   202  	//   description: name of the owner
   203  	//   type: string
   204  	//   required: true
   205  	// - name: repo
   206  	//   in: path
   207  	//   description: name of the repository
   208  	//   type: string
   209  	//   required: true
   210  	// - name: variablename
   211  	//   in: path
   212  	//   description: name of the variable
   213  	//   type: string
   214  	//   required: true
   215  	// responses:
   216  	//   "200":
   217  	//			"$ref": "#/responses/ActionVariable"
   218  	//   "400":
   219  	//     "$ref": "#/responses/error"
   220  	//   "404":
   221  	//     "$ref": "#/responses/notFound"
   222  	v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{
   223  		RepoID: ctx.Repo.Repository.ID,
   224  		Name:   ctx.Params("variablename"),
   225  	})
   226  	if err != nil {
   227  		if errors.Is(err, util.ErrNotExist) {
   228  			ctx.Error(http.StatusNotFound, "GetVariable", err)
   229  		} else {
   230  			ctx.Error(http.StatusInternalServerError, "GetVariable", err)
   231  		}
   232  		return
   233  	}
   234  
   235  	variable := &api.ActionVariable{
   236  		OwnerID: v.OwnerID,
   237  		RepoID:  v.RepoID,
   238  		Name:    v.Name,
   239  		Data:    v.Data,
   240  	}
   241  
   242  	ctx.JSON(http.StatusOK, variable)
   243  }
   244  
   245  // DeleteVariable delete a repo-level variable
   246  func (Action) DeleteVariable(ctx *context.APIContext) {
   247  	// swagger:operation DELETE /repos/{owner}/{repo}/actions/variables/{variablename} repository deleteRepoVariable
   248  	// ---
   249  	// summary: Delete a repo-level variable
   250  	// produces:
   251  	// - application/json
   252  	// parameters:
   253  	// - name: owner
   254  	//   in: path
   255  	//   description: name of the owner
   256  	//   type: string
   257  	//   required: true
   258  	// - name: repo
   259  	//   in: path
   260  	//   description: name of the repository
   261  	//   type: string
   262  	//   required: true
   263  	// - name: variablename
   264  	//   in: path
   265  	//   description: name of the variable
   266  	//   type: string
   267  	//   required: true
   268  	// responses:
   269  	//   "200":
   270  	//			"$ref": "#/responses/ActionVariable"
   271  	//   "201":
   272  	//     description: response when deleting a variable
   273  	//   "204":
   274  	//     description: response when deleting a variable
   275  	//   "400":
   276  	//     "$ref": "#/responses/error"
   277  	//   "404":
   278  	//     "$ref": "#/responses/notFound"
   279  
   280  	if err := actions_service.DeleteVariableByName(ctx, 0, ctx.Repo.Repository.ID, ctx.Params("variablename")); err != nil {
   281  		if errors.Is(err, util.ErrInvalidArgument) {
   282  			ctx.Error(http.StatusBadRequest, "DeleteVariableByName", err)
   283  		} else if errors.Is(err, util.ErrNotExist) {
   284  			ctx.Error(http.StatusNotFound, "DeleteVariableByName", err)
   285  		} else {
   286  			ctx.Error(http.StatusInternalServerError, "DeleteVariableByName", err)
   287  		}
   288  		return
   289  	}
   290  
   291  	ctx.Status(http.StatusNoContent)
   292  }
   293  
   294  // CreateVariable create a repo-level variable
   295  func (Action) CreateVariable(ctx *context.APIContext) {
   296  	// swagger:operation POST /repos/{owner}/{repo}/actions/variables/{variablename} repository createRepoVariable
   297  	// ---
   298  	// summary: Create a repo-level variable
   299  	// produces:
   300  	// - application/json
   301  	// parameters:
   302  	// - name: owner
   303  	//   in: path
   304  	//   description: name of the owner
   305  	//   type: string
   306  	//   required: true
   307  	// - name: repo
   308  	//   in: path
   309  	//   description: name of the repository
   310  	//   type: string
   311  	//   required: true
   312  	// - name: variablename
   313  	//   in: path
   314  	//   description: name of the variable
   315  	//   type: string
   316  	//   required: true
   317  	// - name: body
   318  	//   in: body
   319  	//   schema:
   320  	//     "$ref": "#/definitions/CreateVariableOption"
   321  	// responses:
   322  	//   "201":
   323  	//     description: response when creating a repo-level variable
   324  	//   "204":
   325  	//     description: response when creating a repo-level variable
   326  	//   "400":
   327  	//     "$ref": "#/responses/error"
   328  	//   "404":
   329  	//     "$ref": "#/responses/notFound"
   330  
   331  	opt := web.GetForm(ctx).(*api.CreateVariableOption)
   332  
   333  	repoID := ctx.Repo.Repository.ID
   334  	variableName := ctx.Params("variablename")
   335  
   336  	v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{
   337  		RepoID: repoID,
   338  		Name:   variableName,
   339  	})
   340  	if err != nil && !errors.Is(err, util.ErrNotExist) {
   341  		ctx.Error(http.StatusInternalServerError, "GetVariable", err)
   342  		return
   343  	}
   344  	if v != nil && v.ID > 0 {
   345  		ctx.Error(http.StatusConflict, "VariableNameAlreadyExists", util.NewAlreadyExistErrorf("variable name %s already exists", variableName))
   346  		return
   347  	}
   348  
   349  	if _, err := actions_service.CreateVariable(ctx, 0, repoID, variableName, opt.Value); err != nil {
   350  		if errors.Is(err, util.ErrInvalidArgument) {
   351  			ctx.Error(http.StatusBadRequest, "CreateVariable", err)
   352  		} else {
   353  			ctx.Error(http.StatusInternalServerError, "CreateVariable", err)
   354  		}
   355  		return
   356  	}
   357  
   358  	ctx.Status(http.StatusNoContent)
   359  }
   360  
   361  // UpdateVariable update a repo-level variable
   362  func (Action) UpdateVariable(ctx *context.APIContext) {
   363  	// swagger:operation PUT /repos/{owner}/{repo}/actions/variables/{variablename} repository updateRepoVariable
   364  	// ---
   365  	// summary: Update a repo-level variable
   366  	// produces:
   367  	// - application/json
   368  	// parameters:
   369  	// - name: owner
   370  	//   in: path
   371  	//   description: name of the owner
   372  	//   type: string
   373  	//   required: true
   374  	// - name: repo
   375  	//   in: path
   376  	//   description: name of the repository
   377  	//   type: string
   378  	//   required: true
   379  	// - name: variablename
   380  	//   in: path
   381  	//   description: name of the variable
   382  	//   type: string
   383  	//   required: true
   384  	// - name: body
   385  	//   in: body
   386  	//   schema:
   387  	//     "$ref": "#/definitions/UpdateVariableOption"
   388  	// responses:
   389  	//   "201":
   390  	//     description: response when updating a repo-level variable
   391  	//   "204":
   392  	//     description: response when updating a repo-level variable
   393  	//   "400":
   394  	//     "$ref": "#/responses/error"
   395  	//   "404":
   396  	//     "$ref": "#/responses/notFound"
   397  
   398  	opt := web.GetForm(ctx).(*api.UpdateVariableOption)
   399  
   400  	v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{
   401  		RepoID: ctx.Repo.Repository.ID,
   402  		Name:   ctx.Params("variablename"),
   403  	})
   404  	if err != nil {
   405  		if errors.Is(err, util.ErrNotExist) {
   406  			ctx.Error(http.StatusNotFound, "GetVariable", err)
   407  		} else {
   408  			ctx.Error(http.StatusInternalServerError, "GetVariable", err)
   409  		}
   410  		return
   411  	}
   412  
   413  	if opt.Name == "" {
   414  		opt.Name = ctx.Params("variablename")
   415  	}
   416  	if _, err := actions_service.UpdateVariable(ctx, v.ID, opt.Name, opt.Value); err != nil {
   417  		if errors.Is(err, util.ErrInvalidArgument) {
   418  			ctx.Error(http.StatusBadRequest, "UpdateVariable", err)
   419  		} else {
   420  			ctx.Error(http.StatusInternalServerError, "UpdateVariable", err)
   421  		}
   422  		return
   423  	}
   424  
   425  	ctx.Status(http.StatusNoContent)
   426  }
   427  
   428  // ListVariables list repo-level variables
   429  func (Action) ListVariables(ctx *context.APIContext) {
   430  	// swagger:operation GET /repos/{owner}/{repo}/actions/variables repository getRepoVariablesList
   431  	// ---
   432  	// summary: Get repo-level variables list
   433  	// produces:
   434  	// - application/json
   435  	// parameters:
   436  	// - name: owner
   437  	//   in: path
   438  	//   description: name of the owner
   439  	//   type: string
   440  	//   required: true
   441  	// - name: repo
   442  	//   in: path
   443  	//   description: name of the repository
   444  	//   type: string
   445  	//   required: true
   446  	// - name: page
   447  	//   in: query
   448  	//   description: page number of results to return (1-based)
   449  	//   type: integer
   450  	// - name: limit
   451  	//   in: query
   452  	//   description: page size of results
   453  	//   type: integer
   454  	// responses:
   455  	//   "200":
   456  	//		 "$ref": "#/responses/VariableList"
   457  	//   "400":
   458  	//     "$ref": "#/responses/error"
   459  	//   "404":
   460  	//     "$ref": "#/responses/notFound"
   461  
   462  	vars, count, err := db.FindAndCount[actions_model.ActionVariable](ctx, &actions_model.FindVariablesOpts{
   463  		RepoID:      ctx.Repo.Repository.ID,
   464  		ListOptions: utils.GetListOptions(ctx),
   465  	})
   466  	if err != nil {
   467  		ctx.Error(http.StatusInternalServerError, "FindVariables", err)
   468  		return
   469  	}
   470  
   471  	variables := make([]*api.ActionVariable, len(vars))
   472  	for i, v := range vars {
   473  		variables[i] = &api.ActionVariable{
   474  			OwnerID: v.OwnerID,
   475  			RepoID:  v.RepoID,
   476  			Name:    v.Name,
   477  		}
   478  	}
   479  
   480  	ctx.SetTotalCountHeader(count)
   481  	ctx.JSON(http.StatusOK, variables)
   482  }
   483  
   484  // GetRegistrationToken returns the token to register repo runners
   485  func (Action) GetRegistrationToken(ctx *context.APIContext) {
   486  	// swagger:operation GET /repos/{owner}/{repo}/actions/runners/registration-token repository repoGetRunnerRegistrationToken
   487  	// ---
   488  	// summary: Get a repository's actions runner registration token
   489  	// produces:
   490  	// - application/json
   491  	// parameters:
   492  	// - name: owner
   493  	//   in: path
   494  	//   description: owner of the repo
   495  	//   type: string
   496  	//   required: true
   497  	// - name: repo
   498  	//   in: path
   499  	//   description: name of the repo
   500  	//   type: string
   501  	//   required: true
   502  	// responses:
   503  	//   "200":
   504  	//     "$ref": "#/responses/RegistrationToken"
   505  
   506  	shared.GetRegistrationToken(ctx, 0, ctx.Repo.Repository.ID)
   507  }
   508  
   509  var _ actions_service.API = new(Action)
   510  
   511  // Action implements actions_service.API
   512  type Action struct{}
   513  
   514  // NewAction creates a new Action service
   515  func NewAction() actions_service.API {
   516  	return Action{}
   517  }