feat: restructure whole docker build process

This commit is contained in:
Thomas Boerger 2024-04-28 00:08:51 +02:00
parent 41c25b93b1
commit aee0631540
No known key found for this signature in database
GPG Key ID: F630596501026DB5
44 changed files with 602 additions and 970 deletions

View File

@ -0,0 +1,17 @@
version: "3.4"
volumes:
dredd:
services:
dredd:
build:
context: ../../../
dockerfile: deployment/docker/dredd/Dockerfile
command:
- --config
- .dredd/dredd.yml
environment:
SEMAPHORE_ACCESS_KEY_ENCRYPTION: ${SEMAPHORE_ACCESS_KEY_ENCRYPTION:-IlRqgrrO5Gp27MlWakDX1xVrPv4jhoUx+ARY+qGyDxQ=}
volumes:
- dredd:/data

View File

@ -0,0 +1,9 @@
version: "3.4"
services:
dredd:
environment:
SEMAPHORE_DB_DIALECT: bolt
SEMAPHORE_DB_CONFIG: '{"host": "/data/database.boltdb"}'
depends_on:
- server

View File

@ -0,0 +1,10 @@
version: "3.4"
services:
dredd:
environment:
SEMAPHORE_DB_DIALECT: mysql
SEMAPHORE_DB_CONFIG: '{"host": "db:3306","user": "semaphore","pass": "semaphore","name": "semaphore"}'
depends_on:
- server
- db

View File

@ -0,0 +1,10 @@
version: "3.4"
services:
dredd:
environment:
SEMAPHORE_DB_DIALECT: mysql
SEMAPHORE_DB_CONFIG: '{"host": "db:3306","user": "semaphore","pass": "semaphore","name": "semaphore"}'
depends_on:
- server
- db

View File

@ -0,0 +1,10 @@
version: "3.4"
services:
dredd:
environment:
SEMAPHORE_DB_DIALECT: postgres
SEMAPHORE_DB_CONFIG: '{"host": "db:5432","user": "semaphore","pass": "semaphore","name": "semaphore","options": {"sslmode": "disable"}}'
depends_on:
- server
- db

View File

@ -0,0 +1,9 @@
version: "3.4"
services:
dredd:
environment:
SEMAPHORE_DB_DIALECT: sqlite
SEMAPHORE_DB_CONFIG: '{"host": "/data/database.sqlite3"}'
depends_on:
- server

View File

@ -0,0 +1,60 @@
# Docker
Generally we are building production-grade images for each tag, latest and even
for the development branch which will be pushed to [DockerHub][dockerhub]. If
you still need to build your own image you can easily do that, you just need
install [Docker][docker] and [Task][gotask] on your system.
If you just want to use our pre-built images please follow the instructions on
our [documentation][documentation].
If you want to use [docker-compose][dockercompose] to start Semaphore you could
also read about it on our [documentation][documentation] or take a look at our
collection of [snippets][snippets] within this repository.
## Build
We have prepared multiple tasks to build an publish container images, including
tasks to verify the image contains all required tools:
```console
task docker:build
task docker:push
```
If you want to customize the image names or if you want to use [Podman][podman]
instead of [Docker][docker] you are able to provide some set of environment
variables to the [Task][gotask] command:
* `DOCKER_ORG`: Define a custom organization for the image, defaults to `semaphoreui`
* `DOCKER_SERVER`: Define a different name for the server image, defaults to `semaphore`
* `DOCKER_RUNNER`: Define a different name for the runner image, defaults to `runner`
* `DOCKER_CMD`: Use another command to build the image, defaults to `docker`
## Test
We defined tasks to handle some linting and to verify the images contain the
tools and binaries that are required to run Semaphore. Here we are using
[Hadolint][hadolint] to ensure we are mostly following best-practices and
[Goss][goss] which is using a configuration file to define the requirements.
To install the required tools you also need to install [Golang][golang] on your
system, the installation of [Golang][golang] is not covered by us.
The installation of the dependencies can be customized by providing environment
variables for `INSTALL_PATH` (`/usr/local/bin`) and `REQUIRE_SUDO` (true).
```console
task docker:test
```
[dockerhub]: https://hub.docker.com/r/semaphoreui/semaphore
[docker]: https://docs.docker.com/engine/install/
[podman]: https://podman.io/docs/installation
[gotask]: https://taskfile.dev/installation/
[dockercompose]: https://docs.docker.com/compose/
[golang]: https://go.dev/doc/install
[hadolint]: https://github.com/hadolint/hadolint
[goss]: https://github.com/goss-org/goss
[snippets]: ../compose/README.md
[documentation]: https://docs.semui.co/administration-guide/installation

View File

