biucingcli 0.9.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. biucingcli/__init__.py +5 -0
  2. biucingcli/cli.py +849 -0
  3. biucingcli/template_data/android/template/.editorconfig +9 -0
  4. biucingcli/template_data/android/template/.gitignore +26 -0
  5. biucingcli/template_data/android/template/.mise.toml +3 -0
  6. biucingcli/template_data/android/template/Brewfile +6 -0
  7. biucingcli/template_data/android/template/Makefile +103 -0
  8. biucingcli/template_data/android/template/README.md +163 -0
  9. biucingcli/template_data/android/template/app/build.gradle.kts +178 -0
  10. biucingcli/template_data/android/template/app/proguard-rules.pro +1 -0
  11. biucingcli/template_data/android/template/app/src/androidTest/java/app/HomeRouteUiTest.kt +21 -0
  12. biucingcli/template_data/android/template/app/src/main/AndroidManifest.xml +18 -0
  13. biucingcli/template_data/android/template/app/src/main/java/app/MainActivity.kt +36 -0
  14. biucingcli/template_data/android/template/app/src/main/res/values/strings.xml +4 -0
  15. biucingcli/template_data/android/template/app/src/main/res/values/themes.xml +8 -0
  16. biucingcli/template_data/android/template/app/src/test/java/app/AppSmokeTest.kt +19 -0
  17. biucingcli/template_data/android/template/build.gradle.kts +27 -0
  18. biucingcli/template_data/android/template/core/designsystem/build.gradle.kts +33 -0
  19. biucingcli/template_data/android/template/core/designsystem/src/main/java/designsystem/Components.kt +75 -0
  20. biucingcli/template_data/android/template/core/designsystem/src/main/java/designsystem/DesignTokens.kt +23 -0
  21. biucingcli/template_data/android/template/core/designsystem/src/main/java/designsystem/Theme.kt +110 -0
  22. biucingcli/template_data/android/template/core/model/build.gradle.kts +26 -0
  23. biucingcli/template_data/android/template/core/model/src/main/java/model/Greeting.kt +6 -0
  24. biucingcli/template_data/android/template/core/model/src/test/java/model/GreetingTest.kt +16 -0
  25. biucingcli/template_data/android/template/core/network/build.gradle.kts +26 -0
  26. biucingcli/template_data/android/template/core/network/src/main/java/network/AppEnvironment.kt +17 -0
  27. biucingcli/template_data/android/template/core/testing/build.gradle.kts +27 -0
  28. biucingcli/template_data/android/template/core/testing/src/main/java/testing/FakeAppEnvironmentProvider.kt +13 -0
  29. biucingcli/template_data/android/template/docs/release-delivery.md +68 -0
  30. biucingcli/template_data/android/template/docs/release-signing.properties.example +20 -0
  31. biucingcli/template_data/android/template/fastlane/.env.example +22 -0
  32. biucingcli/template_data/android/template/fastlane/Appfile +1 -0
  33. biucingcli/template_data/android/template/fastlane/Fastfile +121 -0
  34. biucingcli/template_data/android/template/feature/home/build.gradle.kts +35 -0
  35. biucingcli/template_data/android/template/feature/home/src/main/java/home/HomeRoute.kt +38 -0
  36. biucingcli/template_data/android/template/feature/settings/build.gradle.kts +34 -0
  37. biucingcli/template_data/android/template/feature/settings/src/main/java/settings/SettingsRoute.kt +35 -0
  38. biucingcli/template_data/android/template/gradle/libs.versions.toml +35 -0
  39. biucingcli/template_data/android/template/gradle/verification-metadata.xml +3462 -0
  40. biucingcli/template_data/android/template/gradle/wrapper/gradle-wrapper.jar +0 -0
  41. biucingcli/template_data/android/template/gradle/wrapper/gradle-wrapper.jar.sha256 +1 -0
  42. biucingcli/template_data/android/template/gradle/wrapper/gradle-wrapper.properties +10 -0
  43. biucingcli/template_data/android/template/gradle.properties +4 -0
  44. biucingcli/template_data/android/template/gradlew +252 -0
  45. biucingcli/template_data/android/template/gradlew.bat +94 -0
  46. biucingcli/template_data/android/template/scripts/artifact-info +11 -0
  47. biucingcli/template_data/android/template/scripts/bootstrap +17 -0
  48. biucingcli/template_data/android/template/scripts/doctor +133 -0
  49. biucingcli/template_data/android/template/scripts/setup-android-sdk +23 -0
  50. biucingcli/template_data/android/template/scripts/sync-gradle-wrapper +19 -0
  51. biucingcli/template_data/android/template/scripts/verify-gradle-supply-chain +61 -0
  52. biucingcli/template_data/android/template/scripts/verify-release-artifact +104 -0
  53. biucingcli/template_data/android/template/settings.gradle.kts +25 -0
  54. biucingcli/template_data/android/template.json +146 -0
  55. biucingcli/template_data/apple/template/.gitignore +28 -0
  56. biucingcli/template_data/apple/template/.mise.toml +4 -0
  57. biucingcli/template_data/apple/template/.swiftformat +10 -0
  58. biucingcli/template_data/apple/template/.swiftlint.yml +33 -0
  59. biucingcli/template_data/apple/template/App/Config/ProjectDescriptionHelpers/Module.swift +6 -0
  60. biucingcli/template_data/apple/template/App/Config/XCConfig/Base.xcconfig +4 -0
  61. biucingcli/template_data/apple/template/App/Project.swift +66 -0
  62. biucingcli/template_data/apple/template/App/Targets/App/Resources/.gitkeep +1 -0
  63. biucingcli/template_data/apple/template/App/Targets/App/Sources/AppEntry.swift +9 -0
  64. biucingcli/template_data/apple/template/App/Targets/App/Sources/HomeView.swift +18 -0
  65. biucingcli/template_data/apple/template/App/Targets/App/Sources/HomeViewModel.swift +38 -0
  66. biucingcli/template_data/apple/template/App/Targets/AppTests/Sources/AppTests.swift +59 -0
  67. biucingcli/template_data/apple/template/Brewfile +8 -0
  68. biucingcli/template_data/apple/template/Makefile +128 -0
  69. biucingcli/template_data/apple/template/Packages/AppServices/Package.swift +20 -0
  70. biucingcli/template_data/apple/template/Packages/AppServices/Sources/AppServices/StarterMetadata.swift +52 -0
  71. biucingcli/template_data/apple/template/Packages/DesignSystem/Package.swift +20 -0
  72. biucingcli/template_data/apple/template/Packages/DesignSystem/Sources/DesignSystem/Theme.swift +8 -0
  73. biucingcli/template_data/apple/template/README.md +133 -0
  74. biucingcli/template_data/apple/template/Tuist.swift +8 -0
  75. biucingcli/template_data/apple/template/Workspace.swift +11 -0
  76. biucingcli/template_data/apple/template/docs/release-delivery.md +55 -0
  77. biucingcli/template_data/apple/template/fastlane/.env.example +20 -0
  78. biucingcli/template_data/apple/template/fastlane/Appfile +3 -0
  79. biucingcli/template_data/apple/template/fastlane/Fastfile +126 -0
  80. biucingcli/template_data/apple/template/fastlane/Matchfile +7 -0
  81. biucingcli/template_data/apple/template/scripts/bootstrap +9 -0
  82. biucingcli/template_data/apple/template/scripts/doctor +163 -0
  83. biucingcli/template_data/apple/template/scripts/setup-xcode +6 -0
  84. biucingcli/template_data/apple/template/scripts/test-destination +42 -0
  85. biucingcli/template_data/apple/template/scripts/verify-release-identity +88 -0
  86. biucingcli/template_data/apple/template.json +110 -0
  87. biucingcli/template_data/frontend/template/.dockerignore +8 -0
  88. biucingcli/template_data/frontend/template/.env.example +1 -0
  89. biucingcli/template_data/frontend/template/.gitignore +7 -0
  90. biucingcli/template_data/frontend/template/.npmrc +1 -0
  91. biucingcli/template_data/frontend/template/.prettierignore +2 -0
  92. biucingcli/template_data/frontend/template/.prettierrc.json +4 -0
  93. biucingcli/template_data/frontend/template/Dockerfile +25 -0
  94. biucingcli/template_data/frontend/template/Dockerfile.dev +35 -0
  95. biucingcli/template_data/frontend/template/Dockerfile.dev.full +40 -0
  96. biucingcli/template_data/frontend/template/Makefile +137 -0
  97. biucingcli/template_data/frontend/template/README.md +146 -0
  98. biucingcli/template_data/frontend/template/compose.dev.yaml +29 -0
  99. biucingcli/template_data/frontend/template/eslint.config.js +27 -0
  100. biucingcli/template_data/frontend/template/index.html +12 -0
  101. biucingcli/template_data/frontend/template/nginx.conf +11 -0
  102. biucingcli/template_data/frontend/template/package.json +50 -0
  103. biucingcli/template_data/frontend/template/playwright.production.config.ts +21 -0
  104. biucingcli/template_data/frontend/template/playwright.smoke.config.ts +19 -0
  105. biucingcli/template_data/frontend/template/pnpm-lock.yaml +4054 -0
  106. biucingcli/template_data/frontend/template/public/.gitkeep +1 -0
  107. biucingcli/template_data/frontend/template/scripts/browser-smoke-production +55 -0
  108. biucingcli/template_data/frontend/template/scripts/doctor +46 -0
  109. biucingcli/template_data/frontend/template/src/App.test.tsx +18 -0
  110. biucingcli/template_data/frontend/template/src/App.tsx +5 -0
  111. biucingcli/template_data/frontend/template/src/components/MetricCard.tsx +14 -0
  112. biucingcli/template_data/frontend/template/src/components/TaskPanel.tsx +32 -0
  113. biucingcli/template_data/frontend/template/src/config/env.ts +3 -0
  114. biucingcli/template_data/frontend/template/src/hooks/useProjectOverview.ts +24 -0
  115. biucingcli/template_data/frontend/template/src/index.css +208 -0
  116. biucingcli/template_data/frontend/template/src/main.tsx +11 -0
  117. biucingcli/template_data/frontend/template/src/pages/HomePage.tsx +60 -0
  118. biucingcli/template_data/frontend/template/src/router/AppRouter.tsx +14 -0
  119. biucingcli/template_data/frontend/template/src/services/api/client.ts +33 -0
  120. biucingcli/template_data/frontend/template/src/services/projectOverview.ts +39 -0
  121. biucingcli/template_data/frontend/template/src/test/setup.ts +1 -0
  122. biucingcli/template_data/frontend/template/src/types/projectOverview.ts +22 -0
  123. biucingcli/template_data/frontend/template/src/vite-env.d.ts +9 -0
  124. biucingcli/template_data/frontend/template/tests/browser-smoke.spec.ts +16 -0
  125. biucingcli/template_data/frontend/template/tests/production-browser-smoke.spec.ts +22 -0
  126. biucingcli/template_data/frontend/template/tsconfig.json +21 -0
  127. biucingcli/template_data/frontend/template/vite.config.ts +12 -0
  128. biucingcli/template_data/frontend/template.json +81 -0
  129. biucingcli/template_data/harmonyos/template/.gitignore +21 -0
  130. biucingcli/template_data/harmonyos/template/.mise.toml +2 -0
  131. biucingcli/template_data/harmonyos/template/AppScope/app.json5 +10 -0
  132. biucingcli/template_data/harmonyos/template/AppScope/resources/base/element/string.json +8 -0
  133. biucingcli/template_data/harmonyos/template/AppScope/resources/base/media/background.svg +3 -0
  134. biucingcli/template_data/harmonyos/template/AppScope/resources/base/media/foreground.svg +4 -0
  135. biucingcli/template_data/harmonyos/template/AppScope/resources/base/media/layered_image.json +6 -0
  136. biucingcli/template_data/harmonyos/template/Makefile +106 -0
  137. biucingcli/template_data/harmonyos/template/README.md +152 -0
  138. biucingcli/template_data/harmonyos/template/build-profile.json5 +36 -0
  139. biucingcli/template_data/harmonyos/template/code-linter.json5 +12 -0
  140. biucingcli/template_data/harmonyos/template/docs/release-signing.local.properties.example +14 -0
  141. biucingcli/template_data/harmonyos/template/entry/build-profile.json5 +9 -0
  142. biucingcli/template_data/harmonyos/template/entry/hvigorfile.ts +6 -0
  143. biucingcli/template_data/harmonyos/template/entry/oh-package.json5 +9 -0
  144. biucingcli/template_data/harmonyos/template/entry/src/main/ets/core/config/AppConfig.ets +10 -0
  145. biucingcli/template_data/harmonyos/template/entry/src/main/ets/core/designsystem/Tokens.ets +28 -0
  146. biucingcli/template_data/harmonyos/template/entry/src/main/ets/entryability/EntryAbility.ets +29 -0
  147. biucingcli/template_data/harmonyos/template/entry/src/main/ets/pages/Index.ets +47 -0
  148. biucingcli/template_data/harmonyos/template/entry/src/main/ets/pages/Settings.ets +96 -0
  149. biucingcli/template_data/harmonyos/template/entry/src/main/module.json5 +34 -0
  150. biucingcli/template_data/harmonyos/template/entry/src/main/resources/base/element/color.json +24 -0
  151. biucingcli/template_data/harmonyos/template/entry/src/main/resources/base/element/string.json +12 -0
  152. biucingcli/template_data/harmonyos/template/entry/src/main/resources/base/media/app_icon.svg +5 -0
  153. biucingcli/template_data/harmonyos/template/entry/src/main/resources/base/profile/main_pages.json +6 -0
  154. biucingcli/template_data/harmonyos/template/entry/src/test/List.test.ets +11 -0
  155. biucingcli/template_data/harmonyos/template/hvigor/hvigor-config.json5 +8 -0
  156. biucingcli/template_data/harmonyos/template/hvigorfile.ts +6 -0
  157. biucingcli/template_data/harmonyos/template/oh-package-lock.json5 +19 -0
  158. biucingcli/template_data/harmonyos/template/oh-package.json5 +13 -0
  159. biucingcli/template_data/harmonyos/template/scripts/artifact-info +31 -0
  160. biucingcli/template_data/harmonyos/template/scripts/bootstrap +38 -0
  161. biucingcli/template_data/harmonyos/template/scripts/doctor +147 -0
  162. biucingcli/template_data/harmonyos/template/scripts/lint +105 -0
  163. biucingcli/template_data/harmonyos/template/scripts/release-build +199 -0
  164. biucingcli/template_data/harmonyos/template/scripts/release-preflight +109 -0
  165. biucingcli/template_data/harmonyos/template/scripts/test +28 -0
  166. biucingcli/template_data/harmonyos/template/scripts/verify-release-artifact +173 -0
  167. biucingcli/template_data/harmonyos/template/scripts/verify-supply-chain +70 -0
  168. biucingcli/template_data/harmonyos/template.json +134 -0
  169. biucingcli/template_data/microservice/template/.air.toml +11 -0
  170. biucingcli/template_data/microservice/template/.dockerignore +5 -0
  171. biucingcli/template_data/microservice/template/.gitignore +4 -0
  172. biucingcli/template_data/microservice/template/.mise.toml +3 -0
  173. biucingcli/template_data/microservice/template/Brewfile +7 -0
  174. biucingcli/template_data/microservice/template/Dockerfile +44 -0
  175. biucingcli/template_data/microservice/template/Dockerfile.dev +50 -0
  176. biucingcli/template_data/microservice/template/Makefile +145 -0
  177. biucingcli/template_data/microservice/template/README.md +191 -0
  178. biucingcli/template_data/microservice/template/api/buf.gen.yaml +11 -0
  179. biucingcli/template_data/microservice/template/api/buf.yaml +11 -0
  180. biucingcli/template_data/microservice/template/api/proto/service/v1/service.proto +18 -0
  181. biucingcli/template_data/microservice/template/cmd/server/main.go +81 -0
  182. biucingcli/template_data/microservice/template/compose.dev.yaml +49 -0
  183. biucingcli/template_data/microservice/template/configs/config.yaml +18 -0
  184. biucingcli/template_data/microservice/template/deploy/compose.yaml +35 -0
  185. biucingcli/template_data/microservice/template/deploy/otel-collector.yaml +15 -0
  186. biucingcli/template_data/microservice/template/go.mod +53 -0
  187. biucingcli/template_data/microservice/template/go.sum +138 -0
  188. biucingcli/template_data/microservice/template/internal/config/config.go +112 -0
  189. biucingcli/template_data/microservice/template/internal/config/config_test.go +92 -0
  190. biucingcli/template_data/microservice/template/internal/handler/health.go +25 -0
  191. biucingcli/template_data/microservice/template/internal/handler/ping.go +25 -0
  192. biucingcli/template_data/microservice/template/internal/model/health.go +8 -0
  193. biucingcli/template_data/microservice/template/internal/model/ping.go +7 -0
  194. biucingcli/template_data/microservice/template/internal/repository/.gitkeep +1 -0
  195. biucingcli/template_data/microservice/template/internal/router/router.go +26 -0
  196. biucingcli/template_data/microservice/template/internal/runtime/server.go +116 -0
  197. biucingcli/template_data/microservice/template/internal/runtime/server_test.go +161 -0
  198. biucingcli/template_data/microservice/template/internal/service/health.go +26 -0
  199. biucingcli/template_data/microservice/template/internal/service/ping.go +23 -0
  200. biucingcli/template_data/microservice/template/internal/telemetry/telemetry.go +56 -0
  201. biucingcli/template_data/microservice/template/internal/transport/grpc.go +51 -0
  202. biucingcli/template_data/microservice/template/internal/transport/ping.go +28 -0
  203. biucingcli/template_data/microservice/template/scripts/bootstrap +29 -0
  204. biucingcli/template_data/microservice/template/scripts/doctor +115 -0
  205. biucingcli/template_data/microservice/template/tests/server_test.go +153 -0
  206. biucingcli/template_data/microservice/template.json +114 -0
  207. biucingcli/template_data/web-service/template/.air.toml +11 -0
  208. biucingcli/template_data/web-service/template/.dockerignore +5 -0
  209. biucingcli/template_data/web-service/template/.gitignore +5 -0
  210. biucingcli/template_data/web-service/template/.mise.toml +2 -0
  211. biucingcli/template_data/web-service/template/Brewfile +5 -0
  212. biucingcli/template_data/web-service/template/Dockerfile +41 -0
  213. biucingcli/template_data/web-service/template/Dockerfile.dev +45 -0
  214. biucingcli/template_data/web-service/template/Makefile +113 -0
  215. biucingcli/template_data/web-service/template/README.md +164 -0
  216. biucingcli/template_data/web-service/template/cmd/server/main.go +50 -0
  217. biucingcli/template_data/web-service/template/compose.dev.yaml +28 -0
  218. biucingcli/template_data/web-service/template/compose.yaml +14 -0
  219. biucingcli/template_data/web-service/template/configs/config.yaml +10 -0
  220. biucingcli/template_data/web-service/template/go.mod +36 -0
  221. biucingcli/template_data/web-service/template/go.sum +89 -0
  222. biucingcli/template_data/web-service/template/internal/config/config.go +84 -0
  223. biucingcli/template_data/web-service/template/internal/config/config_test.go +88 -0
  224. biucingcli/template_data/web-service/template/internal/handler/health.go +25 -0
  225. biucingcli/template_data/web-service/template/internal/handler/ping.go +25 -0
  226. biucingcli/template_data/web-service/template/internal/handler/user.go +40 -0
  227. biucingcli/template_data/web-service/template/internal/model/health.go +6 -0
  228. biucingcli/template_data/web-service/template/internal/model/ping.go +7 -0
  229. biucingcli/template_data/web-service/template/internal/model/user.go +7 -0
  230. biucingcli/template_data/web-service/template/internal/repository/.gitkeep +1 -0
  231. biucingcli/template_data/web-service/template/internal/repository/user.go +37 -0
  232. biucingcli/template_data/web-service/template/internal/router/router.go +28 -0
  233. biucingcli/template_data/web-service/template/internal/runtime/server.go +76 -0
  234. biucingcli/template_data/web-service/template/internal/runtime/server_test.go +139 -0
  235. biucingcli/template_data/web-service/template/internal/service/health.go +22 -0
  236. biucingcli/template_data/web-service/template/internal/service/ping.go +23 -0
  237. biucingcli/template_data/web-service/template/internal/service/user.go +27 -0
  238. biucingcli/template_data/web-service/template/scripts/bootstrap +23 -0
  239. biucingcli/template_data/web-service/template/scripts/doctor +83 -0
  240. biucingcli/template_data/web-service/template/tests/server_test.go +160 -0
  241. biucingcli/template_data/web-service/template.json +88 -0
  242. biucingcli/template_data/worker/template/.dockerignore +3 -0
  243. biucingcli/template_data/worker/template/.gitignore +2 -0
  244. biucingcli/template_data/worker/template/.mise.toml +2 -0
  245. biucingcli/template_data/worker/template/Brewfile +1 -0
  246. biucingcli/template_data/worker/template/Dockerfile +19 -0
  247. biucingcli/template_data/worker/template/Dockerfile.dev +9 -0
  248. biucingcli/template_data/worker/template/Makefile +105 -0
  249. biucingcli/template_data/worker/template/README.md +164 -0
  250. biucingcli/template_data/worker/template/cmd/worker/main.go +41 -0
  251. biucingcli/template_data/worker/template/compose.dev.yaml +29 -0
  252. biucingcli/template_data/worker/template/configs/config.json +11 -0
  253. biucingcli/template_data/worker/template/go.mod +3 -0
  254. biucingcli/template_data/worker/template/go.sum +1 -0
  255. biucingcli/template_data/worker/template/internal/config/config.go +108 -0
  256. biucingcli/template_data/worker/template/internal/config/config_test.go +86 -0
  257. biucingcli/template_data/worker/template/internal/runtime/runner.go +199 -0
  258. biucingcli/template_data/worker/template/internal/runtime/runner_test.go +215 -0
  259. biucingcli/template_data/worker/template/internal/task/heartbeat.go +19 -0
  260. biucingcli/template_data/worker/template/scripts/bootstrap +6 -0
  261. biucingcli/template_data/worker/template/scripts/doctor +41 -0
  262. biucingcli/template_data/worker/template/tests/worker_test.go +44 -0
  263. biucingcli/template_data/worker/template.json +100 -0
  264. biucingcli/templates.py +902 -0
  265. biucingcli-0.9.1.dist-info/METADATA +204 -0
  266. biucingcli-0.9.1.dist-info/RECORD +269 -0
  267. biucingcli-0.9.1.dist-info/WHEEL +5 -0
  268. biucingcli-0.9.1.dist-info/entry_points.txt +2 -0
  269. biucingcli-0.9.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,105 @@
