Skip to content
Closed
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
52 changes: 52 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
name: Docker

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true

on: # yamllint disable-line rule:truthy
pull_request:
branches:
- main
- stable-*
tags:
- "*"

jobs:
build:
name: Build docker image
runs-on: ubuntu-latest
env:
image_name: "ansible-pattern-service-test"
image_tag: "${{ github.sha }}"
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Build the container image
run: make build
shell: bash
env:
CONTAINER_RUNTIME: docker
IMAGE_NAME: "${{ env.image_name }}"
IMAGE_TAG: "${{ env.image_tag }}"

- name: Run the container image
run: docker run -d -p "8000:5000" --name ansible-pattern-service-api "${IMAGE_NAME}:${IMAGE_TAG}"
shell: bash
env:
IMAGE_NAME: "${{ env.image_name }}"
IMAGE_TAG: "${{ env.image_tag }}"

- name: List running containers
run: docker ps -a
shell: bash

- name: Wait for the services to be up and running
run: sleep 15s
shell: bash

- name: Test the pattern service application
run: curl http://localhost:8000/ping/
shell: bash
12 changes: 5 additions & 7 deletions .github/workflows/units.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,9 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements/requirements-dev.txt

- name: Run migrations
run: python manage.py migrate

- name: Run core tests
run: python manage.py test core
python -m pip install -U tox
shell: bash

- name: Run unit tests
run: tox -e unit_tests -vv
shell: bash
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ pre-commit-user
*.py[c,o]
/.eggs
.python-version

/.install
/.tests

# Testing
.cache
Expand Down
21 changes: 0 additions & 21 deletions Dockerfile.dev

This file was deleted.

20 changes: 13 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
.PHONY: build build-multi run test clean install-deps lint push-quay login-quay push-quay-multi
.PHONY: build clean push build_amd64

# Image name and tag
CONTAINER_RUNTIME ?= podman
IMAGE_NAME ?= ansible-pattern-service
IMAGE_TAG ?= latest


# Build the Docker image
build:
build_amd64:
@echo "Building container image..."
$(CONTAINER_RUNTIME) build -t $(IMAGE_NAME):$(IMAGE_TAG) -f Dockerfile.dev --arch amd64 .
$(CONTAINER_RUNTIME) build -t $(IMAGE_NAME):$(IMAGE_TAG) -f tools/docker/Dockerfile.dev --arch amd64 .

ensure-namespace:
ifndef QUAY_NAMESPACE
$(error QUAY_NAMESPACE is required to push quay.io)
endif
build:
@echo "Building container image..."
$(CONTAINER_RUNTIME) build -t $(IMAGE_NAME):$(IMAGE_TAG) -f tools/docker/Dockerfile.dev .

# Clean up
clean:
Expand All @@ -25,3 +25,9 @@ push: ensure-namespace build
@echo "Tagging and pushing to registry..."
$(CONTAINER_RUNTIME) tag $(IMAGE_NAME):$(IMAGE_TAG) quay.io/$(QUAY_NAMESPACE)/$(IMAGE_NAME):$(IMAGE_TAG)
$(CONTAINER_RUNTIME) push quay.io/$(QUAY_NAMESPACE)/$(IMAGE_NAME):$(IMAGE_TAG)

ensure-namespace:
ifndef QUAY_NAMESPACE
$(error QUAY_NAMESPACE is required to push quay.io)
endif

9 changes: 0 additions & 9 deletions core/admin.py

This file was deleted.

148 changes: 130 additions & 18 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,30 +1,142 @@
[build-system]
requires = ["poetry-core>=2.0,<3.0"]
build-backend = "poetry.core.masonry.api"

[project]
name = "pattern_service"
name = "aap-pattern-service"
version = "0.1.0"
description = "Pattern Service Django project"
description = ""
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"django-ansible-base==2025.5.8"
authors = [
{ name = "Ansible, Inc.", email = "Red Hat, Inc. <[email protected]>" },
]
requires-python = ">=3.11,<3.13"

[project.scripts]
aap-pattern-service-manage = 'pattern_service.manage:main'


# -------------------------------------
# Poetry: Metadata
# -------------------------------------
[tool.poetry]
requires-poetry = ">=2.0,<3.0"
packages = [{ include = "pattern_service", from = "src" }]


# -------------------------------------
# Poetry: Dependencies
# -------------------------------------
[tool.poetry.extras]
all = ["psycopg"]
dev = ["psycopg-binary"]

[tool.poetry.dependencies]
python = ">=3.11,<3.13"
django = ">=4.2,<4.3"
djangorestframework = "3.15.*"
drf-spectacular = ">=0.26.5,<0.27"
channels = { version = "4.0.*", extras = ["daphne"] }
psycopg-binary = { version = "*", optional = true }
django-filter = ">23.2,<24"
pydantic = ">=1.8.1,<1.11"
cryptography = ">=42,<43"
django-ansible-base = { git = "https://github.com/ansible/django-ansible-base.git", tag = "2025.5.8", extras = [
"channel-auth",
"rbac",
"resource-registry",
"jwt-consumer",
"rest-filters",
"feature-flags",
] }
jinja2 = ">=3.1.3,<3.2"
django-split-settings = "^1.2.0"
pexpect = "^4.9.0"
python-gnupg = "^0.5.2"
autobahn = { git = "https://github.com/crossbario/autobahn-python.git", rev = "v24.4.2" }
psycopg = "^3.1.17"
xxhash = "3.4.*"
pyjwt = { version = "2.7.*", extras = ["crypto"] }
ecdsa = "0.18.*"
validators = "^0.34.0"
django-flags = "^5.0.13"
insights-analytics-collector = "^0.3.2"
distro = "^1.9.0"
dispatcherd = { version = "v2025.05.19", extras = ["pg_notify"] }


