Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ca9b508
Set up linters
FluxCapacitor2 Jan 19, 2026
30dc33f
Various CI workflow fixes
FluxCapacitor2 Jan 19, 2026
6d9caa9
Fix type generation during lint script
FluxCapacitor2 Jan 19, 2026
3d89606
Run `npm audit fix`
FluxCapacitor2 Jan 19, 2026
ac9249a
Remove duplicate workflow runs on pull requests
FluxCapacitor2 Jan 19, 2026
331e7fd
Run `prettier -w` and `eslint --fix`
FluxCapacitor2 Jan 19, 2026
9d0632b
Shellcheck fixes
FluxCapacitor2 Jan 19, 2026
55f9a2e
Fix hadolint errors by pinning package versions
FluxCapacitor2 Jan 19, 2026
9078a40
Fix frontend build
FluxCapacitor2 Jan 19, 2026
a1929ce
Fix some Go linter issues
FluxCapacitor2 Jan 19, 2026
7a465b2
Don't run security scan twice on PRs
FluxCapacitor2 Jan 19, 2026
e523d77
Backend ESLint fixes
FluxCapacitor2 Jan 19, 2026
58c0b05
Merge branch 'main' into linting
FluxCapacitor2 Jan 21, 2026
3fa6c54
[ci skip] Add workflow and job names
FluxCapacitor2 Jan 21, 2026
62c24dd
Merge branch 'main' into linting
FluxCapacitor2 Jan 22, 2026
949aa20
Linter fixes
FluxCapacitor2 Jan 22, 2026
442c8e0
Enable `strict` in backend and filebrowser TypeScript configs
FluxCapacitor2 Jan 22, 2026
46ee7f0
Fix lots of frontend linter issues
FluxCapacitor2 Jan 22, 2026
daaef4e
Disable strictNullChecks for now
FluxCapacitor2 Jan 22, 2026
82bf610
Fix lots of TypeScript compiler errors
FluxCapacitor2 Jan 22, 2026
04437f4
Fix API handler TypeScript compiler issues
FluxCapacitor2 Jan 22, 2026
cda551f
Merge branch 'main' into linting
FluxCapacitor2 Jan 23, 2026
2793b40
Remove strikethrough on network policies description in README
FluxCapacitor2 Jan 23, 2026
adea382
Fix all remaining backend ESLint errors
FluxCapacitor2 Jan 24, 2026
e78368c
Fix remaining file browser and frontend ESLint errors
FluxCapacitor2 Jan 24, 2026
12b94ec
Fix shellcheck and golangci-lint errors
FluxCapacitor2 Jan 24, 2026
5f02a1e
Fix Prisma version mismatch
FluxCapacitor2 Jan 24, 2026
84b66e2
Fix useless-cat shellcheck error
FluxCapacitor2 Jan 24, 2026
b0b15e4
Fix file browser responses hanging
FluxCapacitor2 Jan 24, 2026
43c9812
Override lodash to fix CVE in Prisma dependency
FluxCapacitor2 Jan 24, 2026
80140c7
Merge branch 'main' into linting
FluxCapacitor2 Jan 25, 2026
3e31532
Match ecmaVersion with tsconfig lib
skkra0 Jan 26, 2026
730b635
set -uo pipefail
skkra0 Jan 26, 2026
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
44 changes: 44 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Lint
on:
push:
branches: [main]
pull_request:

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v6

- uses: actions/setup-node@v6
with:
node-version: 24
cache: "npm"
cache-dependency-path: |
frontend/package.json
backend/package.json
filebrowser/package.json

- name: Install NPM packages
run: |
cd frontend
npm ci
cd ../backend
npm ci
cd ../filebrowser
npm ci

- name: Install Tools
uses: jdx/mise-action@v3
with:
version: 2026.1.4
install: true
mise_toml: |
[tools]
hadolint = "2.14.0"
golangci-lint = "2.8.0"

- name: Lint
run: ./.github/workflows/lint/lint.sh
74 changes: 74 additions & 0 deletions .github/workflows/lint/lint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/bin/bash
set -uo pipefail

CURRENT_DIR=$(dirname "$0")
PROJECT_ROOT="$PWD/$CURRENT_DIR/../../../"

cd "$PROJECT_ROOT" || exit 1

ERROR=0

