",
18 | "redirect_url": "https://semaphore.test.com/api/auth/oidc/azure/redirect"
19 | }
20 | }
21 | }
22 | ```
--------------------------------------------------------------------------------
/src/administration-guide/openid/gitea.md:
--------------------------------------------------------------------------------
1 |
2 |
OpenID
3 | → Gitea config
4 |
5 |
6 | # Gitea config
7 |
8 | `config.json`:
9 | ```json
10 | "oidc_providers": {
11 | "github": {
12 | "icon": "github",
13 | "display_name": "Sign in with gitea instance",
14 | "client_id": "123-456-789",
15 | "client_secret": "**********",
16 | "redirect_url": "https://your-semaphore.tld/api/auth/oidc/github/redirect",
17 | "endpoint": {
18 | "auth": "https://your-gitea.tld/login/oauth/authorize",
19 | "token": "https://your-gitea.tld/login/oauth/access_token",
20 | "userinfo": "https://your-gitea.tld/api/v1/user"
21 | },
22 | "scopes": ["read:user", "user:email"],
23 | "username_claim": "login",
24 | "email_claim": "email",
25 | "name_claim": "full_name",
26 | "order": 1
27 | }
28 | }
29 | ```
30 |
31 | In your `gitea` instance, go to `https://your-gitea.tld/user/settings/applications` and create a new `oauth2` application.
32 | As redirect URI use `https://your-semaphore.tld/api/auth/oidc/github/redirect`.
33 |
34 | Authentication works fine. But "Name" and "Username" does not recieved correctly. The username will be a unique ID in semaphore and the name will be set to "Anonymous", which is changeable by the user itself. The emails is mapped correctly.
35 |
--------------------------------------------------------------------------------
/src/administration-guide/openid/github.md:
--------------------------------------------------------------------------------
1 |
2 |
OpenID
3 | → GitHub config
4 |
5 |
6 | # GitHub config
7 |
8 | `config.json`:
9 | ```json
10 | {
11 | "oidc_providers": {
12 | "github": {
13 | "icon": "github",
14 | "display_name": "Sign in with GitHub",
15 | "client_id": "***",
16 | "client_secret": "***",
17 | "redirect_url": "https://your-domain.com/api/auth/oidc/github/redirect",
18 | "endpoint": {
19 | "auth": "https://github.com/login/oauth/authorize",
20 | "token": "https://github.com/login/oauth/access_token",
21 | "userinfo": "https://api.github.com/user"
22 | },
23 | "scopes": ["read:user", "user:email"],
24 | "username_claim": "|",
25 | "email_claim": "email | {{ .id }}@github.your-domain.com",
26 | "name_claim": "name",
27 | "order": 1
28 | }
29 | }
30 | }
31 | ```
--------------------------------------------------------------------------------
/src/administration-guide/openid/gitlab.md:
--------------------------------------------------------------------------------
1 |
2 |
OpenID
3 | → GitLab config
4 |
5 |
6 | # GitLab config
7 |
8 | `config.json`:
9 | ```json
10 | {
11 | "oidc_providers": {
12 | "gitlab": {
13 | "display_name": "Sign in with GitLab",
14 | "color": "orange",
15 | "icon": "gitlab",
16 | "provider_url": "https://gitlab.com",
17 | "client_id": "***",
18 | "client_secret": "gloas-***",
19 | "redirect_url": "https://your-domain.com/api/auth/oidc/gitlab/redirect",
20 | "username_claim": "|",
21 | "order": 3
22 | }
23 | }
24 | }
25 | ```
26 |
27 | Tutorial in Semaphore UI blog: [GitLab authentication in Semaphore UI](https://semaphoreui.com/blog/openid-authentication/).
--------------------------------------------------------------------------------
/src/administration-guide/openid/google.md:
--------------------------------------------------------------------------------
1 |
2 |
OpenID
3 | → Google config
4 |
5 |
6 | # Google config
7 |
8 | `config.json`:
9 | ```json
10 | {
11 | "oidc_providers": {
12 | "google": {
13 | "color": "blue",
14 | "icon": "google",
15 | "display_name": "Sign in with Google",
16 | "provider_url": "https://accounts.google.com",
17 | "client_id": "***.apps.googleusercontent.com",
18 | "client_secret": "GOCSPX-***",
19 | "redirect_url": "https://your-domain.com/api/auth/oidc/google/redirect",
20 | "username_claim": "|",
21 | "name_claim": "name",
22 | "order": 2
23 | }
24 | }
25 | }
26 | ```
--------------------------------------------------------------------------------
/src/administration-guide/openid/keycloak.md:
--------------------------------------------------------------------------------
1 |
2 |
OpenID
3 | → Keycloak config
4 |
5 |
6 | # Keycloak config
7 |
8 | `config.json`:
9 |
10 | ```yaml
11 | {
12 | "oidc_providers": {
13 | "keycloak": {
14 | "display_name": "Sign in with keycloak",
15 | "provider_url": "https://keycloak.example.com/realms/master",
16 | "client_id": "***",
17 | "client_secret": "***",
18 | "redirect_url": "https://semaphore.example.com/api/auth/oidc/keycloak/redirect"
19 | }
20 | }
21 | }
22 | ```
23 |
24 |
25 | ## Related GitHub Issues
26 |
27 | * [#2308](https://github.com/semaphoreui/semaphore/issues/2308) — How to disable certificate validation for Keycloak server
28 | * [#2314](https://github.com/semaphoreui/semaphore/issues/2314) — Option to disable TLS verification
29 | * [#1496](https://github.com/semaphoreui/semaphore/issues/1496) — Log out from Keycloak session when logging out from Semaphore
30 |
31 | [Explore all Keycloak-related issues →](https://github.com/semaphoreui/semaphore/issues?q=is%3Aissue%20keycloak)
32 |
33 | ## Related GitHub Discussions
34 |
35 | * [#1745](https://github.com/semaphoreui/semaphore/discussions/1745) — Username differs from `preferred_username` in OpenID
36 | * [#1030](https://github.com/semaphoreui/semaphore/discussions/1030) — SAML support?
37 |
38 | [Explore all Keycloak-related discussions →](https://github.com/semaphoreui/semaphore/discussions?discussions_q=keycloak)
--------------------------------------------------------------------------------
/src/administration-guide/openid/okta.md:
--------------------------------------------------------------------------------
1 |
2 |
OpenID
3 | → Okta config
4 |
5 |
6 | # Okta config
7 |
8 | `config.json`:
9 |
10 | ```yaml
11 | {
12 | "oidc_providers": {
13 | "okta": {
14 | "display_name":"Sign in with Okta",
15 | "provider_url":"https://trial-776xxxx.okta.com/oauth2/default",
16 | "client_id":"***",
17 | "client_secret":"***",
18 | "redirect_url":"https://semaphore.example.com/api/auth/oidc/okta/redirect/"
19 | }
20 | }
21 | }
22 | ```
23 |
24 |
25 | ## Related GitHub Issues
26 |
27 | * [#1434](https://github.com/semaphoreui/semaphore/issues/1434) — Help with OIDC Azure AD configuration/debugging
28 | * [#1864](https://github.com/semaphoreui/semaphore/issues/1864) — v2.9.56 breaks oidc auth with keycloak
29 | * [#1329](https://github.com/semaphoreui/semaphore/issues/1329) — testing oidc_providers
30 |
31 | [Explore all Keycloak-related issues →](https://github.com/semaphoreui/semaphore/issues?q=is%3Aissue%20okta)
32 |
33 | ## Related GitHub Discussions
34 |
35 | * [#2822](https://github.com/semaphoreui/semaphore/discussions/2822) — When setting up GitHub OpenID, parsing is not possible except for Email
36 | * [#1030](https://github.com/semaphoreui/semaphore/discussions/1030) — SAML support?
37 |
38 | [Explore all Keycloak-related discussions →](https://github.com/semaphoreui/semaphore/discussions?discussions_q=okta)
--------------------------------------------------------------------------------
/src/administration-guide/runners.md:
--------------------------------------------------------------------------------
1 | # Runners
2 |
3 | Runners enable running tasks on a separate server from Semaphore UI.
4 |
5 | Semaphore runners operate on the same principle as GitLab or GitHub Actions runners:
6 |
7 | - You launch a runner on a separate server, specifying the Semaphore server's address and an authentication token.
8 | - The runner connects to Semaphore and signals its readiness to accept tasks.
9 | - When a new task appears, Semaphore provides all the necessary information to the runner, which, in turn, clones the repository and runs Ansible, Terraform, PowerShell, etc.
10 | - The runner sends the task execution results back to Semaphore.
11 |
12 | For end users, working with Semaphore with or without runners appears the same.
13 |
14 | Using runners offers the following advantages:
15 | - Executing tasks more securely. For instance, a runner can be located within a closed subnet or isolated docker container.
16 | - Distributing the workload across multiple servers. You can start multiple runners, and tasks will be randomly distributed among them.
17 |
18 | ## Set up
19 |
20 | ### Set up a server
21 |
22 | To set up the server for working with running you should add following option to your Semaphore server configuration:
23 |
24 | ```json
25 | {
26 | "use_remote_runner": true,
27 | "runner_registration_token": "long string of random characters"
28 | }
29 | ```
30 |
31 | or with using environment variables:
32 |
33 | ```bash
34 | SEMAPHORE_USE_REMOTE_RUNNER=True
35 | SEMAPHORE_RUNNER_REGISTRATION_TOKEN=long_string_of_random_characters
36 | ```
37 |
38 | ### Setup a runner
39 |
40 | To set up the runner, use the following command:
41 |
42 | ```bash
43 | semaphore runner setup --config /path/to/your/config/file.json
44 | ```
45 |
46 | This command will create a configuration file at `/path/to/your/config/file.json`.
47 |
48 | But before using this command, you need to understand how runners are registered on the server.
49 |
50 | ### Registering the runner on the server
51 |
52 | There are two ways to register a runner on the Semaphore server:
53 | 1) Add it via the web interface or API.
54 | 2) Use the command line with the `semaphore runner register` command.
55 |
56 | #### Adding the runner via the web UI
57 |
58 |
59 |
60 | #### Registering via CLI
61 |
62 | To register a runner this way, you need to add the `runner_registration_token` option to your Semaphore server's configuration file. This option should be set to an arbitrary string. Choose a sufficiently complex string to avoid security issues.
63 |
64 | When the `semaphore runner setup` command asks if you have a Runner token, answer No. Then use the following command to register the runner:
65 |
66 | `semaphore runner register --config /path/to/your/config/file.json`
67 |
68 | or
69 |
70 | `echo REGISTRATION_TOKEN | semaphore runner register --stdin-registration-token --config /path/to/your/config/file.json`
71 |
72 | ### Configuration file
73 |
74 | As a result of running the `semaphore runner setup` command, a configuration file like the following will be created:
75 |
76 | ```json
77 | {
78 | "tmp_path": "/tmp/semaphore",
79 | "web_host": "https://semaphore_server_host",
80 |
81 | // Here you can provide other settings, for example: git_client, ssh_config_path, etc.
82 | // ...
83 |
84 | // Runner specific options
85 | "runner": {
86 | "token": "your runner's token",
87 | // or
88 | "token_file": "path/to/the/file/where/runner/saves/token"
89 |
90 | // Here you can provide other runner-specific options,
91 | // which will be used for runner registration, for example:
92 | // max_parallel_tasks, webhook, one_off, etc.
93 | // ...
94 | }
95 | }
96 | ```
97 |
98 | You can manually edit this file without needing to call `semaphore runner setup` again.
99 |
100 | To re-register the runner, you can use the `semaphore runner register` command. This will overwrite the token in the file specified in the configuration.
101 |
102 | ## Running the runner
103 |
104 | Now you can start the runner with the command:
105 |
106 | ```
107 | semaphore runner start --config /path/to/your/config/file.json
108 | ```
109 |
110 | Your runner is ready to execute tasks ;)
111 |
112 | ## Runner unregistaration
113 |
114 | You can remove runner using the web interfance.
115 |
116 |
117 |
118 | ---
119 |
120 | Or unregister runner via CLI:
121 |
122 | ```
123 | semaphore runner unregister --config /path/to/your/config/file.json
124 | ```
125 |
126 | ## Security
127 |
128 | Data transfer security is ensured by using asymmetric encryption: the server encrypts data using a public key, the runner decrypts it using a private key.
129 |
130 | Public and private keys are generated automatically when the runner registers on the server.
131 |
132 |
133 | Use the HTTPS protocol for communication between the server and the runner, especially if they are not on the same private network.
134 |
135 |
--------------------------------------------------------------------------------
/src/administration-guide/security.md:
--------------------------------------------------------------------------------
1 | # 🔐 Security
2 |
3 | ## Introduction
4 |
5 | Security is a top priority in Semaphore UI. Whether you're automating critical infrastructure tasks or managing team access to sensitive systems, Semaphore UI is designed to provide robust, secure operations out of the box. This section outlines how Semaphore handles security and what you should consider when deploying it in production.
6 |
7 | ## Authentication & authorization
8 |
9 | Semaphore supports secure authentication and flexible authorization mechanisms:
10 |
11 | - **Login methods:**
12 | - **Username/password**
Default method using credentials stored in the Semaphore database. Passwords are hashed using a strong algorithm (bcrypt).
13 |
14 | - **LDAP**
Allows integration with enterprise directory services. Supports user/group filtering and secure connections via LDAPS.
15 |
16 | - **OpenID Connect (OIDC)**
Enables single sign-on with identity providers like Google, Azure AD, or Keycloak. Supports custom claims and group mappings.
17 |
18 | - **Two-Factor authentication (2FA)**
TOTP-based 2FA is available and recommended for all users.
19 |
20 | - **Role-based access control**
You can assign different roles to users such as Admin, Maintainer, or Viewer, limiting access based on responsibility.
21 |
22 | - **Session management**
Sessions are protected with secure HTTP cookies. Session expiration and logout mechanisms ensure minimal exposure.
23 |
24 |
25 | ## Secrets & credentials
26 |
27 | Managing secrets securely is a core feature:
28 |
29 | - **Encrypted key store**
Credentials and secret variables are encrypted at rest using AES encryption.
30 |
31 | - **Environment isolation**
Secrets are only passed to jobs at runtime and are not exposed to the container environment directly.
32 |
33 | - **SSH keys and tokens**
Users are responsible for uploading valid SSH keys and tokens. These are encrypted and only used when running tasks.
34 |
35 |
36 | ## Running untrusted code / playbooks
37 |
38 | Semaphore runs user-defined playbooks and commands, which can be risky:
39 |
40 | - **Container isolation**
Tasks are executed in isolated Docker containers. These containers have no access to the host system.
41 |
42 | - **Least privilege**
Containers run with minimal permissions and can be restricted further using Docker flags.
43 |
44 | - **Chroot execution**
Semaphore can execute tasks inside a chroot jail to further isolate the execution environment from the host system.
45 |
46 | - **Task process user**
Tasks can be executed under a dedicated non-root system user (e.g., `semaphore`) to reduce the impact of potential exploits. This is optional and can be configured based on system policies.
47 |
48 |
49 | ## Secure Deployment
50 |
51 | To ensure Semaphore is securely deployed:
52 |
53 | - **Use HTTPS**
54 | Semaphore supports HTTPS both via its **built-in TLS support** and through a **reverse proxy like Nginx**. It is strongly recommended to enable HTTPS in production.
55 |
56 | To enable built-in HTTPS support add following block to **config.json**:
57 | ```json
58 | {
59 | ...
60 | "tls": {
61 | "enabled": true,
62 | "cert_file": "/path/to/cert/example.com.cert",
63 | "key_file": "/path/to/key/example.com.key"
64 | }
65 | ...
66 | }
67 | ```
68 |
69 | - **Run behind a firewall**
Limit access to the Semaphore UI and database to only trusted IPs.
70 |
71 | - **Database security**
Use strong passwords and restrict database access to Semaphore only.
72 |
73 | ## Updates & patch management
74 |
75 | Security updates are published regularly:
76 |
77 | - **Stay updated**
Always use the latest stable release.
78 |
79 | - **Changelog**
Review changes on GitHub before updating.
80 |
81 | - **Automatic updates**
If using Docker, consider automation pipelines for regular updates.
82 |
83 |
90 |
91 |
98 |
99 |
105 |
106 |
113 |
114 | ## Reporting Vulnerabilities
115 |
116 | Found a vulnerability? Help us keep Semaphore secure:
117 |
118 | - **Responsible disclosure**
Please email us at `security@semaphoreui.com`.
119 |
120 | - **No public exploits**
Do not share vulnerabilities publicly until patched.
121 |
122 | - **Acknowledgments**
Security researchers may be acknowledged in release notes if desired.
123 |
124 |
--------------------------------------------------------------------------------
/src/administration-guide/security/apache.md:
--------------------------------------------------------------------------------
1 |
2 | Administration Guide
3 | →
Security
4 | → Apache config
5 |
6 |
7 |
8 | # Apache config
9 |
10 | Make sure you have enabled following Apache modules:
11 |
12 | ```bash
13 | sudo a2enmod proxy
14 | sudo a2enmod proxy_http
15 | sudo a2enmod proxy_wstunnel
16 | ```
17 |
18 | Add following virtual host to your Apache configuration:
19 |
20 | ```
21 |
22 |
23 | ServerName example.com
24 |
25 | ServerAdmin webmaster@localhost
26 |
27 | SSLEngine on
28 | SSLCertificateFile /path/to/example.com.crt
29 | SSLCertificateKeyFile /path/to/example.com.key
30 |
31 | ProxyPreserveHost On
32 |
33 |
34 | ProxyPass http://127.0.0.1:3000/
35 | ProxyPassReverse http://127.0.0.1:3000/
36 |
37 |
38 |
39 | RewriteCond %{HTTP:Connection} Upgrade [NC]
40 | RewriteCond %{HTTP:Upgrade} websocket [NC]
41 |
42 | ProxyPass ws://127.0.0.1:3000/api/ws/
43 | ProxyPassReverse ws://127.0.0.1:3000/api/ws/
44 |
45 |
46 |
47 | ```
48 |
--------------------------------------------------------------------------------
/src/administration-guide/security/database.md:
--------------------------------------------------------------------------------
1 |
2 | Administration Guide
3 | →
Security
4 | → Database security
5 |
6 |
7 | # Database security
8 |
9 | ## Data encryption
10 |
11 | Sensitive data is stored in the database, in an encrypted form. You should set the configuration option `access_key_encryption` in configuration file to enable Access Keys encryption. It must be generated by command:
12 |
13 | ```bash
14 | head -c32 /dev/urandom | base64
--------------------------------------------------------------------------------
/src/administration-guide/security/kerberos.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/administration-guide/security/kerberos.md
--------------------------------------------------------------------------------
/src/administration-guide/security/network.md:
--------------------------------------------------------------------------------
1 |
2 | Administration Guide
3 | →
Security
4 | → Database security
5 |
6 |
7 | # Network security
8 |
9 | For security reasons, Semaphore **should not be used** over unencrypted HTTP!
10 |
11 | Why use encrypted connections? See: [Article from Cloudflare](https://www.cloudflare.com/learning/ssl/why-use-https/).
12 |
13 | Options you have:
14 |
15 | * [VPN](#vpn)
16 | * [SSL](#ssl)
17 |
18 | ---
19 |
20 | ## VPN
21 |
22 | You can use a Client-to-Site VPN, that terminates on the Semaphore server, to encrypt & secure the connection.
23 |
24 | ## SSL
25 |
26 | Semaphore supports SSL/TLS starting from v2.12.
27 |
28 | **config.json**:
29 | ```json
30 | {
31 | ...
32 | "tls": {
33 | "enabled": true,
34 | "cert_file": "/path/to/cert/example.com.cert",
35 | "key_file": "/path/to/key/example.com.key"
36 | }
37 | ...
38 | }
39 | ```
40 |
41 | Or environment varibles (useful for Docker):
42 |
43 | ```bash
44 | export SEMAPHORE_TLS_ENABLED=True
45 | export SEMAPHORE_TLS_CERT_FILE=/path/to/cert/example.com.cert
46 | export SEMAPHORE_TLS_KEY_FILE=/path/to/key/example.com.key
47 | ```
48 |
49 | Alternatively, you can use a reverse proxy in front of Semaphore to handle secure connections. For example:
50 |
51 | * [NGINX](./nginx.md)
52 | * [Apache](./apache.md)
53 |
54 |
55 | ### Self-signed SSL certificate
56 |
57 | You can generate your own SSL certificate with using `openssl` CLI tool:
58 |
59 | ```
60 | openssl req -x509 -newkey rsa:4096 \
61 | -keyout key.pem -out cert.pem \
62 | -sha256 -days 3650 -nodes \
63 | -subj "/C=US/ST=California/L=San Francisco/O=CompanyName/OU=DevOps/CN=example.com"
64 | ```
65 |
66 | ### Let's Encrypt SSL certificate
67 |
68 | You can use [Certbot](https://certbot.eff.org/) to generate and automatically renew a Let's Encrypt SSL certificate.
69 |
70 | Example for Apache:
71 |
72 | ```bash
73 | sudo snap install certbot
74 | sudo certbot --apache -n --agree-tos -d example.com -m mail@example.com
75 | ```
76 |
77 | ### Others
78 |
79 | If you want to use any other reverse proxy - make sure to also forward websocket connections on the `/api/ws` route!
80 |
--------------------------------------------------------------------------------
/src/administration-guide/security/nginx.md:
--------------------------------------------------------------------------------
1 |
2 | Administration Guide
3 | →
Security
4 | → Nginx config
5 |
6 |
7 | # Nginx config
8 |
9 |
10 |
11 | Configuration example:
12 |
13 | ```yaml
14 | server {
15 | listen 443 ssl;
16 | server_name example.com;
17 |
18 | # add Strict-Transport-Security to prevent man in the middle attacks
19 | add_header Strict-Transport-Security "max-age=31536000" always;
20 |
21 | # SSL
22 | ssl_certificate /etc/nginx/cert/cert.pem;
23 | ssl_certificate_key /etc/nginx/cert/privkey.pem;
24 |
25 | # Recommendations from
26 | # https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
27 | ssl_protocols TLSv1.1 TLSv1.2;
28 | ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
29 | ssl_prefer_server_ciphers on;
30 | ssl_session_cache shared:SSL:10m;
31 |
32 | # required to avoid HTTP 411: see Issue #1486
33 | # (https://github.com/docker/docker/issues/1486)
34 | chunked_transfer_encoding on;
35 |
36 | location / {
37 | proxy_pass http://127.0.0.1:3000/;
38 | proxy_set_header Host $http_host;
39 | proxy_set_header X-Real-IP $remote_addr;
40 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
41 |
42 | proxy_set_header X-Forwarded-Proto $scheme;
43 |
44 | proxy_buffering off;
45 | proxy_request_buffering off;
46 | }
47 |
48 | location /api/ws {
49 | proxy_pass http://127.0.0.1:3000/api/ws;
50 | proxy_http_version 1.1;
51 | proxy_set_header Upgrade $http_upgrade;
52 | proxy_set_header Connection "upgrade";
53 | proxy_set_header Origin "";
54 | }
55 | }
56 | ```
--------------------------------------------------------------------------------
/src/administration-guide/troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
2 |
3 | ## Renner prints error 404
4 |
5 | ### How to fix
6 |
7 | [Getting 401 error code from Runner](https://github.com/semaphoreui/semaphore/discussions/1873?converting=1)
8 |
9 | ---
10 |
11 | ## Gathering Facts issue for localhost
12 |
13 | The issue can occur on Semaphore UI installed via [Snap](https://snapcraft.io/semaphore) or [Docker](https://hub.docker.com/r/semaphoreui/semaphore).
14 |
15 | ```
16 | 4:10:16 PM
17 | TASK [Gathering Facts] *********************************************************
18 | 4:10:17 PM
19 | fatal: [localhost]: FAILED! => changed=false
20 | ```
21 |
22 | ### Why this happens
23 |
24 | For more information about localhost use in Ansible, read this article [Implicit 'localhost'](https://docs.ansible.com/ansible/latest/inventory/implicit_localhost.html).
25 |
26 | Ansible tries to gather facts locally, but Ansible is located in a limited isolated container which doesn't allow this.
27 |
28 | ### How to fix this
29 |
30 | There are two ways:
31 |
32 | 1. Disable facts gathering:
33 |
34 | ```yaml
35 | - hosts: localhost
36 | gather_facts: False
37 | roles:
38 | - ...
39 | ```
40 |
41 | 2. Explicitly set the connection type to **ssh**:
42 | ```
43 | [localhost]
44 | 127.0.0.1 ansible_connection=ssh ansible_ssh_user=your_localhost_user
45 | ```
46 | ---
47 | ## panic: pq: SSL is not enabled on the server
48 |
49 | This means that your Postgres doesn't work by SSL.
50 |
51 | ### How to fix this
52 |
53 | Add option `sslmode=disable` to the configuration file:
54 |
55 | ```json
56 | "postgres": {
57 | "host": "localhost",
58 | "user": "pastgres",
59 | "pass": "pwd",
60 | "name": "semaphore",
61 | "options": {
62 | "sslmode": "disable"
63 | }
64 | },
65 | ```
66 | ---
67 | ## fatal: bad numeric config value '0' for 'GIT_TERMINAL_PROMPT': invalid unit
68 |
69 | This means that you are trying to access a repository over HTTPS that requires authentication.
70 |
71 | ### How to fix this
72 |
73 | * Go to **Key Store** screen.
74 | * Create a new key `Login with password` type.
75 | * Specify your login for GitHub/BitBucket/etc.
76 | * Specify the password. You can't use your account password for GitHub/BitBucket, you should use a Personal Access Token (PAT) instead of it. Read more [here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token).
77 | * After creating the key, go to the **Repositories** screen, find your repository and specify the key.
78 |
79 | ---
80 |
81 | ## unable to read LDAP response packet: unexpected EOF
82 |
83 | Most likely, you are trying to connect to the LDAP server using an insecure method, although it expects a secure connection (via TLS).
84 |
85 | ### How to fix this
86 |
87 | Enable TLS in your `config.json` file:
88 |
89 | ```json
90 | ...
91 | "ldap_needtls": true
92 | ...
93 | ```
94 |
95 | ---
96 |
97 | ## LDAP Result Code 49 "Invalid Credentials"
98 |
99 | You have the wrong password or `binddn`.
100 |
101 | ### How to fix this
102 |
103 | Use `ldapwhoami` tool and check if your binddn works:
104 |
105 | ```bash
106 | ldapwhoami\
107 | -H ldap://ldap.com:389\
108 | -D "CN=/your/ldap_binddn/value/in/config/file"\
109 | -x\
110 | -W
111 | ```
112 |
113 | It will ask interactively for the password and should return code **0** and echo out the **DN** as specified.
114 |
115 | You also can read the following articles:
116 | * [ldapsearch: Invalid credentials (49)](https://serverfault.com/q/771549/443463)
117 | * [https://github.com/semaphoreui/semaphore/issues/906](https://github.com/semaphoreui/semaphore/issues/906)
118 |
119 | ---
120 |
121 | ## LDAP Result Code 32 "No Such Object"
122 |
123 | Coming soon.
124 |
--------------------------------------------------------------------------------
/src/administration-guide/upgrading.md:
--------------------------------------------------------------------------------
1 | # Upgrading
2 |
3 | There are 4 ways for upgrading Semaphore:
4 |
5 | * Snap
6 | * Package manager
7 | * Docker
8 | * Binary
9 |
10 | ### Snap
11 |
12 | Use the following command for upgrading Semaphore to the latest stable version:
13 |
14 | ```
15 | sudo snap refresh semaphore
16 | ```
17 |
18 | ### Package manager
19 |
20 | Download a package file from [Releases page](https://github.com/semaphoreui/semaphore/releases).
21 |
22 | `*.deb` for Debian and Ubuntu, `*.rpm` for CentOS and RedHat.
23 |
24 | Install it using the package manager.
25 |
26 | {{#tabs }}
27 | {{#tab name="Debian / Ubuntu (x64)" }}
28 | ```
29 | wget https://github.com/semaphoreui/semaphore/releases/\
30 |
31 | download/v2.13.14/semaphore_2.13.14_linux_amd64.deb
32 |
33 | sudo dpkg -i semaphore_2.13.14_linux_amd64.deb
34 | ```
35 | {{#endtab }}
36 |
37 | {{#tab name="Debian / Ubuntu (ARM64)" }}
38 | ```
39 | wget https://github.com/semaphoreui/semaphore/releases/\
40 |
41 | download/v2.13.14/semaphore_2.13.14_linux_arm64.deb
42 |
43 | sudo dpkg -i semaphore_2.13.14_linux_arm64.deb
44 | ```
45 | {{#endtab }}
46 |
47 | {{#tab name="CentOS (x64)" }}
48 | ```
49 | wget https://github.com/semaphoreui/semaphore/releases/\
50 |
51 | download/v2.13.14/semaphore_2.13.14_linux_amd64.rpm
52 |
53 | sudo yum install semaphore_2.13.14_linux_amd64.rpm
54 | ```
55 | {{#endtab }}
56 |
57 | {{#tab name="CentOS (ARM64)" }}
58 | ```
59 | wget https://github.com/semaphoreui/semaphore/releases/\
60 |
61 | download/v2.13.14/semaphore_2.13.14_linux_arm64.rpm
62 |
63 | sudo yum install semaphore_2.13.14_linux_arm64.rpm
64 | ```
65 | {{#endtab }}
66 | {{#endtabs }}
67 |
68 | ### Docker
69 |
70 |
71 | Coming soon
72 |
73 |
74 | ### Binary
75 |
76 | Download a `*.tar.gz` for your platform from [Releases page](https://github.com/semaphoreui/semaphore/releases). Unpack the binary to the directory where your old Semaphore binary is located.
77 |
78 | {{#tabs }}
79 | {{#tab name="Linux (x64)" }}
80 | ```
81 | wget https://github.com/semaphoreui/semaphore/releases/\
82 |
83 | download/v2.13.14/semaphore_2.13.14_linux_amd64.tar.gz
84 |
85 | tar xf semaphore_2.13.14_linux_amd64.tar.gz
86 | ```
87 | {{#endtab }}
88 |
89 | {{#tab name="Linux (ARM64)" }}
90 | ```
91 | wget https://github.com/semaphoreui/semaphore/releases/\
92 |
93 | download/v2.13.14/semaphore_2.13.14_linux_arm64.tar.gz
94 |
95 | tar xf semaphore_2.13.14_linux_arm64.tar.gz
96 | ```
97 | {{#endtab }}
98 |
99 | {{#tab name="Windows (x64)" }}
100 | ```
101 | Invoke-WebRequest `
102 | -Uri ("https://github.com/semaphoreui/semaphore/releases/" +
103 | "download/v2.13.14/semaphore_2.13.14_windows_amd64.zip") `
104 | -OutFile semaphore.zip
105 |
106 | Expand-Archive -Path semaphore.zip -DestinationPath ./
107 | ```
108 | {{#endtab }}
109 | {{#endtabs }}
110 |
111 |
--------------------------------------------------------------------------------
/src/faq/troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
2 |
3 | ## 1. Renner prints error 404
4 |
5 | ### How to fix
6 |
7 | [Getting 401 error code from Runner](https://github.com/semaphoreui/semaphore/discussions/1873?converting=1)
8 |
9 | ---
10 |
11 | ## 2. Gathering Facts issue for localhost
12 |
13 | The issue can occur on Semaphore UI installed via [Snap](https://snapcraft.io/semaphore) or [Docker](https://hub.docker.com/r/semaphoreui/semaphore).
14 |
15 | ```
16 | 4:10:16 PM
17 | TASK [Gathering Facts] *********************************************************
18 | 4:10:17 PM
19 | fatal: [localhost]: FAILED! => changed=false
20 | ```
21 |
22 | ### Why this happens
23 |
24 | For more information about localhost use in Ansible, read this article [Implicit 'localhost'](https://docs.ansible.com/ansible/latest/inventory/implicit_localhost.html).
25 |
26 | Ansible tries to gather facts locally, but Ansible is located in a limited isolated container which doesn't allow this.
27 |
28 | ### How to fix this
29 |
30 | There are two ways:
31 |
32 | 1. Disable facts gathering:
33 |
34 | ```yaml
35 | - hosts: localhost
36 | gather_facts: False
37 | roles:
38 | - ...
39 | ```
40 |
41 | 2. Explicitly set the connection type to **ssh**:
42 | ```
43 | [localhost]
44 | 127.0.0.1 ansible_connection=ssh ansible_ssh_user=your_localhost_user
45 | ```
46 | ---
47 | ## 4. panic: pq: SSL is not enabled on the server
48 |
49 | This means that your Postgres doesn't work by SSL.
50 |
51 | ### How to fix this
52 |
53 | Add option `sslmode=disable` to the configuration file:
54 |
55 | ```json
56 | "postgres": {
57 | "host": "localhost",
58 | "user": "pastgres",
59 | "pass": "pwd",
60 | "name": "semaphore",
61 | "options": {
62 | "sslmode": "disable"
63 | }
64 | },
65 | ```
66 |
67 |
68 | ---
69 |
70 |
71 | ## 5. fatal: bad numeric config value '0' for 'GIT_TERMINAL_PROMPT': invalid unit
72 |
73 | This means that you are trying to access a repository over HTTPS that requires authentication.
74 |
75 | ### How to fix this
76 |
77 | * Go to **Key Store** screen.
78 | * Create a new key `Login with password` type.
79 | * Specify your login for GitHub/BitBucket/etc.
80 | * Specify the password. You can't use your account password for GitHub/BitBucket, you should use a Personal Access Token (PAT) instead of it. Read more [here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token).
81 | * After creating the key, go to the **Repositories** screen, find your repository and specify the key.
82 |
83 |
84 | ---
85 |
86 |
87 |
88 | ## 6. unable to read LDAP response packet: unexpected EOF
89 |
90 | Most likely, you are trying to connect to the LDAP server using an insecure method, although it expects a secure connection (via TLS).
91 |
92 | ### How to fix this
93 |
94 | Enable TLS in your `config.json` file:
95 |
96 | ```json
97 | ...
98 | "ldap_needtls": true
99 | ...
100 | ```
101 |
102 | ---
103 |
104 | ## 7. LDAP Result Code 49 "Invalid Credentials"
105 |
106 | You have the wrong password or `binddn`.
107 |
108 | ### How to fix this
109 |
110 | Use `ldapwhoami` tool and check if your binddn works:
111 |
112 | ```bash
113 | ldapwhoami\
114 | -H ldap://ldap.com:389\
115 | -D "CN=/your/ldap_binddn/value/in/config/file"\
116 | -x\
117 | -W
118 | ```
119 |
120 | It will ask interactively for the password and should return code **0** and echo out the **DN** as specified.
121 |
122 | You also can read the following articles:
123 | * [ldapsearch: Invalid credentials (49)](https://serverfault.com/q/771549/443463)
124 | * [https://github.com/semaphoreui/semaphore/issues/906](https://github.com/semaphoreui/semaphore/issues/906)
125 |
126 | ---
127 |
128 | ## 8. LDAP Result Code 32 "No Such Object"
129 |
130 | Coming soon.
131 |
--------------------------------------------------------------------------------
/src/user-guide/admin/README.md:
--------------------------------------------------------------------------------
1 | # Admin
2 |
--------------------------------------------------------------------------------
/src/user-guide/admin/runners.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/user-guide/admin/runners.md
--------------------------------------------------------------------------------
/src/user-guide/admin/subscription.md:
--------------------------------------------------------------------------------
1 | # Subscription 🅿
2 |
--------------------------------------------------------------------------------
/src/user-guide/admin/tasks.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/user-guide/admin/tasks.md
--------------------------------------------------------------------------------
/src/user-guide/admin/users.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/src/user-guide/admin/users.md
--------------------------------------------------------------------------------
/src/user-guide/environment.md:
--------------------------------------------------------------------------------
1 | # Variable Groups
2 |
3 | The Variable Groups section of Semaphore is a place to store additional variables for an inventory and must be stored in JSON format.
4 |
5 | All task templates require an variable group to be defined even if it is empty.
6 |
7 | ## Create an variable group
8 | 1. Click on the Variable Group tab.
9 | 2. Click on the New Variable Group button.
10 | 3. Name the Variable Group and type or paste in valid JSON variables. If you just need an empty Variable Group type in ```{}```.
11 |
12 | ## Updating an variable group
13 | 1. Click on the Variable Groups tab.
14 | 2. Click the pencil icon.
15 | 3. Make changes and click save.
16 |
17 | ## Deleting the variable group
18 | Before you remove an variable proup, you must remove all resources tied to it.
19 | If you are not sure which resources are being used in an variable group, follow steps 1 and 2 below. It will show you which resources are being used, with links to those resources.
20 |
21 | 1. Click on the Variable Group.
22 | 2. Click the trash can icon next to the Variable Group.
23 | 3. Click Yes if you are sure you want to remove the variable group.
24 |
25 | ## Using Variable Groups - Terraform
26 | When you want utilize a stored variable group variable or secret in your terraform template you must prefix the name with `TF_VAR_` for the terraform script to use it.
27 |
28 | **Example**
29 | Passing Hetzner Cloud API key to OpenTofu/Terraform playbook.
30 |
31 | 1. Click on Variable Group
32 | 2. Click `New Group`
33 | 3. Click on `Secrets` tab
34 | 4. Add `TF_VAR_hcloud_token` and add you `secret` in the hidden field
35 | 5. Click Save
36 |
37 | We will call our secret `TF_VAR_hcloud_token` as `var.hcloud_token` in
38 | hetzner.tf
39 | ```
40 | terraform {
41 | required_providers {
42 | hcloud = {
43 | source = "hetznercloud/hcloud"
44 | version = "~> 1.45"
45 | }
46 | }
47 | }
48 |
49 | # Declare the variable
50 | variable "hcloud_token" {
51 | type = string
52 | description = "Hetzner Cloud API token"
53 | sensitive = true # This prevents the token from being displayed in logs
54 | }
55 |
56 | provider "hcloud" {
57 | token = var.hcloud_token
58 | }
59 |
60 | # Create a new server running debian
61 | resource "hcloud_server" "webserver" {
62 | name = "webserver"
63 | image = "ubuntu-24.04"
64 | server_type = "cpx11"
65 | location = "ash"
66 | ssh_keys = [ "mysshkey" ]
67 | public_net {
68 | ipv4_enabled = true
69 | ipv6_enabled = true
70 | }
71 | }
72 | ```
73 |
--------------------------------------------------------------------------------
/src/user-guide/integrations.md:
--------------------------------------------------------------------------------
1 | # Integrations
2 |
3 | Integrations allow establishing interaction between Semaphore and external services, such as GitHub and GitLab.
4 |
5 | 
6 |
7 | Using integration, you can trigger a specific template by calling a special endpoint (alias), for which you can configure one of the following authentication methods:
8 | * GitHub Webhooks
9 | * Token
10 | * HMAC
11 | * No authentication
12 |
13 | The alias represents a URL in the following format: `/api/integrations/`. Supports `GET` and `POST` requests.
14 |
15 | ## Matchers
16 |
17 | With matchers, you can define parameters of the incoming request. When these parameters match, the template will be invoked.
18 |
19 | ## Value Extractors
20 |
21 | With an extractor, you can extract the necessary data from the incoming request and pass it to the task as environment variables. For the extracted variables to be passed to the
22 | task, you must create an environment with the corresponding keys. Ensure that the environment keys match the variables defined in the extractor, as this allows the task to receive
23 | and use the correct environment variables.
24 |
--------------------------------------------------------------------------------
/src/user-guide/inventory.md:
--------------------------------------------------------------------------------
1 | # Inventory
2 |
3 | An Inventory is a file that contains a list of hosts Ansible will run plays against.
4 | An Inventory also stores variables that can be used by playbooks. An Inventory can be stored in YAML, JSON, or TOML.
5 | More information about Inventories can be found in the [Ansible Documentation.](https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html)
6 |
7 | Semaphore UI can either read an Inventory from a file on the server that the Semaphore user has read access to, or a static Inventory that is edited via the web GUI.
8 | Each Inventory also has at least one credential tied to it.
9 | The user credential is required, and is what Ansible uses to log into hosts for that Inventory. Sudo credentials are used for escalating privileges on that host.
10 | It is required to have a user credential that is either a username with a login, or SSH configured in the Key Store to create an Inventory.
11 | Information about credentials can be found in the [Key Store](key-store.md) section of this site.
12 |
13 | ## Creating an Inventory
14 | 1. Click on the Key Store tab and confirm you have a key that is a login_password or ssh type
15 | 2. Click on the Inventory tab and click New Inventory
16 | 4. Name the Inventory and select the correct user credential from the dropdown. Select the correct sudo credential, if needed
17 | 5. Select the Inventory type
18 | * If you select file, use the absolute path to the file. If this file is located in your git repo, then use relative path. Ex. `inventory/linux-hosts.yaml`
19 | * If you select static, paste in or type your Inventory into the form
20 | 6. Click Create.
21 |
22 | ## Updating an Inventory
23 | 1. Click on the Inventory tab
24 | 2. Click the Pencil Icon next to the Inventory you want to edit
25 | 3. Make your changes
26 | 4. Click Save
27 |
28 | ## Deleting an Inventory
29 | Before you remove an Inventory, you must remove all resources tied to it.
30 | If you are not sure which resources are being used in an environment, follow steps 1 and 2 below. It will show you which resources are being used, with links to those resources.
31 |
32 | 1. Click on the Inventory tab
33 | 2. Click the trash can icon next to the Inventory
34 | 3. Click Yes if you are sure you want to remove the Inventory
35 |
--------------------------------------------------------------------------------
/src/user-guide/inventory/kerberos.md:
--------------------------------------------------------------------------------
1 |
2 | User guide
3 | →
Inventory
4 | → Kerberos
5 |
6 |
7 |
8 | # Kerberos authentication
9 |
10 | Semaphore supports Kerberos authentication when running playbooks against **Windows hosts via WinRM**.
11 |
12 | ## Inventory configuration
13 |
14 | ```ini
15 | [windows]
16 | hostname
17 |
18 | [windows:vars]
19 | ansible_port=5985
20 | ansible_connection=winrm
21 | ansible_winrm_server_cert_validation=ignore
22 | ansible_winrm_transport=ntlm
23 | ansible_winrm_kinit_mode=managed
24 | ansible_winrm_scheme=http
25 | ```
26 |
27 | Also make sure:
28 |
29 | * A username and password are provided (Semaphore credentials)
30 | * The user format is `domain\\username` (e.g., `CORP\\admin`) if needed
31 |
32 | The key setting is:
33 |
34 | ```ini
35 | ansible_winrm_kinit_mode=managed
36 | ```
37 |
38 | This tells Ansible to **automatically acquire a Kerberos ticket** using the provided username/password without requiring you to manually run kinit.
39 |
40 |
41 | ## Example Playbook
42 |
43 | ```yaml
44 | - hosts: all
45 | gather_facts: false
46 |
47 | tasks:
48 | - win_ping:
49 | ```
50 |
51 | This verifies basic connectivity using WinRM + Kerberos.
52 |
53 |
54 | ## Semaphore UI host requirements
55 |
56 | On the Semaphore host, install the following packages:
57 |
58 | ```bash
59 | sudo apt install libkrb5-dev krb5-user
60 | ```
61 |
62 | Then edit `/etc/krb5.conf` and set your default realm (domain name):
63 |
64 | ```ini
65 | [libdefaults]
66 | default_realm = YOUR.DOMAIN.NAME
67 | ```
68 |
69 | This must match your Active Directory domain.
70 |
71 | ## Notes
72 |
73 | * You do not need to run kinit manually — Ansible handles ticket acquisition when `ansible_winrm_kinit_mode=managed` is set.
74 |
75 | * Works with the default NTLM transport (no SSL needed if using HTTP and `cert_validation=ignore`).
--------------------------------------------------------------------------------
/src/user-guide/key-store.md:
--------------------------------------------------------------------------------
1 | # Key Store
2 |
3 | The Key Store in Semaphore is used to store credentials for accessing remote Repositories, accessing remote hosts, sudo credentials, and Ansible vault passwords.
4 |
5 | It is helpful to have configured all required access keys before setting up other resources like Inventories, Repositories, and tasks templates so you do not have to edit them later.
6 |
7 | ## Types
8 |
9 | ### 1. SSH
10 | SSH Keys are used to access remote servers as well as remote Repositories.
11 |
12 | If you need assistance quickly generating a key and placing it on your host, [here is a quick guide.](https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-on-ubuntu-20-04)
13 |
14 | For Git Repositories that use SSH authentication, the Git Repository you are trying to clone from needs to have your public key associated to the private key.
15 |
16 | Below are links to the docs for some common Git Repositories:
17 | * [GitHub](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account)
18 | * [GitLab](https://docs.gitlab.com/ee/user/ssh.html)
19 | * [Bitbucket](https://support.atlassian.com/bitbucket-cloud/docs/set-up-an-ssh-key/)
20 |
21 | ### 2. Login With Password
22 | Login With Password is a username and password/access token combination that can be used to do the following:
23 | * Authenticate to remote hosts (although this is less secure than using SSH keys)
24 | * Sudo credentials on remote hosts
25 | * Authenticate to remote Git Repositories over HTTPS (although SSH is more secure)
26 | * Unlock Ansible vaults
27 |
28 |
29 | This type of secret can be used as Personal Access Token (PAT) or secret string. Simply leave the Login field empty.
30 |
31 |
32 | ### 3. None
33 | This is used as a filler for Repos that do not require authentication, like an Open-Source Repository on GitLab.
34 |
--------------------------------------------------------------------------------
/src/user-guide/key-store/gitlab.md:
--------------------------------------------------------------------------------
1 |
2 | User Guide
3 |
Key Store
4 | → GitLab
5 |
6 |
7 | # GitLab
8 |
9 | ## GitLab repository access token
10 |
11 |
--------------------------------------------------------------------------------
/src/user-guide/netbox-dynamic-inventory.md:
--------------------------------------------------------------------------------
1 | # Netbox Dynamic Inventory Integration with Semaphore
2 |
3 | 
4 | 
5 | 
6 |
7 | ## 🛠 Key Features
8 |
9 | This repository demonstrates the use of the `netbox.netbox.nb_inventory` plugin to create a dynamic inventory in Semaphore. It enables automatic synchronization of data from Netbox, simplifying the management of your infrastructure and the execution of Ansible playbooks.
10 |
11 | ## 🔧 Setup
12 |
13 | ### Requirements
14 |
15 | - Access to Semaphore
16 | - Access to Netbox with configured API
17 |
18 | ### 🔑 Netbox Setup
19 |
20 | Ensure your Netbox is configured and accessible for API interaction. Obtain an API token which will be used to authenticate requests.
21 |
22 | ### 📡 Configuration in Semaphore
23 |
24 | 1. In Semaphore, go to the inventory section.
25 | 2. Create a new inventory.
26 | 3. Enter the following settings for the plugin configuration:
27 |
28 | ```yaml
29 | plugin: netbox.netbox.nb_inventory
30 | api_endpoint: http://your_netbox_url_here
31 | token: YOUR_NETBOX_API_TOKEN
32 | validate_certs: False
33 | config_context: False
34 | ```
35 |
36 | Replace `http://your_netbox_url_here` and `YOUR_NETBOX_API_TOKEN` with the actual data from your Netbox.
37 |
38 | ## 🚀 Usage
39 |
40 | Once configured, you can run Ansible playbooks in Semaphore using the dynamic inventory which automatically updates host data from your Netbox.
41 |
42 | ## 📚 Further Documentation
43 |
44 | Learn more about the `netbox.netbox.nb_inventory` plugin and its capabilities in the [official Ansible documentation](https://docs.ansible.com/ansible/latest/collections/netbox/netbox/nb_inventory_inventory.html).
45 |
--------------------------------------------------------------------------------
/src/user-guide/projects.md:
--------------------------------------------------------------------------------
1 | # Projects
2 |
3 | A project is a place to separate management activity.
4 |
5 | All Semaphore activities occur within the context of a project.
6 |
7 | Projects are independent from one another, so you can use them to organize unrelated systems within a single Semaphore installation.
8 |
9 | This can be useful for managing different teams, infrastructures, environments or applications.
10 |
11 | 
12 |
13 |
--------------------------------------------------------------------------------
/src/user-guide/projects/activity.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # Activity
7 |
--------------------------------------------------------------------------------
/src/user-guide/projects/history.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # History
7 |
--------------------------------------------------------------------------------
/src/user-guide/projects/runners.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # Runners (Pro)
7 |
8 |
--------------------------------------------------------------------------------
/src/user-guide/projects/settings.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # Settings
7 |
--------------------------------------------------------------------------------
/src/user-guide/repositories.md:
--------------------------------------------------------------------------------
1 | # Repositories
2 |
3 | A Repository is a place to store and manage Ansible content like playbooks and roles.
4 |
5 | 
6 |
7 | Semaphore understands Repositories that are:
8 | * a local file system (`/path/to/the/repo`)
9 | * a local Git repository (`file://`)
10 | * a remote Git Repository that is accessed over HTTPS (`https://`), SSH(`ssh://`)
11 | * `git://` protocol supported, but it is not recommended for security reasons.
12 |
13 | All Task Templates require a Repository in order to run.
14 |
15 | ## Authentication
16 | If you are using a remote Repository that requires authentication, you will need to configure a key in the **Key Store** section of Semaphore.
17 |
18 | For remote Repositories that use SSH, you will need to use your SSH key in the **Key Store**.
19 |
20 | For Remote Repositories that do not have authentication, you can create a Key with the type of `None`.
21 |
22 | ## Creating a New Repository
23 | 1. Make sure you have configured the key for the Repository you are about to add in the key store section.
24 |
25 | 2. Go to the Repositories section of Semaphore, click the **New Repository** button in the upper right hand corner.
26 |
27 | 3. Configure the Repository:
28 | * Name Repository
29 | * Add the URL. The URL must start with the following:
30 | * `/path/to/the/repo` for a local folder on the file system
31 | * `https://` for a remote Git Repository accessed over HTTPS
32 | * `ssh://` for a remote Git Repository accessed over SSH
33 | * `file://` for a local Git Repository
34 | * `git://` for a remote Git Repository accessed over Git protocol
35 | * Set the branch of the Repository, if you are not sure what it should be, it is probably master or main
36 | * Select the **Access Key** you configured prior to setting up this Repository.
37 |
38 | 4. Click Save once everything is configured.
39 |
40 | ## Editing an Existing Repository
41 | 1. Go to the Repositories section of Semaphore.
42 |
43 | 2. Click on the pencil icon next to the Repository you wish to change, then you will be presented with the Repository configuration.
44 |
45 | ## Deleting a Repository
46 | Make sure the Repository that is about to be delete is not in use by any Task Templates.
47 | A Repository cannot be deleted if it is used in any Task Templates:
48 | 1. Go to the Repositories section of Semaphore.
49 |
50 | 2. Click on the trash can icon on of the Repository you wish to delete.
51 |
52 | 3. Click Yes on the confirmation pop-up if you are sure you want this Repository to be deleted.
53 |
54 | ## Requirements
55 | Upon project initialization Semaphore searches for and installs Ansible roles and collections from requirements.yml in the following locations and order.
56 |
57 | ### Roles
58 |
59 | * /roles/requirements.yml
60 | * /requirements.yml
61 | * /roles/requirements.yml
62 | * /requirements.yml
63 |
64 | ### Collection
65 |
66 | * /collections/requirements.yml
67 | * /requirements.yml
68 | * /collections/requirements.yml
69 | * /requirements.yml
70 |
71 | ### Processing Logic
72 |
73 | * Each file is processed independently
74 | * If a file exists, it will be processed according to its type (role or collection)
75 | * If any file processing results in an error, the installation process stops and returns the error
76 | * The same requirements.yml file in the root directories (**/requirements.yml** and **/requirements.yml**) is processed twice - once for roles and once for collections
77 |
78 | Semaphore will attempt to process all these locations regardless of whether previous locations were found or successfully processed, except in the case of errors.
79 |
--------------------------------------------------------------------------------
/src/user-guide/repositories/bitbucket_access_token.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # Bitbucket Access Token
7 |
8 | You can use a Bitbucket Access Token in Semaphore to access repositories from Bitbucket.
9 |
10 | First, you need to create an Access Token for your Bitbucket repository with read access permissions.
11 |
12 | 
13 |
14 |
15 | After creation, you will see the access token. Copy it to your clipboard as it will be required for creating an **Access Key** in Semaphore.
16 |
17 | 
18 |
19 | 1. Go to to the **Key Store** section in Semaphore and click the **New Key** button.
20 |
21 | 2. Choose `Login with password` as the type of key.
22 |
23 | 3. Enter `x-token-auth` as **Login** and paste the previously copied key into the **Password** field. Save the key.

24 |
25 | 4. Go to the **Repositories** section and click the **New Repository** button.
26 |
27 | 5. Enter HTTPS URL of the repository (`https://bitbucket.org/path/to/repo`), enter correct branch and select previously created **Access Key**.

--------------------------------------------------------------------------------
/src/user-guide/schedules.md:
--------------------------------------------------------------------------------
1 | # Schedules
2 |
3 | The schedule function in Semaphore allows to automate the execution of templates (e.g. playbook runs) at predefined intervals. This feature allows to implement routine automation tasks, such as regular backups, compliance checks, system updates, and more.
4 |
5 | Make sure to restart the Semaphore service after making changes for them to take effect.
6 |
7 | ## Setup and configuration
8 |
9 | ## Timezone configuration
10 |
11 | By default, the schedule feature operates in the UTC timezone. However, this can be customized to match your local timezone or specific requirements.
12 |
13 | You can change the timezone by updating the configuration file or setting an environment variable:
14 |
15 | 1. **Using the configuration file**:
16 | Add or update the `timezone` field in your Semaphore configuration file:
17 | ```json
18 | {
19 | "schedule": {
20 | "timezone": "America/New_York"
21 | }
22 | }
23 | ```
24 |
25 | 2. **Using an environment variable**:
26 | Set the `SEMAPHORE_SCHEDULE_TIMEZONE` environment variable:
27 | ```bash
28 | export SEMAPHORE_SCHEDULE_TIMEZONE="America/New_York"
29 | ```
30 |
31 | For a list of valid timezone values, refer to the [IANA Time Zone Database](https://www.iana.org/time-zones).
32 |
33 | ### Accessing the schedule feature
34 |
35 | 1. Log in to your Ansible Semaphore web interface
36 | 2. Navigate to the "Schedule" tab in the main navigation menu
37 | 3. Click the "New Schedule" button in the top right corner to create a new schedule
38 |
39 | 
40 |
41 | ### Creating a new schedule
42 |
43 | When creating a new schedule, you'll need to configure the following options:
44 |
45 | | Field | Description |
46 | |-------|-------------|
47 | | Name | A descriptive name for the scheduled task |
48 | | Template | The specific Task Template to execute |
49 | | Timing | Either in cron format for more fexibility or using the built-in options for common intervals |
50 |
51 |  
52 |
53 | ### Cron format syntax
54 |
55 | The schedule uses standard cron syntax with five fields:
56 |
57 | ```
58 | ┌─────── minute (0-59)
59 | │ ┌────── hour (0-23)
60 | │ │ ┌───── day of month (1-31)
61 | │ │ │ ┌───── month (1-12)
62 | │ │ │ │ ┌───── day of week (0-6) (Sunday=0)
63 | │ │ │ │ │
64 | │ │ │ │ │
65 | * * * * *
66 | ```
67 |
68 | Examples:
69 | - `*/15 * * * *` - Run every 15 minutes
70 | - `0 2 * * *` - Run at 2:00 AM every day
71 | - `0 0 * * 0` - Run at midnight on Sundays
72 | - `0 9 1 * *` - Run at 9:00 AM on the first day of every month
73 |
74 | Very helpful cron expression generator: [https://crontab.guru/](https://crontab.guru/)
75 |
76 | ## Use cases
77 |
78 | ### System maintenance
79 |
80 | ```yaml
81 | # Example playbook for system updates
82 | ---
83 | - hosts: all
84 | become: yes
85 | tasks:
86 | - name: Update apt cache
87 | apt:
88 | update_cache: yes
89 |
90 | - name: Upgrade all packages
91 | apt:
92 | upgrade: yes
93 |
94 | - name: Remove dependencies that are no longer required
95 | apt:
96 | autoremove: yes
97 | ```
98 |
99 | Schedule this playbook to run weekly during off-hours to ensure systems stay up-to-date.
100 |
101 | ### Backup operations
102 |
103 | Create schedules for database backups with different frequencies:
104 | - Daily backups that retain for one week
105 | - Weekly backups that retain for one month
106 | - Monthly backups that retain for one year
107 |
108 | ### Compliance checks
109 |
110 | Schedule regular compliance scans to ensure systems meet security requirements:
111 |
112 | ```yaml
113 | # Example compliance check playbook
114 | ---
115 | - hosts: all
116 | tasks:
117 | - name: Run compliance checks
118 | script: /path/to/compliance_script.sh
119 |
120 | - name: Collect compliance reports
121 | fetch:
122 | src: /var/log/compliance-report.log
123 | dest: reports/{{ inventory_hostname }}/
124 | flat: yes
125 | ```
126 |
127 | ### Environment provisioning and cleanup
128 |
129 | For development or testing environments. Schedule cloud environment creation in the morning and teardown in the evening to optimize costs.
130 |
131 | ## Best practices
132 |
133 | * Use descriptive names for schedules that indicate both function and timing (e.g. "Weekly-Backup-Sunday-2AM")
134 | * Avoid scheduling too many resource-intensive tasks concurrently
135 | * Consider the effect of long-running scheduled tasks on other schedules
136 | * Test schedules with short intervals before setting up production schedules with longer intervals
137 | * Document the purpose and expected outcomes of scheduled tasks
138 |
--------------------------------------------------------------------------------
/src/user-guide/task-templates/README.md:
--------------------------------------------------------------------------------
1 | # Task Templates
2 |
3 | Templates define how to run Semaphore tasks. Currently the following task types are supported:
4 |
5 | * Playbook repository
6 | * Playbook filename
7 | * Inventory
8 | * Environment
9 | * Vault password file
10 | * Extra CLI arguments
11 | * and much more
12 |
13 | 
14 |
15 | The task template can be one of the following types:
16 |
17 | * [Task](#task)
18 | * [Build](#build)
19 | * [Deploy](#deploy)
20 |
21 | ### Task
22 |
23 | Just runs specified playbooks with specified parameters.
24 |
25 | ### Build
26 |
27 | This type of template should be used to create [artifacts](https://en.wikipedia.org/wiki/Artifact\_\(software\_development\)). The start version of the artifact can be specified in a template parameter. Each run increments the artifact version.
28 |
29 | .png>)
30 |
31 | Semaphore doesn't support artifacts out-of-box, it only provides task versioning. You should implement the artifact creation yourself. Read the article [CI/CD](../../administration-guide/cicd.md) to know how to do this.
32 |
33 | ### Deploy
34 |
35 | This type of template should be used to deploy artifacts to the destination servers. Each `deploy` template is associated with a `build` template.
36 |
37 | 
38 |
39 | This allows you to deploy a specific version of the artifact to the servers.
40 |
41 | ### Schedule
42 |
43 | You can set up task scheduling by specifying a cron schedule in the template settings. Cron expression format you can find in [documentation](https://pkg.go.dev/github.com/robfig/cron/v3#hdr-CRON\_Expression\_Format).
44 |
45 | 
46 |
47 | #### Run a task when a new commit is added to the repository
48 |
49 | You can use cron to periodically check for new commits in the repository and trigger a task upon their arrival.
50 |
51 | For example you have source code of the app in the git repository. You can add it to **Repositories** and trigger the Build task for new commits.
52 |
53 | 
54 |
--------------------------------------------------------------------------------
/src/user-guide/task-templates/apps/ansible.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # Ansible
7 |
8 | Using Semaphore UI you can run Ansible playbooks. To do this, you need to create an **Ansible Playbook** Template.
9 |
10 | 1. Go go **Task Templates** section, click on **New Template** and then **Ansible Playbook**.
11 |
12 | 
13 |
14 | 2. Set up the template.
15 |
16 | The template allows you to specify the following parameters:
17 |
18 | * Repository
19 | * Path to playbook file
20 | * Inventory
21 | * Variable Groups
22 | * Vaults
23 | * and much more
24 |
25 | 
26 |
27 | An ansible-playbook template can be one of the following types:
28 |
29 | * [Task](#task)
30 | * [Build](#build)
31 | * [Deploy](#deploy)
32 |
33 | ### Task
34 |
35 | Just runs specified playbooks with specified parameters.
36 |
37 | ### Build
38 |
39 | This type of template should be used to create [artifacts](https://en.wikipedia.org/wiki/Artifact\_\(software\_development\)). The start version of the artifact can be specified in a template parameter. Each run increments the artifact version.
40 |
41 | .png>)
42 |
43 | Semaphore doesn't support artifacts out-of-box, it only provides task versioning. You should implement the artifact creation yourself. Read the article [CI/CD](../../administration-guide/cicd.md) to know how to do this.
44 |
45 | ### Deploy
46 |
47 | This type of template should be used to deploy artifacts to the destination servers. Each `deploy` template is associated with a `build` template.
48 |
49 | 
50 |
51 | This allows you to deploy a specific version of the artifact to the servers.
52 |
53 | ### Schedule
54 |
55 | You can set up task scheduling by specifying a cron schedule in the template settings. Cron expression format you can find in [documentation](https://pkg.go.dev/github.com/robfig/cron/v3#hdr-CRON\_Expression\_Format).
56 |
57 | 
58 |
59 | #### Run a task when a new commit is added to the repository
60 |
61 | You can use cron to periodically check for new commits in the repository and trigger a task upon their arrival.
62 |
63 | For example you have source code of the app in the git repository. You can add it to **Repositories** and trigger the Build task for new commits.
64 |
65 | 
66 |
--------------------------------------------------------------------------------
/src/user-guide/task-templates/apps/bash.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # Shell/Bash scripts
7 |
8 | Using Semaphore UI you can run Bash scripts. To do this, you need to create a **Bash Script Template**.
9 |
10 | 1. Go go Task Templates section and click the **New Template** button.
11 |
12 | 
13 |
14 | 2. Set up the template and click the **Create** button.
15 |
16 | 
17 |
18 | 3. You can now run your Bash script.
--------------------------------------------------------------------------------
/src/user-guide/task-templates/apps/powershell.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # PowerShell
7 |
--------------------------------------------------------------------------------
/src/user-guide/task-templates/apps/python.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # Python
7 |
--------------------------------------------------------------------------------
/src/user-guide/task-templates/apps/terraform.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # Terraform/OpenTofu
7 |
8 | Using Semaphore UI you can run Terraform code. To do this, you need to create a **Terraform Code Template**.
9 |
10 | 1. Go go Task Templates section and click the **New Template** button.
11 |
12 | 
13 |
14 | 2. Set up the template and click the **Create** button.
15 |
16 | 
17 |
18 | 3. You can now run your Terraform code.
--------------------------------------------------------------------------------
/src/user-guide/task-templates/apps/terraform/states.md:
--------------------------------------------------------------------------------
1 |
6 |
7 | # HTTP Backend (Pro)
8 |
9 | The Semaphore UI HTTP backend for Terraform securely stores and manages Terraform state files directly within Semaphore. Available in the Pro plan, it offers several key advantages.
10 |
11 | ## Features
12 |
13 | - **Secure State Storage**: State files are stored securely within Semaphore.
14 | - **State Locking**: Prevents concurrent modifications to the same state file.
15 | - **Version History**: Track changes to your infrastructure state over time.
16 | - **UI Integration**: Manage state files directly through the Semaphore interface.
17 |
18 | ## Configuration
19 |
20 | To use the Semaphore UI HTTP backend, add the following configuration to your Terraform configuration:
21 |
22 | ```hcl
23 | terraform {
24 | backend "http" {
25 | address = "https:///api/terraform/state/"
26 | lock_address = "https:///api/terraform/state//lock"
27 | unlock_address = "https:///api/terraform/state//lock"
28 | username = "semaphore"
29 | password = ""
30 | }
31 | }
32 | ```
33 |
34 |
35 |
36 | Replace the following placeholders:
37 | - ``: Your Semaphore instance URL
38 | - ``: Your Semaphore project ID
39 | - ``: Your Semaphore API token
40 |
41 | ## Accessing State Files
42 |
43 | You can access and manage your Terraform state files through the Semaphore UI:
44 |
45 | 1. Navigate to your project in Semaphore
46 | 2. Go to the "Terraform" section
47 | 3. Select "States" from the sidebar
48 | 4. View, download, or manage your state files
49 |
50 | ## Best Practices
51 |
52 | - Always use state locking when working in team environments
53 | - Regularly backup your state files
54 | - Use meaningful names for your state files
55 | - Keep your API tokens secure and rotate them periodically
--------------------------------------------------------------------------------
/src/user-guide/task-templates/apps/terraform/workspaces.md:
--------------------------------------------------------------------------------
1 |
6 |
7 | # Workspaces
8 |
9 | Semaphore provides built-in support for Terraform workspaces, allowing you to manage multiple environments and configurations within a single project. This feature helps you maintain separate state files for different environments like development, staging, and production.
10 |
11 | ## Features
12 |
13 | - **Workspace Management**: Create, switch, and delete workspaces directly from the Semaphore UI.
14 | - **State Isolation**: Each workspace maintains its own state file, preventing conflicts between environments.
15 | - **Environment Variables**: Configure workspace-specific environment variables.
16 | - **Workspace Selection**: Choose the target workspace when running Terraform commands.
17 |
18 | ## Using Workspaces in Semaphore
19 |
20 | ### Creating a Workspace
21 |
22 | In the **Workspaces** section of the Terraform/OpenTofu template where you want to add a workspace, follow these steps:
23 |
24 | 1. Click the ➕ button.
25 | 2. In the menu that appears, select **New Workspace**.
26 | 3. In the modal dialog, enter the workspace name and select the SSH key to be used for cloning modules.
27 | 4. Click the **Create** button to add the new workspace to the template.
28 | 5. You can now use this workspace to run tasks.
29 |
30 | 
31 |
32 | ### Switching workspaces
33 |
34 | You can set the default workspace for a Terraform/OpenTofu template by clicking the **MAKE DEFAULT** button.
35 |
36 | 
37 |
38 | ### Workspace-specific variables
39 |
40 | Semaphore currently does not support workspace-specific variables.
--------------------------------------------------------------------------------
/src/user-guide/task-templates/survey-vars.md:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | 
--------------------------------------------------------------------------------
/src/user-guide/tasks.md:
--------------------------------------------------------------------------------
1 | # Tasks
2 |
3 | A task is an instance of launching an Ansible playbook. You can create the task from [Task Template](task-templates/) by clicking the button Run/Build/Deploy for the required template.
4 |
5 | .png>)
6 |
7 | The **Deploy** task type allows you to specify a version of the build associated with the task. By default, it is the latest build version.
8 |
9 | .png>)
10 |
11 | When the task is running, or it has finished, you can see the task status and the running log.
12 |
13 | .png>)
14 |
15 | ## Tasks log retention
16 | You'll notice that logs of previous runs of your tasks are available in the tasks template or in the dashboard.
17 |
18 | However, by default, log retention is infinite.
19 |
20 | You can configure this by using the `max_tasks_per_template` parameter in `config.json` or the `SEMAPHORE_MAX_TASKS_PER_TEMPLATE` environment variable.
21 |
22 |
--------------------------------------------------------------------------------
/src/user-guide/team.md:
--------------------------------------------------------------------------------
1 | # Teams
2 |
3 | In Semaphore UI, every project is associated with a **Team**. Only team members and admins can access the project. Each member of the team is assigned one of four predefined roles, which govern their level of access and the actions they can perform.
4 |
5 |
6 | To avoid losing access to a project, it’s recommended to have at least two team members with the Owner role.
7 |
8 |
9 | ---
10 |
11 | ## Team roles
12 |
13 | Every team member has exactly one of these four roles:
14 |
15 | - **Owner**
16 | - **Manager**
17 | - **Task Runner**
18 | - **Guest**
19 |
20 | Below are detailed descriptions of each role and its permissions.
21 |
22 | ### Owner
23 |
24 | - **Full permissions**
25 | Owners can do anything within the project, including managing roles, adding/removing members, and configuring any project settings.
26 |
27 | - **Multiple owners**
28 | A project can have multiple Owners, ensuring there is more than one person with full privileges.
29 |
30 | - **Restrictions on self-removal**
31 | An Owner cannot remove themselves if they are the only Owner of the project. This prevents the project from being left without an Owner.
32 |
33 | - **Managing other wwners**
34 | Owners can manage (including remove or change roles of) all team members, including other Owners.
35 |
36 | ### Manager
37 |
38 | - **Broad project control:** Managers have almost the same permissions as Owners, allowing them to handle most day-to-day tasks and manage the project environment.
39 |
40 | - Managers **cannot**:
41 | - Remove the project.
42 | - Remove or change the roles of Owners.
43 |
44 | - **Typical use case:** Assign the Manager role to senior team members who need extensive access but don’t require the authority to delete the project or manage Owners.
45 |
46 | ### Task Runner
47 |
48 | - **Run tasks:** Task Runners can execute any task template that exists within the project.
49 |
50 | - **Read-only for other resources:** While they can run tasks, they only have read‐only access to other resources such as inventory, variables, repositories, etc.
51 |
52 | - **Typical use case:** Developers or QA engineers who need to trigger and monitor tasks but do not need the ability to modify project settings or manage team membership.
53 |
54 | ### Guest
55 |
56 | - **Read-only access:** Guests have read-only access to all project resources (e.g., viewing logs, inventories, dashboards).
57 |
58 | - **No write permissions:** They cannot modify settings, run tasks, or change roles.
59 |
60 | - **Typical use case:** Stakeholders or other collaborators who only need to view project status and details without making changes.
61 |
62 | ---
63 |
64 | ## Managing team members
65 |
66 | - **Inviting new members:** **Owners** and **Managers** can invite new users to join the team and assign them an initial role.
67 |
68 | - **Changing roles:** Owners can always change the roles of any team member. Managers can change the roles of **Task Runners** and **Guests**, but **not** other Managers or Owners.
69 |
70 | - **Removing members:** Owners and Managers can remove team members with lower roles.
71 | - An Owner can remove anyone (including other Owners), but cannot remove themselves if they are the sole Owner.
72 | - A Manager can remove **Task Runners** and **Guests**, but **not** other Managers or Owners.
73 |
74 | ---
75 |
76 | ## Best practices
77 |
78 | 1. **Maintain redundancy:** Assign the **Owner** role to at least two people to ensure continuous access and prevent a single point of failure.
79 | 2. **Follow principle of least privilege:**
80 | - Give team members the minimum role necessary for their tasks.
81 | - Use **Task Runner** or **Guest** roles for those who only need limited permissions.
82 | 3. **Review membership regularly:**
83 | - As team structures change, re‐evaluate roles.
84 | - Revoke access or downgrade roles for users who no longer need high‐level privileges.
85 | 4. **Use managers for day-to-day administration:**
86 | - Reserve the Owner role for a smaller group with ultimate authority.
87 | - Delegate routine project management tasks to Managers to reduce the risk of accidental major changes or project deletions.
88 |
89 | ---
90 |
91 | ## Frequently asked questions
92 |
93 | ### 1. Can an Owner remove another Owner?
94 | Yes, an Owner can remove or change the role of any other Owner, unless they are the only remaining Owner in the project.
95 |
96 | ### 2. Who can delete the project?
97 | Only **Owners** can delete a project.
98 |
99 | ### 3. Can Managers add or remove other Managers?
100 | No. Managers can only add or remove users with **Task Runner** or **Guest** roles. To manage Owners or other Managers, you must be an Owner.
101 |
102 | ### 4. What happens if I remove all Owners by accident?
103 | Semaphore UI prevents the removal of an Owner if it would leave the project with no Owners at all. There must be at least one Owner at all times.
104 |
105 | ### 5. Can Guests run tasks?
106 | No. Guests only have read‐only access and cannot trigger or manage tasks.
107 |
--------------------------------------------------------------------------------
/theme/content.css:
--------------------------------------------------------------------------------
1 | .content img {
2 | border-radius: 6px;
3 | }
4 |
5 | .light .content img {
6 | box-shadow: 0 0 3px var(--searchbar-shadow-color);
7 | }
8 |
9 | hr {
10 | border: 0;
11 | border-bottom: 1px solid rgba(150,150,150,0.1);
12 | }
13 |
14 | .warning {
15 | color: var(--warning-border);
16 | }
--------------------------------------------------------------------------------
/theme/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/semaphoreui/semaphore-docs/ed8d380491d8e5a8ef71a845e3214e8522f358e1/theme/favicon.png
--------------------------------------------------------------------------------
/theme/head.hbs:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/theme/header.css:
--------------------------------------------------------------------------------
1 | .menu-icon {
2 | width: 30px;
3 | transform: translateY(6px);
4 | margin-right: 5px;
5 | opacity: 0.7;
6 | }
7 |
8 | .light .menu-icon {
9 | opacity: 1;
10 | }
11 |
12 |
13 | .chapter > .chapter-item > .toggle {
14 | display: none;
15 | }
16 |
17 | svg {
18 | border-radius: 6px;
19 | }
20 |
21 | #disqus_thread {
22 | padding-top: 100px;
23 | }
24 |
25 | #disqus_thread > iframe {
26 | color-scheme: normal;
27 | }
28 |
29 | .menu-title {
30 | background-position: center;
31 | background-repeat: no-repeat;
32 | background-size: 150px;
33 | /* color: transparent; */
34 | }
35 |
36 | .light .menu-title,
37 | .rust .menu-title {
38 | background-image: url(/.gitbook/assets/semaphore-docs-light.png);
39 | }
40 |
41 | .coal .menu-title,
42 | .navy .menu-title,
43 | .ayu .menu-title {
44 | background-image: url(/.gitbook/assets/semaphore-docs-dark.png);
45 | }
--------------------------------------------------------------------------------
/theme/search.css:
--------------------------------------------------------------------------------
1 | ul#searchresults {
2 | padding-right: 10px;
3 | }
4 |
5 | ul#searchresults li {
6 | margin: 10px 0px;
7 | padding: 2px;
8 | border-radius: 2px;
9 | border-bottom: 1px dashed var(--searchresults-header-fg);
10 | padding-bottom: 10px;
11 | }
12 |
13 | ul#searchresults li:last-child {
14 | border-bottom: 0;
15 | padding-bottom: 0;
16 | }
17 |
18 | .searchresults-outer#searchresults-outer {
19 | border-radius: 6px;
20 | margin-top: 6px;
21 | border: 1px solid var(--searchbar-border-color);
22 | background-color: var(--searchbar-bg);
23 | /* box-shadow: 0 0 3px var(--searchbar-shadow-color); */
24 | }
25 |
26 | ul#searchresults span.teaser {
27 | color: var(--searchbar-fg);
28 | margin-left: 0;
29 | }
--------------------------------------------------------------------------------
/theme/tabs.css:
--------------------------------------------------------------------------------
1 | .mdbook-tabs {
2 | display: flex;
3 | }
4 |
5 | .mdbook-tab {
6 | background-color: var(--table-alternate-bg);
7 | padding: 0.5rem 1rem;
8 | cursor: pointer;
9 | border: none;
10 | font-size: 1.6rem;
11 | line-height: 1.45em;
12 | }
13 |
14 | .mdbook-tab.active {
15 | background-color: var(--table-header-bg);
16 | font-weight: bold;
17 | }
18 |
19 | .mdbook-tab-content {
20 | padding: 1rem 0rem;
21 | }
22 |
23 | .mdbook-tab-content table {
24 | margin: unset;
25 | }
26 |
--------------------------------------------------------------------------------
/theme/tabs.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Change active tab of tabs.
3 | *
4 | * @param {Element} container
5 | * @param {string} name
6 | */
7 | const changeTab = (container, name) => {
8 | for (const child of container.children) {
9 | if (!(child instanceof HTMLElement)) {
10 | continue;
11 | }
12 |
13 | if (child.classList.contains('mdbook-tabs')) {
14 | for (const tab of child.children) {
15 | if (!(tab instanceof HTMLElement)) {
16 | continue;
17 | }
18 |
19 | if (tab.dataset.tabname === name) {
20 | tab.classList.add('active');
21 | } else {
22 | tab.classList.remove('active');
23 | }
24 | }
25 | } else if (child.classList.contains('mdbook-tab-content')) {
26 | if (child.dataset.tabname === name) {
27 | child.classList.remove('hidden');
28 | } else {
29 | child.classList.add('hidden');
30 | }
31 | }
32 | }
33 | };
34 |
35 | document.addEventListener('DOMContentLoaded', () => {
36 | const tabs = document.querySelectorAll('.mdbook-tab');
37 | for (const tab of tabs) {
38 | tab.addEventListener('click', () => {
39 | if (!(tab instanceof HTMLElement)) {
40 | return;
41 | }
42 |
43 | if (!tab.parentElement || !tab.parentElement.parentElement) {
44 | return;
45 | }
46 |
47 | const container = tab.parentElement.parentElement;
48 | const name = tab.dataset.tabname;
49 | const global = container.dataset.tabglobal;
50 |
51 | changeTab(container, name);
52 |
53 | if (global) {
54 | localStorage.setItem(`mdbook-tabs-${global}`, name);
55 |
56 | const globalContainers = document.querySelectorAll(
57 | `.mdbook-tabs-container[data-tabglobal="${global}"]`
58 | );
59 | for (const globalContainer of globalContainers) {
60 | changeTab(globalContainer, name);
61 | }
62 | }
63 | });
64 | }
65 |
66 | const containers = document.querySelectorAll('.mdbook-tabs-container[data-tabglobal]');
67 | for (const container of containers) {
68 | const global = container.dataset.tabglobal;
69 |
70 | const name = localStorage.getItem(`mdbook-tabs-${global}`);
71 | if (name && document.querySelector(`.mdbook-tab[data-tabname=${name}]`)) {
72 | changeTab(container, name);
73 | }
74 | }
75 | });
76 |
--------------------------------------------------------------------------------