7 Commits

Author SHA1 Message Date
b5aaaa0195 fix roadmap items 2022-09-12 12:41:57 +02:00
30db2020e8 with test action 2022-09-12 12:36:00 +02:00
0212ce9df4 with commit 2022-09-12 12:35:10 +02:00
f78d84dc8f gin logging 2022-09-12 09:52:44 +02:00
3f443f52fd with pr 2022-09-11 22:57:37 +02:00
226e47e5f4 with pull into storage 2022-09-11 22:56:54 +02:00
3643d4a467 Added storage 2022-09-11 14:52:21 +02:00
67 changed files with 393 additions and 2121 deletions

BIN
.DS_Store vendored

Binary file not shown.

2
.gitignore vendored
View File

@@ -1,2 +0,0 @@
.cuddle/
.env

View File

@@ -1 +0,0 @@
# Configuration server

249
README.md
View File

@@ -1,249 +0,0 @@
<p align="center">
<image src="https://git.front.kjuulh.io/kjuulh/octopush/raw/branch/v0.2/assets/octopush.svg" width="300" height="300"/>
</p>
<h1 align="center">Octopush - Your cute action executor</h1>
## Purpose
The goal of this project is to easily do batch changes or queries on a host of
repositories. In large organisations using multi-repository strategies, it may
be painful to change even small things across many repositories, because there
are so many of them. Octopush aims to change that.
**DISCLAIMER:** It is still early days, and the api is subject to change.
## Features
- Uses an actions repository, where you store all your pending commands or
queries to be performed across your fleet of repositories. (See \_examples)
- Actions can both execute changes, open pull-requests or in some cases commit
directly to your preferred branch
- Actions natively use either shell, go or docker files to execute changes
(see \_examples/actions)
- Actions can also be analytical, so you can query your fleet for whatever you
would like
- Works both as a client, or as a server
- Supports SSH/https for fetching repos
- Supports GPG signing
- Supports dry-run mode for easy testing when developing your actions (enabled
by default on the cli)
## Roadmap
Refer to [roadmap.md](roadmap.md)
## Installation
Octopush comes in two modes. Client or Client -> Server. Octopush can stand
alone as a client, for smaller and less secure changes. However, for
organisations, it may be useful to use Octopush in server mode, which supports
more features, and has extra security built in.
### Client (CLI)
Download executable from [releases](https://github.com/kjuulh/octopush/releases)
#### Or Use docker image
```bash
docker run --rm kasperhermansen/octopushcli:latest version
```
#### Or Build from source
```bash
git clone https://github.com/kjuulh/octopush.git
cd octopush
go build cmd/octopush/octopush.go
./octopush version
```
#### Or Build with cuddle
```bash
git clone https://github.com/kjuulh/octopush.git
cd octopush
cuddle_cli x build_cli
```
### Server
We prefer to run the server directly as a docker image.
```bash
docker pull kasperhermansen/octopushserver:latest
docker run -p 9090:80 --rm kasperhermansen/octopushserver:latest
```
#### Or Build from source
```bash
git clone https://github.com/kjuulh/octopush.git
cd octopush
go build cmd/server/server.go
./server version
```
#### Or Build with cuddle
```bash
git clone https://github.com/kjuulh/octopush.git
cd octopush
cuddle_cli x build_server
```
## Usage
**DISCLAIMER:** It is still early days, and the api of the CLI is subject to
change, this provides the aim of the project, but as it is currently in flux,
there may not be as much handholding in the actual usage.
I will focus on the client here, as the server provides the same features,
though available through the cli, but instead as configuration options (see
[CONFIGURATION_SERVER.md](CONFIGURATION_SERVER.md))
Octopush ships with autocomplete built in (courtesy of spf13/cobra). To add:
- Bash: `echo 'source <(octopush completion bash)' >> ~/.bashrc`
- Zsh: `echo 'source <(octopush completion zsh)' >> ~/.zshrc`
### Creating a new action
Creating a new action
```bash
git init my-actions # should only be done once
cd my-actions
octopush tmpl init write-a-readme --command
cat write-a-readme/octopush.yml
# Output
# apiVersion: git.front.kjuulh.io/kjuulh/octopush/blob/main/schema/v1
# name: write-a-readme
# select:
# repositories: []
# actions:
# - type: shell
# entry: "main.sh"
```
Octopush also ships with yaml schema, which should help write the yaml
configuration.
#### Add upstream repositories (victims)
Now add a preferred repository
```
cat << EOF > write-a-readme/octopush.yml
apiVersion: git.front.kjuulh.io/kjuulh/octopush/blob/main/schema/v1
name: write-a-readme
select:
providers: # new
- gitea: https://git.front.kjuulh.io # new
organisation: "kjuulh" # new
actions:
- type: shell
entry: "main.sh"
EOF
```
This will take all your repositories under an organisation and run the script
on.
Another could be to use
```bash
cat << EOF > write-a-readme/octopush.yml
apiVersion: git.front.kjuulh.io/kjuulh/octopush/blob/main/schema/v1
name: write-a-readme
select:
repositories: #new
- git@git.front.kjuulh.io:kjuulh/octopush.git #new
- git@git.front.kjuulh.io:kjuulh/octopush-test.git #new
actions:
- type: shell
entry: "main.sh"
EOF
```
This will just apply to those repositories instead. Both can also be combined
for a shared effect.
### Execute action
To run the script use
```bash
octopush process --path "write-a-readme"
```
This will cause the octopush process to automatically apply the action on the
repo and open a pr.
### Query repositories
Octopush can also be used to query.
```bash
cat << EOF > write-a-readme/octopush.yml
apiVersion: git.front.kjuulh.io/kjuulh/octopush/blob/main/schema/v1
name: write-a-readme
select:
repositories:
- git@git.front.kjuulh.io:kjuulh/octopush.git
- git@git.front.kjuulh.io:kjuulh/octopush-test.git
queries:
- type: grep
query: "# README"
EOF
```
Using the same command as above, will return the lines on each repo with those
criteria. Everything is run in docker, even locally, so no need to install fancy
tools.
Do note: All actions will be run as dry-run unless `--apply` is added. This is
to help test locally, as well as not cause serious issues. The server
configuration is pretty much the same, except the command would look like so:
`octopush server process --path "write-a-readme" --apply`. Octopush will try to
infer as much as possible, but it may be needed to apply some extra flags to
specify upstream repositories and such. Octopush will also help you setup keys
and such on the first run, using `octopush setup` or `octopush server setup`.
## Contributing
It is still early days, and as such things are moving fast, I may not be able to
implement features, because I am focusing my energy on the API. That said PRs
are welcome, though they are at your own risk.
### Bugs & features requests
Please use [issues](https://github.com/kjuulh/octopush/issues)
### Development
We use [cuddle](https://git.front.kjuulh.io/kjuulh/cuddle) to improve ease of
use, it is however, not a requirement, and probably won't need to be used
outside core maintainers.
Simply:
```bash
go run cmd/octopush/octopush.go # CLI
go run cmd/server/server.go # Server
```
We follow the `gofmt` formatting, along with optionally but recommend `golines`
If using cuddle
```
cuddle_cli x run # Run both server and client, will do a quick test sweep on the cli
cuddle_cli x watch_run # Automatically refresh both
cuddle_cli x fmt # will format the current code
```

View File

@@ -1,11 +0,0 @@
module write_a_readme
go 1.19
require github.com/bitfield/script v0.20.2
require (
bitbucket.org/creachadair/shell v0.0.7 // indirect
github.com/itchyny/gojq v0.12.7 // indirect
github.com/itchyny/timefmt-go v0.1.3 // indirect
)

View File

@@ -1,20 +0,0 @@
bitbucket.org/creachadair/shell v0.0.7 h1:Z96pB6DkSb7F3Y3BBnJeOZH2gazyMTWlvecSD4vDqfk=
bitbucket.org/creachadair/shell v0.0.7/go.mod h1:oqtXSSvSYr4624lnnabXHaBsYW6RD80caLi2b3hJk0U=
github.com/bitfield/script v0.20.2 h1:4DexsRtBILVMEn3EZwHbtJdDqdk43sXI8gM3F04JXgs=
github.com/bitfield/script v0.20.2/go.mod h1:l3AZPVAtKQrL03bwh7nlNTUtgrgSWurpJSbtqspYrOA=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/itchyny/gojq v0.12.7 h1:hYPTpeWfrJ1OT+2j6cvBScbhl0TkdwGM4bc66onUSOQ=
github.com/itchyny/gojq v0.12.7/go.mod h1:ZdvNHVlzPgUf8pgjnuDTmGfHA/21KoutQUJ3An/xNuw=
github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU=
github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,25 +0,0 @@
package main
import "github.com/bitfield/script"
func main() {
releaseRc := `
branches:
- "main"
- "v0.x"
plugins:
- "@semantic-release/commit-analyzer"
- "@semantic-release/release-notes-generator"
- "@semantic-release/changelog"
- "@semantic-release/git"
`
_, err := script.
Echo(releaseRc).
WriteFile(".releaserc.yml")
if err != nil {
panic(err)
}
}

View File

@@ -1,12 +0,0 @@
apiVersion: git.front.kjuulh.io/kjuulh/octopush/blob/main/schema/v1
name: write-a-readme
select:
repositories:
- git@git.front.kjuulh.io:kjuulh/octopush-test.git
#- git@git.front.kjuulh.io:kjuulh/octopush.git
# providers:
# - gitea: https://git.front.kjuulh.io
# organisation: "cibus"
actions:
- type: go
entry: "main.go"

View File

@@ -1,8 +0,0 @@
FROM debian:bullseye-slim
# Octopush relies on this path being the specified path
WORKDIR /src/work/
COPY entry.sh /src/script.sh
CMD [ "/src/script.sh" ]

View File

@@ -1,5 +0,0 @@
#!/bin/bash
set -e
echo "# README docker" > README.md

View File

@@ -1,11 +0,0 @@
module write_a_readme
go 1.19
require github.com/bitfield/script v0.20.2
require (
bitbucket.org/creachadair/shell v0.0.7 // indirect
github.com/itchyny/gojq v0.12.7 // indirect
github.com/itchyny/timefmt-go v0.1.3 // indirect
)

View File

@@ -1,20 +0,0 @@
bitbucket.org/creachadair/shell v0.0.7 h1:Z96pB6DkSb7F3Y3BBnJeOZH2gazyMTWlvecSD4vDqfk=
bitbucket.org/creachadair/shell v0.0.7/go.mod h1:oqtXSSvSYr4624lnnabXHaBsYW6RD80caLi2b3hJk0U=
github.com/bitfield/script v0.20.2 h1:4DexsRtBILVMEn3EZwHbtJdDqdk43sXI8gM3F04JXgs=
github.com/bitfield/script v0.20.2/go.mod h1:l3AZPVAtKQrL03bwh7nlNTUtgrgSWurpJSbtqspYrOA=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/itchyny/gojq v0.12.7 h1:hYPTpeWfrJ1OT+2j6cvBScbhl0TkdwGM4bc66onUSOQ=
github.com/itchyny/gojq v0.12.7/go.mod h1:ZdvNHVlzPgUf8pgjnuDTmGfHA/21KoutQUJ3An/xNuw=
github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU=
github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,11 +0,0 @@
apiVersion: git.front.kjuulh.io/kjuulh/octopush/blob/main/schema/v1
name: write-a-readme
select:
repositories:
- git@git.front.kjuulh.io:kjuulh/octopush-test.git
# providers:
# - gitea: https://git.front.kjuulh.io
# organisation: "cibus"
actions:
- type: docker-build
entry: Dockerfile

View File

@@ -1,11 +0,0 @@
module write_a_readme
go 1.19
require github.com/bitfield/script v0.20.2
require (
bitbucket.org/creachadair/shell v0.0.7 // indirect
github.com/itchyny/gojq v0.12.7 // indirect
github.com/itchyny/timefmt-go v0.1.3 // indirect
)

View File

@@ -1,20 +0,0 @@
bitbucket.org/creachadair/shell v0.0.7 h1:Z96pB6DkSb7F3Y3BBnJeOZH2gazyMTWlvecSD4vDqfk=
bitbucket.org/creachadair/shell v0.0.7/go.mod h1:oqtXSSvSYr4624lnnabXHaBsYW6RD80caLi2b3hJk0U=
github.com/bitfield/script v0.20.2 h1:4DexsRtBILVMEn3EZwHbtJdDqdk43sXI8gM3F04JXgs=
github.com/bitfield/script v0.20.2/go.mod h1:l3AZPVAtKQrL03bwh7nlNTUtgrgSWurpJSbtqspYrOA=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/itchyny/gojq v0.12.7 h1:hYPTpeWfrJ1OT+2j6cvBScbhl0TkdwGM4bc66onUSOQ=
github.com/itchyny/gojq v0.12.7/go.mod h1:ZdvNHVlzPgUf8pgjnuDTmGfHA/21KoutQUJ3An/xNuw=
github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU=
github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,12 +0,0 @@
package main
import "github.com/bitfield/script"
func main() {
_, err := script.
Echo("# Readme").
WriteFile("README.md")
if err != nil {
panic(err)
}
}

View File

@@ -1,11 +0,0 @@
apiVersion: git.front.kjuulh.io/kjuulh/octopush/blob/main/schema/v1
name: write-a-readme
select:
repositories:
- git@git.front.kjuulh.io:kjuulh/octopush-test.git
# providers:
# - gitea: https://git.front.kjuulh.io
# organisation: "cibus"
actions:
- type: go
entry: "main.go"

View File

@@ -1,9 +0,0 @@
apiVersion: git.front.kjuulh.io/kjuulh/octopush/blob/main/schema/v1
name: write-a-readme
select:
providers:
- gitea: https://git.front.kjuulh.io
organisation: "cibus"
queries:
- type: grep
query: "releaser"

View File

@@ -1,11 +0,0 @@
apiVersion: git.front.kjuulh.io/kjuulh/octopush/blob/main/schema/v1
name: write-a-readme
select:
repositories:
- git@git.front.kjuulh.io:kjuulh/octopush-test.git
# providers:
# - gitea: https://git.front.kjuulh.io
# organisation: "cibus"
queries:
- type: grep
query: "# README"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -1,10 +0,0 @@
<svg width="1000" height="1000" viewBox="0 0 1000 1000" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M975 568C975 830.335 762.335 497 500 497C237.665 497 25 830.335 25 568C25 305.665 237.665 93 500 93C762.335 93 975 305.665 975 568Z" fill="#52DB78"/>
<path d="M237.436 856.185C165.693 827.571 320.875 755.464 377.754 612.854C434.634 470.243 371.672 311.132 443.414 339.746C515.157 368.36 527.205 507.165 470.326 649.776C413.446 792.386 309.178 884.799 237.436 856.185Z" fill="#52DB78"/>
<path d="M428.353 849.847C353.918 829.229 500.308 740.627 541.293 592.663C582.278 444.699 502.337 293.405 576.773 314.023C651.208 334.641 678.325 471.303 637.34 619.267C596.355 767.231 502.788 870.465 428.353 849.847Z" fill="#52DB78"/>
<path d="M598.707 855.902C523.231 839.498 664.415 742.813 697.024 592.781C729.634 442.748 641.321 296.183 716.797 312.588C792.272 328.992 827.022 463.916 794.413 613.948C761.803 763.98 674.183 872.307 598.707 855.902Z" fill="#52DB78"/>
<path d="M47.896 838.855C-19.3259 800.817 144.17 750.323 219.784 616.698C295.397 483.073 254.495 316.918 321.717 354.956C388.938 392.994 382.136 532.155 306.522 665.78C230.909 799.405 115.118 876.893 47.896 838.855Z" fill="#52DB78"/>
<ellipse cx="605.35" cy="265.255" rx="57.5" ry="36.5" transform="rotate(-40.9544 605.35 265.255)" fill="white"/>
<ellipse cx="359.35" cy="265.255" rx="57.5" ry="36.5" transform="rotate(-40.9544 359.35 265.255)" fill="white"/>
<path d="M385.635 374C385.635 374 391.199 435.298 463.528 438.425C535.857 441.553 538.492 380.61 538.492 380.61" stroke="white" stroke-width="8"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -1,10 +0,0 @@
<svg width="1000" height="1000" viewBox="0 0 1000 1000" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M975 568C975 830.335 762.335 497 500 497C237.665 497 25 830.335 25 568C25 305.665 237.665 93 500 93C762.335 93 975 305.665 975 568Z" fill="#52DB78"/>
<path d="M237.436 856.185C165.693 827.571 320.875 755.464 377.754 612.854C434.634 470.243 371.672 311.132 443.414 339.746C515.157 368.36 527.205 507.165 470.326 649.776C413.446 792.386 309.178 884.799 237.436 856.185Z" fill="#52DB78"/>
<path d="M428.353 849.847C353.918 829.229 500.308 740.627 541.293 592.663C582.278 444.699 502.337 293.405 576.773 314.023C651.208 334.641 678.325 471.303 637.34 619.267C596.355 767.231 502.788 870.465 428.353 849.847Z" fill="#52DB78"/>
<path d="M598.707 855.902C523.231 839.498 664.415 742.813 697.024 592.781C729.634 442.748 641.321 296.183 716.797 312.588C792.272 328.992 827.022 463.916 794.413 613.948C761.803 763.98 674.183 872.307 598.707 855.902Z" fill="#52DB78"/>
<path d="M47.896 838.855C-19.3259 800.817 144.17 750.323 219.784 616.698C295.397 483.073 254.495 316.918 321.717 354.956C388.938 392.994 382.136 532.155 306.522 665.78C230.909 799.405 115.118 876.893 47.896 838.855Z" fill="#52DB78"/>
<ellipse cx="605.35" cy="265.255" rx="57.5" ry="36.5" transform="rotate(-40.9544 605.35 265.255)" fill="white"/>
<ellipse cx="359.35" cy="265.255" rx="57.5" ry="36.5" transform="rotate(-40.9544 359.35 265.255)" fill="white"/>
<path d="M385.635 374C385.635 374 391.199 435.298 463.528 438.425C535.857 441.553 538.492 380.61 538.492 380.61" stroke="white" stroke-width="8"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,48 @@
package commands
import (
"bytes"
"encoding/json"
"net/http"
"github.com/spf13/cobra"
)
func CreateKrakenProcessCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "process",
Run: func(cmd *cobra.Command, args []string) {
client := http.Client{}
var buf bytes.Buffer
err := json.NewEncoder(&buf).
Encode(struct {
RepositoryUrls []string `json:"repositoryUrls"`
}{
RepositoryUrls: []string{"git@git.front.kjuulh.io:kjuulh/kraken.git"}})
if err != nil {
panic(err)
}
req, err := http.NewRequestWithContext(
cmd.Context(),
http.MethodPost,
"http://localhost:3000/commands/processRepos",
&buf,
)
if err != nil {
panic(err)
}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
if resp.StatusCode >= 300 {
panic(resp.Status)
}
},
}
return cmd
}

View File

@@ -0,0 +1,14 @@
package commands
import "github.com/spf13/cobra"
func CreateKrakenCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "kraken",
// Run: func(cmd *cobra.Command, args []string) { },
}
cmd.AddCommand(CreateKrakenProcessCmd())
return cmd
}

