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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
ARG GO_VERSION=1.24
ARG DEBIAN_CODENAME=trixie

FROM mcr.microsoft.com/devcontainers/go:${GO_VERSION}-${DEBIAN_CODENAME}

ARG PG_MAJOR=16
ARG MARIADB_MAJOR=12.0.2

USER root

# Add PostgreSQL and MariaDB official repositories (detect codename from /etc/os-release)
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
wget ca-certificates gnupg curl \
&& wget -qO- https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg \
&& echo "deb http://apt.postgresql.org/pub/repos/apt $(. /etc/os-release && echo $VERSION_CODENAME)-pgdg main" > /etc/apt/sources.list.d/pgdg.list \
&& curl -LsSO https://r.mariadb.com/downloads/mariadb_repo_setup \
&& chmod +x mariadb_repo_setup \
&& ./mariadb_repo_setup --mariadb-server-version="mariadb-${MARIADB_MAJOR}" --skip-maxscale --skip-tools \
&& rm -f mariadb_repo_setup \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
postgresql-${PG_MAJOR} postgresql-client-${PG_MAJOR} \
mariadb-server mariadb-client \
&& rm -rf /var/lib/apt/lists/*

# Prepare user-owned data dirs for rootless startup
RUN mkdir -p /home/vscode/.local/share/pg/pgdata \
&& mkdir -p /home/vscode/.local/share/mysql \
&& chown -R vscode:vscode /home/vscode/.local/share

# Set environment variables based on build args
ENV PG_BIN_DIR=/usr/lib/postgresql/${PG_MAJOR}/bin

USER vscode

45 changes: 45 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "singularity",
"build": {
"dockerfile": "Dockerfile",
"args": {
"GO_VERSION": "1.24",
"DEBIAN_CODENAME": "trixie",
"PG_MAJOR": "16",
"MARIADB_MAJOR": "12.0.2"
}
},
"customizations": {
"vscode": {
"extensions": [
"golang.go",
"eamodio.gitlens"
],
"settings": {
"go.useLanguageServer": true,
"go.toolsEnvVars": {
"GOPATH": "/home/vscode/go"
}
}
}
},
"forwardPorts": [],
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/singularity,type=bind,consistency=cached,relabel=private",
"workspaceFolder": "/workspaces/singularity",
"postCreateCommand": "/bin/bash -lc '${containerWorkspaceFolder}/.devcontainer/post-create.sh'",
"postStartCommand": "/bin/bash -lc '${containerWorkspaceFolder}/.devcontainer/start-postgres.sh && ${containerWorkspaceFolder}/.devcontainer/start-mysql.sh'",
"remoteUser": "vscode",
"containerEnv": {
"GOPATH": "/home/vscode/go",
"MYSQL_DATABASE": "singularity",
"MYSQL_USER": "singularity",
"MYSQL_PASSWORD": "singularity",
"MYSQL_SOCKET": "/home/vscode/.local/share/mysql/mysql.sock",
"PGDATA": "/home/vscode/.local/share/pg/pgdata",
"PGPORT": "55432",
"PGSOCK_DIR": "/home/vscode/.local/share/pg"
},
"runArgs": ["--userns=keep-id"],
"containerUser": "vscode"
}

55 changes: 55 additions & 0 deletions .devcontainer/init-mysql.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
set -euo pipefail

# MariaDB client is required for init

# Resolve socket path; default to user-owned socket
SOCKET="${MYSQL_SOCKET:-${HOME}/.local/share/mysql/mysql.sock}"

## Removed one-time init guard; operations below are idempotent

# Determine root auth flags
MYSQL_ROOT_FLAGS=("-uroot")
if [ -n "${MYSQL_ROOT_PASSWORD:-}" ]; then
MYSQL_ROOT_FLAGS+=("-p${MYSQL_ROOT_PASSWORD}")
fi

# Wait for server readiness (best effort)
echo "Waiting for MySQL server at socket: $SOCKET"
for i in {1..60}; do
if mariadb-admin --socket="$SOCKET" ping "${MYSQL_ROOT_FLAGS[@]}" >/dev/null 2>&1; then
echo "MySQL server is ready for init"
break
fi
sleep 1
done

# Bail if still unreachable
if ! mariadb-admin --socket="$SOCKET" ping "${MYSQL_ROOT_FLAGS[@]}" >/dev/null 2>&1; then
echo "MySQL server not reachable, init failed"
exit 1
fi

# Create database and user idempotently (MySQL 8+ supports IF NOT EXISTS for users)
DB=${MYSQL_DATABASE:-singularity}
USER=${MYSQL_USER:-singularity}
PASS=${MYSQL_PASSWORD:-singularity}

echo "Creating database and user: ${USER}@localhost and ${USER}@%"
mariadb --socket="$SOCKET" "${MYSQL_ROOT_FLAGS[@]}" <<SQL
CREATE DATABASE IF NOT EXISTS \`${DB}\`;
CREATE USER IF NOT EXISTS '${USER}'@'localhost' IDENTIFIED BY '${PASS}';
CREATE USER IF NOT EXISTS '${USER}'@'%' IDENTIFIED BY '${PASS}';
-- tests create per-run databases, so grant global privileges for dev only
GRANT ALL PRIVILEGES ON *.* TO '${USER}'@'localhost' WITH GRANT OPTION;
GRANT ALL PRIVILEGES ON *.* TO '${USER}'@'%' WITH GRANT OPTION;
GRANT ALL PRIVILEGES ON \`${DB}\`.* TO '${USER}'@'localhost';
GRANT ALL PRIVILEGES ON \`${DB}\`.* TO '${USER}'@'%';
FLUSH PRIVILEGES;
SQL

echo "MySQL init completed successfully"

## No marker file; script can run safely on every start


55 changes: 55 additions & 0 deletions .devcontainer/post-create.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
set -euo pipefail

# Initialize Go modules
go mod download

chmod +x .devcontainer/*.sh || true

# Initialize database systems
echo "Setting up databases..."

# Create data directories
mkdir -p /home/vscode/.local/share/pg/pgdata /home/vscode/.local/share/pg /home/vscode/.local/share/mysql/data /home/vscode/.local/share/mysql

# Initialize Postgres
if [ ! -f "/home/vscode/.local/share/pg/pgdata/PG_VERSION" ]; then
echo "Initializing Postgres..."
/usr/lib/postgresql/16/bin/initdb -D /home/vscode/.local/share/pg/pgdata --auth trust --auth-local trust --auth-host trust
echo "listen_addresses='localhost'" >> /home/vscode/.local/share/pg/pgdata/postgresql.conf
{
echo 'host all all 127.0.0.1/32 trust'
echo 'host all all ::1/128 trust'
echo 'local all all trust'
} >> /home/vscode/.local/share/pg/pgdata/pg_hba.conf
fi

# Initialize MariaDB
if [ ! -d "/home/vscode/.local/share/mysql/data/mysql" ]; then
echo "Initializing MariaDB..."
mariadb-install-db --datadir=/home/vscode/.local/share/mysql/data --auth-root-authentication-method=normal --skip-test-db >/dev/null
fi

# Start both servers
echo "Starting database servers..."
.devcontainer/start-postgres.sh
.devcontainer/start-mysql.sh

# Create users (databases will be created during testing as needed)
echo "Creating database users..."

# Postgres setup
psql -h localhost -p 55432 -d postgres -c "CREATE USER singularity WITH SUPERUSER CREATEDB CREATEROLE LOGIN;"

# MySQL setup
mariadb --socket=/home/vscode/.local/share/mysql/mysql.sock -uroot <<SQL
CREATE USER 'singularity'@'localhost' IDENTIFIED BY 'singularity';
CREATE USER 'singularity'@'%' IDENTIFIED BY 'singularity';
GRANT ALL PRIVILEGES ON *.* TO 'singularity'@'localhost' WITH GRANT OPTION;
GRANT ALL PRIVILEGES ON *.* TO 'singularity'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
SQL

echo "Database setup completed successfully"


46 changes: 46 additions & 0 deletions .devcontainer/start-mysql.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env bash
set -euo pipefail

# MariaDB server configuration
MYSQL_BASE="${HOME}/.local/share/mysql"
DATA_DIR="${MYSQL_BASE}/data"
SOCKET="${MYSQL_SOCKET:-${MYSQL_BASE}/mysql.sock}"
PID_FILE="${MYSQL_BASE}/mysql.pid"
PORT="${MYSQL_PORT:-3306}"
LOG_FILE="${MYSQL_BASE}/mysql.err"

# Check if already running
if [ -S "${SOCKET}" ] && mariadb-admin --socket="${SOCKET}" ping >/dev/null 2>&1; then
echo "MySQL already running"
exit 0
fi

# Start MariaDB server
echo "Starting MySQL server"
touch "${LOG_FILE}"
nohup mariadbd \
--datadir="${DATA_DIR}" \
--socket="${SOCKET}" \
--pid-file="${PID_FILE}" \
--bind-address=127.0.0.1 \
--port="${PORT}" \
--skip-name-resolve \
--log-error="${LOG_FILE}" \
>/dev/null 2>&1 &

# Wait for MySQL to be ready
for i in {1..60}; do
if [ -S "${SOCKET}" ] && grep -q "ready for connections" "${LOG_FILE}" >/dev/null 2>&1; then
echo "MySQL server is ready"
exit 0
fi
sleep 1
done

echo "MySQL server failed to start"
if [ -f "${LOG_FILE}" ]; then
echo "--- Begin MariaDB error log ---"
tail -n 200 "${LOG_FILE}" || true
echo "--- End MariaDB error log ---"
fi
exit 1
21 changes: 21 additions & 0 deletions .devcontainer/start-postgres.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail

# Postgres server configuration
PGDATA_DIR="${PGDATA:-/home/vscode/.local/share/pg/pgdata}"
LOG_FILE="${PGDATA_DIR}/postgres.log"
PGSOCK_DIR="${PGSOCK_DIR:-/home/vscode/.local/share/pg}"
PGPORT="${PGPORT:-55432}"
PG_BIN_DIR="${PG_BIN_DIR:-/usr/lib/postgresql/16/bin}"

# Check if already running
if "${PG_BIN_DIR}/pg_ctl" -D "$PGDATA_DIR" status >/dev/null 2>&1; then
echo "Postgres already running"
exit 0
fi

# Start Postgres server
echo "Starting Postgres server"
"${PG_BIN_DIR}/pg_ctl" -D "$PGDATA_DIR" -l "$LOG_FILE" -w -o "-p ${PGPORT} -k ${PGSOCK_DIR}" start


1 change: 1 addition & 0 deletions .github/workflows/container-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- 'main'
- 'develop'
tags:
- 'v*'
workflow_run:
Expand Down
94 changes: 94 additions & 0 deletions .github/workflows/devcontainer-podman.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: CI (Podman Devcontainer)

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true

jobs:
devcontainer-checks:
name: Devcontainer CI
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Podman as Docker
uses: parkan/github-actions/setup-podman-docker@v2
with:
disable-docker: true
cache-storage: false # Disabled until cache issues resolved

- name: Build devcontainer
id: build
uses: parkan/github-actions/devcontainer-build@v2
with:
workspace-folder: .
container-runtime: podman
container-id-label: ci=podman

# Fast surface checks and codegen first
- name: Generate swagger code
uses: parkan/github-actions/devcontainer-exec@v2
with:
container-id: ${{ steps.build.outputs.container-id }}
command: 'cd /workspaces/singularity && mkdir -p client/swagger/client && go install github.com/go-swagger/go-swagger/cmd/[email protected] && go generate ./client/swagger/...'
container-runtime: podman

- name: Check formatting
uses: parkan/github-actions/devcontainer-exec@v2
with:
container-id: ${{ steps.build.outputs.container-id }}
command: 'cd /workspaces/singularity && gofmt -l .'
container-runtime: podman

- name: Run go vet
uses: parkan/github-actions/devcontainer-exec@v2
with:
container-id: ${{ steps.build.outputs.container-id }}
command: 'cd /workspaces/singularity && go vet ./...'
container-runtime: podman

- name: Run staticcheck
uses: parkan/github-actions/devcontainer-exec@v2
with:
container-id: ${{ steps.build.outputs.container-id }}
command: 'cd /workspaces/singularity && staticcheck ./...'
container-runtime: podman

- name: Build binary
uses: parkan/github-actions/devcontainer-exec@v2
with:
container-id: ${{ steps.build.outputs.container-id }}
command: 'cd /workspaces/singularity && go build -o singularity .'
container-runtime: podman

- name: Run tests
uses: parkan/github-actions/devcontainer-exec@v2
with:
container-id: ${{ steps.build.outputs.container-id }}
command: 'cd /workspaces/singularity && go test -v ./...'
container-runtime: podman

- name: Run integration tests
uses: parkan/github-actions/devcontainer-exec@v2
with:
container-id: ${{ steps.build.outputs.container-id }}
command: 'cd /workspaces/singularity && SINGULARITY_TEST_INTEGRATION=true go test -v -timeout 20m -run "Integration" ./cmd/...'
container-runtime: podman

- name: Cleanup
if: always()
uses: parkan/github-actions/devcontainer-cleanup@v2
with:
container-id: ${{ steps.build.outputs.container-id }}
container-runtime: podman
3 changes: 0 additions & 3 deletions .github/workflows/go-check-config.json

This file was deleted.

18 changes: 0 additions & 18 deletions .github/workflows/go-check.yml

This file was deleted.

3 changes: 0 additions & 3 deletions .github/workflows/go-test-config.json

This file was deleted.

Loading