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
52 changes: 52 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Bug report
description: Something in the Python bindings (arcadedb or arcadedb-jvm) does not work
labels: ["bug"]
body:
- type: dropdown
id: package
attributes:
label: Package
description: Which distribution you installed. Both import as `arcadedb`.
options:
- arcadedb (native)
- arcadedb-jvm (JVM)
- both
- not sure
validations:
required: true
- type: input
id: version
attributes:
label: Package version
description: "`arcadedb.__version__` is enough."
placeholder: 26.11.1
validations:
required: true
- type: input
id: platform
attributes:
label: OS, architecture and Python version
placeholder: Ubuntu 24.04 x86_64, Python 3.13
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
validations:
required: true
- type: textarea
id: actual
attributes:
label: Actual behavior
description: Include the full traceback or error text.
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce
description: A short Python script that reproduces the problem raises the chance of a quick fix a lot.
render: python
validations:
required: true
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Bug in the ArcadeDB engine (queries, storage, server)
url: https://github.com/ArcadeData/arcadedb/issues/new
about: A problem that also shows in the Java API or in Studio belongs in the engine repository, with a Java or SQL reproduction.
28 changes: 28 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Feature request
description: Something the Python bindings should do
labels: ["enhancement"]
body:
- type: dropdown
id: package
attributes:
label: Package
options:
- arcadedb (native)
- arcadedb-jvm (JVM)
- both
- not sure
validations:
required: true
- type: textarea
id: problem
attributes:
label: What are you trying to do?
description: The task first, then why the current API does not cover it.
validations:
required: true
- type: textarea
id: proposal
attributes:
label: What would you like to see?
validations:
required: false
31 changes: 31 additions & 0 deletions .github/dependency-review-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Configuration for actions/dependency-review-action, read by .github/workflows/dependency-review.yml
# (adapted from ArcadeData/arcadedb). The action's configuration is flat and drops unknown keys
# silently, so every key below is one the action understands. Inline `with:` inputs of the
# workflow would override this file, so the workflow passes only `config-file`.

# Fail the job on a vulnerability of this severity or above.
fail-on-severity: moderate

# Runtime dependencies only, the action's default.
fail-on-scopes:
- runtime

# Licences CLAUDE.md forbids outright (GPL and AGPL families, SSPL). Both the current SPDX ids and
# their deprecated spellings are listed because a dependency may declare either form.
deny-licenses:
- GPL-1.0
- GPL-1.0-only
- GPL-1.0-or-later
- GPL-2.0
- GPL-2.0-only
- GPL-2.0-or-later
- GPL-3.0
- GPL-3.0-only
- GPL-3.0-or-later
- AGPL-1.0
- AGPL-1.0-only
- AGPL-1.0-or-later
- AGPL-3.0
- AGPL-3.0-only
- AGPL-3.0-or-later
- SSPL-1.0
21 changes: 21 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## What does this PR do?

A short description of the change.

## Related issues

`Closes #N` for the issue this fixes (the in-progress label and the release milestone follow from it). Do not use a closing keyword for an issue the change only touches.

## Performance changes

For a change that claims a speed or memory difference: the timed A/B result (`ab.py`, with the noise run), or say why none applies. Leave this out otherwise.

## Docs

Pages under `docs/` and `README.md` that describe what changed, updated in this PR, or why none needs it.

## Checklist

- [ ] A regression test fails on the old code and passes on the new
- [ ] The test suite that the change can affect passes
- [ ] `uvx pre-commit run --files <changed files>` passes
121 changes: 121 additions & 0 deletions .github/scripts/dependabot_merge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#!/usr/bin/env python3
"""Squash-merge open Dependabot pull requests that are safe to merge unattended.

A pull request is merged only when ALL of these hold:

* it is a single-package update whose title reads "... from X to Y" (grouped updates and
anything else Dependabot titles differently stay open for a person);
* the update is a patch or minor one: the major component of X and Y is the same;
* the list of checks is not empty and every check is a success (a skipped or neutral check is
fine; a failed, pending or missing one is not);
* GitHub reports the pull request as mergeable with merge state CLEAN.

The merge is pinned to the head commit that was inspected, so a branch Dependabot rebased in
the meantime fails the merge instead of landing unchecked code. With --dry-run nothing is merged.
Needs the `gh` CLI with GH_TOKEN set (contents: write, pull-requests: write).
"""

import argparse
import json
import re
import subprocess
import sys

TITLE_RE = re.compile(r"\bfrom (\S+) to (\S+)")
VERSION_RE = re.compile(r"^[v>=~^<]*(\d+)((?:\.\d+)*)")
OK_CONCLUSIONS = {"SUCCESS", "SKIPPED", "NEUTRAL"}


def update_kind(title):
"""Return "patch-or-minor", "major" or None when the title cannot be read."""
found = TITLE_RE.search(title)
if not found:
return None
old, new = (VERSION_RE.match(v) for v in found.groups())
if not old or not new:
return None
return "patch-or-minor" if old.group(1) == new.group(1) else "major"


