Skip to content

Commit 76842ca

Browse files
committed
adding docker image and gh action for tests and docker image
1 parent c81e5d3 commit 76842ca

File tree

4 files changed

+283
-64
lines changed

4 files changed

+283
-64
lines changed

.github/workflows/test.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,8 @@ jobs:
4141
- name: Install project
4242
run: poetry install --no-interaction
4343

44-
- name: Run tests
45-
run: poetry run pytest tests/
44+
- name: Run tests (excluding Docker tests)
45+
run: poetry run pytest tests/ --ignore=tests/test_docker.py
46+
47+
- name: Run Docker tests
48+
run: poetry run pytest tests/test_docker.py -v

docs/development.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
# Development Guide
2+
3+
This guide covers development workflows, testing, Docker image management, and CI/CD for the `release-tool` project.
4+
5+
## Table of Contents
6+
- [Testing](#testing)
7+
- [GitHub Actions](#github-actions)
8+
- [Docker Image & Registry](#docker-image--registry)
9+
- [Local Development](#local-development)
10+
11+
## Testing
12+
13+
### Running Tests
14+
15+
The project uses pytest for testing. All tests can be run using Poetry:
16+
17+
```bash
18+
# Run all tests
19+
poetry run pytest
20+
21+
# Run with verbose output
22+
poetry run pytest -v
23+
24+
# Run specific test file
25+
poetry run pytest tests/test_models.py -v
26+
27+
# Run with coverage report
28+
poetry run pytest --cov=release_tool --cov-report=html
29+
```
30+
31+
### Test Categories
32+
33+
#### Unit Tests
34+
Standard unit tests covering:
35+
- Models and data structures (`test_models.py`)
36+
- Configuration management (`test_config.py`)
37+
- Database operations (`test_db.py`)
38+
- Git operations (`test_git_ops.py`)
39+
- Policies and business logic (`test_policies.py`)
40+
- Template rendering (`test_output_template.py`, `test_default_template.py`)
41+
- Ticket management (`test_query_tickets.py`, `test_partial_tickets.py`)
42+
- Publishing and syncing (`test_publish.py`, `test_sync.py`)
43+
44+
```bash
45+
# Run all unit tests except Docker tests
46+
poetry run pytest tests/ --ignore=tests/test_docker.py
47+
```
48+
49+
#### Docker Tests
50+
Docker integration tests verify:
51+
- Docker image builds successfully from the Dockerfile
52+
- `release-tool` executable is available in the image
53+
- `release-tool -h` returns a successful exit code
54+
- Docker image runs `release-tool` as its default command
55+
56+
```bash
57+
# Run Docker tests (requires Docker to be running)
58+
poetry run pytest tests/test_docker.py -v
59+
```
60+
61+
**Note**: Docker tests take longer (~20-30 seconds) as they build the actual Docker image.
62+
63+
## GitHub Actions
64+
65+
The project uses GitHub Actions for continuous integration and delivery. All workflows are defined in `.github/workflows/`.
66+
67+
### Test Workflow
68+
69+
File: `.github/workflows/test.yml`
70+
71+
**Triggers**:
72+
- Push to `main` branch
73+
- Pull requests to `main` branch
74+
75+
**What it does**:
76+
1. Sets up a test matrix for Python 3.10, 3.11, and 3.12
77+
2. Installs Poetry and project dependencies
78+
3. Runs all unit tests (excluding Docker tests)
79+
4. Runs Docker tests separately
80+
81+
**Running locally with act**:
82+
83+
[act](https://github.com/nektos/act) allows you to test GitHub Actions workflows locally:
84+
85+
```bash
86+
# Install act (macOS)
87+
brew install act
88+
89+
# List available workflows
90+
act -l
91+
92+
# Run the test workflow
93+
act -j test --container-architecture linux/amd64
94+
95+
# Run with a specific runner image
96+
act -j test --container-architecture linux/amd64 -P ubuntu-latest=catthehacker/ubuntu:act-latest
97+
98+
# Run and show verbose output
99+
act -j test --container-architecture linux/amd64 -v
100+
```
101+
102+
**Note**: On Apple M-series chips, use `--container-architecture linux/amd64` to avoid compatibility issues.
103+
104+
### Docker Publish Workflow
105+
106+
File: `.github/workflows/docker-publish.yml`
107+
108+
**Triggers**:
109+
- Push to `main` branch → updates `latest` and `main` tags
110+
- Push of version tags (`v*`) → creates versioned image tags (e.g., `v1.0.0`)
111+
- Pull requests → builds image for validation only (doesn't push)
112+
113+
**What it does**:
114+
1. Builds the Docker image from the Dockerfile
115+
2. Tags the image appropriately based on the trigger
116+
3. Pushes to GitHub Container Registry (GHCR)
117+
118+
**Testing locally**:
119+
120+
```bash
121+
# Test the Docker publish workflow locally (builds but doesn't push)
122+
act -j build-and-push --container-architecture linux/amd64
123+
```
124+
125+
## Docker Image & Registry
126+
127+
The `release-tool` is available as a Docker image stored in the GitHub Container Registry (GHCR). This allows other tools (like `release-bot`) or CI pipelines to use the tool without installing Python dependencies manually.
128+
129+
## Public Registry Access
130+
131+
The Docker image is published to:
132+
`ghcr.io/sequentech/release-tool`
133+
134+
### Enabling Public Access
135+
136+
To ensure the image is publicly pullable (so `release-bot` or other users can use it without authentication):
137+
138+
1. **Ensure the repository is public**: Go to the repository settings and verify that the repository visibility is set to "Public".
139+
2. **Configure organization package settings**: In the organization settings (Settings -> Packages), ensure that packages are configured to inherit the repository's visibility. This allows packages from public repositories to be automatically public.
140+
3. **Verify package visibility**: Navigate to the repository's **Packages** section (right sidebar on the main page), click on the `release-tool` package, and confirm it shows as "Public".
141+
142+
### Configuration
143+
The workflow uses the standard `GITHUB_TOKEN` to authenticate with GHCR. No additional secrets are required for the repository itself, provided "Read and write permissions" are enabled for workflows in the repository settings (Settings -> Actions -> General -> Workflow permissions).
144+
145+
## Local Development
146+
147+
### Building the Docker Image Locally
148+
You can build the image locally for testing purposes:
149+
150+
```bash
151+
docker build -t release-tool:local .
152+
```
153+
154+
### VS Code Task
155+
For convenience, a VS Code task is included. Open the Command Palette (`Cmd+Shift+P`) and run **Tasks: Run Build Task** (or select "Docker: Build Image") to build the image directly from the editor.
156+
157+
## Usage
158+
159+
### Pulling the image
160+
```bash
161+
docker pull ghcr.io/sequentech/release-tool:latest
162+
```
163+
164+
### Running the tool
165+
```bash
166+
docker run --rm -v $(pwd):/workspace -w /workspace ghcr.io/sequentech/release-tool release-tool --help
167+
```
168+
169+
### Extending the image (e.g., for Release Bot)
170+
```dockerfile
171+
FROM ghcr.io/sequentech/release-tool:latest
172+
173+
# Add your wrapper scripts
174+
COPY main.py /app/
175+
176+
ENTRYPOINT ["python", "/app/main.py"]
177+
```

docs/docker.md

Lines changed: 0 additions & 62 deletions
This file was deleted.

tests/test_docker.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
"""Tests for Docker image build and functionality."""
2+
3+
import subprocess
4+
import pytest
5+
6+
7+
@pytest.fixture(scope="module")
8+
def docker_image_name():
9+
"""Return the Docker image name to use for tests."""
10+
return "release-tool-test"
11+
12+
13+
@pytest.fixture(scope="module")
14+
def build_docker_image(docker_image_name):
15+
"""Build the Docker image before running tests."""
16+
# Build the Docker image
17+
result = subprocess.run(
18+
["docker", "build", "-t", docker_image_name, "."],
19+
capture_output=True,
20+
text=True,
21+
)
22+
23+
if result.returncode != 0:
24+
pytest.fail(f"Failed to build Docker image: {result.stderr}")
25+
26+
yield docker_image_name
27+
28+
# Cleanup: Remove the Docker image after tests
29+
subprocess.run(
30+
["docker", "rmi", "-f", docker_image_name],
31+
capture_output=True,
32+
)
33+
34+
35+
def test_docker_image_builds_successfully(docker_image_name):
36+
"""Test that the Docker image can be built successfully from the Dockerfile."""
37+
# Build the Docker image
38+
result = subprocess.run(
39+
["docker", "build", "-t", docker_image_name, "."],
40+
capture_output=True,
41+
text=True,
42+
)
43+
44+
assert result.returncode == 0, f"Docker build failed: {result.stderr}"
45+
# Modern Docker output may show success in stdout or stderr
46+
output = result.stdout + result.stderr
47+
assert (
48+
"Successfully built" in output
49+
or "Successfully tagged" in output
50+
or f"naming to docker.io/library/{docker_image_name}" in output
51+
)
52+
53+
54+
def test_release_tool_executable_available(build_docker_image):
55+
"""Test that the release-tool executable is available in the Docker image."""
56+
# Check if release-tool command exists in the image
57+
result = subprocess.run(
58+
["docker", "run", "--rm", build_docker_image, "which", "release-tool"],
59+
capture_output=True,
60+
text=True,
61+
)
62+
63+
assert result.returncode == 0, "release-tool executable not found in PATH"
64+
assert "release-tool" in result.stdout
65+
66+
67+
def test_release_tool_help_returns_success(build_docker_image):
68+
"""Test that running release-tool -h inside the Docker container returns a successful exit code."""
69+
# Run release-tool -h in the container
70+
result = subprocess.run(
71+
["docker", "run", "--rm", build_docker_image, "release-tool", "-h"],
72+
capture_output=True,
73+
text=True,
74+
)
75+
76+
assert result.returncode == 0, f"release-tool -h failed with exit code {result.returncode}"
77+
assert "Usage:" in result.stdout or "usage:" in result.stdout.lower()
78+
79+
80+
def test_docker_image_default_command(build_docker_image):
81+
"""Test that the Docker image runs release-tool as its default command."""
82+
# Run the container without specifying a command
83+
# This should execute the default CMD from the Dockerfile
84+
result = subprocess.run(
85+
["docker", "run", "--rm", build_docker_image],
86+
capture_output=True,
87+
text=True,
88+
timeout=5,
89+
)
90+
91+
# The default command should run release-tool, which without arguments
92+
# should either show help or an error message from release-tool
93+
# We're checking that it's release-tool that runs, not bash or another shell
94+
assert result.returncode in [0, 1, 2], f"Unexpected exit code: {result.returncode}"
95+
96+
# Verify the output contains release-tool-specific content
97+
output = result.stdout + result.stderr
98+
assert any(
99+
keyword in output.lower()
100+
for keyword in ["release-tool", "usage", "command"]
101+
), "Default command does not appear to be release-tool"

0 commit comments

Comments
 (0)