Skip to content
Merged
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
23 changes: 17 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Reset SPM cache
run: swift package purge-cache && swift package reset && swift package update

- name: Build
run: swift build --build-tests
run: swift build --build-tests --enable-code-coverage

- name: Test
run: swift test --enable-code-coverage
timeout-minutes: 15
run: swift test --skip-build --enable-code-coverage --skip LDAPIntegrationTests

- name: Generate Coverage Report
run: |
Expand All @@ -39,19 +43,23 @@ jobs:

build-and-test-macos:
name: macOS (Swift 6.2)
runs-on: macos-15
runs-on: macos-26
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_16.3.app || sudo xcode-select -s /Applications/Xcode_16.2.app || true
run: sudo xcode-select -s /Applications/Xcode_26.2.app || sudo xcode-select -s /Applications/Xcode_26.app || true

- name: Reset SPM cache
run: swift package purge-cache && swift package reset && swift package update

- name: Build
run: swift build --build-tests
run: swift build --build-tests --enable-code-coverage

- name: Test
run: swift test --enable-code-coverage
timeout-minutes: 30
run: swift test --skip-build --enable-code-coverage --skip LDAPIntegrationTests

lint:
name: Swift Lint
Expand All @@ -62,6 +70,9 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Reset SPM cache
run: swift package purge-cache && swift package reset && swift package update

- name: Build (strict concurrency check)
run: swift build 2>&1 | tee build-output.txt

Expand Down
32 changes: 27 additions & 5 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,22 @@ permissions:
security-events: write

jobs:
analyze:
analyze-swift:
name: Analyze (Swift)
runs-on: macos-15
runs-on: macos-26

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_16.3.app || sudo xcode-select -s /Applications/Xcode_16.2.app || true
run: sudo xcode-select -s /Applications/Xcode_26.2.app || sudo xcode-select -s /Applications/Xcode_26.app || true

- name: Reset SPM cache
run: swift package purge-cache && swift package reset && swift package update

- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: swift
build-mode: manual
Expand All @@ -38,6 +41,25 @@ jobs:
run: swift build

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4
with:
category: "/language:swift"

analyze-actions:
name: Analyze (Actions)
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: actions
build-mode: none

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:actions"
30 changes: 22 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,35 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Reset SPM cache
run: swift package purge-cache && swift package reset && swift package update

- name: Build
run: swift build --build-tests
run: swift build --build-tests --enable-code-coverage

- name: Test
run: swift test --enable-code-coverage
timeout-minutes: 15
run: swift test --skip-build --enable-code-coverage --skip LDAPIntegrationTests

test-macos:
name: Test (macOS)
runs-on: macos-15
runs-on: macos-26
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_16.3.app || sudo xcode-select -s /Applications/Xcode_16.2.app || true
run: sudo xcode-select -s /Applications/Xcode_26.2.app || sudo xcode-select -s /Applications/Xcode_26.app || true

- name: Reset SPM cache
run: swift package purge-cache && swift package reset && swift package update

- name: Build
run: swift build --build-tests
run: swift build --build-tests --enable-code-coverage

- name: Test
run: swift test --enable-code-coverage
timeout-minutes: 30
run: swift test --skip-build --enable-code-coverage --skip LDAPIntegrationTests

# ------------------------------------------------------------------
# 2. Build release binaries
Expand All @@ -72,6 +80,9 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Reset SPM cache
run: swift package purge-cache && swift package reset && swift package update

- name: Build release binary
run: swift build -c release --static-swift-stdlib

Expand All @@ -92,13 +103,16 @@ jobs:
build-macos:
name: Build macOS (arm64)
needs: test-macos
runs-on: macos-15
runs-on: macos-26
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_16.3.app || sudo xcode-select -s /Applications/Xcode_16.2.app || true
run: sudo xcode-select -s /Applications/Xcode_26.2.app || sudo xcode-select -s /Applications/Xcode_26.app || true

- name: Reset SPM cache
run: swift package purge-cache && swift package reset && swift package update

- name: Build release binary
run: swift build -c release
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 6.0
// swift-tools-version: 6.2

