Application Context Implementation #4

Closed
kayomn wants to merge 93 commits from event-loop-dev into main
Owner

Work goals:

  • Multi-threaded asynchronous processing context (via AppContext).
  • Synchronous FileAccess interface that may be offloaded to another thread via AppContext.schedule.
  • First pass of custom archive / virtual disk format for accessing game data quickly (nicknamed "Oar" or Ona Archive).
  • A new tool for compiling files and directories into Oar archive files.
  • Platform-abstracted Path data structure for accessing the data archive and user directly over the underlying file-system in an opaque manner.

Implementation of GraphicsContext will come as a separate pull request at a later time. This workload is only focused on implementating the base of the application context for a JavaScript event loop-style work processing that leverages Zig's async-await primitives.

Work goals: - [x] Multi-threaded asynchronous processing context (via `AppContext`). - [x] Synchronous `FileAccess` interface that may be offloaded to another thread via `AppContext.schedule`. - [x] First pass of custom archive / virtual disk format for accessing game data quickly (nicknamed "Oar" or Ona Archive). - [ ] A new tool for compiling files and directories into Oar archive files. - [x] Platform-abstracted `Path` data structure for accessing the data archive and user directly over the underlying file-system in an opaque manner. Implementation of `GraphicsContext` will come as a separate pull request at a later time. This workload is only focused on implementating the base of the application context for a JavaScript event loop-style work processing that leverages Zig's async-await primitives.
kayomn added the
tools
label 2022-10-10 00:20:20 +02:00
kayomn self-assigned this 2022-10-10 00:20:20 +02:00
kayomn added 13 commits 2022-10-10 00:20:21 +02:00
continuous-integration/drone/push Build is passing Details
01d878f933
Remove hardcoded hash from VS Code launch config
continuous-integration/drone/push Build is passing Details
217d539ff7
Fix calling SDL_DestroySemaphore on mutex
continuous-integration/drone/push Build is passing Details
6094dac5f1
Temporary fix for SDL2 symbols not loading at runtime
continuous-integration/drone/push Build is passing Details
38211718e3
Hide away private event loop functions in implementation
continuous-integration/drone/push Build is passing Details
95f48b28c7
Fix compilation error when calling runGraphics
continuous-integration/drone/push Build is passing Details
fbd79b5b41
Fix panic when calling runGraphics in main
continuous-integration/drone/push Build is passing Details
d2f4c0afe1
Fix paths being created with trailing "/" always
continuous-integration/drone/push Build is failing Details
979c2a73f3
Remove global data in file system API
continuous-integration/drone/push Build is passing Details
1289e9634b
Rework file system API to support different backends
continuous-integration/drone/push Build is failing Details
c42885bf61
Add stubs for Tar-based file system logic
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
9ae6e8b4a7
Implement first pass of Oar archive reading mechanism
kayomn changed title from WIP: event-loop-dev to WIP: Application Context Implementation 2022-10-10 00:22:54 +02:00
kayomn added 1 commit 2022-10-10 11:15:56 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
84664b5962
Rename "App" to "AppContext"
Author
Owner

Sudden realization / consideration.

Rather than rolling a duplicate asynchronous interface for offloading synchronous logic, why not expose a single, generic asynchronous offloading function on AppContext.

Sudden realization / consideration. Rather than rolling a duplicate asynchronous interface for offloading synchronous logic, why not expose a single, generic asynchronous offloading function on `AppContext`.
kayomn added 1 commit 2022-10-10 18:46:32 +02:00
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
52f4657872
Replace duplicated async interface with generic function
Author
Owner

May be worth looking into the Quake Pak file format for inspiration on how to lay out the file format in an efficient manner?

