Skip to content

Commit

Permalink
Support TF/Bash (#2077)
Browse files Browse the repository at this point in the history
Support OpenTofu and Bash
  • Loading branch information
fiftin committed Jun 12, 2024
1 parent f144075 commit aca72f0
Show file tree
Hide file tree
Showing 58 changed files with 2,498 additions and 130 deletions.
33 changes: 31 additions & 2 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ jobs:
task e2e:hooks
task e2e:test
deploy-dev:
deploy-server:
runs-on: ubuntu-latest
if: github.repository_owner == 'semaphoreui'

Expand Down Expand Up @@ -583,6 +583,35 @@ jobs:
labels: ${{ steps.server.outputs.labels }}
tags: ${{ steps.server.outputs.tags }}

deploy-runner:
runs-on: ubuntu-latest
if: github.repository_owner == 'semaphoreui'

needs:
- integrate-boltdb
- integrate-mysql
- integrate-mariadb
- integrate-postgres

steps:
- name: Checkout source
uses: actions/checkout@v4

- name: Setup qemu
id: qemu
uses: docker/setup-qemu-action@v3

- name: Setup buildx
id: buildx
uses: docker/setup-buildx-action@v3

- name: Hub login
uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}

- name: Runner meta
id: runner
uses: docker/metadata-action@v5
Expand All @@ -604,7 +633,7 @@ jobs:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: deployment/docker/runner/Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v6
platforms: linux/amd64,linux/arm64 #,linux/arm/v6
push: ${{ github.event_name != 'pull_request' }}
labels: ${{ steps.runner.outputs.labels }}
tags: ${{ steps.runner.outputs.tags }}
5 changes: 3 additions & 2 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ archives:
format: zip

signs:
- artifacts: checksum
args: ["-u", "58A7 CC3D 8A9C A2E5 BB5C 141D 4064 23EA F814 63CA", "--pinentry-mode", "loopback", "--yes", "--batch", "--output", "${signature}", "--detach-sign", "${artifact}"]
-
artifacts: checksum
args: ["-u", "A4FB 7208 48EA 79FA EC4E 9C30 0443 8136 6A5D 4731", "--pinentry-mode", "loopback", "--yes", "--batch", "--output", "${signature}", "--detach-sign", "${artifact}"]

snapshot:
name_template: "{{ .Timestamp }}-{{ .ShortCommit }}-SNAPSHOT"
Expand Down
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
The MIT License (MIT)

Copyright (c) 2014 Castaway Labs LLC
Copyright (c) 2021 Denis Gukov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
68 changes: 8 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Ansible Semaphore
# Semaphore UI


