seperate package to generate schemas

This commit is contained in:
DSeeLP 2025-03-01 19:12:00 +01:00
parent f9d9639244
commit e9244e6be5
8 changed files with 199 additions and 113 deletions

View File

@ -30,7 +30,8 @@ jobs:
info=$(echo "$file" | sd '^result-([a-z0-9_-]+)\.([a-zA-Z]+)(?:-cross-([a-z0-9_-]+))?$' '$1\x1F$2\x1F$3') info=$(echo "$file" | sd '^result-([a-z0-9_-]+)\.([a-zA-Z]+)(?:-cross-([a-z0-9_-]+))?$' '$1\x1F$2\x1F$3')
IFS=$'\x1F' read -r hostArch name crossArch <<< "$info" IFS=$'\x1F' read -r hostArch name crossArch <<< "$info"
arch=${crossArch:-$hostArch} arch=${crossArch:-$hostArch}
containerArch=$(arch=$arch nix eval --raw --impure --expr '(import <nixpkgs> { system = builtins.getEnv "arch";}).go.GOARCH') containerArch=$(arch=$arch nix eval --raw --impure -I nixpkgs=flake:nixpkgs --expr '(import <nixpkgs> { system = builtins.getEnv "arch";}).go.GOARCH')
echo "Processed image for $containerArch"
mv $file images/image-$containerArch.tar.gz mv $file images/image-$containerArch.tar.gz
done done
- name: Upload artifact - name: Upload artifact
@ -44,16 +45,16 @@ jobs:
run: podman login git.dirksys.ovh --username $USERNAME --password $PASSWORD run: podman login git.dirksys.ovh --username $USERNAME --password $PASSWORD
env: env:
USERNAME: ${{ github.actor }} USERNAME: ${{ github.actor }}
PASSWORD: ${{ secrets.GITHUB_TOKEN }} PASSWORD: ${{ secrets.FORGEJO_REGISTRY_TOKEN }}
- name: Push docker images - name: Push docker images
if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch' if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch'
shell: bash shell: bash
run: | run: |
manifest_id=$(podman manifest create git.dirksys.ovh/dirk/bankserver:latest) manifest_id=$(podman manifest create git.dirksys.ovh/dirk/bankserver:latest)
sleep 1 sleep 1
for file in images/*; do for file in $(ls images); do
echo "Loading $file" echo "Loading images/$file"
podman image load < $file podman image load < images/$file
image_id=$(podman image ls --format '{{.ID}}' | head -n 2 | tail -1) image_id=$(podman image ls --format '{{.ID}}' | head -n 2 | tail -1)
architecture=$(podman image inspect $image_id | jq -r ".[0].Architecture") architecture=$(podman image inspect $image_id | jq -r ".[0].Architecture")
tag=$(podman image ls --format "{{.Tag}}" | head -n 2 | tail -1) tag=$(podman image ls --format "{{.Tag}}" | head -n 2 | tail -1)
@ -63,7 +64,8 @@ jobs:
podman manifest add $manifest_id $image_id podman manifest add $manifest_id $image_id
done done
echo "Pushing manifest" echo "Pushing manifest"
podman manifest push git.dirksys.ovh/dirk/bankserver:latest podman login --get-login git.dirksys.ovh
podman --log-level debug manifest push git.dirksys.ovh/dirk/bankserver:latest
- name: Notify server - name: Notify server
if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch' if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch'
env: env:

View File

@ -7,6 +7,10 @@ edition = "2024"
default = ["schemas"] default = ["schemas"]
schemas = ["dep:schemars"] schemas = ["dep:schemars"]
[[bin]]
name = "generate-schemas"
features = ["schemas"]
[dependencies] [dependencies]
axum = "0.8" axum = "0.8"
chrono = { version = "0.4.40", features = ["serde"] } chrono = { version = "0.4.40", features = ["serde"] }

View File

@ -39,7 +39,13 @@
overlay = overlay =
pkgs: pkgs:
let let
rustVersion = pkgs.pkgsBuildHost.rust-bin.stable.latest.minimal; rustVersion = pkgs.pkgsBuildHost.rust-bin.fromRustupToolchain {
channel = "stable";
targets = [
pkgs.pkgsBuildHost.targetPlatform.rust.rustcTarget
pkgs.pkgsBuildHost.buildPlatform.rust.rustcTarget
];
};
rustPlatform = pkgs.makeRustPlatform { rustPlatform = pkgs.makeRustPlatform {
cargo = rustVersion; cargo = rustVersion;
rustc = rustVersion; rustc = rustVersion;

0
out.txt Normal file
View File

View File

@ -5,21 +5,72 @@
redocly, redocly,
yq-go, yq-go,
targetPlatform, targetPlatform,
pkgsBuildBuild,
rev ? "dirty", rev ? "dirty",
}: }:
let
src = lib.fileset.toSource {
root = ./.;
fileset =
with lib.fileset;
intersection (gitTracked ./.) (unions [
(fileFilter (
file:
file.type == "directory"
|| file.hasExt "rs"
|| file.hasExt "toml"
|| file.name == "Cargo.lock"
|| file.hasExt "sql"
|| file.name == "openapi-def.yaml"
|| file.hasExt "html"
) ./.)
]);
};
cargoDeps = rustPlatform.importCargoLock {
lockFile = ./Cargo.lock;
outputHashes = {
"dbmigrator-0.4.4-alpha" = "sha256-Nwxw74IyZeZ9dODb+aneQmuQe0grO+g45B3zv1XaihE=";
};
};
schemas = pkgsBuildBuild.stdenv.mkDerivation {
pname = "bankingserver_schemas";
version = "unstable-${rev}";
inherit src cargoDeps;
cargoBuildType = "debug";
# "CARGO_TARGET_${stdenv.hostPlatform.rust.cargoEnvVarTarget}_LINKER" =
# "${stdenv.cc}/bin/${stdenv.cc.targetPrefix}cc";
nativeBuildInputs = [
rustPlatform.cargoSetupHook
rustPlatform.rust.cargo
];
buildPhase = ''
touch openapi.json
cargo build --bin generate-schemas
target/debug/generate-schemas
runHook postBuild
'';
installPhase = ''
mkdir -p $out/share/bankserver
cp -r schemas $out/share/bankserver/schemas
'';
doCheck = false;
};
in
rustPlatform.buildRustPackage { rustPlatform.buildRustPackage {
pname = "bankingserver"; pname = "bankingserver";
version = "unstable-${rev}"; version = "unstable-${rev}";
inherit src cargoDeps;
src = lib.cleanSource ./.;
nativeBuildInputs = [ nativeBuildInputs = [
schemas
redocly redocly
yq-go yq-go
]; ];
preBuild = '' preBuild = ''
cargo test --features schemas --target ${stdenv.buildPlatform.rust.rustcTarget} echo "Schemas: ${schemas}"
cp -r ${schemas}/share/bankserver/schemas schemas
yq eval-all -n 'load("openapi-def.yaml") *n load("schemas/schemas.json")' > openapi-temp.yaml yq eval-all -n 'load("openapi-def.yaml") *n load("schemas/schemas.json")' > openapi-temp.yaml
redocly bundle openapi-temp.yaml -o openapi.json redocly bundle openapi-temp.yaml -o openapi.json
@ -27,11 +78,7 @@ rustPlatform.buildRustPackage {
buildType = "debug"; buildType = "debug";
useFetchCargoVendor = false; cargoBuildFlags = "--bin bankserver";
cargoLock.lockFile = ./Cargo.lock;
cargoLock.outputHashes = {
"dbmigrator-0.4.4-alpha" = "sha256-Nwxw74IyZeZ9dODb+aneQmuQe0grO+g45B3zv1XaihE=";
};
CARGO_BUILD_TARGET = targetPlatform.config; CARGO_BUILD_TARGET = targetPlatform.config;

View File

@ -0,0 +1,94 @@
use schemars::{
SchemaGenerator,
generate::{Contract, SchemaSettings},
transform::{Transform, transform_subschemas},
};
use std::{
collections::{HashMap, HashSet},
path::PathBuf,
};
fn main() {
let directory = PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/schemas"));
std::fs::create_dir_all(&directory).unwrap();
let mut settings = SchemaSettings::draft2020_12();
settings.definitions_path = "".to_owned();
let mut request_generator = SchemaGenerator::new(settings.clone());
settings.contract = Contract::Serialize;
let mut response_generator = SchemaGenerator::new(settings);
let mut request_schemas = HashMap::new();
let mut response_schemas = HashMap::new();
struct RefTransform;
impl schemars::transform::Transform for RefTransform {
fn transform(&mut self, schema: &mut schemars::Schema) {
if schema
.get("$ref")
.map(|value| value.as_str().map(|v| v.starts_with("#/")))
.flatten()
.unwrap_or(false)
{
if let Some(value) = schema.remove("$ref") {
let mut value = value
.as_str()
.unwrap()
.strip_prefix("#/")
.unwrap()
.to_owned();
value.push_str(".json");
schema.insert("$ref".into(), value.into());
}
}
transform_subschemas(self, schema);
}
}
bankserver::api::schemas().generate(
&mut request_generator,
&mut response_generator,
&mut request_schemas,
&mut response_schemas,
);
let mut request_defs = request_generator.take_definitions();
let mut response_defs = response_generator.take_definitions();
let request_keys: HashSet<String> = request_defs.keys().cloned().collect();
let response_keys: HashSet<String> = response_defs.keys().cloned().collect();
let mut schemas = HashMap::new();
for key in request_keys.union(&response_keys) {
let mut path = directory.join(key);
path.set_extension("json");
let schema = match (request_defs.remove(key), response_defs.remove(key)) {
(None, Some(schema)) | (Some(schema), None) => schema,
(Some(request_schema), Some(response_schema)) => {
if request_schema != response_schema {
panic!("Diverging schema for {key}");
}
request_schema
}
_ => continue,
};
let mut schema: schemars::Schema = schema.try_into().unwrap();
RefTransform.transform(&mut schema);
schemas.insert(
key,
serde_json::json!({"$ref": format!("schemas/{key}.json")}),
);
std::fs::write(
path,
serde_json::to_string_pretty(schema.as_value()).unwrap(),
)
.unwrap();
}
std::fs::write(
directory.join("schemas.json"),
serde_json::to_string_pretty(&serde_json::json!({"components": {"schemas": schemas}}))
.unwrap(),
)
.unwrap();
}

View File

@ -6,101 +6,4 @@ pub use config::Config;
pub use db::setup_db; pub use db::setup_db;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {}
#[cfg(feature = "schemas")]
#[test]
fn generate_schemas() {
use schemars::{
SchemaGenerator,
generate::{Contract, SchemaSettings},
transform::{Transform, transform_subschemas},
};
use std::{
collections::{HashMap, HashSet},
path::PathBuf,
};
let directory = PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/schemas"));
std::fs::create_dir_all(&directory).unwrap();
let mut settings = SchemaSettings::draft2020_12();
settings.definitions_path = "".to_owned();
let mut request_generator = SchemaGenerator::new(settings.clone());
settings.contract = Contract::Serialize;
let mut response_generator = SchemaGenerator::new(settings);
let mut request_schemas = HashMap::new();
let mut response_schemas = HashMap::new();
struct RefTransform;
impl schemars::transform::Transform for RefTransform {
fn transform(&mut self, schema: &mut schemars::Schema) {
if schema
.get("$ref")
.map(|value| value.as_str().map(|v| v.starts_with("#/")))
.flatten()
.unwrap_or(false)
{
if let Some(value) = schema.remove("$ref") {
let mut value = value
.as_str()
.unwrap()
.strip_prefix("#/")
.unwrap()
.to_owned();
value.push_str(".json");
schema.insert("$ref".into(), value.into());
}
}
transform_subschemas(self, schema);
}
}
crate::api::schemas().generate(
&mut request_generator,
&mut response_generator,
&mut request_schemas,
&mut response_schemas,
);
let mut request_defs = request_generator.take_definitions();
let mut response_defs = response_generator.take_definitions();
let request_keys: HashSet<String> = request_defs.keys().cloned().collect();
let response_keys: HashSet<String> = response_defs.keys().cloned().collect();
let mut schemas = HashMap::new();
for key in request_keys.union(&response_keys) {
let mut path = directory.join(key);
path.set_extension("json");
let schema = match (request_defs.remove(key), response_defs.remove(key)) {
(None, Some(schema)) | (Some(schema), None) => schema,
(Some(request_schema), Some(response_schema)) => {
if request_schema != response_schema {
panic!("Diverging schema for {key}");
}
request_schema
}
_ => continue,
};
let mut schema: schemars::Schema = schema.try_into().unwrap();
RefTransform.transform(&mut schema);
schemas.insert(
key,
serde_json::json!({"$ref": format!("schemas/{key}.json")}),
);
std::fs::write(
path,
serde_json::to_string_pretty(schema.as_value()).unwrap(),
)
.unwrap();
}
std::fs::write(
directory.join("schemas.json"),
serde_json::to_string_pretty(&serde_json::json!({"components": {"schemas": schemas}}))
.unwrap(),
)
.unwrap();
}
}

30
test.sh Normal file
View File

@ -0,0 +1,30 @@
set -euxo pipefail
mkdir images
for file in result-*dockerImage*; do
if [ ! -f "$file" ]; then
continue
fi
info=$(echo "$file" | sd '^result-([a-z0-9_-]+)\.([a-zA-Z]+)(?:-cross-([a-z0-9_-]+))?$' '$1\x1F$2\x1F$3')
IFS=$'\x1F' read -r hostArch name crossArch <<< "$info"
arch=${crossArch:-$hostArch}
containerArch=$(arch=$arch nix eval --raw --impure -I nixpkgs=flake:nixpkgs --expr '(import <nixpkgs> { system = builtins.getEnv "arch";}).go.GOARCH')
echo "Processed image for $containerArch"
cp $file images/image-$containerArch.tar.gz
done
manifest_id=$(podman manifest create git.dirksys.ovh/dirk/bankserver:latest)
sleep 1
for file in $(ls images); do
echo "Loading images/$file"
podman image load < images/$file
image_id=$(podman image ls --format '{{.ID}}' | head -n 2 | tail -1)
architecture=$(podman image inspect $image_id | jq -r ".[0].Architecture")
tag=$(podman image ls --format "{{.Tag}}" | head -n 2 | tail -1)
tag="git.dirksys.ovh/dirk/bankserver:$tag-$architecture"
podman image untag $image_id
echo "Adding $architecture image to manifest"
podman manifest add $manifest_id $image_id
done
echo "Pushing manifest"
podman --log-level=debug manifest push git.dirksys.ovh/dirk/bankserver:latest > out.txt