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
20 changes: 20 additions & 0 deletions .agents/skills/code-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,23 @@ A resource that requires deterministic release — database connection or cursor
### call-in-loop

A database query, HTTP request, or other heavy I/O call is issued once per item inside a loop where the project already provides a batched or bulk equivalent. Only flag when the batched helper exists, cited by file and line, and the loop bound is data-dependent rather than a small fixed number.

### possible-sql-injection

SQL is built by interpolating or concatenating untrusted values into the query string with request parameters, user input, or other external data without using parameterized placeholders and bound arguments. Only flag when the interpolated value is not a constant or a trusted identifier whitelist, and when a parameterized API for that database driver or ORM exists in the project or standard library.

### sensitive-data-logged

A log call, print, or error report includes a secret or personally identifying value: password, API key, token, session cookie, authorization header, private key, etc. Only flag when the logged expression is clearly that sensitive value or a structure that embeds it (for example logging an entire headers dict that contains Authorization). Do not flag redacted, hashed, or truncated forms, nor logging of non-secret identifiers such as user ids or request ids.

### hardcoded-secret

A secret is committed as a string literal (API key, password, token, private key, connection string with credentials). Only flag when the value looks like a real credential, not a placeholder/test fixture.

### ssrf

An HTTP client fetches a URL taken from user/external input with no allowlist or host restriction.

### command-injection

subprocess/os.system/os.popen runs a shell with concatenated or formatted user/external input (shell=True, or a single string command). Only flag when the injected value is not a constant/whitelist.
17 changes: 12 additions & 5 deletions .github/workflows/build-wheel.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
name: Build wheel
name: "[python] Build wheel"

on:
- push

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
build-wheel:
runs-on: ubuntu-latest
Expand All @@ -15,13 +22,13 @@ jobs:
node-version: "20"

- name: Set up uv
uses: astral-sh/setup-uv@v6
uses: astral-sh/setup-uv@v7

- name: Build wheel
run: make wheel