[tool.poetry.group.test.dependencies]
pytest = "*"
pytest-env = "*"
pytest-django = "*"
pytest-asyncio = "*"
requests = { version = "*", python = "<4.0" }
pytest-cov = "^4.1.0"
pytest-lazy-fixture = "^0.6.3"
requests-mock = "*"
httpie = "^3.2.3"

[tool.poetry.group.lint.dependencies]
flake8 = "*"
isort = "*"
black = "*"
flake8-broken-line = { version = "*", python = "<4.0" }
flake8-string-format = "*"
# This is an experimental linter.
ruff = "*"
# The rull claims that the flake8 plugins listed below are re-implemented,
# These plugins will remain included until it's verified.
pep8-naming = "*"
flake8-bugbear = "*"
flake8-comprehensions = "*"
flake8-debugger = "*"
flake8-docstrings = "*"
flake8-eradicate = { version = "*", python = "<4.0" }
flake8-print = "*"

[tool.poetry.group.dev.dependencies]
ipython = "*"

# -------------------------------------
# Tools
# -------------------------------------

[tool.black]
line-length = 160
fast = true
skip-string-normalization = true
line-length = 79
target-version = ["py39", "py310"]

[tool.isort]
profile = "black"
force_single_line = true
line_length = 120
combine_as_imports = true
line_length = 79

[build-system]
requires = ["setuptools>=61", "wheel"]
build-backend = "setuptools.build_meta"
[tool.ruff]
line-length = 79

[tool.ruff.lint]
select = [
"E",
"F",
"D", # flake8-docstrings
"TID", # flake8-tidy-imports
]
extend-ignore = [
"D1", # Missing docstrings errors
]


[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]
"src/pattern_service/core/migrations/*" = ["E501"]
"tests/**/*.py" = [
"S101", # Asserts allowed in tests
"ARG", # Fixtures are not always used explicitly
"SLF001", # Call private methods in tests
"D", # Docstrings are not required in tests
]

[tool.setuptools]
packages = ["pattern_service"]
[tool.ruff.lint.flake8-tidy-imports]
ban-relative-imports = "parents"

[[tool.mypy.overrides]]
module = ["ansible_base.*", "rest_framework.*"]
ignore_missing_imports = true
[tool.ruff.lint.pydocstyle]
convention = "pep257"
12 changes: 12 additions & 0 deletions run_mypy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -eux

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )

rm -rf "${SCRIPT_DIR}/.install"
mkdir -p "${SCRIPT_DIR}/.install"
ln -s "${SCRIPT_DIR}/src/pattern_service" "${SCRIPT_DIR}/.install/pattern_service"
cd "${SCRIPT_DIR}/.install"
export MYPYPATH="${SCRIPT_DIR}/.install"
mypy -p pattern_service
rm -rf "${SCRIPT_DIR}/.install"
File renamed without changes.
File renamed without changes.
File renamed without changes.
9 changes: 9 additions & 0 deletions src/pattern_service/core/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.contrib import admin

from .models import Automation, ControllerLabel, Pattern, PatternInstance, Task

admin.site.register(Pattern)
admin.site.register(ControllerLabel)
admin.site.register(PatternInstance)
admin.site.register(Automation)
admin.site.register(Task)
2 changes: 1 addition & 1 deletion core/apps.py → src/pattern_service/core/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

class CoreConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "core"
name = "pattern_service.core"
File renamed without changes.
2 changes: 1 addition & 1 deletion core/models.py → src/pattern_service/core/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from ansible_base.lib.abstract_models import CommonModel
from ansible_base.lib.abstract_models import CommonModel # type: ignore
from django.db import models


Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from ansible_base.lib.serializers.common import CommonModelSerializer
from ansible_base.lib.serializers.common import CommonModelSerializer # type: ignore

from .models import Automation
from .models import ControllerLabel
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
from django.core.exceptions import ValidationError
from django.test import TestCase

from core.models import ControllerLabel
from core.models import Pattern
from core.models import Task
from pattern_service.core.models import ControllerLabel, Pattern, Task


class ModelTestCase(TestCase):
Expand All @@ -23,7 +21,9 @@ def test_task_invalid_status_choice(self):
task.full_clean() # triggers choice validation

def test_task_status_choices_valid_running_with_info(self):
task = Task.objects.create(status="Running", details={"info": "in progress"})
task = Task.objects.create(
status="Running", details={"info": "in progress"}
)
self.assertEqual(task.status, "Running")
self.assertEqual(task.details["info"], "in progress")

Expand Down
Loading
Loading