fix: merge conflict

This commit is contained in:
fiftin 2024-05-21 16:06:30 +02:00
commit 3157397665
No known key found for this signature in database
GPG Key ID: 044381366A5D4731
96 changed files with 2213 additions and 1600 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
web/node_modules/
vendor/

View File

@ -1,8 +1,7 @@
dry-run: null
hookfiles: ./.dredd/compiled_hooks
language: go
#server: context=dev task dc:up
server-wait: 240
server-wait: 5
init: false
custom: {}
names: false
@ -28,4 +27,4 @@ hooks-worker-handler-host: 0.0.0.0
hooks-worker-handler-port: 61321
config: ./.dredd/dredd.yml
blueprint: api-docs.yml
endpoint: 'http://semaphore_ci:3000'
endpoint: 'http://server:3000'

View File

@ -1,8 +1,7 @@
dry-run: null
hookfiles: ./.dredd/compiled_hooks
language: go
#server: context=dev task dc:up
server-wait: 240
server-wait: 5
init: false
custom: {}
names: false

View File

@ -1,8 +1,8 @@
dry-run: null
hookfiles: ./.dredd/compiled_hooks
language: go
#server: context=dev task dc:up
server-wait: 240
server: ./semaphore server --config .dredd/config.json
server-wait: 5
init: false
custom: {}
names: false
@ -28,4 +28,4 @@ hooks-worker-handler-host: 0.0.0.0
hooks-worker-handler-port: 61321
config: ./.dredd/dredd.yml
blueprint: api-docs.yml
endpoint: 'http://semaphore_ci:3000'
endpoint: 'http://localhost:3000'

View File

@ -1,8 +1,7 @@
dry-run: null
hookfiles: ./.dredd/compiled_hooks.exe
language: go
#server: context=dev task dc:up
server-wait: 240
server-wait: 5
init: false
custom: {}
names: false

View File

@ -1,75 +1,127 @@
name: Beta
on:
'on':
push:
tags:
- v*-beta
jobs:
pre-release:
runs-on: [ubuntu-latest]
prerelease:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v3
with: { go-version: '1.21' }
- name: Checkout source
uses: actions/checkout@v4
- uses: actions/setup-node@v3
with: { node-version: '16' }
- name: Setup golang
uses: actions/setup-go@v5
with:
go-version: '^1.21.0'
- run: go install github.com/go-task/task/v3/cmd/task@latest
- name: Setup nodejs
uses: actions/setup-node@v4
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- run: sudo apt update && sudo apt-get install rpm
- name: Install go-task
run: |
go install github.com/go-task/task/v3/cmd/task@latest
- uses: actions/checkout@v3
- name: Install rpm
run: |
sudo apt update && sudo apt-get install rpm
- run: task deps
- name: Install deps
run: |
task deps
- run: |
echo ${{ secrets.GPG_KEY }} | tr " " "\n" | base64 -d | gpg --import --batch
- name: Import gnupg
run: |
echo "${{ secrets.GPG_KEY }}" | tr " " "\n" | base64 -d | gpg --import --batch
gpg --sign -u "58A7 CC3D 8A9C A2E5 BB5C 141D 4064 23EA F814 63CA" --pinentry-mode loopback --yes --batch --passphrase "${{ secrets.GPG_PASS }}" --output unlock.sig --detach-sign README.md
rm -f unlock.sig
- run: git reset --hard
- run: GITHUB_TOKEN=${{ secrets.GH_TOKEN }} task release:prod
- name: Reset repo
run: |
git reset --hard
- name: Run release
run: |
GITHUB_TOKEN=${{ secrets.GH_TOKEN }} task release:prod
deploy-beta:
runs-on: [ubuntu-latest]
runs-on: ubuntu-latest
if: github.repository_owner == 'semaphoreui'
steps:
- uses: actions/setup-go@v3
with: { go-version: '1.21' }
- name: Checkout source
uses: actions/checkout@v4
- run: go install github.com/go-task/task/v3/cmd/task@latest
- name: Setup qemu
id: qemu
uses: docker/setup-qemu-action@v3
- uses: actions/checkout@v3
- name: Setup buildx
id: buildx
uses: docker/setup-buildx-action@v3
- run: context=prod task docker:test
- uses: docker/setup-qemu-action@v2
- uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
- name: Hub login
uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
- name: Build and push
uses: docker/build-push-action@v3
- name: Server meta
id: server
uses: docker/metadata-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
file: ./deployment/docker/prod/buildx.Dockerfile
push: true
tags: semaphoreui/semaphore:beta,semaphoreui/semaphore:${{ github.ref_name }}
github-token: ${{ secrets.GITHUB_TOKEN }}
images: |
semaphoreui/semaphore
labels: |
org.opencontainers.image.vendor=SemaphoreUI
maintainer=Semaphore UI <support@semui.co>
tags: |
type=raw,value=${{ github.ref_name }}
flavor: |
latest=false
- name: Build and push runner
uses: docker/build-push-action@v3
- name: Server build
uses: docker/build-push-action@v5
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
platforms: linux/amd64,linux/arm64
file: ./deployment/docker/prod/runner.buildx.Dockerfile
push: true
tags: semaphoreui/runner:beta,semaphoreui/runner:${{ github.ref_name }}
file: deployment/docker/server/Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v6
push: ${{ github.event_name != 'pull_request' }}
labels: ${{ steps.server.outputs.labels }}
tags: ${{ steps.server.outputs.tags }}
- name: Runner meta
id: runner
uses: docker/metadata-action@v5
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
images: |
semaphoreui/runner
labels: |
org.opencontainers.image.vendor=SemaphoreUI
maintainer=Semaphore UI <support@semui.co>
tags: |
type=raw,value=${{ github.ref_name }}
flavor: |
latest=false
- name: Runner build
uses: docker/build-push-action@v5
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: deployment/docker/runner/Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v6
push: ${{ github.event_name != 'pull_request' }}
labels: ${{ steps.runner.outputs.labels }}
tags: ${{ steps.runner.outputs.tags }}

View File