- name: Upload wheel artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: wheel
path: dist/*.whl
Expand All @@ -31,10 +38,10 @@ jobs:
needs: build-wheel
steps:
- name: Set up uv
uses: astral-sh/setup-uv@v6
uses: astral-sh/setup-uv@v7

- name: Download wheel artifact
uses: actions/download-artifact@v5
uses: actions/download-artifact@v8
with:
name: wheel
path: dist
Expand Down
21 changes: 14 additions & 7 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
name: CI
name: "[python] CI"

on:
push:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
tests:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -38,10 +45,10 @@ jobs:
run: make gen

- name: Set up Docker runtime
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4

- name: Build image
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
context: .
load: true
Expand All @@ -55,7 +62,7 @@ jobs:
run: docker save local/app:latest -o image.tar

- name: Upload image
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: docker-image
path: image.tar
Expand All @@ -70,15 +77,15 @@ jobs:
packages: write
steps:
- name: Download image
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: docker-image

- name: Set up Docker runtime
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
Expand Down
16 changes: 10 additions & 6 deletions .github/workflows/release-wheel.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Release wheels
name: "[python] Release wheels"

on:
workflow_dispatch:
Expand All @@ -8,6 +8,9 @@ on:
required: true
type: string

permissions:
contents: read

jobs:
create-tag:
runs-on: ubuntu-latest
Expand All @@ -27,7 +30,7 @@ jobs:
runs-on: ubuntu-latest
needs: create-tag
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0
ref: "v${{ inputs.version }}"
Expand All @@ -38,16 +41,17 @@ jobs:
node-version: "20"

- name: Set up uv
uses: astral-sh/setup-uv@v6
uses: astral-sh/setup-uv@v7

- name: Build wheel
run: make wheel

- name: Upload wheel artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: wheel
path: dist/*.whl
retention-days: 1

publish-release:
runs-on: ubuntu-latest
Expand All @@ -56,7 +60,7 @@ jobs:
contents: write
steps:
- name: Download wheel artifacts
uses: actions/download-artifact@v5
uses: actions/download-artifact@v8
with:
name: wheel
path: dist
Expand All @@ -68,7 +72,7 @@ jobs:
echo "release_tag=v$version" >> "$GITHUB_OUTPUT"

- name: Create GitHub release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ steps.normalize.outputs.release_tag }}
name: Release ${{ steps.normalize.outputs.release_tag }}
Expand Down
2 changes: 1 addition & 1 deletion .template.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated - DO NOT EDIT
# Parameters of the project as generated from template
_commit: 6b97e37
_commit: 2555e1a
_src_path: gh:kraysent/python-template
package_name: uploader
project_name: uploader
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@rjsf/validator-ajv8": "^6.4.1",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-markdown": "^10.1.0",
"react-router-dom": "^7.13.1"
},
"devDependencies": {
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export type TaskInfo = {
id: string;
title: string;
description: string;
additional_description?: string | null;
group: string;
rerunnable: boolean;
};
Expand All @@ -15,6 +16,7 @@ export async function fetchTasks(): Promise<TaskInfo[]> {
export type TaskSchemaResponse = {
title: string;
description: string;
additional_description?: string | null;
schema: Record<string, unknown>;
};

Expand Down
59 changes: 59 additions & 0 deletions frontend/src/components/Markdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Box from "@mui/material/Box";
import { useTheme } from "@mui/material/styles";
import ReactMarkdown from "react-markdown";

type MarkdownProps = {
children: string;
};

export function Markdown({ children }: MarkdownProps) {
const theme = useTheme();

return (
<Box
sx={{
typography: "body2",
color: "text.secondary",
"& h1, & h2, & h3, & h4": {
color: "text.primary",
fontWeight: 600,
mt: 0,
mb: 1,
},
"& h2": { fontSize: "1.1rem" },
"& h3": { fontSize: "1rem" },
"& p": { mt: 0, mb: 1 },
"& ul, & ol": { mt: 0, mb: 1, pl: 2.5 },
"& li": { mb: 0.5 },
"& code": {
fontFamily:
"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
fontSize: "0.85em",
px: 0.5,
py: 0.15,
borderRadius: 0.5,
backgroundColor:
theme.palette.mode === "dark"
? "rgba(255, 255, 255, 0.08)"
: "rgba(0, 0, 0, 0.06)",
},
"& pre": {
p: 1.5,
borderRadius: 1,
overflow: "auto",
backgroundColor:
theme.palette.mode === "dark"
? "rgba(255, 255, 255, 0.06)"
: "rgba(0, 0, 0, 0.04)",
"& code": {
p: 0,
backgroundColor: "transparent",
},
},
"& a": { color: "primary.main" },
}}
>
<ReactMarkdown>{children}</ReactMarkdown>
</Box>
);
}
18 changes: 17 additions & 1 deletion frontend/src/components/TaskPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import { TaskPage } from "./TaskPage";
const fakeTaskSchema = {
title: "Fake Test Task",
description: "A task used in tests.",
additional_description: '## Expression syntax\n\nUse `col("name")`.',
schema: {
type: "object",
properties: {
name: {
type: "string",
title: "Name",
description: "Your name",
description: "Your name. Target type: `string`.",
},
count: {
type: "integer",
Expand Down Expand Up @@ -63,6 +64,21 @@ describe("TaskPage", () => {
expect(screen.getByText("A task used in tests.")).toBeInTheDocument();
expect(screen.getByLabelText(/name/i)).toBeInTheDocument();
expect(screen.getByLabelText(/count/i)).toBeInTheDocument();
expect(screen.getByText("string")).toBeInTheDocument();
expect(screen.getByText("string").tagName).toBe("CODE");
});

it("shows additional description markdown behind info button", async () => {
renderTaskPage("fake-task");

await waitFor(() => {
expect(screen.getByText("Fake Test Task")).toBeInTheDocument();
});

expect(screen.queryByText("Expression syntax")).not.toBeInTheDocument();
screen.getByLabelText("Additional information").click();
expect(await screen.findByText("Expression syntax")).toBeInTheDocument();
expect(screen.getByText('col("name")')).toBeInTheDocument();
});

it("prefills form when formData is passed via location state", async () => {
Expand Down
Loading
Loading