def checks_green(rollup):
"""True when the rollup is not empty and every entry succeeded or was skipped."""
if not rollup:
return False
for check in rollup:
if check.get("__typename") == "StatusContext":
if check.get("state") != "SUCCESS":
return False
elif (
check.get("status") != "COMPLETED"
or check.get("conclusion") not in OK_CONCLUSIONS
):
return False
return True


def gh(*args):
return subprocess.run(
["gh", *args], check=True, capture_output=True, text=True
).stdout


def main():
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument(
"--dry-run", action="store_true", help="report decisions, merge nothing"
)
args = parser.parse_args()

fields = "number,title,headRefOid,mergeable,mergeStateStatus,statusCheckRollup"
prs = json.loads(
gh(
"pr",
"list",
"--state",
"open",
"--base",
"main",
"--author",
"app/dependabot",
"--limit",
"100",
"--json",
fields,
)
)
failed = False
for pr in sorted(prs, key=lambda p: p["number"]):
number, title = pr["number"], pr["title"]
kind = update_kind(title)
if kind != "patch-or-minor":
print(
f"#{number}: left open ({kind or 'not a single-package update'}): {title}"
)
elif not checks_green(pr["statusCheckRollup"]):
print(f"#{number}: left open (checks not all green yet): {title}")
elif pr["mergeable"] != "MERGEABLE" or pr["mergeStateStatus"] != "CLEAN":
print(
f"#{number}: left open ({pr['mergeable']}, {pr['mergeStateStatus']}): {title}"
)
elif args.dry_run:
print(f"#{number}: would merge: {title}")
else:
try:
gh(
"pr",
"merge",
str(number),
"--squash",
"--delete-branch",
"--match-head-commit",
pr["headRefOid"],
)
print(f"#{number}: merged: {title}")
except subprocess.CalledProcessError as err:
failed = True
print(f"#{number}: merge failed: {err.stderr.strip()}", file=sys.stderr)
return 1 if failed else 0


if __name__ == "__main__":
sys.exit(main())
87 changes: 87 additions & 0 deletions .github/workflows/assign-milestone.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Puts a merged pull request, and the issues it closes, into the open release milestone.
#
# ArcadeData/arcadedb does this by hand: every merged pull request and closed issue sits in the
# milestone of the release it ships in (one milestone per version, with a due date). Here a
# workflow does the step that gets forgotten. The target is the open milestone with the earliest
# due date (ties: the lower number), so closing a release milestone and opening the next one is
# the only manual step. Anything that already has a milestone is left alone, and so are pull
# requests opened by Dependabot (their updates are not release content). An issue closed as "not
# planned" ships in no release and gets none.
#
# `pull_request_target` so a fork's pull request still gets a token that can edit it. Nothing from
# the pull request head is checked out or run: the job calls the GitHub API only.
name: Assign release milestone

on:
pull_request_target:
types: [closed]
issues:
types: [closed]

permissions:
issues: write
pull-requests: read
contents: read

jobs:
assign:
name: Assign the open release milestone
if: >-
(github.event_name == 'pull_request_target' &&
github.event.pull_request.merged == true &&
github.event.pull_request.user.login != 'dependabot[bot]') ||
(github.event_name == 'issues' && github.event.issue.state_reason == 'completed')
runs-on: ubuntu-latest
steps:
- name: Set the milestone
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const { owner, repo } = context.repo;

const open = await github.paginate(github.rest.issues.listMilestones, {
owner, repo, state: 'open', per_page: 100,
});
if (open.length === 0) {
core.info('No open milestone, nothing to assign.');
return;
}
const byDue = (a, b) =>
(a.due_on ? Date.parse(a.due_on) : Infinity) - (b.due_on ? Date.parse(b.due_on) : Infinity) ||
a.number - b.number;
const milestone = open.sort(byDue)[0];

const assign = async (number, current) => {
if (current) {
core.info(`#${number}: already in "${current.title}".`);
return;
}
await github.rest.issues.update({ owner, repo, issue_number: number, milestone: milestone.number });
core.info(`#${number}: milestone "${milestone.title}".`);
};

if (context.eventName === 'issues') {
await assign(context.payload.issue.number, context.payload.issue.milestone);
return;
}

const pr = context.payload.pull_request;
await assign(pr.number, pr.milestone);

// The issues the pull request closes, as GitHub itself resolves them.
const result = await github.graphql(
`query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
closingIssuesReferences(first: 50) {
nodes { number repository { nameWithOwner } milestone { title } }
}
}
}
}`,
{ owner, repo, number: pr.number },
);
for (const issue of result.repository.pullRequest.closingIssuesReferences.nodes) {
if (issue.repository.nameWithOwner !== `${owner}/${repo}`) continue;
await assign(issue.number, issue.milestone);
}
Loading
Loading