Ben преди 1 седмица
родител
ревизия
a7e7afa8fd
променени са 12 файла, в които са добавени 771 реда и са изтрити 19 реда
  1. 5 0
      .gitignore
  2. 28 0
      go.mod
  3. 78 0
      go.sum
  4. 2 2
      pkg/state/state.go
  5. 8 1
      pkg/symbols/symbols.go
  6. 12 6
      state/state.go
  7. 27 0
      tools/aws.go
  8. 13 0
      tools/cloudformation.go
  9. 468 0
      tools/docker.go
  10. 112 0
      tools/git.go
  11. 17 0
      tools/terraform.go
  12. 1 10
      tools/tools.go

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+workspace
+test
+pkg/job
+main.go
+Dockerfile

+ 28 - 0
go.mod

@@ -6,26 +6,54 @@ require (
 	dario.cat/mergo v1.0.2
 	github.com/go-git/go-git/v6 v6.0.0-20260220113129-c02711164eb8
 	github.com/google/uuid v1.6.0
+	github.com/moby/go-archive v0.2.0
+	github.com/moby/moby/api v1.53.0
+	github.com/moby/moby/client v0.2.2
+	github.com/opencontainers/image-spec v1.1.1
 	github.com/otiai10/copy v1.14.1
 	github.com/sirupsen/logrus v1.9.4
 	github.com/traefik/yaegi v0.16.1
 	golang.org/x/mod v0.33.0
+	gopkg.in/yaml.v3 v3.0.1
 )
 
 require (
+	github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
 	github.com/Microsoft/go-winio v0.6.2 // indirect
 	github.com/ProtonMail/go-crypto v1.3.0 // indirect
 	github.com/cloudflare/circl v1.6.1 // indirect
+	github.com/containerd/errdefs v1.0.0 // indirect
+	github.com/containerd/errdefs/pkg v0.3.0 // indirect
+	github.com/containerd/log v0.1.0 // indirect
 	github.com/cyphar/filepath-securejoin v0.6.1 // indirect
+	github.com/distribution/reference v0.6.0 // indirect
+	github.com/docker/go-connections v0.6.0 // indirect
+	github.com/docker/go-units v0.5.0 // indirect
 	github.com/emirpasic/gods v1.18.1 // indirect
+	github.com/felixge/httpsnoop v1.0.4 // indirect
 	github.com/go-git/gcfg/v2 v2.0.2 // indirect
 	github.com/go-git/go-billy/v6 v6.0.0-20260114122816-19306b749ecc // indirect
+	github.com/go-logr/logr v1.4.2 // indirect
+	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
 	github.com/kevinburke/ssh_config v1.5.0 // indirect
+	github.com/klauspost/compress v1.18.2 // indirect
 	github.com/klauspost/cpuid/v2 v2.3.0 // indirect
+	github.com/moby/docker-image-spec v1.3.1 // indirect
+	github.com/moby/patternmatcher v0.6.0 // indirect
+	github.com/moby/sys/sequential v0.6.0 // indirect
+	github.com/moby/sys/user v0.4.0 // indirect
+	github.com/moby/sys/userns v0.1.0 // indirect
+	github.com/moby/term v0.5.2 // indirect
+	github.com/opencontainers/go-digest v1.0.0 // indirect
 	github.com/otiai10/mint v1.6.3 // indirect
 	github.com/pjbgf/sha1cd v0.5.0 // indirect
 	github.com/sergi/go-diff v1.4.0 // indirect
+	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
+	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
+	go.opentelemetry.io/otel v1.35.0 // indirect
+	go.opentelemetry.io/otel/metric v1.35.0 // indirect
+	go.opentelemetry.io/otel/trace v1.35.0 // indirect
 	golang.org/x/crypto v0.48.0 // indirect
 	golang.org/x/net v0.50.0 // indirect
 	golang.org/x/sync v0.8.0 // indirect

+ 78 - 0
go.sum

@@ -1,5 +1,9 @@
 dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
 dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
+github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
+github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
+github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
+github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
 github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
 github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
 github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
@@ -10,13 +14,29 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
 github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
+github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
+github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
+github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
+github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
+github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
+github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
+github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
+github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
 github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
 github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
+github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
+github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
+github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
+github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
+github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
 github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
 github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
 github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo=
@@ -27,17 +47,52 @@ github.com/go-git/go-git-fixtures/v5 v5.1.2-0.20260122163445-0622d7459a67 h1:3hu
 github.com/go-git/go-git-fixtures/v5 v5.1.2-0.20260122163445-0622d7459a67/go.mod h1:xKt0pNHST9tYHvbiLxSY27CQWFwgIxBJuDrOE0JvbZw=
 github.com/go-git/go-git/v6 v6.0.0-20260220113129-c02711164eb8 h1:+d5lkyJxzoOgoTZsBMtfEPvubbdh3pHoUZh0o0IkR1g=
 github.com/go-git/go-git/v6 v6.0.0-20260220113129-c02711164eb8/go.mod h1:B88nWzfnhTlIikoJ4d84Nc9noKS5mJoA7SgDdkt0aPU=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
 github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/kevinburke/ssh_config v1.5.0 h1:3cPZmE54xb5j3G5xQCjSvokqNwU2uW+3ry1+PRLSPpA=
 github.com/kevinburke/ssh_config v1.5.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
+github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
+github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
 github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
 github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
+github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
+github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
+github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
+github.com/moby/moby/api v1.53.0 h1:PihqG1ncw4W+8mZs69jlwGXdaYBeb5brF6BL7mPIS/w=
+github.com/moby/moby/api v1.53.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
+github.com/moby/moby/client v0.2.2 h1:Pt4hRMCAIlyjL3cr8M5TrXCwKzguebPAc2do2ur7dEM=
+github.com/moby/moby/client v0.2.2/go.mod h1:2EkIPVNCqR05CMIzL1mfA07t0HvVUUOl85pasRz/GmQ=
+github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
+github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
+github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
+github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
+github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
+github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
+github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
+github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
+github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
+github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
+github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
+github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
+github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
+github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
 github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
 github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
 github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
@@ -46,6 +101,8 @@ github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
 github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
 github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
 github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
 github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
@@ -56,6 +113,20 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
 github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
 github.com/traefik/yaegi v0.16.1 h1:f1De3DVJqIDKmnasUF6MwmWv1dSEEat0wcpXhD2On3E=
 github.com/traefik/yaegi v0.16.1/go.mod h1:4eVhbPb3LnD2VigQjhYbEJ69vDRFdT2HQNrXx8eEwUY=
+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
+go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
+go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
+go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
+go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
+go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
+go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
+go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
+go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
+go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
+go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
 golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
 golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
 golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
@@ -64,6 +135,7 @@ golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
 golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
 golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
 golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
 golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
 golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
@@ -72,7 +144,13 @@ golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
 golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
+gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
+pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
+pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=

+ 2 - 2
pkg/state/state.go

@@ -9,8 +9,8 @@ import (
 )
 
 type State struct {
-	id   string
-	data sync.Map
+	id      string
+	data    sync.Map
 }
 
 func New() *State {

+ 8 - 1
pkg/symbols/symbols.go

@@ -5,6 +5,7 @@ import (
 	"reflect"
 
 	"dario.cat/mergo"
+	"git.bazzel.dev/bmallen/helios/tools"
 	"github.com/google/uuid"
 )
 
@@ -15,7 +16,13 @@ var (
 			"CommandContext": reflect.ValueOf(exec.CommandContext),
 			"LookPath":       reflect.ValueOf(exec.LookPath),
 		},
-		"git.bazzel.dev/bmallen/helios/tools/tools": {},
+		"git.bazzel.dev/bmallen/helios/tools/tools": {
+			"Docker":         reflect.ValueOf(tools.Docker),
+			"CloudFormation": reflect.ValueOf(tools.CloudFormation),
+			"Terraform":      reflect.ValueOf(tools.Terraform),
+			"Git":            reflect.ValueOf(tools.Git),
+			"AWS":            reflect.ValueOf(tools.AWS),
+		},
 		"github.com/google/uuid/uuid": {
 			"New":       reflect.ValueOf(uuid.New),
 			"NewString": reflect.ValueOf(uuid.NewString),

+ 12 - 6
state/state.go

@@ -1,9 +1,15 @@
 package state
 
-func Get(key string) any
-func Set(key string, value any)
-func Delete(key string)
-func Keys() []string
-func Range(f func(key string, value any) bool)
+import (
+	s "git.bazzel.dev/bmallen/helios/pkg/state"
+)
 
-func ID() string
+var defaultState = s.New()
+
+func Get(key string) any                       { return defaultState.Get(key) }
+func Set(key string, value any)                { defaultState.Set(key, value) }
+func Delete(key string)                        { defaultState.Delete(key) }
+func Keys() []string                           { return defaultState.Keys() }
+func Range(f func(key string, value any) bool) { defaultState.Range(f) }
+
+func ID() string { return defaultState.ID() }

+ 27 - 0
tools/aws.go

@@ -0,0 +1,27 @@
+package tools
+
+func AWS() *aws {
+	return &aws{}
+}
+
+type (
+	aws struct{}
+	s3  struct {
+		aws *aws
+	}
+	ec2 struct {
+		aws *aws
+	}
+)
+
+func (t *aws) S3() *s3   { return &s3{aws: t} }
+func (t *aws) EC2() *ec2 { return &ec2{aws: t} }
+
+func (t *s3) Put(src, dst string) error  { return nil }
+func (t *s3) Get(src, dst string) error  { return nil }
+func (t *s3) List(src, dst string) error { return nil }
+
+func (t *ec2) Create() error    { return nil }
+func (t *ec2) Stop() error      { return nil }
+func (t *ec2) Start() error     { return nil }
+func (t *ec2) Terminate() error { return nil }

+ 13 - 0
tools/cloudformation.go

@@ -0,0 +1,13 @@
+package tools
+
+func CloudFormation() *cloudformation {
+	return &cloudformation{}
+}
+
+type (
+	cloudformation struct{}
+)
+
+func (t *cloudformation) CreateStack() {}
+func (t *cloudformation) UpdateStack() {}
+func (t *cloudformation) DeleteStack() {}

+ 468 - 0
tools/docker.go

@@ -0,0 +1,468 @@
+package tools
+
+import (
+	"context"
+	"io"
+	"net/netip"
+	"os"
+	"strings"
+	"time"
+
+	"github.com/moby/go-archive"
+	"github.com/moby/moby/api/pkg/stdcopy"
+	"github.com/moby/moby/api/types/container"
+	"github.com/moby/moby/api/types/network"
+	"github.com/moby/term"
+
+	"github.com/moby/moby/client"
+	"github.com/moby/moby/client/pkg/jsonmessage"
+	v1 "github.com/opencontainers/image-spec/specs-go/v1"
+)
+
+func Docker() *docker {
+	ctx := context.Background()
+
+	return &docker{
+		ctx: ctx,
+	}
+}
+
+type (
+	docker struct {
+		ctx context.Context
+	}
+	dockerImage struct {
+		docker *docker
+		auth   string
+		out    io.Writer
+	}
+	dockerContainer struct {
+		docker     *docker
+		id         string
+		name       string
+		config     *container.Config
+		hostconfig *container.HostConfig
+		network    *network.NetworkingConfig
+	}
+)
+
+func (t *docker) client() (*client.Client, error) {
+	cli, err := client.New(client.FromEnv)
+	if err != nil {
+		return nil, err
+	}
+
+	return cli, nil
+}
+
+func (t *docker) ContainerList() ([]*dockerContainer, error) {
+	cli, err := t.client()
+	if err != nil {
+		return nil, err
+	}
+
+	defer cli.Close()
+
+	containers, err := cli.ContainerList(context.Background(), client.ContainerListOptions{All: true})
+	if err != nil {
+		return nil, err
+	}
+
+	res := []*dockerContainer{}
+	for _, ctr := range containers.Items {
+		c, err := t.ContainerGetByID(ctr.ID)
+		if err != nil {
+			return nil, err
+		}
+		res = append(res, c)
+	}
+
+	return res, nil
+}
+
+func (t *docker) ContainerGetByID(id string) (*dockerContainer, error) {
+	cli, err := t.client()
+	if err != nil {
+		return nil, err
+	}
+
+	defer cli.Close()
+
+	c, err := cli.ContainerInspect(context.Background(), id, client.ContainerInspectOptions{})
+	if err != nil {
+		return nil, err
+	}
+
+	return &dockerContainer{
+		docker:     t,
+		id:         id,
+		name:       strings.TrimPrefix(c.Container.Name, "/"),
+		config:     c.Container.Config,
+		hostconfig: c.Container.HostConfig,
+		network: &network.NetworkingConfig{
+			EndpointsConfig: map[string]*network.EndpointSettings{},
+		},
+	}, nil
+}
+func (t *docker) ContainerGetByName(name string) (*dockerContainer, error) {
+	cli, err := t.client()
+	if err != nil {
+		return nil, err
+	}
+
+	defer cli.Close()
+
+	filters := client.Filters{}
+	filters.Add("name", name)
+
+	containers, err := cli.ContainerList(context.Background(),
+		client.ContainerListOptions{
+			Filters: filters,
+		})
+	if err != nil {
+		return nil, err
+	}
+
+	var id string
+
+	for _, ctr := range containers.Items {
+		for _, n := range ctr.Names {
+			if strings.TrimPrefix(n, "/") == name {
+				id = ctr.ID
+			}
+		}
+	}
+
+	return t.ContainerGetByID(id)
+}
+
+func (t *docker) ContainerNew(name string) *dockerContainer {
+	return &dockerContainer{
+		name:   name,
+		docker: t,
+		config: &container.Config{
+			ExposedPorts: network.PortSet{},
+		},
+		hostconfig: &container.HostConfig{
+			AutoRemove: false,
+		},
+		network: &network.NetworkingConfig{
+			EndpointsConfig: map[string]*network.EndpointSettings{},
+		},
+	}
+}
+func (t *dockerContainer) ID() string                               { return t.id }
+func (t *dockerContainer) Name() string                             { return t.name }
+func (t *dockerContainer) Config() *container.Config                { return t.config }
+func (t *dockerContainer) HostConfig() *container.HostConfig        { return t.hostconfig }
+func (t *dockerContainer) NetworkConfig() *network.NetworkingConfig { return t.network }
+func (t *dockerContainer) Exec()                                    {}
+func (t *dockerContainer) Run(destOut io.Writer, destErr io.Writer, timeout time.Duration) (int64, error) {
+	err := t.Start()
+	if err != nil {
+		return -1, err
+	}
+
+	res, err := t.Wait(timeout)
+
+	errs := t.Stop()
+	if errs != nil {
+		destErr.Write([]byte(errs.Error()))
+	}
+	errs = t.Logs(destOut, destErr)
+	if errs != nil {
+		destErr.Write([]byte(errs.Error()))
+	}
+	errs = t.Remove()
+	if errs != nil {
+		destErr.Write([]byte(errs.Error()))
+	}
+
+	return res, err
+}
+func (t *dockerContainer) Logs(destOut io.Writer, destErr io.Writer) error {
+	cli, err := t.docker.client()
+	if err != nil {
+		return err
+	}
+	defer cli.Close()
+
+	res, err := cli.ContainerLogs(t.docker.ctx, t.id, client.ContainerLogsOptions{
+		ShowStdout: true,
+		ShowStderr: true,
+		// Details:    true,
+	})
+	if err != nil {
+		return err
+	}
+
+	defer res.Close()
+
+	_, err = stdcopy.StdCopy(destOut, destErr, res)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+func (t *dockerContainer) AddPort(containerPort, address, hostPort string) error {
+	cp, err := network.ParsePort(containerPort)
+	if err != nil {
+		return err
+	}
+	addr, err := netip.ParseAddr(address)
+	if err != nil {
+		return err
+	}
+
+	if t.hostconfig.PortBindings == nil {
+		t.hostconfig.PortBindings = network.PortMap{}
+	}
+
+	t.hostconfig.PortBindings[cp] = []network.PortBinding{
+		{
+			HostPort: hostPort,
+			HostIP:   addr,
+		},
+	}
+
+	return nil
+}
+func (t *dockerContainer) State() (container.ContainerState, error) {
+	cli, err := t.docker.client()
+	if err != nil {
+		return "", err
+	}
+
+	c, err := cli.ContainerInspect(context.Background(), t.id, client.ContainerInspectOptions{})
+	if err != nil {
+		return "", err
+	}
+
+	return c.Container.State.Status, nil
+}
+func (t *dockerContainer) Wait(timeout time.Duration) (int64, error) {
+	cli, err := t.docker.client()
+	if err != nil {
+		return -1, err
+	}
+	defer cli.Close()
+
+	ctx, _ := context.WithTimeout(t.docker.ctx, timeout)
+
+	wait := cli.ContainerWait(ctx, t.id, client.ContainerWaitOptions{})
+	select {
+	case err := <-wait.Error:
+		if err != nil {
+			return -1, err
+		}
+	case res := <-wait.Result:
+		return res.StatusCode, nil
+	}
+
+	return -1, nil
+}
+func (t *dockerContainer) Create() error {
+	cli, err := t.docker.client()
+	if err != nil {
+		return err
+	}
+	defer cli.Close()
+
+	create, err := cli.ContainerCreate(context.Background(), client.ContainerCreateOptions{
+		Name:             t.name,
+		Config:           t.config,
+		HostConfig:       t.hostconfig,
+		NetworkingConfig: t.network,
+		Platform:         &v1.Platform{},
+	})
+	if err != nil {
+		return err
+	}
+
+	t.id = create.ID
+
+	return nil
+}
+func (t *dockerContainer) Start() error {
+	cli, err := t.docker.client()
+	if err != nil {
+		return err
+	}
+
+	defer cli.Close()
+
+	_, err = cli.ContainerStart(t.docker.ctx, t.id, client.ContainerStartOptions{})
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+func (t *dockerContainer) Stop() error {
+	cli, err := t.docker.client()
+	if err != nil {
+		return err
+	}
+
+	defer cli.Close()
+
+	_, err = cli.ContainerStop(t.docker.ctx, t.id, client.ContainerStopOptions{})
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+func (t *dockerContainer) Remove() error {
+	cli, err := t.docker.client()
+	if err != nil {
+		return err
+	}
+
+	defer cli.Close()
+
+	_, err = cli.ContainerRemove(t.docker.ctx, t.id,
+		client.ContainerRemoveOptions{
+			// RemoveLinks:   true,
+			RemoveVolumes: true,
+			Force:         true,
+		})
+	if err != nil {
+		return err
+	}
+
+	t.id = ""
+
+	return nil
+}
+
+func (t *docker) Image() *dockerImage {
+	return &dockerImage{
+		docker: t,
+		out:    os.Stderr,
+	}
+}
+
+// RegistryAuth is the base64 encoded credentials for the registry
+func (t *dockerImage) Auth(RegistryAuth string) { t.auth = RegistryAuth }
+func (t *dockerImage) Out(out io.Writer)        { t.out = out }
+func (t *dockerImage) Build(tag, path, dockerfile string) error {
+	cli, err := t.docker.client()
+	if err != nil {
+		return err
+	}
+
+	defer cli.Close()
+
+	tar, err := archive.TarWithOptions(path, &archive.TarOptions{})
+	if err != nil {
+		return err
+	}
+
+	opts := client.ImageBuildOptions{
+		Dockerfile: dockerfile,
+		Tags:       []string{tag},
+		Remove:     true,
+	}
+
+	buildResponse, err := cli.ImageBuild(t.docker.ctx, tar, opts)
+	if err != nil {
+		return err
+	}
+	defer buildResponse.Body.Close()
+
+	if t.out != nil {
+		fd, isTerminal := term.GetFdInfo(t.out)
+		return jsonmessage.DisplayJSONMessagesStream(buildResponse.Body, t.out, fd, isTerminal, nil)
+	}
+
+	return nil
+}
+func (t *dockerImage) Tag(source, target string) error {
+	cli, err := t.docker.client()
+	if err != nil {
+		return err
+	}
+
+	defer cli.Close()
+
+	_, err = cli.ImageTag(t.docker.ctx, client.ImageTagOptions{
+		Source: source,
+		Target: target,
+	})
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+func (t *dockerImage) Push(image string) error {
+	cli, err := t.docker.client()
+	if err != nil {
+		return err
+	}
+
+	defer cli.Close()
+
+	res, err := cli.ImagePush(t.docker.ctx, image, client.ImagePushOptions{
+		RegistryAuth: t.auth,
+	})
+	if err != nil {
+		return err
+	}
+	defer res.Close()
+
+	if t.out != nil {
+		fd, isTerminal := term.GetFdInfo(t.out)
+		return jsonmessage.DisplayJSONMessagesStream(res, t.out, fd, isTerminal, nil)
+	}
+
+	return nil
+
+}
+func (t *dockerImage) Remove(image string) error {
+	cli, err := t.docker.client()
+	if err != nil {
+		return err
+	}
+
+	defer cli.Close()
+
+	_, err = cli.ImageRemove(t.docker.ctx, image, client.ImageRemoveOptions{})
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+func (t *dockerImage) Pull(image string, timeout time.Duration) error {
+	cli, err := t.docker.client()
+	if err != nil {
+		return err
+	}
+
+	defer cli.Close()
+
+	ctx, _ := context.WithTimeout(t.docker.ctx, timeout)
+	res, err := cli.ImagePull(ctx, image, client.ImagePullOptions{
+		RegistryAuth: t.auth,
+	})
+	if err != nil {
+		return err
+	}
+
+	defer res.Close()
+
+	if t.out != nil {
+		fd, isTerminal := term.GetFdInfo(t.out)
+		return jsonmessage.DisplayJSONMessagesStream(res, t.out, fd, isTerminal, nil)
+	} else {
+		err = res.Wait(ctx)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}

+ 112 - 0
tools/git.go

@@ -0,0 +1,112 @@
+package tools
+
+import (
+	"github.com/go-git/go-git/v6"
+	"github.com/go-git/go-git/v6/plumbing"
+	"github.com/go-git/go-git/v6/plumbing/object"
+	"github.com/go-git/go-git/v6/plumbing/transport/http"
+)
+
+func Git() *g {
+	return &g{}
+}
+
+type (
+	g struct {
+		user  string
+		email string
+		token string
+	}
+)
+
+func (t *g) User(user string) *g {
+	t.user = user
+	return t
+}
+
+func (t *g) Email(email string) *g {
+	t.email = email
+	return t
+}
+
+func (t *g) Token(token string) *g {
+	t.token = token
+	return t
+}
+
+func (t *g) Clone(repo, ref, path string) error {
+	_, err := git.PlainClone(
+		path,
+		&git.CloneOptions{
+			URL:           repo,
+			ReferenceName: plumbing.ReferenceName(ref),
+			Auth: &http.BasicAuth{
+				Username: t.user,
+				Password: t.token,
+			},
+		})
+
+	return err
+}
+
+func (t *g) Add(repo, file string) error {
+	r, err := git.PlainOpen(repo)
+	if err != nil {
+		return err
+	}
+
+	w, err := r.Worktree()
+	if err != nil {
+		return err
+	}
+
+	_, err = w.Add(file)
+	if err != nil {
+		return err
+	}
+
+	return err
+}
+
+func (t *g) Commit(repo, message string) error {
+	r, err := git.PlainOpen(repo)
+	if err != nil {
+		return err
+	}
+
+	w, err := r.Worktree()
+	if err != nil {
+		return err
+	}
+
+	_, err = w.Commit(message, &git.CommitOptions{
+		Author: &object.Signature{
+			Name:  t.user,
+			Email: t.email,
+		},
+	})
+	if err != nil {
+		return err
+	}
+
+	return err
+}
+
+func (t *g) Push(repo string) error {
+	r, err := git.PlainOpen(repo)
+	if err != nil {
+		return err
+	}
+
+	err = r.Push(&git.PushOptions{
+		Auth: &http.BasicAuth{
+			Username: t.user,
+			Password: t.token,
+		},
+	})
+	if err != nil {
+		return err
+	}
+
+	return err
+}

+ 17 - 0
tools/terraform.go

@@ -0,0 +1,17 @@
+package tools
+
+func Terraform() *terraform {
+	return &terraform{}
+}
+
+type (
+	terraform struct{}
+)
+
+func (t *terraform) Init()    {}
+func (t *terraform) Plan()    {}
+func (t *terraform) Apply()   {}
+func (t *terraform) Show()    {}
+func (t *terraform) Outputs() {}
+func (t *terraform) State()   {}
+func (t *terraform) Destroy() {}

+ 1 - 10
tools/tools.go

@@ -1,10 +1 @@
-package state
-
-func DockerRun()
-func DockerStop()
-func DockerRemove()
-
-func DockerImageBuild()
-func DockerImageTag()
-func DockerImagePush()
-func DockerImageRemove()
+package tools