@ -1,134 +1,610 @@
name: Dev
on:
'on':
push:
branches:
- develop
pull_request:
branches: [develop]
branches:
- develop
jobs:
build-local:
runs-on: [ubuntu-latest]
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v3
with: { go-version: '1.21' }
- name: Checkout source
uses: actions/checkout@v4
- uses: actions/setup-node@v3
with: { node-version: '16' }
- name: Setup golang
uses: actions/setup-go@v5
with:
go-version: '^1.21.0'
- run: go install github.com/go-task/task/v3/cmd/task@latest
- name: Setup nodejs
uses: actions/setup-node@v4
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- uses: actions/checkout@v3
- name: Install go-task
run: |
go install github.com/go-task/task/v3/cmd/task@latest
- run: task deps
- name: Install deps
run: |
task deps
- run: task compile
- name: Run build
run: task build
- name: Test that compile did not create/modify untracked files
run: git diff --exit-code --stat -- . ':(exclude)web/package.json' ':(exclude)web/package-lock.json' ':(exclude)go.mod' ':(exclude)go.sum'
- name: Check modification
run: |
git diff --exit-code --stat -- . ':(exclude)web/package.json' ':(exclude)web/package-lock.json' ':(exclude)go.mod' ':(exclude)go.sum'
- run: task build:local GOOS= GOARCH=
- name: Run tests
run: task test
- run: task test
- uses: actions/upload-artifact@master
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: semaphore
path: bin/semaphore
retention-days: 1
migrate-boltdb:
runs-on: ubuntu-latest
needs:
- build-local
test-db-migration:
runs-on: [ubuntu-latest]
needs: [build-local]
steps:
- uses: shogo82148/actions-setup-mysql@v1
with:
mysql-version: '5.6'
- uses: Harmon758/postgresql-action@v1
with:
postgresql version: '11'
postgresql db: 'circle_test'
postgresql user: 'root'
postgresql password: 'pwd'
- uses: actions/download-artifact@master
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: semaphore
- run: sleep 5
- name: Write config
run: |
cat > config.json <<EOF
{
"bolt": {
"host": "/tmp/database.bolt"
},
"dialect": "bolt",
"email_alert": false
}
EOF
- run: "cat > config.json <<EOF\n{\n\t\"mysql\": {\n\t\t\"host\": \"127.0.0.1:3306\"\
,\n\t\t\"user\": \"root\",\n\t\t\"pass\": \"\",\n\t\t\"name\": \"circle_test\"\
\n\t},\n\t\"dialect\": \"mysql\",\n\t\"email_alert\": false\n}\nEOF\n"
- name: Migrate database
run: |
chmod +x ./semaphore && ./semaphore migrate --config config.json
- run: chmod +x ./semaphore && ./semaphore migrate --config config.json
migrate-mysql:
runs-on: ubuntu-latest
- run: "cat > config.json <<EOF\n{\n\t\"postgres\": {\n\t\t\"host\": \"127.0.0.1:5432\"\
,\n\t\t\"options\":{\"sslmode\":\"disable\"}\
,\n\t\t\"user\": \"root\",\n\t\t\"pass\": \"pwd\",\n\t\t\"name\": \"circle_test\"\
\n\t},\n\t\"dialect\": \"postgres\",\n\t\"email_alert\": false\n}\nEOF\n"
needs:
- build-local
- run: chmod +x ./semaphore && ./semaphore migrate --config config.json
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: p455w0rd
MYSQL_USER: semaphore
MYSQL_PASSWORD: p455w0rd
MYSQL_DATABASE: semaphore
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 3306:3306
- run: "cat > config.json <<EOF\n{\n\t\"bolt\": {\n\t\t\"host\": \"/tmp/database.bolt\"\
\n\t},\n\t\"dialect\": \"bolt\",\n\t\"email_alert\": false\n}\nEOF\n"
- run: chmod +x ./semaphore && ./semaphore migrate --config config.json
test-integration:
runs-on: [ubuntu-latest]
needs: [test-db-migration]
steps:
- uses: actions/setup-go@v3
with: { go-version: '1.21' }
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: semaphore
- run: go install github.com/go-task/task/v3/cmd/task@latest
- name: Write config
run: |
cat > config.json <<EOF
{
"mysql": {
"host": "localhost:3306",
"user": "semaphore",
"pass": "p455w0rd",
"name": "semaphore"
},
"dialect": "mysql",
"email_alert": false
}
EOF
- uses: actions/checkout@v3
- name: Migrate database
run: |
chmod +x ./semaphore && ./semaphore migrate --config config.json
- run: context=ci prefix=.postgres task dc:up
- run: context=ci prefix=.bolt task dc:up
- run: context=ci prefix=.mysql task dc:up
migrate-mariadb:
runs-on: ubuntu-latest
needs:
- build-local
services:
mariadb:
image: mariadb:10.4
env:
MARIADB_ROOT_PASSWORD: p455w0rd
MARIADB_USER: semaphore
MARIADB_PASSWORD: p455w0rd
MARIADB_DATABASE: semaphore
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 3306:3306
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: semaphore
- name: Write config
run: |
cat > config.json <<EOF
{
"mysql": {
"host": "localhost:3306",
"user": "semaphore",
"pass": "p455w0rd",
"name": "semaphore"
},
"dialect": "mysql",
"email_alert": false
}
EOF
- name: Migrate database
run: |
chmod +x ./semaphore && ./semaphore migrate --config config.json
migrate-postgres:
runs-on: ubuntu-latest
needs:
- build-local
services:
postgres:
image: postgres:12.18
env:
POSTGRES_USER: semaphore
POSTGRES_PASSWORD: p455w0rd
POSTGRES_DB: semaphore
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: semaphore
- name: Write config
run: |
cat > config.json <<EOF
{
"postgres": {
"host": "localhost:5432",
"user": "semaphore",
"pass": "p455w0rd",
"name": "semaphore",
"options": {
"sslmode": "disable"
}
},
"dialect": "postgres",
"email_alert": false
}
EOF
- name: Migrate database
run: |
chmod +x ./semaphore && ./semaphore migrate --config config.json
integrate-boltdb:
runs-on: ubuntu-latest
needs:
- migrate-boltdb
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Setup golang
uses: actions/setup-go@v5
with:
go-version: '^1.21.0'
- name: Setup nodejs
uses: actions/setup-node@v4
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- name: Install go-task
run: |
go install github.com/go-task/task/v3/cmd/task@latest
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: semaphore
- name: Write config
run: |
cat > config.stdin <<EOF
2
/tmp/database.bolt
/tmp/semaphore
http://localhost:3000
no
no
no
no
no
no
$(pwd)/.dredd
admin
admin@localhost
Developer
password
EOF
- name: Execute setup
run: |
chmod +x ./semaphore && ./semaphore setup - < config.stdin
- name: Launch dredd
run: |
task e2e:goodman
task e2e:deps
task e2e:hooks
task e2e:test
integrate-mysql:
runs-on: ubuntu-latest
needs:
- migrate-mysql
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: p455w0rd
MYSQL_USER: semaphore
MYSQL_PASSWORD: p455w0rd
MYSQL_DATABASE: semaphore
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 3306:3306
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Setup golang
uses: actions/setup-go@v5
with:
go-version: '^1.21.0'
- name: Setup nodejs
uses: actions/setup-node@v4
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- name: Install go-task
run: |
go install github.com/go-task/task/v3/cmd/task@latest
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: semaphore
- name: Write config
run: |
cat > config.stdin <<EOF
1
localhost:3306
semaphore
p455w0rd
semaphore
/tmp/semaphore
http://localhost:3000
no
no
no
no
no
no
$(pwd)/.dredd
admin
admin@localhost
Developer
password
EOF
- name: Execute setup
run: |
chmod +x ./semaphore && ./semaphore setup - < config.stdin
- name: Launch dredd
run: |
task e2e:goodman
task e2e:deps
task e2e:hooks
task e2e:test
integrate-mariadb:
runs-on: ubuntu-latest
needs:
- migrate-mariadb
services:
mariadb:
image: mariadb:10.4
env:
MARIADB_ROOT_PASSWORD: p455w0rd
MARIADB_USER: semaphore
MARIADB_PASSWORD: p455w0rd
MARIADB_DATABASE: semaphore
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 3306:3306
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Setup golang
uses: actions/setup-go@v5
with:
go-version: '^1.21.0'
- name: Setup nodejs
uses: actions/setup-node@v4
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- name: Install go-task
run: |
go install github.com/go-task/task/v3/cmd/task@latest
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: semaphore
- name: Write config
run: |
cat > config.stdin <<EOF
1
localhost:3306
semaphore
p455w0rd
semaphore
/tmp/semaphore
http://localhost:3000
no
no
no
no
no
no
$(pwd)/.dredd
admin
admin@localhost
Developer
password
EOF
- name: Execute setup
run: |
chmod +x ./semaphore && ./semaphore setup - < config.stdin
- name: Launch dredd
run: |
task e2e:goodman
task e2e:deps
task e2e:hooks
task e2e:test
integrate-postgres:
runs-on: ubuntu-latest
needs:
- migrate-postgres
services:
postgres:
image: postgres:12.18
env:
POSTGRES_USER: semaphore
POSTGRES_PASSWORD: p455w0rd
POSTGRES_DB: semaphore
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Setup golang
uses: actions/setup-go@v5
with:
go-version: '^1.21.0'
- name: Setup nodejs
uses: actions/setup-node@v4
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- name: Install go-task
run: |
go install github.com/go-task/task/v3/cmd/task@latest
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: semaphore
- name: Write config
run: |
cat > config.stdin <<EOF
3
localhost:5432
semaphore
p455w0rd
semaphore
/tmp/semaphore
http://localhost:3000
no
no
no
no
no
no
$(pwd)/.dredd
admin
admin@localhost
Developer
password
EOF
- name: Execute setup
run: |
chmod +x ./semaphore && ./semaphore setup - < config.stdin
- name: Launch dredd
run: |
task e2e:goodman
task e2e:deps
task e2e:hooks
task e2e:test
deploy-dev:
runs-on: [ubuntu-latest]
needs: [test-integration]
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
if: github.repository_owner == 'semaphoreui'
needs:
- integrate-boltdb
- integrate-mysql
- integrate-mariadb
- integrate-postgres
steps:
- uses: actions/setup-go@v3
with: { go-version: '1.21' }
- name: Checkout source
uses: actions/checkout@v4
- run: go install github.com/go-task/task/v3/cmd/task@latest
- name: Setup qemu
id: qemu
uses: docker/setup-qemu-action@v3
- uses: actions/checkout@v3
- name: Setup buildx
id: buildx
uses: docker/setup-buildx-action@v3
- uses: docker/setup-qemu-action@v2
- uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
- name: Hub login
uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
- name: Build and push
uses: docker/build-push-action@v3
- name: Server meta
id: server
uses: docker/metadata-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
file: ./deployment/docker/prod/buildx.Dockerfile
push: true
tags: semaphoreui/semaphore:develop
github-token: ${{ secrets.GITHUB_TOKEN }}
images: |
semaphoreui/semaphore
labels: |
org.opencontainers.image.vendor=SemaphoreUI
maintainer=Semaphore UI <support@semui.co>
tags: |
type=raw,value=develop
flavor: |
latest=false
- name: Build and push runner
uses: docker/build-push-action@v3
- name: Server build
uses: docker/build-push-action@v5
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
platforms: linux/amd64,linux/arm64
file: ./deployment/docker/prod/runner.buildx.Dockerfile
push: true
tags: semaphoreui/runner:develop
file: deployment/docker/server/Dockerfile
platforms: linux/amd64,linux/arm64 #,linux/arm/v6
push: ${{ github.event_name != 'pull_request' }}
labels: ${{ steps.server.outputs.labels }}
tags: ${{ steps.server.outputs.tags }}
- name: Runner meta
id: runner
uses: docker/metadata-action@v5
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
images: |
semaphoreui/runner
labels: |
org.opencontainers.image.vendor=SemaphoreUI
maintainer=Semaphore UI <support@semui.co>
tags: |
type=raw,value=develop
flavor: |
latest=false
- name: Runner build
uses: docker/build-push-action@v5
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: deployment/docker/runner/Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v6
push: ${{ github.event_name != 'pull_request' }}
labels: ${{ steps.runner.outputs.labels }}
tags: ${{ steps.runner.outputs.tags }}

View File