18
cmd/kraken/kraken.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import (
"os"
"git.front.kjuulh.io/kjuulh/kraken/cmd/kraken/commands"
)
func main() {
Execute()
}
func Execute() {
err := commands.CreateKrakenCmd().Execute()
if err != nil {
os.Exit(1)
}
}

View File

@@ -1,61 +0,0 @@
package commands
import (
"context"
"time"
"git.front.kjuulh.io/kjuulh/octopush/internal/cli"
"git.front.kjuulh.io/kjuulh/octopush/internal/commands"
"github.com/spf13/cobra"
"go.uber.org/zap"
)
func CreateOctopushProcessCmd(logger *zap.Logger) *cobra.Command {
var (
actionsRepo string
branch string
path string
)
cmd := &cobra.Command{
Use: "process",
RunE: func(cmd *cobra.Command, args []string) error {
if err := cmd.ParseFlags(args); err != nil {
return err
}
ctx := cmd.Context()
deps, cleanupFunc, err := cli.Start(ctx, logger)
if err != nil {
return err
}
defer func() {
ctx, _ = context.WithTimeout(ctx, time.Second*5)
if err := cleanupFunc(ctx); err != nil {
panic(err)
}
}()
err = commands.
NewProcessRepos(logger, deps).
Process(ctx, actionsRepo, branch, path)
if err != nil {
return err
}
return nil
},
}
pf := cmd.PersistentFlags()
pf.StringVar(&actionsRepo, "actions-repo", "", "actions repo is the location of your actions, not where to apply the actions themselves, that should be self contained")
cmd.MarkPersistentFlagRequired("actions-repo")
pf.StringVar(&branch, "branch", "main", "which branch to look for actions in, will default to main")
pf.StringVar(&path, "path", "", "the location of the path inside the repository")
cmd.MarkPersistentFlagRequired("path")
return cmd
}