[![](https://img.shields.io/badge/Telegram-2CA5E0?style=flat-squeare&logo=telegram&logoColor=white)](https://t.me/semaphoreui)
Expand All @@ -12,26 +12,14 @@

[//]: # ([![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/fiftin))

Ansible Semaphore is a modern UI for Ansible. It lets you easily run Ansible playbooks, get notifications about fails, control access to deployment system.
Semaphore is a modern UI for Ansible, Terraform/OpenTofu, Bash and Pulumi. It lets you easily run Ansible playbooks, get notifications about fails, control access to deployment system.

If your project has grown and deploying from the terminal is no longer for you then Ansible Semaphore is what you need.
If your project has grown and deploying from the terminal is no longer for you then Semaphore UI is what you need.

![responsive-ui-phone1](https://user-images.githubusercontent.com/914224/134777345-8789d9e4-ff0d-439c-b80e-ddc56b74fcee.png)

## Installation

### Full documentation
https://docs.semui.co/administration-guide/installation

### Snap

[![semaphore](https://snapcraft.io/semaphore/badge.svg)](https://snapcraft.io/semaphore)

```bash
sudo snap install semaphore
sudo semaphore user add --admin --name "Your Name" --login your_login --email your-email@examaple.com --password your_password
```

### Docker

https://hub.docker.com/r/semaphoreui/semaphore
Expand All @@ -56,55 +44,15 @@ services:
- /path/to/data/lib:/var/lib/semaphore # database.boltdb location (Not required if using mysql or postgres)
```

### Other installation methods
https://docs.semui.co/administration-guide/installation

## Demo

You can test latest version of Semaphore on https://demo.semui.co.
You can test latest version of Semaphore on https://cloud.semui.co.

## Docs

Admin and user docs: https://docs.semui.co.

API description: https://semui.co/api-docs/.

## Contributing

If you want to write an article about Ansible or Semaphore, contact [@fiftin](https://github.com/fiftin) and we will place your article in our [Blog](https://semui.co/blog/) with link to your profile.

PR's & UX reviews are welcome!

Please follow the [contribution](https://github.com/ansible-semaphore/semaphore/blob/develop/CONTRIBUTING.md) guide. Any questions, please open an issue.

[//]: # (## Release Signing)

[//]: # ()
[//]: # (All releases after 2.5.1 are signed with the gpg public key)

[//]: # (`8CDE D132 5E96 F1D9 EABF 17D4 2C96 CF7D D27F AB82`)

## Support

If you like Ansible Semaphore, you can support the project development on [Ko-fi](https://ko-fi.com/fiftin).

## License

MIT License

Copyright (c) 2016 Castaway Consulting LLC

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
API description: https://semui.co/api-docs/.
5 changes: 5 additions & 0 deletions api/helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ func WriteErrorStatus(w http.ResponseWriter, err string, code int) {
}

func WriteError(w http.ResponseWriter, err error) {
if errors.Is(err, tasks.ErrInvalidSubscription) {
WriteErrorStatus(w, "You have no subscription.", http.StatusForbidden)
return
}

if errors.Is(err, db.ErrNotFound) {
w.WriteHeader(http.StatusNotFound)
return
Expand Down
4 changes: 3 additions & 1 deletion api/projects/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func AddInventory(w http.ResponseWriter, r *http.Request) {
}

switch inventory.Type {
case db.InventoryStatic, db.InventoryStaticYaml, db.InventoryFile:
case db.InventoryStatic, db.InventoryStaticYaml, db.InventoryFile, db.InventoryTerraformWorkspace:
break
default:
helpers.WriteJSON(w, http.StatusBadRequest, map[string]string{
Expand Down Expand Up @@ -170,6 +170,8 @@ func UpdateInventory(w http.ResponseWriter, r *http.Request) {
helpers.WriteErrorStatus(w, "Invalid inventory file pathname. Must be: path/to/inventory.", http.StatusBadRequest)
return
}
case db.InventoryTerraformWorkspace:
break
default:
helpers.WriteErrorStatus(w,
"unknown inventory type: "+string(inventory.Type),
Expand Down
8 changes: 7 additions & 1 deletion api/projects/tasks.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package projects

import (
"errors"
"github.com/ansible-semaphore/semaphore/api/helpers"
"github.com/ansible-semaphore/semaphore/db"
"github.com/ansible-semaphore/semaphore/services/tasks"
"github.com/ansible-semaphore/semaphore/util"
"github.com/gorilla/context"
log "github.com/sirupsen/logrus"
Expand All @@ -23,7 +25,11 @@ func AddTask(w http.ResponseWriter, r *http.Request) {

newTask, err := helpers.TaskPool(r).AddTask(taskObj, &user.ID, project.ID)

if err != nil {
if errors.Is(err, tasks.ErrInvalidSubscription) {
helpers.WriteErrorStatus(w, "No active subscription available.", http.StatusForbidden)
return
} else if err != nil {

util.LogErrorWithFields(err, log.Fields{"error": "Cannot write new event to database"})
w.WriteHeader(http.StatusInternalServerError)
return
Expand Down
39 changes: 39 additions & 0 deletions api/projects/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ func AddTemplate(w http.ResponseWriter, r *http.Request) {
return
}

var err error

template.ProjectID = project.ID
newTemplate, err := helpers.Store(r).CreateTemplate(template)

Expand All @@ -79,6 +81,43 @@ func AddTemplate(w http.ResponseWriter, r *http.Request) {
return
}

if newTemplate.App.IsTerraform() {
var inv db.Inventory

if newTemplate.InventoryID == nil {
inv, err = helpers.Store(r).CreateInventory(db.Inventory{
Name: newTemplate.Name + " - default",
ProjectID: project.ID,
HolderID: &newTemplate.ID,
Type: db.InventoryTerraformWorkspace,
Inventory: "default",
})

if err != nil {
helpers.WriteError(w, err)
return
}

newTemplate.InventoryID = &inv.ID
err = helpers.Store(r).UpdateTemplate(newTemplate)

} else {
inv, err = helpers.Store(r).GetInventory(project.ID, *newTemplate.InventoryID)
if err != nil {
helpers.WriteError(w, err)
return
}

inv.HolderID = &newTemplate.ID
err = helpers.Store(r).UpdateInventory(inv)
}

if err != nil {
helpers.WriteError(w, err)
return
}
}

helpers.EventLog(r, helpers.EventLogCreate, helpers.EventLogItem{
UserID: helpers.UserFromContext(r).ID,
ProjectID: project.ID,
Expand Down
2 changes: 1 addition & 1 deletion api/users.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package api

import (
log "github.com/sirupsen/logrus"
"github.com/ansible-semaphore/semaphore/api/helpers"
"github.com/ansible-semaphore/semaphore/db"
log "github.com/sirupsen/logrus"
"net/http"

"github.com/ansible-semaphore/semaphore/util"
Expand Down
11 changes: 5 additions & 6 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ package cmd

import (
"fmt"
"net/http"
"os"
"strings"

"github.com/ansible-semaphore/semaphore/api"
"github.com/ansible-semaphore/semaphore/api/sockets"
"github.com/ansible-semaphore/semaphore/db"
Expand All @@ -17,14 +13,17 @@ import (
"github.com/gorilla/handlers"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"net/http"
"os"
"strings"
)

var configPath string

var rootCmd = &cobra.Command{
Use: "semaphore",
Short: "Ansible Semaphore is a beautiful web UI for Ansible",
Long: `Ansible Semaphore is a beautiful web UI for Ansible.
Short: "Semaphore UI is a beautiful web UI for Ansible",
Long: `Semaphore UI is a beautiful web UI for Ansible.
Source code is available at https://github.com/ansible-semaphore/semaphore.
Complete documentation is available at https://ansible-semaphore.com.`,
Run: func(cmd *cobra.Command, args []string) {
Expand Down
4 changes: 3 additions & 1 deletion db/Inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ const (
InventoryStatic InventoryType = "static"
InventoryStaticYaml InventoryType = "static-yaml"
// InventoryFile means that it is path to the Ansible inventory file
InventoryFile InventoryType = "file"
InventoryFile InventoryType = "file"
InventoryTerraformWorkspace InventoryType = "terraform-workspace"
InventoryTofuWorkspace InventoryType = "tofu-workspace"
)

// Inventory is the model of an ansible inventory file
Expand Down
12 changes: 10 additions & 2 deletions db/Template.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,17 @@ const (
type TemplateApp string

const (
TemplateAnsible = ""
TemplateAnsible TemplateApp = ""
TemplateTerraform TemplateApp = "terraform"
TemplateTofu TemplateApp = "tofu"
TemplateBash TemplateApp = "bash"
TemplatePulumi TemplateApp = "pulumi"
)

func (t TemplateApp) IsTerraform() bool {
return t == TemplateTerraform || t == TemplateTofu
}

type SurveyVarType string

const (
Expand Down Expand Up @@ -102,7 +110,7 @@ func (tpl *Template) Validate() error {
return &ValidationError{"template name can not be empty"}
}

if tpl.Playbook == "" {
if !tpl.App.IsTerraform() && tpl.Playbook == "" {
return &ValidationError{"template playbook can not be empty"}
}

Expand Down
2 changes: 1 addition & 1 deletion db/sql/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (d *SqlDb) SetOption(key string, value string) error {

func (d *SqlDb) getOption(key string) (value string, err error) {
q := squirrel.Select("*").
From(db.OptionProps.TableName).
From("`"+db.OptionProps.TableName+"`").
Where("`key`=?", key)

query, args, err := q.ToSql()
Expand Down
20 changes: 20 additions & 0 deletions db_lib/AppFactory.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,26 @@ func CreateApp(template db.Template, repository db.Repository, logger task_logge
Logger: logger,
},
}
case db.TemplateTerraform:
return &TerraformApp{
Template: template,
Repository: repository,
Logger: logger,
Name: TerraformAppTerraform,
}
case db.TemplateTofu:
return &TerraformApp{
Template: template,
Repository: repository,
Logger: logger,
Name: TerraformAppTofu,
}
case db.TemplateBash:
return &BashApp{
Template: template,
Repository: repository,
Logger: logger,
}
default:
panic("unknown app")
}
Expand Down
Loading

0 comments on commit aca72f0

Please sign in to comment.