Use YAML infra service manifests
This commit is contained in:
parent
a6a86e6c29
commit
7f5896ec5c
9 changed files with 224 additions and 76 deletions
8
.project/kanban/done/DEW-0030.md
Normal file
8
.project/kanban/done/DEW-0030.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
id: DEW-0030
|
||||||
|
title: Use YAML infra service manifests
|
||||||
|
type: task
|
||||||
|
created: 2026-05-05T03:04:56.996343Z
|
||||||
|
---
|
||||||
|
|
||||||
|
Switch infra service manifests from metadata.toml to manifest.yaml and add a package-level JSON Schema for the service manifest contract.
|
||||||
|
|
@ -14,7 +14,7 @@ Path-like values in `dew.yaml` are resolved relative to `.project/dew.yaml`
|
||||||
unless they are absolute (for example, paths under `dew.vault`).
|
unless they are absolute (for example, paths under `dew.vault`).
|
||||||
|
|
||||||
Infrastructure services are not configured in `dew.yaml`; they are discovered
|
Infrastructure services are not configured in `dew.yaml`; they are discovered
|
||||||
from `.project/infrastructure/services/*/metadata.toml`.
|
from `.project/infrastructure/services/*/manifest.yaml`.
|
||||||
|
|
||||||
## Full Schema
|
## Full Schema
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ workflows.
|
||||||
.project/infrastructure/
|
.project/infrastructure/
|
||||||
└── services/
|
└── services/
|
||||||
└── postgres/
|
└── postgres/
|
||||||
├── metadata.toml
|
├── manifest.yaml
|
||||||
├── app_postgres.container
|
├── app_postgres.container
|
||||||
├── app_postgres.container.d/
|
├── app_postgres.container.d/
|
||||||
├── app_postgres.profiles.d/
|
├── app_postgres.profiles.d/
|
||||||
|
|
@ -25,26 +25,29 @@ workflows.
|
||||||
|
|
||||||
## Manifest
|
## Manifest
|
||||||
|
|
||||||
```toml
|
```yaml
|
||||||
[service]
|
service:
|
||||||
id = "postgres"
|
id: postgres
|
||||||
name = "PostgreSQL"
|
name: PostgreSQL
|
||||||
unit = "app_postgres.service"
|
unit: app_postgres.service
|
||||||
container_name = "app_postgres"
|
container_name: app_postgres
|
||||||
|
|
||||||
[runtime]
|
runtime:
|
||||||
type = "podman-quadlet"
|
type: podman-quadlet
|
||||||
|
|
||||||
[container]
|
container:
|
||||||
file = "app_postgres.container"
|
file: app_postgres.container
|
||||||
dropins_dir = "app_postgres.container.d"
|
dropins_dir: app_postgres.container.d
|
||||||
profiles_dir = "app_postgres.profiles.d"
|
profiles_dir: app_postgres.profiles.d
|
||||||
|
|
||||||
[schemas]
|
schemas:
|
||||||
configure = "configure.schema.json"
|
configure: configure.schema.json
|
||||||
init = "init.schema.json"
|
init: init.schema.json
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The package-level schema for this file is
|
||||||
|
`packages/infra/schemas/service-manifest.schema.json`.
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ class InfraRepository {
|
||||||
/// Absolute path to the service directory root.
|
/// Absolute path to the service directory root.
|
||||||
String get servicesDir => p.join(infraDir, 'services');
|
String get servicesDir => p.join(infraDir, 'services');
|
||||||
|
|
||||||
/// Finds all service manifests below `services/*/metadata.toml`.
|
/// Finds all service manifests below `services/*/manifest.yaml`.
|
||||||
Future<List<InfraServiceManifest>> list() async {
|
Future<List<InfraServiceManifest>> list() async {
|
||||||
final root = fs.directory(servicesDir);
|
final root = fs.directory(servicesDir);
|
||||||
if (!await root.exists()) return const [];
|
if (!await root.exists()) return const [];
|
||||||
|
|
@ -31,10 +31,10 @@ class InfraRepository {
|
||||||
final manifests = <InfraServiceManifest>[];
|
final manifests = <InfraServiceManifest>[];
|
||||||
await for (final entity in root.list()) {
|
await for (final entity in root.list()) {
|
||||||
if (entity is! Directory) continue;
|
if (entity is! Directory) continue;
|
||||||
final metadata = fs.file(p.join(entity.path, 'metadata.toml'));
|
final manifest = fs.file(p.join(entity.path, 'manifest.yaml'));
|
||||||
if (!await metadata.exists()) continue;
|
if (!await manifest.exists()) continue;
|
||||||
manifests.add(
|
manifests.add(
|
||||||
await loadFromMetadataPath(metadata.path, serviceDir: entity.path),
|
await loadFromManifestPath(manifest.path, serviceDir: entity.path),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
manifests.sort((a, b) => a.id.compareTo(b.id));
|
manifests.sort((a, b) => a.id.compareTo(b.id));
|
||||||
|
|
@ -52,25 +52,25 @@ class InfraRepository {
|
||||||
|
|
||||||
/// Loads a single service by command-line [id], returning null if absent.
|
/// Loads a single service by command-line [id], returning null if absent.
|
||||||
Future<InfraServiceManifest?> find(String id) async {
|
Future<InfraServiceManifest?> find(String id) async {
|
||||||
final metadataPath = p.join(servicesDir, id, 'metadata.toml');
|
final manifestPath = p.join(servicesDir, id, 'manifest.yaml');
|
||||||
final file = fs.file(metadataPath);
|
final file = fs.file(manifestPath);
|
||||||
if (!await file.exists()) return null;
|
if (!await file.exists()) return null;
|
||||||
return loadFromMetadataPath(
|
return loadFromManifestPath(
|
||||||
metadataPath,
|
manifestPath,
|
||||||
serviceDir: p.dirname(metadataPath),
|
serviceDir: p.dirname(manifestPath),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the manifest at [metadataPath].
|
/// Parses the manifest at [manifestPath].
|
||||||
Future<InfraServiceManifest> loadFromMetadataPath(
|
Future<InfraServiceManifest> loadFromManifestPath(
|
||||||
String metadataPath, {
|
String manifestPath, {
|
||||||
required String serviceDir,
|
required String serviceDir,
|
||||||
}) async {
|
}) async {
|
||||||
final file = fs.file(metadataPath);
|
final file = fs.file(manifestPath);
|
||||||
return InfraServiceManifest.parse(
|
return InfraServiceManifest.parse(
|
||||||
contents: await file.readAsString(),
|
contents: await file.readAsString(),
|
||||||
serviceDir: p.normalize(serviceDir),
|
serviceDir: p.normalize(serviceDir),
|
||||||
metadataPath: p.normalize(metadataPath),
|
manifestPath: p.normalize(manifestPath),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -126,16 +126,16 @@ class InfraValidator {
|
||||||
final dirId = p.basename(manifest.serviceDir);
|
final dirId = p.basename(manifest.serviceDir);
|
||||||
if (manifest.id != dirId) {
|
if (manifest.id != dirId) {
|
||||||
issue(
|
issue(
|
||||||
manifest.metadataPath,
|
manifest.manifestPath,
|
||||||
'service.id "${manifest.id}" must match directory "$dirId".',
|
'service.id "${manifest.id}" must match directory "$dirId".',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!manifest.unit.endsWith('.service')) {
|
if (!manifest.unit.endsWith('.service')) {
|
||||||
issue(manifest.metadataPath, 'service.unit must end with .service.');
|
issue(manifest.manifestPath, 'service.unit must end with .service.');
|
||||||
}
|
}
|
||||||
if (manifest.unit != manifest.expectedUnit) {
|
if (manifest.unit != manifest.expectedUnit) {
|
||||||
issue(
|
issue(
|
||||||
manifest.metadataPath,
|
manifest.manifestPath,
|
||||||
'service.unit "${manifest.unit}" must match container file unit '
|
'service.unit "${manifest.unit}" must match container file unit '
|
||||||
'"${manifest.expectedUnit}".',
|
'"${manifest.expectedUnit}".',
|
||||||
);
|
);
|
||||||
|
|
@ -211,7 +211,7 @@ class InfraValidator {
|
||||||
issues.add(
|
issues.add(
|
||||||
InfraValidationIssue(
|
InfraValidationIssue(
|
||||||
serviceId: manifest.id,
|
serviceId: manifest.id,
|
||||||
path: manifest.metadataPath,
|
path: manifest.manifestPath,
|
||||||
message: 'Missing $label path.',
|
message: 'Missing $label path.',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:toml/toml.dart';
|
import 'package:yaml/yaml.dart';
|
||||||
|
|
||||||
/// Supported infrastructure runtime backends.
|
/// Supported infrastructure runtime backends.
|
||||||
///
|
///
|
||||||
|
|
@ -26,7 +26,7 @@ enum InfraRuntimeKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Infrastructure service metadata loaded from `metadata.toml`.
|
/// Infrastructure service metadata loaded from `manifest.yaml`.
|
||||||
class InfraServiceManifest {
|
class InfraServiceManifest {
|
||||||
const InfraServiceManifest({
|
const InfraServiceManifest({
|
||||||
required this.id,
|
required this.id,
|
||||||
|
|
@ -35,7 +35,7 @@ class InfraServiceManifest {
|
||||||
required this.containerName,
|
required this.containerName,
|
||||||
required this.runtime,
|
required this.runtime,
|
||||||
required this.serviceDir,
|
required this.serviceDir,
|
||||||
required this.metadataPath,
|
required this.manifestPath,
|
||||||
required this.containerFile,
|
required this.containerFile,
|
||||||
this.dropinsDir,
|
this.dropinsDir,
|
||||||
this.profilesDir,
|
this.profilesDir,
|
||||||
|
|
@ -61,8 +61,8 @@ class InfraServiceManifest {
|
||||||
/// Absolute path to the service directory.
|
/// Absolute path to the service directory.
|
||||||
final String serviceDir;
|
final String serviceDir;
|
||||||
|
|
||||||
/// Absolute path to `metadata.toml`.
|
/// Absolute path to `manifest.yaml`.
|
||||||
final String metadataPath;
|
final String manifestPath;
|
||||||
|
|
||||||
/// Relative path to the primary Quadlet/container definition.
|
/// Relative path to the primary Quadlet/container definition.
|
||||||
final String containerFile;
|
final String containerFile;
|
||||||
|
|
@ -111,13 +111,13 @@ class InfraServiceManifest {
|
||||||
String get expectedUnit =>
|
String get expectedUnit =>
|
||||||
'${p.basenameWithoutExtension(containerFile)}.service';
|
'${p.basenameWithoutExtension(containerFile)}.service';
|
||||||
|
|
||||||
/// Decodes [contents] from TOML.
|
/// Decodes [contents] from YAML.
|
||||||
factory InfraServiceManifest.parse({
|
factory InfraServiceManifest.parse({
|
||||||
required String contents,
|
required String contents,
|
||||||
required String serviceDir,
|
required String serviceDir,
|
||||||
required String metadataPath,
|
required String manifestPath,
|
||||||
}) {
|
}) {
|
||||||
final map = TomlDocument.parse(contents).toMap();
|
final map = _asMap(loadYaml(contents));
|
||||||
final service = _section(map, 'service');
|
final service = _section(map, 'service');
|
||||||
final container = _section(map, 'container');
|
final container = _section(map, 'container');
|
||||||
final schemas = _optionalSection(map, 'schemas');
|
final schemas = _optionalSection(map, 'schemas');
|
||||||
|
|
@ -132,7 +132,7 @@ class InfraServiceManifest {
|
||||||
runtimeSection == null ? null : _optionalString(runtimeSection, 'type'),
|
runtimeSection == null ? null : _optionalString(runtimeSection, 'type'),
|
||||||
),
|
),
|
||||||
serviceDir: serviceDir,
|
serviceDir: serviceDir,
|
||||||
metadataPath: metadataPath,
|
manifestPath: manifestPath,
|
||||||
containerFile: _requiredString(container, 'file'),
|
containerFile: _requiredString(container, 'file'),
|
||||||
dropinsDir: _optionalString(container, 'dropins_dir'),
|
dropinsDir: _optionalString(container, 'dropins_dir'),
|
||||||
profilesDir: _optionalString(container, 'profiles_dir'),
|
profilesDir: _optionalString(container, 'profiles_dir'),
|
||||||
|
|
@ -150,7 +150,7 @@ class InfraServiceManifest {
|
||||||
'unit': unit,
|
'unit': unit,
|
||||||
'container_name': containerName,
|
'container_name': containerName,
|
||||||
'runtime': runtime.id,
|
'runtime': runtime.id,
|
||||||
'metadata': metadataPath,
|
'manifest': manifestPath,
|
||||||
'container_file': containerFilePath,
|
'container_file': containerFilePath,
|
||||||
'dropins_dir': dropinsDirPath,
|
'dropins_dir': dropinsDirPath,
|
||||||
'profiles_dir': profilesDirPath,
|
'profiles_dir': profilesDirPath,
|
||||||
|
|
@ -165,12 +165,29 @@ class InfraServiceManifest {
|
||||||
: p.normalize(p.join(serviceDir, value));
|
: p.normalize(p.join(serviceDir, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _asMap(dynamic value) {
|
||||||
|
if (value is YamlMap) {
|
||||||
|
return value.map((key, value) => MapEntry('$key', _asYamlValue(value)));
|
||||||
|
}
|
||||||
|
if (value is Map) {
|
||||||
|
return value.map((key, value) => MapEntry('$key', _asYamlValue(value)));
|
||||||
|
}
|
||||||
|
throw const FormatException('manifest.yaml must contain a YAML object.');
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic _asYamlValue(dynamic value) {
|
||||||
|
if (value is YamlMap || value is Map) return _asMap(value);
|
||||||
|
if (value is YamlList) return value.map(_asYamlValue).toList();
|
||||||
|
if (value is List) return value.map(_asYamlValue).toList();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, dynamic> _section(Map<String, dynamic> map, String key) {
|
Map<String, dynamic> _section(Map<String, dynamic> map, String key) {
|
||||||
final value = map[key];
|
final value = map[key];
|
||||||
if (value is Map) {
|
if (value is Map) {
|
||||||
return value.map((key, value) => MapEntry('$key', value));
|
return value.map((key, value) => MapEntry('$key', value));
|
||||||
}
|
}
|
||||||
throw FormatException('metadata.toml is missing [$key].');
|
throw FormatException('manifest.yaml is missing "$key".');
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic>? _optionalSection(Map<String, dynamic> map, String key) {
|
Map<String, dynamic>? _optionalSection(Map<String, dynamic> map, String key) {
|
||||||
|
|
@ -179,13 +196,13 @@ Map<String, dynamic>? _optionalSection(Map<String, dynamic> map, String key) {
|
||||||
if (value is Map) {
|
if (value is Map) {
|
||||||
return value.map((key, value) => MapEntry('$key', value));
|
return value.map((key, value) => MapEntry('$key', value));
|
||||||
}
|
}
|
||||||
throw FormatException('metadata.toml section [$key] must be a table.');
|
throw FormatException('manifest.yaml field "$key" must be an object.');
|
||||||
}
|
}
|
||||||
|
|
||||||
String _requiredString(Map<String, dynamic> map, String key) {
|
String _requiredString(Map<String, dynamic> map, String key) {
|
||||||
final value = _optionalString(map, key);
|
final value = _optionalString(map, key);
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
throw FormatException('metadata.toml is missing required string "$key".');
|
throw FormatException('manifest.yaml is missing required string "$key".');
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
@ -194,5 +211,5 @@ String? _optionalString(Map<String, dynamic> map, String key) {
|
||||||
final value = map[key];
|
final value = map[key];
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
if (value is String) return value;
|
if (value is String) return value;
|
||||||
throw FormatException('metadata.toml field "$key" must be a string.');
|
throw FormatException('manifest.yaml field "$key" must be a string.');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ dependencies:
|
||||||
json_schema: ^5.2.2
|
json_schema: ^5.2.2
|
||||||
path: ^1.9.0
|
path: ^1.9.0
|
||||||
podman: ^0.1.0
|
podman: ^0.1.0
|
||||||
toml: ^0.18.0
|
yaml: ^3.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
lints: ^6.0.0
|
lints: ^6.0.0
|
||||||
|
|
|
||||||
85
packages/infra/schemas/service-manifest.schema.json
Normal file
85
packages/infra/schemas/service-manifest.schema.json
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"$id": "https://artificery.dev/dew/schemas/infra/service-manifest.schema.json",
|
||||||
|
"title": "Dew Infrastructure Service Manifest",
|
||||||
|
"description": "Schema for .project/infrastructure/services/<service-id>/manifest.yaml.",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["service", "container", "schemas"],
|
||||||
|
"properties": {
|
||||||
|
"service": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["id", "name", "unit", "container_name"],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
"pattern": "^[a-z0-9][a-z0-9_.-]*$"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
"pattern": "^[^/]+\\.service$"
|
||||||
|
},
|
||||||
|
"container_name": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["type"],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["podman-quadlet"],
|
||||||
|
"default": "podman-quadlet"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"container": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["file", "dropins_dir", "profiles_dir"],
|
||||||
|
"properties": {
|
||||||
|
"file": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
"pattern": "^[^/].*\\.container$"
|
||||||
|
},
|
||||||
|
"dropins_dir": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"profiles_dir": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schemas": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["configure", "init"],
|
||||||
|
"properties": {
|
||||||
|
"configure": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
"pattern": "^[^/].*\\.json$"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
"pattern": "^[^/].*\\.json$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io' as io;
|
||||||
|
|
||||||
import 'package:dew_core/dew_core.dart';
|
import 'package:dew_core/dew_core.dart';
|
||||||
import 'package:dew_infra/dew_infra.dart';
|
import 'package:dew_infra/dew_infra.dart';
|
||||||
import 'package:file/memory.dart';
|
import 'package:file/memory.dart';
|
||||||
|
import 'package:json_schema/json_schema.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
|
@ -40,7 +44,7 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
group('InfraRepository', () {
|
group('InfraRepository', () {
|
||||||
test('discovers service metadata', () async {
|
test('discovers service manifests', () async {
|
||||||
final fs = MemoryFileSystem.test();
|
final fs = MemoryFileSystem.test();
|
||||||
_writeService(fs);
|
_writeService(fs);
|
||||||
|
|
||||||
|
|
@ -81,8 +85,8 @@ void main() {
|
||||||
await InfraRepository(
|
await InfraRepository(
|
||||||
infraDir: '/project/.project/infrastructure',
|
infraDir: '/project/.project/infrastructure',
|
||||||
fs: fs,
|
fs: fs,
|
||||||
).loadFromMetadataPath(
|
).loadFromManifestPath(
|
||||||
'/project/.project/infrastructure/services/postgres/metadata.toml',
|
'/project/.project/infrastructure/services/postgres/manifest.yaml',
|
||||||
serviceDir: '/project/.project/infrastructure/services/postgres',
|
serviceDir: '/project/.project/infrastructure/services/postgres',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -143,6 +147,18 @@ void main() {
|
||||||
expect(quadletSearchPath(InfraScope.system), '/etc/containers/systemd');
|
expect(quadletSearchPath(InfraScope.system), '/etc/containers/systemd');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('service-manifest.schema.json', () {
|
||||||
|
test('validates the manifest contract shape', () {
|
||||||
|
final schema = JsonSchema.create(
|
||||||
|
jsonDecode(_schemaFile().readAsStringSync()),
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = schema.validate(_manifestObject());
|
||||||
|
|
||||||
|
expect(result.isValid, isTrue, reason: result.errors.join('\n'));
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _writeService(
|
void _writeService(
|
||||||
|
|
@ -164,23 +180,50 @@ void _writeService(
|
||||||
fs
|
fs
|
||||||
.file('${serviceDir.path}/init.schema.json')
|
.file('${serviceDir.path}/init.schema.json')
|
||||||
.writeAsStringSync('{"type":"object"}');
|
.writeAsStringSync('{"type":"object"}');
|
||||||
fs.file('${serviceDir.path}/metadata.toml').writeAsStringSync('''
|
fs.file('${serviceDir.path}/manifest.yaml').writeAsStringSync('''
|
||||||
[service]
|
service:
|
||||||
id = "$serviceId"
|
id: $serviceId
|
||||||
name = "PostgreSQL"
|
name: PostgreSQL
|
||||||
unit = "$unit"
|
unit: $unit
|
||||||
container_name = "app_postgres"
|
container_name: app_postgres
|
||||||
|
|
||||||
[runtime]
|
runtime:
|
||||||
type = "podman-quadlet"
|
type: podman-quadlet
|
||||||
|
|
||||||
[container]
|
container:
|
||||||
file = "app_postgres.container"
|
file: app_postgres.container
|
||||||
dropins_dir = "app_postgres.container.d"
|
dropins_dir: app_postgres.container.d
|
||||||
profiles_dir = "app_postgres.profiles.d"
|
profiles_dir: app_postgres.profiles.d
|
||||||
|
|
||||||
[schemas]
|
schemas:
|
||||||
configure = "configure.schema.json"
|
configure: configure.schema.json
|
||||||
init = "init.schema.json"
|
init: init.schema.json
|
||||||
''');
|
''');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, Object?> _manifestObject() => {
|
||||||
|
'service': {
|
||||||
|
'id': 'postgres',
|
||||||
|
'name': 'PostgreSQL',
|
||||||
|
'unit': 'app_postgres.service',
|
||||||
|
'container_name': 'app_postgres',
|
||||||
|
},
|
||||||
|
'runtime': {'type': 'podman-quadlet'},
|
||||||
|
'container': {
|
||||||
|
'file': 'app_postgres.container',
|
||||||
|
'dropins_dir': 'app_postgres.container.d',
|
||||||
|
'profiles_dir': 'app_postgres.profiles.d',
|
||||||
|
},
|
||||||
|
'schemas': {'configure': 'configure.schema.json', 'init': 'init.schema.json'},
|
||||||
|
};
|
||||||
|
|
||||||
|
io.File _schemaFile() {
|
||||||
|
for (final path in [
|
||||||
|
'packages/infra/schemas/service-manifest.schema.json',
|
||||||
|
'schemas/service-manifest.schema.json',
|
||||||
|
]) {
|
||||||
|
final file = io.File(path);
|
||||||
|
if (file.existsSync()) return file;
|
||||||
|
}
|
||||||
|
throw StateError('Could not find service-manifest.schema.json.');
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -561,14 +561,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.17"
|
version: "0.6.17"
|
||||||
toml:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: toml
|
|
||||||
sha256: "35a35f782228656a2af31e8c73d1353cc4ef3d683fd68af1111b44631879c05e"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.18.0"
|
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue