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,199 @@
1
+ package runtime
2
+
3
+ import (
4
+ "context"
5
+ "errors"
6
+ "fmt"
7
+ "io"
8
+ "log"
9
+ "time"
10
+ )
11
+
12
+ var ErrShutdownTimeout = errors.New("worker shutdown timed out")
13
+
14
+ type Executor interface {
15
+ Execute(context.Context) (string, error)
16
+ }
17
+
18
+ type Clock interface {
19
+ After(time.Duration) <-chan time.Time
20
+ }
21
+
22
+ type realClock struct{}
23
+
24
+ func (realClock) After(delay time.Duration) <-chan time.Time {
25
+ return time.After(delay)
26
+ }
27
+
28
+ type Options struct {
29
+ Name string
30
+ Mode string
31
+ TickInterval time.Duration
32
+ ShutdownTimeout time.Duration
33
+ MaxAttempts int
34
+ InitialBackoff time.Duration
35
+ MaxBackoff time.Duration
36
+ Executor Executor
37
+ Logger *log.Logger
38
+ Clock Clock
39
+ }
40
+
41
+ type Runner struct {
42
+ name string
43
+ mode string
44
+ tickInterval time.Duration
45
+ shutdownTimeout time.Duration
46
+ maxAttempts int
47
+ initialBackoff time.Duration
48
+ maxBackoff time.Duration
49
+ executor Executor
50
+ logger *log.Logger
51
+ clock Clock
52
+ }
53
+
54
+ func New(options Options) *Runner {
55
+ logger := options.Logger
56
+ if logger == nil {
57
+ logger = log.New(io.Discard, "", 0)
58
+ }
59
+ clock := options.Clock
60
+ if clock == nil {
61
+ clock = realClock{}
62
+ }
63
+ maxAttempts := options.MaxAttempts
64
+ if maxAttempts <= 0 {
65
+ maxAttempts = 1
66
+ }
67
+ initialBackoff := options.InitialBackoff
68
+ if initialBackoff <= 0 {
69
+ initialBackoff = time.Second
70
+ }
71
+ maxBackoff := options.MaxBackoff
72
+ if maxBackoff < initialBackoff {
73
+ maxBackoff = initialBackoff
74
+ }
75
+ return &Runner{
76
+ name: options.Name,
77
+ mode: options.Mode,
78
+ tickInterval: options.TickInterval,
79
+ shutdownTimeout: options.ShutdownTimeout,
80
+ maxAttempts: maxAttempts,
81
+ initialBackoff: initialBackoff,
82
+ maxBackoff: maxBackoff,
83
+ executor: options.Executor,
84
+ logger: logger,
85
+ clock: clock,
86
+ }
87
+ }
88
+
89
+ func (runner *Runner) Run(ctx context.Context) error {
90
+ if runner.executor == nil {
91
+ return errors.New("executor is required")
92
+ }
93
+
94
+ switch runner.mode {
95
+ case "oneshot":
96
+ err := runner.executeWithRetry(ctx)
97
+ if ctx.Err() != nil && !errors.Is(err, ErrShutdownTimeout) {
98
+ return nil
99
+ }
100
+ return err
101
+ case "scheduled":
102
+ return runner.runScheduled(ctx)
103
+ default:
104
+ return errors.New("unsupported worker mode: " + runner.mode)
105
+ }
106
+ }
107
+
108
+ func (runner *Runner) runScheduled(ctx context.Context) error {
109
+ for {
110
+ if err := runner.executeWithRetry(ctx); err != nil {
111
+ if ctx.Err() != nil {
112
+ if errors.Is(err, ErrShutdownTimeout) {
113
+ return err
114
+ }
115
+ runner.logger.Printf("%s: shutdown requested", runner.name)
116
+ return nil
117
+ }
118
+ runner.logger.Printf("%s: execution cycle failed: %v", runner.name, err)
119
+ }
120
+
121
+ if err := runner.wait(ctx, runner.tickInterval); err != nil {
122
+ runner.logger.Printf("%s: shutdown requested", runner.name)
123
+ return nil
124
+ }
125
+ }
126
+ }
127
+
128
+ func (runner *Runner) executeWithRetry(ctx context.Context) error {
129
+ backoff := runner.initialBackoff
130
+ for attempt := 1; attempt <= runner.maxAttempts; attempt++ {
131
+ summary, err := runner.executeAttempt(ctx)
132
+ if err == nil {
133
+ runner.logger.Printf("%s: %s", runner.name, summary)
134
+ return nil
135
+ }
136
+ if ctx.Err() != nil || attempt == runner.maxAttempts {
137
+ return fmt.Errorf("execution failed after %d attempt(s): %w", attempt, err)
138
+ }
139
+
140
+ runner.logger.Printf(
141
+ "%s: attempt %d/%d failed: %v; retrying in %s",
142
+ runner.name,
143
+ attempt,
144
+ runner.maxAttempts,
145
+ err,
146
+ backoff,
147
+ )
148
+ if err := runner.wait(ctx, backoff); err != nil {
149
+ return err
150
+ }
151
+ backoff = nextBackoff(backoff, runner.maxBackoff)
152
+ }
153
+ return nil
154
+ }
155
+
156
+ func (runner *Runner) executeAttempt(ctx context.Context) (string, error) {
157
+ type result struct {
158
+ summary string
159
+ err error
160
+ }
161
+
162
+ executionCtx, cancelExecution := context.WithCancel(context.WithoutCancel(ctx))
163
+ defer cancelExecution()
164
+ resultChannel := make(chan result, 1)
165
+ go func() {
166
+ summary, err := runner.executor.Execute(executionCtx)
167
+ resultChannel <- result{summary: summary, err: err}
168
+ }()
169
+
170
+ select {
171
+ case executionResult := <-resultChannel:
172
+ return executionResult.summary, executionResult.err
173
+ case <-ctx.Done():
174
+ cancelExecution()
175
+ }
176
+
177
+ select {
178
+ case executionResult := <-resultChannel:
179
+ return executionResult.summary, executionResult.err
180
+ case <-runner.clock.After(runner.shutdownTimeout):
181
+ return "", fmt.Errorf("%w after %s", ErrShutdownTimeout, runner.shutdownTimeout)
182
+ }
183
+ }
184
+
185
+ func (runner *Runner) wait(ctx context.Context, delay time.Duration) error {
186
+ select {
187
+ case <-ctx.Done():
188
+ return ctx.Err()
189
+ case <-runner.clock.After(delay):
190
+ return nil
191
+ }
192
+ }
193
+
194
+ func nextBackoff(current time.Duration, maximum time.Duration) time.Duration {
195
+ if current >= maximum || current > maximum/2 {
196
+ return maximum
197
+ }
198
+ return current * 2
199
+ }
@@ -0,0 +1,215 @@
1
+ package runtime
2
+
3
+ import (
4
+ "context"
5
+ "errors"
6
+ "io"
7
+ "log"
8
+ "strings"
9
+ "sync"
10
+ "testing"
11
+ "time"
12
+ )
13
+
14
+ var errTask = errors.New("task failed")
15
+
16
+ type executionResult struct {
17
+ summary string
18
+ err error
19
+ }
20
+
21
+ type scriptedExecutor struct {
22
+ mu sync.Mutex
23
+ results []executionResult
24
+ calls int
25
+ execute func(context.Context) (string, error)
26
+ }
27
+
28
+ func (executor *scriptedExecutor) Execute(ctx context.Context) (string, error) {
29
+ if executor.execute != nil {
30
+ return executor.execute(ctx)
31
+ }
32
+ executor.mu.Lock()
33
+ defer executor.mu.Unlock()
34
+ executor.calls++
35
+ result := executor.results[executor.calls-1]
36
+ return result.summary, result.err
37
+ }
38
+
39
+ func (executor *scriptedExecutor) Count() int {
40
+ executor.mu.Lock()
41
+ defer executor.mu.Unlock()
42
+ return executor.calls
43
+ }
44
+
45
+ type clockRequest struct {
46
+ delay time.Duration
47
+ fire chan time.Time
48
+ }
49
+
50
+ type fakeClock struct {
51
+ requests chan clockRequest
52
+ }
53
+
54
+ func newFakeClock() *fakeClock {
55
+ return &fakeClock{requests: make(chan clockRequest, 16)}
56
+ }
57
+
58
+ func (clock *fakeClock) After(delay time.Duration) <-chan time.Time {
59
+ fire := make(chan time.Time, 1)
60
+ clock.requests <- clockRequest{delay: delay, fire: fire}
61
+ return fire
62
+ }
63
+
64
+ func (clock *fakeClock) Next(t *testing.T) clockRequest {
65
+ t.Helper()
66
+ select {
67
+ case request := <-clock.requests:
68
+ return request
69
+ case <-time.After(time.Second):
70
+ t.Fatal("runner did not request a timer")
71
+ return clockRequest{}
72
+ }
73
+ }
74
+
75
+ func fire(request clockRequest) {
76
+ request.fire <- time.Time{}
77
+ }
78
+
79
+ func newTestRunner(mode string, executor Executor, clock Clock) *Runner {
80
+ return New(Options{
81
+ Name: "demo-worker",
82
+ Mode: mode,
83
+ TickInterval: 10 * time.Second,
84
+ ShutdownTimeout: 5 * time.Second,
85
+ MaxAttempts: 3,
86
+ InitialBackoff: time.Second,
87
+ MaxBackoff: 2 * time.Second,
88
+ Executor: executor,
89
+ Logger: log.New(io.Discard, "", 0),
90
+ Clock: clock,
91
+ })
92
+ }
93
+
94
+ func TestRunOneshotRetriesWithCappedExponentialBackoff(t *testing.T) {
95
+ executor := &scriptedExecutor{results: []executionResult{
96
+ {err: errTask}, {err: errTask}, {summary: "heartbeat completed"},
97
+ }}
98
+ clock := newFakeClock()
99
+ done := make(chan error, 1)
100
+ go func() { done <- newTestRunner("oneshot", executor, clock).Run(context.Background()) }()
101
+
102
+ firstBackoff := clock.Next(t)
103
+ if firstBackoff.delay != time.Second {
104
+ t.Fatalf("expected first backoff %s, got %s", time.Second, firstBackoff.delay)
105
+ }
106
+ fire(firstBackoff)
107
+ secondBackoff := clock.Next(t)
108
+ if secondBackoff.delay != 2*time.Second {
109
+ t.Fatalf("expected capped second backoff %s, got %s", 2*time.Second, secondBackoff.delay)
110
+ }
111
+ fire(secondBackoff)
112
+
113
+ if err := <-done; err != nil {
114
+ t.Fatal(err)
115
+ }
116
+ if executor.Count() != 3 {
117
+ t.Fatalf("expected three attempts, got %d", executor.Count())
118
+ }
119
+ }
120
+
121
+ func TestRunOneshotReturnsFinalErrorAfterRetryExhaustion(t *testing.T) {
122
+ executor := &scriptedExecutor{results: []executionResult{{err: errTask}, {err: errTask}, {err: errTask}}}
123
+ clock := newFakeClock()
124
+ done := make(chan error, 1)
125
+ go func() { done <- newTestRunner("oneshot", executor, clock).Run(context.Background()) }()
126
+
127
+ fire(clock.Next(t))
128
+ fire(clock.Next(t))
129
+ err := <-done
130
+ if !errors.Is(err, errTask) || !strings.Contains(err.Error(), "after 3 attempt(s)") {
131
+ t.Fatalf("expected exhausted retry error, got %v", err)
132
+ }
133
+ }
134
+
135
+ func TestRunScheduledContinuesAfterRetryExhaustion(t *testing.T) {
136
+ executor := &scriptedExecutor{results: []executionResult{
137
+ {err: errTask}, {err: errTask}, {err: errTask}, {summary: "next cycle completed"},
138
+ }}
139
+ clock := newFakeClock()
140
+ ctx, cancel := context.WithCancel(context.Background())
141
+ done := make(chan error, 1)
142
+ go func() { done <- newTestRunner("scheduled", executor, clock).Run(ctx) }()
143
+
144
+ fire(clock.Next(t))
145
+ fire(clock.Next(t))
146
+ scheduleDelay := clock.Next(t)
147
+ if scheduleDelay.delay != 10*time.Second {
148
+ t.Fatalf("expected schedule delay %s, got %s", 10*time.Second, scheduleDelay.delay)
149
+ }
150
+ fire(scheduleDelay)
151
+ nextSchedule := clock.Next(t)
152
+ if nextSchedule.delay != 10*time.Second {
153
+ t.Fatalf("expected next schedule delay %s, got %s", 10*time.Second, nextSchedule.delay)
154
+ }
155
+ cancel()
156
+ if err := <-done; err != nil {
157
+ t.Fatal(err)
158
+ }
159
+ if executor.Count() != 4 {
160
+ t.Fatalf("expected four attempts across two cycles, got %d", executor.Count())
161
+ }
162
+ }
163
+
164
+ func TestCancellationInterruptsRetryBackoff(t *testing.T) {
165
+ executor := &scriptedExecutor{results: []executionResult{{err: errTask}}}
166
+ clock := newFakeClock()
167
+ ctx, cancel := context.WithCancel(context.Background())
168
+ done := make(chan error, 1)
169
+ go func() { done <- newTestRunner("oneshot", executor, clock).Run(ctx) }()
170
+
171
+ request := clock.Next(t)
172
+ if request.delay != time.Second {
173
+ t.Fatalf("expected retry backoff %s, got %s", time.Second, request.delay)
174
+ }
175
+ cancel()
176
+ if err := <-done; err != nil {
177
+ t.Fatal(err)
178
+ }
179
+ if executor.Count() != 1 {
180
+ t.Fatalf("expected cancellation to prevent another attempt, got %d", executor.Count())
181
+ }
182
+ }
183
+
184
+ func TestShutdownTimeoutIsDeterministic(t *testing.T) {
185
+ releaseExecutor := make(chan struct{})
186
+ defer close(releaseExecutor)
187
+ executor := &scriptedExecutor{execute: func(ctx context.Context) (string, error) {
188
+ <-ctx.Done()
189
+ <-releaseExecutor
190
+ return "", ctx.Err()
191
+ }}
192
+ clock := newFakeClock()
193
+ ctx, cancel := context.WithCancel(context.Background())
194
+ done := make(chan error, 1)
195
+ go func() { done <- newTestRunner("oneshot", executor, clock).Run(ctx) }()
196
+
197
+ cancel()
198
+ shutdownTimer := clock.Next(t)
199
+ if shutdownTimer.delay != 5*time.Second {
200
+ t.Fatalf("expected shutdown timeout %s, got %s", 5*time.Second, shutdownTimer.delay)
201
+ }
202
+ fire(shutdownTimer)
203
+ if err := <-done; !errors.Is(err, ErrShutdownTimeout) {
204
+ t.Fatalf("expected shutdown timeout, got %v", err)
205
+ }
206
+ }
207
+
208
+ func TestNextBackoffCapsWithoutOverflow(t *testing.T) {
209
+ if actual := nextBackoff(time.Second, 2*time.Second); actual != 2*time.Second {
210
+ t.Fatalf("expected doubled backoff, got %s", actual)
211
+ }
212
+ if actual := nextBackoff(2*time.Second, 2*time.Second); actual != 2*time.Second {
213
+ t.Fatalf("expected capped backoff, got %s", actual)
214
+ }
215
+ }
@@ -0,0 +1,19 @@
1
+ package task
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+ "time"
7
+ )
8
+
9
+ type HeartbeatTask struct {
10
+ workerName string
11
+ }
12
+
13
+ func NewHeartbeatTask(workerName string) *HeartbeatTask {
14
+ return &HeartbeatTask{workerName: workerName}
15
+ }
16
+
17
+ func (task *HeartbeatTask) Execute(context.Context) (string, error) {
18
+ return fmt.Sprintf("%s heartbeat completed at %s", task.workerName, time.Now().UTC().Format(time.RFC3339)), nil
19
+ }
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env sh
2
+ set -eu
3
+
4
+ mkdir -p .cache/go-build bin
5
+ go mod tidy
6
+ go test ./...
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env sh
2
+ set -eu
3
+
4
+ issues=0
5
+
6
+ echo "Worker environment doctor"
7
+
8
+ if command -v go >/dev/null 2>&1; then
9
+ echo "go is available"
10
+ else
11
+ echo "go is not installed or not on PATH."
12
+ issues=$((issues + 1))
13
+ fi
14
+
15
+ if command -v docker >/dev/null 2>&1; then
16
+ echo "docker is available"
17
+ else
18
+ echo "docker is not installed or not on PATH."
19
+ issues=$((issues + 1))
20
+ fi
21
+
22
+ if [ -f configs/config.json ]; then
23
+ echo "configs/config.json is present"
24
+ else
25
+ echo "configs/config.json is missing"
26
+ issues=$((issues + 1))
27
+ fi
28
+
29
+ if [ -f go.mod ]; then
30
+ echo "go.mod is present"
31
+ else
32
+ echo "go.mod is missing"
33
+ issues=$((issues + 1))
34
+ fi
35
+
36
+ if [ "$issues" -gt 0 ]; then
37
+ echo "Doctor found $issues issue(s)."
38
+ exit 1
39
+ fi
40
+
41
+ echo "Environment looks ready."
@@ -0,0 +1,44 @@
1
+ package tests
2
+
3
+ import (
4
+ "context"
5
+ "io"
6
+ "log"
7
+ "testing"
8
+ "time"
9
+
10
+ "{{MODULE_NAME}}/internal/config"
11
+ "{{MODULE_NAME}}/internal/runtime"
12
+ "{{MODULE_NAME}}/internal/task"
13
+ )
14
+
15
+ func TestWorkerConfigDefaults(t *testing.T) {
16
+ t.Setenv("CONFIG_FILE", "../configs/config.json")
17
+
18
+ cfg, err := config.Load()
19
+ if err != nil {
20
+ t.Fatal(err)
21
+ }
22
+
23
+ if cfg.Worker.Name != "{{WORKER_NAME}}" {
24
+ t.Fatalf("expected worker name %q, got %q", "{{WORKER_NAME}}", cfg.Worker.Name)
25
+ }
26
+ if cfg.Worker.RunMode != "{{RUN_MODE}}" {
27
+ t.Fatalf("expected run mode %q, got %q", "{{RUN_MODE}}", cfg.Worker.RunMode)
28
+ }
29
+ }
30
+
31
+ func TestOneshotRunnerUsesHeartbeatTask(t *testing.T) {
32
+ runner := runtime.New(runtime.Options{
33
+ Name: "{{WORKER_NAME}}",
34
+ Mode: "oneshot",
35
+ TickInterval: 10 * time.Millisecond,
36
+ ShutdownTimeout: time.Second,
37
+ Executor: task.NewHeartbeatTask("{{WORKER_NAME}}"),
38
+ Logger: log.New(io.Discard, "", 0),
39
+ })
40
+
41
+ if err := runner.Run(context.Background()); err != nil {
42
+ t.Fatal(err)
43
+ }
44
+ }
@@ -0,0 +1,100 @@
1
+ {
2
+ "name": "worker",
3
+ "description": "Go background worker starter with scheduled and oneshot execution modes",
4
+ "category": "backend",
5
+ "stack": ["Go", "Docker"],
6
+ "tags": ["worker", "docker", "go", "async"],
7
+ "platforms": ["linux", "container"],
8
+ "maturity": {
9
+ "level": "validated",
10
+ "summary": "Dockerized worker starter with explicit retry semantics, bounded shutdown, and deterministic generated-project tests."
11
+ },
12
+ "validation": {
13
+ "status": "generated-project-verified",
14
+ "verification_tier": "generated-project",
15
+ "evidence": [
16
+ "python unittest template rendering coverage",
17
+ "deterministic injected-clock retry and scheduling tests",
18
+ "real generated project go test validation"
19
+ ]
20
+ },
21
+ "operating_assumptions": [
22
+ "The starter is optimized for background execution rather than a public HTTP API surface.",
23
+ "Generated projects are expected to keep config, verification, and container packaging repo-local."
24
+ ],
25
+ "workflow_labels": ["bootstrap", "dev", "verify", "build", "runtime"],
26
+ "commands": {
27
+ "bootstrap": "make bootstrap",
28
+ "doctor": "make doctor",
29
+ "lint": "make lint",
30
+ "test": "make test",
31
+ "verify": "make verify",
32
+ "build": "make build",
33
+ "clean": "make clean",
34
+ "help": "make help"
35
+ },
36
+ "worktree": {
37
+ "support_level": "worktree-ready",
38
+ "isolation_dimensions": [
39
+ "runtime-names",
40
+ "caches",
41
+ "generated-output",
42
+ "cleanup",
43
+ "diagnostics"
44
+ ],
45
+ "diagnostics": [
46
+ "make worktree-info",
47
+ "make worktree-doctor"
48
+ ],
49
+ "cleanup": [
50
+ "make clean-worktree"
51
+ ]
52
+ },
53
+ "variables": [
54
+ {
55
+ "name": "project_name",
56
+ "required": true,
57
+ "validator": "project-name"
58
+ },
59
+ {
60
+ "name": "module_name",
61
+ "required": true,
62
+ "prompt": "Go module name: ",
63
+ "validator": "go-module"
64
+ },
65
+ {
66
+ "name": "worker_name",
67
+ "required": false,
68
+ "default_from": "project_name",
69
+ "validator": "slug"
70
+ },
71
+ {
72
+ "name": "run_mode",
73
+ "required": false,
74
+ "default": "scheduled",
75
+ "validator": "choice",
76
+ "choices": ["scheduled", "oneshot"]
77
+ },
78
+ {
79
+ "name": "tick_interval_seconds",
80
+ "required": false,
81
+ "default": "30",
82
+ "validator": "positive-integer",
83
+ "maximum": 86400
84
+ },
85
+ {
86
+ "name": "shutdown_timeout_seconds",
87
+ "required": false,
88
+ "default": "10",
89
+ "validator": "positive-integer",
90
+ "maximum": 3600
91
+ }
92
+ ],
93
+ "next_steps": [
94
+ "make bootstrap",
95
+ "make dev",
96
+ "make verify",
97
+ "make docker-build",
98
+ "make docker-run"
99
+ ]
100
+ }