diff --git a/README.md b/README.md index ffb3a3f6b28131828a6e03d253f91c133dcf8e2b..fba182a4529394951195b2055e42e4c9253ea22b 100644 --- a/README.md +++ b/README.md @@ -2,28 +2,6 @@ # jsonnetpecker A [Woodpecker Config Extension](https://woodpecker-ci.org/docs/usage/extensions/configuration-extension) to parse [jsonnet](https://jsonnet.org/) configs. -## Configuration - -Configuration can be provided via flags, environment variables (prefixed with `JSONNETPECKER_`), or a [jsonnet](https://jsonnet.org/) config file (default: `.config.jsonnet`, override with `--config`). - -| Flag | Short | Env | Default | Description | -|------|-------|-----|---------|-------------| -| `--port` | `-p` | `JSONNETPECKER_PORT` | `0` | Port to listen on | -| `--public-key` | `-k` | `JSONNETPECKER_PUBLIC_KEY` | | Woodpecker public key for verification | -| `--public-key-file` | `-K` | `JSONNETPECKER_PUBLIC_KEY_FILE` | | Path to file containing the Woodpecker public key | -| `--log-level` | `-l` | `JSONNETPECKER_LOG_LEVEL` | `info` | Log level (`debug`, `info`, `warn`, `error`) | -| `--json` | `-j` | `JSONNETPECKER_JSON` | `false` | Enable JSON logging | - -### Config file example - -```jsonnet -{ - port: 8080, - "public-key-file": "/etc/woodpecker/public.key", - "log-level": "info", -} -``` - ## License [MIT](LICENSE) diff --git a/debug.patch b/debug.patch deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 Binary files a/debug.patch and /dev/null differ diff --git a/flake.lock b/flake.lock deleted file mode 100644 index 3399eb1da0882c471906638c876f104330f381cc..0000000000000000000000000000000000000000 --- a/flake.lock +++ /dev/null @@ -1,48 +0,0 @@ -{ - "nodes": { - "gitpecker": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1778680061, - "narHash": "sha256-xiYIZtQYAHHZuKWm7kb23ucae4y8JfxgrIPvGCnbpno=", - "ref": "refs/heads/main", - "rev": "8d7b5b7c99eb32a666999910ff391029cbd790af", - "revCount": 4, - "type": "git", - "url": "https://git.jolheiser.com/gitpecker.git" - }, - "original": { - "type": "git", - "url": "https://git.jolheiser.com/gitpecker.git" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1778580735, - "narHash": "sha256-t+8AVV8ExvOmslz2sLIgw/hJBKlyl65rJvxjvvjHgpE=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "48d91f2c0ce7b9e589f967d4f685153dd765dcdd", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "gitpecker": "gitpecker", - "nixpkgs": "nixpkgs" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix deleted file mode 100644 index fa85ec12592da2f3a6df8125d6865b737aad0c45..0000000000000000000000000000000000000000 --- a/flake.nix +++ /dev/null @@ -1,246 +0,0 @@ -{ - description = "jsonnet config extension for woodpecker"; - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; - gitpecker = { - url = "git+https://git.jolheiser.com/gitpecker.git"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - outputs = - { - self, - nixpkgs, - gitpecker, - ... - }: - let - systems = [ - "aarch64-darwin" - "aarch64-linux" - "x86_64-darwin" - "x86_64-linux" - ]; - forAllSystems = f: nixpkgs.lib.genAttrs systems f; - in - { - packages = forAllSystems ( - system: - let - pkgs = import nixpkgs { inherit system; }; - in - { - default = self.packages.${system}.jsonnetpecker; - jsonnetpecker = pkgs.buildGoModule rec { - pname = "jsonnetpecker"; - version = self.rev or "dev"; - src = pkgs.nix-gitignore.gitignoreSource [ ] ( - builtins.path { - name = pname; - path = ./.; - } - ); - vendorHash = nixpkgs.lib.fileContents ./go.mod.sri; - meta = { - description = "jsonnet config extension for woodpecker"; - homepage = "https://git.jolheiser.com/jsonnetpecker"; - mainProgram = "jsonnetpecker"; - }; - }; - } - ); - devShells = forAllSystems ( - system: - let - pkgs = import nixpkgs { inherit system; }; - in - { - default = pkgs.mkShell { - nativeBuildInputs = with pkgs; [ - go - gopls - gofumpt - woodpecker-server - ]; - }; - } - ); - nixosConfigurations.jsonnetpeckerVM = nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - modules = [ - ( - { pkgs, ... }: - { - services.getty.autologinUser = "root"; - services.dex = { - enable = true; - settings = { - issuer = "http://localhost:5556/dex"; - storage.type = "memory"; - web.http = "0.0.0.0:5556"; - connectors = [ - { - type = "mockCallback"; - id = "mock"; - name = "Mock"; - } - ]; - oauth2.skipApprovalScreen = true; - staticClients = [ - { - id = "woodpecker"; - secret = "woodpecker-secret"; - name = "Woodpecker"; - redirectURIs = [ "http://localhost:8000/authorize" ]; - } - ]; - }; - }; - services.woodpecker-server = { - enable = true; - package = pkgs.woodpecker-server.overrideAttrs (oldAttrs: { - doCheck = false; - patches = (oldAttrs.patches or [ ]) ++ [ - ./jsonnet.patch - ./debug.patch - ]; - }); - environment = { - WOODPECKER_HOST = "http://0.0.0.0:8000"; - WOODPECKER_OPEN = "true"; - WOODPECKER_ADDON_FORGE = "${gitpecker.packages.x86_64-linux.gitpecker}/bin/gitpecker"; - WOODPECKER_CONFIG_SERVICE_ENDPOINT = "http://localhost:8080"; - WOODPECKER_EXTENSIONS_ALLOWED_HOSTS = "localhost"; - WOODPECKER_LOG_LEVEL = "trace"; - WOODPECKER_ADMIN = "kilgore@kilgore.trout"; - WOODPECKER_AGENT_SECRET = "Testing123"; - WOODPECKER_EXTRA_DEFAULT_PIPELINE_CONFIGS = ".woodpecker.jsonnet"; - GITPECKER_REPOS = "/var/lib/forge/"; - GITPECKER_URL = "/var/lib/forge/"; - GITPECKER_PROVIDER = "http://localhost:5556/dex"; - GITPECKER_CLIENT_ID = "woodpecker"; - GITPECKER_CLIENT_SECRET = "woodpecker-secret"; - GITPECKER_REDIRECT = "http://localhost:8000/authorize"; - }; - }; - systemd.services.woodpecker-server.after = [ "dex.service" ]; - services.woodpecker-agents.agents."007" = { - enable = true; - path = with pkgs; [ - git - git-lfs - bash - coreutils - woodpecker-plugin-git - ]; - environment = { - WOODPECKER_AGENT_SECRET = "Testing123"; - }; - }; - systemd.services.jsonnetpecker = { - description = "jsonnetpecker config extension"; - wantedBy = [ "multi-user.target" ]; - after = [ - "woodpecker-server.service" - "network.target" - ]; - path = with pkgs; [ curl ]; - serviceConfig = { - Type = "simple"; - RuntimeDirectory = "jsonnetpecker"; - ExecStartPre = pkgs.writeShellScript "jsonnetpecker-fetch-pubkey" '' - until curl -sf http://localhost:8000/api/healthz > /dev/null 2>&1; do sleep 1; done - curl -sf "http://localhost:8000/api/signature/public-key" -o /run/jsonnetpecker/pubkey.pem - ''; - ExecStart = "${self.packages.x86_64-linux.jsonnetpecker}/bin/jsonnetpecker --port 8080 --log-level debug --public-key-file /run/jsonnetpecker/pubkey.pem"; - }; - }; - virtualisation.vmVariant.virtualisation = { - cores = 2; - memorySize = 2048; - graphics = false; - forwardPorts = [ - { - from = "host"; - host.port = 8000; - guest.port = 8000; - } - { - from = "host"; - host.port = 5556; - guest.port = 5556; - } - ]; - }; - networking.firewall.enable = false; - systemd.services."setup-vm" = { - wantedBy = [ "multi-user.target" ]; - path = with pkgs; [ - git - ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - User = "root"; - Group = "root"; - ExecStart = - let - pipeline = pkgs.writeText "pipeline.jsonnet" '' - local step(name) = { - name: name, - image: "bash", - commands: ['echo "' + name + '"'], - }; - - { - // when: {event: "manual"}, - steps: [step("honk"), step("bonk")], - } - ''; - in - pkgs.writeShellScript "setup-vm-script" '' - git config --global user.name "NixUser" - git config --global user.email "nixuser@example.com" - git config --global init.defaultBranch main - git config --global push.autoSetupRemote true - - mkdir -p /var/lib/forge - pushd /var/lib/forge - git init --bare test1.git - popd - git clone /var/lib/forge/test1.git - pushd test1 - cp ${pipeline} .woodpecker.jsonnet - git add . - git commit -m "honk" - git push - popd - ''; - }; - }; - environment.systemPackages = with pkgs; [ - git - ]; - system.stateVersion = "23.11"; - } - ) - ]; - }; - apps = forAllSystems ( - system: - let - pkgs = import nixpkgs { inherit system; }; - in - { - vm = { - type = "app"; - program = "${pkgs.writeShellScript "vm" '' - nixos-rebuild build-vm --flake .#jsonnetpeckerVM - QEMU_NET_OPTS="restrict=off" ./result/bin/run-nixos-vm - rm nixos.qcow2 - ''}"; - }; - } - ); - }; -} diff --git a/go.mod.sri b/go.mod.sri deleted file mode 100644 index ed9babc7712fefcaf638ad4a546e0aa117263f5f..0000000000000000000000000000000000000000 --- a/go.mod.sri +++ /dev/null @@ -1 +0,0 @@ -sha256-kRc/W1YqcqOVdwgJKSQCwQlAx2Pr5nw5q6HZlSik1S0= \ No newline at end of file diff --git a/jsonnet.patch b/jsonnet.patch deleted file mode 100644 index a22c9a39e53081cfc844bf971a5722c6f76c4df5..0000000000000000000000000000000000000000 --- a/jsonnet.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 6d284379349794ad8939842e4067828aaab86cb9 Mon Sep 17 00:00:00 2001 -From: jolheiser -Date: Wed, 13 May 2026 15:14:24 -0500 -Subject: [PATCH] feat: allow admins to extend default pipeline config paths - -Signed-off-by: jolheiser ---- - cmd/server/flags.go | 8 ++++++++ - cmd/server/setup.go | 4 ++++ - server/services/config/forge.go | 2 +- - shared/constant/constant.go | 4 ++-- - 4 files changed, 15 insertions(+), 3 deletions(-) - -diff --git a/cmd/server/flags.go b/cmd/server/flags.go -index 2638bf5f7..ecfdb5d5f 100644 ---- a/cmd/server/flags.go -+++ b/cmd/server/flags.go -@@ -289,6 +289,14 @@ var flags = append([]cli.Flag{ - Name: "config-extension-netrc", - Usage: "whether global configuration extension should receive netrc data", - }, -+ &cli.StringSliceFlag{ -+ Sources: cli.EnvVars("WOODPECKER_EXTRA_DEFAULT_PIPELINE_CONFIGS"), -+ Name: "extra-default-pipeline-configs", -+ Usage: "extra default pipeline config paths to check", -+ Config: cli.StringConfig{ -+ TrimSpace: true, -+ }, -+ }, - &cli.StringFlag{ - Sources: cli.EnvVars("WOODPECKER_REGISTRY_EXTENSION_ENDPOINT"), - Name: "registry-extension-endpoint", -diff --git a/cmd/server/setup.go b/cmd/server/setup.go -index 6e8bb194c..5e74fbaf4 100644 ---- a/cmd/server/setup.go -+++ b/cmd/server/setup.go -@@ -45,6 +45,7 @@ import ( - "go.woodpecker-ci.org/woodpecker/v3/server/store" - "go.woodpecker-ci.org/woodpecker/v3/server/store/datastore" - "go.woodpecker-ci.org/woodpecker/v3/server/store/types" -+ "go.woodpecker-ci.org/woodpecker/v3/shared/constant" - ) - - const ( -@@ -158,6 +159,9 @@ func setupJWTSecret(_store store.Store) (string, error) { - } - - func setupEvilGlobals(ctx context.Context, c *cli.Command, s store.Store) (err error) { -+ // default config paths -+ constant.DefaultConfigOrder = append(constant.DefaultConfigOrder, c.StringSlice("extra-default-pipeline-configs")...) -+ - // services - server.Config.Services.Logs = logging.New() - server.Config.Services.Membership = setupMembershipService(ctx, s) -diff --git a/server/services/config/forge.go b/server/services/config/forge.go -index 57bf040e8..aee3ad7c2 100644 ---- a/server/services/config/forge.go -+++ b/server/services/config/forge.go -@@ -97,7 +97,7 @@ func (f *forgeFetcherContext) fetch(c context.Context, config string) ([]*types. - - log.Trace().Msgf("configFetcher[%s]: user did not define own config, following default procedure", f.repo.FullName) - // for the order see shared/constants/constants.go -- fileMetas, err := f.getFirstAvailableConfig(ctx, constant.DefaultConfigOrder[:]) -+ fileMetas, err := f.getFirstAvailableConfig(ctx, constant.DefaultConfigOrder) - if err == nil { - return fileMetas, nil - } -diff --git a/shared/constant/constant.go b/shared/constant/constant.go -index e56957b82..9de20ca53 100644 ---- a/shared/constant/constant.go -+++ b/shared/constant/constant.go -@@ -16,9 +16,9 @@ package constant - - import "time" - --// DefaultConfigOrder represent the priority in witch woodpecker search for a pipeline config by default -+// DefaultConfigOrder represent the priority in which woodpecker searches for a pipeline config by default - // folders are indicated by supplying a trailing slash. --var DefaultConfigOrder = [...]string{ -+var DefaultConfigOrder = []string{ - ".woodpecker/", - ".woodpecker.yaml", - ".woodpecker.yml", - -base-commit: 48e1ece20057e59de4e8e3fe25fc1f3a41e8a020 --- -2.50.1 - diff --git a/main.go b/main.go index 98fa14b4f3f18e8bac1f88d7be582f24a861941a..8b22f43e0407c32e4b9fc2f0e2eceec185f748f2 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,6 @@ "encoding/pem" "errors" "flag" "fmt" - "io" "log/slog" "net/http" "os" @@ -33,7 +32,7 @@ logLevel slog.Level logJSON bool } -const magicKey = "woodpecker-ci-extensions" +const magicKey = "woodpcecker-ci-extensions" func maine() error { var args args @@ -72,14 +71,6 @@ ff.WithEnvVarPrefix("JSONNETPECKER"), ); err != nil { return err } - logOpts := &slog.HandlerOptions{Level: args.logLevel} - var logger slog.Handler - if args.logJSON { - logger = slog.NewJSONHandler(os.Stderr, logOpts) - } else { - logger = slog.NewTextHandler(os.Stderr, logOpts) - } - slog.SetDefault(slog.New(logger)) if args.pubKeyFile != "" { data, err := os.ReadFile(args.pubKeyFile) @@ -109,15 +100,8 @@ mux.Get("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(`send me some jsonnet`)) }) mux.With(verifyMiddleware(pubKey)).Post("/", func(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - json.NewEncoder(w).Encode(map[string]string{"error": "could not read body"}) - w.WriteHeader(http.StatusBadRequest) - return - } - slog.Debug("incoming request", slog.String("body", string(body))) var req Request - if err := json.Unmarshal(body, &req); err != nil { + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]string{"error": "could not decode JSON"}) return @@ -125,7 +109,6 @@ } configs := make([]Config, 0, len(req.Configs)) for _, cfg := range req.Configs { - slog.Debug("incoming config", slog.String("name", cfg.Name), slog.String("data", cfg.Data)) if strings.HasSuffix(cfg.Name, "jsonnet") { vm := jsonnet.MakeVM() jsonData, err := vm.EvaluateAnonymousSnippet(cfg.Name, cfg.Data) @@ -147,8 +130,7 @@ cfg.Data = string(yamlData) } configs = append(configs, cfg) } - slog.Debug("YAML response", slog.Any("configs", configs)) - json.NewEncoder(w).Encode(Response{Configs: configs}) + json.NewEncoder(w).Encode(Request{Configs: configs}) }) return mux @@ -191,7 +173,6 @@ func verifyMiddleware(pubKey ed25519.PublicKey) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := verify(pubKey, r); err != nil { - slog.Error("verify failed", slog.Any("err", err), slog.String("signature", r.Header.Get("Signature")), slog.String("signature-input", r.Header.Get("Signature-Input")), slog.String("content-digest", r.Header.Get("Content-Digest"))) w.WriteHeader(http.StatusUnauthorized) json.NewEncoder(w).Encode(map[string]string{"error": "Failed to verify request"}) return @@ -202,10 +183,6 @@ } } type Request struct { - Configs []Config `json:"configuration"` -} - -type Response struct { Configs []Config `json:"configs"` } diff --git a/main_test.go b/main_test.go index bf56fa6c655f613d14d7ea4701a4736f7a484fdd..1e563ffd4089ee6081746c6b315e3f52ec568214 100644 --- a/main_test.go +++ b/main_test.go @@ -182,7 +182,7 @@ handler.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) - var resp Response + var resp Request err = json.NewDecoder(w.Body).Decode(&resp) assert.NoError(t, err) assert.Equal(t, 1, len(resp.Configs)) @@ -214,7 +214,7 @@ handler.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) - var resp Response + var resp Request err = json.NewDecoder(w.Body).Decode(&resp) assert.NoError(t, err) assert.Equal(t, 1, len(resp.Configs)) @@ -255,7 +255,7 @@ handler.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) - var resp Response + var resp Request err = json.NewDecoder(w.Body).Decode(&resp) assert.NoError(t, err) assert.Equal(t, 3, len(resp.Configs)) @@ -302,7 +302,7 @@ handler.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) - var resp Response + var resp Request err = json.NewDecoder(w.Body).Decode(&resp) assert.NoError(t, err) assert.Equal(t, 2, len(resp.Configs))