@ -1,75 +1,131 @@
name: Release
on:
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
jobs:
release:
runs-on: [ubuntu-latest]
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v3
with: { go-version: '1.21' }
- name: Checkout source
uses: actions/checkout@v4
- uses: actions/setup-node@v3
with: { node-version: '16' }
- name: Setup golang
uses: actions/setup-go@v5
with:
go-version: '^1.21.0'
- run: go install github.com/go-task/task/v3/cmd/task@latest
- name: Setup nodejs
uses: actions/setup-node@v4
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- run: sudo apt update && sudo apt-get install rpm
- name: Install go-task
run: |
go install github.com/go-task/task/v3/cmd/task@latest
- uses: actions/checkout@v3
- name: Install rpm
run: |
sudo apt update && sudo apt-get install rpm
- run: task deps
- name: Install deps
run: |
task deps
- run: |
echo ${{ secrets.GPG_KEY }} | tr " " "\n" | base64 -d | gpg --import --batch
- name: Import gnupg
run: |
echo "${{ secrets.GPG_KEY }}" | tr " " "\n" | base64 -d | gpg --import --batch
gpg --sign -u "58A7 CC3D 8A9C A2E5 BB5C 141D 4064 23EA F814 63CA" --pinentry-mode loopback --yes --batch --passphrase "${{ secrets.GPG_PASS }}" --output unlock.sig --detach-sign README.md
rm -f unlock.sig
- run: git reset --hard
- run: GITHUB_TOKEN=${{ secrets.GH_TOKEN }} task release:prod
- name: Reset repo
run: |
git reset --hard
- name: Run release
run: |
GITHUB_TOKEN=${{ secrets.GH_TOKEN }} task release:prod
deploy-prod:
runs-on: [ubuntu-latest]
runs-on: ubuntu-latest
if: github.repository_owner == 'semaphoreui'
steps:
- uses: actions/setup-go@v3
with: { go-version: '1.21' }
- name: Checkout source
uses: actions/checkout@v4
- run: go install github.com/go-task/task/v3/cmd/task@latest
- name: Setup qemu
id: qemu
uses: docker/setup-qemu-action@v3
- uses: actions/checkout@v3
- name: Setup buildx
id: buildx
uses: docker/setup-buildx-action@v3
- run: context=prod task docker:test
- uses: docker/setup-qemu-action@v2
- uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
- name: Hub login
uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
- name: Build and push
uses: docker/build-push-action@v3
- name: Server meta
id: server
uses: docker/metadata-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
file: ./deployment/docker/prod/buildx.Dockerfile
push: true
tags: semaphoreui/semaphore:latest,semaphoreui/semaphore:${{ github.ref_name }}
github-token: ${{ secrets.GITHUB_TOKEN }}
images: |
semaphoreui/semaphore
labels: |
org.opencontainers.image.vendor=SemaphoreUI
maintainer=Semaphore UI <support@semui.co>
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
flavor: |
latest=true
- name: Build and push runner
uses: docker/build-push-action@v3
- name: Server build
uses: docker/build-push-action@v5
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
platforms: linux/amd64,linux/arm64
file: ./deployment/docker/prod/runner.buildx.Dockerfile
push: true
tags: semaphoreui/runner:latest,semaphoreui/runner:${{ github.ref_name }}
file: deployment/docker/server/Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v6
push: ${{ github.event_name != 'pull_request' }}
labels: ${{ steps.server.outputs.labels }}
tags: ${{ steps.server.outputs.tags }}
- name: Runner meta
id: runner
uses: docker/metadata-action@v5
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
images: |
semaphoreui/runner
labels: |
org.opencontainers.image.vendor=SemaphoreUI
maintainer=Semaphore UI <support@semui.co>
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
flavor: |
latest=true
- name: Runner build
uses: docker/build-push-action@v5
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: deployment/docker/runner/Dockerfile
platforms: linux/amd64,linux/arm64 #,linux/arm/v6
push: ${{ github.event_name != 'pull_request' }}
labels: ${{ steps.runner.outputs.labels }}
tags: ${{ steps.runner.outputs.tags }}

1
.gitignore vendored
View File

@ -18,7 +18,6 @@ node_modules/
/semaphore.iml
/bin/
util/version.go
/vendor/
/coverage.out
/public/package-lock.json

View File

@ -1,77 +1,72 @@
# Goreleaser configuration
# for building binaries and packages for distributions and releasing on github
dist: bin
before:
hooks:
- task compile
- task build:fe
builds:
- binary: semaphore
env:
- CGO_ENABLED=0
main: ./cli/main.go
ldflags: -s -w -X github.com/ansible-semaphore/semaphore/util.Ver={{ .Version }} -X github.com/ansible-semaphore/semaphore/util.Commit={{ .ShortCommit }} -X github.com/ansible-semaphore/semaphore/util.Date={{ .Timestamp }}
tags:
- netgo
goos:
- windows
- darwin
- linux
- freebsd
#- openbsd
#- netbsd
goarch:
- 386
- amd64
#- 386
- arm
- arm64
- ppc64le
ignore:
- goos: darwin
goarch: 386
# hooks:
# pre: task compile
- goos: darwin
goarch: arm
- goos: darwin
goarch: ppc64le
- goos: windows
goarch: ppc64le
archives:
-
- files:
- LICENSE
format_overrides:
- goos: windows
format: zip
files:
- LICENSE
signs:
-
artifacts: checksum
- artifacts: checksum
args: ["-u", "58A7 CC3D 8A9C A2E5 BB5C 141D 4064 23EA F814 63CA", "--pinentry-mode", "loopback", "--yes", "--batch", "--output", "${signature}", "--detach-sign", "${artifact}"]
# Start the snapshot name with a numerical value
# so it does not need to be force installed
snapshot:
name_template: "{{ .Timestamp }}-{{ .ShortCommit }}-SNAPSHOT"
nfpms:
-
file_name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
vendor: Castaway Consulting LLC
homepage: https://github.com/ansible-semaphore/semaphore
maintainer: Castaway Consulting LLC <support@castawaylabs.com>
- file_name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
description: Open Source alternative to Ansible Tower
homepage: https://github.com/semaphoreui/semaphore
vendor: Semaphore UI
maintainer: Semaphore UI <support@semui.co>
license: MIT
formats:
- deb
- rpm
# Packages your package depends on.
dependencies:
- git
suggests:
- ansible
# install binary in /usr/bin
bindir: /usr/bin
release:
# Do not auto publish release
draft: true
name_template: "{{.Tag}}"

View File

