Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions api-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,12 @@ definitions:
playbook:
type: string
example: test.yml
working_directory:
type: string
description: >-
Repository-relative current directory for Ansible commands. It is
valid only for Ansible templates and must stay inside the repository.
x-nullable: true
arguments:
type: string
example: '[]'
Expand Down Expand Up @@ -1004,6 +1010,12 @@ definitions:
playbook:
type: string
example: test.yml
working_directory:
type: string
description: >-
Repository-relative current directory for Ansible commands. It is
valid only for Ansible templates and must stay inside the repository.
x-nullable: true
arguments:
type: string
example: '[]'
Expand Down
2 changes: 0 additions & 2 deletions db/Inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@ func (e Inventory) GetFilename() string {
}

return e.Inventory

//return strings.TrimPrefix(e.Inventory, "/")
}

func (e Inventory) Validate() error {
Expand Down
1 change: 1 addition & 0 deletions db/Migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ func GetMigrations(dialect string) []Migration {
{Version: "2.19.14"},
{Version: "2.20.0"},
{Version: "2.20.1"},
{Version: "2.20.2"},
}

return append(initScripts, commonScripts...)
Expand Down
15 changes: 15 additions & 0 deletions db/Template.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,9 @@ type Template struct {
Name string `db:"name" json:"name"`
// playbook name in the form of "some_play.yml"
Playbook string `db:"playbook" json:"playbook"`
// WorkingDirectory is the repository-relative current directory for Ansible
// commands. It is valid only for Ansible templates.
WorkingDirectory *string `db:"working_directory" json:"working_directory,omitempty"`
// to fit into []string
Arguments *string `db:"arguments" json:"arguments,omitempty"`
// if true, semaphore will not prepend any arguments to `arguments` like inventory, etc
Expand Down Expand Up @@ -413,6 +416,18 @@ func (tpl *Template) Validate() error {
return err
}

if tpl.WorkingDirectory != nil {
if tpl.App != AppAnsible {
return common_errors.NewValidationError("template working directory is supported only for Ansible templates")
}
if strings.TrimSpace(*tpl.WorkingDirectory) == "" {
return common_errors.NewValidationError("template working directory can not be empty")
}
if err := ValidateWorkingDirectoryLexically(*tpl.WorkingDirectory); err != nil {
return err
}
}

if tpl.Arguments != nil {
if !json.Valid([]byte(*tpl.Arguments)) {
return common_errors.NewValidationError("template arguments must be valid JSON")
Expand Down
14 changes: 7 additions & 7 deletions db/sql/migration_2_19_14_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,16 @@ func TestMigration_2_19_14_DataSurvivesRebuild(t *testing.T) {
})
require.NoError(t, err)

template, err := store.CreateTemplate(db.Template{
ProjectID: projectID,
RepositoryID: repo.ID,
Name: "tpl",
Playbook: "site.yml",
})
// SqlDb.CreateTemplate writes working_directory, which appears only in 2.20.2,
// so seed the template with SQL matching the 2.19.12 schema.
templateID, err := store.insert("id",
"insert into project__template (project_id, repository_id, name, playbook, app) "+
"values (?, ?, 'tpl', 'site.yml', '')",
projectID, repo.ID)
require.NoError(t, err)

task, err := store.CreateTask(db.Task{
TemplateID: template.ID,
TemplateID: templateID,
ProjectID: projectID,
Status: "success",
Playbook: "site.yml",
Expand Down
1 change: 1 addition & 0 deletions db/sql/migrations/v2.20.2.err.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
alter table `project__template` drop column `working_directory`;
1 change: 1 addition & 0 deletions db/sql/migrations/v2.20.2.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
alter table `project__template` add column `working_directory` text null;
217 changes: 92 additions & 125 deletions db/sql/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,161 +4,127 @@ import (
"encoding/json"
"errors"

"github.com/Masterminds/squirrel"
sq "github.com/Masterminds/squirrel"
"github.com/semaphoreui/semaphore/db"
"github.com/semaphoreui/semaphore/pkg/common_errors"
log "github.com/sirupsen/logrus"
)

func (d *SqlDb) CreateTemplate(template db.Template) (newTemplate db.Template, err error) {
err = template.Validate()

func (d *SqlDb) CreateTemplate(tmpl db.Template) (db.Template, error) {
if err := tmpl.Validate(); err != nil {
return db.Template{}, err
}

tmpl.ApplyLegacyEnvironmentField()

query, args, err := sq.Insert("project__template").
SetMap(map[string]any{
"project_id": tmpl.ProjectID,
"inventory_id": tmpl.InventoryID,
"repository_id": tmpl.RepositoryID,
"name": tmpl.Name,
"playbook": tmpl.Playbook,
"working_directory": tmpl.WorkingDirectory,
"arguments": tmpl.Arguments,
"allow_override_args_in_task": tmpl.AllowOverrideArgsInTask,
"description": tmpl.Description,
"`type`": tmpl.Type,
"start_version": tmpl.StartVersion,
"build_template_id": tmpl.BuildTemplateID,
"view_id": tmpl.ViewID,
"autorun": tmpl.Autorun,
"survey_vars": db.ObjectToJSON(tmpl.SurveyVars),
"suppress_success_alerts": tmpl.SuppressSuccessAlerts,
"app": tmpl.App,
"git_branch": tmpl.GitBranch,
"runner_tag": tmpl.RunnerTag,
"task_params": tmpl.TaskParams,
"allow_override_branch_in_task": tmpl.AllowOverrideBranchInTask,
"allow_parallel_tasks": tmpl.AllowParallelTasks,
"jwt_params": tmpl.JWTParams,
"executor_image": tmpl.NormalizedExecutorImage(),
}).
ToSql()
if err != nil {
return
return db.Template{}, err
}

template.ApplyLegacyEnvironmentField()

insertID, err := d.insert(
"id",
"insert into project__template ("+
"project_id, inventory_id, repository_id, name, "+
"playbook, arguments, allow_override_args_in_task, description, `type`, "+
"start_version, build_template_id, view_id, autorun, survey_vars, "+
"suppress_success_alerts, app, git_branch, runner_tag, task_params, "+
"allow_override_branch_in_task, allow_parallel_tasks, jwt_params, executor_image)"+
"values ("+
"?, ?, ?, ?, "+
"?, ?, ?, ?, ?, "+
"?, ?, ?, ?, ?, "+
"?, ?, ?, ?, ?,"+
"?, ?, ?, ?)",
template.ProjectID,
template.InventoryID,
template.RepositoryID,
template.Name,

template.Playbook,
template.Arguments,
template.AllowOverrideArgsInTask,
template.Description,
template.Type,

template.StartVersion,
template.BuildTemplateID,
template.ViewID,
template.Autorun,
db.ObjectToJSON(template.SurveyVars),

template.SuppressSuccessAlerts,
template.App,
template.GitBranch,
template.RunnerTag,
template.TaskParams,

template.AllowOverrideBranchInTask,
template.AllowParallelTasks,
template.JWTParams,
template.NormalizedExecutorImage(),
)

tmplId, err := d.insert("id", query, args...)
if err != nil {
return
return db.Template{}, err
}

err = d.UpdateTemplateVaults(template.ProjectID, insertID, template.Vaults)
err = d.UpdateTemplateVaults(tmpl.ProjectID, tmplId, tmpl.Vaults)
if err != nil {
return
return db.Template{}, err
}

err = d.UpdateTemplateEnvironments(template.ProjectID, insertID, template.EnvironmentIDs)
err = d.UpdateTemplateEnvironments(tmpl.ProjectID, tmplId, tmpl.EnvironmentIDs)
if err != nil {
return
return db.Template{}, err
}

err = db.FillTemplate(d, &newTemplate)

if err != nil {
return
tmpl.ID = tmplId
if err = db.FillTemplate(d, &tmpl); err != nil {
return db.Template{}, err
}

newTemplate = template
newTemplate.ID = insertID

return
return tmpl, nil
}

func (d *SqlDb) UpdateTemplate(template db.Template) error {
err := template.Validate()

func (d *SqlDb) UpdateTemplate(tmpl db.Template) error {
err := tmpl.Validate()
if err != nil {
return err
}

_, err = d.exec("update project__template set "+
"inventory_id=?, "+
"repository_id=?, "+
"name=?, "+
"playbook=?, "+
"arguments=?, "+
"allow_override_args_in_task=?, "+
"description=?, "+
"`type`=?, "+
"start_version=?,"+
"build_template_id=?, "+
"view_id=?, "+
"autorun=?, "+
"survey_vars=?, "+
"suppress_success_alerts=?, "+
"app=?, "+
"`git_branch`=?, "+
"task_params=?, "+
"runner_tag=?, "+
"allow_override_branch_in_task=?, "+
"allow_parallel_tasks=?, "+
"jwt_params=?, "+
"executor_image=? "+
"where id=? and project_id=?",
template.InventoryID,
template.RepositoryID,
template.Name,
template.Playbook,
template.Arguments,
template.AllowOverrideArgsInTask,
template.Description,
template.Type,
template.StartVersion,
template.BuildTemplateID,
template.ViewID,
template.Autorun,
db.ObjectToJSON(template.SurveyVars),
template.SuppressSuccessAlerts,
template.App,
template.GitBranch,
template.TaskParams,
template.RunnerTag,
template.AllowOverrideBranchInTask,
template.AllowParallelTasks,
template.JWTParams,
template.NormalizedExecutorImage(),

template.ID,
template.ProjectID,
)
query, args, err := sq.Update("project__template").
SetMap(map[string]any{
"inventory_id": tmpl.InventoryID,
"repository_id": tmpl.RepositoryID,
"name": tmpl.Name,
"playbook": tmpl.Playbook,
"working_directory": tmpl.WorkingDirectory,
"arguments": tmpl.Arguments,
"allow_override_args_in_task": tmpl.AllowOverrideArgsInTask,
"description": tmpl.Description,
"`type`": tmpl.Type,
"start_version": tmpl.StartVersion,
"build_template_id": tmpl.BuildTemplateID,
"view_id": tmpl.ViewID,
"autorun": tmpl.Autorun,
"survey_vars": db.ObjectToJSON(tmpl.SurveyVars),
"suppress_success_alerts": tmpl.SuppressSuccessAlerts,
"app": tmpl.App,
"`git_branch`": tmpl.GitBranch,
"task_params": tmpl.TaskParams,
"runner_tag": tmpl.RunnerTag,
"allow_override_branch_in_task": tmpl.AllowOverrideBranchInTask,
"allow_parallel_tasks": tmpl.AllowParallelTasks,
"jwt_params": tmpl.JWTParams,
"executor_image": tmpl.NormalizedExecutorImage(),
}).
Where(sq.Eq{
"id": tmpl.ID,
"project_id": tmpl.ProjectID,
}).
ToSql()
if err != nil {
return err
}

err = d.UpdateTemplateVaults(template.ProjectID, template.ID, template.Vaults)
_, err = d.exec(query, args...)
if err != nil {
return err
}

template.ApplyLegacyEnvironmentField()

err = d.UpdateTemplateEnvironments(template.ProjectID, template.ID, template.EnvironmentIDs)
err = d.UpdateTemplateVaults(tmpl.ProjectID, tmpl.ID, tmpl.Vaults)
if err != nil {
return err
}

return err
tmpl.ApplyLegacyEnvironmentField()
return d.UpdateTemplateEnvironments(tmpl.ProjectID, tmpl.ID, tmpl.EnvironmentIDs)
}

func (d *SqlDb) GetTemplateEnvironments(projectID int, templateID int) (environmentIDs []int, err error) {
Expand Down Expand Up @@ -268,6 +234,7 @@ func (d *SqlDb) getTemplates(
"pt.name",
"pt.description",
"pt.playbook",
"pt.working_directory",
"pt.arguments",
"pt.allow_override_args_in_task",
"pt.build_template_id",
Expand All @@ -291,7 +258,7 @@ func (d *SqlDb) getTemplates(
fields = append(fields, "ptr.permissions permissions")
}

q := squirrel.Select(fields...).From("project__template pt")
q := sq.Select(fields...).From("project__template pt")

if userID != nil {
q = q.LeftJoin("project__user pu ON (pu.project_id = pt.project_id AND pu.user_id = ?)", *userID).
Expand Down Expand Up @@ -474,7 +441,7 @@ func (d *SqlDb) GetTemplateRefs(projectID int, templateID int) (db.ObjectReferre

func (d *SqlDb) GetTemplateRole(projectID int, templateID int, id int) (templateRole db.TemplateRolePerm, err error) {

query, args, err := squirrel.Select("*").
query, args, err := sq.Select("*").
From("project__template_role").
Where("project_id = ?", projectID).
Where("template_id = ?", templateID).
Expand Down Expand Up @@ -522,7 +489,7 @@ func (d *SqlDb) GetTemplatePermission(projectID int, templateID int, userID int)
roleSlug = role.Slug
}

query, args, err := squirrel.Select("permissions").
query, args, err := sq.Select("permissions").
From("project__template_role").
Where("project_id = ?", projectID).
Where("template_id = ?", templateID).
Expand Down Expand Up @@ -552,7 +519,7 @@ func (d *SqlDb) GetTemplatePermission(projectID int, templateID int, userID int)
}

func (d *SqlDb) GetTemplateRoles(projectID int, templateID int) (roles []db.TemplateRolePerm, err error) {
query, args, err := squirrel.Select("*").
query, args, err := sq.Select("*").
From("project__template_role").
Where("project_id = ?", projectID).
Where("template_id = ?", templateID).
Expand Down
Loading
Loading