Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# 0004 TypeScript Checklist

## Baseline

- [x] Plugin work lives under `extended/plugins/send-message-ts/`.
- [x] The subtree stands alone.
- [x] No committed Slack credential appears anywhere.
- [x] The plugin owns Kubernetes reads and Slack calls.

## Phase 0: pin the contract

- [x] Re-read `proposal.md`.
- [x] Re-read `spec.md`.
- [x] Re-read the `send-message` slice in proposal `0002`.
- [x] Keep scope to Slack only.

## Phase 1: create the tiny repo

- [x] Create `extended/plugins/send-message-ts/`.
- [x] Add runtime source.
- [x] Add image build.
- [x] Add `plugin.yaml`.
- [x] Add CRD manifests.
- [x] Add RBAC manifests.
- [x] Add smoke assets.

## Phase 2: implement the runtime

- [x] Implement `POST /api/v1/step.execute`.
- [x] Enforce bearer auth from `/var/run/kargo/token`.
- [x] Read `MessageChannel`.
- [x] Read `ClusterMessageChannel`.
- [x] Read referenced `Secret`.
- [x] Send plaintext Slack payloads.
- [x] Send encoded Slack payloads.
- [x] Return `slack.threadTS`.

## Phase 3: test the contract

- [x] Add auth tests.
- [x] Add channel lookup tests.
- [x] Add Secret lookup tests.
- [x] Add plaintext payload tests.
- [x] Add encoded payload tests.
- [x] Add XML decode tests.
- [x] Add Slack failure tests.

## Phase 4: smoke

- [x] Add plugin-owned `smoke/smoke-test.ts`.
- [x] Keep smoke orchestration in TypeScript, not shell.
- [x] Build the image.
- [x] Load it into kind.
- [x] Install CRDs and RBAC.
- [x] Install StepPlugin `ConfigMap`.
- [x] Create local-only test Secret.
- [x] Create test `MessageChannel`.
- [x] Run a `Stage` with `uses: send-message`.
- [x] Assert `Succeeded`.
- [x] Assert non-empty `slack.threadTS`.

## Phase 5: mandatory radical simplification pass 1

- [x] Ask "can I remove a package and still keep this nicer?"
- [x] Ask "can I remove a build step and still keep this clearer?"
- [x] Ask "am I typing around the problem instead of solving it?"
- [x] Delete anything that fails those checks.

## Phase 6: mandatory radical simplification pass 2

- [x] Smoke re-run is still pending local env and repo hook wiring.
- [x] Re-run tests and smoke from a green tree.
- [x] Ask "can I make this radically simpler?"
- [x] Remove type, framework, or folder structure that only signals taste.
- [x] Remove wrappers around direct HTTP or Slack calls unless they buy clarity.
- [x] Stop only when the repo still reads like a small third-party plugin.
97 changes: 97 additions & 0 deletions extended/docs/proposals/0004-send-message-step-plugin/plan.ts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# 0004 TypeScript Plan

## Goal

- Show the same `send-message` plugin contract in a form that benefits from
the strongest Slack SDK support.
- Keep all plugin-owned code under `extended/plugins/send-message-ts/`.
- Keep the subtree standalone enough to behave like its own Git repo.
- Match the same Slack-only contract as `spec.md`.

## Design Rule

- Prefer the smallest runtime that still looks natural to a TypeScript user.
- Use Slack's first-party Node SDK if it reduces code and ambiguity.
- Do not pull in a Kubernetes client unless it is clearly smaller than direct
HTTPS calls.
- If typing machinery becomes louder than the plugin logic, cut it back.

## Runtime Shape

- Runtime:
- TypeScript on Node
- Suggested layout:
- `extended/plugins/send-message-ts/`
- `src/server.ts`
- `smoke/smoke-test.ts`
- `package.json`
- `tsconfig.json`
- `Dockerfile`
- `plugin.yaml`
- `manifests/`
- `smoke/`
- Suggested server shape:
- one small HTTP server
- one request parser
- one Kubernetes reader
- one Slack sender

## Minimal Dependency Target

- Acceptable:
- `@slack/web-api`
- `yaml`
- `fast-xml-parser`
- Strong preference:
- built-in Node HTTP server
- built-in `fetch`
- direct HTTPS to Kubernetes
- Avoid:
- Express, Nest, or similar frameworks
- generated API clients
- build chains that are bigger than the plugin

## Behavior

- Implement `POST /api/v1/step.execute`.
- Enforce bearer auth from `/var/run/kargo/token`.
- Read `MessageChannel` and `ClusterMessageChannel` directly from Kubernetes.
- Read referenced `Secret` directly from Kubernetes.
- Send Slack messages with the Slack Web API client or direct HTTP if smaller.
- Support:
- plaintext
- `json`
- `yaml`
- `xml`
- Match the response contract in `spec.md`.

## Tests

- Keep tests inside the subtree.
- Prefer a small test runner and direct fixtures.
- Cover:
- auth
- channel lookup
- Secret lookup
- plaintext payload shaping
- encoded payload shaping
- XML decode shape
- Slack error handling

## Smoke

- Own `smoke/smoke-test.ts` inside the subtree.
- Assume Kargo already exists.
- Use TypeScript for the smoke orchestration too.
- Build image, install manifests, create Secret and channel, run a Stage,
assert `Succeeded`, assert non-empty `slack.threadTS`.

## Mandatory Simplify Passes

- Simplify pass 1, before full smoke:
- ask "can I delete a package and keep the code clearer?"
- ask "can I shrink the build chain?"
- Simplify pass 2, after green:
- ask "can I make this radically simpler?"
- remove type or framework structure that makes the plugin look harder than
it is
3 changes: 3 additions & 0 deletions extended/plugins/send-message-ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dist/
node_modules/

22 changes: 22 additions & 0 deletions extended/plugins/send-message-ts/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM node:22-alpine AS build

WORKDIR /app

COPY package.json package-lock.json ./
RUN npm ci

COPY tsconfig.json ./
COPY src ./src
RUN npm run build

FROM node:22-alpine

WORKDIR /app
ENV NODE_ENV=production

COPY package.json package-lock.json ./
RUN npm ci --omit=dev

COPY --from=build /app/dist/src ./dist/src

ENTRYPOINT ["node", "dist/src/send-message-plugin.js"]
82 changes: 82 additions & 0 deletions extended/plugins/send-message-ts/manifests/crds.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: messagechannels.ee.kargo.akuity.io
spec:
group: ee.kargo.akuity.io
names:
kind: MessageChannel
plural: messagechannels
singular: messagechannel
listKind: MessageChannelList
scope: Namespaced
versions:
- name: v1alpha1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
secretRef:
type: object
properties:
name:
type: string
required:
- name
slack:
type: object
properties:
channelID:
type: string
required:
- channelID
required:
- secretRef
- slack
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: clustermessagechannels.ee.kargo.akuity.io
spec:
group: ee.kargo.akuity.io
names:
kind: ClusterMessageChannel
plural: clustermessagechannels
singular: clustermessagechannel
listKind: ClusterMessageChannelList
scope: Cluster
versions:
- name: v1alpha1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
secretRef:
type: object
properties:
name:
type: string
required:
- name
slack:
type: object
properties:
channelID:
type: string
required:
- channelID
required:
- secretRef
- slack

19 changes: 19 additions & 0 deletions extended/plugins/send-message-ts/manifests/rbac.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: send-message-step-plugin-reader
rules:
- apiGroups:
- ee.kargo.akuity.io
resources:
- messagechannels
- clustermessagechannels
verbs:
- get
- apiGroups:
- ""
resources:
- secrets
verbs:
- get

Loading
Loading