check_error() {
if [ $? -ne 0 ]; then
echo "Command exited with an error"
ERROR=1
else
echo "Success"
fi
}

# Shellcheck: shell scripts
printf "\n======================================\nRunning Shellcheck\n======================================\n"
find . -name "*.sh" -and -not -wholename '**node_modules**' -and -not -wholename '**.husky**' -exec shellcheck {} +
check_error

# Hadolint: Dockerfiles
printf "\n======================================\nRunning Hadolint\n======================================\n"
find . -name "*Dockerfile" -exec hadolint {} +
check_error

# golangci-lint: Go files
printf "\n======================================\nRunning golangci-lint (log-shipper)\n======================================\n"
cd "$PROJECT_ROOT/log-shipper" || exit 1
golangci-lint run ./...
check_error

printf "\n======================================\nRunning golangci-lint (regclient-napi)\n======================================\n"
cd "$PROJECT_ROOT/backend/regclient-napi" || exit 1
npx node-gyp configure

NAPI_HEADER_DIR=$(node -p 'require("node-addon-api").include' | cut -d\" -f 2 -)
NODE_HEADER_DIR="$(grep nodedir build/config.gypi | cut -d\" -f 4 -)/include/node"
cd src || exit 1

go build -o ../gobuild/main.a -buildmode=c-archive main.go
export CGO_CXXFLAGS="-I../gobuild -I$NODE_HEADER_DIR -I$NAPI_HEADER_DIR"

golangci-lint run ./...

check_error

# ESLint: TypeScript files
cd "$PROJECT_ROOT/openapi" || exit 1
npm ci
npm run generate

cd "$PROJECT_ROOT/backend" || exit 1
DATABASE_URL=placeholder npx prisma generate

printf "\n======================================\nRunning eslint (frontend)\n======================================\n"
cd "$PROJECT_ROOT/frontend" || exit 1
npm run lint
check_error

printf "\n======================================\nRunning eslint (backend)\n======================================\n"
cd "$PROJECT_ROOT/backend" || exit 1
npm run lint
check_error

printf "\n======================================\nRunning eslint (filebrowser)\n======================================\n"
cd "$PROJECT_ROOT/filebrowser" || exit 1
npm run lint
check_error

exit $ERROR
5 changes: 4 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
name: Release

on:
workflow_dispatch:
inputs:
Expand All @@ -16,6 +18,7 @@ on:

jobs:
release:
name: Release
runs-on: ubuntu-latest
permissions:
contents: write
Expand Down Expand Up @@ -46,7 +49,7 @@ jobs:
docker buildx inspect --bootstrap

- name: Release
run: ./.github/workflows/scripts/release-${{ inputs.environment }}.sh "${{ inputs.version }}"
run: ./.github/workflows/release/release-${{ inputs.environment }}.sh "${{ inputs.version }}"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ if [ -z "$1" ]; then
fi

if [ ! -v CI ]; then
read -p "Script isn't being called from CI; are you sure you want to create a new GitHub release and push to the production registry namespace? (y/n): " response
read -pr "Script isn't being called from CI; are you sure you want to create a new GitHub release and push to the production registry namespace? (y/n): " response

if [ "$response" != "y" ]; then
exit 0
fi
fi

if [ "$BRANCH" != "main" ]; then
read -p "Current branch isn't main; are you sure you want to create a new GitHub release and push to the production registry namespace? (y/n): " response
read -pr "Current branch isn't main; are you sure you want to create a new GitHub release and push to the production registry namespace? (y/n): " response

if [ "$response" != "y" ]; then
exit 0
Expand All @@ -32,4 +32,4 @@ export HELM_ARTIFACT_TAG="oci://registry.anvil.rcac.purdue.edu/anvilops/chart"
export GENERATE_GITHUB_RELEASE="1"

CURRENT_DIR=$(dirname "$0")
$CURRENT_DIR/release.sh "$1"
"$CURRENT_DIR"/release.sh "$1"
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ COMMIT="$(git rev-parse HEAD)"
VERSION="0.0.0-staging.$(date +%s)-${COMMIT:0:10}"

CURRENT_DIR=$(dirname "$0")
$CURRENT_DIR/release.sh "$VERSION"
"$CURRENT_DIR"/release.sh "$VERSION"
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,15 @@ build_and_push() {
-t "$IMAGE_TAG" \
--cache-from="type=registry,ref=$CACHE_TAG" \
--cache-to="type=registry,ref=$CACHE_TAG,mode=max" \
--build-arg BUILD_DATE=$(date -uIseconds) \
--build-arg BUILD_DATE="$(date -uIseconds)" \
"$BUILD_CONTEXT"

IMAGE_ID=$(cat "$IIDFILE") # looks like "sha256:32975dcafd44d8c6f921d2276e2a39f42f268e8c9584d6c4d4c88f5a073b7b1d"
rm "$IIDFILE"

IMAGE_REF="$IMAGE_TAG@$IMAGE_ID"
echo "Built image: $IMAGE_REF"
echo "- \`$IMAGE_REF\`" >> $NOTES_FILE
echo "- \`$IMAGE_REF\`" >> "$NOTES_FILE"

set_value "$VALUES_FILE" "$CHART_KEY" "$IMAGE_REF"
}
Expand Down Expand Up @@ -94,10 +94,10 @@ copy_image() {

IMAGE_REF="$DEST@$IMAGE_ID"
set_value "$VALUES_FILE" "$KEY" "$IMAGE_REF"
echo "- \`$IMAGE_REF\`" >> $NOTES_FILE
echo "- \`$IMAGE_REF\`" >> "$NOTES_FILE"
}

RAILPACK_VERSION=$(cat "$PROJECT_ROOT/builders/railpack/Dockerfile" | grep "RAILPACK_VERSION=" | cut -d= -f 2)
RAILPACK_VERSION=$(grep "RAILPACK_VERSION=" "$PROJECT_ROOT/builders/railpack/Dockerfile" | cut -d= -f 2)
RAILPACK_RELEASE_SHA=$(gh api "repos/railwayapp/railpack/commits/v$RAILPACK_VERSION" --jq '.sha')

get_railpack_image_tag() {
Expand All @@ -122,7 +122,7 @@ get_railpack_image_tag() {
}

build_images() {
echo "### Container Images" >> $NOTES_FILE
echo "### Container Images" >> "$NOTES_FILE"

build_and_push "Dockerfile" "" ".anvilops.image" "$REGISTRY_BASE/anvilops"
build_and_push "backend/prisma/Dockerfile" "backend" ".anvilops.dbMigrateImage" "$REGISTRY_BASE/migrate-db"
Expand Down Expand Up @@ -151,12 +151,12 @@ publish_chart() {
CHART_PACKAGE_DIR=$(mktemp -d)

helm package --destination "$CHART_PACKAGE_DIR" "$CHART_DIR"
CHART_PACKAGE_FILE="$CHART_PACKAGE_DIR/$(ls $CHART_PACKAGE_DIR)"
CHART_PACKAGE_FILE="$CHART_PACKAGE_DIR/$(ls "$CHART_PACKAGE_DIR")"

helm push "$CHART_PACKAGE_FILE" "$HELM_ARTIFACT_TAG"
rm -rf "$CHART_PACKAGE_DIR"

cat << EOF >> $NOTES_FILE
cat << EOF >> "$NOTES_FILE"

### Install with Helm
\`\`\`sh
Expand Down
26 changes: 26 additions & 0 deletions .github/workflows/security-scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Security Scan

on:
push:
branches: [main]
pull_request:

jobs:
security-scan:
name: Run Trivy
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v6

- name: Install Tools
uses: jdx/mise-action@v3
with:
version: 2026.1.4
install: true
mise_toml: |
[tools]
trivy = "0.68.2"

- name: Run Trivy
run: trivy repository --exit-code 1 .
9 changes: 8 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,11 @@ charts/**/*.yaml
!charts/**/values.yaml
templates/extensions/**/*.yaml
!templates/extensions/**/values.yaml
# ^ Prettier doesn't know how to interpret Go templates so it thinks we're writing invalid YAML
# ^ Prettier doesn't know how to interpret Go templates so it thinks we're writing invalid YAML

frontend/dist
frontend/src/generated
backend/src/generated
docs/dist
docs/.astro
backend/regclient-napi/dist
4 changes: 3 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
{}
{
"plugins": ["prettier-plugin-tailwindcss"]
}
4 changes: 3 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"github.vscode-github-actions",
"unifiedjs.vscode-mdx",
"Prisma.prisma",
"redhat.vscode-yaml"
"redhat.vscode-yaml",
"dbaeumer.vscode-eslint",
"timonwong.shellcheck"
]
}
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"editor.tabSize": 2,
// Automatically sort imports when saving a file
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
"source.organizeImports": "explicit",
"source.fixAll.eslint": "explicit"
}
}
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,13 @@ RUN npm run prisma:generate
# BACKEND: compile regclient Node-API bindings
FROM base AS compile_regclient_bindings

ARG TARGET_ARCH
# https://docs.docker.com/reference/dockerfile/#example-cache-apt-packages
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && \
apt-get install -y --no-install-recommends build-essential golang ca-certificates python3
apt-get install -y --no-install-recommends build-essential=12.12 golang=2:1.24~2 ca-certificates=20250419 python3=3.13.5-1

WORKDIR /app
COPY backend/package*.json .
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ AnvilOps is not designed for public use. Access to AnvilOps should only be share
#### Applications

- End users' applications are deployed in separate namespaces.
- ~~By default, [network policies](https://kubernetes.io/docs/concepts/services-networking/network-policies/) are created that prevent applications from communicating with other pods in the cluster. AnvilOps apps must be allowed to cross-communicate by placing them in the same App Group.~~ (WIP)
- When enabled, [network policies](https://kubernetes.io/docs/concepts/services-networking/network-policies/) are created that prevent other pods in the cluster from connecting to AnvilOps applications. AnvilOps apps can be allowed to cross-communicate by placing them in the same App Group.
- For stronger isolation, consider using something like [Kata Containers](https://katacontainers.io/), which places containers into separate microVMs.

#### Secrets
Expand Down
3 changes: 1 addition & 2 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,14 @@ APP_DOMAIN=http://local
FIELD_ENCRYPTION_KEY=
SESSION_SECRET=

DELETE_REPO_HOST=
DELETE_REPO_USERNAME=
DELETE_REPO_PASSWORD=

CURRENT_NAMESPACE=anvilops-dev

CHART_PROJECT_NAME=anvilops-chart
REGISTRY_API_URL=https://registry.anvil.rcac.purdue.edu/api/v2.0
REGISTRY_HOSTNAME=registry.anvil.rcac.purdue.edu
REGISTRY_PROTOCOL=https

STORAGE_CLASS_NAME=standard
STORAGE_ACCESS_MODES=ReadWriteOnce
Expand Down
3 changes: 2 additions & 1 deletion backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ In order to correctly refresh the kubeconfig, set the key `use-cluster-name` in

AnvilOps expects environment variables to be set to credentials of an account with repository delete permissions from your Harbor project:

- `DELETE_REPO_HOST`: the hostname of the registry
- `REGISTRY_HOSTNAME`: the hostname of the registry
- `REGISTRY_PROTOCOL`: the protocol registry (`http` or `https`)
- `DELETE_REPO_USERNAME`: the account's username (if you're using a robot account, which we recommend, make sure to include the `robot$<project name>+` prefix)
- `DELETE_REPO_PASSWORD`: the account's password (if you're using a robot account, this is referred to as the account's secret in the Harbor UI)

Expand Down
38 changes: 38 additions & 0 deletions backend/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// @ts-check

import js from "@eslint/js";
import { defineConfig } from "eslint/config";
import globals from "globals";
import tseslint from "typescript-eslint";

export default defineConfig({
files: ["**/*.{ts,tsx}"],
ignores: ["src/generated/**"],
languageOptions: {
ecmaVersion: 2024,
globals: globals.node,
parserOptions: {
projectService: true,
},
},
linterOptions: {
reportUnusedInlineConfigs: "error",
},
rules: {
"array-callback-return": "error",
"preserve-caught-error": "warn",
"no-await-in-loop": "warn",
"no-control-regex": "error",
"no-unassigned-vars": "error",
"no-useless-assignment": "warn",
"no-use-before-define": ["error", { functions: false }],
"no-throw-literal": "error",
"no-unused-expressions": "warn",
"no-warning-comments": "warn",
"default-case": "warn",
"guard-for-in": "warn",
"no-console": "error",
"require-await": "warn",
},
extends: [js.configs.recommended, ...tseslint.configs.recommendedTypeChecked],
});
Loading
Loading