May be worth looking into the [Quake Pak file format](https://quakewiki.org/wiki/.pak) for inspiration on how to lay out the file format in an efficient manner?
Author
Owner

Proposed archive format:

  • Header containing magic identifier and version number.
  • Version-specific header containing useful global metadata.
  • Rest of the file is a file entry followed by the raw data one after the other.
Proposed archive format: * Header containing magic identifier and version number. * Version-specific header containing useful global metadata. * Rest of the file is a file entry followed by the raw data one after the other.
kayomn added 1 commit 2022-10-11 02:03:07 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
e108486c17
Add data validation to Oar entries
Author
Owner

Further considerations have made me realize that the file format would be better served from a simplicity standpoint if entries each encoded their own header data.

This would allow partial upgrades of existing files should changes ever be made to the file spec, and keep the file format simple - as the logic flow for running through the whole archive would be.

file = open("file.oar")

while true:
  entry = read_entry(file)

  if !validate_entry(entry) error()

  file.seek(entry.file_size)
Further considerations have made me realize that the file format would be better served from a simplicity standpoint if entries each encoded their own header data. This would allow partial upgrades of existing files should changes ever be made to the file spec, and keep the file format simple - as the logic flow for running through the whole archive would be. ```py file = open("file.oar") while true: entry = read_entry(file) if !validate_entry(entry) error() file.seek(entry.file_size) ```
kayomn added 2 commits 2022-10-11 02:15:26 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
287a054d22
Tidy up Oar Entry default data
kayomn added 1 commit 2022-10-13 15:37:43 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
961e79200d
Implement Oar file reading logic in archive file system
kayomn added 1 commit 2022-10-14 02:13:31 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
4e5883f384
Add stubs for archive entry caching
kayomn added 1 commit 2022-10-14 18:10:35 +02:00
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is failing Details
53a369952e
Implement hash table lookup logic
kayomn added 2 commits 2022-10-15 02:25:25 +02:00
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is failing Details
26f342e518
Add entry caching to archive file systems
kayomn added 1 commit 2022-10-15 02:28:18 +02:00
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
01e6a7cb56
Fix compilation error in tests
kayomn added 1 commit 2022-10-15 12:38:28 +02:00
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
5f4e4cc811
Implement more hash table logic
kayomn added 1 commit 2022-10-15 14:14:09 +02:00
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2792f27473
Split archive-specific logic into own module
kayomn added 2 commits 2022-10-15 22:11:22 +02:00
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
449b56947e
Improve memory safety of Oar archive lookups
kayomn added 1 commit 2022-10-15 22:21:07 +02:00
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
3d3d0e488a
Fix incorrectly cleaning up data archive memory in app context
kayomn added 1 commit 2022-10-17 11:34:13 +02:00
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is failing Details
489ece4b7b
Refactor codebase
kayomn added 1 commit 2022-10-17 13:20:44 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
1891a420e8
Reorganize and refactor project tooling
kayomn added 2 commits 2022-10-17 13:29:36 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
87d7126632
Add missing comments to Ona module
kayomn added 1 commit 2022-10-17 13:44:40 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
d96364e04b
Merge I/O and memory logic in Ona module
kayomn added 1 commit 2022-10-17 15:01:01 +02:00
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is failing Details
c8992fec99
Add missing unit tests
kayomn added 2 commits 2022-10-17 15:04:54 +02:00
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
1997c38e97
Fix test build error in hash table
kayomn added 5 commits 2022-10-17 16:49:56 +02:00
kayomn added 1 commit 2022-10-19 01:02:29 +02:00
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
3a23f5feca
Change FileAccess wrapper functions to pass by value
kayomn added 1 commit 2022-10-19 01:05:56 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
6769ea92af
Simplify Oar API
kayomn added 2 commits 2022-10-19 15:16:12 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
1a28dc2404
Share Oar path type with file system
kayomn added 1 commit 2022-10-20 00:59:02 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
32bb049f73
Refactor SDL2 interface code
kayomn added 1 commit 2022-10-20 23:30:52 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
ef2c6c3a3c
Extract Ona IO writer into reusable "Function" type
kayomn added 2 commits 2022-10-23 00:21:14 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
b698f18c4d
Temporarily hardcode path to test binary
kayomn added 1 commit 2022-10-23 00:34:37 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
ccb96875b1
Add "Reader" IO closure type for reading resources
kayomn added 1 commit 2022-10-23 19:11:12 +02:00
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is failing Details
813df95e02
Tidy up codebase structure
kayomn added 3 commits 2022-10-23 19:16:17 +02:00
kayomn added 1 commit 2022-10-24 02:02:13 +02:00
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is failing Details
da7d9cfcc0
Add additional closures and uses
kayomn added 2 commits 2022-10-24 14:59:44 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
d578bb422e
Change Allocator to use interface style
kayomn added 1 commit 2022-10-24 15:01:33 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
e24868406c
Remove default build task from VS Code task configuration
kayomn added 2 commits 2022-10-25 01:40:19 +02:00
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is failing Details
90f503f7c0
More work on OAR packing utility
kayomn added 2 commits 2022-10-25 02:02:25 +02:00
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
8ab80f07b8
Add install step to tests in build script
kayomn added 1 commit 2022-10-25 14:20:05 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
bc5b69ac05
Add test install location to VS code debug config
kayomn added 1 commit 2022-10-25 18:33:07 +02:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
32b18e4ebf
Plan out archive API and use in Oar tool program
kayomn added 1 commit 2022-10-26 19:10:06 +02:00
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is failing Details
7599ce61f2
Initial work merging Oar into Ona itself
kayomn added 6 commits 2022-10-30 23:08:06 +01:00
kayomn added 2 commits 2022-11-01 16:08:16 +01:00
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is failing Details
e95c754d62
Refactor Oar implementation
kayomn added 1 commit 2022-11-01 16:11:29 +01:00
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is failing Details
1773a04e52
Update drone CI script
kayomn added 1 commit 2022-11-01 16:14:59 +01:00
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is failing Details
b9c41971ee
Add Musl to drone CI script
kayomn added 1 commit 2022-11-01 16:26:31 +01:00
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is failing Details
342d296bfd
Add libc-dev dependency in Drone CI script
kayomn added 1 commit 2022-11-01 17:07:20 +01:00
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is failing Details
a401de3bae
Add Alpine build-base dependency to Drone CI
kayomn added 1 commit 2022-11-01 17:09:11 +01:00
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/pr Build is failing Details
f8bfecc983
Add Alpine SDL2 to Drone CI dependencies
kayomn added 1 commit 2022-11-01 17:10:20 +01:00
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is failing Details
40f2e869ac
Fix incorrect Alpine SDL2 dependency being used in Drone CI
kayomn added 1 commit 2022-11-01 17:11:50 +01:00
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
595ad83cdd
Fix incorrect test binary path in Drone CI
kayomn added 4 commits 2022-11-02 11:04:44 +01:00
kayomn added 3 commits 2022-11-02 16:15:26 +01:00
kayomn added 1 commit 2022-11-02 18:45:25 +01:00
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
14b3921001
Add fixed stack-backed allocator implementation
kayomn added 2 commits 2022-11-03 16:53:41 +01:00
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
4bb86c41bc
Add more test coverage and clean up code
Author
Owner

Cutting implementation of Oar tool as part of this PR as it has gotten out of hand in size.

Will be resolved as a separate PR.

Cutting implementation of Oar tool as part of this PR as it has gotten out of hand in size. Will be resolved as a separate PR.
kayomn reviewed 2022-11-03 18:06:02 +01:00
kayomn left a comment
Author
Owner

Minor issues with missing / out-of-date comments but major issue with missing implementation of Oar archive file opening.

Minor issues with missing / out-of-date comments but major issue with missing implementation of Oar archive file opening.
src/core/io.zig Outdated
@ -0,0 +151,4 @@
///
/// Returns a sliced reference of the raw bytes in `pointer`.
///
/// **Note** that passing a slice will convert it to a byte slice.
Author
Owner

Comment needs clarify as it appears slightly misleading / ambiguous in its current wording.

Comment needs clarify as it appears slightly misleading / ambiguous in its current wording.
Author
Owner

On further thought, having the comment here to begin with only adds confusion.

While slices don't have as trivial of a memory layout as other pointer types, they're still language primitives and should reasonably be treated the same as other pointer kinds.

On further thought, having the comment here to begin with only adds confusion. While slices don't have as trivial of a memory layout as other pointer types, they're still language primitives and should reasonably be treated the same as other pointer kinds.
kayomn marked this conversation as resolved
src/core/io.zig Outdated
@ -0,0 +254,4 @@
}
///
/// Searches for the first instance of an `Element` equal to `needle` in `haystack`, returning its
Author
Owner

