feat: initial commit — kubectl/configure, infisical/fetch-secret, helm/upgrade actions

This commit is contained in:
2026-05-20 18:32:57 -04:00
commit a8265aa6e0
28 changed files with 889 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
# Docker Build and Push
<!-- action-docs-description source="action.yml" -->
## Description
Build a Docker image and push it to the Gitea container registry
<!-- action-docs-description source="action.yml" -->
<!-- action-docs-inputs source="action.yml" -->
## Inputs
| name | description | required | default |
| --- | --- | --- | --- |
| `REGISTRY` | <p>Container registry hostname</p> | `false` | `gitea.pixelparasol.com` |
| `REGISTRY_USERNAME` | <p>Registry login username</p> | `false` | `deac` |
| `REGISTRY_TOKEN` | <p>Registry login token or password</p> | `true` | `""` |
| `IMAGE_PATH` | <p>Full registry image path (e.g. gitea.pixelparasol.com/stat-tackler/stat-tackler-api)</p> | `true` | `""` |
| `IMAGE_TAG` | <p>Tag to apply in addition to latest (e.g. stage-<sha>)</p> | `true` | `""` |
| `PLATFORMS` | <p>Comma-separated buildx platform list</p> | `false` | `linux/amd64,linux/arm/v7,linux/arm64` |
| `ARTIFACT_NAME` | <p>Name of the build artifact to download</p> | `false` | `dist` |
| `ARTIFACT_PATH` | <p>Destination path for the downloaded artifact</p> | `false` | `dist` |
| `TAG_LATEST` | <p>Also tag and push the image as latest</p> | `false` | `false` |
<!-- action-docs-inputs source="action.yml" -->
<!-- action-docs-runs source="action.yml" -->
## Runs
This action is a `composite` action.
<!-- action-docs-runs source="action.yml" -->
+58
View File
@@ -0,0 +1,58 @@
name: Docker Build and Push
description: Build a Docker image and push it to the Gitea container registry
inputs:
REGISTRY:
description: "Container registry hostname"
default: "gitea.pixelparasol.com"
REGISTRY_USERNAME:
description: "Registry login username"
default: "deac"
REGISTRY_TOKEN:
description: "Registry login token or password"
required: true
IMAGE_PATH:
description: "Full registry image path (e.g. gitea.pixelparasol.com/stat-tackler/stat-tackler-api)"
required: true
IMAGE_TAG:
description: "Tag to apply in addition to latest (e.g. stage-<sha>)"
required: true
PLATFORMS:
description: "Comma-separated buildx platform list"
default: "linux/amd64,linux/arm/v7,linux/arm64"
ARTIFACT_NAME:
description: "Name of the build artifact to download"
default: "dist"
ARTIFACT_PATH:
description: "Destination path for the downloaded artifact"
default: "dist"
TAG_LATEST:
description: "Also tag and push the image as latest"
default: "false"
runs:
using: composite
steps:
- name: Login to Gitea Container Registry
uses: docker/login-action@v3
with:
registry: ${{ inputs.REGISTRY }}
username: ${{ inputs.REGISTRY_USERNAME }}
password: ${{ inputs.REGISTRY_TOKEN }}
- name: Download Build Artifact
uses: actions/download-artifact@v3
with:
name: ${{ inputs.ARTIFACT_NAME }}
path: ${{ inputs.ARTIFACT_PATH }}
- name: Docker Build and Push
shell: sh
run: |
TAGS="-t ${{ inputs.IMAGE_PATH }}:${{ inputs.IMAGE_TAG }}"
if [ "${{ inputs.TAG_LATEST }}" = "true" ]; then
TAGS="$TAGS -t ${{ inputs.IMAGE_PATH }}:latest"
fi
docker buildx build -f Dockerfile . \
--platform ${{ inputs.PLATFORMS }} \
--push \
$TAGS
+22
View File
@@ -0,0 +1,22 @@
# Create and Push Git Tag
<!-- action-docs-description source="action.yml" -->
## Description
Creates and pushes a git tag in the current repository
<!-- action-docs-description source="action.yml" -->
<!-- action-docs-inputs source="action.yml" -->
## Inputs
| name | description | required | default |
| --- | --- | --- | --- |
| `tag` | <p>The tag name to create</p> | `true` | `""` |
| `token` | <p>Gitea token with repository write access</p> | `true` | `""` |
<!-- action-docs-inputs source="action.yml" -->
<!-- action-docs-runs source="action.yml" -->
## Runs
This action is a `composite` action.
<!-- action-docs-runs source="action.yml" -->
+23
View File
@@ -0,0 +1,23 @@
name: Create and Push Git Tag
description: Creates and pushes a git tag in the current repository
inputs:
tag:
description: "The tag name to create"
required: true
token:
description: "Gitea token with repository write access"
required: true
runs:
using: composite
steps:
- name: Create and push tag
shell: sh
env:
TAG: ${{ inputs.tag }}
TOKEN: ${{ inputs.token }}
run: |
git config user.email "gitea-actions@gitea.pixelparasol.com"
git config user.name "Gitea Actions"
git tag "${TAG}"
git push "https://gitea-actions:${TOKEN}@gitea.pixelparasol.com/${{ gitea.repository }}.git" "${TAG}"
+26
View File
@@ -0,0 +1,26 @@
# Helm Diff Deployment
<!-- action-docs-description source="action.yml" -->
## Description
Diff a Helm chart for a deployment in a Kubernetes cluster
<!-- action-docs-description source="action.yml" -->
<!-- action-docs-inputs source="action.yml" -->
## Inputs
| name | description | required | default |
| --- | --- | --- | --- |
| `DEPLOYMENT_NAME` | <p>The Kubernetes Deployment to update</p> | `true` | `""` |
| `DEPLOYMENT_NAMESPACE` | <p>The Kubernetes namespace of the Deployment</p> | `true` | `""` |
| `IMAGE_PATH` | <p>The registry path to the image</p> | `true` | `""` |
| `IMAGE_TAG` | <p>The image tag to deploy</p> | `true` | `""` |
| `CONTAINER_NAME` | <p>The container component to update</p> | `true` | `""` |
| `VALUES_FILE` | <p>The values file to use</p> | `false` | `./helm/values.yaml` |
<!-- action-docs-inputs source="action.yml" -->
<!-- action-docs-runs source="action.yml" -->
## Runs
This action is a `composite` action.
<!-- action-docs-runs source="action.yml" -->
+35
View File
@@ -0,0 +1,35 @@
name: Helm Diff Deployment
description: Diff a Helm chart for a deployment in a Kubernetes cluster
inputs:
DEPLOYMENT_NAME:
description: "The Kubernetes Deployment to update"
required: true
DEPLOYMENT_NAMESPACE:
description: "The Kubernetes namespace of the Deployment"
required: true
IMAGE_PATH:
description: "The registry path to the image"
required: true
IMAGE_TAG:
description: "The image tag to deploy"
required: true
CONTAINER_NAME:
description: "The container component to update"
required: true
VALUES_FILE:
description: "The values file to use"
default: "./helm/values.yaml"
runs:
using: composite
steps:
- name: Install Helm Diff
shell: sh
run: |
helm plugin list | grep -q diff || helm plugin install https://github.com/databus23/helm-diff
- name: Helm Diff
shell: sh
run: |
CMD="helm diff upgrade ${{ inputs.DEPLOYMENT_NAME }} ./helm -n ${{ inputs.DEPLOYMENT_NAMESPACE }} --values ${{ inputs.VALUES_FILE }} --set deploy.${{ inputs.CONTAINER_NAME }}.tag=${{ inputs.IMAGE_TAG }} --set image.repository=${{ inputs.IMAGE_PATH }} --context 5"
echo "Running: $CMD"
eval "$CMD"
@@ -0,0 +1,26 @@
# Helm Upgrade Deployment Image
<!-- action-docs-description source="action.yml" -->
## Description
Set the image for a deployment in a Kubernetes
<!-- action-docs-description source="action.yml" -->
<!-- action-docs-inputs source="action.yml" -->
## Inputs
| name | description | required | default |
| --- | --- | --- | --- |
| `DEPLOYMENT_NAME` | <p>The Kubernetes Deployment to update</p> | `true` | `""` |
| `DEPLOYMENT_NAMESPACE` | <p>The Kubernetes namespace of the Deployment</p> | `true` | `""` |
| `IMAGE_PATH` | <p>The registry path to the image</p> | `true` | `""` |
| `IMAGE_TAG` | <p>The image tag to deploy</p> | `true` | `""` |
| `CONTAINER_NAME` | <p>The container component to update</p> | `true` | `""` |
| `VALUES_FILE` | <p>The values file to use</p> | `false` | `./helm/values.yaml` |
<!-- action-docs-inputs source="action.yml" -->
<!-- action-docs-runs source="action.yml" -->
## Runs
This action is a `composite` action.
<!-- action-docs-runs source="action.yml" -->
@@ -0,0 +1,31 @@
name: Helm Upgrade Deployment Image
description: Set the image for a deployment in a Kubernetes
inputs:
DEPLOYMENT_NAME:
description: "The Kubernetes Deployment to update"
required: true
DEPLOYMENT_NAMESPACE:
description: "The Kubernetes namespace of the Deployment"
required: true
IMAGE_PATH:
description: "The registry path to the image"
required: true
IMAGE_TAG:
description: "The image tag to deploy"
required: true
CONTAINER_NAME:
description: "The container component to update"
required: true
VALUES_FILE:
description: "The values file to use"
default: "./helm/values.yaml"
runs:
using: composite
steps:
- name: Helm Set Image
shell: sh
run: |
CMD="helm upgrade ${{ inputs.DEPLOYMENT_NAME }} ./helm -n ${{ inputs.DEPLOYMENT_NAMESPACE }} --values ${{ inputs.VALUES_FILE }} --set deploy.${{ inputs.CONTAINER_NAME }}.tag=${{ inputs.IMAGE_TAG }} --set image.repository=${{ inputs.IMAGE_PATH }}"
echo "Running: $CMD"
eval "$CMD"
+26
View File
@@ -0,0 +1,26 @@
# Helm Template Deployment
<!-- action-docs-description source="action.yml" -->
## Description
Template a Helm chart for a deployment in a Kubernetes cluster
<!-- action-docs-description source="action.yml" -->
<!-- action-docs-inputs source="action.yml" -->
## Inputs
| name | description | required | default |
| --- | --- | --- | --- |
| `DEPLOYMENT_NAME` | <p>The Kubernetes Deployment to update</p> | `true` | `""` |
| `DEPLOYMENT_NAMESPACE` | <p>The Kubernetes namespace of the Deployment</p> | `true` | `""` |
| `IMAGE_PATH` | <p>The registry path to the image</p> | `true` | `""` |
| `IMAGE_TAG` | <p>The image tag to deploy</p> | `true` | `""` |
| `CONTAINER_NAME` | <p>The container component to update</p> | `true` | `""` |
| `VALUES_FILE` | <p>The values file to use</p> | `false` | `./helm/values.yaml` |
<!-- action-docs-inputs source="action.yml" -->
<!-- action-docs-runs source="action.yml" -->
## Runs
This action is a `composite` action.
<!-- action-docs-runs source="action.yml" -->
+31
View File
@@ -0,0 +1,31 @@
name: Helm Template Deployment
description: Template a Helm chart for a deployment in a Kubernetes cluster
inputs:
DEPLOYMENT_NAME:
description: "The Kubernetes Deployment to update"
required: true
DEPLOYMENT_NAMESPACE:
description: "The Kubernetes namespace of the Deployment"
required: true
IMAGE_PATH:
description: "The registry path to the image"
required: true
IMAGE_TAG:
description: "The image tag to deploy"
required: true
CONTAINER_NAME:
description: "The container component to update"
required: true
VALUES_FILE:
description: "The values file to use"
default: "./helm/values.yaml"
runs:
using: composite
steps:
- name: Helm Template
shell: sh
run: |
CMD="helm template ${{ inputs.DEPLOYMENT_NAME }} ./helm -n ${{ inputs.DEPLOYMENT_NAMESPACE }} --values ${{ inputs.VALUES_FILE }} --set deploy.${{ inputs.CONTAINER_NAME }}.tag=${{ inputs.IMAGE_TAG }} --set image.repository=${{ inputs.IMAGE_PATH }}"
echo "Running: $CMD"
eval "$CMD"
+24
View File
@@ -0,0 +1,24 @@
# Helm Upgrade
<!-- action-docs-description source="action.yml" -->
## Description
Login to an OCI registry, update chart dependencies, and run helm upgrade for the chart in the current directory
<!-- action-docs-description source="action.yml" -->
<!-- action-docs-inputs source="action.yml" -->
## Inputs
| name | description | required | default |
| --- | --- | --- | --- |
| `DEPLOYMENT_NAME` | <p>The Helm release name and target namespace</p> | `true` | `""` |
| `REGISTRY` | <p>OCI registry hostname for helm dependency login</p> | `true` | `""` |
| `REGISTRY_USERNAME` | <p>Username for OCI registry login</p> | `true` | `""` |
| `REGISTRY_TOKEN` | <p>Token for OCI registry login</p> | `true` | `""` |
<!-- action-docs-inputs source="action.yml" -->
<!-- action-docs-runs source="action.yml" -->
## Runs
This action is a `composite` action.
<!-- action-docs-runs source="action.yml" -->
+32
View File
@@ -0,0 +1,32 @@
name: Helm Upgrade
description: Login to an OCI registry, update chart dependencies, and run helm upgrade for the chart in the current directory
inputs:
DEPLOYMENT_NAME:
description: "The Helm release name and target namespace"
required: true
REGISTRY:
description: "OCI registry hostname for helm dependency login"
required: true
REGISTRY_USERNAME:
description: "Username for OCI registry login"
required: true
REGISTRY_TOKEN:
description: "Token for OCI registry login"
required: true
runs:
using: composite
steps:
- name: Helm OCI Login
shell: sh
run: |
echo "${{ inputs.REGISTRY_TOKEN }}" | helm registry login ${{ inputs.REGISTRY }} \
--username ${{ inputs.REGISTRY_USERNAME }} \
--password-stdin
- name: Helm Upgrade
shell: sh
run: |
helm dependency update
echo "Running: helm upgrade ${{ inputs.DEPLOYMENT_NAME }} ./ -n ${{ inputs.DEPLOYMENT_NAME }}"
helm upgrade ${{ inputs.DEPLOYMENT_NAME }} ./ -n ${{ inputs.DEPLOYMENT_NAME }}
@@ -0,0 +1,34 @@
# Fetch Secret from Infisical
<!-- action-docs-description source="action.yml" -->
## Description
Fetches a single secret value from Infisical using a machine identity token
<!-- action-docs-description source="action.yml" -->
<!-- action-docs-inputs source="action.yml" -->
## Inputs
| name | description | required | default |
| --- | --- | --- | --- |
| `INFISICAL_TOKEN` | <p>Machine identity access token</p> | `true` | `""` |
| `SECRET_NAME` | <p>The secret key to fetch</p> | `true` | `""` |
| `INFISICAL_HOST` | <p>Infisical API base URL</p> | `false` | `https://infisical.pixelparasol.com` |
| `WORKSPACE_ID` | <p>Infisical project UUID</p> | `false` | `""` |
| `ENVIRONMENT` | <p>Infisical environment slug</p> | `false` | `prod` |
| `SECRET_PATH` | <p>Folder path within the environment</p> | `false` | `/` |
<!-- action-docs-inputs source="action.yml" -->
<!-- action-docs-outputs source="action.yml" -->
## Outputs
| name | description |
| --- | --- |
| `value` | <p>The fetched secret value</p> |
<!-- action-docs-outputs source="action.yml" -->
<!-- action-docs-runs source="action.yml" -->
## Runs
This action is a `composite` action.
<!-- action-docs-runs source="action.yml" -->
@@ -0,0 +1,68 @@
name: Fetch Secret from Infisical
description: Fetches a single secret value from Infisical using a machine identity token
inputs:
INFISICAL_TOKEN:
description: "Machine identity access token"
required: true
SECRET_NAME:
description: "The secret key to fetch"
required: true
INFISICAL_HOST:
description: "Infisical API base URL"
required: false
default: "https://infisical.pixelparasol.com"
WORKSPACE_ID:
description: "Infisical project UUID"
required: true
ENVIRONMENT:
description: "Infisical environment slug"
required: false
default: "prod"
SECRET_PATH:
description: "Folder path within the environment"
required: false
default: "/"
outputs:
value:
description: "The fetched secret value"
value: ${{ steps.fetch.outputs.value }}
runs:
using: composite
steps:
- name: Fetch secret
id: fetch
shell: sh
run: |
if ! command -v jq >/dev/null 2>&1; then
apk add --no-cache jq 2>/dev/null \
|| apt-get install -y -q --no-install-recommends jq 2>/dev/null \
|| { echo "Error: jq not available and could not be installed" >&2; exit 1; }
fi
HTTP_STATUS=$(curl -s -o /tmp/_infisical_resp.json -w "%{http_code}" \
-H "Authorization: Bearer ${{ inputs.INFISICAL_TOKEN }}" \
"${{ inputs.INFISICAL_HOST }}/api/v3/secrets/raw/${{ inputs.SECRET_NAME }}?workspaceId=${{ inputs.WORKSPACE_ID }}&environment=${{ inputs.ENVIRONMENT }}&secretPath=${{ inputs.SECRET_PATH }}")
if [ "$HTTP_STATUS" != "200" ]; then
echo "Error: Infisical returned HTTP $HTTP_STATUS for secret '${{ inputs.SECRET_NAME }}'" >&2
echo "Response: $(cat /tmp/_infisical_resp.json)" >&2
rm -f /tmp/_infisical_resp.json
exit 1
fi
RESPONSE=$(cat /tmp/_infisical_resp.json)
rm -f /tmp/_infisical_resp.json
VALUE=$(echo "$RESPONSE" | jq -r '.secret.secretValue')
if [ -z "$VALUE" ] || [ "$VALUE" = "null" ]; then
echo "Error: secret '${{ inputs.SECRET_NAME }}' is empty or not found" >&2
exit 1
fi
DELIMITER="INFISICAL_EOF_$$"
echo "value<<${DELIMITER}" >> "$GITHUB_OUTPUT"
echo "$VALUE" >> "$GITHUB_OUTPUT"
echo "${DELIMITER}" >> "$GITHUB_OUTPUT"
echo "Successfully fetched secret '${{ inputs.SECRET_NAME }}'"
@@ -0,0 +1,23 @@
# Update Infra Version
<!-- action-docs-description source="action.yml" -->
## Description
Updates the service tag in the stat-tackler-infra releases/versions.yaml
<!-- action-docs-description source="action.yml" -->
<!-- action-docs-inputs source="action.yml" -->
## Inputs
| name | description | required | default |
| --- | --- | --- | --- |
| `token` | <p>Gitea token with read/write access to the infra repo</p> | `true` | `""` |
| `service` | <p>Service name key in versions.yaml (e.g. stat-tackler-api)</p> | `true` | `""` |
| `tag` | <p>The image tag to set for the service</p> | `true` | `""` |
<!-- action-docs-inputs source="action.yml" -->
<!-- action-docs-runs source="action.yml" -->
## Runs
This action is a `composite` action.
<!-- action-docs-runs source="action.yml" -->
@@ -0,0 +1,42 @@
name: Update Infra Version
description: Updates the service tag in the stat-tackler-infra releases/versions.yaml
inputs:
token:
description: "Gitea token with read/write access to the infra repo"
required: true
service:
description: "Service name key in versions.yaml (e.g. stat-tackler-api)"
required: true
tag:
description: "The image tag to set for the service"
required: true
runs:
using: composite
steps:
- name: Clone infra repo
shell: sh
run: |
git clone https://gitea-actions:${{ inputs.token }}@gitea.pixelparasol.com/stat-tackler/stat-tackler-infra.git infra
- name: Install yq
shell: sh
run: |
wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
chmod +x /usr/local/bin/yq
- name: Update tag in versions.yaml
shell: sh
run: |
cd infra
yq e ".services.${{ inputs.service }}.tag = \"${{ inputs.tag }}\"" -i releases/versions.yaml
- name: Commit and push
shell: sh
run: |
cd infra
git config user.email "gitea-actions@gitea.pixelparasol.com"
git config user.name "Gitea Actions"
git add releases/versions.yaml
git commit -m "chore: update ${{ inputs.service }} to ${{ inputs.tag }}"
git push
@@ -0,0 +1,23 @@
# Configure Kubectl
<!-- action-docs-description source="action.yml" -->
## Description
Configure kubectl for use with Kubernetes
<!-- action-docs-description source="action.yml" -->
<!-- action-docs-inputs source="action.yml" -->
## Inputs
| name | description | required | default |
| --- | --- | --- | --- |
| `K8S_CONFIG` | <p>The RAW Kubernetes config</p> | `true` | `""` |
| `K8S_NAMESPACE` | <p>The K8S namespace</p> | `true` | `""` |
| `K8S_CONTEXT` | <p>The K8S context</p> | `true` | `""` |
<!-- action-docs-inputs source="action.yml" -->
<!-- action-docs-runs source="action.yml" -->
## Runs
This action is a `composite` action.
<!-- action-docs-runs source="action.yml" -->
@@ -0,0 +1,33 @@
name: Configure Kubectl
description: Configure kubectl for use with Kubernetes
inputs:
K8S_CONFIG:
description: "The RAW Kubernetes config"
required: true
K8S_NAMESPACE:
description: "The K8S namespace"
required: true
K8S_CONTEXT:
description: "The K8S context"
required: true
runs:
using: composite
steps:
- name: Configure kubectl
shell: sh
run: |
echo "Remove existing kubeconfig"
rm -f ~/.kube/config
echo "Re-creating .kube directory"
mkdir -p ~/.kube
echo "Set kubeconfig"
echo "${{ inputs.K8S_CONFIG }}" > ~/.kube/config
echo "Set kubeconfig context"
kubectl config set-context ${{ inputs.K8S_CONTEXT }} --cluster=${{ inputs.K8S_CONTEXT }} --namespace=${{ inputs.K8S_NAMESPACE }}
echo "Use kubeconfig context ${{ inputs.K8S_CONTEXT }}"
kubectl config use-context ${{ inputs.K8S_CONTEXT }}
+26
View File
@@ -0,0 +1,26 @@
# Node Build
<!-- action-docs-description source="action.yml" -->
## Description
Install dependencies, build, and upload a build artifact
<!-- action-docs-description source="action.yml" -->
<!-- action-docs-inputs source="action.yml" -->
## Inputs
| name | description | required | default |
| --- | --- | --- | --- |
| `INSTALL_CMD` | <p>Install command</p> | `false` | `npm ci` |
| `BUILD_SCRIPT` | <p>npm script to run for the main build</p> | `false` | `build:stage` |
| `EXTRA_BUILD_SCRIPT` | <p>Optional additional npm script to run after the main build</p> | `false` | `""` |
| `ARTIFACT_NAME` | <p>Name to give the uploaded artifact</p> | `false` | `dist` |
| `ARTIFACT_PATH` | <p>Path to upload as the artifact</p> | `false` | `dist` |
| `COPY_PRISMA_ENGINE` | <p>Copy the Prisma query engine binaries into the build directory</p> | `false` | `false` |
<!-- action-docs-inputs source="action.yml" -->
<!-- action-docs-runs source="action.yml" -->
## Runs
This action is a `composite` action.
<!-- action-docs-runs source="action.yml" -->
+56
View File
@@ -0,0 +1,56 @@
name: Node Build
description: Install dependencies, build, and upload a build artifact
inputs:
INSTALL_CMD:
description: "Install command"
default: "npm ci"
BUILD_SCRIPT:
description: "npm script to run for the main build"
default: "build:stage"
EXTRA_BUILD_SCRIPT:
description: "Optional additional npm script to run after the main build"
default: ""
ARTIFACT_NAME:
description: "Name to give the uploaded artifact"
default: "dist"
ARTIFACT_PATH:
description: "Path to upload as the artifact"
default: "dist"
COPY_PRISMA_ENGINE:
description: "Copy the Prisma query engine binaries into the build directory"
default: "false"
runs:
using: composite
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: actions/setup-node@v5
with:
node-version-file: package.json
- name: Install
shell: sh
run: ${{ inputs.INSTALL_CMD }}
- name: Build
shell: sh
run: |
npm run ${{ inputs.BUILD_SCRIPT }}
if [ -n "${{ inputs.EXTRA_BUILD_SCRIPT }}" ]; then
npm run ${{ inputs.EXTRA_BUILD_SCRIPT }}
fi
- name: Copy Prisma Client Engine
if: inputs.COPY_PRISMA_ENGINE == 'true'
shell: sh
run: |
mkdir -p build/prisma
cp node_modules/.prisma/client/libquery_engine-* build/prisma/
- name: Upload Build Artifact
uses: actions/upload-artifact@v3
with:
name: ${{ inputs.ARTIFACT_NAME }}
path: ${{ inputs.ARTIFACT_PATH }}
+29
View File
@@ -0,0 +1,29 @@
# test/npm
Composite action: install dependencies and run an npm test script.
## Inputs
| Input | Description | Default |
|---|---|---|
| `INSTALL_CMD` | Install command | `npm ci` |
| `TEST_SCRIPT` | npm script to run (must exist in `package.json`) | `test` |
| `WORKING_DIRECTORY` | Directory to run commands in | `.` |
## Usage
```yaml
- uses: stat-tackler/stat-tackler-infra/.gitea/actions/test/npm@main
with:
TEST_SCRIPT: test:unit
```
### Common test scripts by project
| Project | Script | Runner |
|---|---|---|
| `stat-tackler-api` | `test` | Jest |
| `stat-tackler-scorekeeper` | `test:unit` | Vitest |
| `stat-tackler-scorekeeper` | `test:integration` | Vitest |
| `stat-tackler-scorekeeper` | `test:run` | Vitest (unit + integration) |
| `stat-tackler-scorekeeper` | `test:coverage` | Vitest |
+40
View File
@@ -0,0 +1,40 @@
name: NPM Test
description: Install dependencies and run npm tests
inputs:
INSTALL_CMD:
description: "Install command"
default: "npm ci"
TEST_SCRIPT:
description: "npm script to run (must exist in package.json)"
default: "test"
WORKING_DIRECTORY:
description: "Directory to run commands in"
default: "."
runs:
using: composite
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: actions/setup-node@v5
with:
node-version-file: package.json
- name: Cache node_modules
uses: actions/cache@v4
with:
path: ${{ inputs.WORKING_DIRECTORY }}/node_modules
key: node-modules-${{ hashFiles(format('{0}/package-lock.json', inputs.WORKING_DIRECTORY)) }}
restore-keys: node-modules-
- name: Install
shell: sh
working-directory: ${{ inputs.WORKING_DIRECTORY }}
run: ${{ inputs.INSTALL_CMD }}
- name: Test
shell: sh
working-directory: ${{ inputs.WORKING_DIRECTORY }}
run: npm run ${{ inputs.TEST_SCRIPT }}
+23
View File
@@ -0,0 +1,23 @@
# Trivy Scan Image
<!-- action-docs-description source="action.yml" -->
## Description
Scan a container image with Trivy
<!-- action-docs-description source="action.yml" -->
<!-- action-docs-inputs source="action.yml" -->
## Inputs
| name | description | required | default |
| --- | --- | --- | --- |
| `IMAGE_PATH` | <p>The registry path to the image to scan</p> | `true` | `""` |
| `IMAGE_TAG` | <p>The image tag to scan</p> | `true` | `""` |
| `FAIL_HARD` | <p>Boolean: true will fail the build if vulnerabilities are found, false will not</p> | `false` | `false` |
<!-- action-docs-inputs source="action.yml" -->
<!-- action-docs-runs source="action.yml" -->
## Runs
This action is a `composite` action.
<!-- action-docs-runs source="action.yml" -->
@@ -0,0 +1,21 @@
name: Trivy Scan Image
description: Scan a container image with Trivy
inputs:
IMAGE_PATH:
description: "The registry path to the image to scan"
required: true
IMAGE_TAG:
description: "The image tag to scan"
required: true
FAIL_HARD:
description: "Boolean: true will fail the build if vulnerabilities are found, false will not"
required: false
default: 'false'
runs:
using: composite
steps:
- name: Scan Container Registry Image
shell: sh
run: |
trivy image ${{ inputs.IMAGE_PATH }}:${{ inputs.IMAGE_TAG }} --report=all --exit-code=${{ contains(fromJSON('["true"]'), inputs.FAIL_HARD) && '1' || '0' }} --severity CRITICAL,HIGH
@@ -0,0 +1,21 @@
# Trivy Scan K8S Namespace
<!-- action-docs-description source="action.yml" -->
## Description
Scan kubernetes namespace for vulnerabilities
<!-- action-docs-description source="action.yml" -->
<!-- action-docs-inputs source="action.yml" -->
## Inputs
| name | description | required | default |
| --- | --- | --- | --- |
| `NAMESPACE` | <p>The Kubernetes namespace to scan</p> | `true` | `""` |
<!-- action-docs-inputs source="action.yml" -->
<!-- action-docs-runs source="action.yml" -->
## Runs
This action is a `composite` action.
<!-- action-docs-runs source="action.yml" -->
@@ -0,0 +1,14 @@
name: Trivy Scan K8S Namespace
description: Scan kubernetes namespace for vulnerabilities
inputs:
NAMESPACE:
description: "The Kubernetes namespace to scan"
required: true
runs:
using: composite
steps:
- name: Scan Kubernetes Namespace
shell: sh
run: |
trivy k8s --namespace ${{ inputs.NAMESPACE }} --report=all all
+26
View File
@@ -0,0 +1,26 @@
ACTIONS := \
.gitea/actions/docker \
.gitea/actions/node \
.gitea/actions/git/create_tag \
.gitea/actions/helm/diff \
.gitea/actions/helm/set_deployment_image \
.gitea/actions/helm/template \
.gitea/actions/infra/update_version \
.gitea/actions/kubectl/configure \
.gitea/actions/trivy/image_scan \
.gitea/actions/trivy/namespace_scan
.PHONY: help docs
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "Usage: make \033[36m<target>\033[0m [REGION=us-east-2]\n"} \
/^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0,5) } \
/^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-16s\033[0m %s\n", $$1, $$2 }' \
$(MAKEFILE_LIST)
##@ Setup
docs: ## Create litellm-secrets (prompts for master_key)
@for dir in $(ACTIONS); do \
echo "Generating docs for $$dir"; \
(cd $$dir && action-docs --no-banner --update-readme README.md); \
done
+47
View File
@@ -0,0 +1,47 @@
# stat-tackler-infra
Central deployment repository for the stat-tackler platform. All production releases are coordinated from here.
## How to cut a production release
1. Update `releases/versions.yaml` with the semver tag you want deployed for each service.
2. Open a PR to `main` and merge it.
3. Publish a release in Gitea. The deploy workflow fires automatically and rolls out every service in the versions file.
To skip a service in a given release, comment it out in `versions.yaml`.
## Repository layout
```
.gitea/
actions/kubectl/configure/ # Reusable kubectl setup action
workflows/
deploy-prod.yaml # Coordinated production deployment
releases/
versions.yaml # Source of truth for what is deployed to production
```
## Services
| Service | Repo |
|---|---|
| API | `stat-tackler-api` |
| Auth UI | `stat-tackler-auth` |
| Scorekeeper UI | `stat-tackler-scorekeeper` |
| Admin UI | `stat-tackler-admin` |
| Marketing site | `stat-tackler-marketing` |
| Email relay | `stat-tackler-email-relay` |
| MCP server | `stat-tackler-mcp` |
Each service owns its own Helm chart (`./helm/`) and handles its own staging deploys. This repo only manages coordinated production releases.
## Runner requirements
The `helm` runner must have `helm`, `kubectl`, and `yq` available.
## Required secrets
| Secret | Purpose |
|---|---|
| `K8S_TROWBRIDGE_K0S0_CONFIG` | Kubeconfig for the production cluster |
| `REGISTRY_AGENT_TOKEN` | Gitea token with read access to all service repos and the container registry |