{"id":492899,"date":"2026-08-31T08:51:32","date_gmt":"2026-08-31T08:51:32","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=492899"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2026-08-31T08:51:32","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=492899","title":{"rendered":"\u0421\u0443\u0434\u044c\u0431\u0430 \u0432\u00a0\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0435: \u043f\u0438\u0448\u0435\u043c \u0434\u0435\u0442\u0435\u0440\u043c\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 Quality Gate \u043d\u0430\u00a0Go \u0441\u00a0\u043c\u0435\u0445\u0430\u043d\u0438\u043a\u043e\u0439 \u0422\u0430\u0440\u043e"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e40\/61b\/86f\/e4061b86f8aeede3f6d5661ddbd1ee14.png\" alt=\"\u0412\u044b\u0432\u043e\u0434 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c \u043a\u0430\u0440\u0442\u044b\" title=\"\u0412\u044b\u0432\u043e\u0434 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c \u043a\u0430\u0440\u0442\u044b\" width=\"2048\" height=\"1177\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/e40\/61b\/86f\/e4061b86f8aeede3f6d5661ddbd1ee14.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e40\/61b\/86f\/e4061b86f8aeede3f6d5661ddbd1ee14.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0412\u044b\u0432\u043e\u0434 \u0432\u00a0\u043a\u043e\u043d\u0441\u043e\u043b\u044c \u043a\u0430\u0440\u0442\u044b<\/figcaption><\/div>\n<\/figure>\n<p>\u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0432\u00a0\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430\u0445 \u0443\u0436\u0435 \u0441\u0442\u0430\u043b\u0438 \u0440\u0443\u0442\u0438\u043d\u043e\u0439,\u00a0\u043b\u0438\u043d\u0442\u0435\u0440\u044b, \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432, \u043e\u0431\u0440\u0430\u0437\u043e\u0432\u00a0\u2014 \u0432\u00a0\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u043a\u043b\u0430\u0434\u0435\u0442\u0441\u044f \u0432\u0441\u0435, \u0447\u0442\u043e\u00a0\u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u043b\u0438\u0437 \u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u0435\u0433\u043e \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u0435\u0435. \u0418 \u0432\u043e\u0442 \u0443\u043b\u0443\u0447\u0448\u0430\u044f \u0441\u0432\u043e\u0439 devsecops \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d, \u044f \u0437\u0430\u0434\u0443\u043c\u0430\u043b\u0441\u044f, \u043d\u0430\u0434 \u0442\u0435\u043c \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f\u00a0CI\/CD \u0438 \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c \u0435\u0433\u043e \u0432\u00a0\u0440\u0430\u043c\u043a\u0430\u0445 GitHub Actions. <\/p>\n<p>\u041e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u043d\u0435\u00a0\u0442\u0430\u043a \u0443\u0436 \u0438 \u0441\u043b\u043e\u0436\u043d\u043e, \u0432\u0435\u0434\u044c \u0434\u043b\u044f\u00a0\u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u043b\u043e\u0436\u0438\u0442\u044c \u0441\u0432\u043e\u044e \u0443\u0442\u0438\u043b\u0438\u0442\u0443 \u0432\u00a0GitHub Marketplace, \u043a\u043e\u0442\u043e\u0440\u044b\u0439\u00a0\u0431\u044b \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043b\u0441\u044f \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u0447\u043a\u043e\u0439, \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u043e \u043e\u0444\u043e\u0440\u043c\u0438\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0438 \u0438 \u0437\u0430\u0440\u0435\u043b\u0438\u0437\u0438\u0442\u044c \u0441\u043e\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0441\u00a0\u0442\u0435\u0433\u043e\u043c \u0438 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u043e\u043c\u00a0\u2014 \u043c\u043e\u0434\u0435\u0440\u0430\u0446\u0438\u044f \u0432\u00a0\u043c\u0430\u0440\u043a\u0435\u0442\u043f\u043b\u0435\u0439\u0441\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f.\u00a0<\/p>\n<p>\u0421\u0430\u043c\u0430 \u0438\u0434\u0435\u044f \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0441\u0442\u0438 \u0432\u043d\u0443\u0442\u0440\u0438, \u0442\u0430\u043a\u2011\u0442\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0432\u0430\u0436\u043d\u043e\u0433\u043e \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u0430, \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430, \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0441\u0442\u0440\u0430\u043d\u043d\u043e\u0439, \u043d\u043e\u00a0\u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0438\u0440\u043e\u043d\u0438\u0447\u043d\u043e\u0439, \u0432\u0435\u0434\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u0433\u0430\u0434\u0430\u0435\u0442 \u043d\u0430\u00a0\u043a\u0430\u0440\u0442\u0430\u0445 \u0422\u0430\u0440\u043e, \u0431\u0443\u0434\u0435\u0442 \u0440\u0435\u043b\u0438\u0437 \u0443\u0434\u0430\u0447\u043d\u044b\u043c \u0438\u043b\u0438\u00a0\u043d\u0435\u0442. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b \u0433\u043b\u0430\u0432\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438: \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043b\u0435\u0433\u043a\u0443\u044e \u0438 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c\u0443\u044e \u0443\u0442\u0438\u043b\u0438\u0442\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f\u00a0\u0431\u044b\u043b\u0430\u00a0\u0431\u044b \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u0430 \u0432\u00a0\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 \u0432\u043d\u0443\u0442\u0440\u0438 GitHub Actions \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u0432\u043e\u0435\u0433\u043e \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f \u0438 \u0441\u0430\u043c\u0430\u00a0\u0431\u044b \u0441\u0442\u0430\u043b\u0430 \u0447\u0430\u0441\u0442\u044c\u044e \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430, \u0442\u0430\u043a\u0430\u044f \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u044f \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f.\u00a0<\/p>\n<h3>\u041f\u043e\u0447\u0435\u043c\u0443 \u0447\u0438\u0441\u0442\u044b\u0439 \u0440\u0430\u043d\u0434\u043e\u043c \u0443\u0431\u0438\u0432\u0430\u0435\u0442 CI\/CD?<\/h3>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u044f\u0437\u044b\u043a \u043d\u0430\u00a0\u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 Arcana Gate (\u0442\u0430\u043a \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0435\u043a\u0442)\u00a0\u2014 Golang, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e\u00a0\u043c\u043d\u0435\u00a0\u0431\u044b\u043b\u0430 \u0432\u0430\u0436\u043d\u0430 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0438 \u0441\u0431\u043e\u0440\u043a\u0430 \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a\u0430 \u043f\u043e\u0434\u00a0\u0440\u0430\u0437\u043d\u044b\u0435 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b, \u0430\u00a0\u0443\u00a0Go \u0435\u0441\u0442\u044c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 go\u2011releaser, \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432\u00a0yaml\u2011\u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0435 \u043f\u0440\u044f\u043c \u0432\u00a0\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438. (\u043f\u043b\u044e\u0441 \u043c\u043d\u0435 \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f Go)<\/p>\n<p>\u0421\u0443\u0442\u044c \u0432\u00a0\u0442\u043e\u043c, \u0447\u0442\u043e\u00a0\u043d\u0430\u00a0\u043d\u0443\u0436\u043d\u043e\u043c \u044d\u0442\u0430\u043f\u0435 (\u044f\u00a0\u0431\u044b \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b \u0435\u0433\u043e \u043f\u0435\u0440\u0435\u0434 \u0441\u0430\u043c\u0438\u043c \u0434\u0435\u043f\u043b\u043e\u0435\u043c \u0432\u00a0\u0440\u0435\u043b\u0438\u0437\u00a0\u043b\u0438\u0431\u043e \u0441\u0430\u043c\u044b\u043c \u043f\u0435\u0440\u0432\u044b\u043c) \u0443\u0442\u0438\u043b\u0438\u0442\u0430\u00a0\u0431\u044b \u0432\u044b\u0442\u0430\u0441\u043a\u0438\u0432\u0430\u043b\u0430 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u0443\u044e \u043a\u0430\u0440\u0442\u0443 \u0438\u0437\u00a0\u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0439 \u043a\u043e\u043b\u043e\u0434\u044b \u0438 \u043f\u043e\u00a0\u0435\u0435 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u043b\u0430 \u00ab\u0431\u0443\u0434\u0443\u0449\u0435\u0435\u00bb \u0440\u0435\u043b\u0438\u0437\u0430, \u0431\u0443\u0434\u0435\u0442 \u043e\u043d \u0443\u0434\u0430\u0447\u043d\u044b\u043c \u0438\u043b\u0438\u00a0\u043d\u0435\u0442. \u0410\u00a0\u0432\u043e\u0442 \u043a\u0430\u043a\u00a0\u0440\u0430\u0437 \u043f\u043e\u043b\u043d\u0430\u044f \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0441\u0442\u044c \u043d\u0435\u00a0\u043e\u0447\u0435\u043d\u044c \u0432\u043e\u043e\u0434\u0443\u0448\u0435\u0432\u043b\u044f\u0435\u0442, \u0442\u0430\u043a \u043a\u0430\u043a\u00a0\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0434\u043e\u043b\u0436\u0435\u043d\u00a0\u0431\u044b\u0442\u044c \u0438\u0434\u0435\u043c\u043f\u043e\u0442\u0435\u043d\u0442\u043d\u044b\u043c. <\/p>\n<p>\u041f\u0440\u0438\u00a0\u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430 \u0434\u043b\u044f\u00a0\u043e\u0434\u043d\u043e\u0433\u043e \u0438 \u0442\u043e\u0433\u043e\u2011\u0436\u0435 \u043a\u043e\u043c\u043c\u0438\u0442\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043d\u0435\u00a0\u0434\u043e\u043b\u0436\u0435\u043d\u00a0\u0431\u044b\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u043c, \u0438 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0431\u0440\u0430\u0442\u044c math\/rand \u0441\u00a0\u0441\u0438\u0434\u043e\u043c \u043e\u0442\u00a0\u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 (<a href=\"http:\/\/time.Now\" rel=\"noopener nofollow\">time.Now<\/a>().UnixNano()) \u0434\u043b\u044f\u00a0\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043a\u0430\u0440\u0442\u044b \u043f\u043e\u043b\u043e\u043c\u0430\u0435\u0442 \u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c seed \u0438\u0437\u00a0\u0445\u044d\u0448\u0430 \u043a\u043e\u043c\u043c\u0438\u0442\u0430 (GITHUB_SHA). \u041d\u043e\u00a0\u043a\u0430\u043a\u00a0\u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0447\u0438\u0441\u043b\u043e\u0432\u043e\u0439 seed \u0438\u0437\u00a0SHA \u0434\u043b\u044f\u00a0\u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440\u0430 \u043f\u0441\u0435\u0432\u0434\u043e\u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0447\u0438\u0441\u0435\u043b?<\/p>\n<pre><code class=\"go\">func (e Engine) createRNG(seed string) rand.Rand {  hash := sha256.Sum256([]byte(seed))  seedInt := int64(binary.BigEndian.Uint64(hash[:8]))  return rand.New(rand.NewSource(seedInt))}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043b\u043e\u0433\u0438\u043a\u0430 \u043f\u0440\u043e\u0441\u0442\u0430\u044f, \u0431\u0435\u0440\u0435\u043c \u0445\u044d\u0448 \u043a\u043e\u043c\u043c\u0438\u0442\u0430, \u043f\u0440\u043e\u0433\u043e\u043d\u044f\u0435\u043c \u0447\u0435\u0440\u0435\u0437 SHA-256, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0440\u0430\u0432\u043d\u043e\u043c\u0435\u0440\u043d\u043e\u0435 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0431\u0430\u0439\u0442, \u0438 \u043e\u0442\u0440\u0435\u0437\u0430\u0435\u043c \u043f\u0435\u0440\u0432\u044b\u0435 8\u00a0\u0431\u0430\u0439\u0442, \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u044f \u0438\u0445 \u0432\u00a0int64. \u042d\u0442\u043e\u0442 seedInt \u043c\u044b \u043e\u0442\u0434\u0430\u0435\u043c \u0432\u00a0rand.NewSource(). \u0422\u0435\u043f\u0435\u0440\u044c, \u0441\u043a\u043e\u043b\u044c\u043a\u043e\u00a0\u0431\u044b \u0440\u0430\u0437 \u043d\u0435\u00a0\u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043b\u0438 \u0443\u043f\u0430\u0432\u0448\u0438\u0439 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0432\u00a0GitHub Actions \u043a\u043d\u043e\u043f\u043a\u043e\u0439 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430, \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0434\u0430\u0441\u0442 \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u0443\u044e \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c.\u00a0<\/p>\n<p><strong>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 CLI \u043d\u0430\u00a0Go<\/strong><\/p>\n<p>\u0418\u0435\u0440\u0430\u0440\u0445\u0438\u044f \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f \u0438 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<pre><code>arcana-gate\/\u251c\u2500\u2500 .github\/\u2502   \u2514\u2500\u2500 workflows\/\u2502       \u251c\u2500\u2500 pipeline.yml\u2502       \u2514\u2500\u2500 demo.yml\u251c\u2500\u2500 cmd\/\u2502   \u2514\u2500\u2500 arcana-gate\/\u251c\u2500\u2500 internal\/\u2502   \u251c\u2500\u2500 config\/\u2502   \u251c\u2500\u2500 domain\/\u2502   \u251c\u2500\u2500 engine\/\u2502   \u2514\u2500\u2500 presenter\/\u251c\u2500\u2500 action.yml\u251c\u2500\u2500 Dockerfile\u2514\u2500\u2500 .goreleaser.yml<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u0431\u043e\u00a0\u0432\u0441\u0435\u0445 \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0445 \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043f\u043e\u00a0\u0445\u043e\u0434\u0443 \u0441\u0442\u0430\u0442\u044c\u0438.<\/p>\n<h3>\u041f\u043e\u0434\u0433\u0440\u0443\u0437\u043a\u0430 GITHUB_SHA: internal\/config\/<\/h3>\n<p>\u0414\u043b\u044f\u00a0\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f seed \u0438\u0437\u00a0GITHUB_SHA, \u043d\u0430\u0434\u043e \u043f\u043e\u0434\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0435\u0433\u043e \u0434\u043b\u044f\u00a0\u043d\u0430\u0447\u0430\u043b\u0430 \u0447\u0435\u0440\u0435\u0437 os.getenv. Seed \u043f\u043e\u0434\u0433\u0440\u0443\u0437\u0438\u0442\u0441\u044f \u0438\u0437\u00a0\u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437\u00a0\u0442\u0440\u0435\u0445 \u043c\u0435\u0441\u0442: \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u0438\u0434\u0430 arcana_seed (\u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u0432\u043d\u0435 github), github_sha \u0438 ci_commit_sha. \u0422\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u043c\u0438\u043c\u043e \u044d\u0442\u043e\u0433\u043e \u0444\u043e\u0440\u043c\u0430\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0443\u0442\u0438\u043b\u0438\u0442\u044b\u00a0\u2014 strict, \u044d\u0442\u0430 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0431\u0443\u0434\u0435\u0442\u00a0\u043b\u0438 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u043f\u0440\u0438\u00a0\u043d\u0435\u0433\u0430\u0442\u0438\u0432\u043d\u043e\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0440\u0430\u0441\u043a\u043b\u0430\u0434\u0430. \u0410\u00a0\u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0434\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u0443\u044e \u043a\u043e\u043b\u043e\u0434\u0443 \u043a\u0430\u0440\u0442.<\/p>\n<details class=\"spoiler\">\n<summary>config.go<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">type Config struct {Seed           stringStrict         boolCustomDeckPath string}func Load(args []string) (*Config, error) {flags := flag.NewFlagSet(\"arcana-gate\", flag.ContinueOnError)var (seed           stringstrict         boolcustomDeckPath string)flags.StringVar(&amp;seed, \"seed\", \"\", \"Deterministic seed for divination (defaults to GITHUB_SHA or CI_COMMIT_SHA)\")flags.BoolVar(&amp;strict, \"strict\", false, \"Enable strict mode where all reversed cards result in a BLOCK\")flags.StringVar(&amp;customDeckPath, \"deck\", \"\", \"Path to a custom YAML deck file\")if err := flags.Parse(args); err != nil {return nil, fmt.Errorf(\"failed to parse command-line flags: %w\", err)}cfg := &amp;Config{Seed:           seed,Strict:         strict,CustomDeckPath: customDeckPath,}if cfg.Seed == \"\" {if val := os.Getenv(\"ARCANA_SEED\"); val != \"\" {cfg.Seed = val} else if val := os.Getenv(\"GITHUB_SHA\"); val != \"\" {cfg.Seed = val} else if val := os.Getenv(\"CI_COMMIT_SHA\"); val != \"\" {cfg.Seed = val}}if !cfg.Strict {if val := os.Getenv(\"ARCANA_STRICT\"); val != \"\" {parsed, err := strconv.ParseBool(val)if err == nil {cfg.Strict = parsed}}}if cfg.CustomDeckPath == \"\" {if val := os.Getenv(\"ARCANA_DECK_PATH\"); val != \"\" {cfg.CustomDeckPath = val}}return cfg, nil}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<\/div>\n<\/details>\n<h3>\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043a\u0430\u0440\u0442 \u0438 \u043a\u043e\u043b\u043e\u0434\u044b: internal\/domain\/<\/h3>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a\u00a0\u0441\u0430\u043c\u043e\u043c\u0443 \u043d\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044e, \u043a\u00a0\u043a\u0430\u0440\u0442\u0430\u043c, \u0438\u0445 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f\u043c \u0438 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u043c, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e\u00a0\u043a\u0430\u0440\u0442\u044b \u0440\u0430\u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043a\u0430\u043a\u00a0\u0432\u00a0\u043e\u0431\u044b\u0447\u043d\u043e\u043c \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0442\u0430\u043a \u0438 \u043c\u043e\u0433\u0443\u0442\u00a0\u0431\u044b\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u0435\u0440\u043d\u0443\u0442\u044b.\u00a0<\/p>\n<details class=\"spoiler\">\n<summary>card.go<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">type Position stringconst (Upright  Position = \"UPRIGHT\"Reversed Position = \"REVERSED\")type Suit stringconst (MajorArcana Suit = \"MAJOR_ARCANA\"Wands       Suit = \"WANDS\"Cups        Suit = \"CUPS\"Swords      Suit = \"SWORDS\"Pentacles   Suit = \"PENTACLES\")type Card struct {ID            string `json:\"id\" yaml:\"id\"`Name          string `json:\"name\" yaml:\"name\"`Suit          Suit   `json:\"suit\" yaml:\"suit\"`Value         int    `json:\"value\" yaml:\"value\"`Description   string `json:\"description\" yaml:\"description\"`BlockUpright  bool   `json:\"block_upright\" yaml:\"block_upright\"`BlockReversed bool   `json:\"block_reversed\" yaml:\"block_reversed\"`}type DrawnCard struct {Card     Card     `json:\"card\"`Position Position `json:\"position\"`}func (dc DrawnCard) IsBlocking() bool {if dc.Position == Upright &amp;&amp; dc.Card.BlockUpright {return true}if dc.Position == Reversed &amp;&amp; dc.Card.BlockReversed {return true}return false}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<\/div>\n<\/details>\n<p>\u042f \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0440\u0430\u0437\u043d\u044b\u0435 \u043d\u0430\u0431\u043e\u0440\u044b \u043a\u0430\u0440\u0442, \u043d\u043e\u00a0\u043f\u043e\u043a\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0430\u0440\u0448\u0438\u0435 \u0430\u0440\u043a\u0430\u043d\u044b MAJOR_ARCANA, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e\u00a0\u043c\u0435\u0445\u0430\u043d\u0438\u043a\u0430 \u043f\u043e\u043a\u0430 \u0441\u0442\u0440\u043e\u0438\u0442\u0441\u044f \u043d\u0430\u00a0\u0432\u044b\u0442\u044f\u0433\u0438\u0432\u0430\u043d\u0438\u0438 \u043e\u0434\u043d\u043e\u0439 \u043a\u0430\u0440\u0442\u044b. \u041e\u0431\u044b\u0447\u043d\u043e \u0440\u0430\u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u044e\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u0430\u0440\u0442, \u043d\u043e\u00a0\u043d\u0430\u00a0\u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0444\u043e\u0440\u043c\u0430\u0442 \u0432\u044b\u0442\u044f\u0433\u0438\u0432\u0430\u043d\u0438\u044f \u043a\u0430\u0440\u0442 \u0441\u0445\u043e\u0436 \u0441\u00a0\u043c\u0435\u0445\u0430\u043d\u0438\u043a\u043e\u0439 \u0422\u0430\u0440\u043e \u0438\u0437\u00a0Phasmophobia. \u0412\u0441\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442\u00a0\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u043a\u00a0\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u043a\u0430\u0440\u0442\u044b, \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0432\u00a0\u0444\u0443\u043d\u043a\u0446\u0438\u0438 IsBlocking. \u0418\u043c\u0435\u043d\u043d\u043e \u043d\u0430\u00a0\u044d\u0442\u0430\u043f\u0435 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u0430\u0440\u0442\u044b \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u0432\u00a0\u043a\u0430\u043a\u043e\u043c \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043e\u043d\u0430 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d. C\u0430\u043c\u0430 \u043a\u043e\u043b\u043e\u0434\u0430 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<details class=\"spoiler\">\n<summary>deck.go<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">func DefaultDeck() []domain.Card {return []domain.Card{{ID:            \"tower\",Name:          \"The Tower\",Suit:          domain.MajorArcana,Value:         16,Description:   \"Catastrophic infrastructure outage, production crash, and data loss.\",BlockUpright:  true,BlockReversed: true,},{ID:            \"death\",Name:          \"Death\",Suit:          domain.MajorArcana,Value:         13,Description:   \"Irreversible architecture transformation. Danger: potential database wipe.\",BlockUpright:  true,BlockReversed: false,},{ID:            \"devil\",Name:          \"The Devil\",Suit:          domain.MajorArcana,Value:         15,Description:   \"Critical technical debt and unmaintainable code hacks have breached production.\",BlockUpright:  true,BlockReversed: true,},{ID:            \"sun\",Name:          \"The Sun\",Suit:          domain.MajorArcana,Value:         19,Description:   \"Flawless release, 100% test coverage, and absolute uptime stability.\",BlockUpright:  false,BlockReversed: false,},{ID:            \"world\",Name:          \"The World\",Suit:          domain.MajorArcana,Value:         21,Description:   \"Perfect microservices synergy, zero latency, and pure green telemetry.\",BlockUpright:  false,BlockReversed: false,},{ID:            \"fool\",Name:          \"The Fool\",Suit:          domain.MajorArcana,Value:         0,Description:   \"Untested Friday evening release. Pure luck-driven deployment.\",BlockUpright:  false,BlockReversed: true,},{ID:            \"high_priestess\",Name:          \"The High Priestess\",Suit:          domain.MajorArcana,Value:         2,Description:   \"Hidden edge-case bugs, subtle race conditions, and unhandled memory leaks.\",BlockUpright:  true,BlockReversed: false,},{ID:            \"wheel_of_fortune\",Name:          \"The Wheel of Fortune\",Suit:          domain.MajorArcana,Value:         10,Description:   \"Pipeline outcome depends entirely on pure chance and external dependencies.\",BlockUpright:  false,BlockReversed: false,},{ID:            \"hanged_man\",Name:          \"The Hanged Man\",Suit:          domain.MajorArcana,Value:         12,Description:   \"Deadlocks, hanging background workers, and infinite CI\/CD build loops.\",BlockUpright:  true,BlockReversed: false,},}}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<\/div>\n<\/details>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043f\u043e\u043b\u0435 description \u043a\u0430\u0440\u0442\u044b \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u0431\u0449\u0435\u0435 \u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0430\u043d\u0438\u0435 \u043d\u0430\u00a0\u0440\u0435\u043b\u0438\u0437, \u0443\u00a0\u043a\u0430\u0436\u0434\u043e\u0439 \u043a\u0430\u0440\u0442\u044b \u0441\u0432\u043e\u0435. \u042d\u0442\u0443 \u043a\u043e\u043b\u043e\u0434\u0443 \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0443\u0433\u043e\u0434\u043d\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043d\u043e\u0432\u044b\u043c\u0438 \u043a\u0430\u0440\u0442\u0430\u043c\u0438 \u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f\u043c\u0438.<\/p>\n<h3>\u041b\u043e\u0433\u0438\u043a\u0430 \u0434\u0432\u0438\u0436\u043a\u0430: internal\/engine\/<\/h3>\n<p>\u200b\u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u0433\u043b\u0430\u0432\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e Divine. \u041d\u0430\u00a0\u0432\u0445\u043e\u0434 \u043e\u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0442\u043e\u0442 \u0441\u0430\u043c\u044b\u0439 \u0441\u0438\u0434(\u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0438\u0437\u00a0GITHUB_SHA) \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0435\u0433\u043e. \u0415\u0441\u043b\u0438 \u0441\u0438\u0434 \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u043d\u0435\u043f\u0443\u0441\u0442\u044b\u043c, \u0442\u043e \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u043f\u0441\u0435\u0432\u0434\u043e\u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0447\u0438\u0441\u0435\u043b RNG \u043d\u0430\u00a0\u043e\u0441\u043d\u043e\u0432\u0435 \u044d\u0442\u043e\u0433\u043e \u0441\u0438\u0434\u0430. \u0417\u0430\u0442\u0435\u043c \u0432\u00a0\u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043a\u0430\u0440\u0442 \u0432\u00a0\u043a\u043e\u043b\u043e\u0434\u0435 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f \u0438\u043d\u0434\u0435\u043a\u0441 \u043e\u0434\u043d\u043e\u0439 \u0438\u0437\u00a0\u043d\u0438\u0445, \u0438\u0437\u00a0\u043a\u043e\u043b\u043e\u0434\u044b \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u0440\u0442\u0430 \u0441\u00a0\u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0438\u043d\u0434\u0435\u043a\u0441\u043e\u043c drawnCard. \u0422\u0435\u043f\u0435\u0440\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0440\u0438\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043a\u0430\u0440\u0442\u044b(\u043f\u0440\u044f\u043c\u0430\u044f\/\u043f\u0435\u0440\u0435\u0432\u0435\u0440\u043d\u0443\u0442\u0430\u044f), \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c \u043c\u0435\u0436\u0434\u0443 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430\u043c\u0438 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f 50\u00a0\u043d\u0430 50. <\/p>\n<p>\u0418\u0437\u00a0\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442 resultCard \u043e\u0437\u043d\u0430\u0447\u0430\u044e\u0449\u0438\u0439 \u0444\u0438\u043d\u0430\u043b\u044c\u043d\u0443\u044e \u043a\u0430\u0440\u0442\u0443 \u0441\u00a0\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c. \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0438 \u043f\u043e\u0441\u043b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0438 \u043a\u0430\u0440\u0442\u044b \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438. \u041e\u0434\u043d\u0430\u043a\u043e \u0435\u0449\u0435 \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 config.Strict, \u043e\u043d\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u00a0\u043b\u0438 \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u0430\u043b\u044c\u0448\u0435 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0435\u0441\u043b\u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043a\u0430\u0440\u0442\u044b \u0431\u0443\u0434\u0435\u0442 block. \u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f strict \u0437\u0430\u0434\u0430\u0435\u0442\u0441\u044f \u043d\u0430\u00a0\u044d\u0442\u0430\u043f\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u0432\u00a0\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0435.<\/p>\n<details class=\"spoiler\">\n<summary>engine.go<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">type Config struct {Strict bool}type Engine struct {deck   []domain.Cardconfig Config}func New(deck []domain.Card, cfg Config) *Engine {if len(deck) == 0 {deck = DefaultDeck()}return &amp;Engine{deck:   deck,config: cfg,}}func (e *Engine) Divine(ctx context.Context, seed string) (*domain.GateResult, error) {if seed == \"\" {return nil, fmt.Errorf(\"seed cannot be empty for deterministic divination\")}rng := e.createRNG(seed)cardIndex := rng.Intn(len(e.deck))drawnCard := e.deck[cardIndex]position := domain.Uprightif rng.Float32() &lt; 0.5 {position = domain.Reversed}resultCard := domain.DrawnCard{Card:     drawnCard,Position: position,}status := domain.StatusPassreason := fmt.Sprintf(\"Fate allows release with %s (%s)\", drawnCard.Name, position)if resultCard.IsBlocking() {status = domain.StatusBlockreason = fmt.Sprintf(\"Card %s in %s position blocks the pipeline\", drawnCard.Name, position)} else if position == domain.Reversed &amp;&amp; e.config.Strict {status = domain.StatusBlockreason = fmt.Sprintf(\"Strict mode enabled: Reversed card %s blocks the pipeline\", drawnCard.Name)}return &amp;domain.GateResult{Status:    status,DrawnCard: resultCard,Reason:    reason,Seed:      seed,}, nil}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<\/div>\n<\/details>\n<h3>\u0420\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433 \u043a\u0430\u0440\u0442\u044b \u0432\u00a0\u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b: internal\/presenter\/<\/h3>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a\u00a0\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 CLI \u0432\u00a0\u043a\u043e\u043d\u0441\u043e\u043b\u044c\u00a0\u0431\u044b\u043b\u043e\u00a0\u0431\u044b \u043d\u0435\u043f\u043b\u043e\u0445\u043e \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u044c \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u044b\u0439 \u043f\u0440\u0438\u043d\u0442.<\/p>\n<details class=\"spoiler\">\n<summary>terminal.go<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">func (p *TerminalPresenter) Render(res *domain.GateResult) error {statusColor := colorGreenif res.Status == domain.StatusBlock {statusColor = colorRed}var positionText stringif res.DrawnCard.Position == domain.Reversed {positionText = \"REVERSED\"} else {positionText = \"UPRIGHT\"}statusStr := string(res.Status)labelPrefix := \"  Status: \"paddingLen := 40 - (1 + len(labelPrefix) + len(statusStr) + 1)if paddingLen &lt; 0 {paddingLen = 0}statusPadding := strings.Repeat(\" \", paddingLen)var sb strings.Buildersb.WriteString(fmt.Sprintf(\"%s%s\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550%s\\n\", colorBold, colorCyan, colorReset))sb.WriteString(fmt.Sprintf(\"%s          \ud83d\udd2e ARCANA GATE DECISION \ud83d\udd2e          %s\\n\", colorBold, colorReset))sb.WriteString(fmt.Sprintf(\"%s%s\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550%s\\n\", colorBold, colorCyan, colorReset))sb.WriteString(\" \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \\n\")sb.WriteString(fmt.Sprintf(\" \u2502 %s \u2502 \\n\", centerText(res.DrawnCard.Card.Name, 38)))sb.WriteString(fmt.Sprintf(\" \u2502 %s \u2502 \\n\", centerText(\"[\"+positionText+\"]\", 38)))sb.WriteString(\" \u2502                                        \u2502 \\n\")sb.WriteString(fmt.Sprintf(\" \u2502%s%s%s%s%s  \u2502 \\n\", labelPrefix, statusColor+colorBold, statusStr, colorReset, statusPadding))sb.WriteString(\" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \\n\")sb.WriteString(fmt.Sprintf(\"%sDetails:%s %s\\n\", colorYellow, colorReset, res.Reason))sb.WriteString(fmt.Sprintf(\"%sMeaning:%s %s\\n\", colorCyan, colorReset, res.DrawnCard.Card.Description))sb.WriteString(fmt.Sprintf(\"%sSeed:%s    %s\\n\", colorBold, colorReset, res.Seed))sb.WriteString(fmt.Sprintf(\"%s\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550%s\\n\", colorCyan, colorReset))_, err := fmt.Fprint(p.writer, sb.String())return err}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<\/div>\n<\/details>\n<p>\u0412\u044b\u0432\u043e\u0434\u0438\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435, \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u0441\u0442\u0430\u0442\u0443\u0441 \u0437\u0430\u0432\u0435\u0440\u043d\u0443\u0442\u044b\u0435 \u0432\u00a0\u0442\u0430\u043a\u0443\u044e \u0441\u0445\u0435\u043c\u0430\u0442\u0438\u0447\u043d\u0443\u044e \u043a\u0430\u0440\u0442\u0443 \u0432\u00a0\u0432\u0438\u0434\u0435 \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a\u0430. \u041f\u043e\u043c\u0438\u043c\u043e \u044d\u0442\u043e\u0433\u043e \u0441\u0430\u043c\u043e \u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0430\u043d\u0438\u0435 \u043d\u0430\u00a0\u0440\u0435\u043b\u0438\u0437 \u0438 \u0441\u0438\u0434. \u041f\u043e\u00a0\u0438\u0442\u043e\u0433\u0443 \u043a\u0430\u0440\u0442\u0430 \u0432\u00a0\u043a\u043e\u043d\u0441\u043e\u043b\u0438 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0432\u043e\u0442 \u0442\u0430\u043a:<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/7ae\/a5a\/b9c\/7aea5ab9cf0ea50c8ab45e36d12f9294.png\" alt=\"\u0412\u044b\u0432\u043e\u0434 \u043a\u0430\u0440\u0442\u044b \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c\" title=\"\u0412\u044b\u0432\u043e\u0434 \u043a\u0430\u0440\u0442\u044b \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c\" width=\"2106\" height=\"1210\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/7ae\/a5a\/b9c\/7aea5ab9cf0ea50c8ab45e36d12f9294.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/7ae\/a5a\/b9c\/7aea5ab9cf0ea50c8ab45e36d12f9294.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0412\u044b\u0432\u043e\u0434 \u043a\u0430\u0440\u0442\u044b \u0432\u00a0\u043a\u043e\u043d\u0441\u043e\u043b\u044c<\/figcaption><\/div>\n<\/figure>\n<h3>\u0423\u043f\u0430\u043a\u043e\u0432\u043a\u0430 \u0432\u00a0GitHub Action \u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f Step Summary: internal\/presenter\/<\/h3>\n<p>\u041a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434 stdout \u044d\u0442\u043e \u043d\u043e\u0440\u043c, \u043d\u043e\u00a0\u043c\u043d\u0435 \u0437\u0430\u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u044f\u043c\u043e \u043d\u0430\u00a0\u0433\u043b\u0430\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u043f\u0440\u043e\u0433\u043e\u043d\u0430 Action \u0432\u044b\u0432\u043e\u0434\u0438\u043b \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0439 \u043e\u0442\u0447\u0435\u0442. \u0423\u00a0GitHub Actions \u0434\u043b\u044f\u00a0\u044d\u0442\u043e\u0433\u043e \u0435\u0441\u0442\u044c \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f $GITHUB_STEP_SUMMARY. \u0415\u0441\u043b\u0438 \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0442\u0443\u0434\u0430 Markdown\u2011\u0442\u0435\u043a\u0441\u0442 \u0432\u043e\u00a0\u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0448\u0430\u0433\u0430, \u0433\u0438\u0442\u0445\u0430\u0431 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0442\u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442 \u0435\u0433\u043e \u0432\u00a0\u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0439 \u0431\u043b\u043e\u043a \u043e\u0442\u0447\u0435\u0442\u0430. \u0420\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433 Markdown\u2011\u043e\u0442\u0447\u0435\u0442\u0430:<\/p>\n<details class=\"spoiler\">\n<summary>github.go<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">func (g *GitHubPresenter) Render(res *domain.GateResult) error {if g.summaryPath == \"\" {return nil}statusEmoji := \"\u2705\"if res.Status == domain.StatusBlock {statusEmoji = \"\u274c\"}markdown := fmt.Sprintf(`## %s Arcana Gate Decision| Property | Value || :--- | :--- || **Drawn Card** | %s || **Position** | %s || **Status** | %s **%s** || **Determinism Seed** | `+\"`%s`\"+` |&gt; **Divination Insight:**&gt; %s*%s*`,statusEmoji,res.DrawnCard.Card.Name,res.DrawnCard.Position,statusEmoji,res.Status,res.Seed,res.DrawnCard.Card.Description,res.Reason,)file, err := os.OpenFile(g.summaryPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)if err != nil {return fmt.Errorf(\"failed to open GITHUB_STEP_SUMMARY file: %w\", err)}defer file.Close()if _, err := file.WriteString(markdown); err != nil {return fmt.Errorf(\"failed to write to GITHUB_STEP_SUMMARY: %w\", err)}return nil}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<\/div>\n<\/details>\n<p>\u041f\u043e\u00a0\u0444\u0430\u043a\u0442\u0443 \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0442\u0434\u0430\u0435\u043c \u0437\u0430\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 markdown \u043e\u0442\u0447\u0435\u0442 \u0432\u00a0step summary \u0438 \u0432\u043e\u0442 \u0442\u0430\u043a\u043e\u0439 \u043e\u043d \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0441\u044f:<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1e8\/c93\/ea7\/1e8c93ea7c9a0f16cf4ab12552a9ff83.png\" width=\"2048\" height=\"1177\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/1e8\/c93\/ea7\/1e8c93ea7c9a0f16cf4ab12552a9ff83.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1e8\/c93\/ea7\/1e8c93ea7c9a0f16cf4ab12552a9ff83.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h3>\u041a\u043e\u0434\u044b \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0430: cmd\/arcana\u2011gate\/<\/h3>\n<p>\u0414\u043b\u044f\u00a0CI\/CD \u0443\u0442\u0438\u043b\u0438\u0442 \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0430\u0436\u043d\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u0440\u0430\u043d\u0442\u0430\u0439\u043c\u0443 \u043a\u043e\u0434\u044b \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f. \u0415\u0441\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0432\u00a0\u043a\u043e\u043d\u0441\u043e\u043b\u044c \u00ab\u041d\u0435 \u043f\u0443\u0441\u043a\u0430\u0435\u043c\u00bb, \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u0440\u0430\u0434\u043e\u0441\u0442\u043d\u043e \u043f\u043e\u0439\u0434\u0435\u0442 \u0434\u0435\u043f\u043b\u043e\u0438\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0437\u0430\u0432\u044f\u0437\u0430\u043d\u043e \u043d\u0430\u00a0\u0441\u0442\u0440\u043e\u0433\u0438\u0435 os.Exit:<\/p>\n<p>exit 0\u00a0\u2014 \u043a\u0430\u0440\u0442\u0430 \u0431\u043b\u0430\u0433\u043e\u0441\u043a\u043b\u043e\u043d\u043d\u0430, \u0440\u0435\u043b\u0438\u0437 \u043e\u0434\u043e\u0431\u0440\u0435\u043d (\u0438\u043b\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d \u0440\u0435\u0436\u0438\u043c \u2011soft\u2011mode, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0430\u0435\u043c, \u043d\u043e\u00a0\u043d\u0435\u00a0\u043b\u043e\u043c\u0430\u0435\u043c \u0431\u0438\u043b\u0434).<\/p>\n<p>exit 1\u00a0\u2014 \u0432\u044b\u043f\u0430\u043b\u0430 \u043a\u0430\u0440\u0442\u0430 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0435\u0440\u0435\u0432\u0435\u0440\u043d\u0443\u0442\u044b\u0439 The Fool \u0438\u043b\u0438\u00a0\u0411\u0430\u0448\u043d\u044f), \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u043d\u0435\u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f.<\/p>\n<pre><code class=\"go\">func main() {if err := run(); err != nil {fmt.Fprintf(os.Stderr, \"\\n\u274c %v\\n\", err)os.Exit(1)}}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u043e\u0442 \u0442\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u0432\u0441\u0435 \u044d\u0442\u0430\u043f\u044b \u0432\u043c\u0435\u0441\u0442\u0435, \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441\u00a0\u043f\u043e\u0434\u0433\u0440\u0443\u0437\u043a\u0438 GITHUB_SHA, \u0437\u0430\u043a\u0430\u043d\u0447\u0438\u0432\u0430\u044f \u0432\u044b\u0432\u043e\u0434\u043e\u043c \u0432\u00a0\u043a\u043e\u043d\u0441\u043e\u043b\u044c \u0438 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u043e\u0439 \u0432\u00a0step summary:<\/p>\n<details class=\"spoiler\">\n<summary>main.go<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">func run() error {cfg, err := config.Load(os.Args[1:])if err != nil {return fmt.Errorf(\"failed to load configuration: %w\", err)}if cfg.Seed == \"\" {cfg.Seed = fmt.Sprintf(\"runtime-seed-%d\", time.Now().UnixNano())}var customDeck []domain.Cardeng := engine.New(customDeck, engine.Config{Strict: cfg.Strict,})ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()result, err := eng.Divine(ctx, cfg.Seed)if err != nil {return fmt.Errorf(\"divination failed: %w\", err)}termPresenter := presenter.NewTerminalPresenter(os.Stdout)if err := termPresenter.Render(result); err != nil {return fmt.Errorf(\"failed to render terminal output: %w\", err)}ghPresenter := presenter.NewGitHubPresenter()if err := ghPresenter.Render(result); err != nil {fmt.Fprintf(os.Stderr, \"Warning: failed to write GitHub step summary: %v\\n\", err)}if result.Status == domain.StatusBlock {return fmt.Errorf(\"arcana gate blocked the pipeline: %s\", result.Reason)}return nil}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<\/div>\n<\/details>\n<h3>\u0421\u0431\u043e\u0440\u043a\u0430 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430:.goreleaser.yml, Dockerfile<\/h3>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043a\u043e\u0433\u0434\u0430 \u043a\u043e\u0434 \u0433\u043e\u0442\u043e\u0432, \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a\u00a0\u0441\u0431\u043e\u0440\u043a\u0435 \u0432\u0441\u0435\u0433\u043e \u044d\u0442\u043e\u0433\u043e. \u041d\u0430\u0447\u043d\u0435\u043c \u0441\u00a0\u043a\u043e\u043d\u0444\u0438\u0433\u0430 \u0434\u043b\u044f\u00a0goreleaser. \u041f\u043e\u043c\u0438\u043c\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430, \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043f\u043e\u0434\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0439\u0434\u0435\u0442 \u0431\u0438\u043b\u0434. \u0422\u0430\u043a\u0436\u0435 \u0432\u0430\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c CGO_ENABLED=0\u00a0\u0434\u043b\u044f\u00a0\u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043e\u043d \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043b\u0441\u044f \u043d\u0430\u00a0\u043b\u044e\u0431\u043e\u0439 Linux \u043c\u0430\u0448\u0438\u043d\u0435.<\/p>\n<details class=\"spoiler\">\n<summary>.goreleaser.yml<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\">version: 2before:  hooks:    - go mod tidybuilds:  - id: arcana-gate    main: .\/cmd\/arcana-gate    binary: arcana-gate    env:      - CGO_ENABLED=0    goos:      - linux      - darwin      - windows    goarch:      - amd64      - arm64archives:  - format: tar.gz    name_template: \"{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}\"    format_overrides:      - goos: windows        format: zipchecksum:  name_template: \"checksums.txt\"snapshot:  version_template: \"{{ incpatch .Version }}-next\"changelog:  sort: asc  filters:    exclude:      - \"^docs:\"      - \"^test:\"<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<\/div>\n<\/details>\n<p>Dockerfile \u043f\u0438\u0448\u0435\u043c \u043f\u043e\u00a0\u0431\u0435\u0441\u0442\u2011\u043f\u0440\u0430\u043a\u0442\u0438\u0441\u0443(\u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u043d\u043e\u00a0\u0438 \u043f\u043e\u00a0\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438). \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043c\u0443\u043b\u044c\u0442\u0438\u0441\u0442\u0435\u0439\u0434\u0436 \u0441\u0431\u043e\u0440\u043a\u0443, \u0434\u0438\u0441\u0442\u0440\u043e\u043b\u0435\u0441\u0441 \u043e\u0431\u0440\u0430\u0437 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a \u043e\u0442\u00a0nonroot \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<details class=\"spoiler\">\n<summary>Dockerfile<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>FROM golang:1.26-alpine AS builderWORKDIR \/appCOPY go.mod .\/RUN go mod downloadCOPY . .RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \\    -ldflags=\"-s -w\" \\    -o \/app\/arcana-gate .\/cmd\/arcana-gateFROM gcr.io\/distroless\/static-debian12:nonrootWORKDIR \/COPY --from=builder \/app\/arcana-gate \/arcana-gateUSER nonroot:nonrootENTRYPOINT [\"\/arcana-gate\"]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<\/div>\n<\/details>\n<h3>\u0411\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0439 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0434\u043b\u044f\u00a0\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430:.github\/workflows\/<\/h3>\n<p>\u0420\u0430\u0437 \u0443\u0436 \u044f \u0434\u0435\u043b\u0430\u044e \u0443\u0442\u0438\u043b\u0438\u0442\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0440\u0435\u0448\u0430\u0435\u0442 \u0441\u0443\u0434\u044c\u0431\u0443 \u0440\u0435\u043b\u0438\u0437\u043e\u0432 \u0438 \u0432\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432\u00a0\u0447\u0443\u0436\u0438\u0435 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u044b, \u0441\u0430\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 arcana\u2011gate \u043e\u0431\u044f\u0437\u0430\u043d\u00a0\u0431\u044b\u0442\u044c \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u043c. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u043e\u0434\u0432\u0435\u0440\u0433\u043d\u0443\u0442\u044c \u0435\u0433\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430\u043c. \u041d\u0430\u00a0\u043a\u0430\u0436\u0434\u044b\u0439 Pull Request \u0438 \u043a\u043e\u043c\u043c\u0438\u0442 \u0432\u00a0main \u0443\u00a0\u043c\u0435\u043d\u044f \u043a\u0440\u0443\u0442\u0438\u0442\u0441\u044f DevSecOps Pipeline:<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e19\/72a\/d24\/e1972ad2493517048bd1a3ae70d5e345.png\" alt=\"\u041f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0432 GitHub Actions\" title=\"\u041f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0432 GitHub Actions\" width=\"2048\" height=\"695\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/e19\/72a\/d24\/e1972ad2493517048bd1a3ae70d5e345.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e19\/72a\/d24\/e1972ad2493517048bd1a3ae70d5e345.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u041f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0432\u00a0GitHub Actions<\/figcaption><\/div>\n<\/figure>\n<p>\u041e\u00a0\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430\u0445 \u043c\u043e\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0442\u0430\u0442\u044c\u0438, \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e \u0438\u0445 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u044f, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u043e\u0439\u0434\u0435\u043c\u0441\u044f \u043f\u043e\u00a0\u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u043c\u043e\u043c\u0435\u043d\u0442\u0430\u043c. \u0420\u0430\u0437\u043e\u0431\u044c\u0435\u043c \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u043d\u0430\u00a0\u0442\u0440\u0438 \u044d\u0442\u0430\u043f\u0430 (\u0432\u0430\u0436\u043d\u043e, \u0447\u0442\u043e\u00a0\u0441\u0430\u043c\u0438\u0445 \u0441\u0442\u0435\u0439\u0434\u0436\u0435\u0439 \u0431\u043e\u043b\u044c\u0448\u0435, \u043d\u043e\u00a0\u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0435\u0439\u0434\u0436\u0435\u0439 \u044f \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u043b \u0432\u00a0\u043e\u0434\u0438\u043d \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u044d\u0442\u0430\u043f).<\/p>\n<p>\u0412\u00a0\u0440\u0430\u043c\u043a\u0430\u0445 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u044d\u0442\u0430\u043f\u0430 \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u043f\u043e\u00a0\u043b\u0438\u043d\u0442\u0435\u0440\u0430\u043c \u0438\u00a0\u044e\u043d\u0438\u0442 \u0442\u0435\u0441\u0442\u0430\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u0430\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u044b \u0432\u00a0go test \u0444\u0430\u0439\u043b\u0430\u0445, \u0434\u0430\u043b\u0435\u0435 \u0441\u043a\u0430\u043d\u0438\u0440\u0443\u0435\u043c Go \u043a\u043e\u0434 \u043d\u0430\u00a0\u043d\u0435\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0435 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 (\u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 os\/exec, \u0443\u0442\u0435\u0447\u043a\u0438 \u043f\u0430\u043c\u044f\u0442\u0438) \u0441\u00a0\u043f\u043e\u043c\u043e\u0449\u044c\u044e govulncheck(SAST). \u0417\u0430\u0442\u0435\u043c \u0438\u043d\u0441\u043f\u0435\u043a\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0431\u0440\u0430\u0437\u044b \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432 \u0434\u043b\u044f\u00a0\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0438 \u0441\u0431\u043e\u0440\u043a\u0438 \u0441\u00a0\u043f\u043e\u043c\u043e\u0449\u044c\u044e Trivy. \u041d\u0430\u00a0\u044d\u0442\u043e\u043c \u043f\u0435\u0440\u0432\u044b\u0439 \u044d\u0442\u0430\u043f \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u0437\u0430\u043a\u0430\u043d\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0438 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u044c \u043a\u00a0\u0440\u0435\u043b\u0438\u0437\u0443.<\/p>\n<p>\u0412\u00a0\u0440\u0430\u043c\u043a\u0430\u0445 \u0432\u0442\u043e\u0440\u043e\u0433\u043e \u044d\u0442\u0430\u043f\u0430 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u043a\u0440\u043e\u0441\u0441\u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435\u043d\u043d\u044b\u0435 \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a\u0438 \u0441\u00a0\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0435\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u044b\u0445 \u0447\u0435\u043a\u0441\u0443\u043c SHA-256\u00a0\u0441\u00a0\u043f\u043e\u043c\u043e\u0449\u044c\u044e GoReleaser. \u042d\u0442\u043e\u0442 \u044d\u0442\u0430\u043f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u0434\u043b\u044f\u00a0\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430, \u0430\u00a0\u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438\u00a0\u043f\u0443\u0448\u0435 \u0442\u0435\u0433\u0430 \u0441\u00a0\u043d\u043e\u043c\u0435\u0440\u043e\u043c \u0432\u0435\u0440\u0441\u0438\u0438. \u041f\u043e\u00a0\u0438\u0442\u043e\u0433\u0443 \u044d\u0442\u043e\u0433\u043e \u044d\u0442\u0430\u043f\u0430 \u0432\u043e\u00a0\u0432\u043a\u043b\u0430\u0434\u043a\u0435 releases \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u043d\u043e\u043c\u0435\u0440\u043d\u043e\u0439 \u0440\u0435\u043b\u0438\u0437.<\/p>\n<p>\u0422\u0440\u0435\u0442\u0438\u0439 \u044d\u0442\u0430\u043f \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0435\u0442 \u043d\u0430\u0441, \u043a\u0430\u043a\u00a0\u0431\u044b \u043d\u0435\u00a0\u043f\u0440\u043e\u0448\u0435\u043b \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d(passed\/failed), \u043d\u043e\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u00a0\u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0430 \u0441\u00a0\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u043c\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0432\u0441\u0435\u0445 \u044d\u0442\u0430\u043f\u043e\u0432. \u0410\u043f\u0438 \u0431\u043e\u0442\u0430 \u0434\u043b\u044f\u00a0\u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c \u0432\u00a0\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u043c\u044b \u043d\u0435\u00a0\u043f\u0438\u0448\u0435\u043c(\u043a\u0430\u043a \u0438 \u0432\u00a0\u043b\u044e\u0431\u044b\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0438), \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u00a0\u0441\u0435\u043a\u0440\u0435\u0442\u044b \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f.<\/p>\n<details class=\"spoiler\">\n<summary>pipeline.yml<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\">name: CI\/CD Pipelineon:  push:    branches: [ main ]    tags:      - 'v*'    paths-ignore:      - 'assets\/**'      - '**.md'      - 'docs\/**'      - 'LICENSE'      - '.gitignore'  pull_request:    branches: [ main ]    paths-ignore:      - 'assets\/**'      - '**.md'      - 'docs\/**'      - 'LICENSE'      - '.gitignore'permissions:  contents: writeconcurrency:  group: ${{ github.workflow }}-${{ github.ref }}  cancel-in-progress: truejobs:  # Stage 1: Code Quality &amp; Unit Tests  test-and-lint:    name: Code Quality &amp; Unit Tests    runs-on: ubuntu-latest    steps:      - name: Checkout repository        uses: actions\/checkout@v4      - name: Set up Go        uses: actions\/setup-go@v5        with:          go-version: '1.24'          cache: true      - name: Run golangci-lint        uses: golangci\/golangci-lint-action@v6        with:          version: v1.64.5      - name: Run Unit Tests with Coverage &amp; Race Detector        run: |          go test -race -v -coverprofile=coverage.out $(go list .\/... | grep -v \/cmd)      - name: Verify Code Coverage Threshold        run: |          COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's\/%\/\/')          echo \"Total Code Coverage: ${COVERAGE}%\"          if (( $(echo \"$COVERAGE &lt; 80.0\" | bc -l) )); then            echo \"Error: Code coverage ($COVERAGE%) is below required threshold (80%)!\"            exit 1          fi  # Stage 2: Static Application Security Testing (SAST)  sast-scan:    name: Security Audit (SAST)    runs-on: ubuntu-latest    steps:      - name: Checkout repository        uses: actions\/checkout@v4      - name: Set up Go        uses: actions\/setup-go@v5        with:          go-version: '1.26'      - name: Run Official Go Vulnerability Checker (govulncheck)        run: |          go install golang.org\/x\/vuln\/cmd\/govulncheck@latest          govulncheck .\/...      - name: Run Gosec Security Scanner        uses: securego\/gosec@master        with:          args: '-no-fail -fmt sarif -out gosec.sarif .\/...'  # Stage 3: Container Vulnerability Scan  container-security:    name: Container Security (Trivy)    runs-on: ubuntu-latest    steps:      - name: Checkout repository        uses: actions\/checkout@v4      - name: Build Local Docker Image        run: |          docker build -t arcana-gate:test .      - name: Run Trivy Vulnerability Scanner        uses: aquasecurity\/trivy-action@master        with:          image-ref: 'arcana-gate:test'          format: 'table'          exit-code: '1'          ignore-unfixed: true          vuln-type: 'os,library'          severity: 'CRITICAL,HIGH'  # Stage 4: CD \/ Automated Release  release:    name: Create GitHub Release    needs: [test-and-lint, sast-scan, container-security]    if: startsWith(github.ref, 'refs\/tags\/v')    runs-on: ubuntu-latest    steps:      - name: Checkout repository        uses: actions\/checkout@v4        with:          fetch-depth: 0      - name: Set up Go        uses: actions\/setup-go@v5        with:          go-version: '1.26'      - name: Run GoReleaser        uses: goreleaser\/goreleaser-action@v6        with:          distribution: goreleaser          version: '~&gt; v2'          args: release --clean        env:          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}          # Stage 5: Telegram notification  telegram-notification:    name: \ud83d\udcf1 Telegram Alert    runs-on: ubuntu-latest    needs: [test-and-lint, sast-scan, container-security, release]    if: always()    steps:      - name: Send Telegram Notification        uses: appleboy\/telegram-action@master        with:          to: ${{ secrets.TELEGRAM_TO }}          token: ${{ secrets.TELEGRAM_TOKEN }}          format: markdown          message: |            \ud83d\udee1\ufe0f *DevSecOps Pipeline Status*                        \ud83d\udd39 *Repository:* `${{ github.repository }}`            \ud83d\udd39 *Branch\/Tag:* `${{ github.ref_name }}`            \ud83d\udd39 *Commit:* `${{ github.sha }}`                        \ud83d\udcca *Pipeline Status:* ${{ (needs.test-and-lint.result == 'success' &amp;&amp; needs.sast-scan.result == 'success' &amp;&amp; needs.container-security.result == 'success' &amp;&amp; needs.release.result != 'failure') &amp;&amp; '\u2705 SUCCESS' || '\u274c FAILED' }}            \ud83d\ude80 *Release \/ Deploy:* ${{ needs.release.result == 'success' &amp;&amp; '\u2705 Published' || (needs.release.result == 'skipped' &amp;&amp; '\u23e9 Skipped (No Tag)' || '\u274c Failed') }}                        \ud83d\udd17 [View Workflow Run](https:\/\/github.com\/${{ github.repository }}\/actions\/runs\/${{ github.run_id }})<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<\/div>\n<\/details>\n<h3>\u041f\u0440\u043e\u0432\u0435\u0440\u043e\u0447\u043d\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u0432\u043d\u0443\u0442\u0440\u0438 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f:.github\/workflows\/<\/h3>\n<p>\u041d\u0430\u043f\u0438\u0448\u0435\u043c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0434\u043b\u044f\u00a0\u0442\u0435\u0441\u0442\u0430 \u0432\u043d\u0443\u0442\u0440\u0438 \u044d\u0442\u043e\u0433\u043e\u00a0\u0436\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f. <code>uses: .\/<\/code> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0438\u0437\u00a0\u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e\u00a0\u0436\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f. \u0422\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043a\u0430\u043a\u00a0\u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043d\u0430\u0448 \u0432\u044b\u0432\u043e\u0434 step summary \u0432\u043d\u0443\u0442\u0440\u0438 github.<\/p>\n<pre><code class=\"yaml\">name: Demo Runon:  workflow_dispatch:jobs:  test-summary:    name: Run Arcana Gate Demo    runs-on: ubuntu-latest    steps:      - name: Checkout repository        uses: actions\/checkout@v4      - name: Test Local Action        uses: .\/<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u044b\u0432\u043e\u0434 step summary \u0432\u00a0GitHub Actions.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/171\/702\/696\/1717026969ff41770bf292af57657afc.png\" alt=\"demo.yml\" title=\"demo.yml\" width=\"2132\" height=\"1420\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/171\/702\/696\/1717026969ff41770bf292af57657afc.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/171\/702\/696\/1717026969ff41770bf292af57657afc.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>demo.yml<\/figcaption><\/div>\n<\/figure>\n<h3>\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u043a\u00a0GitHub Marketplace: action.yml<\/h3>\n<p>\u0421\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0430\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0438\u0437\u00a0\u043f\u0440\u043e\u0441\u0442\u043e \u0443\u0442\u0438\u043b\u0438\u0442\u044b \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432\u00a0\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u0447\u043a\u043e\u0439, \u0438 \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043d\u0430\u00a0GitHub Marketplace. \u0414\u043b\u044f\u00a0\u044d\u0442\u043e\u0433\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442 \u0432\u00a0\u0444\u0430\u0439\u043b\u0435 action.yml \u043f\u0440\u044f\u043c \u0432\u043d\u0443\u0442\u0440\u0438 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f:<\/p>\n<details class=\"spoiler\">\n<summary>action.yml<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"yaml\">name: 'Arcana CI Gate'description: 'Deterministic fate-based quality gate for CI\/CD pipelines using Tarot Arcana.'author: 'RewithSolo'branding:  icon: 'shield'  color: 'purple'inputs:  seed:    description: 'Deterministic seed for divination (defaults to GITHUB_SHA)'    required: false    default: '${{ github.sha }}'  strict:    description: 'Enable strict mode where all reversed cards result in a BLOCK'    required: false    default: 'false'  deck:    description: 'Path to custom YAML deck file'    required: false    default: ''runs:  using: 'composite'  steps:    - name: Set up Go      uses: actions\/setup-go@v5      with:        go-version: '1.26'    - name: Build Go binary      shell: bash      run: go build -o arcana-gate .\/cmd\/arcana-gate    - name: Run Arcana Gate      shell: bash      run: .\/arcana-gate      env:        ARCANA_SEED: ${{ inputs.seed }}        ARCANA_STRICT: ${{ inputs.strict }}        ARCANA_DECK_PATH: ${{ inputs.deck }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<\/div>\n<\/details>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0430 \u0432\u00a0\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 GitHub \u0441\u0430\u043c \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u0442 \u0432\u044b\u043b\u043e\u0436\u0438\u0442\u044c \u0435\u0433\u043e \u043d\u0430\u00a0Marketplace. \u0417\u0430\u0442\u0435\u043c \u0432\u00a0\u043b\u044e\u0431\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432\u00a0\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d:<\/p>\n<pre><code class=\"yaml\">- name: Check Fate (Arcana Gate)\u00a0\u00a0uses: RewithSolo\/arcana-gate@v1\u00a0\u00a0with:\u00a0\u00a0\u00a0\u00a0strict: 'true'<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430\u0442\u0438\u0432\u043d\u043e.<\/p>\n<h3>\u041f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0435 \u043a\u0430\u043c\u043d\u0438 \u0438 \u0442\u043e\u043d\u043a\u043e\u0441\u0442\u0438 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438<\/h3>\n<p>\u041f\u0440\u0438\u00a0\u0441\u0431\u043e\u0440\u043a\u0435 CLI\u2011\u0443\u0442\u0438\u043b\u0438\u0442 \u0434\u043b\u044f\u00a0CI\/CD \u0432\u044b\u043b\u0435\u0437\u0430\u0435\u0442 \u043f\u0430\u0440\u0430 \u043d\u0435\u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0445 \u043c\u043e\u043c\u0435\u043d\u0442\u043e\u0432:<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b Go \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043b\u0441\u044f \u043d\u0430\u00a0\u043b\u044e\u0431\u043e\u043c Linux \u0440\u0430\u043d\u043d\u0435\u0440\u0435 (\u0445\u043e\u0442\u044c Ubuntu, \u0445\u043e\u0442\u044c Alpine \u0431\u0435\u0437\u00a0glibc), \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u0435\u043c \u0441\u0442\u0440\u043e\u0433\u043e \u0431\u0435\u0437\u00a0CGO:<\/p>\n<pre><code>CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \\    -ldflags=\"-s -w\" \\    -o \/app\/arcana-gate .\/cmd\/arcana-gate<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0424\u043b\u0430\u0433\u0438 \u2011s \u2011w \u0432\u044b\u0440\u0435\u0437\u0430\u044e\u0442 \u043e\u0442\u043b\u0430\u0434\u043e\u0447\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e DWARF \u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432, \u0441\u0440\u0435\u0437\u0430\u044f \u0440\u0430\u0437\u043c\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430 \u043f\u043e\u0447\u0442\u0438 \u0432\u00a0\u0434\u0432\u0430 \u0440\u0430\u0437\u0430.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e, \u0442\u043e \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 GITHUB_SHA \u0432\u00a0\u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0438 \u043d\u0435\u0442. \u0427\u0442\u043e\u0431\u044b \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u043d\u0435\u00a0\u043f\u0430\u0434\u0430\u043b\u0430 \u0441\u00a0\u043f\u0430\u043d\u0438\u043a\u043e\u0439 \u043d\u0430\u00a0\u043f\u0443\u0441\u0442\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435, \u0431\u0435\u0440\u0435\u043c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u0441\u0435\u0432\u0434\u043e\u2011\u0441\u0438\u0434.<\/p>\n<h3>\u0418\u0442\u043e\u0433\u0438 \u0438 \u0412\u044b\u0432\u043e\u0434\u044b<\/h3>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430\u0447\u0438\u043d\u0430\u043b\u0441\u044f \u043a\u0430\u043a\u00a0\u0448\u0443\u0442\u043a\u0430 \u0438 \u0438\u0440\u043e\u043d\u0438\u044f \u043d\u0430\u0434 \u043d\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0441\u0442\u044c\u044e \u0438 \u043c\u0430\u0433\u0438\u0435\u0439 \u0432\u043e\u043a\u0440\u0443\u0433 \u0440\u0435\u043b\u0438\u0437\u0430, \u043d\u043e\u00a0\u0432\u00a0\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u043b\u0441\u044f \u0432\u00a0\u043a\u043b\u0430\u0441\u0441\u043d\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0434\u043b\u044f\u00a0\u043e\u0431\u043a\u0430\u0442\u043a\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0432\u0430\u0436\u043d\u044b\u0445 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u0439: <\/p>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u0434\u0435\u043c\u043f\u043e\u0442\u0435\u043d\u0442\u043d\u043e\u0433\u043e \u0438 \u0434\u0435\u0442\u0435\u0440\u043c\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u0441\u0435\u0432\u0434\u043e\u2011\u0440\u0430\u043d\u0434\u043e\u043c\u0430 \u043d\u0430\u00a0\u0431\u0430\u0437\u0435 \u0445\u044d\u0448\u0435\u0439 \u043a\u043e\u043c\u043c\u0438\u0442\u043e\u0432.<\/p>\n<p>\u041d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0447\u0438\u0441\u0442\u043e\u0433\u043e, \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0433\u043e CLI \u043d\u0430\u00a0Go \u0431\u0435\u0437\u00a0\u043b\u0438\u0448\u043d\u0438\u0445 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439.<\/p>\n<p>\u041e\u043f\u044b\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430 \u0434\u043b\u044f\u00a0GitHub Actions \u0441\u00a0\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0435\u0439 Markdown\u2011\u043e\u0442\u0447\u0435\u0442\u043e\u0432 \u043f\u0440\u044f\u043c\u043e \u0432\u00a0GITHUB_STEP_SUMMARY.<\/p>\n<p>\u041f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 DevSecOps\u2011\u043a\u043e\u043d\u0442\u0443\u0440\u0430, \u0437\u0430\u0449\u0438\u0449\u0430\u044e\u0449\u0435\u0433\u043e \u0441\u0430\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u043e\u0442\u00a0\u0443\u044f\u0437\u0432\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u0438 \u0443\u0442\u0435\u0447\u0435\u043a.<\/p>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043e\u0442\u043a\u0440\u044b\u0442\u044b: <a href=\"https:\/\/github.com\/RewithSolo\/arcana-gate\" rel=\"noopener nofollow\">https:\/\/github.com\/RewithSolo\/arcana\u2011gate<\/a><\/p>\n<p>\u041c\u043e\u0439 \u0442\u0433 \u043a\u0430\u043d\u0430\u043b: <a href=\"https:\/\/t.me\/rewithsolo\" rel=\"noopener nofollow\">https:\/\/t.me\/rewithsolo<\/a><\/p>\n<p>\u0411\u0443\u0434\u0443 \u0440\u0430\u0434 \u0437\u0432\u0435\u0437\u0434\u043e\u0447\u043a\u0430\u043c, \u0431\u0430\u0433\u0440\u0435\u043f\u043e\u0440\u0442\u0430\u043c \u0438 \u043d\u043e\u0432\u044b\u043c \u0438\u0434\u0435\u044f\u043c \u043a\u0430\u0440\u0442 \u0434\u043b\u044f\u00a0\u043a\u043e\u043b\u043e\u0434\u044b \u0432\u00a0Issues! \u0418 \u043f\u0443\u0441\u0442\u044c \u0432\u0430\u0448\u0438 \u0440\u0435\u043b\u0438\u0437\u044b \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u0443\u0434\u0443\u0442 \u0443\u0434\u0430\u0447\u043d\u044b\u043c\u0438.<\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1076600\/\">https:\/\/habr.com\/ru\/articles\/1076600\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0412\u044b\u0432\u043e\u0434 \u0432\u00a0\u043a\u043e\u043d\u0441\u043e\u043b\u044c \u043a\u0430\u0440\u0442\u044b\u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0432\u00a0\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430\u0445 \u0443\u0436\u0435 \u0441\u0442\u0430\u043b\u0438 \u0440\u0443\u0442\u0438\u043d\u043e\u0439,\u00a0\u043b\u0438\u043d\u0442\u0435\u0440\u044b, \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u043a\u043e\u043d\u0444\u0438\u0433\u043e\u0432, \u043e\u0431\u0440\u0430\u0437\u043e\u0432\u00a0\u2014 \u0432\u00a0\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u043a\u043b\u0430\u0434\u0435\u0442\u0441\u044f \u0432\u0441\u0435, \u0447\u0442\u043e\u00a0\u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u043b\u0438\u0437 \u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u0435\u0433\u043e \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u0435\u0435. \u0418 \u0432\u043e\u0442 \u0443\u043b\u0443\u0447\u0448\u0430\u044f \u0441\u0432\u043e\u0439 devsecops \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d, \u044f \u0437\u0430\u0434\u0443\u043c\u0430\u043b\u0441\u044f, \u043d\u0430\u0434 \u0442\u0435\u043c \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f\u00a0CI\/CD \u0438 \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c \u0435\u0433\u043e \u0432\u00a0\u0440\u0430\u043c\u043a\u0430\u0445 GitHub Actions. \u041e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u043d\u0435\u00a0\u0442\u0430\u043a \u0443\u0436 \u0438 \u0441\u043b\u043e\u0436\u043d\u043e, \u0432\u0435\u0434\u044c \u0434\u043b\u044f\u00a0\u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u043b\u043e\u0436\u0438\u0442\u044c \u0441\u0432\u043e\u044e \u0443\u0442\u0438\u043b\u0438\u0442\u0443 \u0432\u00a0GitHub Marketplace, \u043a\u043e\u0442\u043e\u0440\u044b\u0439\u00a0\u0431\u044b \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043b\u0441\u044f \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u0447\u043a\u043e\u0439, \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u043e \u043e\u0444\u043e\u0440\u043c\u0438\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433\u0438 \u0438 \u0437\u0430\u0440\u0435\u043b\u0438\u0437\u0438\u0442\u044c \u0441\u043e\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0441\u00a0\u0442\u0435\u0433\u043e\u043c \u0438 \u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u043e\u043c\u00a0\u2014 \u043c\u043e\u0434\u0435\u0440\u0430\u0446\u0438\u044f \u0432\u00a0\u043c\u0430\u0440\u043a\u0435\u0442\u043f\u043b\u0435\u0439\u0441\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f.\u00a0\u0421\u0430\u043c\u0430 \u0438\u0434\u0435\u044f \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0441\u0442\u0438 \u0432\u043d\u0443\u0442\u0440\u0438, \u0442\u0430\u043a\u2011\u0442\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0432\u0430\u0436\u043d\u043e\u0433\u043e \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u0430, \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430, \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0441\u0442\u0440\u0430\u043d\u043d\u043e\u0439, \u043d\u043e\u00a0\u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0438\u0440\u043e\u043d\u0438\u0447\u043d\u043e\u0439, \u0432\u0435\u0434\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u0433\u0430\u0434\u0430\u0435\u0442 \u043d\u0430\u00a0\u043a\u0430\u0440\u0442\u0430\u0445 \u0422\u0430\u0440\u043e, \u0431\u0443\u0434\u0435\u0442 \u0440\u0435\u043b\u0438\u0437 \u0443\u0434\u0430\u0447\u043d\u044b\u043c \u0438\u043b\u0438\u00a0\u043d\u0435\u0442. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b \u0433\u043b\u0430\u0432\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438: \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043b\u0435\u0433\u043a\u0443\u044e \u0438 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c\u0443\u044e \u0443\u0442\u0438\u043b\u0438\u0442\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f\u00a0\u0431\u044b\u043b\u0430\u00a0\u0431\u044b \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u0430 \u0432\u00a0\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 \u0432\u043d\u0443\u0442\u0440\u0438 GitHub Actions \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u0432\u043e\u0435\u0433\u043e \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f \u0438 \u0441\u0430\u043c\u0430\u00a0\u0431\u044b \u0441\u0442\u0430\u043b\u0430 \u0447\u0430\u0441\u0442\u044c\u044e \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430, \u0442\u0430\u043a\u0430\u044f \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u044f \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f.\u00a0\u041f\u043e\u0447\u0435\u043c\u0443 \u0447\u0438\u0441\u0442\u044b\u0439 \u0440\u0430\u043d\u0434\u043e\u043c \u0443\u0431\u0438\u0432\u0430\u0435\u0442 CI\/CD?\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u044f\u0437\u044b\u043a \u043d\u0430\u00a0\u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 Arcana Gate (\u0442\u0430\u043a \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0435\u043a\u0442)\u00a0\u2014 Golang, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e\u00a0\u043c\u043d\u0435\u00a0\u0431\u044b\u043b\u0430 \u0432\u0430\u0436\u043d\u0430 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0438 \u0441\u0431\u043e\u0440\u043a\u0430 \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a\u0430 \u043f\u043e\u0434\u00a0\u0440\u0430\u0437\u043d\u044b\u0435 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b, \u0430\u00a0\u0443\u00a0Go \u0435\u0441\u0442\u044c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 go\u2011releaser, \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432\u00a0yaml\u2011\u043c\u0430\u043d\u0438\u0444\u0435\u0441\u0442\u0435 \u043f\u0440\u044f\u043c \u0432\u00a0\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438. (\u043f\u043b\u044e\u0441 \u043c\u043d\u0435 \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f Go)\u0421\u0443\u0442\u044c \u0432\u00a0\u0442\u043e\u043c, \u0447\u0442\u043e\u00a0\u043d\u0430\u00a0\u043d\u0443\u0436\u043d\u043e\u043c \u044d\u0442\u0430\u043f\u0435 (\u044f\u00a0\u0431\u044b \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b \u0435\u0433\u043e \u043f\u0435\u0440\u0435\u0434 \u0441\u0430\u043c\u0438\u043c \u0434\u0435\u043f\u043b\u043e\u0435\u043c \u0432\u00a0\u0440\u0435\u043b\u0438\u0437\u00a0\u043b\u0438\u0431\u043e \u0441\u0430\u043c\u044b\u043c \u043f\u0435\u0440\u0432\u044b\u043c) \u0443\u0442\u0438\u043b\u0438\u0442\u0430\u00a0\u0431\u044b \u0432\u044b\u0442\u0430\u0441\u043a\u0438\u0432\u0430\u043b\u0430 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u0443\u044e \u043a\u0430\u0440\u0442\u0443 \u0438\u0437\u00a0\u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0439 \u043a\u043e\u043b\u043e\u0434\u044b \u0438 \u043f\u043e\u00a0\u0435\u0435 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u043b\u0430 \u00ab\u0431\u0443\u0434\u0443\u0449\u0435\u0435\u00bb \u0440\u0435\u043b\u0438\u0437\u0430, \u0431\u0443\u0434\u0435\u0442 \u043e\u043d \u0443\u0434\u0430\u0447\u043d\u044b\u043c \u0438\u043b\u0438\u00a0\u043d\u0435\u0442. \u0410\u00a0\u0432\u043e\u0442 \u043a\u0430\u043a\u00a0\u0440\u0430\u0437 \u043f\u043e\u043b\u043d\u0430\u044f \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0441\u0442\u044c \u043d\u0435\u00a0\u043e\u0447\u0435\u043d\u044c \u0432\u043e\u043e\u0434\u0443\u0448\u0435\u0432\u043b\u044f\u0435\u0442, \u0442\u0430\u043a \u043a\u0430\u043a\u00a0\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0434\u043e\u043b\u0436\u0435\u043d\u00a0\u0431\u044b\u0442\u044c \u0438\u0434\u0435\u043c\u043f\u043e\u0442\u0435\u043d\u0442\u043d\u044b\u043c. \u041f\u0440\u0438\u00a0\u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430 \u0434\u043b\u044f\u00a0\u043e\u0434\u043d\u043e\u0433\u043e \u0438 \u0442\u043e\u0433\u043e\u2011\u0436\u0435 \u043a\u043e\u043c\u043c\u0438\u0442\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043d\u0435\u00a0\u0434\u043e\u043b\u0436\u0435\u043d\u00a0\u0431\u044b\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u043c, \u0438 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0431\u0440\u0430\u0442\u044c math\/rand \u0441\u00a0\u0441\u0438\u0434\u043e\u043c \u043e\u0442\u00a0\u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 (time.Now().UnixNano()) \u0434\u043b\u044f\u00a0\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043a\u0430\u0440\u0442\u044b \u043f\u043e\u043b\u043e\u043c\u0430\u0435\u0442 \u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0430. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c seed \u0438\u0437\u00a0\u0445\u044d\u0448\u0430 \u043a\u043e\u043c\u043c\u0438\u0442\u0430 (GITHUB_SHA). \u041d\u043e\u00a0\u043a\u0430\u043a\u00a0\u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0447\u0438\u0441\u043b\u043e\u0432\u043e\u0439 seed \u0438\u0437\u00a0SHA \u0434\u043b\u044f\u00a0\u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440\u0430 \u043f\u0441\u0435\u0432\u0434\u043e\u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0447\u0438\u0441\u0435\u043b?func (e Engine) createRNG(seed string) rand.Rand {  hash := sha256.Sum256([]byte(seed))  seedInt := int64(binary.BigEndian.Uint64(hash[:8]))  return rand.New(rand.NewSource(seedInt))}\u0417\u0434\u0435\u0441\u044c \u043b\u043e\u0433\u0438\u043a\u0430 \u043f\u0440\u043e\u0441\u0442\u0430\u044f, \u0431\u0435\u0440\u0435\u043c \u0445\u044d\u0448 \u043a\u043e\u043c\u043c\u0438\u0442\u0430, \u043f\u0440\u043e\u0433\u043e\u043d\u044f\u0435\u043c \u0447\u0435\u0440\u0435\u0437 SHA-256, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0440\u0430\u0432\u043d\u043e\u043c\u0435\u0440\u043d\u043e\u0435 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0431\u0430\u0439\u0442, \u0438 \u043e\u0442\u0440\u0435\u0437\u0430\u0435\u043c \u043f\u0435\u0440\u0432\u044b\u0435 8\u00a0\u0431\u0430\u0439\u0442, \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u044f \u0438\u0445 \u0432\u00a0int64. \u042d\u0442\u043e\u0442 seedInt \u043c\u044b \u043e\u0442\u0434\u0430\u0435\u043c \u0432\u00a0rand.NewSource(). \u0422\u0435\u043f\u0435\u0440\u044c, \u0441\u043a\u043e\u043b\u044c\u043a\u043e\u00a0\u0431\u044b \u0440\u0430\u0437 \u043d\u0435\u00a0\u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043b\u0438 \u0443\u043f\u0430\u0432\u0448\u0438\u0439 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0432\u00a0GitHub Actions \u043a\u043d\u043e\u043f\u043a\u043e\u0439 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430, \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0434\u0430\u0441\u0442 \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u0443\u044e \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c.\u00a0\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 CLI \u043d\u0430\u00a0Go\u0418\u0435\u0440\u0430\u0440\u0445\u0438\u044f \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f \u0438 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430.arcana-gate\/\u251c\u2500\u2500 .github\/\u2502   \u2514\u2500\u2500 workflows\/\u2502       \u251c\u2500\u2500 pipeline.yml\u2502       \u2514\u2500\u2500 demo.yml\u251c\u2500\u2500 cmd\/\u2502   \u2514\u2500\u2500 arcana-gate\/\u251c\u2500\u2500 internal\/\u2502   \u251c\u2500\u2500 config\/\u2502   \u251c\u2500\u2500 domain\/\u2502   \u251c\u2500\u2500 engine\/\u2502   \u2514\u2500\u2500 presenter\/\u251c\u2500\u2500 action.yml\u251c\u2500\u2500 Dockerfile\u2514\u2500\u2500 .goreleaser.yml\u041e\u0431\u043e\u00a0\u0432\u0441\u0435\u0445 \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0445 \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043f\u043e\u00a0\u0445\u043e\u0434\u0443 \u0441\u0442\u0430\u0442\u044c\u0438.\u041f\u043e\u0434\u0433\u0440\u0443\u0437\u043a\u0430 GITHUB_SHA: internal\/config\/\u0414\u043b\u044f\u00a0\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f seed \u0438\u0437\u00a0GITHUB_SHA, \u043d\u0430\u0434\u043e \u043f\u043e\u0434\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0435\u0433\u043e \u0434\u043b\u044f\u00a0\u043d\u0430\u0447\u0430\u043b\u0430 \u0447\u0435\u0440\u0435\u0437 os.getenv. Seed \u043f\u043e\u0434\u0433\u0440\u0443\u0437\u0438\u0442\u0441\u044f \u0438\u0437\u00a0\u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437\u00a0\u0442\u0440\u0435\u0445 \u043c\u0435\u0441\u0442: \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u0438\u0434\u0430 arcana_seed (\u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u0432\u043d\u0435 github), github_sha \u0438 ci_commit_sha. \u0422\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u043c\u0438\u043c\u043e \u044d\u0442\u043e\u0433\u043e \u0444\u043e\u0440\u043c\u0430\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0443\u0442\u0438\u043b\u0438\u0442\u044b\u00a0\u2014 strict, \u044d\u0442\u0430 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0431\u0443\u0434\u0435\u0442\u00a0\u043b\u0438 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u043f\u0440\u0438\u00a0\u043d\u0435\u0433\u0430\u0442\u0438\u0432\u043d\u043e\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0440\u0430\u0441\u043a\u043b\u0430\u0434\u0430. \u0410\u00a0\u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0434\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u0443\u044e \u043a\u043e\u043b\u043e\u0434\u0443 \u043a\u0430\u0440\u0442.config.gotype Config struct {Seed           stringStrict         boolCustomDeckPath string}func Load(args []string) (*Config, error) {flags := flag.NewFlagSet(&#171;arcana-gate&#187;, flag.ContinueOnError)var (seed           stringstrict         boolcustomDeckPath string)flags.StringVar(&amp;seed, &#171;seed&#187;, &#171;&#187;, &#171;Deterministic seed for divination (defaults to GITHUB_SHA or CI_COMMIT_SHA)&#187;)flags.BoolVar(&amp;strict, &#171;strict&#187;, false, &#171;Enable strict mode where all reversed cards result in a BLOCK&#187;)flags.StringVar(&amp;customDeckPath, &#171;deck&#187;, &#171;&#187;, &#171;Path to a custom YAML deck file&#187;)if err := flags.Parse(args); err != nil {return nil, fmt.Errorf(&#171;failed to parse command-line flags: %w&#187;, err)}cfg := &amp;Config{Seed:           seed,Strict:         strict,CustomDeckPath: customDeckPath,}if cfg.Seed == &#171;&#187; {if val := os.Getenv(&#171;ARCANA_SEED&#187;); val != &#171;&#187; {cfg.Seed = val} else if val := os.Getenv(&#171;GITHUB_SHA&#187;); val != &#171;&#187; {cfg.Seed = val} else if val := os.Getenv(&#171;CI_COMMIT_SHA&#187;); val != &#171;&#187; {cfg.Seed = val}}if !cfg.Strict {if val := os.Getenv(&#171;ARCANA_STRICT&#187;); val != &#171;&#187; {parsed, err := strconv.ParseBool(val)if err == nil {cfg.Strict = parsed}}}if cfg.CustomDeckPath == &#171;&#187; {if val := os.Getenv(&#171;ARCANA_DECK_PATH&#187;); val != &#171;&#187; {cfg.CustomDeckPath = val}}return cfg, nil}\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043a\u0430\u0440\u0442 \u0438 \u043a\u043e\u043b\u043e\u0434\u044b: internal\/domain\/\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a\u00a0\u0441\u0430\u043c\u043e\u043c\u0443 \u043d\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044e, \u043a\u00a0\u043a\u0430\u0440\u0442\u0430\u043c, \u0438\u0445 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f\u043c \u0438 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u043c, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e\u00a0\u043a\u0430\u0440\u0442\u044b \u0440\u0430\u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043a\u0430\u043a\u00a0\u0432\u00a0\u043e\u0431\u044b\u0447\u043d\u043e\u043c \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0442\u0430\u043a \u0438 \u043c\u043e\u0433\u0443\u0442\u00a0\u0431\u044b\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u0435\u0440\u043d\u0443\u0442\u044b.\u00a0card.gotype Position stringconst (Upright  Position = &#171;UPRIGHT&#187;Reversed Position = &#171;REVERSED&#187;)type Suit stringconst (MajorArcana Suit = &#171;MAJOR_ARCANA&#187;Wands       Suit = &#171;WANDS&#187;Cups        Suit = &#171;CUPS&#187;Swords      Suit = &#171;SWORDS&#187;Pentacles   Suit = &#171;PENTACLES&#187;)type Card struct {ID            string `json:&#187;id&#187; yaml:&#187;id&#187;`Name          string `json:&#187;name&#187; yaml:&#187;name&#187;`Suit          Suit   `json:&#187;suit&#187; yaml:&#187;suit&#187;`Value         int    `json:&#187;value&#187; yaml:&#187;value&#187;`Description   string `json:&#187;description&#187; yaml:&#187;description&#187;`BlockUpright  bool   `json:&#187;block_upright&#187; yaml:&#187;block_upright&#187;`BlockReversed bool   `json:&#187;block_reversed&#187; yaml:&#187;block_reversed&#187;`}type DrawnCard struct {Card     Card     `json:&#187;card&#187;`Position Position `json:&#187;position&#187;`}func (dc DrawnCard) IsBlocking() bool {if dc.Position == Upright &amp;&amp; dc.Card.BlockUpright {return true}if dc.Position == Reversed &amp;&amp; dc.Card.BlockReversed {return true}return false}\u042f \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0440\u0430\u0437\u043d\u044b\u0435 \u043d\u0430\u0431\u043e\u0440\u044b \u043a\u0430\u0440\u0442, \u043d\u043e\u00a0\u043f\u043e\u043a\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0430\u0440\u0448\u0438\u0435 \u0430\u0440\u043a\u0430\u043d\u044b MAJOR_ARCANA, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e\u00a0\u043c\u0435\u0445\u0430\u043d\u0438\u043a\u0430 \u043f\u043e\u043a\u0430 \u0441\u0442\u0440\u043e\u0438\u0442\u0441\u044f \u043d\u0430\u00a0\u0432\u044b\u0442\u044f\u0433\u0438\u0432\u0430\u043d\u0438\u0438 \u043e\u0434\u043d\u043e\u0439 \u043a\u0430\u0440\u0442\u044b. \u041e\u0431\u044b\u0447\u043d\u043e \u0440\u0430\u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u044e\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u0430\u0440\u0442, \u043d\u043e\u00a0\u043d\u0430\u00a0\u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0444\u043e\u0440\u043c\u0430\u0442 \u0432\u044b\u0442\u044f\u0433\u0438\u0432\u0430\u043d\u0438\u044f \u043a\u0430\u0440\u0442 \u0441\u0445\u043e\u0436 \u0441\u00a0\u043c\u0435\u0445\u0430\u043d\u0438\u043a\u043e\u0439 \u0422\u0430\u0440\u043e \u0438\u0437\u00a0Phasmophobia. \u0412\u0441\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442\u00a0\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u043a\u00a0\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u043a\u0430\u0440\u0442\u044b, \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0432\u00a0\u0444\u0443\u043d\u043a\u0446\u0438\u0438 IsBlocking. \u0418\u043c\u0435\u043d\u043d\u043e \u043d\u0430\u00a0\u044d\u0442\u0430\u043f\u0435 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u0430\u0440\u0442\u044b \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u0432\u00a0\u043a\u0430\u043a\u043e\u043c \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043e\u043d\u0430 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d. C\u0430\u043c\u0430 \u043a\u043e\u043b\u043e\u0434\u0430 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:deck.gofunc DefaultDeck() []domain.Card {return []domain.Card{{ID:            &#171;tower&#187;,Name:          &#171;The Tower&#187;,Suit:          domain.MajorArcana,Value:         16,Description:   &#171;Catastrophic infrastructure outage, production crash, and data loss.&#187;,BlockUpright:  true,BlockReversed: true,},{ID:            &#171;death&#187;,Name:          &#171;Death&#187;,Suit:          domain.MajorArcana,Value:         13,Description:   &#171;Irreversible architecture transformation. Danger: potential database wipe.&#187;,BlockUpright:  true,BlockReversed: false,},{ID:            &#171;devil&#187;,Name:          &#171;The Devil&#187;,Suit:          domain.MajorArcana,Value:         15,Description:   &#171;Critical technical debt and unmaintainable code hacks have breached production.&#187;,BlockUpright:  true,BlockReversed: true,},{ID:            &#171;sun&#187;,Name:          &#171;The Sun&#187;,Suit:          domain.MajorArcana,Value:         19,Description:   &#171;Flawless release, 100% test coverage, and absolute uptime stability.&#187;,BlockUpright:  false,BlockReversed: false,},{ID:            &#171;world&#187;,Name:          &#171;The World&#187;,Suit:          domain.MajorArcana,Value:         21,Description:   &#171;Perfect microservices synergy, zero latency, and pure green telemetry.&#187;,BlockUpright:  false,BlockReversed: false,},{ID:            &#171;fool&#187;,Name:          &#171;The Fool&#187;,Suit:          domain.MajorArcana,Value:         0,Description:   &#171;Untested Friday evening release. Pure luck-driven deployment.&#187;,BlockUpright:  false,BlockReversed: true,},{ID:            &#171;high_priestess&#187;,Name:          &#171;The High Priestess&#187;,Suit:          domain.MajorArcana,Value:         2,Description:   &#171;Hidden edge-case bugs, subtle race conditions, and unhandled memory leaks.&#187;,BlockUpright:  true,BlockReversed: false,},{ID:            &#171;wheel_of_fortune&#187;,Name:          &#171;The Wheel of Fortune&#187;,Suit:          domain.MajorArcana,Value:         10,Description:   &#171;Pipeline outcome depends entirely on pure chance and external dependencies.&#187;,BlockUpright:  false,BlockReversed: false,},{ID:            &#171;hanged_man&#187;,Name:          &#171;The Hanged Man&#187;,Suit:          domain.MajorArcana,Value:         12,Description:   &#171;Deadlocks, hanging background workers, and infinite CI\/CD build loops.&#187;,BlockUpright:  true,BlockReversed: false,},}}\u0422\u0430\u043a\u0436\u0435 \u043f\u043e\u043b\u0435 description \u043a\u0430\u0440\u0442\u044b \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u0431\u0449\u0435\u0435 \u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0430\u043d\u0438\u0435 \u043d\u0430\u00a0\u0440\u0435\u043b\u0438\u0437, \u0443\u00a0\u043a\u0430\u0436\u0434\u043e\u0439 \u043a\u0430\u0440\u0442\u044b \u0441\u0432\u043e\u0435. \u042d\u0442\u0443 \u043a\u043e\u043b\u043e\u0434\u0443 \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0443\u0433\u043e\u0434\u043d\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043d\u043e\u0432\u044b\u043c\u0438 \u043a\u0430\u0440\u0442\u0430\u043c\u0438 \u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f\u043c\u0438.\u041b\u043e\u0433\u0438\u043a\u0430 \u0434\u0432\u0438\u0436\u043a\u0430: internal\/engine\/\u200b\u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u0433\u043b\u0430\u0432\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e Divine. \u041d\u0430\u00a0\u0432\u0445\u043e\u0434 \u043e\u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0442\u043e\u0442 \u0441\u0430\u043c\u044b\u0439 \u0441\u0438\u0434(\u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0438\u0437\u00a0GITHUB_SHA) \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0435\u0433\u043e. \u0415\u0441\u043b\u0438 \u0441\u0438\u0434 \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u043d\u0435\u043f\u0443\u0441\u0442\u044b\u043c, \u0442\u043e \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u043f\u0441\u0435\u0432\u0434\u043e\u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0447\u0438\u0441\u0435\u043b RNG \u043d\u0430\u00a0\u043e\u0441\u043d\u043e\u0432\u0435 \u044d\u0442\u043e\u0433\u043e \u0441\u0438\u0434\u0430. \u0417\u0430\u0442\u0435\u043c \u0432\u00a0\u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043a\u0430\u0440\u0442 \u0432\u00a0\u043a\u043e\u043b\u043e\u0434\u0435 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f \u0438\u043d\u0434\u0435\u043a\u0441 \u043e\u0434\u043d\u043e\u0439 \u0438\u0437\u00a0\u043d\u0438\u0445, \u0438\u0437\u00a0\u043a\u043e\u043b\u043e\u0434\u044b \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u0440\u0442\u0430 \u0441\u00a0\u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0438\u043d\u0434\u0435\u043a\u0441\u043e\u043c drawnCard. \u0422\u0435\u043f\u0435\u0440\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0440\u0438\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043a\u0430\u0440\u0442\u044b(\u043f\u0440\u044f\u043c\u0430\u044f\/\u043f\u0435\u0440\u0435\u0432\u0435\u0440\u043d\u0443\u0442\u0430\u044f), \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c \u043c\u0435\u0436\u0434\u0443 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430\u043c\u0438 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f 50\u00a0\u043d\u0430 50. \u0418\u0437\u00a0\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442 resultCard \u043e\u0437\u043d\u0430\u0447\u0430\u044e\u0449\u0438\u0439 \u0444\u0438\u043d\u0430\u043b\u044c\u043d\u0443\u044e \u043a\u0430\u0440\u0442\u0443 \u0441\u00a0\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c. \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0438 \u043f\u043e\u0441\u043b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0438 \u043a\u0430\u0440\u0442\u044b \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438. \u041e\u0434\u043d\u0430\u043a\u043e \u0435\u0449\u0435 \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 config.Strict, \u043e\u043d\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u00a0\u043b\u0438 \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0434\u0430\u043b\u044c\u0448\u0435 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d \u0435\u0441\u043b\u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043a\u0430\u0440\u0442\u044b \u0431\u0443\u0434\u0435\u0442 block. \u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f strict \u0437\u0430\u0434\u0430\u0435\u0442\u0441\u044f \u043d\u0430\u00a0\u044d\u0442\u0430\u043f\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u0432\u00a0\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0435.engine.gotype Config struct {Strict bool}type Engine struct {deck   []domain.Cardconfig Config}func New(deck []domain.Card, cfg Config) *Engine {if len(deck) == 0 {deck = DefaultDeck()}return &amp;Engine{deck:   deck,config: cfg,}}func (e *Engine) Divine(ctx context.Context, seed string) (*domain.GateResult, error) {if seed == &#171;&#187; {return nil,&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-492899","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/492899","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=492899"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/492899\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=492899"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=492899"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=492899"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}