diff --git a/.github/workflows/auto-approve.yml b/.github/workflows/auto-approve.yml index e95b6f3..64d6ffc 100644 --- a/.github/workflows/auto-approve.yml +++ b/.github/workflows/auto-approve.yml @@ -2,7 +2,7 @@ name: Auto Approve on: workflow_run: - workflows: [Tests, Build Docker Image] + workflows: [Tests] types: [completed] jobs: diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index 05bf51e..0000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Build Docker Image - -on: - push: - branches: [main, develop] - pull_request: - branches: [main, develop] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v5 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - context: . - push: false - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2d3a530..36ccbc2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,65 +1,83 @@ name: Tests - on: + workflow_dispatch: push: - branches: [main, develop] + branches: + - main + - develop pull_request: - branches: [main, develop] - + branches: + - main + - develop jobs: test: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ['3.13', '3.14'] - + os: + - ubuntu-latest + - macos-latest + - windows-latest + python-version: + - '3.13' + - '3.14' steps: - - uses: actions/checkout@v5 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -e ".[dev]" - - - name: Run linters - run: | - ruff check src tests - mypy src --ignore-missing-imports - - - name: Run tests with coverage - run: | - pytest --cov=src --cov-report=xml --cov-report=term --cov-fail-under=80 - - - name: Upload coverage reports - uses: codecov/codecov-action@v5 - with: - files: ./coverage.xml - flags: unittests - name: codecov-umbrella - fail_ci_if_error: false - + - uses: actions/checkout@v5 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e ".[dev]" + - name: Run linters + run: | + ruff check src tests + mypy src --ignore-missing-imports + - name: Run tests with coverage + run: pytest --cov=src --cov-report=xml --cov-report=term --cov-fail-under=80 + - name: Upload coverage reports + uses: codecov/codecov-action@v5 + with: + files: ./coverage.xml + flags: unittests + name: codecov-umbrella + fail_ci_if_error: false security: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 - - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: '3.14' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install bandit safety - - - name: Run security checks - run: | - bandit -r src -f json -o bandit-report.json || true - safety check --json || true + - uses: actions/checkout@v5 + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: '3.14' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install bandit safety + - name: Run security checks + run: | + bandit -r src -f json -o bandit-report.json || true + safety check --json || true + push-to-gitlab: + name: Push to GitLab + runs-on: ubuntu-latest + needs: + - test + if: always() && needs.test.result == 'success' + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + fetch-depth: 0 + - name: Push to GitLab + env: + GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} + run: | + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + git remote add gitlab https://gitlab.com/dk-raas/dkai/high-command/high-command-api.git || true + git config credential.helper '!f() { echo "username=oauth2"; echo "password=${GITLAB_TOKEN}"; }; f' + git fetch gitlab main || true + git push gitlab HEAD:main || echo "GitLab push failed" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..673ffaf --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,39 @@ +stages: + - build + +variables: + HARBOR_REGISTRY: harbor.dataknife.net + HARBOR_PROJECT: library + IMAGE_NAME: high-command-api + +docker-build: + stage: build + image: docker:latest + services: + - docker:dind + before_script: + - echo "$HARBOR_PASSWORD" | docker login -u "$HARBOR_USERNAME" --password-stdin $HARBOR_REGISTRY + script: + - | + # Build image once + docker build -t $HARBOR_REGISTRY/$HARBOR_PROJECT/$IMAGE_NAME:latest . + + # Tag with commit SHA + docker tag $HARBOR_REGISTRY/$HARBOR_PROJECT/$IMAGE_NAME:latest \ + $HARBOR_REGISTRY/$HARBOR_PROJECT/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA + + # Push images + docker push $HARBOR_REGISTRY/$HARBOR_PROJECT/$IMAGE_NAME:latest + docker push $HARBOR_REGISTRY/$HARBOR_PROJECT/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA + + # Tag with version if tag exists + if [ -n "$CI_COMMIT_TAG" ]; then + docker tag $HARBOR_REGISTRY/$HARBOR_PROJECT/$IMAGE_NAME:latest \ + $HARBOR_REGISTRY/$HARBOR_PROJECT/$IMAGE_NAME:$CI_COMMIT_TAG + docker push $HARBOR_REGISTRY/$HARBOR_PROJECT/$IMAGE_NAME:$CI_COMMIT_TAG + fi + rules: + - if: '$CI_COMMIT_BRANCH == "main"' + - if: '$CI_COMMIT_TAG' + tags: + - docker diff --git a/Makefile b/Makefile index e916412..cfd1d07 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: help install dev test lint format clean docker-build docker-run run check check-all commit-changes release venv +.PHONY: help install dev test lint format clean docker-build docker-login docker-push docker-pull docker-run run check check-all commit-changes release venv # Force use of bash shell (required for make to work properly with line continuations) SHELL := /bin/bash @@ -29,6 +29,9 @@ help: @echo " format Format code with black and ruff" @echo " clean Remove build artifacts and cache files" @echo " docker-build Build Docker image" + @echo " docker-login Login to Harbor registry (requires HARBOR_USERNAME and HARBOR_PASSWORD)" + @echo " docker-push Build and push image to Harbor registry" + @echo " docker-pull Pull image from Harbor registry" @echo " docker-run Run Docker container" @echo " check Run linters and tests" @echo " check-all Format, lint, and test (quality gate)" @@ -70,8 +73,31 @@ clean: find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true rm -rf build dist .pytest_cache .mypy_cache htmlcov .ruff_cache .coverage +HARBOR_REGISTRY := harbor.dataknife.net +HARBOR_IMAGE := $(HARBOR_REGISTRY)/library/$(APP_NAME) +IMAGE_TAG ?= latest + +docker-login: + @echo "Logging into Harbor registry..." + @if [ -z "$$HARBOR_USERNAME" ] || [ -z "$$HARBOR_PASSWORD" ]; then \ + echo "Error: HARBOR_USERNAME and HARBOR_PASSWORD must be set"; \ + exit 1; \ + fi + @echo "$$HARBOR_PASSWORD" | docker login $(HARBOR_REGISTRY) \ + -u "$$HARBOR_USERNAME" \ + --password-stdin + docker-build: docker build -t $(APP_NAME):latest . + docker tag $(APP_NAME):latest $(HARBOR_IMAGE):$(IMAGE_TAG) + +# Ensure we are logged into Harbor before building/tagging and pushing the image. +docker-push: docker-login docker-build + docker push $(HARBOR_IMAGE):$(IMAGE_TAG) + +docker-pull: + docker pull $(HARBOR_IMAGE):$(IMAGE_TAG) + docker tag $(HARBOR_IMAGE):$(IMAGE_TAG) $(APP_NAME):latest docker-run: docker-build docker run -it --rm -p $(PORT):5000 $(APP_NAME):latest diff --git a/docker-compose.yml b/docker-compose.yml index b78d4b4..2bddb0c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.8' services: api: - build: . + image: harbor.dataknife.net/library/high-command-api:latest ports: - "5000:5000" environment: