Add org-agenda-api container builds and fly.io deployment
Consolidates container builds from colonelpanic-org-agenda-api repo: - Add org-agenda-api input to nixos flake - Add container-colonelpanic and container-kat package outputs - Add org-agenda-api cachix as substituter - Add org-agenda-api devShell for deployment work New org-agenda-api directory contains: - container.nix: Container build logic using mkContainer - configs/: Instance configs (custom-config.el, fly.toml, secrets) - deploy.sh: Fly.io deployment script - secrets.nix: agenix secret declarations Build with: nix build .#container-colonelpanic Deploy with: cd org-agenda-api && ./deploy.sh colonelpanic Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
351
nixos/flake.lock
generated
351
nixos/flake.lock
generated
@@ -29,7 +29,7 @@
|
|||||||
"railbird-secrets",
|
"railbird-secrets",
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
"systems": "systems_7"
|
"systems": "systems_9"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1707830867,
|
"lastModified": 1707830867,
|
||||||
@@ -123,6 +123,28 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"emacs-overlay": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"org-agenda-api",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1769448145,
|
||||||
|
"narHash": "sha256-499V3+SiDzp+Vkwo2Osp1AkstlWeDc3KzfOn+xoiu6Y=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "emacs-overlay",
|
||||||
|
"rev": "2d2050b17743f564ffed290602e7816614a0dbdd",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "emacs-overlay",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -341,6 +363,42 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-utils_10": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_13"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1685518550,
|
||||||
|
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_11": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_14"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681202837,
|
||||||
|
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-utils_2": {
|
"flake-utils_2": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems_3"
|
"systems": "systems_3"
|
||||||
@@ -414,9 +472,45 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-utils_6": {
|
"flake-utils_6": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_7"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_7": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems_8"
|
"systems": "systems_8"
|
||||||
},
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_8": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_10"
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1709126324,
|
"lastModified": 1709126324,
|
||||||
"narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=",
|
"narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=",
|
||||||
@@ -431,9 +525,9 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-utils_7": {
|
"flake-utils_9": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems_10"
|
"systems": "systems_12"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1710146030,
|
"lastModified": 1710146030,
|
||||||
@@ -449,42 +543,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-utils_8": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems_11"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1685518550,
|
|
||||||
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-utils_9": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems_12"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681202837,
|
|
||||||
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fourmolu-011": {
|
"fourmolu-011": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -625,6 +683,29 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"git-sync-rs": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_7",
|
||||||
|
"nixpkgs": [
|
||||||
|
"org-agenda-api",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"rust-overlay": "rust-overlay"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1769370203,
|
||||||
|
"narHash": "sha256-J52a10KE7GrR4EZjZiKdOOOeSe2uJiCYmUz1nVqC+Ec=",
|
||||||
|
"owner": "colonelpanic8",
|
||||||
|
"repo": "git-sync-rs",
|
||||||
|
"rev": "a0a68207460cc8ffed6b0f7a3045d5c4cae826c9",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "colonelpanic8",
|
||||||
|
"repo": "git-sync-rs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"gitignore": {
|
"gitignore": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -990,7 +1071,7 @@
|
|||||||
"haskell-language-server_2": {
|
"haskell-language-server_2": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat_6",
|
"flake-compat": "flake-compat_6",
|
||||||
"flake-utils": "flake-utils_8",
|
"flake-utils": "flake-utils_10",
|
||||||
"fourmolu-011": "fourmolu-011_2",
|
"fourmolu-011": "fourmolu-011_2",
|
||||||
"fourmolu-012": "fourmolu-012_2",
|
"fourmolu-012": "fourmolu-012_2",
|
||||||
"gitignore": "gitignore_4",
|
"gitignore": "gitignore_4",
|
||||||
@@ -1002,7 +1083,7 @@
|
|||||||
"lsp": "lsp_2",
|
"lsp": "lsp_2",
|
||||||
"lsp-test": "lsp-test_2",
|
"lsp-test": "lsp-test_2",
|
||||||
"lsp-types": "lsp-types_2",
|
"lsp-types": "lsp-types_2",
|
||||||
"nixpkgs": "nixpkgs_15",
|
"nixpkgs": "nixpkgs_16",
|
||||||
"ormolu-052": "ormolu-052_2",
|
"ormolu-052": "ormolu-052_2",
|
||||||
"ormolu-07": "ormolu-07_2",
|
"ormolu-07": "ormolu-07_2",
|
||||||
"stylish-haskell-0145": "stylish-haskell-0145_2"
|
"stylish-haskell-0145": "stylish-haskell-0145_2"
|
||||||
@@ -1641,6 +1722,22 @@
|
|||||||
"url": "https://hackage.haskell.org/package/lsp-2.2.0.0/lsp-2.2.0.0.tar.gz"
|
"url": "https://hackage.haskell.org/package/lsp-2.2.0.0/lsp-2.2.0.0.tar.gz"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mova": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1769543761,
|
||||||
|
"narHash": "sha256-z7MzEnGWJPeSeQ6gBw3Cj4frE7rGOTtd8BFVPABIlSw=",
|
||||||
|
"owner": "colonelpanic8",
|
||||||
|
"repo": "mova",
|
||||||
|
"rev": "d4c61ee8489d3bf9a0ff91c0a95171e939f8ba1b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "colonelpanic8",
|
||||||
|
"repo": "mova",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nix": {
|
"nix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat_3",
|
"flake-compat": "flake-compat_3",
|
||||||
@@ -1784,6 +1881,22 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixpkgs-stable": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1767313136,
|
||||||
|
"narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "ac62194c3917d5f474c1a844b6fd6da2db95077d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-25.05",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs_10": {
|
"nixpkgs_10": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769170682,
|
"lastModified": 1769170682,
|
||||||
@@ -1849,6 +1962,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_14": {
|
"nixpkgs_14": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1744536153,
|
||||||
|
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_15": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1709703039,
|
"lastModified": 1709703039,
|
||||||
"narHash": "sha256-6hqgQ8OK6gsMu1VtcGKBxKQInRLHtzulDo9Z5jxHEFY=",
|
"narHash": "sha256-6hqgQ8OK6gsMu1VtcGKBxKQInRLHtzulDo9Z5jxHEFY=",
|
||||||
@@ -1864,7 +1993,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_15": {
|
"nixpkgs_16": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1686874404,
|
"lastModified": 1686874404,
|
||||||
"narHash": "sha256-u2Ss8z+sGaVlKtq7sCovQ8WvXY+OoXJmY1zmyxITiaY=",
|
"narHash": "sha256-u2Ss8z+sGaVlKtq7sCovQ8WvXY+OoXJmY1zmyxITiaY=",
|
||||||
@@ -1880,7 +2009,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_16": {
|
"nixpkgs_17": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1682134069,
|
"lastModified": 1682134069,
|
||||||
"narHash": "sha256-TnI/ZXSmRxQDt2sjRYK/8j8iha4B4zP2cnQCZZ3vp7k=",
|
"narHash": "sha256-TnI/ZXSmRxQDt2sjRYK/8j8iha4B4zP2cnQCZZ3vp7k=",
|
||||||
@@ -2063,6 +2192,81 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"org-agenda-api": {
|
||||||
|
"inputs": {
|
||||||
|
"emacs-overlay": "emacs-overlay",
|
||||||
|
"flake-utils": "flake-utils_6",
|
||||||
|
"git-sync-rs": "git-sync-rs",
|
||||||
|
"mova": "mova",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"org-project-capture": "org-project-capture",
|
||||||
|
"org-wild-notifier": "org-wild-notifier",
|
||||||
|
"org-window-habit": "org-window-habit"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1769633802,
|
||||||
|
"narHash": "sha256-7rtHTFTqcQcXtOlBtZSAa0MAeUeDxkmS+xRYLmVxW2c=",
|
||||||
|
"owner": "colonelpanic8",
|
||||||
|
"repo": "org-agenda-api",
|
||||||
|
"rev": "2f05853be4c90f996f55c18b11535fea2a31d8d0",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "colonelpanic8",
|
||||||
|
"repo": "org-agenda-api",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"org-project-capture": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1768723645,
|
||||||
|
"narHash": "sha256-W/Y825QZ3WsUK+Kvio138W/6vS4hQ9xPqy2MZRpYfYM=",
|
||||||
|
"owner": "colonelpanic8",
|
||||||
|
"repo": "org-project-capture",
|
||||||
|
"rev": "4303dd869b0d638bd09014702803cde50f4e7fed",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "colonelpanic8",
|
||||||
|
"repo": "org-project-capture",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"org-wild-notifier": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1769492021,
|
||||||
|
"narHash": "sha256-1CWB61A9yAFZLcwylShXh7/cM5EDyTsBPZd/XOLAGEY=",
|
||||||
|
"owner": "emacsorphanage",
|
||||||
|
"repo": "org-wild-notifier",
|
||||||
|
"rev": "9211d14128a1097f78081dd7f8ba849671604575",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "emacsorphanage",
|
||||||
|
"repo": "org-wild-notifier",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"org-window-habit": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1769531910,
|
||||||
|
"narHash": "sha256-jiWxQW56z9yA7KJHxr70QIvWKTh+CVFi+9Biund2Xjg=",
|
||||||
|
"owner": "colonelpanic8",
|
||||||
|
"repo": "org-window-habit",
|
||||||
|
"rev": "39cdc616b5b8eb21716324e51ac7b2e9ccedb50f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "colonelpanic8",
|
||||||
|
"repo": "org-window-habit",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ormolu-052": {
|
"ormolu-052": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -2173,8 +2377,8 @@
|
|||||||
"railbird-secrets": {
|
"railbird-secrets": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"agenix": "agenix_2",
|
"agenix": "agenix_2",
|
||||||
"flake-utils": "flake-utils_6",
|
"flake-utils": "flake-utils_8",
|
||||||
"nixpkgs": "nixpkgs_14"
|
"nixpkgs": "nixpkgs_15"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769209874,
|
"lastModified": 1769209874,
|
||||||
@@ -2208,15 +2412,34 @@
|
|||||||
"nixpkgs": "nixpkgs_10",
|
"nixpkgs": "nixpkgs_10",
|
||||||
"nixtheplanet": "nixtheplanet",
|
"nixtheplanet": "nixtheplanet",
|
||||||
"notifications-tray-icon": "notifications-tray-icon",
|
"notifications-tray-icon": "notifications-tray-icon",
|
||||||
|
"org-agenda-api": "org-agenda-api",
|
||||||
"railbird-secrets": "railbird-secrets",
|
"railbird-secrets": "railbird-secrets",
|
||||||
"status-notifier-item": "status-notifier-item_2",
|
"status-notifier-item": "status-notifier-item_2",
|
||||||
"systems": "systems_9",
|
"systems": "systems_11",
|
||||||
"taffybar": "taffybar_2",
|
"taffybar": "taffybar_2",
|
||||||
"vscode-server": "vscode-server",
|
"vscode-server": "vscode-server",
|
||||||
"xmonad": "xmonad_3",
|
"xmonad": "xmonad_3",
|
||||||
"xmonad-contrib": "xmonad-contrib"
|
"xmonad-contrib": "xmonad-contrib"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"rust-overlay": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs_14"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1755311859,
|
||||||
|
"narHash": "sha256-NspGtm0ZpihxlFD628pvh5ZEhL/Q6/Z9XBpe3n6ZtEw=",
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"rev": "07619500e5937cc4669f24fec355d18a8fec0165",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"status-notifier-item": {
|
"status-notifier-item": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -2372,6 +2595,36 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"systems_13": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems_14": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"systems_2": {
|
"systems_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1689347949,
|
"lastModified": 1689347949,
|
||||||
@@ -2525,7 +2778,7 @@
|
|||||||
},
|
},
|
||||||
"taffybar_2": {
|
"taffybar_2": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils_7",
|
"flake-utils": "flake-utils_9",
|
||||||
"git-ignore-nix": "git-ignore-nix_3",
|
"git-ignore-nix": "git-ignore-nix_3",
|
||||||
"gtk-sni-tray": "gtk-sni-tray_3",
|
"gtk-sni-tray": "gtk-sni-tray_3",
|
||||||
"gtk-strut": "gtk-strut_4",
|
"gtk-strut": "gtk-strut_4",
|
||||||
@@ -2601,8 +2854,8 @@
|
|||||||
},
|
},
|
||||||
"vscode-server": {
|
"vscode-server": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils_9",
|
"flake-utils": "flake-utils_11",
|
||||||
"nixpkgs": "nixpkgs_16"
|
"nixpkgs": "nixpkgs_17"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1753541826,
|
"lastModified": 1753541826,
|
||||||
|
|||||||
@@ -27,6 +27,11 @@
|
|||||||
|
|
||||||
agenix = {url = "github:ryantm/agenix";};
|
agenix = {url = "github:ryantm/agenix";};
|
||||||
|
|
||||||
|
org-agenda-api = {
|
||||||
|
url = "github:colonelpanic8/org-agenda-api";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
# Hyprland and plugins from official flakes for proper plugin compatibility
|
# Hyprland and plugins from official flakes for proper plugin compatibility
|
||||||
hyprland = {
|
hyprland = {
|
||||||
url = "git+https://github.com/hyprwm/Hyprland?submodules=1&ref=refs/tags/v0.53.0";
|
url = "git+https://github.com/hyprwm/Hyprland?submodules=1&ref=refs/tags/v0.53.0";
|
||||||
@@ -138,6 +143,8 @@
|
|||||||
imalison-taffybar,
|
imalison-taffybar,
|
||||||
hyprland,
|
hyprland,
|
||||||
hy3,
|
hy3,
|
||||||
|
org-agenda-api,
|
||||||
|
flake-utils,
|
||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
# Nixpkgs PR patches - just specify PR number and hash
|
# Nixpkgs PR patches - just specify PR number and hash
|
||||||
@@ -315,10 +322,12 @@
|
|||||||
extra-substituters = [
|
extra-substituters = [
|
||||||
"http://192.168.1.26:5050"
|
"http://192.168.1.26:5050"
|
||||||
"https://cache.flox.dev"
|
"https://cache.flox.dev"
|
||||||
|
"https://org-agenda-api.cachix.org"
|
||||||
];
|
];
|
||||||
extra-trusted-public-keys = [
|
extra-trusted-public-keys = [
|
||||||
"1896Folsom.duckdns.org:U2FTjvP95qwAJo0oGpvmUChJCgi5zQoG1YisoI08Qoo="
|
"1896Folsom.duckdns.org:U2FTjvP95qwAJo0oGpvmUChJCgi5zQoG1YisoI08Qoo="
|
||||||
"flox-cache-public-1:7F4OyH7ZCnFhcze3fJdfyXYLQw/aV7GEed86nQ7IsOs="
|
"flox-cache-public-1:7F4OyH7ZCnFhcze3fJdfyXYLQw/aV7GEed86nQ7IsOs="
|
||||||
|
"org-agenda-api.cachix.org-1:liKFemKkOLV/rJt2txDNcpDjRsqLuBneBjkSw/UVXKA="
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
nixosConfigurations =
|
nixosConfigurations =
|
||||||
@@ -332,5 +341,57 @@
|
|||||||
mkConfig (params // machineParams)
|
mkConfig (params // machineParams)
|
||||||
)
|
)
|
||||||
defaultConfigurationParams;
|
defaultConfigurationParams;
|
||||||
|
} // flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
|
||||||
|
# Get short revs for tagging
|
||||||
|
orgApiRev = builtins.substring 0 7 (org-agenda-api.rev or "unknown");
|
||||||
|
dotfilesRev = builtins.substring 0 7 (self.rev or self.dirtyRev or "dirty");
|
||||||
|
|
||||||
|
# Get tangled config files from org-agenda-api.nix
|
||||||
|
dotfilesOrgApi = import ./org-agenda-api.nix {
|
||||||
|
inherit pkgs system;
|
||||||
|
inherit inputs;
|
||||||
|
};
|
||||||
|
tangledConfig = dotfilesOrgApi.org-agenda-custom-config;
|
||||||
|
|
||||||
|
# Import container build logic
|
||||||
|
containerLib = import ../org-agenda-api/container.nix {
|
||||||
|
inherit pkgs system tangledConfig org-agenda-api orgApiRev dotfilesRev;
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
packages = {
|
||||||
|
container-colonelpanic = containerLib.containers.colonelpanic;
|
||||||
|
container-kat = containerLib.containers.kat;
|
||||||
|
# Default container
|
||||||
|
container = containerLib.containers.colonelpanic;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Dev shell for org-agenda-api deployment
|
||||||
|
devShells.org-agenda-api = pkgs.mkShell {
|
||||||
|
buildInputs = [
|
||||||
|
pkgs.flyctl
|
||||||
|
agenix.packages.${system}.default
|
||||||
|
pkgs.age
|
||||||
|
pkgs.ssh-to-age
|
||||||
|
pkgs.git
|
||||||
|
pkgs.jq
|
||||||
|
pkgs.just
|
||||||
|
pkgs.curl
|
||||||
|
];
|
||||||
|
shellHook = ''
|
||||||
|
echo ""
|
||||||
|
echo "org-agenda-api deployment shell"
|
||||||
|
echo ""
|
||||||
|
echo "Commands:"
|
||||||
|
echo " just --list - Show available API commands"
|
||||||
|
echo " ./deploy.sh <instance> - Deploy to Fly.io (colonelpanic or kat)"
|
||||||
|
echo " flyctl - Fly.io CLI"
|
||||||
|
echo " agenix -e <file> - Edit encrypted secrets"
|
||||||
|
echo ""
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
1
org-agenda-api/.envrc
Normal file
1
org-agenda-api/.envrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
use flake ../nixos#org-agenda-api
|
||||||
2
org-agenda-api/.gitignore
vendored
Normal file
2
org-agenda-api/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
result*
|
||||||
|
.direnv/
|
||||||
6
org-agenda-api/configs/colonelpanic/config.env
Normal file
6
org-agenda-api/configs/colonelpanic/config.env
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# colonelpanic instance configuration
|
||||||
|
FLY_APP="colonelpanic-org-agenda"
|
||||||
|
GIT_SYNC_REPOSITORY="git@github.com:colonelpanic8/org.git"
|
||||||
|
GIT_USER_EMAIL="org-agenda-api@colonelpanic.io"
|
||||||
|
GIT_USER_NAME="org-agenda-api"
|
||||||
|
AUTH_USER="imalison"
|
||||||
252
org-agenda-api/configs/colonelpanic/custom-config.el
Normal file
252
org-agenda-api/configs/colonelpanic/custom-config.el
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
;;; custom-config.el --- Container config loader -*- lexical-binding: t; -*-
|
||||||
|
|
||||||
|
;; Helper function used by org-config (must be defined before loading preface)
|
||||||
|
(defun imalison:join-paths (&rest paths)
|
||||||
|
"Join PATHS together into a single path."
|
||||||
|
(let ((result (car paths)))
|
||||||
|
(dolist (p (cdr paths))
|
||||||
|
(setq result (expand-file-name p result)))
|
||||||
|
result))
|
||||||
|
|
||||||
|
;; Load tangled config files in order
|
||||||
|
(let ((config-dir (file-name-directory load-file-name)))
|
||||||
|
;; Load preface first (defines variables with default values)
|
||||||
|
(when (file-exists-p (expand-file-name "org-config-preface.el" config-dir))
|
||||||
|
(load (expand-file-name "org-config-preface.el" config-dir)))
|
||||||
|
|
||||||
|
;; Override paths for container environment AFTER loading preface
|
||||||
|
;; Use setq to ensure we override the defvar values from preface
|
||||||
|
(setq imalison:org-dir "/data/org")
|
||||||
|
(setq imalison:shared-org-dir nil)
|
||||||
|
|
||||||
|
;; Re-derive all path variables using the container org-dir
|
||||||
|
(setq imalison:org-gtd-file (imalison:join-paths imalison:org-dir "gtd.org"))
|
||||||
|
(setq imalison:org-habits-file (imalison:join-paths imalison:org-dir "habits.org"))
|
||||||
|
(setq imalison:org-calendar-file (imalison:join-paths imalison:org-dir "calendar.org"))
|
||||||
|
(setq imalison:org-inbox-file (imalison:join-paths imalison:org-dir "inbox.org"))
|
||||||
|
|
||||||
|
;; Shared paths are nil in container (no shared org dir)
|
||||||
|
(setq imalison:shared-org-gtd-file nil)
|
||||||
|
(setq imalison:shared-habits-file nil)
|
||||||
|
(setq imalison:shared-calendar-file nil)
|
||||||
|
(setq imalison:shared-shopping-file nil)
|
||||||
|
(setq imalison:shared-repeating-file nil)
|
||||||
|
(setq imalison:orgzly-files (list (imalison:join-paths imalison:org-dir "orgzly.org")))
|
||||||
|
(setq imalison:repeating-org-files (list imalison:org-habits-file))
|
||||||
|
|
||||||
|
;; Enable habits in agenda - this adds habits.org to org-agenda-files
|
||||||
|
(setq imalison:include-repeating-in-agenda t)
|
||||||
|
|
||||||
|
;; org-config-custom.el uses customize format (var value), convert to setq
|
||||||
|
(when (file-exists-p (expand-file-name "org-config-custom.el" config-dir))
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert-file-contents (expand-file-name "org-config-custom.el" config-dir))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(condition-case nil
|
||||||
|
(while t
|
||||||
|
(let ((form (read (current-buffer))))
|
||||||
|
(when (and (listp form) (symbolp (car form)))
|
||||||
|
(set (car form) (eval (cadr form))))))
|
||||||
|
(end-of-file nil))))
|
||||||
|
|
||||||
|
;; Load main config (sets up org-agenda-files using the variables we just set)
|
||||||
|
(when (file-exists-p (expand-file-name "org-config-config.el" config-dir))
|
||||||
|
(load (expand-file-name "org-config-config.el" config-dir)))
|
||||||
|
|
||||||
|
;; Load optional overrides (instance-specific customizations)
|
||||||
|
(when (file-exists-p (expand-file-name "overrides.el" config-dir))
|
||||||
|
(load (expand-file-name "overrides.el" config-dir))))
|
||||||
|
|
||||||
|
;; Define no-op stubs for unavailable packages (overwrite autoloads)
|
||||||
|
(defun org-bullets-mode (&optional _arg)
|
||||||
|
"No-op stub for org-bullets-mode (package not available in container)."
|
||||||
|
nil)
|
||||||
|
|
||||||
|
;; Override shared-org-file-p to handle nil imalison:shared-org-dir
|
||||||
|
;; The original calls (file-truename imalison:shared-org-dir) which errors when nil
|
||||||
|
(defun imalison:shared-org-file-p ()
|
||||||
|
"Check if current file is in the shared org directory.
|
||||||
|
Returns nil if imalison:shared-org-dir is not set."
|
||||||
|
(and imalison:shared-org-dir
|
||||||
|
(string-prefix-p (file-truename imalison:shared-org-dir)
|
||||||
|
(file-truename default-directory))))
|
||||||
|
|
||||||
|
;; Helper functions used by org-agenda-custom-commands
|
||||||
|
;; These are defined in README.org but needed for custom views
|
||||||
|
(defun imalison:compare-int-list (a b)
|
||||||
|
"Compare two lists of integers lexicographically."
|
||||||
|
(when (and a b)
|
||||||
|
(cond ((> (car a) (car b)) 1)
|
||||||
|
((< (car a) (car b)) -1)
|
||||||
|
(t (imalison:compare-int-list (cdr a) (cdr b))))))
|
||||||
|
|
||||||
|
(defun get-date-created-from-agenda-entry (agenda-entry)
|
||||||
|
"Get the CREATED property timestamp from an agenda entry."
|
||||||
|
(org-time-string-to-time
|
||||||
|
(org-entry-get (get-text-property 1 'org-marker agenda-entry) "CREATED")))
|
||||||
|
|
||||||
|
;; Auto-convert org-capture-templates to org-agenda-api-capture-templates
|
||||||
|
(defun imalison:extract-template-string (template-spec)
|
||||||
|
"Extract the template string from TEMPLATE-SPEC.
|
||||||
|
Handles string templates, function templates, and file templates."
|
||||||
|
(let ((template-part (nth 4 template-spec)))
|
||||||
|
(cond
|
||||||
|
((stringp template-part) template-part)
|
||||||
|
((and (listp template-part)
|
||||||
|
(eq (car template-part) 'function))
|
||||||
|
;; Try to evaluate the function to get template string
|
||||||
|
(condition-case nil
|
||||||
|
(let ((result (funcall (eval (cadr template-part)))))
|
||||||
|
(if (stringp result) result ""))
|
||||||
|
(error "")))
|
||||||
|
((and (listp template-part)
|
||||||
|
(eq (car template-part) 'file))
|
||||||
|
;; File template - read file contents
|
||||||
|
(condition-case nil
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert-file-contents (eval (cadr template-part)))
|
||||||
|
(buffer-string))
|
||||||
|
(error "")))
|
||||||
|
(t ""))))
|
||||||
|
|
||||||
|
(defun imalison:infer-prompt-type (prompt-match)
|
||||||
|
"Infer the prompt type from PROMPT-MATCH.
|
||||||
|
PROMPT-MATCH is the full match string like \"%^{Name}t\" or \"%^{Title}\"."
|
||||||
|
(let ((suffix (substring prompt-match (1- (length prompt-match)))))
|
||||||
|
(cond
|
||||||
|
((string-match-p "[tTuU]$" prompt-match) 'date)
|
||||||
|
((string= "g" suffix) 'tag)
|
||||||
|
((string= "G" suffix) 'tag)
|
||||||
|
((string= "C" suffix) 'string) ; completion
|
||||||
|
(t 'string))))
|
||||||
|
|
||||||
|
(defun imalison:extract-prompts-from-template (template-string)
|
||||||
|
"Extract prompt definitions from TEMPLATE-STRING.
|
||||||
|
Returns a list of (NAME :type TYPE :required t) for each %^{...} found."
|
||||||
|
(let ((prompts '())
|
||||||
|
(seen-names '())
|
||||||
|
(pos 0))
|
||||||
|
(while (string-match "%\\^{\\([^}|]+\\)\\(?:|[^}]*\\)?}\\([tTuUgGC]\\)?" template-string pos)
|
||||||
|
(let* ((name (match-string 1 template-string))
|
||||||
|
(full-match (match-string 0 template-string))
|
||||||
|
(type (imalison:infer-prompt-type full-match)))
|
||||||
|
(unless (member name seen-names)
|
||||||
|
(push name seen-names)
|
||||||
|
(push (list name :type type :required t) prompts))
|
||||||
|
(setq pos (match-end 0))))
|
||||||
|
(nreverse prompts)))
|
||||||
|
|
||||||
|
(defun imalison:convert-capture-template (template-spec)
|
||||||
|
"Convert a single org-capture TEMPLATE-SPEC to API format.
|
||||||
|
Returns nil for non-entry templates or templates that can't be converted."
|
||||||
|
(when (and (listp template-spec)
|
||||||
|
(>= (length template-spec) 4))
|
||||||
|
(let* ((key (nth 0 template-spec))
|
||||||
|
(description (nth 1 template-spec))
|
||||||
|
(type (nth 2 template-spec)))
|
||||||
|
;; Only convert entry-type templates, skip menu items (no type)
|
||||||
|
(when (and (stringp key)
|
||||||
|
(stringp description)
|
||||||
|
(eq type 'entry))
|
||||||
|
(let* ((template-string (imalison:extract-template-string template-spec))
|
||||||
|
(prompts (imalison:extract-prompts-from-template template-string))
|
||||||
|
;; Create a unique API key from the hotkey
|
||||||
|
(api-key (concat "capture-" key)))
|
||||||
|
(list api-key
|
||||||
|
:name description
|
||||||
|
:template template-spec
|
||||||
|
:prompts prompts))))))
|
||||||
|
|
||||||
|
(defun imalison:convert-all-capture-templates ()
|
||||||
|
"Convert all org-capture-templates to org-agenda-api-capture-templates format."
|
||||||
|
(let ((converted '()))
|
||||||
|
(dolist (template org-capture-templates)
|
||||||
|
(let ((api-template (imalison:convert-capture-template template)))
|
||||||
|
(when api-template
|
||||||
|
(push api-template converted))))
|
||||||
|
(nreverse converted)))
|
||||||
|
|
||||||
|
;; Default prompts for TODO templates when extraction fails
|
||||||
|
(defvar imalison:default-todo-prompts
|
||||||
|
'(("Title" :type string :required t))
|
||||||
|
"Default prompts for TODO capture templates.")
|
||||||
|
|
||||||
|
(defun imalison:ensure-prompts (template-plist)
|
||||||
|
"Ensure TEMPLATE-PLIST has non-empty prompts, using defaults if needed."
|
||||||
|
(let ((prompts (plist-get template-plist :prompts)))
|
||||||
|
(if (and prompts (not (null prompts)))
|
||||||
|
template-plist
|
||||||
|
(plist-put (copy-sequence template-plist) :prompts imalison:default-todo-prompts))))
|
||||||
|
|
||||||
|
;; Auto-generate API capture templates from org-capture-templates
|
||||||
|
;; Add a "default" entry that mirrors the "g" (GTD Todo) template
|
||||||
|
;; Remove "capture-g" from the list since "default" is its replacement
|
||||||
|
;; Ensure all templates have prompts (use defaults if extraction failed)
|
||||||
|
(let* ((converted (imalison:convert-all-capture-templates))
|
||||||
|
;; Ensure all converted templates have prompts
|
||||||
|
(converted-with-prompts
|
||||||
|
(mapcar (lambda (tmpl)
|
||||||
|
(cons (car tmpl) (imalison:ensure-prompts (cdr tmpl))))
|
||||||
|
converted))
|
||||||
|
(gtd-template (assoc "capture-g" converted-with-prompts))
|
||||||
|
(filtered (if gtd-template
|
||||||
|
(cl-remove "capture-g" converted-with-prompts :key #'car :test #'string=)
|
||||||
|
converted-with-prompts))
|
||||||
|
(default-entry (if gtd-template
|
||||||
|
(cons "default" (cdr gtd-template))
|
||||||
|
(list "default" :name "GTD Todo" :prompts imalison:default-todo-prompts))))
|
||||||
|
(setq org-agenda-api-capture-templates (cons default-entry filtered)))
|
||||||
|
|
||||||
|
;; Set up org-project-capture for container environment
|
||||||
|
(require 'org-project-capture)
|
||||||
|
(require 'org-category-capture)
|
||||||
|
|
||||||
|
(setq org-project-capture-projects-file
|
||||||
|
(imalison:join-paths imalison:org-dir "projects.org"))
|
||||||
|
|
||||||
|
;; Create the org-project-capture strategy for the container
|
||||||
|
;; Use single-file strategy since we don't have projectile in the container
|
||||||
|
(setq org-project-capture-strategy
|
||||||
|
(make-instance 'org-project-capture-single-file-strategy))
|
||||||
|
|
||||||
|
(setq org-project-capture-capture-template
|
||||||
|
(format "%s%s" "* TODO %?" imalison:created-property-string))
|
||||||
|
|
||||||
|
;; Register category strategies for the API
|
||||||
|
;; This exposes org-project-capture categories through the /category-types,
|
||||||
|
;; /categories, /category-tasks, and /category-capture endpoints
|
||||||
|
(setq org-agenda-api-category-strategies
|
||||||
|
`(("projects" :strategy ,org-project-capture-strategy
|
||||||
|
:template ,org-project-capture-capture-template)))
|
||||||
|
|
||||||
|
;; Add project capture todo files to org-agenda-files
|
||||||
|
(when (fboundp 'org-project-capture-todo-files)
|
||||||
|
(imalison:add-to-org-agenda-files (org-project-capture-todo-files)))
|
||||||
|
|
||||||
|
;; Also add the main projects file
|
||||||
|
(imalison:add-to-org-agenda-files (list org-project-capture-projects-file))
|
||||||
|
|
||||||
|
;; Configure org-wild-notifier for the API
|
||||||
|
;; These settings must be set explicitly since we call org-wild-notifier functions
|
||||||
|
;; directly without enabling org-wild-notifier-mode
|
||||||
|
(when (require 'org-wild-notifier nil t)
|
||||||
|
(setq org-wild-notifier-keyword-whitelist nil)
|
||||||
|
(setq org-wild-notifier-tags-blacklist '("nonotify"))
|
||||||
|
(setq org-wild-notifier-alert-time '(10))
|
||||||
|
(setq org-wild-notifier-show-any-overdue-with-day-wide-alerts t)
|
||||||
|
(setq org-wild-notifier-day-wide-alert-times '("10pm"))
|
||||||
|
(message "org-wild-notifier configured for API"))
|
||||||
|
|
||||||
|
;; Enable org-window-habit if available in the container
|
||||||
|
;; This provides enhanced habit tracking with visual progress indicators
|
||||||
|
(when (require 'org-window-habit nil t)
|
||||||
|
(setq org-window-habit-property-prefix nil)
|
||||||
|
(setq org-window-habit-repeat-to-scheduled t)
|
||||||
|
(setq org-window-habit-preceding-intervals 21)
|
||||||
|
(setq org-window-habit-following-days 2)
|
||||||
|
(org-window-habit-mode +1)
|
||||||
|
(message "org-window-habit-mode enabled"))
|
||||||
|
;; The org-agenda-api-window-habit.el module is now included in the container,
|
||||||
|
;; providing /habit-config and /habit-status endpoints for window-habit integration.
|
||||||
|
|
||||||
|
;;; custom-config.el ends here
|
||||||
35
org-agenda-api/configs/colonelpanic/fly.toml
Normal file
35
org-agenda-api/configs/colonelpanic/fly.toml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# fly.toml - Fly.io deployment configuration
|
||||||
|
# See https://fly.io/docs/reference/configuration/
|
||||||
|
|
||||||
|
app = "colonelpanic-org-agenda"
|
||||||
|
primary_region = "ord"
|
||||||
|
|
||||||
|
# Image is built from flake and passed via deploy.sh --image flag
|
||||||
|
|
||||||
|
[env]
|
||||||
|
TZ = "America/Los_Angeles"
|
||||||
|
|
||||||
|
[http_service]
|
||||||
|
internal_port = 80
|
||||||
|
force_https = true
|
||||||
|
auto_stop_machines = false
|
||||||
|
auto_start_machines = true
|
||||||
|
min_machines_running = 1
|
||||||
|
|
||||||
|
# Give services time to start (nginx + emacs)
|
||||||
|
[http_service.concurrency]
|
||||||
|
type = "connections"
|
||||||
|
hard_limit = 25
|
||||||
|
soft_limit = 20
|
||||||
|
|
||||||
|
[[http_service.checks]]
|
||||||
|
grace_period = "120s"
|
||||||
|
interval = "30s"
|
||||||
|
timeout = "10s"
|
||||||
|
method = "GET"
|
||||||
|
path = "/health"
|
||||||
|
|
||||||
|
[[vm]]
|
||||||
|
cpu_kind = "shared"
|
||||||
|
cpus = 1
|
||||||
|
memory_mb = 512
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
age-encryption.org/v1
|
||||||
|
-> ssh-rsa gwJx0Q
|
||||||
|
DPbChrJRIw/0GNKJFW0yVpHj/nS0De/I3t9z+tlqL9BFUpIKBNBZeLBW92xPdTgq
|
||||||
|
wAlDKj5Zl1qrxTga4DcHkzA7QJVhTg3GNnEeJqBwaToPb4yEyiToW8B05xjkSrMM
|
||||||
|
fPQ+ZkSbdTfupPrfjmnaHED/4RJXJFt2LvdI1dW4XSPdk4rp7oVbs3dnNqWObAhD
|
||||||
|
ATiQETPLJ33gAdyrM7A5xo7mwRBV+Kvjr+HrXX9dR3LhcMdecAVBbqI0508VjxvQ
|
||||||
|
XP2q30jfavX6x1cNuHNP9UbKRWFZrRvbxi2soz7V7wM5hSiJIuVuYS2Y0hGdIGJy
|
||||||
|
SdSozcsa2wJ/aobH4fMImg
|
||||||
|
-> ssh-ed25519 YFIoHA 0bHvbcnQZjfBkScK0vXJXjTVzAjPRiUz2wn3l1vQplI
|
||||||
|
2wmc1m1XT0f1nHRlgfAGvBJ1xFM84Y0/pVvTSPyxFOU
|
||||||
|
-> ssh-ed25519 KQfiow QFs5J3qngIVkFSae2SN1WWtHKzaNvWeaqw/I6Cukbys
|
||||||
|
k1YNLwNCKjZNUkwM7CwIEK8FcICPElPr94JEXM4bYrI
|
||||||
|
-> ssh-ed25519 kScIxg ucsEcywwdpkDxL327bgvegJXx7/tQf5DZkxl/82bxDY
|
||||||
|
BjaZ78vGeZz54/JK+on8TEaQpK7LHmuZ6OtAyszMjV4
|
||||||
|
-> ssh-ed25519 HzX1zw VQtRquhDhEFTRBa0S0gVEJW90AeTb5hPe8bXJAyxyXw
|
||||||
|
rcUxBPvkIJUk3fvY6vGZgY+mCSzQLDFmKJ5tYafDvAc
|
||||||
|
-> ssh-ed25519 1o2X0w +6SwM5hLyc8wvbxBDJbfivjV+wN7whIgFRdX5z2LiCQ
|
||||||
|
a+UxJ2YKAWV+XJtOluMvq/8p0nfw27lF2JOzvCYHkmg
|
||||||
|
-> ssh-ed25519 KQ5iUA 8BSg4c4T4y0w3msKAmLZJY+J+oHw25mPPlKdFkj5Mlg
|
||||||
|
PsAz9C4XthB/hfn/F4IJ9Ifq280B9IXix0C1mk9RyI8
|
||||||
|
-> ssh-ed25519 AKGkDw KcGz1q5Fe5RdnYKQtHZpuZtcUtdYysxxzGm10rSFvQ8
|
||||||
|
/j2QuqhT1xQDjz/N6KGsBmEGIaL+Cm10YNHeZ6Sw+VU
|
||||||
|
-> ssh-ed25519 0eS5+A QLIh5xJ3+A9eeGMkvzbk01HNi41CaTjRGN75y00SSl0
|
||||||
|
/Axjo9OeRpgTYxfugpiAeQTiTEtbUgUXWc1Rg7aggI0
|
||||||
|
-> ssh-ed25519 9/4Prw 7jpwaON0KKlubIDN3/xllVX7pZhJRoaMVnX5Sc7+wkw
|
||||||
|
ObueU1r7o5F9D0e/DxZkU40AsN9lXK5eOcF0N2M1H0g
|
||||||
|
-> ssh-ed25519 gAk3+Q cklYccOHJ2HFT7m3Oje8McxLif8kOma0h36ERo2uEC8
|
||||||
|
2pPIQfO+V45jdFEZCFRUUFGY1aZWindpoPbCLE1Mies
|
||||||
|
-> ssh-ed25519 X6eGtQ IOpU7iT9w8Dmehx/LEA1Cpr8BnAIFwl2sOj8ZZiY00I
|
||||||
|
dfx6A+tL7Xc4PXRdt6zxh2rtrB3Wb5HhAhpHT+n9cx0
|
||||||
|
-> ssh-ed25519 0ma8Cw C0hjuEmVLn+djtVvJURuVi8b47JVEcux84P6QoX/fGs
|
||||||
|
Vnf3b3kU5zFyW9Km+idxgIlx+CFusnKBdN5sOsB/hVk
|
||||||
|
-> ssh-ed25519 Tp0Z1Q x4jysmX0AOaOWc0hiTzBA2Lwjwza5G/cqfcP22NuiC8
|
||||||
|
pC3MxtgfZHQ1sk/JLtsBKUXPkkC53vH49OVwrWypq1A
|
||||||
|
-> ssh-ed25519 ePNWZQ QPNC4Hw4cLAgZgso+Vgqz60sBd1wUgOVUqxl7yYMEkQ
|
||||||
|
99w0DohDiy6fXwbKHZYcFZNSvCUroxBxerHVPKY16lg
|
||||||
|
-> ssh-ed25519 hILzzA 8tShLcIvpPifSyY0OjKH2fj2F0rgHAol0LRSAAE+7Vw
|
||||||
|
9K3iKvI+KKhjY75rWt3n0v9Bz4yqP548PTgWi35c9m4
|
||||||
|
--- foeVPG32rt4SIuJ0BtwWh+mTUVoQipAapftZUIA/7gw
|
||||||
|
=<3D><><EFBFBD>Q<EFBFBD>uE <09><>U<EFBFBD>u<EFBFBD>-WT<57>3YL<1F><03>iu<69>@<40>?<3F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>'<01>G2}<7D>E <20>US<>A<EFBFBD><41>Yi<59><01>
|
||||||
BIN
org-agenda-api/configs/colonelpanic/secrets/git-ssh-key.age
Normal file
BIN
org-agenda-api/configs/colonelpanic/secrets/git-ssh-key.age
Normal file
Binary file not shown.
6
org-agenda-api/configs/kat/config.env
Normal file
6
org-agenda-api/configs/kat/config.env
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# kat instance configuration
|
||||||
|
FLY_APP="kat-org-agenda-api"
|
||||||
|
GIT_SYNC_REPOSITORIES='[{"url":"ssh://gitea@dev.railbird.ai:1123/colonelpanic/katnivan.git","path":"org"},{"url":"ssh://gitea@dev.railbird.ai:1123/kkathuang/org.git","path":"shared"}]'
|
||||||
|
GIT_USER_EMAIL="kat-org-agenda-api@colonelpanic.io"
|
||||||
|
GIT_USER_NAME="kat-org-agenda-api"
|
||||||
|
AUTH_USER="kat"
|
||||||
171
org-agenda-api/configs/kat/custom-config.el
Normal file
171
org-agenda-api/configs/kat/custom-config.el
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
;;; custom-config.el --- Container config loader -*- lexical-binding: t; -*-
|
||||||
|
|
||||||
|
;; Helper function used by org-config (must be defined before loading preface)
|
||||||
|
(defun imalison:join-paths (&rest paths)
|
||||||
|
"Join PATHS together into a single path."
|
||||||
|
(let ((result (car paths)))
|
||||||
|
(dolist (p (cdr paths))
|
||||||
|
(setq result (expand-file-name p result)))
|
||||||
|
result))
|
||||||
|
|
||||||
|
;; Load tangled config files in order
|
||||||
|
(let ((config-dir (file-name-directory load-file-name)))
|
||||||
|
;; Load preface first (defines variables with default values)
|
||||||
|
(when (file-exists-p (expand-file-name "org-config-preface.el" config-dir))
|
||||||
|
(load (expand-file-name "org-config-preface.el" config-dir)))
|
||||||
|
|
||||||
|
;; Override paths for container environment AFTER loading preface
|
||||||
|
;; Use setq to ensure we override the defvar values from preface
|
||||||
|
(setq imalison:org-dir "/data/org")
|
||||||
|
(setq imalison:shared-org-dir "/data/shared")
|
||||||
|
|
||||||
|
;; Re-derive all path variables using the container org-dir
|
||||||
|
(setq imalison:org-gtd-file (imalison:join-paths imalison:org-dir "gtd.org"))
|
||||||
|
(setq imalison:org-habits-file (imalison:join-paths imalison:org-dir "habits.org"))
|
||||||
|
(setq imalison:org-calendar-file (imalison:join-paths imalison:org-dir "calendar.org"))
|
||||||
|
(setq imalison:org-inbox-file (imalison:join-paths imalison:org-dir "inbox.org"))
|
||||||
|
|
||||||
|
;; Shared paths derived from shared-org-dir
|
||||||
|
(setq imalison:shared-org-gtd-file (imalison:join-paths imalison:shared-org-dir "gtd.org"))
|
||||||
|
(setq imalison:shared-habits-file (imalison:join-paths imalison:shared-org-dir "habits.org"))
|
||||||
|
(setq imalison:shared-calendar-file (imalison:join-paths imalison:shared-org-dir "calendar.org"))
|
||||||
|
(setq imalison:shared-shopping-file (imalison:join-paths imalison:shared-org-dir "shopping.org"))
|
||||||
|
(setq imalison:shared-repeating-file (imalison:join-paths imalison:shared-org-dir "repeating.org"))
|
||||||
|
(setq imalison:orgzly-files (list (imalison:join-paths imalison:org-dir "orgzly.org")))
|
||||||
|
(setq imalison:repeating-org-files (list imalison:org-habits-file
|
||||||
|
imalison:shared-habits-file
|
||||||
|
imalison:shared-repeating-file))
|
||||||
|
|
||||||
|
;; org-config-custom.el uses customize format (var value), convert to setq
|
||||||
|
(when (file-exists-p (expand-file-name "org-config-custom.el" config-dir))
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert-file-contents (expand-file-name "org-config-custom.el" config-dir))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(condition-case nil
|
||||||
|
(while t
|
||||||
|
(let ((form (read (current-buffer))))
|
||||||
|
(when (and (listp form) (symbolp (car form)))
|
||||||
|
(set (car form) (eval (cadr form))))))
|
||||||
|
(end-of-file nil))))
|
||||||
|
|
||||||
|
;; Load main config (sets up org-agenda-files using the variables we just set)
|
||||||
|
(when (file-exists-p (expand-file-name "org-config-config.el" config-dir))
|
||||||
|
(load (expand-file-name "org-config-config.el" config-dir)))
|
||||||
|
|
||||||
|
;; Load optional overrides (instance-specific customizations)
|
||||||
|
(when (file-exists-p (expand-file-name "overrides.el" config-dir))
|
||||||
|
(load (expand-file-name "overrides.el" config-dir))))
|
||||||
|
|
||||||
|
;; Define no-op stubs for unavailable packages (overwrite autoloads)
|
||||||
|
(defun org-bullets-mode (&optional _arg)
|
||||||
|
"No-op stub for org-bullets-mode (package not available in container)."
|
||||||
|
nil)
|
||||||
|
|
||||||
|
;; Override shared-org-file-p to handle nil imalison:shared-org-dir
|
||||||
|
;; The original calls (file-truename imalison:shared-org-dir) which errors when nil
|
||||||
|
(defun imalison:shared-org-file-p ()
|
||||||
|
"Check if current file is in the shared org directory.
|
||||||
|
Returns nil if imalison:shared-org-dir is not set."
|
||||||
|
(and imalison:shared-org-dir
|
||||||
|
(string-prefix-p (file-truename imalison:shared-org-dir)
|
||||||
|
(file-truename default-directory))))
|
||||||
|
|
||||||
|
;; Helper functions used by org-agenda-custom-commands
|
||||||
|
;; These are defined in README.org but needed for custom views
|
||||||
|
(defun imalison:compare-int-list (a b)
|
||||||
|
"Compare two lists of integers lexicographically."
|
||||||
|
(when (and a b)
|
||||||
|
(cond ((> (car a) (car b)) 1)
|
||||||
|
((< (car a) (car b)) -1)
|
||||||
|
(t (imalison:compare-int-list (cdr a) (cdr b))))))
|
||||||
|
|
||||||
|
(defun get-date-created-from-agenda-entry (agenda-entry)
|
||||||
|
"Get the CREATED property timestamp from an agenda entry."
|
||||||
|
(org-time-string-to-time
|
||||||
|
(org-entry-get (get-text-property 1 'org-marker agenda-entry) "CREATED")))
|
||||||
|
|
||||||
|
;; Auto-convert org-capture-templates to org-agenda-api-capture-templates
|
||||||
|
(defun imalison:extract-template-string (template-spec)
|
||||||
|
"Extract the template string from TEMPLATE-SPEC.
|
||||||
|
Handles string templates, function templates, and file templates."
|
||||||
|
(let ((template-part (nth 4 template-spec)))
|
||||||
|
(cond
|
||||||
|
((stringp template-part) template-part)
|
||||||
|
((and (listp template-part)
|
||||||
|
(eq (car template-part) 'function))
|
||||||
|
;; Try to evaluate the function to get template string
|
||||||
|
(condition-case nil
|
||||||
|
(let ((result (funcall (eval (cadr template-part)))))
|
||||||
|
(if (stringp result) result ""))
|
||||||
|
(error "")))
|
||||||
|
((and (listp template-part)
|
||||||
|
(eq (car template-part) 'file))
|
||||||
|
;; File template - read file contents
|
||||||
|
(condition-case nil
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert-file-contents (eval (cadr template-part)))
|
||||||
|
(buffer-string))
|
||||||
|
(error "")))
|
||||||
|
(t ""))))
|
||||||
|
|
||||||
|
(defun imalison:infer-prompt-type (prompt-match)
|
||||||
|
"Infer the prompt type from PROMPT-MATCH.
|
||||||
|
PROMPT-MATCH is the full match string like \"%^{Name}t\" or \"%^{Title}\"."
|
||||||
|
(let ((suffix (substring prompt-match (1- (length prompt-match)))))
|
||||||
|
(cond
|
||||||
|
((string-match-p "[tTuU]$" prompt-match) 'date)
|
||||||
|
((string= "g" suffix) 'tag)
|
||||||
|
((string= "G" suffix) 'tag)
|
||||||
|
((string= "C" suffix) 'string) ; completion
|
||||||
|
(t 'string))))
|
||||||
|
|
||||||
|
(defun imalison:extract-prompts-from-template (template-string)
|
||||||
|
"Extract prompt definitions from TEMPLATE-STRING.
|
||||||
|
Returns a list of (NAME :type TYPE :required t) for each %^{...} found."
|
||||||
|
(let ((prompts '())
|
||||||
|
(seen-names '())
|
||||||
|
(pos 0))
|
||||||
|
(while (string-match "%\\^{\\([^}|]+\\)\\(?:|[^}]*\\)?}\\([tTuUgGC]\\)?" template-string pos)
|
||||||
|
(let* ((name (match-string 1 template-string))
|
||||||
|
(full-match (match-string 0 template-string))
|
||||||
|
(type (imalison:infer-prompt-type full-match)))
|
||||||
|
(unless (member name seen-names)
|
||||||
|
(push name seen-names)
|
||||||
|
(push (list name :type type :required t) prompts))
|
||||||
|
(setq pos (match-end 0))))
|
||||||
|
(nreverse prompts)))
|
||||||
|
|
||||||
|
(defun imalison:convert-capture-template (template-spec)
|
||||||
|
"Convert a single org-capture TEMPLATE-SPEC to API format.
|
||||||
|
Returns nil for non-entry templates or templates that can't be converted."
|
||||||
|
(when (and (listp template-spec)
|
||||||
|
(>= (length template-spec) 4))
|
||||||
|
(let* ((key (nth 0 template-spec))
|
||||||
|
(description (nth 1 template-spec))
|
||||||
|
(type (nth 2 template-spec)))
|
||||||
|
;; Only convert entry-type templates, skip menu items (no type)
|
||||||
|
(when (and (stringp key)
|
||||||
|
(stringp description)
|
||||||
|
(eq type 'entry))
|
||||||
|
(let* ((template-string (imalison:extract-template-string template-spec))
|
||||||
|
(prompts (imalison:extract-prompts-from-template template-string))
|
||||||
|
;; Create a unique API key from the hotkey
|
||||||
|
(api-key (concat "capture-" key)))
|
||||||
|
(list api-key
|
||||||
|
:name description
|
||||||
|
:template template-spec
|
||||||
|
:prompts prompts))))))
|
||||||
|
|
||||||
|
(defun imalison:convert-all-capture-templates ()
|
||||||
|
"Convert all org-capture-templates to org-agenda-api-capture-templates format."
|
||||||
|
(let ((converted '()))
|
||||||
|
(dolist (template org-capture-templates)
|
||||||
|
(let ((api-template (imalison:convert-capture-template template)))
|
||||||
|
(when api-template
|
||||||
|
(push api-template converted))))
|
||||||
|
(nreverse converted)))
|
||||||
|
|
||||||
|
;; Auto-generate API capture templates from org-capture-templates
|
||||||
|
(setq org-agenda-api-capture-templates (imalison:convert-all-capture-templates))
|
||||||
|
|
||||||
|
;;; custom-config.el ends here
|
||||||
32
org-agenda-api/configs/kat/fly.toml
Normal file
32
org-agenda-api/configs/kat/fly.toml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# fly.toml - Fly.io deployment configuration
|
||||||
|
# See https://fly.io/docs/reference/configuration/
|
||||||
|
|
||||||
|
app = "kat-org-agenda-api"
|
||||||
|
primary_region = "ord"
|
||||||
|
|
||||||
|
# Image is built from flake and passed via deploy.sh --image flag
|
||||||
|
|
||||||
|
[http_service]
|
||||||
|
internal_port = 80
|
||||||
|
force_https = true
|
||||||
|
auto_stop_machines = false
|
||||||
|
auto_start_machines = true
|
||||||
|
min_machines_running = 1
|
||||||
|
|
||||||
|
# Give services time to start (nginx + emacs)
|
||||||
|
[http_service.concurrency]
|
||||||
|
type = "connections"
|
||||||
|
hard_limit = 25
|
||||||
|
soft_limit = 20
|
||||||
|
|
||||||
|
[[http_service.checks]]
|
||||||
|
grace_period = "10s"
|
||||||
|
interval = "30s"
|
||||||
|
timeout = "5s"
|
||||||
|
method = "GET"
|
||||||
|
path = "/health"
|
||||||
|
|
||||||
|
[[vm]]
|
||||||
|
cpu_kind = "shared"
|
||||||
|
cpus = 1
|
||||||
|
memory_mb = 512
|
||||||
5
org-agenda-api/configs/kat/secrets/auth-password.age
Normal file
5
org-agenda-api/configs/kat/secrets/auth-password.age
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
age-encryption.org/v1
|
||||||
|
-> ssh-ed25519 Tp0Z1Q ZvS3ewY5ZCm7rWQeliPHPXnzSpfFeqK/1a7pWY/l83s
|
||||||
|
gJoiP/tSEsPYrxiVFsD1eIRPALL2tdKJFWBNMj/dpAk
|
||||||
|
--- lN9hXwr2IFAJjhe/u52xiOpGTaDU/fWXhhquOhgBc8U
|
||||||
|
<}t<><74><EFBFBD>]<5D> <20>0@<40>y՝z?Y~R4?"<22><><0F>D9<44>X<EFBFBD><58>p}C<>
|
||||||
42
org-agenda-api/configs/kat/secrets/git-ssh-key.age
Normal file
42
org-agenda-api/configs/kat/secrets/git-ssh-key.age
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
age-encryption.org/v1
|
||||||
|
-> ssh-rsa gwJx0Q
|
||||||
|
ugmhcDkQfVGmgY2b4IY0DT9ORMInX+qa8HSaes5RmNItUeRP3ufIeXvSVKC+R0zV
|
||||||
|
YCqOf2uyLyjUIOupRKw/1kTh5Jr+74tX2ug4C8dD2A132yDpyziuA1AoKP3P5Uhp
|
||||||
|
M1pBA9/QtIwzZByA7gYd5aU7SIqXHR/+DM/dnt/uuKXqKfvDPJrCZWcXIbf2k7xP
|
||||||
|
Tyd6NBWfbVmUFZi23qLzN9ugl51Wq7VOvvIpzh+trKFmeTuMyvMCjhRGr/m50sGs
|
||||||
|
y3HRMuoXnkTAKXRZ4aQpHGhTwnVgS3SJWHzhH6OVn2ReSObOKZHieYl55kqcJc99
|
||||||
|
0ONaWAy54klsa9uHTO4C6w
|
||||||
|
-> ssh-ed25519 YFIoHA a6Mdqeh2kYAKiD5etrZiFIlXZaeFcaMooabCWFyG1D0
|
||||||
|
zkHFO8d7/I7qnLchwv3ONNqaxqwenX6o/0ky8Z2A+W0
|
||||||
|
-> ssh-ed25519 KQfiow vuC5xZ6WYVrpqv44zyNAqFqKlA221SPdpBSm4mlpSU0
|
||||||
|
9yXSgczQxZviQf4ZpsAwKa+JOxWH/MQsU5dq4+94Xyc
|
||||||
|
-> ssh-ed25519 kScIxg Jy61wrM+pU7QzEIHO9WRJ3KDNKfFk1ekylqRSgzKwVc
|
||||||
|
h3ZHKf+cFeGVjgXwof9KlLyDtx43vaSafzgrOiQXe5I
|
||||||
|
-> ssh-ed25519 HzX1zw 1en9ihLijagyKd0c7QMJYtH96oIX2phAhJQqM4ysBi4
|
||||||
|
JOBDfrom9qo1ZUn99jcxva3DP58yWOIFzwQebJx/158
|
||||||
|
-> ssh-ed25519 KQfiow WCcqJwJoqy0dnOb/aUe8oW4BGmkZ4PwEijFJEI1o1ho
|
||||||
|
WLjp7UxiY5Kfwq+z5JQzapxUhncZTfsjMUi7FdMtpTU
|
||||||
|
-> ssh-ed25519 1o2X0w aOnNDcvUxNjHT9nKDWvlOBPg2nFUHiweT8oz6vvdF2M
|
||||||
|
L/PXsInIR+iN/2GStkkL7dnJR1aekne8vfgHi3wADck
|
||||||
|
-> ssh-ed25519 KQ5iUA PNAoXHVxtqbzzBfyfdPm4zVBi/ck3Pu5AQpGVqXpQyE
|
||||||
|
C98+eL5rcKXgI41iikybPrKC3Zr+uFMTyQOWczOulhs
|
||||||
|
-> ssh-ed25519 AKGkDw lDwdoiwK/TKbxDORSggj7wFqoQMguUrzWuqpMZz2Y0U
|
||||||
|
rezIK003vf5Jqr7yMED2A6AzIuywDK4TZDYOAGxr5WI
|
||||||
|
-> ssh-ed25519 0eS5+A btXiZqnbH6Q0uYNAeu95W6X1h2j8a8MDJNSRYPGYyg0
|
||||||
|
0/wsL9Va6+db9BEJ/TJEg6gLo7HbdzLpPKdgUiTqoMc
|
||||||
|
-> ssh-ed25519 9/4Prw RMEfjY5nsrEAKFn4IyLD3OyzNVmxgVG3tfNJ4CTbryA
|
||||||
|
7PaRcNos01e83BrxgjR9C1FizAlqY+pq+er5lVwVn0w
|
||||||
|
-> ssh-ed25519 gAk3+Q ktstz8AM5OafJekAf3SDdIi3VJXudT7q3dWx2hUSWWE
|
||||||
|
Yyu/Vudc6aXQXyCQ6rRvFFer5d8IeHegNnpb19UODfs
|
||||||
|
-> ssh-ed25519 X6eGtQ nGjC6TfQPXxDnbDVlR8OC6IID2a8uHMkBsft9AvoDzE
|
||||||
|
zGue+yzKNJTTOEsRMf1KGTxqjRAEfqII40t5MTMqoYU
|
||||||
|
-> ssh-ed25519 0ma8Cw dvgCcSrhVazWrmhkOd60NKLB5nZliQNpdiDYQCDYaAE
|
||||||
|
6GtVprnzy25LtPYtqynoZ+Jy4SI1Y8h0jToAueURptg
|
||||||
|
-> ssh-ed25519 Tp0Z1Q c8MDIlbXAWC9IWMJMg7HvOBdd4ujm67TyyhjBt2lcio
|
||||||
|
2sRBX77p6C+4ZZsiZRLSkyIffVoJL4LFfMFiDMKhvxg
|
||||||
|
-> ssh-ed25519 ePNWZQ Uz1bLbcA/U0aU0BCY/VR8HQU5G1CKg+duSkyXVHbk2k
|
||||||
|
1vyIWbDlw9YotcHhZ0rIVTaBiiUSsqFs4QVsMfTlreY
|
||||||
|
-> ssh-ed25519 hILzzA RWNiC+y7F1XzHMuiM7EQymDH2kyxUhKhRw7og3/frwI
|
||||||
|
MjcexeBdoWG0Sskj86zD5M9aRCT8cJcMkbiF0osDmV0
|
||||||
|
--- yE8GNNA+dIpXZeFOX9zJYI39UPeTN3hYaaLsdOWk6PI
|
||||||
|
{P<><50><1B>_<EFBFBD>'<27><>_<>#<23><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><1F><03>L<EFBFBD><4C><14>W<>dNWQ<57>}<7D> <0E><><EFBFBD><EFBFBD><EFBFBD><E0AB86>(<0C>@<0F><>M<EFBFBD><4D><EFBFBD><0B>k<><6B>H<EFBFBD>&<26>s<><73>z<05><>k<6B>i<EFBFBD><69>Z.
|
||||||
1
org-agenda-api/configs/kat/secrets/git-ssh-key.pub
Normal file
1
org-agenda-api/configs/kat/secrets/git-ssh-key.pub
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGZZsY0ho0kF6TXKEyDG316awWyrO05auloywf+NTiyb kat-org-agenda-api
|
||||||
43
org-agenda-api/container.nix
Normal file
43
org-agenda-api/container.nix
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Container build logic for org-agenda-api instances
|
||||||
|
# Imported by the main dotfiles flake to expose container outputs
|
||||||
|
{ pkgs, system, tangledConfig, org-agenda-api, orgApiRev, dotfilesRev }:
|
||||||
|
|
||||||
|
let
|
||||||
|
# Build container for a specific instance
|
||||||
|
mkInstanceContainer = instanceName: customConfigEl: overridesEl:
|
||||||
|
let
|
||||||
|
# Combine tangled config with instance-specific loader and overrides
|
||||||
|
orgAgendaCustomConfig = pkgs.runCommand "org-agenda-custom-config-${instanceName}" {} ''
|
||||||
|
mkdir -p $out
|
||||||
|
|
||||||
|
# Copy tangled files from dotfiles
|
||||||
|
cp ${tangledConfig}/*.el $out/ 2>/dev/null || true
|
||||||
|
|
||||||
|
# Add instance-specific custom-config.el loader
|
||||||
|
cp ${customConfigEl} $out/custom-config.el
|
||||||
|
|
||||||
|
# Add optional overrides.el if provided
|
||||||
|
${if overridesEl != null then "cp ${overridesEl} $out/overrides.el" else ""}
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
org-agenda-api.lib.${system}.mkContainer {
|
||||||
|
customElispFile = "${orgAgendaCustomConfig}/custom-config.el";
|
||||||
|
# Use content-based tag to avoid caching issues
|
||||||
|
tag = "${instanceName}-${orgApiRev}-${dotfilesRev}";
|
||||||
|
};
|
||||||
|
|
||||||
|
configsDir = ./configs;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
inherit mkInstanceContainer;
|
||||||
|
|
||||||
|
# Pre-built instance containers
|
||||||
|
containers = {
|
||||||
|
colonelpanic = mkInstanceContainer "colonelpanic"
|
||||||
|
"${configsDir}/colonelpanic/custom-config.el"
|
||||||
|
null;
|
||||||
|
kat = mkInstanceContainer "kat"
|
||||||
|
"${configsDir}/kat/custom-config.el"
|
||||||
|
null;
|
||||||
|
};
|
||||||
|
}
|
||||||
140
org-agenda-api/deploy.sh
Executable file
140
org-agenda-api/deploy.sh
Executable file
@@ -0,0 +1,140 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Deploy customized org-agenda-api container to Fly.io
|
||||||
|
# Usage: ./deploy.sh <instance> [flyctl deploy args...]
|
||||||
|
# Example: ./deploy.sh colonelpanic
|
||||||
|
# ./deploy.sh kat
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
NIXOS_DIR="$SCRIPT_DIR/../nixos"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
# Parse instance argument
|
||||||
|
INSTANCE="${1:-}"
|
||||||
|
if [[ -z "$INSTANCE" ]]; then
|
||||||
|
echo "Usage: $0 <instance> [flyctl deploy args...]"
|
||||||
|
echo "Available instances:"
|
||||||
|
for dir in configs/*/; do
|
||||||
|
echo " - $(basename "$dir")"
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
|
||||||
|
CONFIG_DIR="$SCRIPT_DIR/configs/$INSTANCE"
|
||||||
|
if [[ ! -d "$CONFIG_DIR" ]]; then
|
||||||
|
echo "Error: Instance '$INSTANCE' not found in configs/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Source instance configuration
|
||||||
|
if [[ -f "$CONFIG_DIR/config.env" ]]; then
|
||||||
|
source "$CONFIG_DIR/config.env"
|
||||||
|
else
|
||||||
|
echo "Error: $CONFIG_DIR/config.env not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Deploying instance: $INSTANCE"
|
||||||
|
echo " Fly app: $FLY_APP"
|
||||||
|
|
||||||
|
# Check for uncommitted changes
|
||||||
|
if [[ -n "$(git status --porcelain)" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "WARNING: Working directory has uncommitted changes!"
|
||||||
|
echo "For reproducibility, consider committing before deploying."
|
||||||
|
echo ""
|
||||||
|
read -p "Continue anyway? [y/N] " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get input revisions for reproducibility from the nixos flake
|
||||||
|
ORG_API_NODE=$(jq -r '.nodes.root.inputs."org-agenda-api"' "$NIXOS_DIR/flake.lock")
|
||||||
|
ORG_API_REV=$(jq -r ".nodes.\"$ORG_API_NODE\".locked.rev" "$NIXOS_DIR/flake.lock")
|
||||||
|
# For dotfiles rev, use the current git commit since we're building from local
|
||||||
|
DOTFILES_REV=$(git -C "$NIXOS_DIR/.." rev-parse HEAD)
|
||||||
|
|
||||||
|
SHORT_DOTFILES="${DOTFILES_REV:0:7}"
|
||||||
|
SHORT_ORG_API="${ORG_API_REV:0:7}"
|
||||||
|
|
||||||
|
echo "Versions:"
|
||||||
|
echo " org-agenda-api: $ORG_API_REV"
|
||||||
|
echo " dotfiles: $DOTFILES_REV"
|
||||||
|
|
||||||
|
# Build container from nixos flake for this instance
|
||||||
|
# Use --refresh to ensure we're not using stale cached builds
|
||||||
|
echo "Building container from flake..."
|
||||||
|
nix build "$NIXOS_DIR#container-$INSTANCE" -o "result-container-$INSTANCE" --refresh
|
||||||
|
|
||||||
|
# Load into Docker
|
||||||
|
echo "Loading container into Docker..."
|
||||||
|
LOADED_IMAGE=$(docker load < "result-container-$INSTANCE" 2>&1 | grep -oP 'Loaded image: \K.*')
|
||||||
|
echo "Loaded: $LOADED_IMAGE"
|
||||||
|
|
||||||
|
# Tag with both versions for full reproducibility
|
||||||
|
# Format: api-<org-api-rev>-cfg-<dotfiles-rev>
|
||||||
|
IMAGE_TAG="api-${SHORT_ORG_API}-cfg-${SHORT_DOTFILES}"
|
||||||
|
IMAGE_NAME="registry.fly.io/$FLY_APP:$IMAGE_TAG"
|
||||||
|
echo "Tagging as $IMAGE_NAME..."
|
||||||
|
docker tag "$LOADED_IMAGE" "$IMAGE_NAME"
|
||||||
|
|
||||||
|
echo "Pushing to Fly.io registry..."
|
||||||
|
flyctl auth docker
|
||||||
|
docker push "$IMAGE_NAME"
|
||||||
|
|
||||||
|
# Decrypt secrets
|
||||||
|
echo "Decrypting secrets..."
|
||||||
|
|
||||||
|
IDENTITY=""
|
||||||
|
for key_type in ed25519 rsa; do
|
||||||
|
if [[ -f "$HOME/.ssh/id_${key_type}" ]]; then
|
||||||
|
IDENTITY="$HOME/.ssh/id_${key_type}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "$IDENTITY" ]]; then
|
||||||
|
echo "Error: No SSH identity found" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
GIT_SSH_KEY=$(age -d -i "$IDENTITY" "$CONFIG_DIR/secrets/git-ssh-key.age")
|
||||||
|
AUTH_PASSWORD=$(age -d -i "$IDENTITY" "$CONFIG_DIR/secrets/auth-password.age")
|
||||||
|
|
||||||
|
echo "Setting Fly.io secrets..."
|
||||||
|
|
||||||
|
SECRET_ARGS=(
|
||||||
|
"GIT_SSH_PRIVATE_KEY=$GIT_SSH_KEY"
|
||||||
|
"AUTH_USER=$AUTH_USER"
|
||||||
|
"AUTH_PASSWORD=$AUTH_PASSWORD"
|
||||||
|
"GIT_USER_EMAIL=$GIT_USER_EMAIL"
|
||||||
|
"GIT_USER_NAME=$GIT_USER_NAME"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Use GIT_SYNC_REPOSITORIES (multi-repo) or GIT_SYNC_REPOSITORY (single repo)
|
||||||
|
if [[ -n "${GIT_SYNC_REPOSITORIES:-}" ]]; then
|
||||||
|
SECRET_ARGS+=("GIT_SYNC_REPOSITORIES=$GIT_SYNC_REPOSITORIES")
|
||||||
|
elif [[ -n "${GIT_SYNC_REPOSITORY:-}" ]]; then
|
||||||
|
SECRET_ARGS+=("GIT_SYNC_REPOSITORY=$GIT_SYNC_REPOSITORY")
|
||||||
|
else
|
||||||
|
echo "Error: Neither GIT_SYNC_REPOSITORIES nor GIT_SYNC_REPOSITORY set in config.env"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
flyctl secrets set "${SECRET_ARGS[@]}" --stage -a "$FLY_APP"
|
||||||
|
|
||||||
|
echo "Deploying $IMAGE_NAME..."
|
||||||
|
flyctl deploy --image "$IMAGE_NAME" -c "$CONFIG_DIR/fly.toml" "$@"
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
rm -f "result-container-$INSTANCE"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Done! Deployed to $FLY_APP"
|
||||||
|
echo " Image: $IMAGE_NAME"
|
||||||
|
echo " org-agenda-api: $ORG_API_REV"
|
||||||
|
echo " dotfiles: $DOTFILES_REV"
|
||||||
35
org-agenda-api/justfile
Normal file
35
org-agenda-api/justfile
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# org-agenda-api commands
|
||||||
|
|
||||||
|
base_url := "https://colonelpanic-org-agenda.fly.dev"
|
||||||
|
user := "imalison"
|
||||||
|
|
||||||
|
# Get all todos
|
||||||
|
get-all-todos:
|
||||||
|
@curl -s -u "{{user}}:$(pass show org-agenda-api/imalison | head -1)" "{{base_url}}/get-all-todos" | jq .
|
||||||
|
|
||||||
|
# Get today's agenda
|
||||||
|
get-todays-agenda:
|
||||||
|
@curl -s -u "{{user}}:$(pass show org-agenda-api/imalison | head -1)" "{{base_url}}/get-todays-agenda" | jq .
|
||||||
|
|
||||||
|
# Get agenda (day view)
|
||||||
|
agenda:
|
||||||
|
@curl -s -u "{{user}}:$(pass show org-agenda-api/imalison | head -1)" "{{base_url}}/agenda" | jq .
|
||||||
|
|
||||||
|
# Get agenda files
|
||||||
|
agenda-files:
|
||||||
|
@curl -s -u "{{user}}:$(pass show org-agenda-api/imalison | head -1)" "{{base_url}}/agenda-files" | jq .
|
||||||
|
|
||||||
|
# Get todo states
|
||||||
|
todo-states:
|
||||||
|
@curl -s -u "{{user}}:$(pass show org-agenda-api/imalison | head -1)" "{{base_url}}/todo-states" | jq .
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
health:
|
||||||
|
@curl -s "{{base_url}}/health" | jq .
|
||||||
|
|
||||||
|
# Create a todo
|
||||||
|
create-todo title:
|
||||||
|
@curl -s -X POST -u "{{user}}:$(pass show org-agenda-api/imalison | head -1)" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"title": "{{title}}"}' \
|
||||||
|
"{{base_url}}/create-todo" | jq .
|
||||||
12
org-agenda-api/secrets.nix
Normal file
12
org-agenda-api/secrets.nix
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
let
|
||||||
|
keys = import ../nixos/keys.nix;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# colonelpanic instance
|
||||||
|
"configs/colonelpanic/secrets/git-ssh-key.age".publicKeys = keys.kanivanKeys;
|
||||||
|
"configs/colonelpanic/secrets/auth-password.age".publicKeys = keys.kanivanKeys;
|
||||||
|
|
||||||
|
# kat instance
|
||||||
|
"configs/kat/secrets/git-ssh-key.age".publicKeys = keys.kanivanKeys;
|
||||||
|
"configs/kat/secrets/auth-password.age".publicKeys = keys.kanivanKeys;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user