1
+ APP_NAME={{WORKER_NAME}}
2
+ WORKTREE_ROOT ?=$(shell git rev-parse --show-toplevel 2>/dev/null || pwd)
3
+ WORKTREE_LABEL ?=$(shell basename "$(WORKTREE_ROOT)" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-' | sed 's/^-//; s/-$$//')
4
+ WORKTREE_ID ?=$(shell printf '%s' "$(WORKTREE_ROOT)" | shasum | cut -c1-8)
5
+ WORKTREE_SLUG ?=$(APP_NAME)-$(WORKTREE_ID)
6
+ COMPOSE_PROJECT_NAME ?=$(WORKTREE_SLUG)
7
+ IMAGE ?=$(WORKTREE_SLUG)
8
+ TAG ?=dev
9
+ IMAGE_REF := $(IMAGE):$(TAG)
10
+ DEV_IMAGE ?=$(WORKTREE_SLUG)-dev
11
+ DEV_TAG ?=dev
12
+ DEV_IMAGE_REF := $(DEV_IMAGE):$(DEV_TAG)
13
+ COMPOSE ?=COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) docker compose
14
+ DEV_COMPOSE_FILE ?=compose.dev.yaml
15
+ DEV_SERVICE ?=app-dev
16
+ GOPROXY ?=https://proxy.golang.org,direct
17
+ GOSUMDB ?=sum.golang.org
18
+ BUILDER_IMAGE ?=golang:1.26-alpine
19
+ RUNTIME_IMAGE ?=alpine:3.20
20
+ WORKER_RUN_MODE ?={{RUN_MODE}}
21
+ WORKER_TICK_INTERVAL_SECONDS ?={{TICK_INTERVAL_SECONDS}}
22
+ WORKER_SHUTDOWN_TIMEOUT_SECONDS ?={{SHUTDOWN_TIMEOUT_SECONDS}}
23
+ WORKER_RETRY_MAX_ATTEMPTS ?=3
24
+ WORKER_RETRY_INITIAL_BACKOFF_SECONDS ?=1
25
+ WORKER_RETRY_MAX_BACKOFF_SECONDS ?=30
26
+ GOCACHE ?= $(CURDIR)/.cache/go-build
27
+ GOMODCACHE ?= $(CURDIR)/.cache/go-mod
28
+ export COMPOSE_PROJECT_NAME
29
+ export DEV_IMAGE
30
+ export DEV_TAG
31
+ export WORKER_RUN_MODE
32
+ export WORKER_TICK_INTERVAL_SECONDS
33
+ export WORKER_SHUTDOWN_TIMEOUT_SECONDS
34
+ export WORKER_RETRY_MAX_ATTEMPTS
35
+ export WORKER_RETRY_INITIAL_BACKOFF_SECONDS
36
+ export WORKER_RETRY_MAX_BACKOFF_SECONDS
37
+ export GOCACHE
38
+ export GOMODCACHE
39
+
40
+ .PHONY: worktree-info worktree-doctor worktree-compose-config clean-worktree bootstrap doctor dev dev-shell lint test verify run build clean help docker-build docker-run
41
+
42
+ worktree-info:
43
+ @printf '%s\n' "Worktree root: $(WORKTREE_ROOT)"
44
+ @printf '%s\n' "Worktree label: $(WORKTREE_LABEL)"
45
+ @printf '%s\n' "Worktree ID: $(WORKTREE_ID)"
46
+ @printf '%s\n' "Worktree slug: $(WORKTREE_SLUG)"
47
+ @printf '%s\n' "Compose project: $(COMPOSE_PROJECT_NAME)"
48
+ @printf '%s\n' "Dev image ref: $(DEV_IMAGE_REF)"
49
+ @printf '%s\n' "Runtime image ref: $(IMAGE_REF)"
50
+ @printf '%s\n' "Go cache: $(GOCACHE)"
51
+ @printf '%s\n' "Go module cache: $(GOMODCACHE)"
52
+ @printf '%s\n' "Worker run mode: $(WORKER_RUN_MODE)"
53
+ @printf '%s\n' "Worker retry attempts: $(WORKER_RETRY_MAX_ATTEMPTS)"
54
+
55
+ worktree-doctor:
56
+ @$(MAKE) --no-print-directory worktree-info
57
+ @case "$(GOCACHE)" in "$(WORKTREE_ROOT)"/*) ;; *) printf '%s\n' "GOCACHE should stay inside this worktree: $(GOCACHE)"; exit 1;; esac
58
+ @case "$(GOMODCACHE)" in "$(WORKTREE_ROOT)"/*) ;; *) printf '%s\n' "GOMODCACHE should stay inside this worktree: $(GOMODCACHE)"; exit 1;; esac
59
+ @printf '%s\n' "Worker has no published host ports by default."
60
+ @printf '%s\n' "Compose config check: make worktree-compose-config"
61
+
62
+ worktree-compose-config:
63
+ @if ! command -v docker >/dev/null 2>&1; then printf '%s\n' "Docker is not available; cannot render Compose config."; exit 1; fi
64
+ $(COMPOSE) -f $(DEV_COMPOSE_FILE) config
65
+
66
+ clean-worktree:
67
+ $(COMPOSE) -f $(DEV_COMPOSE_FILE) down --remove-orphans --volumes
68
+
69
+ bootstrap:
70
+ GOPROXY=$(GOPROXY) GOSUMDB=$(GOSUMDB) $(COMPOSE) -f $(DEV_COMPOSE_FILE) build $(DEV_SERVICE)
71
+
72
+ doctor:
73
+ $(COMPOSE) -f $(DEV_COMPOSE_FILE) run --rm $(DEV_SERVICE) ./scripts/doctor
74
+
75
+ lint:
76
+ $(COMPOSE) -f $(DEV_COMPOSE_FILE) run --rm $(DEV_SERVICE) go vet ./...
77
+
78
+ dev:
79
+ WORKER_RUN_MODE=$(WORKER_RUN_MODE) WORKER_TICK_INTERVAL_SECONDS=$(WORKER_TICK_INTERVAL_SECONDS) WORKER_SHUTDOWN_TIMEOUT_SECONDS=$(WORKER_SHUTDOWN_TIMEOUT_SECONDS) WORKER_RETRY_MAX_ATTEMPTS=$(WORKER_RETRY_MAX_ATTEMPTS) WORKER_RETRY_INITIAL_BACKOFF_SECONDS=$(WORKER_RETRY_INITIAL_BACKOFF_SECONDS) WORKER_RETRY_MAX_BACKOFF_SECONDS=$(WORKER_RETRY_MAX_BACKOFF_SECONDS) GOPROXY=$(GOPROXY) GOSUMDB=$(GOSUMDB) $(COMPOSE) -f $(DEV_COMPOSE_FILE) up $(DEV_SERVICE)
80
+
81
+ dev-shell:
82
+ $(COMPOSE) -f $(DEV_COMPOSE_FILE) run --rm $(DEV_SERVICE) sh
83
+
84
+ test:
85
+ $(COMPOSE) -f $(DEV_COMPOSE_FILE) run --rm $(DEV_SERVICE) go test ./...
86
+
87
+ verify: doctor lint test build
88
+
89
+ run:
90
+ WORKER_RUN_MODE=$(WORKER_RUN_MODE) WORKER_TICK_INTERVAL_SECONDS=$(WORKER_TICK_INTERVAL_SECONDS) WORKER_SHUTDOWN_TIMEOUT_SECONDS=$(WORKER_SHUTDOWN_TIMEOUT_SECONDS) WORKER_RETRY_MAX_ATTEMPTS=$(WORKER_RETRY_MAX_ATTEMPTS) WORKER_RETRY_INITIAL_BACKOFF_SECONDS=$(WORKER_RETRY_INITIAL_BACKOFF_SECONDS) WORKER_RETRY_MAX_BACKOFF_SECONDS=$(WORKER_RETRY_MAX_BACKOFF_SECONDS) $(COMPOSE) -f $(DEV_COMPOSE_FILE) run --rm $(DEV_SERVICE)
91
+
92
+ build:
93
+ $(COMPOSE) -f $(DEV_COMPOSE_FILE) run --rm $(DEV_SERVICE) go build -o bin/$(APP_NAME) ./cmd/worker
94
+
95
+ clean:
96
+ $(COMPOSE) -f $(DEV_COMPOSE_FILE) down --remove-orphans
97
+
98
+ help:
99
+ @printf '%s\n' "Common commands:" " make bootstrap Prepare the development environment" " make doctor Check required tools and project configuration" " make lint Run static analysis" " make test Run tests" " make verify Run the complete local verification contract" " make build Build the application" " make clean Remove generated runtime state" " make help Show this command summary"
100
+
101
+ docker-build:
102
+ docker build --build-arg BUILDER_IMAGE=$(BUILDER_IMAGE) --build-arg RUNTIME_IMAGE=$(RUNTIME_IMAGE) --build-arg GOPROXY=$(GOPROXY) --build-arg GOSUMDB=$(GOSUMDB) -t $(IMAGE_REF) .
103
+
104
+ docker-run:
105
+ docker run --rm -e WORKER_RUN_MODE=$(WORKER_RUN_MODE) -e WORKER_TICK_INTERVAL_SECONDS=$(WORKER_TICK_INTERVAL_SECONDS) -e WORKER_SHUTDOWN_TIMEOUT_SECONDS=$(WORKER_SHUTDOWN_TIMEOUT_SECONDS) -e WORKER_RETRY_MAX_ATTEMPTS=$(WORKER_RETRY_MAX_ATTEMPTS) -e WORKER_RETRY_INITIAL_BACKOFF_SECONDS=$(WORKER_RETRY_INITIAL_BACKOFF_SECONDS) -e WORKER_RETRY_MAX_BACKOFF_SECONDS=$(WORKER_RETRY_MAX_BACKOFF_SECONDS) $(IMAGE_REF)
@@ -0,0 +1,164 @@
1
+ # {{WORKER_NAME}}
2
+
3
+ This project was generated by BiucingCLI.
4
+
5
+ ## Stack
6
+
7
+ - Go
8
+ - Docker
9
+
10
+ ## Project Layout
11
+
12
+ - `cmd/worker`: background process entrypoint
13
+ - `internal/config`: config loading and environment overrides
14
+ - `internal/runtime`: execution loop and graceful shutdown behavior
15
+ - `internal/task`: starter task implementation
16
+ - `configs`: local config files
17
+ - `tests`: generated-project runtime tests
18
+
19
+ ## Worker Shape
20
+
21
+ This starter is intentionally narrow:
22
+
23
+ - it is designed for background execution rather than a public HTTP API;
24
+ - it supports `scheduled` and `oneshot` execution modes;
25
+ - it keeps operational basics such as config loading, logging, and graceful shutdown explicit.
26
+
27
+ The default config file is `configs/config.json`.
28
+
29
+ ## Docker Workflow
30
+
31
+ ```bash
32
+ make bootstrap
33
+ make dev
34
+ ```
35
+
36
+ The starter is designed to run development, checks, and runtime packaging inside Docker by default.
37
+
38
+ ## Available Commands
39
+
40
+ ```bash
41
+ make bootstrap
42
+ make dev
43
+ make dev-shell
44
+ make doctor
45
+ make lint
46
+ make test
47
+ make verify
48
+ make build
49
+ make docker-build
50
+ make docker-run
51
+ make clean
52
+ make help
53
+ ```
54
+
55
+ ## Execution Modes
56
+
57
+ - `scheduled`: runs the starter task immediately and then on a repeating interval
58
+ - `oneshot`: runs the starter task once and exits
59
+
60
+ Each execution cycle has explicit retry semantics:
61
+
62
+ - `retry_max_attempts` includes the initial attempt; set it to `1` to disable retries.
63
+ - failed attempts use exponential backoff from `retry_initial_backoff_seconds`, capped by `retry_max_backoff_seconds`.
64
+ - `oneshot` returns the final error after retries are exhausted, so the process exits non-zero.
65
+ - `scheduled` logs an exhausted cycle, waits one `tick_interval_seconds` fixed delay, and starts the next cycle.
66
+ - cancellation interrupts scheduled waits and retry backoff immediately. An active task receives a canceled context and has `shutdown_timeout_seconds` to return before shutdown fails.
67
+
68
+ The generated defaults are:
69
+
70
+ - `run_mode={{RUN_MODE}}`
71
+ - `tick_interval_seconds={{TICK_INTERVAL_SECONDS}}`
72
+ - `shutdown_timeout_seconds={{SHUTDOWN_TIMEOUT_SECONDS}}`
73
+ - `retry_max_attempts=3`
74
+ - `retry_initial_backoff_seconds=1`
75
+ - `retry_max_backoff_seconds=30`
76
+
77
+ You can override them with environment variables:
78
+
79
+ - `WORKER_NAME`
80
+ - `WORKER_RUN_MODE`
81
+ - `WORKER_TICK_INTERVAL_SECONDS`
82
+ - `WORKER_SHUTDOWN_TIMEOUT_SECONDS`
83
+ - `WORKER_RETRY_MAX_ATTEMPTS`
84
+ - `WORKER_RETRY_INITIAL_BACKOFF_SECONDS`
85
+ - `WORKER_RETRY_MAX_BACKOFF_SECONDS`
86
+ - `CONFIG_FILE`
87
+
88
+ Example oneshot run:
89
+
90
+ ```bash
91
+ WORKER_RUN_MODE=oneshot make run
92
+ ```
93
+
94
+ Example scheduled run with a shorter loop:
95
+
96
+ ```bash
97
+ WORKER_RUN_MODE=scheduled WORKER_TICK_INTERVAL_SECONDS=5 make dev
98
+ ```
99
+
100
+ ## Environment
101
+
102
+ - `Brewfile`: optional host workstation tool installation
103
+ - `.mise.toml`: pinned Go version for host workflows
104
+ - `Dockerfile.dev`: development image used by Docker Compose
105
+ - `compose.dev.yaml`: local Docker development workflow
106
+ - `scripts/bootstrap`: host-oriented setup helper when you do not use Docker
107
+ - `scripts/doctor`: local environment checks
108
+
109
+ ## Runtime Packaging
110
+
111
+ Build the runtime image:
112
+
113
+ ```bash
114
+ make docker-build
115
+ ```
116
+
117
+ Run the runtime image with the default scheduled mode:
118
+
119
+ ```bash
120
+ make docker-run
121
+ ```
122
+
123
+ Run the runtime image once and exit:
124
+
125
+ ```bash
126
+ make docker-run WORKER_RUN_MODE=oneshot
127
+ ```
128
+
129
+ The runtime image defaults are:
130
+
131
+ - `BUILDER_IMAGE=golang:1.26-alpine`
132
+ - `RUNTIME_IMAGE=alpine:3.20`
133
+
134
+ ## Worktree Workflow
135
+
136
+ This starter is safe to run from parallel Git worktrees without publishing host ports by default.
137
+
138
+ ```bash
139
+ make worktree-info
140
+ make worktree-doctor
141
+ make worktree-compose-config
142
+ make dev
143
+ make clean-worktree
144
+ ```
145
+
146
+ - `WORKTREE_LABEL` is derived from the worktree directory name for display.
147
+ - `WORKTREE_ID` is derived from the current worktree path by default and can be overridden.
148
+ - `COMPOSE_PROJECT_NAME` defaults to `{{WORKER_NAME}}-$(WORKTREE_ID)`, so Compose containers, networks, and cache volumes stay scoped to the worktree.
149
+ - `DEV_IMAGE` and `IMAGE` default to worktree-specific names so dev/runtime image tags do not overwrite another worktree by default.
150
+ - `GOCACHE` and `GOMODCACHE` stay under the current worktree's `.cache/` directory.
151
+ - The Docker dev workflow mounts Go build and module caches as Compose-scoped volumes, so dependency versions and downloads do not bleed across worktrees.
152
+ - `make worktree-compose-config` renders the current worktree's Compose config without starting containers.
153
+ - `make clean-worktree` removes only the current worktree's Compose containers, networks, and volumes.
154
+
155
+ ## Validation
156
+
157
+ The generated project includes:
158
+
159
+ - config-loading tests under `internal/config`
160
+ - runtime-loop tests under `internal/runtime`
161
+ - deterministic retry, fixed-delay scheduling, cancellation, and shutdown-timeout tests using an injected clock; they do not sleep to advance worker time
162
+ - a generated-project test under `tests/worker_test.go`
163
+
164
+ `make verify` runs the local doctor checks plus `go test ./...` inside Docker.
@@ -0,0 +1,41 @@
1
+ package main
2
+
3
+ import (
4
+ "context"
5
+ "log"
6
+ "os"
7
+ "os/signal"
8
+ "syscall"
9
+ "time"
10
+
11
+ "{{MODULE_NAME}}/internal/config"
12
+ "{{MODULE_NAME}}/internal/runtime"
13
+ "{{MODULE_NAME}}/internal/task"
14
+ )
15
+
16
+ func main() {
17
+ cfg, err := config.Load()
18
+ if err != nil {
19
+ log.Fatalf("load config: %v", err)
20
+ }
21
+
22
+ ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
23
+ defer stop()
24
+
25
+ logger := log.New(os.Stdout, "", log.LstdFlags)
26
+ runner := runtime.New(runtime.Options{
27
+ Name: cfg.Worker.Name,
28
+ Mode: cfg.Worker.RunMode,
29
+ TickInterval: time.Duration(cfg.Worker.TickIntervalSeconds) * time.Second,
30
+ ShutdownTimeout: time.Duration(cfg.Worker.ShutdownTimeoutSeconds) * time.Second,
31
+ MaxAttempts: cfg.Worker.RetryMaxAttempts,
32
+ InitialBackoff: time.Duration(cfg.Worker.RetryInitialSeconds) * time.Second,
33
+ MaxBackoff: time.Duration(cfg.Worker.RetryMaxSeconds) * time.Second,
34
+ Executor: task.NewHeartbeatTask(cfg.Worker.Name),
35
+ Logger: logger,
36
+ })
37
+
38
+ if err := runner.Run(ctx); err != nil {
39
+ log.Fatalf("run worker: %v", err)
40
+ }
41
+ }
@@ -0,0 +1,29 @@
1
+ services:
2
+ app-dev:
3
+ build:
4
+ context: .
5
+ dockerfile: Dockerfile.dev
6
+ args:
7
+ GOPROXY: ${GOPROXY:-https://proxy.golang.org,direct}
8
+ GOSUMDB: ${GOSUMDB:-sum.golang.org}
9
+ image: ${DEV_IMAGE:-{{WORKER_NAME}}-dev}:${DEV_TAG:-latest}
10
+ working_dir: /app
11
+ environment:
12
+ CONFIG_FILE: configs/config.json
13
+ WORKER_RUN_MODE: ${WORKER_RUN_MODE:-{{RUN_MODE}}}
14
+ WORKER_TICK_INTERVAL_SECONDS: ${WORKER_TICK_INTERVAL_SECONDS:-{{TICK_INTERVAL_SECONDS}}}
15
+ WORKER_SHUTDOWN_TIMEOUT_SECONDS: ${WORKER_SHUTDOWN_TIMEOUT_SECONDS:-{{SHUTDOWN_TIMEOUT_SECONDS}}}
16
+ WORKER_RETRY_MAX_ATTEMPTS: ${WORKER_RETRY_MAX_ATTEMPTS:-3}
17
+ WORKER_RETRY_INITIAL_BACKOFF_SECONDS: ${WORKER_RETRY_INITIAL_BACKOFF_SECONDS:-1}
18
+ WORKER_RETRY_MAX_BACKOFF_SECONDS: ${WORKER_RETRY_MAX_BACKOFF_SECONDS:-30}
19
+ GOCACHE: /cache/go-build
20
+ GOMODCACHE: /cache/go-mod
21
+ volumes:
22
+ - .:/app
23
+ - worker-go-build-cache:/cache/go-build
24
+ - worker-go-mod-cache:/cache/go-mod
25
+ command: sh -lc "go run ./cmd/worker"
26
+
27
+ volumes:
28
+ worker-go-build-cache:
29
+ worker-go-mod-cache:
@@ -0,0 +1,11 @@
1
+ {
2
+ "worker": {
3
+ "name": "{{WORKER_NAME}}",
4
+ "run_mode": "{{RUN_MODE}}",
5
+ "tick_interval_seconds": {{TICK_INTERVAL_SECONDS}},
6
+ "shutdown_timeout_seconds": {{SHUTDOWN_TIMEOUT_SECONDS}},
7
+ "retry_max_attempts": 3,
8
+ "retry_initial_backoff_seconds": 1,
9
+ "retry_max_backoff_seconds": 30
10
+ }
11
+ }
@@ -0,0 +1,3 @@
1
+ module {{MODULE_NAME}}
2
+
3
+ go 1.26.0
@@ -0,0 +1,108 @@
1
+ package config
2
+
3
+ import (
4
+ "encoding/json"
5
+ "errors"
6
+ "os"
7
+ "strconv"
8
+ )
9
+
10
+ const defaultConfigFile = "configs/config.json"
11
+
12
+ type Config struct {
13
+ Worker WorkerConfig `json:"worker"`
14
+ }
15
+
16
+ type WorkerConfig struct {
17
+ Name string `json:"name"`
18
+ RunMode string `json:"run_mode"`
19
+ TickIntervalSeconds int `json:"tick_interval_seconds"`
20
+ ShutdownTimeoutSeconds int `json:"shutdown_timeout_seconds"`
21
+ RetryMaxAttempts int `json:"retry_max_attempts"`
22
+ RetryInitialSeconds int `json:"retry_initial_backoff_seconds"`
23
+ RetryMaxSeconds int `json:"retry_max_backoff_seconds"`
24
+ }
25
+
26
+ func Load() (Config, error) {
27
+ configFile := os.Getenv("CONFIG_FILE")
28
+ if configFile == "" {
29
+ configFile = defaultConfigFile
30
+ }
31
+
32
+ data, err := os.ReadFile(configFile)
33
+ if err != nil {
34
+ return Config{}, err
35
+ }
36
+
37
+ var cfg Config
38
+ if err := json.Unmarshal(data, &cfg); err != nil {
39
+ return Config{}, err
40
+ }
41
+
42
+ applyStringOverride("WORKER_NAME", &cfg.Worker.Name)
43
+ applyStringOverride("WORKER_RUN_MODE", &cfg.Worker.RunMode)
44
+ if err := applyIntOverride("WORKER_TICK_INTERVAL_SECONDS", &cfg.Worker.TickIntervalSeconds); err != nil {
45
+ return Config{}, err
46
+ }
47
+ if err := applyIntOverride("WORKER_SHUTDOWN_TIMEOUT_SECONDS", &cfg.Worker.ShutdownTimeoutSeconds); err != nil {
48
+ return Config{}, err
49
+ }
50
+ if err := applyIntOverride("WORKER_RETRY_MAX_ATTEMPTS", &cfg.Worker.RetryMaxAttempts); err != nil {
51
+ return Config{}, err
52
+ }
53
+ if err := applyIntOverride("WORKER_RETRY_INITIAL_BACKOFF_SECONDS", &cfg.Worker.RetryInitialSeconds); err != nil {
54
+ return Config{}, err
55
+ }
56
+ if err := applyIntOverride("WORKER_RETRY_MAX_BACKOFF_SECONDS", &cfg.Worker.RetryMaxSeconds); err != nil {
57
+ return Config{}, err
58
+ }
59
+
60
+ if cfg.Worker.Name == "" {
61
+ return Config{}, errors.New("worker.name is required")
62
+ }
63
+ if cfg.Worker.RunMode == "" {
64
+ cfg.Worker.RunMode = "{{RUN_MODE}}"
65
+ }
66
+ if cfg.Worker.RunMode != "scheduled" && cfg.Worker.RunMode != "oneshot" {
67
+ return Config{}, errors.New("worker.run_mode must be one of: scheduled, oneshot")
68
+ }
69
+ if cfg.Worker.TickIntervalSeconds <= 0 {
70
+ cfg.Worker.TickIntervalSeconds = {{TICK_INTERVAL_SECONDS}}
71
+ }
72
+ if cfg.Worker.ShutdownTimeoutSeconds <= 0 {
73
+ cfg.Worker.ShutdownTimeoutSeconds = {{SHUTDOWN_TIMEOUT_SECONDS}}
74
+ }
75
+ if cfg.Worker.RetryMaxAttempts <= 0 {
76
+ cfg.Worker.RetryMaxAttempts = 3
77
+ }
78
+ if cfg.Worker.RetryInitialSeconds <= 0 {
79
+ cfg.Worker.RetryInitialSeconds = 1
80
+ }
81
+ if cfg.Worker.RetryMaxSeconds <= 0 {
82
+ cfg.Worker.RetryMaxSeconds = 30
83
+ }
84
+ if cfg.Worker.RetryMaxSeconds < cfg.Worker.RetryInitialSeconds {
85
+ return Config{}, errors.New("worker.retry_max_backoff_seconds must be greater than or equal to worker.retry_initial_backoff_seconds")
86
+ }
87
+
88
+ return cfg, nil
89
+ }
90
+
91
+ func applyStringOverride(envKey string, target *string) {
92
+ if value := os.Getenv(envKey); value != "" {
93
+ *target = value
94
+ }
95
+ }
96
+
97
+ func applyIntOverride(envKey string, target *int) error {
98
+ value := os.Getenv(envKey)
99
+ if value == "" {
100
+ return nil
101
+ }
102
+ parsed, err := strconv.Atoi(value)
103
+ if err != nil {
104
+ return errors.New(envKey + " must be an integer")
105
+ }
106
+ *target = parsed
107
+ return nil
108
+ }
@@ -0,0 +1,86 @@
1
+ package config
2
+
3
+ import (
4
+ "os"
5
+ "path/filepath"
6
+ "testing"
7
+ )
8
+
9
+ func TestLoadDefaults(t *testing.T) {
10
+ tempDir := t.TempDir()
11
+ configPath := filepath.Join(tempDir, "config.json")
12
+ if err := os.WriteFile(configPath, []byte(`{"worker":{"name":"demo-worker","run_mode":"scheduled","tick_interval_seconds":15,"shutdown_timeout_seconds":8}}`), 0o644); err != nil {
13
+ t.Fatal(err)
14
+ }
15
+
16
+ t.Setenv("CONFIG_FILE", configPath)
17
+
18
+ cfg, err := Load()
19
+ if err != nil {
20
+ t.Fatal(err)
21
+ }
22
+
23
+ if cfg.Worker.Name != "demo-worker" {
24
+ t.Fatalf("expected worker name %q, got %q", "demo-worker", cfg.Worker.Name)
25
+ }
26
+ if cfg.Worker.RunMode != "scheduled" {
27
+ t.Fatalf("expected run mode %q, got %q", "scheduled", cfg.Worker.RunMode)
28
+ }
29
+ if cfg.Worker.TickIntervalSeconds != 15 {
30
+ t.Fatalf("expected tick interval %d, got %d", 15, cfg.Worker.TickIntervalSeconds)
31
+ }
32
+ if cfg.Worker.RetryMaxAttempts != 3 || cfg.Worker.RetryInitialSeconds != 1 || cfg.Worker.RetryMaxSeconds != 30 {
33
+ t.Fatalf("unexpected retry defaults: %+v", cfg.Worker)
34
+ }
35
+ }
36
+
37
+ func TestLoadUsesEnvironmentOverrides(t *testing.T) {
38
+ tempDir := t.TempDir()
39
+ configPath := filepath.Join(tempDir, "config.json")
40
+ if err := os.WriteFile(configPath, []byte(`{"worker":{"name":"demo-worker","run_mode":"scheduled","tick_interval_seconds":15,"shutdown_timeout_seconds":8}}`), 0o644); err != nil {
41
+ t.Fatal(err)
42
+ }
43
+
44
+ t.Setenv("CONFIG_FILE", configPath)
45
+ t.Setenv("WORKER_NAME", "override-worker")
46
+ t.Setenv("WORKER_RUN_MODE", "oneshot")
47
+ t.Setenv("WORKER_TICK_INTERVAL_SECONDS", "3")
48
+ t.Setenv("WORKER_SHUTDOWN_TIMEOUT_SECONDS", "2")
49
+ t.Setenv("WORKER_RETRY_MAX_ATTEMPTS", "5")
50
+ t.Setenv("WORKER_RETRY_INITIAL_BACKOFF_SECONDS", "2")
51
+ t.Setenv("WORKER_RETRY_MAX_BACKOFF_SECONDS", "8")
52
+
53
+ cfg, err := Load()
54
+ if err != nil {
55
+ t.Fatal(err)
56
+ }
57
+
58
+ if cfg.Worker.Name != "override-worker" {
59
+ t.Fatalf("expected worker name %q, got %q", "override-worker", cfg.Worker.Name)
60
+ }
61
+ if cfg.Worker.RunMode != "oneshot" {
62
+ t.Fatalf("expected run mode %q, got %q", "oneshot", cfg.Worker.RunMode)
63
+ }
64
+ if cfg.Worker.TickIntervalSeconds != 3 {
65
+ t.Fatalf("expected tick interval %d, got %d", 3, cfg.Worker.TickIntervalSeconds)
66
+ }
67
+ if cfg.Worker.ShutdownTimeoutSeconds != 2 {
68
+ t.Fatalf("expected shutdown timeout %d, got %d", 2, cfg.Worker.ShutdownTimeoutSeconds)
69
+ }
70
+ if cfg.Worker.RetryMaxAttempts != 5 || cfg.Worker.RetryInitialSeconds != 2 || cfg.Worker.RetryMaxSeconds != 8 {
71
+ t.Fatalf("unexpected retry overrides: %+v", cfg.Worker)
72
+ }
73
+ }
74
+
75
+ func TestLoadRejectsRetryBackoffRange(t *testing.T) {
76
+ configPath := filepath.Join(t.TempDir(), "config.json")
77
+ data := []byte(`{"worker":{"name":"demo-worker","retry_initial_backoff_seconds":10,"retry_max_backoff_seconds":2}}`)
78
+ if err := os.WriteFile(configPath, data, 0o644); err != nil {
79
+ t.Fatal(err)
80
+ }
81
+ t.Setenv("CONFIG_FILE", configPath)
82
+
83
+ if _, err := Load(); err == nil {
84
+ t.Fatal("expected retry backoff range validation to fail")
85
+ }
86
+ }