@ -1,83 +0,0 @@
# Docker Deployments
Production images for each tag, latest and the development branch will be pushed to [docker hub](https://hub.docker.com/r/semaphoreui/semaphore).
To build images locally see the contexts included here and use the `d` and `dc` tasks in the root Taskfile.yml to help with building and running.
## Contexts
### Prod
To build a production image you should run
context=prod task docker:build
this will create an image called `semaphoreui/semaphore:latest` which will be compiled from the currently checked out code
This image is run as non root user 1001 (for PaaS systems such as openshift) and is build on alpine with added glibc.
With ansible etc... installed in the container it is ~283MiB in size.
You will need to provide environmental variables so that the configuration can be built correctly for your environment.
See `docker-compose.yml` for an example, or look at `../common/entrypoint` to see which variables are available
If you want to build an image with a custom tag you can optionally pass a tag to the command
context=prod tag=mybranch task docker:build
#### Example Configuration
To run Semaphore in a simple production-like docker configuration run the following command:
task dc:prod
You can then access Semaphore directly from the url http://localhost:8081/
#### SSL Termination Using Nginx
Generate a cert, ca cert, and key file and place into `prod/proxy/cert/` with
these names:
* `cert.pem`
* `privkey.pem`
* `fullchain.pem`
(I've used letsencrypt generated certs with success.)
Run `task dc:prod` and your Semaphore instance will then be at the url
https://localhost:8443
If you do not add certificates the container will create self-signed certs instead
## Dev
To start a development start you could run
```
context=dev task dc:up
```
The development stack will run `task watch` by default and `dc:up` will volume link the application in to the container.
Without `dc:up` the application will run the version of the application which existed at image build time.
The development container is based on [micro-golang](https://github.com/twhiston/micro-golang)'s test base image
which contains the go toolchain and glibc in alpine.
Because the test image links your local volume it expects that you have run `task deps` and `task compile` locally
as necessary to make the application usable.
## CI
This context is a proxyless stack used to test the API in the ci. Essentially it just installs the app, adds a few bootstrapping files
and starts up so that dredd can be run against it. This should not be used in production as it does not remove the build toolchain,
or source code.
It is more advisable to use the dev context locally as it volume links the application directory and defaults to the watch task.
## Convenience Functions
### dc:dev
`dc:dev` rebuilds the development images and runs a development stack, with the semaphore root as a volume link
This allows you to work inside the container with live code. The container has all the tools you need to build and test semaphore
### dc:prod
`dc:prod` rebuilds the production example images and starts the production-like stack.
This will compile the application for the currently checked out code but will not leave build tools or source in the container.
Therefore file changes will result in needing a rebuild.

View File

@ -1,42 +0,0 @@
FROM golang:1.21-alpine3.18
ENV SEMAPHORE_VERSION="development" SEMAPHORE_ARCH="linux_amd64" \
SEMAPHORE_CONFIG_PATH="${SEMAPHORE_CONFIG_PATH:-/etc/semaphore}" \
APP_ROOT="/go/src/github.com/ansible-semaphore/semaphore/"
# hadolint ignore=DL3013
RUN apk add --no-cache gcc g++ sshpass git mysql-client python3 py3-pip py-openssl openssl ca-certificates curl curl-dev openssh-client-default tini nodejs npm bash rsync tzdata zip unzip tar && \
apk --update add --virtual build-dependencies python3-dev libffi-dev openssl-dev build-base &&\
rm -rf /var/cache/apk/*
RUN gcc --version
RUN pip3 install --upgrade pip cffi && \
pip3 install ansible &&\
apk del build-dependencies
RUN adduser -D -u 1002 -g 0 semaphore && \
mkdir -p /go/src/github.com/ansible-semaphore/semaphore && \
mkdir -p /tmp/semaphore && \
mkdir -p /etc/semaphore && \
mkdir -p /var/lib/semaphore && \
chown -R semaphore:0 /go && \
chown -R semaphore:0 /tmp/semaphore && \
chown -R semaphore:0 /etc/semaphore && \
chown -R semaphore:0 /var/lib/semaphore && \
ssh-keygen -t rsa -q -f "/root/.ssh/id_rsa" -N "" && \
ssh-keyscan -H github.com > /root/.ssh/known_hosts
RUN cd $(go env GOPATH) && go install github.com/go-task/task/v3/cmd/task@latest
RUN git config --global --add safe.directory /go/src/github.com/ansible-semaphore/semaphore
# Copy in app source
WORKDIR ${APP_ROOT}
COPY . ${APP_ROOT}
RUN deployment/docker/ci/bin/install
USER semaphore
EXPOSE 3000
ENTRYPOINT ["/usr/local/bin/semaphore-wrapper"]
CMD ["./bin/semaphore", "server", "--config", "/etc/semaphore/config.json"]

View File

@ -1,15 +0,0 @@
#!/usr/bin/env bash
echo "--> Turn off StrictKeyChecking"
cat > /etc/ssh/ssh_config <<EOF
Host *
StrictHostKeyChecking no
UserKnownHostsFile=/dev/null
EOF
echo "--> Install Semaphore entrypoint wrapper script"
cp ./deployment/docker/common/semaphore-wrapper /usr/local/bin/semaphore-wrapper
cp ./deployment/docker/common/runner-wrapper /usr/local/bin/runner-wrapper
task deps
task compile
task build:local GOOS= GOARCH=

View File

@ -1,38 +0,0 @@
version: '2'
volumes:
data:
services:
semaphore_ci:
image: semaphoreui/semaphore:ci-compose
build:
context: ./../../../
dockerfile: ./deployment/docker/ci/Dockerfile
environment:
SEMAPHORE_DB_DIALECT: bolt
SEMAPHORE_ADMIN_PASSWORD: password
SEMAPHORE_ADMIN_NAME: "Developer"
SEMAPHORE_ADMIN_EMAIL: admin@localhost
SEMAPHORE_ADMIN: admin
SEMAPHORE_WEB_ROOT: http://0.0.0.0:3000
SEMAPHORE_ACCESS_KEY_ENCRYPTION: "IlRqgrrO5Gp27MlWakDX1xVrPv4jhoUx+ARY+qGyDxQ="
ports:
- "3000:3000"
volumes:
- data:/var/lib/semaphore
dredd:
image: semaphoreui/dredd:ci
command: [ "--config", ".dredd/dredd.yml" ]
environment:
SEMAPHORE_ACCESS_KEY_ENCRYPTION: "IlRqgrrO5Gp27MlWakDX1xVrPv4jhoUx+ARY+qGyDxQ="
SEMAPHORE_DB_DIALECT: bolt
build:
context: ./../../../
dockerfile: ./deployment/docker/ci/dredd.Dockerfile
depends_on:
- semaphore_ci
volumes:
- data:/data

View File

@ -1,49 +0,0 @@
version: '2'
services:
mysql:
image: mysql:5.6
environment:
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
MYSQL_DATABASE: semaphore
MYSQL_USER: semaphore
MYSQL_PASSWORD: semaphore
ports:
- "3306:3306"
semaphore_ci:
image: semaphoreui/semaphore:ci-compose
build:
context: ./../../../
dockerfile: ./deployment/docker/ci/Dockerfile
environment:
SEMAPHORE_DB_DIALECT: mysql
SEMAPHORE_DB_USER: semaphore
SEMAPHORE_DB_PASS: semaphore
SEMAPHORE_DB_HOST: mysql
SEMAPHORE_DB_PORT: 3306
SEMAPHORE_DB: semaphore
SEMAPHORE_ADMIN_PASSWORD: password
SEMAPHORE_ADMIN_NAME: "Developer"
SEMAPHORE_ADMIN_EMAIL: admin@localhost
SEMAPHORE_ADMIN: admin
SEMAPHORE_WEB_ROOT: http://0.0.0.0:3000
SEMAPHORE_ACCESS_KEY_ENCRYPTION: "IlRqgrrO5Gp27MlWakDX1xVrPv4jhoUx+ARY+qGyDxQ="
ports:
- "3000:3000"
depends_on:
- mysql
dredd:
image: ansiblesemaphore/dredd:ci
command: ["--config", ".dredd/dredd.yml"]
environment:
SEMAPHORE_ACCESS_KEY_ENCRYPTION: "IlRqgrrO5Gp27MlWakDX1xVrPv4jhoUx+ARY+qGyDxQ="
SEMAPHORE_DB_DIALECT: mysql
build:
context: ./../../../
dockerfile: ./deployment/docker/ci/dredd.Dockerfile
depends_on:
- semaphore_ci
- mysql

View File

@ -1,48 +0,0 @@
version: '2'
services:
postgres:
image: postgres:14
environment:
POSTGRES_USER: semaphore
POSTGRES_PASSWORD: semaphore
POSTGRES_DB: semaphore
ports:
- "5432:5432"
semaphore_ci:
image: semaphoreui/semaphore:ci-compose
build:
context: ./../../../
dockerfile: ./deployment/docker/ci/Dockerfile
environment:
SEMAPHORE_DB_DIALECT: postgres
SEMAPHORE_DB_USER: semaphore
SEMAPHORE_DB_PASS: semaphore
SEMAPHORE_DB_HOST: postgres
SEMAPHORE_DB_PORT: 5432
SEMAPHORE_DB: semaphore
SEMAPHORE_ADMIN_PASSWORD: password
SEMAPHORE_ADMIN_NAME: "Developer"
SEMAPHORE_ADMIN_EMAIL: admin@localhost
SEMAPHORE_ADMIN: admin
SEMAPHORE_WEB_ROOT: http://0.0.0.0:3000
SEMAPHORE_ACCESS_KEY_ENCRYPTION: "IlRqgrrO5Gp27MlWakDX1xVrPv4jhoUx+ARY+qGyDxQ="
ports:
- "3000:3000"
depends_on:
- postgres
dredd:
image: ansiblesemaphore/dredd:ci
command: ["--config", ".dredd/dredd.yml"]
environment:
SEMAPHORE_ACCESS_KEY_ENCRYPTION: "IlRqgrrO5Gp27MlWakDX1xVrPv4jhoUx+ARY+qGyDxQ="
SEMAPHORE_DB_DIALECT: postgres
build:
context: ./../../../
dockerfile: ./deployment/docker/ci/dredd.Dockerfile
depends_on:
- semaphore_ci
- postgres

View File

@ -1,27 +0,0 @@
FROM golang:1.21-alpine3.18 as golang
RUN apk add --no-cache curl git
# We need the source and task to compile the hooks
COPY . /semaphore/
RUN (cd /usr && curl -sL https://taskfile.dev/install.sh | sh)
WORKDIR /semaphore
RUN task deps:tools && task deps:be && task compile:be && task compile:api:hooks
FROM apiaryio/dredd:13.0.0 as dredd
RUN apk add --no-cache bash go git
RUN go get github.com/snikch/goodman/cmd/goodman
COPY --from=golang /semaphore /semaphore
WORKDIR /semaphore
COPY deployment/docker/ci/dredd/entrypoint /usr/local/bin
COPY deployment/docker/ci/dredd/gen-config-bolt /usr/local/bin
COPY deployment/docker/ci/dredd/gen-config-mysql /usr/local/bin
COPY deployment/docker/ci/dredd/gen-config-postgres /usr/local/bin
ENTRYPOINT ["/usr/local/bin/entrypoint"]

View File

@ -1,17 +0,0 @@
#!/usr/bin/env bash
set -e
echo "---> Add Config"
/usr/local/bin/gen-config-${SEMAPHORE_DB_DIALECT} > /semaphore/.dredd/config.json
echo "---> Waiting for semaphore"
while ! nc -z semaphore_ci 3000; do
sleep 1
done
echo "---> Run Dredd"
# We do this because otherwise it can fail out
sleep 5
dredd $@

View File

@ -1,11 +0,0 @@
#!/usr/bin/env bash
cat <<EOF
{
"bolt": {
"host": "/data/database.boltdb"
},
"dialect": "bolt",
"access_key_encryption": "${SEMAPHORE_ACCESS_KEY_ENCRYPTION}"
}
EOF

View File

@ -1,15 +0,0 @@
#!/usr/bin/env bash
cat <<EOF
{
"mysql": {
"host": "mysql:3306",
"user": "semaphore",
"pass": "semaphore",
"name": "semaphore"
},
"dialect": "mysql",
"access_key_encryption": "${SEMAPHORE_ACCESS_KEY_ENCRYPTION}"
}
EOF

View File

@ -1,17 +0,0 @@
#!/usr/bin/env bash
cat <<EOF
{
"postgres": {
"host": "postgres:5432",
"user": "semaphore",
"pass": "semaphore",
"name": "semaphore",
"options": {
"sslmode": "disable"
}
},
"dialect": "postgres",
"access_key_encryption": "${SEMAPHORE_ACCESS_KEY_ENCRYPTION}"
}
EOF

View File

@ -1,14 +0,0 @@
# semaphore-wrapper
## What it does
`semaphore-wrapper` generates `config.json` using `setup` command and execute provided command.
## How to test semaphore-wrapper
```bash
SEMAPHORE_DB_DIALECT=bolt \
SEMAPHORE_CONFIG_PATH=/tmp/semaphore \
SEMAPHORE_DB_HOST=/tmp/semaphore \
./semaphore-wrapper ../../../bin/semaphore server --config /tmp/semaphore/config.json
```

View File

@ -1,20 +0,0 @@
#!/bin/sh
echo Loading environment...
source /home/semaphore/venv/bin/activate
echo Staring runner...
export ANSIBLE_HOST_KEY_CHECKING=False
export ANSIBLE_STDOUT_CALLBACK=yaml
export ANSIBLE_LOAD_CALLBACK_PLUGINS=yes
export ANSIBLE_CONFIG=/tmp/semaphore/ansible.cfg
tee "/tmp/semaphore/ansible.cfg" <<CONFIG
[defaults]
host_key_checking = False
stdout_callback = yaml
bin_ansible_callbacks = yes
CONFIG
/usr/local/bin/semaphore runner start --config /etc/semaphore/config.json

View File

@ -1,173 +0,0 @@
#!/bin/sh
set -e
echoerr() { printf "%s\n" "$*" >&2; }
file_env() {
local var=""
local fileVar=""
eval var="\$${1}"
eval fileVar="\$${1}_FILE"
local def="${2:-}"
if [ -n "${var:-}" ] && [ -n "${fileVar:-}" ]; then
echo >&2 "error: both ${1} and ${1}_FILE are set (but are exclusive)"
exit 1
fi
local val="$def"
if [ -n "${var:-}" ]; then
val="${var}"
elif [ -n "${fileVar:-}" ]; then
val="$(cat "${fileVar}")"
fi
export "${1}"="$val"
unset "${1}_FILE"
}
SEMAPHORE_CONFIG_PATH="${SEMAPHORE_CONFIG_PATH:-/etc/semaphore}"
SEMAPHORE_TMP_PATH="${SEMAPHORE_TMP_PATH:-/tmp/semaphore}"
# Semaphore database env config
SEMAPHORE_DB_DIALECT="${SEMAPHORE_DB_DIALECT:-mysql}"
SEMAPHORE_DB_HOST="${SEMAPHORE_DB_HOST:-0.0.0.0}"
SEMAPHORE_DB_PATH="${SEMAPHORE_DB_PATH:-/var/lib/semaphore}"
SEMAPHORE_DB_PORT="${SEMAPHORE_DB_PORT:-}"
SEMAPHORE_DB="${SEMAPHORE_DB:-semaphore}"
file_env 'SEMAPHORE_DB_USER' 'semaphore'
file_env 'SEMAPHORE_DB_PASS' 'semaphore'
# Email alert env config
SEMAPHORE_WEB_ROOT="${SEMAPHORE_WEB_ROOT:-}"
# Semaphore Admin env config
file_env 'SEMAPHORE_ADMIN' 'admin'
SEMAPHORE_ADMIN_EMAIL="${SEMAPHORE_ADMIN_EMAIL:-admin@localhost}"
SEMAPHORE_ADMIN_NAME="${SEMAPHORE_ADMIN_NAME:-Semaphore Admin}"
file_env 'SEMAPHORE_ADMIN_PASSWORD' 'semaphorepassword'
#Semaphore LDAP env config
SEMAPHORE_LDAP_ACTIVATED="${SEMAPHORE_LDAP_ACTIVATED:-no}"
SEMAPHORE_LDAP_HOST="${SEMAPHORE_LDAP_HOST:-}"
SEMAPHORE_LDAP_PORT="${SEMAPHORE_LDAP_PORT:-}"
SEMAPHORE_LDAP_NEEDTLS="${SEMAPHORE_LDAP_NEEDTLS:-no}"
SEMAPHORE_LDAP_DN_BIND="${SEMAPHORE_LDAP_DN_BIND:-}"
file_env 'SEMAPHORE_LDAP_PASSWORD'
SEMAPHORE_LDAP_DN_SEARCH="${SEMAPHORE_LDAP_DN_SEARCH:-}"
SEMAPHORE_LDAP_SEARCH_FILTER="${SEMAPHORE_LDAP_SEARCH_FILTER:-(uid=%s)}"
SEMAPHORE_LDAP_MAPPING_DN="${SEMAPHORE_LDAP_MAPPING_DN:-dn}"
SEMAPHORE_LDAP_MAPPING_USERNAME="${SEMAPHORE_LDAP_MAPPING_USERNAME:-uid}"
SEMAPHORE_LDAP_MAPPING_FULLNAME="${SEMAPHORE_LDAP_MAPPING_FULLNAME:-cn}"
SEMAPHORE_LDAP_MAPPING_EMAIL="${SEMAPHORE_LDAP_MAPPING_EMAIL:-mail}"
file_env 'SEMAPHORE_ACCESS_KEY_ENCRYPTION'
[ -d "${SEMAPHORE_TMP_PATH}" ] || mkdir -p "${SEMAPHORE_TMP_PATH}" || {
echo "Can't create Semaphore tmp path ${SEMAPHORE_TMP_PATH}."
exit 1
}
[ -d "${SEMAPHORE_CONFIG_PATH}" ] || mkdir -p "${SEMAPHORE_CONFIG_PATH}" || {
echo "Can't create Semaphore Config path ${SEMAPHORE_CONFIG_PATH}."
exit 1
}
[ -d "${SEMAPHORE_DB_PATH}" ] || mkdir -p "${SEMAPHORE_DB_PATH}" || {
echo "Can't create data path ${SEMAPHORE_DB_PATH}."
exit 1
}
# wait on db to be up
if [ "${SEMAPHORE_DB_DIALECT}" != 'bolt' ]; then
echoerr "Attempting to connect to database ${SEMAPHORE_DB} on ${SEMAPHORE_DB_HOST}:${SEMAPHORE_DB_PORT} with user ${SEMAPHORE_DB_USER} ..."
TIMEOUT=30
while ! $(nc -z "$SEMAPHORE_DB_HOST" "$SEMAPHORE_DB_PORT") >/dev/null 2>&1; do
TIMEOUT=$(expr $TIMEOUT - 1)
if [ $TIMEOUT -eq 0 ]; then
echoerr "Could not connect to database server. Exiting."
exit 1
fi
echo -n "."
sleep 1
done
fi
if [ -n "${SEMAPHORE_DB_PORT}" ]; then
SEMAPHORE_DB_HOST="${SEMAPHORE_DB_HOST}:${SEMAPHORE_DB_PORT}"
fi
case ${SEMAPHORE_DB_DIALECT} in
mysql) SEMAPHORE_DB_DIALECT_ID=1;;
bolt) SEMAPHORE_DB_DIALECT_ID=2;;
postgres) SEMAPHORE_DB_DIALECT_ID=3;;
*)
echoerr "Unknown database dialect: ${SEMAPHORE_DB_DIALECT}"
exit 1
;;
esac
# Create a config if it does not exist in the current config path
if [ ! -f "${SEMAPHORE_CONFIG_PATH}/config.json" ]; then
echoerr "Generating ${SEMAPHORE_TMP_PATH}/config.stdin ..."
cat << EOF > "${SEMAPHORE_TMP_PATH}/config.stdin"
${SEMAPHORE_DB_DIALECT_ID}
EOF
if [ "${SEMAPHORE_DB_DIALECT}" = "bolt" ]; then
cat << EOF >> "${SEMAPHORE_TMP_PATH}/config.stdin"
${SEMAPHORE_DB_PATH}/database.boltdb
EOF
else
cat << EOF >> "${SEMAPHORE_TMP_PATH}/config.stdin"
${SEMAPHORE_DB_HOST}
${SEMAPHORE_DB_USER}
${SEMAPHORE_DB_PASS}
${SEMAPHORE_DB}
EOF
fi
cat << EOF >> "${SEMAPHORE_TMP_PATH}/config.stdin"
${SEMAPHORE_TMP_PATH}
${SEMAPHORE_WEB_ROOT}
no
no
no
no
no
${SEMAPHORE_LDAP_ACTIVATED}
EOF
if [ "${SEMAPHORE_LDAP_ACTIVATED}" = "yes" ]; then
cat << EOF >> "${SEMAPHORE_TMP_PATH}/config.stdin"
${SEMAPHORE_LDAP_HOST}:${SEMAPHORE_LDAP_PORT}
${SEMAPHORE_LDAP_NEEDTLS}
${SEMAPHORE_LDAP_DN_BIND}
${SEMAPHORE_LDAP_PASSWORD}
${SEMAPHORE_LDAP_DN_SEARCH}
${SEMAPHORE_LDAP_SEARCH_FILTER}
${SEMAPHORE_LDAP_MAPPING_DN}
${SEMAPHORE_LDAP_MAPPING_USERNAME}
${SEMAPHORE_LDAP_MAPPING_FULLNAME}
${SEMAPHORE_LDAP_MAPPING_EMAIL}
EOF
fi;
cat << EOF >> "${SEMAPHORE_TMP_PATH}/config.stdin"
${SEMAPHORE_CONFIG_PATH}
${SEMAPHORE_ADMIN}
${SEMAPHORE_ADMIN_EMAIL}
${SEMAPHORE_ADMIN_NAME}
${SEMAPHORE_ADMIN_PASSWORD}
EOF
$1 setup - < "${SEMAPHORE_TMP_PATH}/config.stdin"
echoerr "Run Semaphore with semaphore server --config ${SEMAPHORE_CONFIG_PATH}/config.json"
fi
# Install additional python requirements
if [ -f "${SEMAPHORE_CONFIG_PATH}/requirements.txt" ]; then
echoerr "Installing additional python packages"
pip3 install --upgrade --user -r "${SEMAPHORE_CONFIG_PATH}/requirements.txt"
else
echoerr "No additional python packages to install"
fi
# run our command
exec "$@"

View File

@ -1,40 +0,0 @@
FROM golang:1.21-alpine3.18
ENV SEMAPHORE_VERSION="development" SEMAPHORE_ARCH="linux_amd64" \
SEMAPHORE_CONFIG_PATH="${SEMAPHORE_CONFIG_PATH:-/etc/semaphore}" \
APP_ROOT="/go/src/github.com/ansible-semaphore/semaphore/"
# hadolint ignore=DL3013
RUN apk add --no-cache gcc g++ sshpass git mysql-client python3 py3-pip py-openssl openssl ca-certificates curl curl-dev openssh-client-default tini nodejs npm bash rsync tzdata zip unzip tar && \
apk --update add --virtual build-dependencies python3-dev libffi-dev openssl-dev build-base &&\
rm -rf /var/cache/apk/*
RUN pip3 install --upgrade pip cffi &&\
apk del build-dependencies && \
pip3 install ansible
RUN adduser -D -u 1002 -g 0 semaphore && \
mkdir -p /go/src/github.com/ansible-semaphore/semaphore && \
mkdir -p /tmp/semaphore && \
mkdir -p /etc/semaphore && \
mkdir -p /var/lib/semaphore && \
chown -R semaphore:0 /go && \
chown -R semaphore:0 /tmp/semaphore && \
chown -R semaphore:0 /etc/semaphore && \
chown -R semaphore:0 /var/lib/semaphore && \
ssh-keygen -t rsa -q -f "/root/.ssh/id_rsa" -N "" && \
ssh-keyscan -H github.com > /root/.ssh/known_hosts
RUN cd $(go env GOPATH) && curl -sL https://taskfile.dev/install.sh | sh
RUN git config --global --add safe.directory /go/src/github.com/ansible-semaphore/semaphore
# Copy in app source
WORKDIR ${APP_ROOT}
COPY . ${APP_ROOT}
RUN deployment/docker/dev/bin/install
USER semaphore
EXPOSE 3000
ENTRYPOINT ["/usr/local/bin/semaphore-wrapper"]
CMD ["./bin/semaphore", "server", "--config", "/etc/semaphore/config.json"]

View File

@ -1,16 +0,0 @@
#!/usr/bin/env bash
set -e
echo "--> Turn off StrictKeyChecking"
cat > /etc/ssh/ssh_config <<EOF
Host *
StrictHostKeyChecking no
UserKnownHostsFile=/dev/null
EOF
echo "--> Install Semaphore entrypoint wrapper script"
mv ./deployment/docker/common/semaphore-wrapper /usr/local/bin/semaphore-wrapper
mv ./deployment/docker/common/runner-wrapper /usr/local/bin/runner-wrapper
task deps
chmod -R 0777 /go

View File

@ -1,39 +0,0 @@
version: '2'
services:
mysql:
image: mariadb:10
environment:
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
MYSQL_DATABASE: semaphore
MYSQL_USER: semaphore
MYSQL_PASSWORD: semaphore
## uncomment if you want to store mysql data between launches
#volumes:
# - /tmp/mysql_data:/var/lib/mysql
ports:
- "3306:3306"
semaphore_dev:
image: semaphoreui/semaphore:dev-compose
build:
context: ./../../../
dockerfile: ./deployment/docker/dev/Dockerfile
volumes:
- "./../../../:/go/src/github.com/ansible-semaphore/semaphore:rw"
environment:
SEMAPHORE_DB_DIALECT: mysql
SEMAPHORE_DB_USER: semaphore
SEMAPHORE_DB_PASS: semaphore
SEMAPHORE_DB_HOST: mysql
SEMAPHORE_DB_PORT: 3306
SEMAPHORE_DB: semaphore
SEMAPHORE_ADMIN_PASSWORD: password
SEMAPHORE_ADMIN_NAME: "Developer"
SEMAPHORE_ADMIN_EMAIL: admin@localhost
SEMAPHORE_ADMIN: admin
SEMAPHORE_WEB_ROOT: http://0.0.0.0:3000
ports:
- "3000:3000"
depends_on:
- mysql

View File

@ -0,0 +1,28 @@
FROM golang:1.22-alpine3.19 as golang
RUN apk add --no-cache -U \
curl git
WORKDIR /usr/local
# hadolint ignore=DL4006
RUN curl -sL https://taskfile.dev/install.sh | sh
COPY . /semaphore/
WORKDIR /semaphore
RUN task deps:tools && \
task deps:be && \
task e2e:hooks
FROM apiaryio/dredd:13.0.0
RUN apk add --no-cache -U \
bash go git
RUN go get github.com/snikch/goodman/cmd/goodman
COPY --from=golang /semaphore /semaphore
WORKDIR /semaphore
COPY deployment/docker/dredd/entrypoint /usr/local/bin
ENTRYPOINT ["/usr/local/bin/entrypoint"]

View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -eo pipefail
echo "---> Gen semaphore config"
cat << EOF > /semaphore/.dredd/config.json
{
"dialect": "${SEMAPHORE_DB_DIALECT}",
"${SEMAPHORE_DB_DIALECT}": ${SEMAPHORE_DB_CONFIG},
"access_key_encryption": "${SEMAPHORE_ACCESS_KEY_ENCRYPTION}"
}
EOF
echo "---> Waiting for semaphore"
while ! nc -z server 3000; do
sleep 1
done
echo "---> Start dredd server"
sleep 5
dredd $@

View File

@ -1,32 +0,0 @@
# ansible-semaphore production image
FROM golang:1.21-alpine3.18 as builder
COPY ./ /go/src/github.com/ansible-semaphore/semaphore
WORKDIR /go/src/github.com/ansible-semaphore/semaphore
RUN apk add --no-cache -U libc-dev curl nodejs npm git gcc g++ && \
./deployment/docker/prod/bin/install
FROM alpine:3.18 as runner
LABEL maintainer="Tom Whiston <tom.whiston@gmail.com>"
RUN apk add --no-cache sshpass git curl ansible mysql-client openssh-client-default tini py3-aiohttp tzdata zip unzip tar py3-pip && \
adduser -D -u 1001 -G root semaphore && \
mkdir -p /tmp/semaphore && \
mkdir -p /etc/semaphore && \
mkdir -p /var/lib/semaphore && \
chown -R semaphore:0 /tmp/semaphore && \
chown -R semaphore:0 /etc/semaphore && \
chown -R semaphore:0 /var/lib/semaphore
COPY --from=builder /usr/local/bin/semaphore-wrapper /usr/local/bin/
COPY --from=builder /usr/local/bin/semaphore /usr/local/bin/
RUN chown -R semaphore:0 /usr/local/bin/semaphore-wrapper &&\
chown -R semaphore:0 /usr/local/bin/semaphore
WORKDIR /home/semaphore
USER 1001
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["/usr/local/bin/semaphore-wrapper", "/usr/local/bin/semaphore", "server", "--config", "/etc/semaphore/config.json"]

View File

@ -1,24 +0,0 @@
#!/usr/bin/env sh
set -e
# Get prerequisites for building the app
(cd $(go env GOPATH) && curl -sL https://taskfile.dev/install.sh | sh)
# Compile and build
task deps
set +e
task compile
set -e
task build:local GOOS=$1 GOARCH=$2
mv ./bin/semaphore /usr/local/bin/
chown 1001:0 /usr/local/bin/semaphore
chmod +x /usr/local/bin/semaphore
mv ./deployment/docker/common/semaphore-wrapper /usr/local/bin/semaphore-wrapper
chown 1001:0 /usr/local/bin/semaphore-wrapper
mv ./deployment/docker/common/runner-wrapper /usr/local/bin/runner-wrapper
chown 1001:0 /usr/local/bin/runner-wrapper

View File

@ -1,37 +0,0 @@
# ansible-semaphore production image
FROM --platform=$BUILDPLATFORM golang:1.21-alpine3.18 as builder
COPY ./ /go/src/github.com/ansible-semaphore/semaphore
WORKDIR /go/src/github.com/ansible-semaphore/semaphore
ARG TARGETOS
ARG TARGETARCH
RUN apk add --no-cache -U libc-dev curl nodejs npm git gcc
RUN ./deployment/docker/prod/bin/install ${TARGETOS} ${TARGETARCH}
FROM alpine:3.18 as runner
LABEL maintainer="Tom Whiston <tom.whiston@gmail.com>"
RUN apk add --no-cache sshpass git curl ansible mysql-client openssh-client-default tini py3-aiohttp tzdata py3-pip && \
adduser -D -u 1001 -G root semaphore && \
mkdir -p /tmp/semaphore && \
mkdir -p /etc/semaphore && \
mkdir -p /var/lib/semaphore && \
chown -R semaphore:0 /tmp/semaphore && \
chown -R semaphore:0 /etc/semaphore && \
chown -R semaphore:0 /var/lib/semaphore
COPY --from=builder /usr/local/bin/semaphore-wrapper /usr/local/bin/
COPY --from=builder /usr/local/bin/semaphore /usr/local/bin/
RUN chown -R semaphore:0 /usr/local/bin/semaphore-wrapper &&\
chown -R semaphore:0 /usr/local/bin/semaphore &&\
chmod +x /usr/local/bin/semaphore-wrapper &&\
chmod +x /usr/local/bin/semaphore
WORKDIR /home/semaphore
USER 1001
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["/usr/local/bin/semaphore-wrapper", "/usr/local/bin/semaphore", "server", "--config", "/etc/semaphore/config.json"]

View File

@ -1,41 +0,0 @@
# This dockerfile provides an example of using the production image in a working stack
version: '2'
services:
mysql:
image: mysql:5.6
hostname: mysql
environment:
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
MYSQL_DATABASE: semaphore
MYSQL_USER: semaphore
MYSQL_PASSWORD: hx4hjxqthfwbfsy5535u4agfdscm
semaphore:
image: semaphoreui/semaphore:prod-compose
build:
context: ./../../../
dockerfile: ./deployment/docker/prod/Dockerfile
environment:
SEMAPHORE_DB_DIALECT: mysql
SEMAPHORE_DB_USER: semaphore
SEMAPHORE_DB_PASS: hx4hjxqthfwbfsy5535u4agfdscm
SEMAPHORE_DB_HOST: mysql
SEMAPHORE_DB_PORT: 3306
SEMAPHORE_DB: semaphore
SEMAPHORE_ADMIN_PASSWORD: cangetin
SEMAPHORE_ADMIN_NAME: admin
SEMAPHORE_ADMIN_EMAIL: admin@localhost
SEMAPHORE_ADMIN: admin
depends_on:
- mysql
semaphore_proxy:
image: ansiblesemaphore/proxy:latest
build:
context: ./proxy
ports:
- "80:80"
- "443:443"
depends_on:
- semaphore

View File

@ -1,11 +0,0 @@
FROM nginx:alpine
LABEL maintainer="Tom Whiston <tom.whiston@gmail.com>"
COPY nginx.conf /etc/nginx/nginx.conf
COPY cert/ /etc/nginx/cert
COPY bin /usr/local/bin
RUN apk add --no-cache openssl && \
/usr/local/bin/install && \
apk del openssl && \
rm -rf /var/cache/apk/*

View File

@ -1,6 +0,0 @@
#!/usr/bin/env sh
if [ ! -f /etc/nginx/cert/privkey.pem ] && [ ! -f /etc/nginx/cert/cert.pem ]; then
echo "--> Generating self-signed cert. This should not be used in production"
openssl req -x509 -newkey rsa:4096 -keyout /etc/nginx/cert/privkey.pem -out /etc/nginx/cert/cert.pem -sha256 -nodes -subj "/O=Ansible Semaphore/OU=Production Test/CN=github.com\/ansible-semaphore\/semaphore"
fi

View File

@ -1,68 +0,0 @@
worker_processes auto;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
tcp_nodelay on;
# this is necessary for us to be able to disable request buffering in all cases
proxy_http_version 1.1;
upstream semaphore {
server semaphore:3000;
}
server {
listen [::]:80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name _;
# add Strict-Transport-Security to prevent man in the middle attacks
add_header Strict-Transport-Security "max-age=31536000" always;
# SSL
ssl_certificate /etc/nginx/cert/cert.pem;
ssl_certificate_key /etc/nginx/cert/privkey.pem;
# Recommendations from https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
ssl_protocols TLSv1.1 TLSv1.2;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
# disable any limits to avoid HTTP 413 for large image uploads
client_max_body_size 0;
# required to avoid HTTP 411: see Issue #1486 (https://github.com/docker/docker/issues/1486)
chunked_transfer_encoding on;
location / {
proxy_pass http://semaphore/;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_request_buffering off;
}
location /api/ws {
proxy_pass http://semaphore/api/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Origin "";
}
}
}

View File

@ -1,32 +0,0 @@
FROM dind-ansible:latest
RUN apk add --no-cache wget git rsync tzdata zip unzip tar
RUN adduser -D -u 1001 -G root -G docker semaphore && \
mkdir -p /tmp/semaphore && \
mkdir -p /etc/semaphore && \
mkdir -p /var/lib/semaphore && \
chown -R semaphore:0 /tmp/semaphore && \
chown -R semaphore:0 /etc/semaphore && \
chown -R semaphore:0 /var/lib/semaphore
RUN wget https://raw.githubusercontent.com/ansible-semaphore/semaphore/develop/deployment/docker/common/runner-wrapper -P /usr/local/bin/ && chmod +x /usr/local/bin/runner-wrapper
RUN wget https://github.com/ansible-semaphore/semaphore/releases/download/v2.9.37/semaphore_2.9.37_linux_amd64.tar.gz -O - | tar -xz -C /usr/local/bin/ semaphore
RUN chown -R semaphore:0 /usr/local/bin/runner-wrapper &&\
chown -R semaphore:0 /usr/local/bin/semaphore &&\
chmod +x /usr/local/bin/runner-wrapper &&\
chmod +x /usr/local/bin/semaphore
WORKDIR /home/semaphore
USER 1001
RUN mkdir ./venv
RUN python3 -m venv ./venv --system-site-packages && \
source ./venv/bin/activate && \
pip3 install --upgrade pip boto3 botocore requests
RUN echo '{"tmp_path": "/tmp/semaphore","dialect": "bolt", "runner": {"config_file": "/var/lib/semaphore/runner.json"}}' > /etc/semaphore/config.json
CMD [ "/usr/local/bin/runner-wrapper" ]

View File

@ -1,46 +0,0 @@
# ansible-semaphore production image
FROM --platform=$BUILDPLATFORM golang:1.21-alpine3.18 as builder
COPY ./ /go/src/github.com/ansible-semaphore/semaphore
WORKDIR /go/src/github.com/ansible-semaphore/semaphore
ARG TARGETOS
ARG TARGETARCH
RUN apk add --no-cache -U libc-dev curl nodejs npm git gcc zip unzip tar
RUN ./deployment/docker/prod/bin/install ${TARGETOS} ${TARGETARCH}
FROM alpine/ansible:latest
RUN apk add --no-cache wget git rsync tzdata
RUN adduser -D -u 1001 -G root semaphore && \
mkdir -p /tmp/semaphore && \
mkdir -p /etc/semaphore && \
mkdir -p /var/lib/semaphore && \
chown -R semaphore:0 /tmp/semaphore && \
chown -R semaphore:0 /etc/semaphore && \
chown -R semaphore:0 /var/lib/semaphore
COPY --from=builder /usr/local/bin/runner-wrapper /usr/local/bin/
COPY --from=builder /usr/local/bin/semaphore /usr/local/bin/
RUN chown -R semaphore:0 /usr/local/bin/runner-wrapper &&\
chown -R semaphore:0 /usr/local/bin/semaphore &&\
chmod +x /usr/local/bin/runner-wrapper &&\
chmod +x /usr/local/bin/semaphore
WORKDIR /home/semaphore
USER 1001
RUN mkdir ./venv
RUN python3 -m venv ./venv --system-site-packages && \
source ./venv/bin/activate && \
pip3 install --upgrade pip
RUN pip3 install boto3 botocore
RUN echo '{"tmp_path": "/tmp/semaphore","dialect": "bolt", "runner": {"config_file": "/var/lib/semaphore/runner.json"}}' > /etc/semaphore/config.json
CMD [ "/usr/local/bin/runner-wrapper" ]

View File

@ -0,0 +1,54 @@
FROM --platform=$BUILDPLATFORM golang:1.22-alpine3.19 as builder
RUN apk add --no-cache -U \
libc-dev curl nodejs npm git gcc zip unzip tar
WORKDIR /usr/local
# hadolint ignore=DL4006
RUN curl -sL https://taskfile.dev/install.sh | sh
COPY ./ /go/src/semaphore
WORKDIR /go/src/semaphore
ARG TARGETOS
ARG TARGETARCH
RUN task deps && \
task compile && \
task build:local GOOS=${TARGETOS} GOARCH=${TARGETARCH}
FROM alpine:3.19
RUN apk add --no-cache -U \
curl git mysql-client openssh-client-default python3 py3-pip rsync sshpass tar tini tzdata unzip wget zip && \
rm -rf /var/cache/apk/* && \
adduser -D -u 1001 -G root semaphore && \
mkdir -p /tmp/semaphore && \
mkdir -p /etc/semaphore && \
mkdir -p /var/lib/semaphore && \
chown -R semaphore:0 /tmp/semaphore && \
chown -R semaphore:0 /etc/semaphore && \
chown -R semaphore:0 /var/lib/semaphore
COPY --chown=1001:0 ./deployment/docker/runner/ansible.cfg /tmp/semaphore/ansible.cfg
COPY --from=builder /go/src/semaphore/deployment/docker/runner/runner-wrapper /usr/local/bin/
COPY --from=builder /go/src/semaphore/bin/semaphore /usr/local/bin/
RUN chown -R semaphore:0 /usr/local/bin/runner-wrapper && \
chmod +x /usr/local/bin/runner-wrapper && \
chown -R semaphore:0 /usr/local/bin/semaphore && \
chmod +x /usr/local/bin/semaphore
WORKDIR /home/semaphore
USER 1001
# renovate: datasource=pypi depName=ansible
ENV ANSIBLE_VERSION 9.4.0
# hadolint ignore=DL3013
RUN pip3 install -U --break-system-packages --no-cache-dir \
ansible==${ANSIBLE_VERSION} \
boto3 botocore requests netaddr
ENTRYPOINT ["/sbin/tini", "--"]
CMD [ "/usr/local/bin/runner-wrapper"]

View File

@ -0,0 +1,4 @@
[defaults]
host_key_checking = False
bin_ansible_callbacks = True
stdout_callback = yaml

View File

@ -1,5 +1,5 @@
file:
/usr/local/bin/semaphore-wrapper:
/usr/local/bin/runner-wrapper:
exists: true
owner: semaphore
group: root
@ -9,31 +9,51 @@ file:
owner: semaphore
group: root
filetype: file
package:
ansible:
installed: true
curl:
installed: true
git:
installed: true
go:
installed: false
libc-dev:
installed: false
mysql-client:
installed: true
nodejs:
installed: false
curl:
installed: true
git:
installed: true
mysql-client:
installed: true
openssh-client-default:
installed: true
python3:
installed: true
py3-pip:
installed: true
rsync:
installed: true
sshpass:
installed: true
tar:
installed: true
tini:
installed: true
tzdata:
installed: true
unzip:
installed: true
wget:
installed: true
zip:
installed: true
user:
semaphore:
exists: true
uid: 1001
gid: 0
home: /home/semaphore
command:
semaphore:
exit-status: 0

View File

@ -0,0 +1,30 @@
#!/bin/sh
set -e
echoerr() { printf "%s\n" "$*" >&2; }
export SEMAPHORE_CONFIG_PATH="${SEMAPHORE_CONFIG_PATH:-/etc/semaphore}"
export SEMAPHORE_TMP_PATH="${SEMAPHORE_TMP_PATH:-/tmp/semaphore}"
export ANSIBLE_CONFIG="${ANSIBLE_CONFIG:-${SEMAPHORE_TMP_PATH}/ansible.cfg}"
if test -f "${SEMAPHORE_CONFIG_PATH}/packages.txt"; then
echoerr "Installing additional system dependencies"
apk add --no-cache --upgrade \
$(cat "${SEMAPHORE_CONFIG_PATH}/packages.txt" | xargs)
else
echoerr "No additional system dependencies to install"
fi
if test -f "${SEMAPHORE_CONFIG_PATH}/requirements.yaml"; then
echoerr "Installing additional python dependencies"
pip3 install --upgrade --user \
-r "${SEMAPHORE_CONFIG_PATH}/requirements.yaml"
else
echoerr "No additional python dependencies to install"
fi
echoerr "Starting semaphore runner"
if test "$#" -ne 1; then
exec /usr/local/bin/semaphore runner start --config "${SEMAPHORE_CONFIG_PATH}/config.json"
else
exec "$@"
fi

View File

@ -0,0 +1,54 @@
FROM --platform=$BUILDPLATFORM golang:1.22-alpine3.19 as builder
RUN apk add --no-cache -U \
libc-dev curl nodejs npm git gcc zip unzip tar
WORKDIR /usr/local
# hadolint ignore=DL4006
RUN curl -sL https://taskfile.dev/install.sh | sh
COPY ./ /go/src/semaphore
WORKDIR /go/src/semaphore
ARG TARGETOS
ARG TARGETARCH
RUN task deps && \
task compile && \
task build:local GOOS=${TARGETOS} GOARCH=${TARGETARCH}
FROM alpine:3.19
RUN apk add --no-cache -U \
curl git mysql-client openssh-client-default python3 py3-pip rsync sshpass tar tini tzdata unzip wget zip && \
rm -rf /var/cache/apk/* && \
adduser -D -u 1001 -G root semaphore && \
mkdir -p /tmp/semaphore && \
mkdir -p /etc/semaphore && \
mkdir -p /var/lib/semaphore && \
chown -R semaphore:0 /tmp/semaphore && \
chown -R semaphore:0 /etc/semaphore && \
chown -R semaphore:0 /var/lib/semaphore
COPY --chown=1001:0 ./deployment/docker/server/ansible.cfg /tmp/semaphore/ansible.cfg
COPY --from=builder /go/src/semaphore/deployment/docker/server/server-wrapper /usr/local/bin/
COPY --from=builder /go/src/semaphore/bin/semaphore /usr/local/bin/
RUN chown -R semaphore:0 /usr/local/bin/server-wrapper && \
chmod +x /usr/local/bin/server-wrapper && \
chown -R semaphore:0 /usr/local/bin/semaphore && \
chmod +x /usr/local/bin/semaphore
WORKDIR /home/semaphore
USER 1001
# renovate: datasource=pypi depName=ansible
ENV ANSIBLE_VERSION 9.4.0
# hadolint ignore=DL3013
RUN pip3 install -U --break-system-packages --no-cache-dir \
ansible==${ANSIBLE_VERSION} \
boto3 botocore requests netaddr
ENTRYPOINT ["/sbin/tini", "--"]
CMD [ "/usr/local/bin/server-wrapper"]

View File

@ -0,0 +1,4 @@
[defaults]
host_key_checking = False
bin_ansible_callbacks = True
stdout_callback = yaml

View File

@ -0,0 +1,60 @@
file:
/usr/local/bin/server-wrapper:
exists: true
owner: semaphore
group: root
filetype: file
/usr/local/bin/semaphore:
exists: true
owner: semaphore
group: root
filetype: file
package:
go:
installed: false
libc-dev:
installed: false
nodejs:
installed: false
curl:
installed: true
git:
installed: true
mysql-client:
installed: true
openssh-client-default:
installed: true
python3:
installed: true
py3-pip:
installed: true
rsync:
installed: true
sshpass:
installed: true
tar:
installed: true
tini:
installed: true
tzdata:
installed: true
unzip:
installed: true
wget:
installed: true
zip:
installed: true
user:
semaphore:
exists: true
uid: 1001
gid: 0
home: /home/semaphore
command:
semaphore:
exit-status: 0
timeout: 10000

View File

@ -0,0 +1,194 @@
#!/bin/sh
set -e
echoerr() { printf "%s\n" "$*" >&2; }
file_env() {
local var=""
local fileVar=""
eval var="\$${1}"
eval fileVar="\$${1}_FILE"
local def="${2:-}"
if [ -n "${var:-}" ] && [ -n "${fileVar:-}" ]; then
echo >&2 "error: both ${1} and ${1}_FILE are set (but are exclusive)"
exit 1
fi
local val="$def"
if [ -n "${var:-}" ]; then
val="${var}"
elif [ -n "${fileVar:-}" ]; then
val="$(cat "${fileVar}")"
fi
export "${1}"="$val"
unset "${1}_FILE"
}
export SEMAPHORE_CONFIG_PATH="${SEMAPHORE_CONFIG_PATH:-/etc/semaphore}"
export SEMAPHORE_TMP_PATH="${SEMAPHORE_TMP_PATH:-/tmp/semaphore}"
export ANSIBLE_CONFIG="${ANSIBLE_CONFIG:-${SEMAPHORE_TMP_PATH}/ansible.cfg}"
export SEMAPHORE_DB_DIALECT="${SEMAPHORE_DB_DIALECT:-mysql}"
export SEMAPHORE_DB_HOST="${SEMAPHORE_DB_HOST:-0.0.0.0}"
export SEMAPHORE_DB_PATH="${SEMAPHORE_DB_PATH:-/var/lib/semaphore}"
export SEMAPHORE_DB_PORT="${SEMAPHORE_DB_PORT:-}"
export SEMAPHORE_DB="${SEMAPHORE_DB:-semaphore}"
file_env 'SEMAPHORE_DB_USER' 'semaphore'
file_env 'SEMAPHORE_DB_PASS' 'semaphore'
export SEMAPHORE_WEB_ROOT="${SEMAPHORE_WEB_ROOT:-}"
file_env 'SEMAPHORE_ADMIN' 'admin'
export SEMAPHORE_ADMIN_EMAIL="${SEMAPHORE_ADMIN_EMAIL:-admin@localhost}"
export SEMAPHORE_ADMIN_NAME="${SEMAPHORE_ADMIN_NAME:-Semaphore Admin}"
file_env 'SEMAPHORE_ADMIN_PASSWORD' 'semaphorepassword'
export SEMAPHORE_LDAP_ACTIVATED="${SEMAPHORE_LDAP_ACTIVATED:-no}"
export SEMAPHORE_LDAP_HOST="${SEMAPHORE_LDAP_HOST:-}"
export SEMAPHORE_LDAP_PORT="${SEMAPHORE_LDAP_PORT:-}"
export SEMAPHORE_LDAP_NEEDTLS="${SEMAPHORE_LDAP_NEEDTLS:-no}"
export SEMAPHORE_LDAP_DN_BIND="${SEMAPHORE_LDAP_DN_BIND:-}"
file_env 'SEMAPHORE_LDAP_PASSWORD'
export SEMAPHORE_LDAP_DN_SEARCH="${SEMAPHORE_LDAP_DN_SEARCH:-}"
export SEMAPHORE_LDAP_SEARCH_FILTER="${SEMAPHORE_LDAP_SEARCH_FILTER:-(uid=%s)}"
export SEMAPHORE_LDAP_MAPPING_DN="${SEMAPHORE_LDAP_MAPPING_DN:-dn}"
export SEMAPHORE_LDAP_MAPPING_USERNAME="${SEMAPHORE_LDAP_MAPPING_USERNAME:-uid}"
export SEMAPHORE_LDAP_MAPPING_FULLNAME="${SEMAPHORE_LDAP_MAPPING_FULLNAME:-cn}"
export SEMAPHORE_LDAP_MAPPING_EMAIL="${SEMAPHORE_LDAP_MAPPING_EMAIL:-mail}"
file_env 'SEMAPHORE_ACCESS_KEY_ENCRYPTION'
[ -d "${SEMAPHORE_TMP_PATH}" ] || mkdir -p "${SEMAPHORE_TMP_PATH}" || {
echo "Can't create Semaphore tmp path ${SEMAPHORE_TMP_PATH}."
exit 1
}
[ -d "${SEMAPHORE_CONFIG_PATH}" ] || mkdir -p "${SEMAPHORE_CONFIG_PATH}" || {
echo "Can't create Semaphore config path ${SEMAPHORE_CONFIG_PATH}."
exit 1
}
[ -d "${SEMAPHORE_DB_PATH}" ] || mkdir -p "${SEMAPHORE_DB_PATH}" || {
echo "Can't create Semaphore data path ${SEMAPHORE_DB_PATH}."
exit 1
}
if [ "${SEMAPHORE_DB_DIALECT}" != 'bolt' ]; then
echoerr "Attempting to connect to database ${SEMAPHORE_DB} on ${SEMAPHORE_DB_HOST}:${SEMAPHORE_DB_PORT} with user ${SEMAPHORE_DB_USER} ..."
TIMEOUT=30
while ! $(nc -z "$SEMAPHORE_DB_HOST" "$SEMAPHORE_DB_PORT") >/dev/null 2>&1; do
TIMEOUT=$(expr $TIMEOUT - 1)
if [ $TIMEOUT -eq 0 ]; then
echoerr "Could not connect to database server. Exiting."
exit 1
fi
echo -n "."
sleep 1
done
fi
if [ -n "${SEMAPHORE_DB_PORT}" ]; then
SEMAPHORE_DB_HOST="${SEMAPHORE_DB_HOST}:${SEMAPHORE_DB_PORT}"
fi
case ${SEMAPHORE_DB_DIALECT} in
mysql)
SEMAPHORE_DB_DIALECT_ID=1
;;
bolt)
SEMAPHORE_DB_DIALECT_ID=2
;;
postgres)
SEMAPHORE_DB_DIALECT_ID=3
;;
*)
echoerr "Unknown database dialect: ${SEMAPHORE_DB_DIALECT}"
exit 1
;;
esac
if [ ! -f "${SEMAPHORE_CONFIG_PATH}/config.json" ]; then
echoerr "Generating ${SEMAPHORE_TMP_PATH}/config.stdin ..."
cat << EOF > "${SEMAPHORE_TMP_PATH}/config.stdin"
${SEMAPHORE_DB_DIALECT_ID}
EOF
if [ "${SEMAPHORE_DB_DIALECT}" = "bolt" ]; then
cat << EOF >> "${SEMAPHORE_TMP_PATH}/config.stdin"
${SEMAPHORE_DB_PATH}/database.boltdb
EOF
else
cat << EOF >> "${SEMAPHORE_TMP_PATH}/config.stdin"
${SEMAPHORE_DB_HOST}
${SEMAPHORE_DB_USER}
${SEMAPHORE_DB_PASS}
${SEMAPHORE_DB}
EOF
fi
cat << EOF >> "${SEMAPHORE_TMP_PATH}/config.stdin"
${SEMAPHORE_TMP_PATH}
${SEMAPHORE_WEB_ROOT}
no
no
no
no
no
${SEMAPHORE_LDAP_ACTIVATED}
EOF
if [ "${SEMAPHORE_LDAP_ACTIVATED}" = "yes" ]; then
cat << EOF >> "${SEMAPHORE_TMP_PATH}/config.stdin"
${SEMAPHORE_LDAP_HOST}:${SEMAPHORE_LDAP_PORT}
${SEMAPHORE_LDAP_NEEDTLS}
${SEMAPHORE_LDAP_DN_BIND}
${SEMAPHORE_LDAP_PASSWORD}
${SEMAPHORE_LDAP_DN_SEARCH}
${SEMAPHORE_LDAP_SEARCH_FILTER}
${SEMAPHORE_LDAP_MAPPING_DN}
${SEMAPHORE_LDAP_MAPPING_USERNAME}
${SEMAPHORE_LDAP_MAPPING_FULLNAME}
${SEMAPHORE_LDAP_MAPPING_EMAIL}
EOF
fi;
cat << EOF >> "${SEMAPHORE_TMP_PATH}/config.stdin"
${SEMAPHORE_CONFIG_PATH}
${SEMAPHORE_ADMIN}
${SEMAPHORE_ADMIN_EMAIL}
${SEMAPHORE_ADMIN_NAME}
${SEMAPHORE_ADMIN_PASSWORD}
EOF
echoerr "Executing semaphore setup"
if test "$#" -ne 1; then
/usr/local/bin/semaphore setup - < "${SEMAPHORE_TMP_PATH}/config.stdin"
else
"$1" setup - < "${SEMAPHORE_TMP_PATH}/config.stdin"
fi
fi
if test -f "${SEMAPHORE_CONFIG_PATH}/packages.txt"; then
echoerr "Installing additional system dependencies"
apk add --no-cache --upgrade \
$(cat "${SEMAPHORE_CONFIG_PATH}/packages.txt" | xargs)
else
echoerr "No additional system dependencies to install"
fi
if test -f "${SEMAPHORE_CONFIG_PATH}/requirements.yaml"; then
echoerr "Installing additional python dependencies"
pip3 install --upgrade --user \
-r "${SEMAPHORE_CONFIG_PATH}/requirements.yaml"
else
echoerr "No additional python dependencies to install"
fi
SEMAPHORE_CONFIG_PATH
echoerr "Starting semaphore server"
if test "$#" -ne 1; then
exec /usr/local/bin/semaphore server --config "${SEMAPHORE_CONFIG_PATH}/config.json"
else
exec "$@"
fi