View File

@@ -1,18 +0,0 @@
package commands
import (
"git.front.kjuulh.io/kjuulh/octopush/cmd/octopush/commands/server"
"github.com/spf13/cobra"
"go.uber.org/zap"
)
func CreateOctopushCmd(logger *zap.Logger) *cobra.Command {
cmd := &cobra.Command{
Use: "octopush",
}
cmd.AddCommand(CreateOctopushProcessCmd(logger))
cmd.AddCommand(server.CreateOctopushServerCmd(logger))
return cmd
}

View File

@@ -1,73 +0,0 @@
package server
import (
"bytes"
"encoding/json"
"net/http"
"github.com/spf13/cobra"
)
func CreateOctopushProcessCmd() *cobra.Command {
var (
actionsRepo string
branch string
path string
)
cmd := &cobra.Command{
Use: "process",
RunE: func(cmd *cobra.Command, args []string) error {
if err := cmd.ParseFlags(args); err != nil {
return err
}
client := http.Client{}
var buf bytes.Buffer
err := json.NewEncoder(&buf).
Encode(struct {
Repository string `json:"repository"`
Branch string `json:"branch"`
Path string `json:"path"`
}{
Repository: actionsRepo,
Branch: branch,
Path: path,
})
if err != nil {
panic(err)
}
req, err := http.NewRequestWithContext(
cmd.Context(),
http.MethodPost,
"http://localhost:3000/commands/processRepos",
&buf,
)
if err != nil {
panic(err)
}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
if resp.StatusCode >= 300 {
panic(resp.Status)
}
return nil
},
}
pf := cmd.PersistentFlags()
pf.StringVar(&actionsRepo, "actions-repo", "", "actions repo is the location of your actions, not where to apply the actions themselves, that should be self contained")
cmd.MarkPersistentFlagRequired("actions-repo")
pf.StringVar(&branch, "branch", "main", "which branch to look for actions in, will default to main")
pf.StringVar(&path, "path", "", "the location of the path inside the repository")
cmd.MarkPersistentFlagRequired("path")
return cmd
}

View File

@@ -1,16 +0,0 @@
package server
import (
"github.com/spf13/cobra"
"go.uber.org/zap"
)
func CreateOctopushServerCmd(logger *zap.Logger) *cobra.Command {
cmd := &cobra.Command{
Use: "server",
}
cmd.AddCommand(CreateOctopushProcessCmd())
return cmd
}

View File

@@ -1,28 +0,0 @@
package main
import (
"os"
"git.front.kjuulh.io/kjuulh/octopush/cmd/octopush/commands"
"git.front.kjuulh.io/kjuulh/octopush/internal/logger"
"go.uber.org/zap"
)
func main() {
logger, err := logger.New()
if err != nil {
panic(err)
}
_ = logger.Sync()
zap.ReplaceGlobals(logger)
Execute(logger)
}
func Execute(logger *zap.Logger) {
err := commands.CreateOctopushCmd(logger).Execute()
if err != nil {
os.Exit(1)
}
}

View File

