mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-12-10 08:37:19 +01:00
Merge pull request #77 from dawnsystem/claude/fix-github-tests-019ptUhQs4TYnT2CyQNLV6bF
Claude/fix GitHub tests 019pt uh qs4 t yn t2 cy qnlv6b f
This commit is contained in:
commit
d1dcb7c678
6 changed files with 188 additions and 45 deletions
94
.github/workflows/ci.yml
vendored
94
.github/workflows/ci.yml
vendored
|
|
@ -74,6 +74,100 @@ jobs:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
- name: Check files
|
- name: Check files
|
||||||
uses: pre-commit/action@v3.0.1
|
uses: pre-commit/action@v3.0.1
|
||||||
|
verify-environment:
|
||||||
|
name: "Verify Environment & Services"
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
needs:
|
||||||
|
- pre-commit
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
- name: Verify Docker installation
|
||||||
|
run: |
|
||||||
|
if ! command -v docker &> /dev/null; then
|
||||||
|
echo "✗ Docker is not installed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ Docker is installed: $(docker --version)"
|
||||||
|
- name: Verify docker-compose installation
|
||||||
|
run: |
|
||||||
|
if ! command -v docker &> /dev/null || ! docker compose version &> /dev/null; then
|
||||||
|
echo "✗ docker-compose is not installed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ docker compose is installed: $(docker compose version)"
|
||||||
|
- name: Verify compose file exists
|
||||||
|
env:
|
||||||
|
COMPOSE_FILE: docker/compose/docker-compose.intellidocs.yml
|
||||||
|
run: |
|
||||||
|
if [ ! -f "$COMPOSE_FILE" ]; then
|
||||||
|
echo "✗ Compose file not found: $COMPOSE_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ Compose file found: $COMPOSE_FILE"
|
||||||
|
- name: Set up Python
|
||||||
|
id: setup-python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
|
- name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v6
|
||||||
|
with:
|
||||||
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
|
enable-cache: true
|
||||||
|
python-version: ${{ steps.setup-python.outputs.python-version }}
|
||||||
|
- name: Generate and verify requirements.txt
|
||||||
|
run: |
|
||||||
|
uv export --quiet --no-dev --all-extras --format requirements-txt --output-file requirements.txt
|
||||||
|
if [ ! -f "requirements.txt" ]; then
|
||||||
|
echo "✗ requirements.txt was not generated"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ requirements.txt generated successfully"
|
||||||
|
- name: Verify Python dependencies installation
|
||||||
|
run: |
|
||||||
|
# Verify that requirements.txt can be parsed
|
||||||
|
if ! python -c "
|
||||||
|
import sys
|
||||||
|
try:
|
||||||
|
with open('requirements.txt', 'r') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
print(f'✓ requirements.txt has {len(lines)} entries')
|
||||||
|
sys.exit(0)
|
||||||
|
except Exception as e:
|
||||||
|
print(f'✗ Error reading requirements.txt: {e}')
|
||||||
|
sys.exit(1)
|
||||||
|
"; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- name: Start Redis service
|
||||||
|
run: |
|
||||||
|
docker compose --file docker/compose/docker-compose.intellidocs.yml up -d broker
|
||||||
|
echo "Waiting for Redis to be ready..."
|
||||||
|
sleep 10
|
||||||
|
- name: Verify Redis is responding
|
||||||
|
run: |
|
||||||
|
MAX_RETRIES=30
|
||||||
|
RETRY_COUNT=0
|
||||||
|
|
||||||
|
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
|
||||||
|
if docker compose --file docker/compose/docker-compose.intellidocs.yml exec -T broker redis-cli ping &> /dev/null; then
|
||||||
|
echo "✓ Redis is responding"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
RETRY_COUNT=$((RETRY_COUNT + 1))
|
||||||
|
echo "Waiting for Redis... (attempt $RETRY_COUNT/$MAX_RETRIES)"
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "✗ Redis is not responding after $MAX_RETRIES attempts"
|
||||||
|
docker compose --file docker/compose/docker-compose.intellidocs.yml logs broker
|
||||||
|
exit 1
|
||||||
|
- name: Stop services
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
docker compose --file docker/compose/docker-compose.intellidocs.yml down -v
|
||||||
documentation:
|
documentation:
|
||||||
name: "Build & Deploy Documentation"
|
name: "Build & Deploy Documentation"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
|
||||||
8
.github/workflows/docker-intellidocs.yml
vendored
8
.github/workflows/docker-intellidocs.yml
vendored
|
|
@ -25,10 +25,10 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.12'
|
python-version: '3.12'
|
||||||
|
|
||||||
|
|
@ -104,7 +104,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
@ -242,7 +242,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
|
|
|
||||||
17
.github/workflows/pr-bot.yml
vendored
17
.github/workflows/pr-bot.yml
vendored
|
|
@ -83,11 +83,18 @@ jobs:
|
||||||
const pr = context.payload.pull_request;
|
const pr = context.payload.pull_request;
|
||||||
const user = pr.user.login;
|
const user = pr.user.login;
|
||||||
|
|
||||||
const { data: members } = await github.rest.orgs.listMembers({
|
// Try to get org members, but handle if this is not an org
|
||||||
org: 'paperless-ngx',
|
let memberLogins = [];
|
||||||
});
|
try {
|
||||||
|
const { data: members } = await github.rest.orgs.listMembers({
|
||||||
|
org: context.repo.owner,
|
||||||
|
});
|
||||||
|
memberLogins = members.map(m => m.login.toLowerCase());
|
||||||
|
} catch (error) {
|
||||||
|
// If not an organization, only skip for repo collaborators
|
||||||
|
core.info('Not an organization or unable to fetch members');
|
||||||
|
}
|
||||||
|
|
||||||
const memberLogins = members.map(m => m.login.toLowerCase());
|
|
||||||
if (memberLogins.includes(user.toLowerCase())) {
|
if (memberLogins.includes(user.toLowerCase())) {
|
||||||
core.info('Skipping comment: user is org member');
|
core.info('Skipping comment: user is org member');
|
||||||
return;
|
return;
|
||||||
|
|
@ -98,7 +105,7 @@ jobs:
|
||||||
"Thank you very much for submitting this PR to us!\n\n" +
|
"Thank you very much for submitting this PR to us!\n\n" +
|
||||||
"This is what will happen next:\n\n" +
|
"This is what will happen next:\n\n" +
|
||||||
"1. CI tests will run against your PR to ensure quality and consistency.\n" +
|
"1. CI tests will run against your PR to ensure quality and consistency.\n" +
|
||||||
"2. Next, human contributors from paperless-ngx review your changes.\n" +
|
"2. Next, human contributors will review your changes.\n" +
|
||||||
"3. Please address any issues that come up during the review as soon as you are able to.\n" +
|
"3. Please address any issues that come up during the review as soon as you are able to.\n" +
|
||||||
"4. If accepted, your pull request will be merged into the `dev` branch and changes there will be tested further.\n" +
|
"4. If accepted, your pull request will be merged into the `dev` branch and changes there will be tested further.\n" +
|
||||||
"5. Eventually, changes from you and other contributors will be merged into `main` and a new release will be made.\n\n" +
|
"5. Eventually, changes from you and other contributors will be merged into `main` and a new release will be made.\n\n" +
|
||||||
|
|
|
||||||
1
.github/workflows/translate-strings.yml
vendored
1
.github/workflows/translate-strings.yml
vendored
|
|
@ -63,6 +63,7 @@ jobs:
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
uses: stefanzweifel/git-auto-commit-action@v6
|
uses: stefanzweifel/git-auto-commit-action@v6
|
||||||
with:
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
file_pattern: 'src-ui/messages.xlf src/locale/en_US/LC_MESSAGES/django.po'
|
file_pattern: 'src-ui/messages.xlf src/locale/en_US/LC_MESSAGES/django.po'
|
||||||
commit_message: "Auto translate strings"
|
commit_message: "Auto translate strings"
|
||||||
commit_user_name: "GitHub Actions"
|
commit_user_name: "GitHub Actions"
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ import {
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { Subject } from 'rxjs'
|
import { Subject, of } from 'rxjs'
|
||||||
import { takeUntil } from 'rxjs/operators'
|
import { takeUntil, catchError } from 'rxjs/operators'
|
||||||
import {
|
import {
|
||||||
AISuggestion,
|
AISuggestion,
|
||||||
AISuggestionStatus,
|
AISuggestionStatus,
|
||||||
|
|
@ -134,10 +134,19 @@ export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy {
|
||||||
(s) => s.type === AISuggestionType.Tag
|
(s) => s.type === AISuggestionType.Tag
|
||||||
)
|
)
|
||||||
if (tagSuggestions.length > 0) {
|
if (tagSuggestions.length > 0) {
|
||||||
this.tagService.listAll().pipe(takeUntil(this.destroy$)).subscribe((tags) => {
|
this.tagService
|
||||||
this.tags = tags.results
|
.listAll()
|
||||||
this.updateSuggestionLabels()
|
.pipe(
|
||||||
})
|
takeUntil(this.destroy$),
|
||||||
|
catchError((error) => {
|
||||||
|
console.error('Failed to load tags:', error)
|
||||||
|
return of({ results: [] })
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe((tags) => {
|
||||||
|
this.tags = tags.results
|
||||||
|
this.updateSuggestionLabels()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load correspondents if needed
|
// Load correspondents if needed
|
||||||
|
|
@ -145,10 +154,19 @@ export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy {
|
||||||
(s) => s.type === AISuggestionType.Correspondent
|
(s) => s.type === AISuggestionType.Correspondent
|
||||||
)
|
)
|
||||||
if (correspondentSuggestions.length > 0) {
|
if (correspondentSuggestions.length > 0) {
|
||||||
this.correspondentService.listAll().pipe(takeUntil(this.destroy$)).subscribe((correspondents) => {
|
this.correspondentService
|
||||||
this.correspondents = correspondents.results
|
.listAll()
|
||||||
this.updateSuggestionLabels()
|
.pipe(
|
||||||
})
|
takeUntil(this.destroy$),
|
||||||
|
catchError((error) => {
|
||||||
|
console.error('Failed to load correspondents:', error)
|
||||||
|
return of({ results: [] })
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe((correspondents) => {
|
||||||
|
this.correspondents = correspondents.results
|
||||||
|
this.updateSuggestionLabels()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load document types if needed
|
// Load document types if needed
|
||||||
|
|
@ -156,10 +174,19 @@ export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy {
|
||||||
(s) => s.type === AISuggestionType.DocumentType
|
(s) => s.type === AISuggestionType.DocumentType
|
||||||
)
|
)
|
||||||
if (documentTypeSuggestions.length > 0) {
|
if (documentTypeSuggestions.length > 0) {
|
||||||
this.documentTypeService.listAll().pipe(takeUntil(this.destroy$)).subscribe((documentTypes) => {
|
this.documentTypeService
|
||||||
this.documentTypes = documentTypes.results
|
.listAll()
|
||||||
this.updateSuggestionLabels()
|
.pipe(
|
||||||
})
|
takeUntil(this.destroy$),
|
||||||
|
catchError((error) => {
|
||||||
|
console.error('Failed to load document types:', error)
|
||||||
|
return of({ results: [] })
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe((documentTypes) => {
|
||||||
|
this.documentTypes = documentTypes.results
|
||||||
|
this.updateSuggestionLabels()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load storage paths if needed
|
// Load storage paths if needed
|
||||||
|
|
@ -167,10 +194,19 @@ export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy {
|
||||||
(s) => s.type === AISuggestionType.StoragePath
|
(s) => s.type === AISuggestionType.StoragePath
|
||||||
)
|
)
|
||||||
if (storagePathSuggestions.length > 0) {
|
if (storagePathSuggestions.length > 0) {
|
||||||
this.storagePathService.listAll().pipe(takeUntil(this.destroy$)).subscribe((storagePaths) => {
|
this.storagePathService
|
||||||
this.storagePaths = storagePaths.results
|
.listAll()
|
||||||
this.updateSuggestionLabels()
|
.pipe(
|
||||||
})
|
takeUntil(this.destroy$),
|
||||||
|
catchError((error) => {
|
||||||
|
console.error('Failed to load storage paths:', error)
|
||||||
|
return of({ results: [] })
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe((storagePaths) => {
|
||||||
|
this.storagePaths = storagePaths.results
|
||||||
|
this.updateSuggestionLabels()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load custom fields if needed
|
// Load custom fields if needed
|
||||||
|
|
@ -178,10 +214,19 @@ export class AiSuggestionsPanelComponent implements OnChanges, OnDestroy {
|
||||||
(s) => s.type === AISuggestionType.CustomField
|
(s) => s.type === AISuggestionType.CustomField
|
||||||
)
|
)
|
||||||
if (customFieldSuggestions.length > 0) {
|
if (customFieldSuggestions.length > 0) {
|
||||||
this.customFieldsService.listAll().pipe(takeUntil(this.destroy$)).subscribe((customFields) => {
|
this.customFieldsService
|
||||||
this.customFields = customFields.results
|
.listAll()
|
||||||
this.updateSuggestionLabels()
|
.pipe(
|
||||||
})
|
takeUntil(this.destroy$),
|
||||||
|
catchError((error) => {
|
||||||
|
console.error('Failed to load custom fields:', error)
|
||||||
|
return of({ results: [] })
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe((customFields) => {
|
||||||
|
this.customFields = customFields.results
|
||||||
|
this.updateSuggestionLabels()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { HttpClient } from '@angular/common/http'
|
import { HttpClient } from '@angular/common/http'
|
||||||
import { Injectable, inject } from '@angular/core'
|
import { Injectable, inject } from '@angular/core'
|
||||||
import { BehaviorSubject, Observable, interval, Subscription } from 'rxjs'
|
import { BehaviorSubject, Observable, interval, Subscription, of } from 'rxjs'
|
||||||
import { catchError, map, startWith, switchMap } from 'rxjs/operators'
|
import { catchError, map, startWith, switchMap } from 'rxjs/operators'
|
||||||
import { AIStatus } from 'src/app/data/ai-status'
|
import { AIStatus } from 'src/app/data/ai-status'
|
||||||
import { environment } from 'src/environments/environment'
|
import { environment } from 'src/environments/environment'
|
||||||
|
|
@ -86,19 +86,15 @@ export class AIStatusService {
|
||||||
}),
|
}),
|
||||||
catchError((error) => {
|
catchError((error) => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
console.warn('Failed to fetch AI status, using mock data:', error)
|
console.warn('Failed to fetch AI status:', error)
|
||||||
// Return mock data if endpoint doesn't exist yet
|
// Return default status if endpoint fails
|
||||||
return [
|
return of({
|
||||||
{
|
active: false,
|
||||||
active: true,
|
processing: false,
|
||||||
processing: false,
|
documents_scanned_today: 0,
|
||||||
documents_scanned_today: 42,
|
suggestions_applied: 0,
|
||||||
suggestions_applied: 15,
|
pending_deletion_requests: 0,
|
||||||
pending_deletion_requests: 2,
|
})
|
||||||
last_scan: new Date().toISOString(),
|
|
||||||
version: '1.0.0',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue