name: Deploy on: push: branches: ["main"] workflow_dispatch: env: APP_NAME: bc-netts-energy BINARY_NAME: netts-energy jobs: build-and-deploy: runs-on: ubuntu-latest env: SERVICE_PORT: ${{ secrets.SERVICE_PORT }} NETTS_API_KEY: ${{ secrets.NETTS_API_KEY }} steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Go uses: actions/setup-go@v5 with: go-version: "1.24" - name: Go module cache uses: actions/cache@v4 with: path: | ~/go/pkg/mod ~/.cache/go-build key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - name: Run unit tests run: go test ./... - name: Build binary run: | mkdir -p build go build -o build/${{ env.BINARY_NAME }} ./cmd/server - name: Upload binary to server uses: appleboy/scp-action@v0.1.7 with: host: ${{ secrets.SSH_HOST }} port: ${{ secrets.SSH_PORT }} username: ${{ secrets.SSH_USER }} password: ${{ secrets.SSH_PASSWORD }} source: build/${{ env.BINARY_NAME }} target: /tmp - name: Deploy on remote host uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.SSH_HOST }} port: ${{ secrets.SSH_PORT }} username: ${{ secrets.SSH_USER }} password: ${{ secrets.SSH_PASSWORD }} script_stop: true command_timeout: 30m envs: APP_NAME,BINARY_NAME,SERVICE_PORT,GITHUB_SHA,NETTS_API_KEY script: | set -euo pipefail APP_DIR="/opt/${APP_NAME}" RELEASE_ROOT="${APP_DIR}/releases" RELEASE_PATH="${RELEASE_ROOT}/${GITHUB_SHA}" CURRENT_LINK="${APP_DIR}/current" SHARED_DIR="${APP_DIR}/shared" BINARY_PATH="${RELEASE_PATH}/${BINARY_NAME}" REMOTE_TMP="/tmp/${BINARY_NAME}" mkdir -p "${RELEASE_ROOT}" "${SHARED_DIR}" if [ ! -f "${REMOTE_TMP}" ]; then echo "Binary ${REMOTE_TMP} not found on remote host." >&2 exit 1 fi mkdir -p "${RELEASE_PATH}" mv "${REMOTE_TMP}" "${BINARY_PATH}" chmod +x "${BINARY_PATH}" SERVICE_NAME="${APP_NAME}.service" SERVICE_EXISTS=0 if systemctl list-unit-files "${SERVICE_NAME}" >/dev/null 2>&1; then SERVICE_EXISTS=1 if systemctl is-active --quiet "${SERVICE_NAME}"; then systemctl stop "${SERVICE_NAME}" fi fi TARGET_PORT="${SERVICE_PORT:-8080}" if command -v ss >/dev/null 2>&1; then LISTEN_CMD="ss -ltn" elif command -v netstat >/dev/null 2>&1; then LISTEN_CMD="netstat -ltn" else echo "Neither ss nor netstat is available to check port usage." >&2 rm -rf "${RELEASE_PATH}" if [ "${SERVICE_EXISTS}" = "1" ]; then systemctl start "${SERVICE_NAME}" fi exit 1 fi if ${LISTEN_CMD} | awk '{print $4}' | grep -q ":${TARGET_PORT}$"; then echo "Port ${TARGET_PORT} is already in use. Deployment aborted." >&2 rm -rf "${RELEASE_PATH}" if [ "${SERVICE_EXISTS}" = "1" ]; then systemctl start "${SERVICE_NAME}" fi exit 1 fi if [ ! -f "${SHARED_DIR}/config.yaml" ]; then echo "Missing config at ${SHARED_DIR}/config.yaml. Deployment aborted." >&2 rm -rf "${RELEASE_PATH}" if [ "${SERVICE_EXISTS}" = "1" ]; then systemctl start "${SERVICE_NAME}" fi exit 1 fi if [ -z "${NETTS_API_KEY:-}" ]; then echo "NETTS_API_KEY is not provided. Deployment aborted." >&2 rm -rf "${RELEASE_PATH}" if [ "${SERVICE_EXISTS}" = "1" ]; then systemctl start "${SERVICE_NAME}" fi exit 1 fi if [ ! -f "/etc/systemd/system/${SERVICE_NAME}" ]; then cat < "/etc/systemd/system/${SERVICE_NAME}" [Unit] Description=BC Netts Energy Service After=network.target [Service] Type=simple WorkingDirectory=${APP_DIR}/current ExecStart=${APP_DIR}/current/${BINARY_NAME} -config ${SHARED_DIR}/config.yaml Restart=on-failure RestartSec=5 EnvironmentFile=-/opt/${APP_NAME}/shared/.env [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable "${SERVICE_NAME}" else systemctl daemon-reload fi ln -sfn "${RELEASE_PATH}" "${CURRENT_LINK}" ENV_FILE="${SHARED_DIR}/.env" cat < "${ENV_FILE}" NETTS_API_KEY=${NETTS_API_KEY} EOF chmod 600 "${ENV_FILE}" systemctl restart "${SERVICE_NAME}" systemctl --no-pager status "${SERVICE_NAME}"