@ -1,77 +1,59 @@
# Semaphore Tasks
# These tasks should be used to build and develop Semaphore
#
# Tasks without a `desc:` field are intended mainly to be called
# internally by other tasks and therefore are not listed when running `task` or `task -l`
version: '3'
version: "3"
vars:
docker_namespace: semaphoreui
docker_image: semaphore
DOCKER_ORG: semaphoreui
DOCKER_SERVER: semaphore
DOCKER_RUNNER: runner
DOCKER_CMD: docker
tasks:
all:
desc: Install, Compile, Test and Build Semaphore for local architecture
desc: Install, test and build Semaphore for local architecture
cmds:
- task: deps
- task: compile
- task: test
- task: build:local
- task: build
vars:
GOOS: ''
GOARCH: ''
GOOS: ""
GOARCH: ""
deps:
desc: Install all dependencies (except dredd requirements)
desc: Install all build dependencies
cmds:
- task: deps:tools
- task: deps:be
- task: deps:fe2
- task: deps:fe
deps:tools:
desc: Installs required tools to build and publish
vars:
SWAGGER_VERSION: v0.30.5
GORELEASER_VERSION: v1.25.1
GOLINTER_VERSION: v1.57.2
cmds:
- go install github.com/go-swagger/go-swagger/cmd/swagger@{{ .SWAGGER_VERSION }}
- go install github.com/goreleaser/goreleaser@{{ .GORELEASER_VERSION }}
- go install github.com/golangci/golangci-lint/cmd/golangci-lint@{{ .GOLINTER_VERSION }}
deps:be:
desc: Vendor application dependencies
cmds:
- go mod vendor
deps:fe2:
desc: Installs npm requirements for front end from package.json
deps:fe:
desc: Installs nodejs requirements
dir: web
cmds:
- npm install
# - npm audit fix
deps:integration:
desc: Installs requirements for integration testing with dredd
dir: web
build:
desc: Build a full set of release binaries and packages
cmds:
- npm install dredd@13.1.2
# - npm audit fix
- task: build:fe
- task: build:be
deps:tools:
desc: Installs tools needed
dir: web
vars:
GORELEASER_VERSION: "0.183.0"
GOLINTER_VERSION: "1.46.2"
cmds:
- go install github.com/snikch/goodman/cmd/goodman@latest
- go install github.com/go-swagger/go-swagger/cmd/swagger@v0.29.0
- '{{ if ne OS "windows" }} sh -c "curl -L https://github.com/goreleaser/goreleaser/releases/download/v{{ .GORELEASER_VERSION }}/goreleaser_$(uname -s)_$(uname -m).tar.gz | tar -xz -C $(go env GOPATH)/bin goreleaser"{{ else }} {{ end }}'
- '{{ if ne OS "windows" }} chmod +x $(go env GOPATH)/bin/goreleaser{{ else }} {{ end }}'
- '{{ if eq OS "windows" }} echo "NOTICE: You must download goreleaser manually to build this application https://github.com/goreleaser/goreleaser/releases "{{ else }}:{{ end }}'
# - '{{ if ne OS "windows" }} sh -c "curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v{{ .GOLINTER_VERSION }}"{{ else }}{{ end }}'
- '{{ if eq OS "windows" }} echo "NOTICE: You need to install golangci-lint manually to build this application https://github.com/golangci/golangci-lint#install"{{ else }}{{ end }}'
compile:
desc: Generates compiled frontend and backend resources (must be in this order)
cmds:
- task: compile:fe2
- task: compile:be
compile:fe2:
desc: Build vue.js project
build:fe:
desc: Build VueJS project
dir: web
sources:
- src/*.*
@ -90,199 +72,230 @@ tasks:
cmds:
- npm run build
compile:be:
desc: Generate the version
build:be:
desc: Build server binary
cmds:
- go run util/version_gen/generator.go {{ if .TAG }}{{ .TAG }}{{ else }}{{ if .SEMAPHORE_VERSION }}{{ .SEMAPHORE_VERSION }}{{ else }}{{ .BRANCH }}-{{ .SHA }}-{{ .TIMESTAMP }}{{ if .DIRTY }}-dirty{{ end }}{{ end }}{{end}}
- >-
env CGO_ENABLED=0 GOOS={{ .GOOS }} GOARCH={{ .GOARCH }}
go build -o bin/semaphore{{ if eq OS "windows" }}.exe{{ end }}
-tags "netgo"
-ldflags "-s -w -X {{ .IMPORT }}/util.Ver={{ .VERSION }} -X {{ .IMPORT }}/util.Commit={{ .SHA }} -X {{ .IMPORT }}/util.Date={{ .DATE }}" ./cli
vars:
TAG:
sh: git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null | sed -n 's/^\([^^~]\{1,\}\)\(\^0\)\{0,1\}$/\1/p'
BRANCH:
sh: git rev-parse --abbrev-ref HEAD
DIRTY:
# We must exclude the package-lock file as npm install can change it!
sh: git diff --exit-code --stat -- . ':(exclude)web/package-lock.json' ':(exclude)web/package.json' || true
sh: git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || git rev-parse --abbrev-ref HEAD
SHA:
sh: git log --pretty=format:'%h' -n 1
TIMESTAMP:
sh: date +%s
compile:api:hooks:
dir: ./.dredd/hooks
cmds:
- go build -o ../compiled_hooks{{ if eq OS "windows" }}.exe{{ end }}
build:
desc: Build a full set of release binaries and packages
cmds:
- task: release
build:local:
desc: Build a binary for the current architecture
dir: cli
cmds:
- env CGO_ENABLED=0 GOOS={{ .GOOS }} GOARCH={{ .GOARCH }} go build -o ../bin/semaphore{{ if eq OS "windows" }}.exe{{ end }}
release:
desc: creates a release without performing validations or publishing artifacts
cmds:
- goreleaser --snapshot --rm-dist
release:prod:
cmds:
- goreleaser
VERSION: "{{ if eq .GITHUB_REF_TYPE \"tag\" }}{{ .GITHUB_REF_NAME }}{{ else }}{{ .TAG }}{{ end }}"
DATE: "{{ now | unixEpoch }}"
IMPORT: "github.com/ansible-semaphore/semaphore"
lint:
cmds:
- task: lint:fe
- task: lint:be
lint:fe:
dir: web
cmds:
- npm run lint
lint:be:
# --errors
cmds:
- golangci-lint run --disable goconst --timeout 240s ./...
- go vet ./...
- swagger validate ./api-docs.yml
test:
cmds:
# - task: test:fe
- task: test:be
test:fe:
dir: web
cmds:
- npm run test:unit
test:be:
desc: Run go code tests
cmds:
- go vet ./...
- swagger validate ./api-docs.yml
- go test -v -coverprofile=coverage.out ./...
test:api:
desc: test the api with dredd
e2e:goodman:
desc: Installs goodman which is required by dredd
cmds:
- ./web/node_modules/.bin/dredd --config .dredd/dredd.yml
- go install github.com/snikch/goodman/cmd/goodman@latest
ci:artifacts:
e2e:deps:
desc: Installs dredd dep for integration testing
dir: web
cmds:
- rsync -a bin/ $CIRCLE_ARTIFACTS/
- npm install dredd@13.1.2
# docker(-compose) commands
dc:dev:
desc: build and start a development stack using docker-compose
e2e:hooks:
desc: Compile required dredd hooks built
dir: ./.dredd/hooks
cmds:
- task: docker
vars:
context: dev
args: build semaphore_dev
compose: true
- task: dc:up
vars:
context: dev
- go build -o ../compiled_hooks{{ if eq OS "windows" }}.exe{{ end }}
# convenience function to build and start a production like stack
dc:prod:
desc: build and start a production like stack using docker-compose
e2e:test:
desc: Run end to end test for API with dredd
cmds:
- task: docker
vars:
context: prod
args: build semaphore
compose: true
- task: dc:up
vars:
context: prod
- ./web/node_modules/.bin/dredd --config .dredd/dredd.testing.yml
dc:up:
desc: start a docker-compose instance, requires context
release:prod:
desc: Create and publish a release
cmds:
- task: docker
vars:
compose: true
args: up --abort-on-container-exit
prefix: "{{ .prefix }}"
context: "{{ .context }}"
- goreleaser
dc:build:
desc: build a set of docker-compose containers, requires context
release:test:
desc: Create a local test release
cmds:
- task: docker
vars:
compose: true
args: build
context: "{{ .context }}"
dc:down:
desc: down a docker-compose instance, requires context
cmds:
- task: docker
vars:
compose: true
args: down
context: "{{ .context }}"
dc:stop:
desc: stop a docker-compose instance, requires context
cmds:
- task: docker
vars:
compose: true
args: stop
context: "{{ .context }}"
docker:build:
desc: Build an image for Semaphore, requires context
vars:
tag: "{{ if .tag }}{{ .tag }}{{ else }}latest{{ end }}"
cmds:
- task: docker
vars:
context: "{{ .context }}"
action: build
tag: "{{ .tag }}"
args: -t "{{ .docker_namespace }}/{{ .docker_image }}:{{ .tag }}" .
deps:docker:
desc: Install docker testing dependencies. These must be installed explicitly and are not included in the general deps task.
status:
- test -f /usr/local/bin/goss
- test -f /usr/local/bin/dgoss
- test -f /usr/local/bin/hadolint
cmds:
- sudo curl -L https://github.com/aelsabbahy/goss/releases/download/v0.3.5/goss-linux-amd64 -o /usr/local/bin/goss
- sudo chmod +rx /usr/local/bin/goss
- sudo curl -L https://raw.githubusercontent.com/aelsabbahy/goss/v0.3.5/extras/dgoss/dgoss -o /usr/local/bin/dgoss
- sudo chmod +rx /usr/local/bin/dgoss
- sudo curl -L https://github.com/hadolint/hadolint/releases/download/v2.10.0/hadolint-Linux-x86_64 -o /usr/local/bin/hadolint
- sudo chmod +rx /usr/local/bin/hadolint
- goreleaser --auto-snapshot --clean --skip=sign
docker:test:
desc: Test docker containers by building them, running tests and deleting
deps: ['deps:docker']
desc: Test containers by building, running, testing and deleting them
deps:
- task: docker:deps
cmds:
- task: docker:lint
vars:
context: "{{ .context }}"
- task: docker:build
vars:
tag: "{{ .context }}-test"
- task: docker:goss
- docker rmi "{{ .docker_namespace }}/{{ .docker_image }}:{{ .context }}-test"
tag: test
docker:goss:
dir: "deployment/docker/{{ .context}}"
deps: ['deps:docker']
cmds:
- GOSS_FILES_STRATEGY='cp' dgoss run -it "{{ .docker_namespace }}/{{ .docker_image }}:{{ .context }}-test"
- task: docker:goss
- task: docker:lint
- "{{ .DOCKER_CMD }} rmi {{ .DOCKER_ORG }}/{{ .DOCKER_SERVER }}:test"
- "{{ .DOCKER_CMD }} rmi {{ .DOCKER_ORG }}/{{ .DOCKER_RUNNER }}:test"
docker:lint:
desc: hadolint a dockerfile. Ignores version pinning warning
dir: "deployment/docker/{{ .context}}"
desc: Lint all dockerfiles based on Hadolint
deps:
- task: docker:deps
cmds:
- task: docker:lint:server
- task: docker:lint:runner
docker:lint:server:
desc: Lint server dockerfile based on Hadolint
dir: deployment/docker/server
cmds:
- hadolint Dockerfile --ignore DL3018
docker:push:
desc: push a docker image to a repo. Defaults to the official docker hub
docker:lint:runner:
desc: Lint runner dockerfile based on Hadolint
dir: deployment/docker/runner
cmds:
- docker push {{ .docker_namespace }}/{{ .docker_image }}:{{ .tag }}
- hadolint Dockerfile --ignore DL3018
# templated command to reduce code duplication
docker:
vars:
docker_root: deployment/docker/
docker:goss:
desc: Check if container contains defined files
deps:
- task: docker:deps
cmds:
- docker{{ if .compose }}-compose{{ end }} {{ if .action }}{{ .action }}{{ end }} -f {{ .docker_root }}{{ .context }}/{{ if .compose }}docker-compose{{ if .prefix }}{{ .prefix }}{{ end }}.yml{{ else }}Dockerfile{{ if .prefix }}{{ .prefix }}{{ end }}{{ end }} {{if .args }}{{ .args }}{{ end }}
- task: docker:goss:server
- task: docker:goss:runner
docker:goss:server:
desc: Check if server contains defined files
dir: deployment/docker/server
env:
GOSS_FILES_STRATEGY: cp
cmds:
- dgoss run -it "{{ .DOCKER_ORG }}/{{ .DOCKER_SERVER }}:test"
docker:goss:runner:
desc: Check if runner contains defined files
dir: deployment/docker/runner
env:
GOSS_FILES_STRATEGY: cp
cmds:
- dgoss run -it "{{ .DOCKER_ORG }}/{{ .DOCKER_RUNNER }}:test"
docker:build:
desc: Build all defined images for Semaphore
vars:
tag: "{{ if .tag }}{{ .tag }}{{ else }}latest{{ end }}"
cmds:
- task: docker:build:server
vars:
tag: "{{ .tag }}"
- task: docker:build:runner
vars:
tag: "{{ .tag }}"
docker:build:server:
desc: Build an image for Semaphore server
vars:
tag: "{{ if .tag }}{{ .tag }}{{ else }}latest{{ end }}"
cmds:
- "{{ .DOCKER_CMD }} build -f deployment/docker/server/Dockerfile -t {{ .DOCKER_ORG }}/{{ .DOCKER_SERVER }}:{{ .tag }} ."
docker:build:runner:
desc: Build an image for Semaphore runner
vars:
tag: "{{ if .tag }}{{ .tag }}{{ else }}latest{{ end }}"
cmds:
- "{{ .DOCKER_CMD }} build -f deployment/docker/runner/Dockerfile -t {{ .DOCKER_ORG }}/{{ .DOCKER_RUNNER }}:{{ .tag }} ."
docker:push:
desc: Push the images to registry
cmds:
- docker push {{ .DOCKER_ORG }}/{{ .DOCKER_SERVER }}:{{ .tag }}
- docker push {{ .DOCKER_ORG }}/{{ .DOCKER_RUNNER }}:{{ .tag }}
docker:deps:
desc: Install docker testing dependencies
vars:
INSTALL_PATH: '{{ .INSTALL_PATH | default "/usr/local/bin" }}'
REQUIRE_SUDO: '{{ .REQUIRE_SUDO | default "true" }}'
cmds:
- task: docker:deps:hadolint
vars:
INSTALL_PATH: "{{ .INSTALL_PATH }}"
REQUIRE_SUDO: "{{ .REQUIRE_SUDO }}"
- task: docker:deps:goss
vars:
INSTALL_PATH: "{{ .INSTALL_PATH }}"
REQUIRE_SUDO: "{{ .REQUIRE_SUDO }}"
- task: docker:deps:dgoss
vars:
INSTALL_PATH: "{{ .INSTALL_PATH }}"
REQUIRE_SUDO: "{{ .REQUIRE_SUDO }}"
docker:deps:hadolint:
platforms:
- linux/amd64
- linux/arm64
- darwin/amd64
- darwin/arm64
vars:
HADOLINT_VERSION: v2.10.0
status:
- test -f "{{ .INSTALL_PATH }}/hadolint"
cmds:
- '{{ if eq .REQUIRE_SUDO "true" }}sudo {{ end }}curl -sSL https://github.com/hadolint/hadolint/releases/download/{{ .HADOLINT_VERSION }}/hadolint-{{ if eq OS "linux" }}Linux{{ end }}{{ if eq OS "darwin" }}Darwin{{ end }}-{{ if eq ARCH "amd64" }}x86_64{{ else }}{{ ARCH }}{{ end }} -o {{ .INSTALL_PATH }}/hadolint'
- '{{ if eq .REQUIRE_SUDO "true" }}sudo {{ end }}chmod +x {{ .INSTALL_PATH }}/hadolint'
docker:deps:goss:
platforms:
- linux
- darwin
vars:
GOSS_VERSION: v0.3.5
status:
- test -f "{{ .INSTALL_PATH }}/goss"
cmds:
- '{{ if eq .REQUIRE_SUDO "true" }}sudo {{ end }}curl -sSL https://github.com/aelsabbahy/goss/releases/download/{{ .GOSS_VERSION }}/goss-{{ OS }}-{{ ARCH }} -o {{ .INSTALL_PATH }}/goss'
- '{{ if eq .REQUIRE_SUDO "true" }}sudo {{ end }}chmod +x {{ .INSTALL_PATH }}/goss'
docker:deps:dgoss:
platforms:
- linux
- darwin
vars:
GOSS_VERSION: v0.3.5
status:
- test -f "{{ .INSTALL_PATH }}/dgoss"
cmds:
- '{{ if eq .REQUIRE_SUDO "true" }}sudo {{ end }}curl -sSL https://raw.githubusercontent.com/aelsabbahy/goss/{{ .GOSS_VERSION }}/extras/dgoss/dgoss -o {{ .INSTALL_PATH }}/dgoss'
- '{{ if eq .REQUIRE_SUDO "true" }}sudo {{ end }}chmod +x {{ .INSTALL_PATH }}/dgoss'

View File

@ -1,9 +0,0 @@
version: '2'
tasks:
compile:be:
cmds:
- go run util/version_gen/generator.go 1
build:local:
dir: cli
cmds:
- go build -o ../bin/semaphore{{ if eq OS "windows" }}.exe{{ end }}

View File

@ -644,6 +644,8 @@ definitions:
type: string
environment:
type: string
secret:
type: string
limit:
type: string
TaskOutput:

View File

@ -253,7 +253,6 @@ func RunIntegration(integration db.Integration, project db.Project, r *http.Requ
var taskDefinition = db.Task{
TemplateID: integration.TemplateID,
ProjectID: integration.ProjectID,
Debug: true,
Environment: environmentJSONString,
IntegrationID: &integration.ID,
}

View File

@ -431,7 +431,7 @@ func serveFile(w http.ResponseWriter, r *http.Request, name string) {
func getSystemInfo(w http.ResponseWriter, r *http.Request) {
body := map[string]interface{}{
"version": util.Version,
"version": util.Version(),
"ansible": util.AnsibleVersion(),
}

View File

@ -2,7 +2,10 @@ package cmd
import (
"fmt"
log "github.com/sirupsen/logrus"
"net/http"
"os"
"strings"
"github.com/ansible-semaphore/semaphore/api"
"github.com/ansible-semaphore/semaphore/api/sockets"
"github.com/ansible-semaphore/semaphore/db"
@ -12,10 +15,8 @@ import (
"github.com/ansible-semaphore/semaphore/util"
"github.com/gorilla/context"
"github.com/gorilla/handlers"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"net/http"
"os"
"strings"
)
var configPath string
@ -56,7 +57,7 @@ func runService() {
}
fmt.Printf("Tmp Path (projects home) %v\n", util.Config.TmpPath)
fmt.Printf("Semaphore %v\n", util.Version)
fmt.Printf("Semaphore %v\n", util.Version())
fmt.Printf("Interface %v\n", util.Config.Interface)
fmt.Printf("Port %v\n", util.Config.Port)

View File

@ -1,102 +0,0 @@
package cmd
import (
"errors"
"fmt"
"github.com/ansible-semaphore/semaphore/util"
"github.com/google/go-github/github"
"github.com/spf13/cobra"
"io"
"net/http"
"os"
"path/filepath"
"runtime"
"strings"
"time"
)
// Adapted from github.com/apex/apex
// doUpgrade checks for an update, and if available downloads the binary and installs it
func doUpgrade() error {
updateAvailable, err := util.CheckUpdate()
if err != nil || updateAvailable == nil {
return err
}
asset := findAsset(updateAvailable)
if asset == nil {
return errors.New("cannot find binary for your system")
}
// create tmp file
tmpPath := filepath.Join(os.TempDir(), "semaphore-upgrade")
f, err := os.OpenFile(tmpPath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0755) //nolint: gas
if err != nil {
return err
}
// download binary
fmt.Printf("downloading %s\n", *asset.BrowserDownloadURL)
res, err := http.Get(*asset.BrowserDownloadURL)
if err != nil {
return err
}
defer res.Body.Close() //nolint: errcheck
// copy it down
_, err = io.Copy(f, res.Body)
if err != nil {
return err
}
// replace it
cmdPath := util.FindSemaphore()
if len(cmdPath) == 0 {
return errors.New("cannot find semaphore binary")
}
fmt.Printf("replacing %s\n", cmdPath)
err = os.Rename(tmpPath, cmdPath)
if err != nil {
return err
}
fmt.Println("visit https://github.com/ansible-semaphore/semaphore/releases for the changelog")
go func() {
time.Sleep(time.Second * 3)
os.Exit(0)
}()
return nil
}
// findAsset returns the binary for this platform.
func findAsset(release *github.RepositoryRelease) *github.ReleaseAsset {
for _, asset := range release.Assets {
suffix := fmt.Sprintf("_%s_%s.tar.gz", runtime.GOOS, runtime.GOARCH)
if strings.HasPrefix(*asset.Name, "semaphore_") &&
strings.HasSuffix(*asset.Name, suffix) {
return &asset
}
}
return nil
}
func init() {
rootCmd.AddCommand(upgradeCmd)
}
var upgradeCmd = &cobra.Command{
Use: "upgrade",
Short: "Upgrade to latest stable version",
Run: func(cmd *cobra.Command, args []string) {
err := doUpgrade()
if err != nil {
panic(err)
}
},
}

View File

@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"github.com/ansible-semaphore/semaphore/util"
"github.com/spf13/cobra"
)
@ -14,6 +15,6 @@ var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version of Semaphore",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(util.Version)
fmt.Println(util.Version())
},
}
}

View File

@ -8,7 +8,7 @@ func TestObjectToJSON(t *testing.T) {
Title: "Test",
}
s := ObjectToJSON(v)
if s == nil || *s != "{\"name\":\"test\",\"title\":\"Test\",\"required\":false,\"type\":\"\",\"description\":\"\"}" {
if s == nil || *s != "{\"name\":\"test\",\"title\":\"Test\",\"required\":false,\"type\":\"\",\"description\":\"\",\"values\":null}" {
t.Fail()
}
}
@ -27,7 +27,7 @@ func TestObjectToJSON3(t *testing.T) {
Title: "Test",
}
s := ObjectToJSON(v)
if s == nil || *s != "{\"name\":\"test\",\"title\":\"Test\",\"required\":false,\"type\":\"\",\"description\":\"\"}" {
if s == nil || *s != "{\"name\":\"test\",\"title\":\"Test\",\"required\":false,\"type\":\"\",\"description\":\"\",\"values\":null}" {
t.Fail()
}
}

View File

@ -26,6 +26,7 @@ type Task struct {
Playbook string `db:"playbook" json:"playbook"`
Environment string `db:"environment" json:"environment"`
Limit string `db:"hosts_limit" json:"limit"`
Secret string `db:"-" json:"secret"`
UserID *int `db:"user_id" json:"user_id"`

View File

@ -21,16 +21,23 @@ const (
type SurveyVarType string
const (
SurveyVarStr TemplateType = ""
SurveyVarInt TemplateType = "int"
SurveyVarStr TemplateType = ""
SurveyVarInt TemplateType = "int"
SurveyVarEnum TemplateType = "enum"
)
type SurveyVarEnumValue struct {
Name string `json:"name"`
Value string `json:"value"`
}
type SurveyVar struct {
Name string `json:"name"`
Title string `json:"title"`
Required bool `json:"required"`
Type SurveyVarType `json:"type"`
Description string `json:"description"`
Name string `json:"name"`
Title string `json:"title"`
Required bool `json:"required"`
Type SurveyVarType `json:"type"`
Description string `json:"description"`
Values []SurveyVarEnumValue `json:"values"`
}
type TemplateFilter struct {

View File

@ -91,7 +91,7 @@ func (t *AnsibleApp) getRepoPath() string {
}
func (t *AnsibleApp) installRolesRequirements() error {
requirementsFilePath := fmt.Sprintf("%s/roles/requirements.yml", t.getRepoPath())
requirementsFilePath := path.Join(t.getRepoPath(), "roles", "requirements.yml")
requirementsHashFilePath := fmt.Sprintf("%s.md5", requirementsFilePath)
if _, err := os.Stat(requirementsFilePath); err != nil {
@ -126,7 +126,7 @@ func (t *AnsibleApp) GetPlaybookDir() string {
}
func (t *AnsibleApp) installCollectionsRequirements() error {
requirementsFilePath := path.Join(t.GetPlaybookDir(), "collections", "requirements.yml")
requirementsFilePath := path.Join(t.getRepoPath(), "collections", "requirements.yml")
requirementsHashFilePath := fmt.Sprintf("%s.md5", requirementsFilePath)
if _, err := os.Stat(requirementsFilePath); err != nil {

View File

@ -31,18 +31,8 @@ func (p AnsiblePlaybook) makeCmd(command string, args []string, environmentVars
cmd.Env = append(cmd.Env, *environmentVars...)
}
sensitiveEnvs := []string{
"SEMAPHORE_ACCESS_KEY_ENCRYPTION",
"SEMAPHORE_ADMIN_PASSWORD",
"SEMAPHORE_DB_USER",
"SEMAPHORE_DB_NAME",
"SEMAPHORE_DB_HOST",
"SEMAPHORE_DB_PASS",
"SEMAPHORE_LDAP_PASSWORD",
}
// Remove sensitive env variables from cmd process
for _, env := range sensitiveEnvs {
for _, env := range getSensitiveEnvs() {
cmd.Env = append(cmd.Env, env+"=")
}

View File

@ -6,6 +6,18 @@ import (
"github.com/ansible-semaphore/semaphore/pkg/task_logger"
)
func getSensitiveEnvs() []string {
return []string{
"SEMAPHORE_ACCESS_KEY_ENCRYPTION",
"SEMAPHORE_ADMIN_PASSWORD",
"SEMAPHORE_DB_USER",
"SEMAPHORE_DB_NAME",
"SEMAPHORE_DB_HOST",
"SEMAPHORE_DB_PASS",
"SEMAPHORE_LDAP_PASSWORD",
}
}
type LocalApp interface {
SetLogger(logger task_logger.Logger) task_logger.Logger
InstallRequirements() error

View File

@ -0,0 +1,131 @@
# Compose
With the `docker-compose` snippets within this directory you are able to plug
different setups of Semaphore UI together. Below you can find some example
combinations.
Some of the snippets define environment variables which could be optionally
overwritten if needed.
## Server
First of all we need the server definition and we need to decide if we want to
build the image dynamically or if we just want to use a released image.
### Build
This simply takes the currently cloned source and builds a new image including
all local changes.
```console
docker-compose -f deployment/compose/server/base.yml -f deployment/compose/server/build.yml up
```
### Image
This simply downloads the defined image from DockerHub and starts/configures it
properly based on the integrated bootstrapping scripts.
```console
docker-compose -f deployment/compose/server/base.yml -f deployment/compose/server/image.yml up
```
### Config
If you want to provide a custom `config.json` file to add options which are not
exposed as environment variables you could add this snippet which sources the
file from the current working directory.
```console
docker-compose <server from above> -f deployment/compose/server/config.yml up
```
## Runner
If you want to try the remote runner functionality of Semaphore you could just
add this snippet to get a runner up and connected to semaphore. Similar to the
examples above for the server you got different options like building the runner
from the source or using our prebuilt images.
### Build
This simply takes the currently cloned source and builds a new image including
all local changes.
```console
docker-compose <server from above> -f deployment/compose/runner/base.yml -f deployment/compose/runner/build.yml up
```
### Image
This simply downloads the defined image from DockerHub and starts/configures it
properly based on the integrated bootstrapping scripts.
```console
docker-compose <server from above> -f deployment/compose/runner/base.yml -f deployment/compose/runner/image.yml up
```
### Config
If you want to provide a custom `config.json` file to add options which are not
exposed as environment variables you could add this snippet which sources the
file from the current working directory.
```console
docker-compose <runner from above> -f deployment/compose/runner/config.yml up
```
## Database
After deciding the base of it you should choose one of the supported databases.
Here we got currently the following options so far.
### SQLite
This simply configures a named volume for the SQLite storage used as a database
backend.
```console
docker-compose <server/runner from above> -f deployment/compose/store/sqlite.yml up
```
### BoltDB
This simply configures a named volume for the BoltDB storage used as a database
backend.
```console
docker-compose <server/runner from above> -f deployment/compose/store/boltdb.yml up
```
### MariaDB
This simply starts an additional container for a MariaDB instance used as a
database backend including the required credentials.
```console
docker-compose <server/runner from above> -f deployment/compose/store/mariadb.yml up
```
### MySQL
This simply starts an additional container for a MySQL instance used as a
database backend including the required credentials.
```console
docker-compose <server/runner from above> -f deployment/compose/store/mysql.yml up
```
### PostgreSQL
This simply starts an additional container for a PostgreSQL instance used as a
database backend including the required credentials.
```console
docker-compose <server/runner from above> -f deployment/compose/store/postgres.yml up
```
## Cleanup
After playing with the setup you are able to stop the whole setup by just
replacing `up` at the end of the command with `down`.

View File

@ -0,0 +1,25 @@
version: "3.4"
volumes:
dredd:
services:
server:
environment:
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
dredd:
build:
context: ../../../
dockerfile: deployment/docker/dredd/Dockerfile
command:
- --config
- .dredd/dredd.docker.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,13 @@
version: "3.4"
services:
runner:
image: docker.io/semaphoreui/runner:${SEMAPHORE_VERSION:-latest}
restart: always
environment:
SEMAPHORE_RUNNER_API_URL: ${SEMAPHORE_RUNNER_API_URL:-http://server:3000/api}
SEMAPHORE_RUNNER_REGISTRATION_TOKEN: ${SEMAPHORE_RUNNER_REGISTRATION_TOKEN:-H1wDyorbg6gTSwJlVwle2Fne}
server:
environment:
SEMAPHORE_RUNNER_REGISTRATION_TOKEN: ${SEMAPHORE_RUNNER_REGISTRATION_TOKEN:-H1wDyorbg6gTSwJlVwle2Fne}

View File

@ -0,0 +1,7 @@
version: "3.4"
services:
runner:
build:
context: ../../../
dockerfile: deployment/docker/runner/Dockerfile

View File

@ -0,0 +1,6 @@
version: "3.4"
services:
runner:
volumes:
- ${SEMAPHORE_RUNNER_LOCAL_CONFIG:-runner.json}:/etc/semaphore/config.json:Z

View File

@ -0,0 +1,20 @@
version: "3.4"
volumes:
server:
services:
server:
image: docker.io/semaphoreui/semaphore:${SEMAPHORE_VERSION:-latest}
restart: always
environment:
SEMAPHORE_ADMIN_NAME: ${SEMAPHORE_ADMIN_NAME:-Admin}
SEMAPHORE_ADMIN: ${SEMAPHORE_ADMIN_USERNAME:-admin}
SEMAPHORE_ADMIN_PASSWORD: ${SEMAPHORE_ADMIN_PASSWORD:-p455w0rd}
SEMAPHORE_ADMIN_EMAIL: ${SEMAPHORE_ADMIN_EMAIL:-admin@localhost}
SEMAPHORE_WEB_ROOT: ${SEMAPHORE_WEB_ROOT:-http://0.0.0.0:3000}
SEMAPHORE_ACCESS_KEY_ENCRYPTION: ${SEMAPHORE_ACCESS_KEY_ENCRYPTION:-IlRqgrrO5Gp27MlWakDX1xVrPv4jhoUx+ARY+qGyDxQ=}
volumes:
- server:/var/lib/semaphore
ports:
- "3000:3000"

View File

@ -0,0 +1,7 @@
version: "3.4"
services:
server:
build:
context: ../../../
dockerfile: deployment/docker/server/Dockerfile

View File

@ -0,0 +1,6 @@
version: "3.4"
services:
server:
volumes:
- ${SEMAPHORE_RUNNER_LOCAL_CONFIG:-config.json}:/etc/semaphore/config.json:Z

View File

@ -0,0 +1,12 @@
version: "3.4"
volumes:
boltdb:
services:
server:
environment:
SEMAPHORE_DB_DIALECT: bolt
SEMAPHORE_DB_PATH: /var/lib/database
volumes:
- boltdb:/var/lib/database

View File

@ -0,0 +1,31 @@
version: "3.4"
volumes:
mariadb:
postgres:
services:
mariadb:
image: mariadb:10.8
restart: always
environment:
MARIADB_ROOT_PASSWORD: root
MARIADB_USER: semaphore
MARIADB_PASSWORD: semaphore
MARIADB_DATABASE: semaphore
volumes:
- mariadb:/var/lib/mysql
ports:
- 3306:3306
postgres:
image: postgres:14.3
restart: always
environment:
POSTGRES_USER: semaphore
POSTGRES_PASSWORD: semaphore
POSTGRES_DB: semaphore
volumes:
- postgres:/var/lib/postgresql
ports:
- 5432:5432

View File

@ -0,0 +1,27 @@
version: "3.4"
volumes:
mariadb:
services:
server:
environment:
SEMAPHORE_DB_DIALECT: mysql
SEMAPHORE_DB_HOST: db
SEMAPHORE_DB_PORT: 3306
SEMAPHORE_DB_USER: ${MARIADB_USERNAME:-semaphore}
SEMAPHORE_DB_PASS: ${MARIADB_PASSWORD:-semaphore}
SEMAPHORE_DB: ${MARIADB_DATABASE:-semaphore}
depends_on:
- db
db:
image: mariadb:10.8
restart: always
environment:
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT:-root}
MARIADB_USER: ${MARIADB_USERNAME:-semaphore}
MARIADB_PASSWORD: ${MARIADB_PASSWORD:-semaphore}
MARIADB_DATABASE: ${MARIADB_DATABASE:-semaphore}
volumes:
- mariadb:/var/lib/mysql

View File

@ -0,0 +1,27 @@
version: "3.4"
volumes:
mysql:
services:
server:
environment:
SEMAPHORE_DB_DIALECT: mysql
SEMAPHORE_DB_HOST: db
SEMAPHORE_DB_PORT: 3306
SEMAPHORE_DB_USER: ${MYSQL_USERNAME:-semaphore}
SEMAPHORE_DB_PASS: ${MYSQL_PASSWORD:-semaphore}
SEMAPHORE_DB: ${MYSQL_DATABASE:-semaphore}
depends_on:
- db
db:
image: mysql:8.0
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT:-root}
MYSQL_USER: ${MYSQL_USERNAME:-semaphore}
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-semaphore}
MYSQL_DATABASE: ${MYSQL_DATABASE:-semaphore}
volumes:
- mysql:/var/lib/mysql

View File

@ -0,0 +1,26 @@
version: "3.4"
volumes:
postgres:
services:
server:
environment:
SEMAPHORE_DB_DIALECT: postgres
SEMAPHORE_DB_HOST: db
SEMAPHORE_DB_PORT: 5432
SEMAPHORE_DB_USER: ${POSTGRES_USERNAME:-semaphore}
SEMAPHORE_DB_PASS: ${POSTGRES_PASSWORD:-semaphore}
SEMAPHORE_DB: ${POSTGRES_DATABASE:-semaphore}
depends_on:
- db
db:
image: postgres:14.3
restart: always
environment:
POSTGRES_USER: ${POSTGRES_USERNAME:-semaphore}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-semaphore}
POSTGRES_DB: ${POSTGRES_DATABASE:-semaphore}
volumes:
- postgres:/var/lib/postgresql

View File

@ -0,0 +1,12 @@
version: "3.4"
volumes:
sqlite:
services:
server:
environment:
SEMAPHORE_DB_DIALECT: sqlite
SEMAPHORE_DB_PATH: /var/lib/database
volumes:
- sqlite:/var/lib/database

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,34 @@
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
WORKDIR /go/src/semaphore
COPY go.mod go.sum /go/src/semaphore/
RUN --mount=type=cache,target=/go/pkg \
go mod download -x
COPY . /go/src/semaphore
RUN --mount=type=cache,target=/go/pkg --mount=type=cache,target=/root/.cache/go-build \
task deps:tools && \
task deps:be && \
task e2e:goodman && \
task e2e:hooks
FROM apiaryio/dredd:13.0.0
RUN apk add --no-cache -U \
bash git go
COPY --from=golang /go/bin/goodman /root/go/bin/goodman
COPY --from=golang /go/src/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,63 @@
FROM --platform=$BUILDPLATFORM golang:1.22-alpine3.18 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
WORKDIR /go/src/semaphore
COPY go.mod go.sum /go/src/semaphore/
RUN --mount=type=cache,target=/go/pkg \
go mod download -x
COPY . /go/src/semaphore
ARG TARGETOS
ARG TARGETARCH
RUN --mount=type=cache,target=/go/src/semaphore/web/node_modules \
--mount=type=cache,target=/go/pkg \
--mount=type=cache,target=/root/.cache/go-build \
task deps && \
task build GOOS=${TARGETOS} GOARCH=${TARGETARCH}
FROM alpine:3.19
RUN apk add --no-cache -U \
bash curl git gnupg mysql-client openssh-client-default python3 python3-dev py3-pip rsync sshpass tar tini tzdata unzip wget zip build-base openssl-dev libffi-dev cargo && \
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
ENV PATH /home/semaphore/.local/bin:$PATH
# 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.txt"; then
echoerr "Installing additional python dependencies"
pip3 install --upgrade --user \
-r "${SEMAPHORE_CONFIG_PATH}/requirements.txt"
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,63 @@
FROM --platform=$BUILDPLATFORM golang:1.22-alpine3.18 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
WORKDIR /go/src/semaphore
COPY go.mod go.sum /go/src/semaphore/
RUN --mount=type=cache,target=/go/pkg \
go mod download -x
COPY . /go/src/semaphore
ARG TARGETOS
ARG TARGETARCH
RUN --mount=type=cache,target=/go/src/semaphore/web/node_modules \
--mount=type=cache,target=/go/pkg \
--mount=type=cache,target=/root/.cache/go-build \
task deps && \
task build GOOS=${TARGETOS} GOARCH=${TARGETARCH}
FROM alpine:3.19
RUN apk add --no-cache -U \
bash curl git gnupg mysql-client openssh-client-default python3 python3-dev py3-pip rsync sshpass tar tini tzdata unzip wget zip build-base openssl-dev libffi-dev cargo && \
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
ENV PATH /home/semaphore/.local/bin:$PATH
# 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.txt"; then
echoerr "Installing additional python dependencies"
pip3 install --upgrade --user \
-r "${SEMAPHORE_CONFIG_PATH}/requirements.txt"
else
echoerr "No additional python dependencies to install"
fi
echoerr "Starting semaphore server"
if test "$#" -ne 1; then
exec /usr/local/bin/semaphore server --config "${SEMAPHORE_CONFIG_PATH}/config.json"
else
exec "$@"
fi

View File

@ -5,6 +5,7 @@ import (
"fmt"
"os"
"strconv"
"maps"
"github.com/ansible-semaphore/semaphore/db"
"github.com/ansible-semaphore/semaphore/db_lib"
@ -19,6 +20,7 @@ type LocalJob struct {
Inventory db.Inventory
Repository db.Repository
Environment db.Environment
Secret string
Logger task_logger.Logger
App db_lib.LocalApp
@ -90,6 +92,7 @@ func (t *LocalJob) getEnvironmentExtraVars(username string, incomingVersion *str
func (t *LocalJob) getEnvironmentExtraVarsJSON(username string, incomingVersion *string) (str string, err error) {
extraVars := make(map[string]interface{})
extraSecretVars := make(map[string]interface{})
if t.Environment.JSON != "" {
err = json.Unmarshal([]byte(t.Environment.JSON), &extraVars)
@ -97,6 +100,15 @@ func (t *LocalJob) getEnvironmentExtraVarsJSON(username string, incomingVersion
return
}
}
if t.Secret != "" {
err = json.Unmarshal([]byte(t.Secret), &extraSecretVars)
if err != nil {
return
}
}
t.Secret = "{}"
maps.Copy(extraVars, extraSecretVars)
taskDetails := make(map[string]interface{})

View File

@ -318,6 +318,8 @@ func (p *TaskPool) AddTask(taskObj db.Task, userID *int, projectID int) (newTask
taskObj.Status = task_logger.TaskWaitingStatus
taskObj.UserID = userID
taskObj.ProjectID = projectID
extraSecretVars := taskObj.Secret
taskObj.Secret = "{}"
tpl, err := p.store.GetTemplate(projectID, taskObj.TemplateID)
if err != nil {
@ -375,6 +377,7 @@ func (p *TaskPool) AddTask(taskObj db.Task, userID *int, projectID int) (newTask
Inventory: taskRunner.Inventory,
Repository: taskRunner.Repository,
Environment: taskRunner.Environment,
Secret: extraSecretVars,
Logger: app.SetLogger(&taskRunner),
App: app,
}

View File

@ -134,7 +134,7 @@ type ConfigType struct {
// SshConfigPath is a path to the custom SSH config file.
// Default path is ~/.ssh/config.
SshConfigPath string `json:"ssh_config_path" env:"SEMAPHORE_TMP_PATH"`
SshConfigPath string `json:"ssh_config_path" env:"SEMAPHORE_SSH_PATH"`
GitClientId string `json:"git_client" rule:"^go_git|cmd_git$" env:"SEMAPHORE_GIT_CLIENT" default:"cmd_git"`
@ -598,7 +598,7 @@ func CheckUpdate() (updateAvailable *github.RepositoryRelease, err error) {
}
updateAvailable = nil
if (*releases[0].TagName)[1:] != Version {
if (*releases[0].TagName)[1:] != Version() {
updateAvailable = releases[0]
}

View File

@ -1,13 +0,0 @@
[Unit]
Description=Semaphore Ansible
After=network.target
[Service]
User=www-data
Group=www-data
ExecStart=/usr/bin/semaphore server --config /etc/semaphore/config.json
ExecReload=/bin/kill -HUP $MAINPID
Type=simple
[Install]
WantedBy=multi-user.target

19
util/version.go Normal file
View File

@ -0,0 +1,19 @@
package util
import (
"strings"
)
var (
Ver = "undefined"
Commit = "00000000"
Date = ""
)
func Version() string {
return strings.Join([]string{
Ver,
Commit,
Date,
}, "-")
}

View File

@ -1,47 +0,0 @@
// +build ignore
package main
import (
"log"
"os"
"text/template"
)
var versionTmpl = `package util
//Version is the Semaphore build version as a string
var Version = "{{ .VERSION }}"
`
func main(){
if len(os.Args) <= 1 {
log.Fatalln("Must pass in version number")
}
data := make(map[string]string)
data["VERSION"] = os.Args[1]
tmpl := template.New("version")
var err error
if tmpl, err = tmpl.Parse(versionTmpl); err != nil {
log.Fatalln(err)
}
f, err := os.Create("util/version.go")
if err != nil {
log.Fatalln(err)
}
defer func(r *os.File) {
err = r.Close()
if err != nil {
log.Fatalln(err)
}
}(f)
err = tmpl.Execute(f, data)
if err != nil {
log.Println(err)
}
}

38
web/package-lock.json generated
View File

@ -11441,15 +11441,21 @@
}
},
"node_modules/nanoid": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz",
"integrity": "sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==",
"version": "5.0.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz",
"integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.js"
},
"engines": {
"node": "^14 || ^16 || >=18"
"node": "^18 || >=20"
}
},
"node_modules/nanomatch": {
@ -13456,10 +13462,16 @@
"dev": true
},
"node_modules/postcss/node_modules/nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@ -27513,9 +27525,9 @@
}
},
"nanoid": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz",
"integrity": "sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==",
"version": "5.0.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz",
"integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==",
"dev": true
},
"nanomatch": {
@ -28684,9 +28696,9 @@
},
"dependencies": {
"nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"dev": true
}
}

View File

@ -13,6 +13,12 @@
lazy-validation
v-if="editedVar != null"
>
<v-alert
:value="formError"
color="error"
>{{ formError }}
</v-alert>
<v-text-field
:label="$t('name2')"
v-model.trim="editedVar.name"
@ -39,6 +45,57 @@
item-value="id"
item-text="name"
></v-select>
<v-data-table
v-if="editedVar.type === 'enum'"
:items="editedValues"
:items-per-page="-1"
class="elevation-1"
hide-default-footer
no-data-text="No values"
>
<template v-slot:item="props">
<tr>
<td class="pa-1">
<v-text-field
solo-inverted
flat
hide-details
v-model="props.item.name"
class="v-text-field--solo--no-min-height"
></v-text-field>
</td>
<td class="pa-1">
<v-text-field
solo-inverted
flat
hide-details
v-model="props.item.value"
class="v-text-field--solo--no-min-height"
></v-text-field>
</td>
<td style="width: 38px;">
<v-icon
small
class="pa-1"
@click="removeEditedVarValue(props.item)"
>
mdi-delete
</v-icon>
</td>
</tr>
</template>
</v-data-table>
<div class="text-right mt-2">
<v-btn
color="primary"
v-if="editedVar.type === 'enum'"
@click="addEditedVarValue()"
>Add Value</v-btn>
</div>
<v-checkbox
:label="$t('required')"
v-model="editedVar.required"
@ -114,6 +171,7 @@ export default {
return {
editDialog: null,
editedVar: null,
editedValues: [],
editedVarIndex: null,
modifiedVars: null,
varTypes: [{
@ -122,29 +180,86 @@ export default {
}, {
id: 'int',
name: 'Integer',
}, {
id: 'secret',
name: 'Secret',
}, {
id: 'enum',
name: 'Enum',
}],
formError: null,
};
},
methods: {
addEditedVarValue() {
this.editedValues.push({
name: '',
value: '',
});
},
removeEditedVarValue(val) {
const i = this.editedValues.findIndex((v) => v.name === val.name);
if (i > -1) {
this.editedValues.splice(i, 1);
}
},
editVar(index) {
this.editedVar = index != null ? { ...this.modifiedVars[index] } : {};
this.editedValues = [];
this.editedValues.push(...(this.editedVar.values || []));
this.editedVar.values = this.editedValues;
this.editedVarIndex = index;
if (this.$refs.form) {
this.$refs.form.resetValidation();
}
this.editDialog = true;
},
saveVar() {
this.formError = null;
if (!this.$refs.form.validate()) {
return;
}
if (this.editedVar.type === 'enum') {
if (this.editedValues.length === 0) {
this.formError = 'Enumeration must have values.';
return;
}
const uniq = new Set(this.editedValues.map((v) => v.name));
if (this.editedValues.length !== uniq.size) {
this.formError = 'Enumeration values must have unique names.';
return;
}
this.editedValues.forEach((v) => {
if (v.name === '') {
this.formError = 'Value name cannot be empty.';
}
});
if (this.formError != null) {
return;
}
} else {
this.editedVar.values = [];
}
if (this.editedVarIndex != null) {
this.modifiedVars[this.editedVarIndex] = this.editedVar;
} else {
this.modifiedVars.push(this.editedVar);
}
this.editDialog = false;
this.editedVar = null;
this.$emit('change', this.modifiedVars);

View File

@ -45,19 +45,48 @@
:disabled="formSaving"
/>
<v-text-field
v-for="(v) in template.survey_vars || []"
:key="v.name"
:label="v.title"
:hint="v.description"
v-model="editedEnvironment[v.name]"
:required="v.required"
:rules="[
val => !v.required || !!val || v.title + $t('isRequired'),
<div v-for="(v) in template.survey_vars || []" :key="v.name">
<v-text-field
v-if="v.type === 'secret'"
:label="v.title"
:hint="v.description"
v-model="editedSecretEnvironment[v.name]"
:required="v.required"
type="password"
:rules="[
val => !v.required || !!val || v.title + $t('isRequired'),
]"
/>
<v-select
clearable
v-else-if="v.type === 'enum'"
:label="v.title + (v.required ? ' *' : '')"
:hint="v.description"
v-model="editedEnvironment[v.name]"
:required="v.required"
:rules="[
val => !v.required || val != null || v.title + ' ' + $t('isRequired')
]"
:items="v.values"
item-text="name"
item-value="value"
/>
<v-text-field
v-else
:label="v.title + (v.required ? ' *' : '')"
:hint="v.description"
v-model="editedEnvironment[v.name]"
:required="v.required"
:rules="[
val => !v.required || !!val || v.title + ' ' + $t('isRequired'),
val => !val || v.type !== 'int' || /^\d+$/.test(val) ||
v.title + ' ' + $t('mustBeInteger'),
]"
/>
/>
</div>
<v-row no-gutters class="mt-6">
<v-col cols="12" sm="6">
@ -104,7 +133,6 @@
text
class="mb-2"
>
{{ $t('pleaseAllowOverridingCliArgumentInTaskTemplateSett') }}<br>
<div style="position: relative; margin-top: 10px;">
<video
autoplay
@ -155,6 +183,7 @@ export default {
buildTasks: null,
commitAvailable: null,
editedEnvironment: null,
editedSecretEnvironment: null,
cmOptions: {
tabSize: 2,
mode: 'application/json',
@ -214,6 +243,7 @@ export default {
});
this.editedEnvironment = JSON.parse(v.environment || '{}');
this.editedSecretEnvironment = JSON.parse(v.secret || '{}');
this.commitAvailable = v.commit_hash != null;
},
@ -225,6 +255,7 @@ export default {
beforeSave() {
this.item.environment = JSON.stringify(this.editedEnvironment);
this.item.secret = JSON.stringify(this.editedSecretEnvironment);
},
async afterLoadData() {

View File

@ -130,7 +130,7 @@ export default {
diff: 'Diff',
advanced: 'Advanced',
hide: 'Hide',
pleaseAllowOverridingCliArgumentInTaskTemplateSett: 'Please allow overriding CLI argument in Task Template settings',
pleaseAllowOverridingCliArgumentInTaskTemplateSett: 'To allow overriding CLI argument in Task Template settings',
cliArgsJsonArrayExampleIMyinventoryshPrivatekeythe: 'CLI Args (JSON array). Example: [ "-i", "@myinventory.sh", "--private-key=/there/id_rsa", "-vvvv" ]',
started: 'Started',
author: 'Author',