// SPDX-License-Identifier: (see LICENSE)
// Mayam — Swift Package Manager Manifest
Expand Down
15 changes: 15 additions & 0 deletions Sources/MayamCore/Storage/BackupManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,21 @@ public actor BackupManager {
private func performLocalBackup(
to destinationPath: String
) async throws -> (objectCount: Int, sizeBytes: Int64) {
try BackupManager.copyArchive(
from: archivePath,
to: destinationPath
)
}

/// Copies `.dcm` files from the archive to a timestamped backup directory.
///
/// This is a `nonisolated` synchronous helper so that
/// `NSDirectoryEnumerator` iteration (which is unavailable from async
/// contexts in Swift 6.2) can be used directly.
private nonisolated static func copyArchive(
from archivePath: String,
to destinationPath: String
) throws -> (objectCount: Int, sizeBytes: Int64) {
let fm = FileManager.default

// Validate destination
Expand Down
25 changes: 20 additions & 5 deletions Sources/MayamCore/Storage/IntegrityScanner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,9 @@ public actor IntegrityScanner {

logger.info("Integrity scan: Starting full archive scan at '\(archivePath)'")

let fm = FileManager.default
guard let enumerator = fm.enumerator(atPath: archivePath) else {
throw IntegrityScanError.archiveNotAccessible(path: archivePath)
}
let dcmPaths = try IntegrityScanner.collectDCMPaths(at: archivePath)

for case let relativePath as String in enumerator where relativePath.hasSuffix(".dcm") {
for relativePath in dcmPaths {
result.scannedCount += 1
let absolutePath = archivePath + "/" + relativePath

Expand All @@ -187,6 +184,7 @@ public actor IntegrityScanner {
}

// Read file and compute checksum
let fm = FileManager.default
guard let fileData = fm.contents(atPath: absolutePath) else {
result.errorCount += 1
result.violations.append(IntegrityViolation(
Expand Down Expand Up @@ -225,6 +223,23 @@ public actor IntegrityScanner {
return result
}

/// Collects `.dcm` file paths from the archive directory.
///
/// This is a `nonisolated` synchronous helper so that
/// `NSDirectoryEnumerator` iteration (which is unavailable from async
/// contexts in Swift 6.2) can be used directly.
private nonisolated static func collectDCMPaths(at archivePath: String) throws -> [String] {
let fm = FileManager.default
guard let enumerator = fm.enumerator(atPath: archivePath) else {
throw IntegrityScanError.archiveNotAccessible(path: archivePath)
}
var paths: [String] = []
for case let relativePath as String in enumerator where relativePath.hasSuffix(".dcm") {
paths.append(relativePath)
}
return paths
}

/// Returns the history of scan results.
public func getScanHistory() -> [ScanResult] {
scanHistory
Expand Down
25 changes: 16 additions & 9 deletions Sources/MayamWeb/Admin/Handlers/AdminStorageHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,7 @@ public actor AdminStorageHandler {
/// - Returns: An ``IntegrityCheckResult`` with the count of examined files.
public func runIntegrityCheck(archivePath: String) async -> IntegrityCheckResult {
let startedAt = Date()
let fileManager = FileManager.default
var checkedCount = 0

if let enumerator = fileManager.enumerator(atPath: archivePath) {
for case let filePath as String in enumerator
where filePath.hasSuffix(".dcm") {
checkedCount += 1
}
}
let checkedCount = AdminStorageHandler.countDCMFiles(at: archivePath)

return IntegrityCheckResult(
startedAt: startedAt,
Expand All @@ -83,6 +75,21 @@ public actor AdminStorageHandler {
)
}

/// Counts `.dcm` files under the given path.
///
/// This is a `nonisolated` synchronous helper so that
/// `NSDirectoryEnumerator` iteration (which is unavailable from async
/// contexts in Swift 6.2) can be used directly.
private nonisolated static func countDCMFiles(at archivePath: String) -> Int {
let fm = FileManager.default
guard let enumerator = fm.enumerator(atPath: archivePath) else { return 0 }
var count = 0
for case let filePath as String in enumerator where filePath.hasSuffix(".dcm") {
count += 1
}
return count
}

/// Returns the current HSM status including tier statistics.
///
/// - Parameter hsmConfig: The HSM configuration.
Expand Down
Loading