Worth mentioning that it performs linear time, making it O(n) time complexity?

Worth mentioning that it performs linear time, making it O(n) time complexity?
kayomn marked this conversation as resolved
src/core/io.zig Outdated
@ -0,0 +279,4 @@
}
///
/// Searches for the first instance of an `Element` sequence equal to the contents of `needle` in
Author
Owner

Worth mentioning that this uses linear search?

Worth mentioning that this uses linear search?
kayomn marked this conversation as resolved
@ -0,0 +1,60 @@
const std = @import("std");
const testing = @import("./testing.zig");
pub const IntFittingRange = std.math.IntFittingRange;
Author
Owner

Warrants TODO comment mentioning that this stdlib dependency should be removed in future.

Warrants `TODO` comment mentioning that this stdlib dependency should be removed in future.
kayomn marked this conversation as resolved
@ -0,0 +133,4 @@
///
/// Potential errors that may occur while trying to push one or more elements into a stack.
///
pub const PushError = io.MakeError;
Author
Owner

Seeing as FixedStack does not depend on any kind of dynamic allocation directly, does it make sense to make PushError a direct alias of io.MakeError?

Would it make more sense to use BufferOverflow instead of OutOfMemory?

Seeing as `FixedStack` does not depend on any kind of dynamic allocation directly, does it make sense to make `PushError` a direct alias of `io.MakeError`? Would it make more sense to use `BufferOverflow` instead of `OutOfMemory`?
kayomn marked this conversation as resolved
@ -0,0 +155,4 @@
(stack_address + stack.filled)) return buffer;
// TODO: Investigate ways of freeing if it is the last allocation.
return null;
Author
Owner