@@ -7,7 +7,7 @@ import (
func CreateServerCmd(logger *zap.Logger) *cobra.Command {
cmd := &cobra.Command{
Use: "octopushserver",
Use: "krakenserver",
}
cmd.AddCommand(NewStartServerCommand(logger))

View File

@@ -1,7 +1,7 @@
package commands
import (
"git.front.kjuulh.io/kjuulh/octopush/internal/server"
"git.front.kjuulh.io/kjuulh/kraken/internal/server"
"github.com/spf13/cobra"
"go.uber.org/zap"
)
@@ -9,7 +9,7 @@ import (
func NewStartServerCommand(logger *zap.Logger) *cobra.Command {
cmd := &cobra.Command{
Use: "start",
Short: "Start the octopush server",
Short: "Start the kraken server",
RunE: func(cmd *cobra.Command, args []string) error {
return server.Start(logger)
},

View File

@@ -3,8 +3,8 @@ package main
import (
"os"
"git.front.kjuulh.io/kjuulh/octopush/cmd/server/commands"
"git.front.kjuulh.io/kjuulh/octopush/internal/logger"
"git.front.kjuulh.io/kjuulh/kraken/cmd/server/commands"
"git.front.kjuulh.io/kjuulh/kraken/internal/logger"
"go.uber.org/zap"
)
@@ -15,8 +15,6 @@ func main() {
}
_ = logger.Sync()
zap.ReplaceGlobals(logger)
Execute(logger)
}

View File

@@ -1,17 +0,0 @@
# yaml-language-server: $schema=https://git.front.kjuulh.io/kjuulh/cuddle/raw/branch/main/schemas/base.json
base: "git@git.front.kjuulh.io:kjuulh/cuddle-go-plan.git"
vars:
service: "octopush"
deployments: "git@git.front.kjuulh.io:kjuulh/deployments.git"
scripts:
push_github:
type: shell
run_server:
type: shell
run_client:
type: shell
run:
type: shell

View File

@@ -1,17 +0,0 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----
lIYEYx8kxRYJKwYBBAHaRw8BAQdAwlYhGGWpLSSxZoHUmzvl6iJeZgtfKu/8/cjt
LLQ8Swf+BwMCGPF3fdZbweT7+Y/bMMnelXmhYsTgEk30h+FeXOnGy/ZvJgnqoBed
eRPRO5VDN4xq30D8zp04em8tgPXXS50yXvf7PUIKcx4u0IDteTC/Q7QjS3Jha2Vu
IDxrcmFrZW5Aa2FzcGVyaGVybWFuc2VuLmNvbT6IkwQTFgoAOxYhBKh3AMKI2yc/
qX90YXHawJCw+EZkBQJjHyTFAhsDBQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheA
AAoJEHHawJCw+EZkJMQA/AgeMkam18RasuPcl9kiiFkE2EA2TvO25IieZesbCEf5
APwLjuXkMNYrPSAGPk0VZY7Eq8hWQd3qh9GHV9vDEUvND5yLBGMfJMUSCisGAQQB
l1UBBQEBB0D4pGbjQW+s2aYO3DZX7M0yyq4JkZ+Wana3v2BuAXXYEwMBCAf+BwMC
7hV7XuPdNrP7q1BylZe5GKz0TP0LSRbVjPgnetTyDqOaWEtdRzc996rBR0WcvUJO
xN7oRR8XNMp1v6Up2LcvUs6XDpJ4f1MBGh3npytF7oh4BBgWCgAgFiEEqHcAwojb
Jz+pf3RhcdrAkLD4RmQFAmMfJMUCGwwACgkQcdrAkLD4RmTvQgEAqGhqQuiZQskW
Zbr27HBpQIukcIOVFle+wNXNyhKTJlkBAKoM/wTrQNIyS2gnGPQ1IE/AtDhMvwsV
hCIzhb/ybLMH
=9Nw9
-----END PGP PRIVATE KEY BLOCK-----

14
go.mod
View File

@@ -1,37 +1,32 @@
module git.front.kjuulh.io/kjuulh/octopush
module git.front.kjuulh.io/kjuulh/kraken
go 1.19
require (
code.gitea.io/sdk/gitea v0.15.1
git.front.kjuulh.io/kjuulh/curre v1.3.5
github.com/ProtonMail/go-crypto v0.0.0-20220822140716-1678d6eb0cbe
github.com/gin-contrib/zap v0.0.2
git.front.kjuulh.io/kjuulh/curre v1.2.2
github.com/gin-gonic/gin v1.8.1
github.com/go-git/go-git/v5 v5.4.2
github.com/google/uuid v1.3.0
github.com/spf13/cobra v1.5.0
github.com/stretchr/testify v1.8.0
github.com/whilp/git-urls v1.0.0
go.uber.org/zap v1.23.0
golang.org/x/net v0.0.0-20220909164309-bea034e7d591
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-contrib/zap v0.0.2 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.3.1 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.10.0 // indirect
github.com/goccy/go-json v0.9.7 // indirect
github.com/hashicorp/go-version v1.2.1 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
@@ -56,4 +51,5 @@ require (
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

26
go.sum
View File

@@ -1,15 +1,11 @@
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
code.gitea.io/sdk/gitea v0.15.1 h1:WJreC7YYuxbn0UDaPuWIe/mtiNKTvLN8MLkaw71yx/M=
code.gitea.io/sdk/gitea v0.15.1/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA=
git.front.kjuulh.io/kjuulh/curre v1.3.5 h1:oKYh5Z0vInjViLnS4ppzK0G2Mnj7vXq8mA5i/rsWId4=
git.front.kjuulh.io/kjuulh/curre v1.3.5/go.mod h1:m7WpSehONLqPh/XF3F0BI0UOpLOfGuDmDEFI1XsM6fE=
git.front.kjuulh.io/kjuulh/curre v1.2.2 h1:0OwWIfekrMykdQg9bdmG80I+Mjc2k4i+sy903phuDWs=
git.front.kjuulh.io/kjuulh/curre v1.2.2/go.mod h1:m7WpSehONLqPh/XF3F0BI0UOpLOfGuDmDEFI1XsM6fE=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/ProtonMail/go-crypto v0.0.0-20220822140716-1678d6eb0cbe h1:R2HeCk7SG/XpoYZlEeI1v7sId7w2AMWwzOaVqXn45FE=
github.com/ProtonMail/go-crypto v0.0.0-20220822140716-1678d6eb0cbe/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
@@ -18,9 +14,6 @@ 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/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY=
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -69,8 +62,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
@@ -146,12 +137,9 @@ github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU=
github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
github.com/xanzy/ssh-agent v0.3.2 h1:eKj4SX2Fe7mui28ZgnFW5fmTz1EIr7ugo5s6wDxdHBM=
github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
@@ -171,17 +159,14 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
@@ -189,7 +174,6 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -210,7 +194,6 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 h1:wM1k/lXfpc5HdkJJyW9GELpd8ERGdnh8sMGL6Gzq3Ho=
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -225,12 +208,11 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=

View File

@@ -7,7 +7,7 @@ import (
"os"
"testing"
"git.front.kjuulh.io/kjuulh/octopush/internal/server"
"git.front.kjuulh.io/kjuulh/kraken/internal/server"
"go.uber.org/zap"
)

View File

@@ -9,7 +9,7 @@ import (
"path"
"testing"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/storage"
"git.front.kjuulh.io/kjuulh/kraken/internal/services/storage"
"github.com/stretchr/testify/require"
)

View File

@@ -1,77 +0,0 @@
package actions
import (
"context"
"errors"
"git.front.kjuulh.io/kjuulh/octopush/internal/actions/builders"
"git.front.kjuulh.io/kjuulh/octopush/internal/actions/querier"
"git.front.kjuulh.io/kjuulh/octopush/internal/schema"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/storage"
"go.uber.org/zap"
)
type Action struct {
Schema *schema.OctopushSchema
SchemaPath string
}
func (a *Action) Execute(ctx context.Context, area *storage.Area) error {
for _, action := range a.Schema.Actions {
switch action.Type {
case "go":
exe, err := builders.NewGo(zap.L()).Build(ctx, a.SchemaPath, action.Entry)
if err != nil {
return err
}
err = exe(ctx, area.Path)
if err != nil {
return err
}
zap.L().Debug("Execution done")
case "docker-build":
zap.L().Debug("Building docker-build")
runCmd, err := builders.NewDockerBuild(zap.L()).Build(ctx, a.SchemaPath, action.Entry)
if err != nil {
return err
}
err = runCmd(ctx, area.Path)
if err != nil {
return err
}
return nil
default:
return errors.New("could not determine action type")
}
}
return nil
}
func (a *Action) Query(ctx context.Context, area *storage.Area) ([]string, bool, error) {
for _, query := range a.Schema.Queries {
switch query.Type {
case "grep":
exe, err := querier.NewRipGrep(zap.L()).Build(ctx, a.SchemaPath, query.Query)
if err != nil {
return nil, false, err
}
output, found, err := exe(ctx, area.Path)
if err != nil {
return nil, false, err
}
zap.L().Debug("Execution done")
return output, found, nil
default:
return nil, false, errors.New("could not determine query type")
}
}
return nil, false, nil
}

View File

@@ -1,85 +0,0 @@
package actions
import (
"context"
"fmt"
"os"
"path"
"time"
"git.front.kjuulh.io/kjuulh/octopush/internal/schema"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/providers"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/storage"
"go.uber.org/zap"
)
type (
ActionCreatorOps struct {
RepositoryUrl string
Branch string
Path string
}
ActionCreator struct {
logger *zap.Logger
storage *storage.Service
git *providers.GoGit
}
ActionCreatorDeps interface {
GetStorageService() *storage.Service
GetGitProvider() *providers.GoGit
}
)
func NewActionCreator(logger *zap.Logger, deps ActionCreatorDeps) *ActionCreator {
return &ActionCreator{
logger: logger,
storage: deps.GetStorageService(),
git: deps.GetGitProvider(),
}
}
func (ac *ActionCreator) Prepare(ctx context.Context, ops *ActionCreatorOps) (*Action, error) {
area, err := ac.storage.CreateArea(ctx)
if err != nil {
ac.logger.Error("failed to allocate area", zap.Error(err))
return nil, err
}
cloneCtx, _ := context.WithTimeout(ctx, time.Second*10)
_, err = ac.git.CloneBranch(cloneCtx, area, ops.RepositoryUrl, ops.Branch)
if err != nil {
ac.logger.Error("could not clone repo", zap.Error(err))
return nil, err
}
executorUrl := path.Join(area.Path, ops.Path)
if _, err = os.Stat(executorUrl); os.IsNotExist(err) {
return nil, fmt.Errorf("path is invalid: %s", ops.Path)
}
contents, err := os.ReadFile(path.Join(executorUrl, "octopush.yml"))
if err != nil {
return nil, err
}
octopushSchema, err := schema.Unmarshal(string(contents))
if err != nil {
return nil, err
}
ac.logger.Debug("Action creator done")
return &Action{
Schema: octopushSchema,
SchemaPath: executorUrl,
}, nil
}
func (ac *ActionCreator) Cleanup(ctx context.Context, area *storage.Area) {
ac.logger.Debug("Removing area", zap.String("path", area.Path))
err := ac.storage.RemoveArea(ctx, area)
if err != nil {
panic(err)
}
}

View File

@@ -1,95 +0,0 @@
package builders
import (
"context"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"os"
"os/exec"
"go.uber.org/zap"
"go.uber.org/zap/zapio"
)
type DockerBuild struct {
logger *zap.Logger
}
func NewDockerBuild(logger *zap.Logger) *DockerBuild {
return &DockerBuild{logger: logger}
}
type DockerRunCommand func(ctx context.Context, victimPath string) error
func (g *DockerBuild) Build(ctx context.Context, modulePath, entryPath string) (DockerRunCommand, error) {
g.logger.Debug("Building docker image", zap.String("actiondir", modulePath), zap.String("entry", entryPath))
if _, err := os.Stat(fmt.Sprintf("%s/%s", modulePath, entryPath)); os.IsNotExist(err) {
return nil, errors.New("could not find entry")
}
b := make([]byte, 20)
_, err := rand.Reader.Read(b)
if err != nil {
return nil, err
}
tag := hex.EncodeToString(b)
buildDockerCmd := fmt.Sprintf("(cd %s; docker build -f %s --tag octopush/%s .)", modulePath, entryPath, tag)
g.logger.Debug("Running command", zap.String("command", buildDockerCmd))
cmd := exec.CommandContext(
ctx,
"/bin/bash",
"-c",
buildDockerCmd,
)
debugwriter := &zapio.Writer{
Log: g.logger,
Level: zap.DebugLevel,
}
defer debugwriter.Close()
cmd.Stdout = debugwriter
cmd.Stderr = debugwriter
err = cmd.Start()
if err != nil {
return nil, err
}
err = cmd.Wait()
if err != nil {
return nil, err
}
g.logger.Debug("Docker image built!")
return func(ctx context.Context, victimPath string) error {
g.logger.Debug("Executing script", zap.String("victim", victimPath))
cmd := exec.CommandContext(
ctx,
"/bin/bash",
"-c",
fmt.Sprintf("docker run --rm -v %s/:/src/work/ octopush/%s", victimPath, tag),
)
runDockerWriter := &zapio.Writer{
Log: g.logger,
Level: zap.DebugLevel,
}
defer runDockerWriter.Close()
cmd.Stdout = runDockerWriter
cmd.Stderr = runDockerWriter
err = cmd.Start()
if err != nil {
return err
}
return cmd.Wait()
}, nil
}

View File

@@ -1,46 +0,0 @@
package builders
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"go.uber.org/zap"
)
type Go struct {
logger *zap.Logger
}
func NewGo(logger *zap.Logger) *Go {
return &Go{logger: logger}
}
type GoExecutable func(ctx context.Context, victimPath string) error
func (g *Go) Build(ctx context.Context, modulePath, entryPath string) (GoExecutable, error) {
g.logger.Debug("Building go binary", zap.String("actiondir", modulePath), zap.String("entry", entryPath))
if _, err := os.Stat(fmt.Sprintf("%s/%s", modulePath, entryPath)); os.IsNotExist(err) {
return nil, errors.New("could not find entry")
}
err := exec.CommandContext(
ctx,
"/bin/bash",
"-c",
fmt.Sprintf("(cd %s; go build -o main %s)", modulePath, entryPath),
).Run()
if err != nil {
return nil, err
}
g.logger.Debug("Go binary built!")
return func(ctx context.Context, victimPath string) error {
g.logger.Debug("Executing script", zap.String("victim", victimPath))
return exec.CommandContext(ctx, "/bin/bash", "-c", fmt.Sprintf("(cd %s; %s/main)", victimPath, modulePath)).Run()
}, nil
}

View File

@@ -1,106 +0,0 @@
package querier
import (
"context"
"fmt"
"io"
"os/exec"
"strings"
"go.uber.org/zap"
"go.uber.org/zap/zapio"
)
type RipGrep struct {
logger *zap.Logger
}
func NewRipGrep(logger *zap.Logger) *RipGrep {
return &RipGrep{logger: logger}
}
type RipGrepCommand func(ctx context.Context, victimPath string) ([]string, bool, error)
func (g *RipGrep) Build(ctx context.Context, modulePath, query string) (RipGrepCommand, error) {
g.logger.Debug("Pulling docker image", zap.String("actiondir", modulePath), zap.String("query", query))
pullDockerImage := "docker pull mbologna/docker-ripgrep"
g.logger.Debug("Running command", zap.String("command", pullDockerImage))
cmd := exec.CommandContext(
ctx,
"/bin/bash",
"-c",
pullDockerImage,
)
debugwriter := &zapio.Writer{
Log: g.logger,
Level: zap.DebugLevel,
}
defer debugwriter.Close()
cmd.Stdout = debugwriter
cmd.Stderr = debugwriter
err := cmd.Start()
if err != nil {
return nil, err
}
err = cmd.Wait()
if err != nil {
return nil, err
}
g.logger.Debug("Docker image pulled")
return func(ctx context.Context, victimPath string) ([]string, bool, error) {
g.logger.Debug("Executing script", zap.String("victim", victimPath))
runRipGrepCmd := fmt.Sprintf("docker run --rm -v %s/:/data:ro mbologna/docker-ripgrep rg -i '%s' || true", victimPath, query)
g.logger.Debug("Execute ripgrep query", zap.String("command", runRipGrepCmd))
cmd := exec.CommandContext(
ctx,
"/bin/bash",
"-c",
runRipGrepCmd,
)
runDockerWriter := &zapio.Writer{
Log: g.logger,
Level: zap.DebugLevel,
}
defer runDockerWriter.Close()
builder := &strings.Builder{}
combinedWriter := io.MultiWriter(runDockerWriter, builder)
cmd.Stdout = combinedWriter
cmd.Stderr = combinedWriter
err = cmd.Start()
if err != nil {
return nil, false, err
}
err = cmd.Wait()
if err != nil {
return nil, false, err
}
contents := strings.Split(builder.String(), "\n")
validatedOutput := make([]string, 0)
for _, c := range contents {
if !strings.Contains(c, "WARNING:") {
validatedOutput = append(validatedOutput, c)
}
}
found := len(validatedOutput) > 0
return validatedOutput, found, nil
}, nil
}

View File

@@ -4,9 +4,9 @@ import (
"context"
"net/http"
"git.front.kjuulh.io/kjuulh/octopush/internal/commands"
"git.front.kjuulh.io/kjuulh/octopush/internal/serverdeps"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/jobs"
"git.front.kjuulh.io/kjuulh/kraken/internal/commands"
"git.front.kjuulh.io/kjuulh/kraken/internal/serverdeps"
"git.front.kjuulh.io/kjuulh/kraken/internal/services/jobs"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"go.uber.org/zap"
@@ -16,9 +16,7 @@ func CommandRoute(logger *zap.Logger, app *gin.Engine, deps *serverdeps.ServerDe
commandRoute := app.Group("commands")
commandRoute.POST("processRepos", func(c *gin.Context) {
type processReposRequest struct {
Repository string `json:"repository"`
Branch string `json:"branch"`
Path string `json:"path"`
RepositoryUrls []string `json:"repositoryUrls"`
}
var request processReposRequest
err := c.BindJSON(&request)
@@ -30,14 +28,11 @@ func CommandRoute(logger *zap.Logger, app *gin.Engine, deps *serverdeps.ServerDe
jobId := uuid.New().String()
go func(repository string, branch string, path string, jobId string) {
go func(repositoryUrls []string, jobId string) {
ctx := context.WithValue(context.Background(), jobs.JobId{}, jobId)
processRepos := commands.NewProcessRepos(logger, deps)
err = processRepos.Process(ctx, repository, branch, path)
if err != nil {
logger.Error("could not process repo", zap.Error(err))
}
}(request.Repository, request.Branch, request.Path, jobId)
err = processRepos.Process(ctx, repositoryUrls)
}(request.RepositoryUrls, jobId)
c.Status(http.StatusAccepted)
})

View File

@@ -1,7 +1,7 @@
package api
import (
"git.front.kjuulh.io/kjuulh/octopush/internal/serverdeps"
"git.front.kjuulh.io/kjuulh/kraken/internal/serverdeps"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)

View File

@@ -1,30 +0,0 @@
package cli
import (
"context"
"git.front.kjuulh.io/kjuulh/curre"
"git.front.kjuulh.io/kjuulh/octopush/internal/server"
"git.front.kjuulh.io/kjuulh/octopush/internal/serverdeps"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/signer"
"go.uber.org/zap"
)
func Start(ctx context.Context, logger *zap.Logger) (*serverdeps.ServerDeps, curre.CleanupFunc, error) {
deps := serverdeps.NewServerDeps(logger)
readyChan := make(chan curre.ComponentsAreReady, 1)
cleanupFunc, err := curre.NewManager().
Register(
server.NewStorageServer(logger.With(zap.Namespace("storage")), deps),
).
Register(
signer.NewOpenPGPApp(deps.GetOpenPGP()),
).
RunNonBlocking(ctx, readyChan)
<-readyChan
return deps, cleanupFunc, err
}

View File

@@ -3,239 +3,137 @@ package commands
import (
"context"
"fmt"
"strings"
"io/fs"
"os"
"path"
"path/filepath"
"sync"
"time"
"git.front.kjuulh.io/kjuulh/octopush/internal/actions"
"git.front.kjuulh.io/kjuulh/octopush/internal/gitproviders"
"git.front.kjuulh.io/kjuulh/octopush/internal/schema"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/providers"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/storage"
giturls "github.com/whilp/git-urls"
"git.front.kjuulh.io/kjuulh/kraken/internal/services/actions"
"git.front.kjuulh.io/kjuulh/kraken/internal/services/providers"
"git.front.kjuulh.io/kjuulh/kraken/internal/services/storage"
"go.uber.org/zap"
)
type (
ProcessRepos struct {
logger *zap.Logger
storage *storage.Service
git *providers.GoGit
actionCreator *actions.ActionCreator
gitea *gitproviders.Gitea
logger *zap.Logger
storage *storage.Service
git *providers.Git
action *actions.Action
}
ProcessReposDeps interface {
GetStorageService() *storage.Service
GetGitProvider() *providers.GoGit
GetActionCreator() *actions.ActionCreator
GetGitea() *gitproviders.Gitea
GetGitProvider() *providers.Git
GetAction() *actions.Action
}
)
func NewProcessRepos(logger *zap.Logger, deps ProcessReposDeps) *ProcessRepos {
return &ProcessRepos{
logger: logger,
storage: deps.GetStorageService(),
git: deps.GetGitProvider(),
actionCreator: deps.GetActionCreator(),
gitea: deps.GetGitea(),
logger: logger,
storage: deps.GetStorageService(),
git: deps.GetGitProvider(),
action: deps.GetAction(),
}
}
func (pr *ProcessRepos) Process(ctx context.Context, repository string, branch string, actionPath string) error {
action, err := pr.actionCreator.Prepare(ctx, &actions.ActionCreatorOps{
RepositoryUrl: repository,
Branch: branch,
Path: actionPath,
})
if err != nil {
return err
}
repositoryUrls, err := pr.getRepoUrls(ctx, action.Schema)
if err != nil {
return err
}
func (pr *ProcessRepos) Process(ctx context.Context, repositoryUrls []string) error {
// Clone repos
wg := sync.WaitGroup{}
wg.Add(len(repositoryUrls))
errChan := make(chan error, 1)
for _, repoUrl := range repositoryUrls {
go func(ctx context.Context, repoUrl string) {
defer func() {
wg.Done()
}()
err := pr.processRepo(ctx, repoUrl, action)
pr.logger.Debug("Creating area", zap.String("repoUrl", repoUrl))
area, err := pr.storage.CreateArea(ctx)
if err != nil {
pr.logger.Error("could not process repo", zap.Error(err))
pr.logger.Error("failed to allocate area", zap.Error(err))
errChan <- err
return
}
defer func(ctx context.Context) {
pr.logger.Debug("Removing area", zap.String("path", area.Path), zap.String("repoUrl", repoUrl))
err = pr.storage.RemoveArea(ctx, area)
if err != nil {
errChan <- err
return
}
}(ctx)
pr.logger.Debug("Cloning repo", zap.String("path", area.Path), zap.String("repoUrl", repoUrl))
cloneCtx, _ := context.WithTimeout(ctx, time.Second*5)
repo, err := pr.git.Clone(cloneCtx, area, repoUrl)
if err != nil {
pr.logger.Error("could not clone repo", zap.Error(err))
errChan <- err
return
}
err = pr.action.Run(
ctx,
area,
func(_ context.Context, area *storage.Area) (bool, error) {
pr.logger.Debug("checking predicate", zap.String("area", area.Path))
contains := false
filepath.WalkDir(area.Path, func(path string, d fs.DirEntry, err error) error {
if d.Name() == "roadmap.md" {
contains = true
}
return nil
})
return contains, nil
},
func(_ context.Context, area *storage.Area) error {
pr.logger.Debug("running action", zap.String("area", area.Path))
readme := path.Join(area.Path, "README.md")
file, err := os.Create(readme)
if err != nil {
return fmt.Errorf("could not create readme: %w", err)
}
_, err = file.WriteString("# Readme")
if err != nil {
return fmt.Errorf("could not write readme: %w", err)
}
_, err = pr.git.Add(ctx, area, repo)
if err != nil {
return fmt.Errorf("could not add file: %w", err)
}
_, err = pr.git.Commit(ctx, repo)
if err != nil {
return fmt.Errorf("could not get diff: %w", err)
}
return nil
}, false)
if err != nil {
pr.logger.Error("could not run action", zap.Error(err))
errChan <- err
return
}
pr.logger.Debug("processing done", zap.String("path", area.Path), zap.String("repoUrl", repoUrl))
}(ctx, repoUrl)
}
wg.Wait()
pr.logger.Debug("finished processing all repos", zap.Strings("repos", repositoryUrls))
close(errChan)
pr.logger.Debug("finished processing all repos")
return nil
}
func (pr *ProcessRepos) getRepoUrls(ctx context.Context, schema *schema.OctopushSchema) ([]string, error) {
repoUrls := make([]string, 0)
repoUrls = append(repoUrls, schema.Select.Repositories...)
for _, provider := range schema.Select.Providers {
repos, err := pr.gitea.ListRepositoriesForOrganization(ctx, provider.Gitea, provider.Organisation)
if err != nil {
return nil, err
}
repoUrls = append(repoUrls, repos...)
}
return repoUrls, nil
}
func (pr *ProcessRepos) processRepo(ctx context.Context, repoUrl string, action *actions.Action) error {
cleanup, area, err := pr.prepareAction(ctx)
defer func() {
if cleanup != nil {
cleanup(ctx)
}
}()
if err != nil {
for err := range errChan {
return err
}
repo, err := pr.clone(ctx, area, repoUrl)
if err != nil {
return err
}
if len(action.Schema.Queries) > 0 {
result, found, err := action.Query(ctx, area)
if err != nil {
return err
}
if found {
pr.logger.Info("Query result", zap.Strings("result", result))
// TODO: Append to real result, and return together
}
}
if len(action.Schema.Actions) > 0 {
err = action.Execute(ctx, area)
if err != nil {
return err
}
err = pr.commit(ctx, area, repo, repoUrl)
if err != nil {
return err
}
}
pr.logger.Debug("processing done", zap.String("path", area.Path), zap.String("repoUrl", repoUrl))
return nil
}
func (pr *ProcessRepos) prepareAction(
ctx context.Context,
) (func(ctx context.Context), *storage.Area, error) {
pr.logger.Debug("Creating area")
area, err := pr.storage.CreateArea(ctx)
if err != nil {
return nil, nil, err
}
cleanupfunc := func(ctx context.Context) {
pr.logger.Debug("Removing area", zap.String("path", area.Path))
err = pr.storage.RemoveArea(ctx, area)
if err != nil {
panic(err)
}
}
return cleanupfunc, area, nil
}
func (pr *ProcessRepos) clone(ctx context.Context, area *storage.Area, repoUrl string) (*providers.GoGitRepo, error) {
pr.logger.Debug("Cloning repo", zap.String("path", area.Path), zap.String("repoUrl", repoUrl))
cloneCtx, _ := context.WithTimeout(ctx, time.Second*5)
repo, err := pr.git.Clone(cloneCtx, area, repoUrl)
if err != nil {
return nil, err
}
err = pr.git.CreateBranch(ctx, repo)
if err != nil {
return nil, err
}
return repo, nil
}
func (pr *ProcessRepos) commit(ctx context.Context, area *storage.Area, repo *providers.GoGitRepo, repoUrl string) error {
wt, err := pr.git.Add(ctx, area, repo)
if err != nil {
return fmt.Errorf("could not add file: %w", err)
}
status, err := wt.Status()
if err != nil {
return err
}
if status.IsClean() {
// TODO: check for pr
pr.logger.Info("Returning early, as no modifications are detected")
//return nil
}
err = pr.git.Commit(ctx, repo)
if err != nil {
return fmt.Errorf("could not get diff: %w", err)
}
dryrun := false
if !dryrun {
err = pr.git.Push(ctx, repo)
if err != nil {
return fmt.Errorf("could not push to repo: %w", err)
}
url, err := giturls.Parse(repoUrl)
if err != nil {
return err
}
head, err := repo.GetHEAD()
if err != nil {
return err
}
path := strings.Split(url.Path, "/")
pr.logger.Debug("path string", zap.Strings("paths", path), zap.String("HEAD", head))
org := path[0]
repoName := path[1]
semanticName, _, ok := strings.Cut(repoName, ".")
if !ok {
semanticName = repoName
}
originHead, err := pr.git.GetOriginHEADForRepo(ctx, repo)
if err != nil {
return err
}
err = pr.gitea.CreatePr(ctx, fmt.Sprintf("%s://%s", "https", url.Host), org, semanticName, head, originHead, "octopush-apply")
if err != nil {
return err
}
}
return nil
}

View File

@@ -1,143 +0,0 @@
package gitproviders
import (
"context"
"errors"
"fmt"
"os"
"sync"
"code.gitea.io/sdk/gitea"
"go.uber.org/zap"
)
type Gitea struct {
logger *zap.Logger
giteamu sync.Mutex
giteaClients map[string]*gitea.Client
}
func NewGitea(logger *zap.Logger) *Gitea {
return &Gitea{
logger: logger,
giteamu: sync.Mutex{},
giteaClients: make(map[string]*gitea.Client, 0),
}
}
func (g *Gitea) ListRepositoriesForOrganization(
ctx context.Context,
server string,
organization string,
) ([]string, error) {
client, err := g.getOrCreateClient(ctx, server)
if err != nil {
return nil, err
}
g.logger.Debug("Listing repos for gitea", zap.String("server", server))
repos, resp, err := client.ListOrgRepos(organization, gitea.ListOrgReposOptions{
ListOptions: gitea.ListOptions{
Page: 0,
PageSize: 20,
},
})
if err != nil {
return nil, fmt.Errorf("could not list repos: %w", err)
}
if resp.StatusCode >= 300 {
return nil, fmt.Errorf("gitea responded with a non 200 status code (gitea response: %s)", resp.Status)
}
repoUrls := make([]string, len(repos))
for i, repo := range repos {
repoUrls[i] = repo.SSHURL
}
return repoUrls, err
}
func (g *Gitea) CreatePr(
ctx context.Context,
server string,
organization string,
repository string,
head string,
base string,
actionName string,
) error {
client, err := g.getOrCreateClient(ctx, server)
if err != nil {
return err
}
prs, _, err := client.ListRepoPullRequests(organization, repository, gitea.ListPullRequestsOptions{
ListOptions: gitea.ListOptions{
Page: 0,
PageSize: 30,
},
State: gitea.StateOpen,
Sort: "recentupdate",
Milestone: 0,
})
if err != nil {
return fmt.Errorf(
"could not list repos, needed because we need to check for conflicts. Original error: %w",
err,
)
}
for _, pr := range prs {
if pr.Head.Name == head {
g.logger.Info(
"returning early from creating pull-request, as it already exists.",
zap.String("repository", repository),
zap.String("pull-request", pr.URL),
)
return nil
}
}
pr, _, err := client.CreatePullRequest(organization, repository, gitea.CreatePullRequestOption{
Head: head,
Base: base,
Title: actionName,
})
if err != nil {
return err
}
g.logger.Debug(
"Created pr",
zap.String("repository", repository),
zap.String("branch", head),
zap.String("pull-request", pr.URL),
)
return nil
}
func (g *Gitea) getOrCreateClient(ctx context.Context, server string) (*gitea.Client, error) {
g.giteamu.Lock()
defer g.giteamu.Unlock()
client, ok := g.giteaClients[server]
if !ok || client == nil {
c, err := gitea.NewClient(server)
username, ok := os.LookupEnv("GITEA_USERNAME")
if !ok {
return nil, errors.New("missing environment variable GITEA_USERNAME")
}
apitoken, ok := os.LookupEnv("GITEA_API_TOKEN")
if !ok {
return nil, errors.New("missing environment variable GITEA_API_TOKEN")
}
c.SetBasicAuth(username, apitoken)
if err != nil {
return nil, err
}
g.giteaClients[server] = c
return c, nil
}
return client, nil
}

View File

@@ -12,7 +12,7 @@ func New() (*zap.Logger, error) {
return lvl >= zapcore.ErrorLevel
})
lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl < zapcore.ErrorLevel // && lvl > zapcore.DebugLevel
return lvl < zapcore.ErrorLevel
})
config := zap.NewDevelopmentEncoderConfig()
@@ -28,6 +28,5 @@ func New() (*zap.Logger, error) {
)
logger := zap.New(core)
return logger, nil
}

View File

@@ -1,32 +0,0 @@
package schema
import "gopkg.in/yaml.v3"
type OctopushSchema struct {
ApiVersion string `yaml:"apiVersion"`
Name string `yaml:"name"`
Select struct {
Repositories []string `yaml:"repositories"`
Providers []struct {
Gitea string `yaml:"gitea"`
Organisation string `yaml:"organisation"`
} `yaml:"providers"`
} `yaml:"select"`
Actions []struct {
Type string `yaml:"type"`
Entry string `yaml:"entry"`
} `yaml:"actions"`
Queries []struct {
Type string `yaml:"type"`
Query string `yaml:"query"`
} `yaml:"queries"`
}
func Unmarshal(raw string) (*OctopushSchema, error) {
k := &OctopushSchema{}
err := yaml.Unmarshal([]byte(raw), k)
if err != nil {
return nil, err
}
return k, nil
}

View File

@@ -7,8 +7,8 @@ import (
"time"
"git.front.kjuulh.io/kjuulh/curre"
"git.front.kjuulh.io/kjuulh/octopush/internal/api"
"git.front.kjuulh.io/kjuulh/octopush/internal/serverdeps"
"git.front.kjuulh.io/kjuulh/kraken/internal/api"
"git.front.kjuulh.io/kjuulh/kraken/internal/serverdeps"
ginzap "github.com/gin-contrib/zap"
"github.com/gin-gonic/gin"
"go.uber.org/zap"

View File

@@ -4,8 +4,7 @@ import (
"context"
"git.front.kjuulh.io/kjuulh/curre"
"git.front.kjuulh.io/kjuulh/octopush/internal/serverdeps"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/signer"
"git.front.kjuulh.io/kjuulh/kraken/internal/serverdeps"
"go.uber.org/zap"
)
@@ -17,6 +16,5 @@ func Start(logger *zap.Logger) error {
return curre.NewManager().
Register(NewGinHttpServer(logger.With(zap.Namespace("ginHttpServer")), deps)).
Register(NewStorageServer(logger.With(zap.Namespace("storageServer")), deps)).
Register(signer.NewOpenPGPApp(deps.GetOpenPGP())).
Run(ctx)
}

View File

@@ -5,7 +5,7 @@ import (
"time"
"git.front.kjuulh.io/kjuulh/curre"
"git.front.kjuulh.io/kjuulh/octopush/internal/serverdeps"
"git.front.kjuulh.io/kjuulh/kraken/internal/serverdeps"
"go.uber.org/zap"
)

View File

@@ -1,22 +1,16 @@
package serverdeps
import (
actionc "git.front.kjuulh.io/kjuulh/octopush/internal/actions"
"git.front.kjuulh.io/kjuulh/octopush/internal/gitproviders"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/actions"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/providers"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/signer"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/storage"
"git.front.kjuulh.io/kjuulh/kraken/internal/services/actions"
"git.front.kjuulh.io/kjuulh/kraken/internal/services/providers"
"git.front.kjuulh.io/kjuulh/kraken/internal/services/storage"
"go.uber.org/zap"
)
type ServerDeps struct {
logger *zap.Logger
logger *zap.Logger
storageConfig *storage.StorageConfig
gitCfg *providers.GitConfig
openPGP *signer.OpenPGP
}
func NewServerDeps(logger *zap.Logger) *ServerDeps {
@@ -39,13 +33,6 @@ func NewServerDeps(logger *zap.Logger) *ServerDeps {
SshPrivateKeyPassword: "",
}
openPGPConfig := &signer.OpenPgpConfig{
PrivateKeyFilePath: "./example/testkey.private.pgp",
PrivateKeyPassword: "somepassword",
PrivateKeyIdentity: "kraken@kasperhermansen.com",
}
deps.openPGP = signer.NewOpenPGP(logger.With(zap.Namespace("openpgp")), openPGPConfig)
return deps
}
@@ -53,22 +40,10 @@ func (deps *ServerDeps) GetStorageService() *storage.Service {
return storage.NewService(deps.logger.With(zap.Namespace("storage")), deps.storageConfig)
}
func (deps *ServerDeps) GetGitProvider() *providers.GoGit {
return providers.NewGit(deps.logger.With(zap.Namespace("gitProvider")), deps.gitCfg, deps.openPGP)
func (deps *ServerDeps) GetGitProvider() *providers.Git {
return providers.NewGit(deps.logger.With(zap.Namespace("gitProvider")), deps.gitCfg)
}
func (deps *ServerDeps) GetAction() *actions.Action {
return actions.NewAction(deps.logger.With(zap.Namespace("action")))
}
func (deps *ServerDeps) GetActionCreator() *actionc.ActionCreator {
return actionc.NewActionCreator(deps.logger.With(zap.Namespace("action")), deps)
}
func (deps *ServerDeps) GetGitea() *gitproviders.Gitea {
return gitproviders.NewGitea(deps.logger.With(zap.Namespace("gitea")))
}
func (deps *ServerDeps) GetOpenPGP() *signer.OpenPGP {
return deps.openPGP
}

View File

@@ -3,7 +3,7 @@ package actions
import (
"context"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/storage"
"git.front.kjuulh.io/kjuulh/kraken/internal/services/storage"
"go.uber.org/zap"
)

View File

@@ -1 +1,169 @@
package providers
import (
"context"
"time"
"git.front.kjuulh.io/kjuulh/kraken/internal/services/storage"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
"go.uber.org/zap"
"go.uber.org/zap/zapio"
)
// Git is a native git provider, it can clone, pull
// , push and as in abstraction on native git operations
type Git struct {
logger *zap.Logger
gitConfig *GitConfig
}
type GitRepo struct {
repo *git.Repository
}
type GitAuth string
const (
GIT_AUTH_SSH GitAuth = "ssh"
GIT_AUTH_USERNAME_PASSWORD GitAuth = "username_password"
GIT_AUTH_ACCESS_TOKEN GitAuth = "access_token"
GIT_AUTH_ANONYMOUS GitAuth = "anonymous"
GIT_AUTH_SSH_AGENT GitAuth = "ssh_agent"
)
type GitConfig struct {
AuthOption GitAuth
User string
Password string
AccessToken string
SshPublicKeyFilePath string
SshPrivateKeyPassword string
}
func NewGit(logger *zap.Logger, gitConfig *GitConfig) *Git {
return &Git{logger: logger, gitConfig: gitConfig}
}
func (g *Git) Clone(ctx context.Context, storageArea *storage.Area, repoUrl string) (*GitRepo, error) {
g.logger.Debug(
"cloning repository",
zap.String("repoUrl", repoUrl),
zap.String("path", storageArea.Path),
)
auth, err := g.GetAuth()
if err != nil {
return nil, err
}
cloneOptions := git.CloneOptions{
URL: repoUrl,
Auth: auth,
RemoteName: "origin",
ReferenceName: "refs/heads/main",
SingleBranch: true,
NoCheckout: false,
Depth: 1,
RecurseSubmodules: 1,
Progress: &zapio.Writer{
Log: g.logger.With(zap.String("process", "go-git")),
Level: zap.DebugLevel,
},
Tags: 0,
InsecureSkipTLS: false,
CABundle: []byte{},
}
repo, err := git.PlainCloneContext(ctx, storageArea.Path, false, &cloneOptions)
if err != nil {
return nil, err
}
g.logger.Debug("done cloning repo")
return &GitRepo{repo: repo}, nil
}
func (g *Git) Add(ctx context.Context, storageArea *storage.Area, gitRepo *GitRepo) (*git.Worktree, error) {
worktree, err := gitRepo.repo.Worktree()
if err != nil {
return nil, err
}
err = worktree.AddWithOptions(&git.AddOptions{
All: true,
})
if err != nil {
return nil, err
}
status, err := worktree.Status()
if err != nil {
return nil, err
}
g.logger.Info("git status", zap.String("status", status.String()))
return worktree, nil
}
func (g *Git) Commit(ctx context.Context, gitRepo *GitRepo) error {
worktree, err := gitRepo.repo.Worktree()
if err != nil {
return err
}
_, err = worktree.Commit("some-commit", &git.CommitOptions{
All: true,
Author: &object.Signature{
Name: "kraken",
Email: "kraken@kasperhermansen.com",
When: time.Now(),
},
Committer: &object.Signature{
Name: "kraken",
Email: "kraken@kasperhermansen.com",
When: time.Now(),
},
})
if err != nil {
return err
}
return nil
}
func (g *Git) GetAuth() (transport.AuthMethod, error) {
switch g.gitConfig.AuthOption {
case GIT_AUTH_SSH:
sshKey, err := ssh.NewPublicKeysFromFile(
g.gitConfig.User,
g.gitConfig.SshPublicKeyFilePath,
g.gitConfig.SshPrivateKeyPassword,
)
if err != nil {
return nil, err
}
return sshKey, nil
case GIT_AUTH_USERNAME_PASSWORD:
return &http.BasicAuth{
Username: g.gitConfig.User,
Password: g.gitConfig.Password,
}, nil
case GIT_AUTH_ACCESS_TOKEN:
return &http.BasicAuth{
Username: "required-username",
Password: g.gitConfig.AccessToken,
}, nil
case GIT_AUTH_ANONYMOUS:
return nil, nil
case GIT_AUTH_SSH_AGENT:
return ssh.NewSSHAgentAuth(g.gitConfig.User)
default:
return nil, nil
}
}

View File

@@ -1,339 +0,0 @@
package providers
import (
"context"
"errors"
"fmt"
"time"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/signer"
"git.front.kjuulh.io/kjuulh/octopush/internal/services/storage"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
"go.uber.org/zap"
"go.uber.org/zap/zapio"
)
// GoGit is a native git provider, it can clone, pull
// , push and as in abstraction on native git operations
type GoGit struct {
logger *zap.Logger
gitConfig *GitConfig
openPGP *signer.OpenPGP
}
type GoGitRepo struct {
repo *git.Repository
}
func (gr *GoGitRepo) GetHEAD() (string, error) {
head, err := gr.repo.Head()
if err != nil {
return "", err
}
return head.Name().Short(), nil
}
type GitAuth string
const (
GIT_AUTH_SSH GitAuth = "ssh"
GIT_AUTH_USERNAME_PASSWORD GitAuth = "username_password"
GIT_AUTH_ACCESS_TOKEN GitAuth = "access_token"
GIT_AUTH_ANONYMOUS GitAuth = "anonymous"
GIT_AUTH_SSH_AGENT GitAuth = "ssh_agent"
)
type GitConfig struct {
AuthOption GitAuth
User string
Password string
AccessToken string
SshPublicKeyFilePath string
SshPrivateKeyPassword string
}
func NewGit(logger *zap.Logger, gitConfig *GitConfig, openPGP *signer.OpenPGP) *GoGit {
return &GoGit{logger: logger, gitConfig: gitConfig, openPGP: openPGP}
}
func (g *GoGit) GetOriginHEADForRepo(ctx context.Context, gitRepo *GoGitRepo) (string, error) {
auth, err := g.GetAuth()
if err != nil {
return "", err
}
remote, err := gitRepo.repo.Remote("origin")
if err != nil {
return "", err
}
refs, err := remote.ListContext(ctx, &git.ListOptions{
Auth: auth,
})
if err != nil {
return "", err
}
headRef := ""
for _, ref := range refs {
//g.logger.Debug(ref.String())
if ref.Target().IsBranch() {
headRef = ref.Target().Short()
}
}
if headRef == "" {
return "", errors.New("no upstream HEAD branch could be found")
}
return headRef, nil
}
func (g *GoGit) CloneBranch(ctx context.Context, storageArea *storage.Area, repoUrl string, branch string) (*GoGitRepo, error) {
g.logger.Debug(
"cloning repository",
zap.String("repoUrl", repoUrl),
zap.String("path", storageArea.Path),
)
auth, err := g.GetAuth()
if err != nil {
return nil, err
}
cloneOptions := git.CloneOptions{
URL: repoUrl,
Auth: auth,
RemoteName: "origin",
ReferenceName: plumbing.NewBranchReferenceName(branch),
SingleBranch: false,
NoCheckout: false,
Depth: 1,
RecurseSubmodules: 1,
Progress: g.getProgressWriter(),
Tags: 0,
InsecureSkipTLS: false,
CABundle: []byte{},
}
repo, err := git.PlainCloneContext(ctx, storageArea.Path, false, &cloneOptions)
if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
return nil, err
}
g.logger.Debug("done cloning repo")
return &GoGitRepo{repo: repo}, nil
}
func (g *GoGit) Clone(ctx context.Context, storageArea *storage.Area, repoUrl string) (*GoGitRepo, error) {
g.logger.Debug(
"cloning repository",
zap.String("repoUrl", repoUrl),
zap.String("path", storageArea.Path),
)
auth, err := g.GetAuth()
if err != nil {
return nil, err
}
cloneOptions := git.CloneOptions{
URL: repoUrl,
Auth: auth,
RemoteName: "origin",
ReferenceName: "",
SingleBranch: false,
NoCheckout: false,
Depth: 1,
RecurseSubmodules: 1,
Progress: g.getProgressWriter(),
Tags: 0,
InsecureSkipTLS: false,
CABundle: []byte{},
}
repo, err := git.PlainCloneContext(ctx, storageArea.Path, false, &cloneOptions)
if err != nil {
return nil, err
}
g.logger.Debug("done cloning repo")
return &GoGitRepo{repo: repo}, nil
}
func (g *GoGit) getProgressWriter() *zapio.Writer {
return &zapio.Writer{
Log: g.logger.With(zap.String("process", "go-git")),
Level: zap.DebugLevel,
}
}
func (g *GoGit) Add(ctx context.Context, storageArea *storage.Area, gitRepo *GoGitRepo) (*git.Worktree, error) {
worktree, err := gitRepo.repo.Worktree()
if err != nil {
return nil, err
}
err = worktree.AddWithOptions(&git.AddOptions{
All: true,
})
if err != nil {
return nil, err
}
status, err := worktree.Status()
if err != nil {
return nil, err
}
g.logger.Debug("git status", zap.String("status", status.String()))
return worktree, nil
}
func (g *GoGit) CreateBranch(ctx context.Context, gitRepo *GoGitRepo) error {
worktree, err := gitRepo.repo.Worktree()
if err != nil {
return err
}
refSpec := plumbing.NewBranchReferenceName("octopush-apply")
err = gitRepo.repo.CreateBranch(&config.Branch{
Name: "octopush-apply",
Remote: "origin",
Merge: refSpec,
Rebase: "",
})
if err != nil {
return fmt.Errorf("could not create branch: %w", err)
}
err = worktree.Checkout(&git.CheckoutOptions{
Branch: plumbing.ReferenceName(refSpec.String()),
Create: true,
Force: false,
Keep: false,
})
if err != nil {
return fmt.Errorf("could not checkout branch: %w", err)
}
//remoteRef := plumbing.NewRemoteReferenceName("origin", "octopush-apply")
//ref := plumbing.NewSymbolicReference(refSpec, remoteRef)
//err = gitRepo.repo.Storer.SetReference(ref)
//if err != nil {
// return fmt.Errorf("could not set reference: %w", err)
//}
auth, err := g.GetAuth()
if err != nil {
return err
}
err = worktree.PullContext(ctx, &git.PullOptions{
RemoteName: "origin",
ReferenceName: "",
SingleBranch: false,
Depth: 1,
Auth: auth,
RecurseSubmodules: 1,
Progress: g.getProgressWriter(),
Force: true,
InsecureSkipTLS: false,
CABundle: []byte{},
})
if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
return fmt.Errorf("could not pull from origin: %w", err)
}
g.logger.Debug("done creating branches")
return nil
}
func (g *GoGit) Commit(ctx context.Context, gitRepo *GoGitRepo) error {
worktree, err := gitRepo.repo.Worktree()
if err != nil {
return err
}
_, err = worktree.Commit("some-commit", &git.CommitOptions{
All: true,
Author: &object.Signature{Name: "octopush", Email: "octopush@kasperhermansen.com", When: time.Now()},
Committer: &object.Signature{Name: "octopush", Email: "octopush@kasperhermansen.com", When: time.Now()},
SignKey: g.openPGP.SigningKey,
})
if err != nil {
return err
}
g.logger.Debug("done commiting objects")
return nil
}
func (g *GoGit) Push(ctx context.Context, gitRepo *GoGitRepo) error {
auth, err := g.GetAuth()
if err != nil {
return err
}
err = gitRepo.repo.PushContext(ctx, &git.PushOptions{
RemoteName: "origin",
RefSpecs: []config.RefSpec{},
Auth: auth,
Progress: g.getProgressWriter(),
Prune: false,
Force: true,
InsecureSkipTLS: false,
CABundle: []byte{},
RequireRemoteRefs: []config.RefSpec{},
})
if err != nil {
return err
}
g.logger.Debug("done pushing branch")
return nil
}
func (g *GoGit) GetAuth() (transport.AuthMethod, error) {
switch g.gitConfig.AuthOption {
case GIT_AUTH_SSH:
sshKey, err := ssh.NewPublicKeysFromFile(
g.gitConfig.User,
g.gitConfig.SshPublicKeyFilePath,
g.gitConfig.SshPrivateKeyPassword,
)
if err != nil {
return nil, err
}
return sshKey, nil
case GIT_AUTH_USERNAME_PASSWORD:
return &http.BasicAuth{
Username: g.gitConfig.User,
Password: g.gitConfig.Password,
}, nil
case GIT_AUTH_ACCESS_TOKEN:
return &http.BasicAuth{
Username: "required-username",
Password: g.gitConfig.AccessToken,
}, nil
case GIT_AUTH_ANONYMOUS:
return nil, nil
case GIT_AUTH_SSH_AGENT:
return ssh.NewSSHAgentAuth(g.gitConfig.User)
default:
return nil, nil
}
}

View File

@@ -1,81 +0,0 @@
package signer
import (
"context"
"errors"
"os"
"strings"
"git.front.kjuulh.io/kjuulh/curre"
"github.com/ProtonMail/go-crypto/openpgp"
"go.uber.org/zap"
)
type OpenPGP struct {
logger *zap.Logger
SigningKey *openpgp.Entity
config *OpenPgpConfig
}
type OpenPgpConfig struct {
PrivateKeyFilePath string
PrivateKeyPassword string
PrivateKeyIdentity string
}
func NewOpenPGP(logger *zap.Logger, config *OpenPgpConfig) *OpenPGP {
return &OpenPGP{
logger: logger,
config: config,
}
}
func NewOpenPGPApp(openPGP *OpenPGP) curre.Component {
return curre.NewFunctionalComponent(&curre.FunctionalComponent{
InitFunc: func(_ *curre.FunctionalComponent, ctx context.Context) error {
keyring, err := buildKeyring(ctx, openPGP)
if err != nil {
openPGP.logger.Panic("could not build keyring", zap.Error(err))
return err
}
openPGP.SigningKey = keyring
return nil
},
StartFunc: func(fc *curre.FunctionalComponent, ctx context.Context) error {
return nil
},
StopFunc: func(fc *curre.FunctionalComponent, ctx context.Context) error {
return nil
},
})
}
func buildKeyring(_ context.Context, openPGP *OpenPGP) (*openpgp.Entity, error) {
content, err := os.ReadFile(openPGP.config.PrivateKeyFilePath)
if err != nil {
return nil, err
}
reader := strings.NewReader(string(content))
es, err := openpgp.ReadArmoredKeyRing(reader)
if err != nil {
return nil, err
}
for _, key := range es {
for k := range key.Identities {
if strings.Contains(k, openPGP.config.PrivateKeyIdentity) {
err = key.PrivateKey.Decrypt([]byte(openPGP.config.PrivateKeyPassword))
if err != nil {
return nil, err
}
return key, nil
}
}
}
return nil, errors.New("could not find key matching identity")
}

View File

@@ -22,7 +22,7 @@ func NewDefaultStorageConfig() (*StorageConfig, error) {
return nil, err
}
return &StorageConfig{
Path: path.Join(tempDir, "octopush"),
Path: path.Join(tempDir, "kraken"),
}, nil
}

View File

@@ -2,62 +2,27 @@
## POC:
- [x] Add cuddle
- [ ] Add cuddle
- [x] Create storage mechanism
- [x] Pull repository into storage
- [x] Create test action to run on repository
- [x] Sign commit using gpg
- [x] Push commits to branch
- [ ] Sign commit using gpg
### Not in scope
- Pooled runners
- CLI with options
- Server app
- Git hosting providers
- [ ] Pooled runners
- [ ] CLI with options
- [ ] Server app
- [ ] Git hosting providers
## Version 0.1
- [x] Setup a way to choose actions and predicates
- [x] Allow instantiation of actions, octopush template repo etc.
- [x] Implement docker action
- [x] Create pr for gitea provider
- [x] Providing query results
- [x] Create CLI to trigger action
- [ ] Allow instantiation of actions, kraken template repo etc.
- [ ] Create predicate handle
- [ ] Think about some sort of isolation
- [ ] Create CLI to trigger action
- [ ] Setup pool of runners
- [ ] Run authenticated on servers
- [ ] Create queuing system
## Version 0.2
- [x] Write README
- [x] Fix git issues
- [x] Allow octopush to run directly on the cli
## Version 0.3
- [ ] Make select depend on query
- [ ] Make configurable ssh user
- [ ] Make configurable gpg keyset
- [ ] Make configurable git provider
- [ ] Add github
- [ ] Create templating function for easily creating new actions
- [ ] Add way to see progress of runners
- [ ] Implement global .octopush store for easy access to settings
- [ ] Move builders to start instead of every building on every action
- [ ] Setup releases on github
- [ ] Setup CI
- [ ] Setup static analysis
- [ ] Setup releases on gitea using drone
- [ ] Figure out a license (probably MIT)
## Version 0.4
- [ ] Create setup version for local actions
- [ ] Create setup version for server actions
- [ ] Create json schema
- [ ] Move roadmap to release / changelog
## Version 0.x
- Think about some sort of isolation
- Run authenticated on servers
- Create queuing system
- Setup pool of runners
## Version 1.0

View File

@@ -1,7 +0,0 @@
#!/bin/bash
set -e
git remote add github git@github.com:kjuulh/octopush.git || true
git push -f github main

View File

@@ -1,15 +0,0 @@
#!/bin/bash
set -e
run_server="cuddle_cli x run_server"
$run_server &
sleep 1s
cuddle_cli x run_client
sleep 5s
kill %1

View File

@@ -1,13 +0,0 @@
#!/bin/bash
set -e
current_branch=$(git branch --show-current)
export $(cat .env | xargs)
#go run cmd/octopush/octopush.go process --actions-repo "git@git.front.kjuulh.io:kjuulh/octopush.git" --branch "$current_branch" --path "_examples/actions/write_a_readme"
go run cmd/octopush/octopush.go process \
--actions-repo "git@git.front.kjuulh.io:kjuulh/octopush.git"\
--branch "$current_branch" \
--path "_examples/actions/add_releaserc"

View File

@@ -1,7 +0,0 @@
#!/bin/bash
set -e
export $(cat .env | xargs)
go run cmd/server/server.go start

View File

@@ -1,7 +0,0 @@
FROM golang
COPY . .
RUN go build cmd/server/server.go
CMD [ "server", "start" ]