Reallocation could benefit from the same kind of last-alloc check optimization as deallocation.

May be worth clarifying that in the comment and code structure.

Reallocation could benefit from the same kind of last-alloc check optimization as deallocation. May be worth clarifying that in the comment and code structure.
kayomn marked this conversation as resolved
@ -0,0 +134,4 @@
/// Returns the current load factor of `self`, which is derived from the number of capacity
/// that has been filled.
///
pub fn loadFactor(self: Self) f32 {
Author
Owner

Since this function is only ever used to check if the load factor is at / beyond its maximum, would it make more sense to replace it with isMaxLoad(Self) bool or something like that which meets the requirements of it uses more precisely?

Since this function is only ever used to check if the load factor is at / beyond its maximum, would it make more sense to replace it with `isMaxLoad(Self) bool` or something like that which meets the requirements of it uses more precisely?
kayomn marked this conversation as resolved
@ -0,0 +141,4 @@
///
/// Searches for a value indexed with `key` in `self`.
///
/// The found value is returned or `null` if an key matching `key` failed to be found.
Author
Owner

Comment typo: if any key instead of if an key.

Comment typo: `if any key` instead of `if an key`.
kayomn marked this conversation as resolved
src/ona/main.zig Outdated
@ -0,0 +9,4 @@
return nosuspend await async sys.display(anyerror, run);
}
fn run(app: *sys.App, graphics: *sys.Graphics) anyerror!void {
Author
Owner

Missing documentation comment.

Missing documentation comment.
kayomn marked this conversation as resolved
src/ona/oar.zig Outdated
@ -0,0 +22,4 @@
///
///
///
pub const Entry = struct {
Author
Owner

Missing documentation comment.

Missing documentation comment.
kayomn marked this conversation as resolved
src/ona/oar.zig Outdated
@ -0,0 +29,4 @@
///
///
///
pub const FindError = error {
Author
Owner

Missing documentation comment.

Missing documentation comment.
kayomn marked this conversation as resolved
src/ona/oar.zig Outdated
@ -0,0 +37,4 @@
///
///
///
pub fn find(archive_file: *sys.ReadableFile, entry_path: sys.Path) FindError!Entry {
Author
Owner

Missing documentation comment.

Missing documentation comment.
kayomn marked this conversation as resolved
src/ona/oar.zig Outdated
@ -0,0 +80,4 @@
///
///
///
pub fn read(entry: Entry, archive_file: *sys.ReadableFile,
Author
Owner

Missing documentation comment.

Missing documentation comment.
kayomn marked this conversation as resolved
src/ona/oar.zig Outdated
@ -0,0 +91,4 @@
///
///
///
const Header = extern struct {
Author
Owner

Missing documentation comment.

Missing documentation comment.
kayomn marked this conversation as resolved
src/ona/oar.zig Outdated
@ -0,0 +108,4 @@
///
///
///
const revision_magic = 0;
Author
Owner

Missing documentation comment.

Missing documentation comment.
kayomn marked this conversation as resolved
src/ona/sys.zig Outdated
@ -0,0 +95,4 @@
///
///
///
pub const ReadableFile = opaque {
Author
Owner

Missing documentation comment.

Missing documentation comment.
kayomn marked this conversation as resolved
@ -0,0 +99,4 @@
///
///
///
pub fn close(readable_file: *ReadableFile) void {
Author
Owner

Missing documentation comment.

Missing documentation comment.
kayomn marked this conversation as resolved
src/ona/sys.zig Outdated
@ -0,0 +108,4 @@
///
///
///
pub fn read(readable_file: *ReadableFile, offset: u64, buffer: []u8) FileError!u64 {
Author
Owner

Missing documentation comment.

Missing documentation comment.
kayomn marked this conversation as resolved
src/ona/sys.zig Outdated
@ -0,0 +149,4 @@
///
///
///
pub fn rwOpsCast(readable_file: *ReadableFile) *ext.SDL_RWops {
Author
Owner

Missing documentation comment.

Missing documentation comment.
Author
Owner

Also, should this be public?

Also, should this be public?
kayomn marked this conversation as resolved
src/ona/sys.zig Outdated
@ -0,0 +156,4 @@
///
///
///
pub fn size(readable_file: *ReadableFile) FileError!u64 {
Author
Owner

Missing documentation comment.

Missing documentation comment.
kayomn marked this conversation as resolved
src/ona/sys.zig Outdated
@ -0,0 +208,4 @@
/// Returns a [ReadableFile] reference that provides access to the file referenced by `path`or a
/// [OpenError] if it failed.
///
pub fn openRead(file_system: *const FileSystem, path: Path) OpenError!*ReadableFile {
Author
Owner

Out of date documentation comment.

Out of date documentation comment.
kayomn marked this conversation as resolved
src/ona/sys.zig Outdated
@ -0,0 +214,4 @@
const entry = oar.Entry.find(archive_file, path) catch return error.FileNotFound;
_ = entry;
// TODO: Alloc file context.
Author
Owner

This should be resolved as part of the PR.

This should be resolved as part of the PR.
kayomn marked this conversation as resolved
src/ona/sys.zig Outdated
@ -0,0 +348,4 @@
///
///
///
pub fn compare(this: Path, that: Path) isize {
Author
Owner

Missing documentation comment.

Missing documentation comment.
kayomn marked this conversation as resolved
src/ona/sys.zig Outdated
@ -0,0 +434,4 @@
///
/// Returns a [core.io.Allocator] bound to the underlying system allocator.
///
pub fn allocator() core.io.Allocator {
Author
Owner

Unused and out of date.

Unused and out of date.
kayomn marked this conversation as resolved
kayomn added 1 commit 2022-11-04 11:38:42 +01:00
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
4f0224a029
Fix documentation / tidy up code
kayomn changed title from WIP: Application Context Implementation to Application Context Implementation 2022-11-04 11:40:50 +01:00
kayomn added 1 commit 2022-11-08 00:51:49 +01:00
continuous-integration/drone/pr Build is failing Details
continuous-integration/drone/push Build is failing Details
47a997b0ec
Implement loading of Oar archive entry-backed files
Author
Owner

Two remaining tasks before this can be merged in:

  • Compiler bug causing the build error needs working around - either by patching the error or upgrading Zig to 0.10.0.

  • The amount of code duplication between the Allocator, Writer, and FileReader interfaces needs fixing - likely by introducing some sort of generic dynamic dispatch metaprogramming utility into the meta namespace of the core library.

Two remaining tasks before this can be merged in: - [ ] Compiler bug causing the build error needs working around - either by patching the error or upgrading Zig to 0.10.0. - [ ] The amount of code duplication between the `Allocator`, `Writer`, and `FileReader` interfaces needs fixing - likely by introducing some sort of generic dynamic dispatch metaprogramming utility into the meta namespace of the core library.
Author
Owner

Made redundant by #5.

Made redundant by #5.
kayomn closed this pull request 2023-02-20 02:33:24 +01:00
Some checks failed
continuous-integration/drone/pr Build is failing
Required
Details
continuous-integration/drone/push Build is failing
Required
Details

Pull request closed

Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: kayomn/ona#4
No description provided.