Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a93edf16a1 | ||
|
|
69de6b7bde | ||
|
|
29ec2fe7ab | ||
|
|
99e54a2e52 | ||
|
|
4c65582ada | ||
|
|
3cd251636e | ||
|
|
fb3679fcf8 | ||
|
|
916dc0d645 | ||
|
|
4c39d6f8ee | ||
|
|
19a38d9df6 | ||
|
|
8cff9d9141 | ||
|
|
e83d603047 | ||
|
|
cb1c1d985c | ||
|
|
71bab40e53 | ||
|
|
667fdde139 | ||
|
|
d42d283def | ||
|
|
bad162bdce | ||
|
|
5812860910 | ||
|
|
1a313c8e16 | ||
|
|
785502ea9e | ||
|
|
c20ae68d07 | ||
|
|
ca9195daca | ||
|
|
aa93ced550 | ||
|
|
3a6264a608 | ||
|
|
8af0f02a5a | ||
|
|
3c0211285a | ||
|
|
4aac193439 | ||
|
|
c195a11759 | ||
|
|
31acb9e844 | ||
|
|
ff31170fd6 | ||
|
|
4634c135e7 | ||
|
|
c1e6a31120 | ||
|
|
62cc66f9e5 | ||
|
|
a40397fabc | ||
|
|
acbc4c3074 | ||
|
|
fa777c6a79 | ||
|
|
c58a55220f | ||
|
|
68c1f878a2 | ||
|
|
25f72f2ac4 | ||
|
|
f4cdfc01aa | ||
|
|
13aeb5001b | ||
|
|
74a93aaa7c | ||
|
|
9c08afa728 | ||
|
|
32937d1ca7 | ||
|
|
cee841830c | ||
|
|
ca004e5206 | ||
|
|
3689c955f9 | ||
|
|
fbf93e679f | ||
|
|
3c4cfaa23c | ||
|
|
ffde3ed1dd | ||
|
|
ffd6c56f44 |
No files matched your search
@@ -1,8 +0,0 @@
|
||||
[alias]
|
||||
xtask = "run --package xtask --"
|
||||
|
||||
# Windows release binaries link the C runtime statically, so they run without the VC++
|
||||
# redistributable (packaging/windows/package.ps1 builds with --target, which keeps the flag
|
||||
# off build scripts and proc-macros).
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
rustflags = ["-C", "target-feature=+crt-static"]
|
||||
@@ -1,13 +0,0 @@
|
||||
# Every change to FieldCraft needs the maintainer's review before it merges.
|
||||
* @jyviko
|
||||
|
||||
# Release and supply-chain paths, named explicitly so they stay owner-reviewed if the rule above
|
||||
# is ever relaxed: workflows and secrets use, packaging and signing, the updater, dependencies,
|
||||
# licences.
|
||||
/.github/ @jyviko
|
||||
/packaging/ @jyviko
|
||||
/crates/update/ @jyviko
|
||||
/Cargo.toml @jyviko
|
||||
/Cargo.lock @jyviko
|
||||
/ATTRIBUTION.md @jyviko
|
||||
/deny.toml @jyviko
|
||||
@@ -1,47 +0,0 @@
|
||||
name: Bug report
|
||||
description: Something FieldCraft does wrong, a wrong result, or a crash
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: textarea
|
||||
id: observed
|
||||
attributes:
|
||||
label: Observed failure
|
||||
description: Exactly what happened. Quote error messages and wrong numbers as they appear.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: Numbered steps, or a script (`fieldcraft-cli run script.json`, control-channel JSON lines), with the sample, corpus part or attached `.fieldcraft` / `.solidcraft` file that shows it.
|
||||
placeholder: |
|
||||
1. fieldcraft --sample cantilever
|
||||
2. Mesh ▸ Tet ▸ OK
|
||||
3. ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: evidence
|
||||
attributes:
|
||||
label: Evidence
|
||||
description: |
|
||||
Pictures first: for anything you can see (the view, a mesh, a panel, a plot), drag a screenshot or a short screen recording in here, and mark the problem on it. For a mesh, a section through it (`fieldcraft-cli snapshot … --section`) shows what the outside hides. Then log output and numbers. For a crash, attach the crash report (`fieldcraft-cli crashes`, then `crashes show <id>`).
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected behaviour
|
||||
description: What should happen instead, and why — a closed form, a published benchmark, the documentation, or how the rest of the app behaves. Never output from a commercial CAE product.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: investigation
|
||||
attributes:
|
||||
label: Investigation
|
||||
description: Anything you found about the cause — code you suspect, what you ruled out. Leave empty if you have not looked.
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Version and platform
|
||||
placeholder: "v0.6.0 (or commit), macOS 15 arm64"
|
||||
validations:
|
||||
required: true
|
||||
@@ -1 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
@@ -1,33 +0,0 @@
|
||||
name: Feature request
|
||||
description: A capability FieldCraft should have
|
||||
labels: [enhancement]
|
||||
body:
|
||||
- type: textarea
|
||||
id: current
|
||||
attributes:
|
||||
label: Current behaviour
|
||||
description: What you can and cannot do today, and what happens when you try.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: need
|
||||
attributes:
|
||||
label: What you need and why
|
||||
description: The analysis or workflow you are trying to do.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected behaviour
|
||||
description: What FieldCraft should do, as precisely as you can.
|
||||
- type: textarea
|
||||
id: pictures
|
||||
attributes:
|
||||
label: Pictures
|
||||
description: A screenshot of where it would go, a sketch, or a picture of the result you are after. Drag images in here.
|
||||
- type: textarea
|
||||
id: reference
|
||||
attributes:
|
||||
label: References
|
||||
description: Papers, textbooks, public specifications or published benchmarks that define the behaviour. Do not paste text, files or results from commercial CAE products.
|
||||
@@ -1,59 +0,0 @@
|
||||
# Weekly dependency updates against develop, the trunk, grouped so each ecosystem opens one pull
|
||||
# request a week. Security updates are switched on in the repository settings and open their own
|
||||
# pull requests as advisories appear. CI runs the full gate on Dependabot's pull requests
|
||||
# (.github/workflows/ci.yml).
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: cargo
|
||||
directory: /
|
||||
target-branch: develop
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: "05:00"
|
||||
timezone: Etc/UTC
|
||||
open-pull-requests-limit: 5
|
||||
commit-message:
|
||||
prefix: deps
|
||||
groups:
|
||||
# Compatible updates (Cargo.lock only) in one pull request; a breaking release of a
|
||||
# dependency comes on its own, since it usually needs code changes.
|
||||
cargo-compatible:
|
||||
update-types: [minor, patch]
|
||||
ignore:
|
||||
# Pinned to the SolidCraft commit FieldCraft builds against, together with the truck and
|
||||
# egui-wgpu patches from that commit ([patch.crates-io] in Cargo.toml); moved by hand.
|
||||
- dependency-name: "solidcraft-*"
|
||||
- dependency-name: "truck-*"
|
||||
- dependency-name: matext4cgmath
|
||||
- dependency-name: egui-wgpu
|
||||
# The egui line follows SolidCraft's (its egui-wgpu fixes are patched in): patch releases
|
||||
# come with the group, a new line (0.36 -> 0.37) moves by hand with SolidCraft.
|
||||
- dependency-name: egui
|
||||
update-types: ["version-update:semver-major", "version-update:semver-minor"]
|
||||
- dependency-name: eframe
|
||||
update-types: ["version-update:semver-major", "version-update:semver-minor"]
|
||||
# The shared Craft components follow VectorCraft's rev.
|
||||
- dependency-name: craft-ui
|
||||
# The exact predicates' BigInt must be num-rational's (0.4 uses num-bigint 0.4): both move
|
||||
# together, by hand.
|
||||
- dependency-name: num-bigint
|
||||
update-types: ["version-update:semver-major", "version-update:semver-minor"]
|
||||
- dependency-name: num-rational
|
||||
update-types: ["version-update:semver-major", "version-update:semver-minor"]
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
target-branch: develop
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: "05:00"
|
||||
timezone: Etc/UTC
|
||||
open-pull-requests-limit: 3
|
||||
commit-message:
|
||||
prefix: ci
|
||||
groups:
|
||||
# Actions are pinned by commit SHA with a version comment; Dependabot updates both.
|
||||
actions:
|
||||
patterns: ["*"]
|
||||
@@ -1,15 +0,0 @@
|
||||
Closes #
|
||||
|
||||
## What and why
|
||||
|
||||
## Before and after
|
||||
<!-- For any visible change (the view, meshes, panels, results, icons): a picture before and after. Drag them in here. -->
|
||||
|
||||
## How it was verified
|
||||
<!-- Tests added or changed; oracle cases (tests/oracle) for solver and meshing results, with their cited references. -->
|
||||
|
||||
## Checklist
|
||||
- [ ] The issue was assigned to me before I started (see CONTRIBUTING.md)
|
||||
- [ ] `cargo fmt --all`, `cargo clippy -p <crates I touched> --all-targets -- -D warnings` and their tests pass
|
||||
- [ ] Before/after pictures for any visible change
|
||||
- [ ] No code, text or data from GPL-family projects or commercial CAE products
|
||||
@@ -1,30 +0,0 @@
|
||||
FieldCraft is open-source CAE pre-processing in Rust: it turns geometry into analysis-ready
|
||||
finite element models, writes Abaqus, Nastran and LS-DYNA decks, and solves linear static and
|
||||
modal problems with its own solver. AI agents can drive it over MCP (`fieldcraft-cli mcp`).
|
||||
[README](https://github.com/tangency-labs/fieldcraft#readme) ·
|
||||
[roadmap](https://github.com/tangency-labs/fieldcraft/blob/main/ROADMAP.md) ·
|
||||
[issues](https://github.com/tangency-labs/fieldcraft/issues)
|
||||
|
||||
**Which file?**
|
||||
- **macOS 11+** (Apple silicon and Intel): open `fieldcraft-{version}-macos-universal.dmg` and
|
||||
drag `FieldCraft.app` onto Applications. The command-line tool is included at
|
||||
`/Applications/FieldCraft.app/Contents/MacOS/fieldcraft-cli` and also available separately in
|
||||
`fieldcraft-cli-{version}-macos-universal.zip`.
|
||||
- **Linux** (x86_64, glibc 2.35+: Ubuntu 22.04, Debian 12, Fedora 36 or newer):
|
||||
`fieldcraft-{version}-linux-x86_64.AppImage` (`chmod +x`, then run it), or unpack
|
||||
`fieldcraft-{version}-linux-x86_64.tar.gz` and run `bin/fieldcraft` (`bin/fieldcraft-cli` is the
|
||||
command-line tool).
|
||||
- **Windows 10+** (x64): unzip `fieldcraft-{version}-windows-x64.zip` and run `fieldcraft.exe`
|
||||
(`fieldcraft-cli.exe` is the command-line tool).
|
||||
- `SHA256SUMS.txt`: `sha256sum -c SHA256SUMS.txt --ignore-missing` (macOS:
|
||||
`shasum -a 256 -c SHA256SUMS.txt --ignore-missing`).
|
||||
|
||||
To start with a sample model, run the app with `--sample cantilever` (macOS:
|
||||
`/Applications/FieldCraft.app/Contents/MacOS/FieldCraft --sample cantilever`).
|
||||
|
||||
**Updates.** {update_signing}
|
||||
|
||||
**Signing.** {macos_signing} The Linux and Windows builds are not signed; on Windows, SmartScreen
|
||||
may say "Windows protected your PC": choose **More info**, then **Run anyway**.
|
||||
|
||||
Every package carries `LICENSE-MIT`, `LICENSE-APACHE`, `NOTICE` and `THIRD-PARTY-LICENSES.txt`.
|
||||
@@ -1,83 +0,0 @@
|
||||
# Linux on other architectures, weekly and on demand: CI and the release build x86_64 Linux,
|
||||
# macOS and Windows only.
|
||||
# - arm64: a native runner builds the shipped binaries (release profile) and runs the CLI and
|
||||
# engine tests.
|
||||
# - riscv64: cross-built with Ubuntu's GNU toolchain and linked; the CLI then runs under QEMU.
|
||||
# The GUI needs no target system libraries at link time (Wayland, X11 and GL are opened at run
|
||||
# time), so only the cross C toolchain and libc are installed.
|
||||
# The RISC-V job is adapted from SolidCraft's CI (see ATTRIBUTION.md).
|
||||
name: Linux arm64 and riscv64
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '17 5 * * 1'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: arch-check-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
arm64:
|
||||
name: Linux arm64 (native)
|
||||
runs-on: ubuntu-24.04-arm
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: arm64
|
||||
save-if: ${{ github.ref == 'refs/heads/develop' }}
|
||||
- name: Build fieldcraft and fieldcraft-cli (release)
|
||||
run: cargo build --locked --release -p fieldcraft -p fieldcraft-cli
|
||||
- name: Run the binaries
|
||||
run: |
|
||||
file target/release/fieldcraft target/release/fieldcraft-cli
|
||||
target/release/fieldcraft --version
|
||||
target/release/fieldcraft-cli --version
|
||||
target/release/fieldcraft-cli commands >/dev/null
|
||||
- name: CLI and engine tests
|
||||
run: cargo test --locked --profile ci -p fieldcraft-cli -p fieldcraft-engine
|
||||
|
||||
riscv64:
|
||||
name: Linux riscv64 (cross)
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
TARGET: riscv64gc-unknown-linux-gnu
|
||||
CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER: riscv64-linux-gnu-gcc
|
||||
CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_RUNNER: qemu-riscv64 -L /usr/riscv64-linux-gnu
|
||||
CC_riscv64gc_unknown_linux_gnu: riscv64-linux-gnu-gcc
|
||||
CXX_riscv64gc_unknown_linux_gnu: riscv64-linux-gnu-g++
|
||||
AR_riscv64gc_unknown_linux_gnu: riscv64-linux-gnu-ar
|
||||
PKG_CONFIG_ALLOW_CROSS: 1
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: riscv64gc-unknown-linux-gnu
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: riscv64
|
||||
save-if: ${{ github.ref == 'refs/heads/develop' }}
|
||||
- name: Install the cross toolchain and QEMU
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends gcc-riscv64-linux-gnu g++-riscv64-linux-gnu \
|
||||
libc6-dev-riscv64-cross qemu-user
|
||||
- name: Build fieldcraft and fieldcraft-cli
|
||||
run: cargo build --locked --profile ci --target "$TARGET" -p fieldcraft -p fieldcraft-cli
|
||||
- name: Run the CLI under QEMU
|
||||
run: |
|
||||
bin="target/$TARGET/ci"
|
||||
file "$bin/fieldcraft" "$bin/fieldcraft-cli"
|
||||
qemu-riscv64 -L /usr/riscv64-linux-gnu "$bin/fieldcraft-cli" --version
|
||||
qemu-riscv64 -L /usr/riscv64-linux-gnu "$bin/fieldcraft-cli" commands >/dev/null
|
||||
qemu-riscv64 -L /usr/riscv64-linux-gnu "$bin/fieldcraft" --version
|
||||
@@ -1,244 +0,0 @@
|
||||
name: CI
|
||||
|
||||
# CI runs once per merge on `develop`, the trunk (AGENTS.md, "Workflow for every change"): the core
|
||||
# team's pull requests are checked locally on the crates they touch and merged; a red trunk is
|
||||
# fixed forward. `main` holds released code only (docs/releasing.md).
|
||||
# Pull requests from forks (outside contributors) run the full gate, once the maintainer approves
|
||||
# the run, so the review sees it; so do Dependabot's (.github/dependabot.yml), which nobody tests
|
||||
# locally. The heavy job stays off pull requests.
|
||||
#
|
||||
# What runs when (docs/releasing.md, "What CI runs when"): the `plan` job picks a base, the last
|
||||
# `develop` commit whose CI run passed (a pull request: its merge base; nightly and manual runs:
|
||||
# none), and `cargo xtask affected` maps the change since it to crates. `cargo xtask ci --affected`
|
||||
# then lints and tests those crates and their dependents only; the heavy and oracle jobs run when
|
||||
# the change reaches the solvers, the oracle or everything (Cargo.toml/Cargo.lock/xtask/workflows).
|
||||
# Nightly and manual runs, and every release (release.yml), run all of it.
|
||||
on:
|
||||
pull_request:
|
||||
# A draft starts no run; marking it ready for review does.
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
paths:
|
||||
- '**'
|
||||
- '!**/*.md'
|
||||
- '!docs/**'
|
||||
- 'docs/features.md'
|
||||
- 'docs/oracle.md'
|
||||
push:
|
||||
branches: [develop]
|
||||
# A merge that only touches prose starts no run. The generated pages are checked by the jobs
|
||||
# (features.md by `cargo xtask ci`, oracle.md by the oracle job), so they still count.
|
||||
# (Later patterns override earlier ones.)
|
||||
paths:
|
||||
- '**'
|
||||
- '!**/*.md'
|
||||
- '!docs/**'
|
||||
- 'docs/features.md'
|
||||
- 'docs/oracle.md'
|
||||
schedule:
|
||||
# Nightly, on the default branch (develop): the whole gate, the heavy tests and the oracle.
|
||||
- cron: '23 3 * * *'
|
||||
# Weekly: the oracle on macOS too, so results that differ by platform show up in CI (the
|
||||
# fixtures are often blessed on a Mac; #115).
|
||||
- cron: '41 4 * * 0'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# A newer merge cancels the run still going for the one before it, so a burst of merges is tested
|
||||
# once, at the tip; nothing is missed, since the tip's run tests everything since the last green
|
||||
# run. Nightly and manual runs are grouped apart and never cancelled by a merge.
|
||||
concurrency:
|
||||
group: ci-${{ github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
plan:
|
||||
name: Plan (affected crates)
|
||||
if: github.event.schedule != '41 4 * * 0' && (github.event_name != 'pull_request' || (!github.event.pull_request.draft && (github.event.pull_request.head.repo.full_name != github.repository || github.event.pull_request.user.login == 'dependabot[bot]')))
|
||||
runs-on: ${{ github.event_name != 'pull_request' && vars.SELF_HOSTED_RUNNER || 'ubuntu-24.04' }}
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
contents: read
|
||||
# To find the last green run on develop.
|
||||
actions: read
|
||||
outputs:
|
||||
base: ${{ steps.base.outputs.base }}
|
||||
everything: ${{ steps.affected.outputs.everything }}
|
||||
solver: ${{ steps.affected.outputs.solver }}
|
||||
wasm: ${{ steps.affected.outputs.wasm }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: plan
|
||||
save-if: ${{ github.ref == 'refs/heads/develop' }}
|
||||
- name: Pick the base
|
||||
id: base
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
EVENT: ${{ github.event_name }}
|
||||
PR_BASE: ${{ github.base_ref }}
|
||||
run: |
|
||||
base=""
|
||||
case "$EVENT" in
|
||||
push)
|
||||
# The newest develop commit whose push run passed and that this commit contains.
|
||||
# Runs that were cancelled or failed are skipped, so their changes are tested here.
|
||||
for sha in $(gh api "repos/$GITHUB_REPOSITORY/actions/workflows/ci.yml/runs?branch=develop&event=push&status=success&per_page=100" --jq '.workflow_runs[].head_sha'); do
|
||||
if [ "$sha" != "$GITHUB_SHA" ] && git merge-base --is-ancestor "$sha" HEAD; then
|
||||
base="$sha"
|
||||
break
|
||||
fi
|
||||
done
|
||||
;;
|
||||
pull_request)
|
||||
base="$(git merge-base "origin/$PR_BASE" HEAD || true)"
|
||||
;;
|
||||
esac
|
||||
# Nightly and manual runs, or no green run to start from: everything.
|
||||
echo "base=$base" >>"$GITHUB_OUTPUT"
|
||||
echo "### Base: \`${base:-none (everything)}\`" >>"$GITHUB_STEP_SUMMARY"
|
||||
- name: cargo xtask affected
|
||||
id: affected
|
||||
env:
|
||||
BASE: ${{ steps.base.outputs.base }}
|
||||
run: |
|
||||
if [ -n "$BASE" ]; then
|
||||
cargo xtask affected --base "$BASE" --github-output | tee -a "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
printf 'everything=true\nsolver=true\nwasm=true\n' >>"$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
bots:
|
||||
name: Claim and triage bot tests
|
||||
if: github.event.schedule != '41 4 * * 0' && (github.event_name != 'pull_request' || (!github.event.pull_request.draft && (github.event.pull_request.head.repo.full_name != github.repository || github.event.pull_request.user.login == 'dependabot[bot]')))
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
sparse-checkout: scripts
|
||||
# scripts/claim and scripts/triage run in .github/workflows/claim.yml and triage.yml through
|
||||
# actions/github-script; their tests use only node:test against a fake client.
|
||||
- run: node --test scripts/claim/*.test.js scripts/triage/*.test.js
|
||||
|
||||
ci:
|
||||
name: cargo xtask ci
|
||||
needs: plan
|
||||
runs-on: ${{ github.event_name != 'pull_request' && vars.SELF_HOSTED_RUNNER || 'ubuntu-24.04' }}
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt, clippy
|
||||
targets: wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
# Shared keys: release.yml's verify jobs restore these caches (they save only on main).
|
||||
shared-key: ci
|
||||
save-if: ${{ github.ref == 'refs/heads/develop' }}
|
||||
- name: Software GPU (lavapipe), so headless snapshots run the GPU pipeline
|
||||
run: sudo apt-get update -q && sudo apt-get install -y -q --no-install-recommends mesa-vulkan-drivers libvulkan1
|
||||
- name: cargo xtask ci (the affected crates, or everything)
|
||||
env:
|
||||
BASE: ${{ needs.plan.outputs.base }}
|
||||
# The drawing tests run on lavapipe and fail, instead of skipping, without it.
|
||||
FIELDCRAFT_GPU: software
|
||||
FIELDCRAFT_REQUIRE_GPU: "1"
|
||||
run: |
|
||||
if [ -n "$BASE" ]; then cargo xtask ci --affected "$BASE"; else cargo xtask ci; fi
|
||||
|
||||
heavy:
|
||||
# The heavy verification and corpus tests (#[ignore]d) and the doc tests, when the change
|
||||
# reaches them.
|
||||
name: cargo xtask test --heavy
|
||||
needs: plan
|
||||
if: github.event_name != 'pull_request' && (needs.plan.outputs.solver == 'true' || needs.plan.outputs.everything == 'true')
|
||||
runs-on: ${{ github.event_name != 'pull_request' && vars.SELF_HOSTED_RUNNER || 'ubuntu-24.04' }}
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
shared-key: heavy
|
||||
save-if: ${{ github.ref == 'refs/heads/develop' }}
|
||||
- run: cargo xtask test --heavy
|
||||
|
||||
oracle:
|
||||
# Verification and validation (docs/verification.md): every case against its reference and
|
||||
# its fixture, and docs/oracle.md must be the table the cases produce. Cases marked `slow`
|
||||
# run here too while the whole set stays within a few minutes; `--quick` skips them. It runs
|
||||
# when the change reaches the solvers or the oracle's package.
|
||||
name: cargo xtask oracle
|
||||
needs: plan
|
||||
if: needs.plan.outputs.solver == 'true' || needs.plan.outputs.everything == 'true'
|
||||
runs-on: ${{ github.event_name != 'pull_request' && vars.SELF_HOSTED_RUNNER || 'ubuntu-24.04' }}
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
shared-key: oracle
|
||||
save-if: ${{ github.ref == 'refs/heads/develop' }}
|
||||
- run: cargo xtask oracle
|
||||
- name: docs/oracle.md is current
|
||||
run: git diff --exit-code docs/oracle.md
|
||||
|
||||
oracle-macos:
|
||||
# The same oracle on macOS, weekly and on demand: the solvers must agree across platforms
|
||||
# to the fixture bands, and docs/oracle.md must read the same.
|
||||
name: cargo xtask oracle (macOS)
|
||||
if: github.event.schedule == '41 4 * * 0' || github.event_name == 'workflow_dispatch'
|
||||
runs-on: macos-14
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
shared-key: oracle-macos
|
||||
save-if: ${{ github.ref == 'refs/heads/develop' }}
|
||||
- run: cargo xtask oracle
|
||||
- name: docs/oracle.md is current
|
||||
run: git diff --exit-code docs/oracle.md
|
||||
|
||||
windows:
|
||||
# The desktop app's tests on Windows, nightly and on demand: the Windows-only code
|
||||
# (apps/fieldcraft/src/native_crash.rs, the exception filter) builds and a child process that
|
||||
# faults on purpose leaves its native crash report (#299). Releases build Windows too.
|
||||
name: Windows app tests
|
||||
if: github.event.schedule == '23 3 * * *' || github.event_name == 'workflow_dispatch'
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
shared-key: windows
|
||||
save-if: ${{ github.ref == 'refs/heads/develop' }}
|
||||
- run: cargo test --locked --profile ci -p fieldcraft
|
||||
|
||||
advisories:
|
||||
name: Dependency licences, advisories and sources
|
||||
if: github.event.schedule != '41 4 * * 0' && (github.event_name != 'pull_request' || (!github.event.pull_request.draft && (github.event.pull_request.head.repo.full_name != github.repository || github.event.pull_request.user.login == 'dependabot[bot]')))
|
||||
runs-on: ${{ github.event_name != 'pull_request' && vars.SELF_HOSTED_RUNNER || 'ubuntu-24.04' }}
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Install cargo-deny
|
||||
run: cargo install cargo-deny --version 0.20.2 --locked
|
||||
- run: cargo deny --locked check --warn unmaintained advisories bans licenses sources
|
||||
@@ -1,53 +0,0 @@
|
||||
# `/claim` and `/unclaim` on issues (CONTRIBUTING.md, "Before you start: claim an issue"), and a
|
||||
# daily sweep that frees claims with no visible progress for 14 days. A pull request that
|
||||
# references an issue (`Closes #N`, `<summary> (#N)`) claims it too. The logic is in
|
||||
# scripts/claim/claim.js (tested in ci.yml).
|
||||
name: Claim
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
# pull_request_target so PRs from forks can label and assign. It runs the base branch's
|
||||
# scripts/claim and only reads the PR's title, body and author; the PR's code is never checked out.
|
||||
pull_request_target:
|
||||
types: [opened, reopened]
|
||||
schedule:
|
||||
- cron: '17 5 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read # to check out scripts/claim
|
||||
issues: write
|
||||
pull-requests: read
|
||||
|
||||
# One run at a time per issue, so two simultaneous /claim comments can't both win.
|
||||
concurrency:
|
||||
group: claim-${{ github.event.issue.number || github.event.pull_request.number || 'sweep' }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
claim:
|
||||
name: Claim bot
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
if: >-
|
||||
github.event_name != 'issue_comment' ||
|
||||
(!github.event.issue.pull_request && github.event.comment.user.type != 'Bot' &&
|
||||
(startsWith(github.event.comment.body, '/claim') || startsWith(github.event.comment.body, '/unclaim')))
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
sparse-checkout: scripts/claim
|
||||
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const claim = require('./scripts/claim/claim.js');
|
||||
if (context.eventName === 'issue_comment') {
|
||||
await claim.handleComment({ github, context });
|
||||
} else if (context.eventName === 'pull_request_target') {
|
||||
const claimed = await claim.handlePull({ github, context });
|
||||
core.info(`Claimed: ${claimed.join(', ') || 'none'}`);
|
||||
} else {
|
||||
const released = await claim.sweep({ github, context });
|
||||
core.info(`Released: ${released.join(', ') || 'none'}`);
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
# Quick static checks for the release pipeline (seconds, no Rust build): workflow syntax, the
|
||||
# packaging shell and PowerShell scripts, the macOS property lists and the Linux desktop entry.
|
||||
# These files are otherwise exercised only by a full release run (release.yml).
|
||||
# Adapted from SolidCraft's packaging-lint.yml, itself adapted from PhotoCraft's (see ATTRIBUTION.md).
|
||||
name: Packaging lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [develop]
|
||||
paths: [".github/workflows/**", "packaging/**"]
|
||||
pull_request:
|
||||
# A draft starts no run; marking it ready for review does.
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
paths: [".github/workflows/**", "packaging/**"]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: packaging-lint-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Packaging lint
|
||||
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
ACTIONLINT_VERSION: 1.7.12
|
||||
ACTIONLINT_SHA256: 8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8
|
||||
PSSCRIPTANALYZER_VERSION: 1.24.0
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Install tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends shellcheck desktop-file-utils libxml2-utils
|
||||
archive="$RUNNER_TEMP/actionlint.tar.gz"
|
||||
curl -fsSL -o "$archive" \
|
||||
"https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
|
||||
echo "$ACTIONLINT_SHA256 $archive" | sha256sum -c -
|
||||
tar -xzf "$archive" -C "$RUNNER_TEMP" actionlint
|
||||
- name: actionlint
|
||||
# Also runs shellcheck on every `run:` script.
|
||||
run: '"$RUNNER_TEMP/actionlint" -color'
|
||||
- name: shellcheck
|
||||
# packaging/.shellcheckrc lets the scripts' `source=../env.sh` directives resolve.
|
||||
run: shellcheck packaging/*.sh packaging/*/*.sh packaging/linux/AppRun
|
||||
- name: PowerShell
|
||||
shell: pwsh
|
||||
run: |
|
||||
$failed = $false
|
||||
foreach ($f in Get-ChildItem packaging/windows/*.ps1) {
|
||||
$errors = $null
|
||||
[System.Management.Automation.Language.Parser]::ParseFile($f.FullName, [ref]$null, [ref]$errors) | Out-Null
|
||||
if ($errors) {
|
||||
$errors | ForEach-Object { Write-Output "::error file=$($f.FullName),line=$($_.Extent.StartLineNumber)::$($_.Message)" }
|
||||
$failed = $true
|
||||
} else {
|
||||
Write-Output "parsed $($f.Name)"
|
||||
}
|
||||
}
|
||||
if ($failed) { exit 1 }
|
||||
if (-not (Get-Module -ListAvailable PSScriptAnalyzer | Where-Object Version -eq $env:PSSCRIPTANALYZER_VERSION)) {
|
||||
Install-Module PSScriptAnalyzer -RequiredVersion $env:PSSCRIPTANALYZER_VERSION -Scope CurrentUser -Force
|
||||
}
|
||||
Import-Module PSScriptAnalyzer -RequiredVersion $env:PSSCRIPTANALYZER_VERSION
|
||||
$issues = Invoke-ScriptAnalyzer -Path packaging/windows -Recurse -Severity Error, Warning
|
||||
$issues | ForEach-Object { Write-Output "::error file=$($_.ScriptPath),line=$($_.Line)::$($_.RuleName): $($_.Message)" }
|
||||
if ($issues) { exit 1 }
|
||||
- name: Property lists and desktop entry
|
||||
run: |
|
||||
app_id="$(sed -n 's/^APP_ID=//p' packaging/env.sh)"
|
||||
test -n "$app_id"
|
||||
xmllint --noout packaging/macos/*.plist*
|
||||
desktop="packaging/linux/$app_id.desktop"
|
||||
desktop-file-validate "$desktop"
|
||||
# The window's X11 class / Wayland app_id is packaging's APP_ID (apps/fieldcraft/src/main.rs).
|
||||
grep -qx "StartupWMClass=$app_id" "$desktop"
|
||||
grep -qF "const APP_ID: &str = \"$app_id\";" apps/fieldcraft/src/main.rs
|
||||
@@ -1,92 +0,0 @@
|
||||
# The web app (apps/fieldcraft-web, docs/web.md) on GitHub Pages: https://tangency-labs.github.io/fieldcraft/
|
||||
# After SolidCraft's pages.yml (MIT OR Apache-2.0; see ATTRIBUTION.md).
|
||||
#
|
||||
# A push to `main` (a release; main holds released code only) builds the app with Trunk (release,
|
||||
# wasm-opt) and deploys it. A merge to `develop` that touches code builds it without deploying, so a
|
||||
# broken web build shows up before the release. A manual run builds the branch it runs on and
|
||||
# deploys only from `main`. Needs Settings › Pages › Source = GitHub Actions, and `main` allowed
|
||||
# by the github-pages environment's deployment branches.
|
||||
name: Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
# As ci.yml: a merge that only touches prose builds nothing.
|
||||
paths:
|
||||
- '**'
|
||||
- '!**/*.md'
|
||||
- '!docs/**'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# One deployment at a time; a newer push waits its turn instead of cancelling a deploy part way.
|
||||
concurrency:
|
||||
group: pages-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
TRUNK_VERSION: v0.21.14
|
||||
TRUNK_SHA256: f2b4680cd239693a646a2795e4633c625328d7b2a044fbe749fa3a2fe9e7036b
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# POSTHOG_KEY lives in the `release` environment (main only, docs/releasing.md). The deploy job
|
||||
# already uses github-pages and a job has one environment, so the build job reads the key: on
|
||||
# main it runs in `release`, elsewhere in no environment and with an empty key.
|
||||
environment: ${{ github.ref == 'refs/heads/main' && 'release' || '' }}
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: web
|
||||
save-if: ${{ github.ref == 'refs/heads/develop' }}
|
||||
- name: Install Trunk
|
||||
run: |
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
curl -sSfL -o trunk.tar.gz "https://github.com/trunk-rs/trunk/releases/download/${TRUNK_VERSION}/trunk-x86_64-unknown-linux-gnu.tar.gz"
|
||||
echo "${TRUNK_SHA256} trunk.tar.gz" | sha256sum -c -
|
||||
tar -xzf trunk.tar.gz -C "$HOME/.local/bin"
|
||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
- name: Build the web app
|
||||
working-directory: apps/fieldcraft-web
|
||||
env:
|
||||
# Usage analytics only in the deployed build (main), and only with the user's consent
|
||||
# (docs/privacy.md); develop builds get an empty key, which sends nothing.
|
||||
FIELDCRAFT_POSTHOG_KEY: ${{ github.ref == 'refs/heads/main' && secrets.POSTHOG_KEY || '' }}
|
||||
run: trunk build --release --public-url /fieldcraft/
|
||||
- name: Bundle size
|
||||
run: |
|
||||
{
|
||||
echo "### Web app bundle"
|
||||
echo '| file | bytes | gzip |'
|
||||
echo '|---|---:|---:|'
|
||||
for f in dist/web/*.wasm dist/web/*.js; do
|
||||
echo "| $(basename "$f") | $(stat -c %s "$f") | $(gzip -9 -c "$f" | wc -c) |"
|
||||
done
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
- if: github.ref == 'refs/heads/main'
|
||||
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
|
||||
with:
|
||||
path: dist/web
|
||||
|
||||
deploy:
|
||||
if: github.ref == 'refs/heads/main'
|
||||
needs: build
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
pages: write
|
||||
id-token: write
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- id: deployment
|
||||
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
|
||||
@@ -1,424 +0,0 @@
|
||||
# Release builds (docs/releasing.md), after SolidCraft's and VectorCraft's release.yml
|
||||
# (MIT OR Apache-2.0; see ATTRIBUTION.md).
|
||||
#
|
||||
# A push to `main` (fast-forwarded from a verified `develop` commit; main holds released code only)
|
||||
# builds every package and creates or updates the
|
||||
# DRAFT GitHub Release "FieldCraft v<version>", with SHA256SUMS.txt (signed with the release key
|
||||
# into SHA256SUMS.txt.minisig when the FIELDCRAFT_SIGNING_KEY secret exists) and generated notes. The
|
||||
# version is [workspace.package] version in Cargo.toml; a suffix (0.2.0-rc.1) makes a
|
||||
# pre-release. Every package, and the release itself, carries THIRD-PARTY-LICENSES.txt, generated
|
||||
# here by `cargo xtask licences` (it is not committed). A person verifies the draft and publishes
|
||||
# it; this workflow never publishes, and pushes to develop or tags never start it.
|
||||
#
|
||||
# macOS universal (arm64 + x86_64) .dmg with FieldCraft.app, the CLI as a .zip packaging/macos
|
||||
# Linux x86_64 (glibc 2.35+) .tar.gz, .AppImage, .deb and .rpm packaging/linux
|
||||
# Windows x64 .zip with fieldcraft.exe and fieldcraft-cli.exe packaging/windows
|
||||
#
|
||||
# macOS: signed with a Developer ID and notarized when the APPLE_* secrets exist, ad-hoc
|
||||
# otherwise (the release notes say which). Linux and Windows builds are not signed.
|
||||
#
|
||||
# The verify jobs run everything CI can: the full `cargo xtask ci`, `cargo xtask test --heavy` and
|
||||
# `cargo xtask oracle` on Linux, and the oracle on macOS. Develop's CI tests only what each change
|
||||
# affects (ci.yml), so this is where a release proves all of it. The packaging jobs build in
|
||||
# parallel with them and only upload workflow artifacts; the `release` job, the only one that
|
||||
# signs SHA256SUMS.txt or touches a GitHub Release, needs every verify and packaging job, so a
|
||||
# failing verify means no draft and no signature.
|
||||
#
|
||||
# Dry run: `gh workflow run release.yml --ref <branch>` verifies and builds everything and uploads the
|
||||
# `release-assets` artifact without creating a release. `-f draft=true` on `main` retries
|
||||
# the draft.
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
draft:
|
||||
description: "Create/update the draft release (main only; off: build artifacts only)"
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
# Never cancel a run mid-notarization or mid-upload; a newer push waits its turn.
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
# The same CARGO_* environment as ci.yml: rust-cache hashes it into the cache key, so a
|
||||
# difference would stop the verify jobs restoring develop's caches. (rust-cache already sets
|
||||
# CARGO_INCREMENTAL=0 for every job.)
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
version:
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
version: ${{ steps.v.outputs.version }}
|
||||
prerelease: ${{ steps.v.outputs.prerelease }}
|
||||
draft: ${{ steps.v.outputs.draft }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- id: v
|
||||
name: Resolve the version
|
||||
env:
|
||||
REQUEST_DRAFT: ${{ github.event_name == 'push' || inputs.draft }}
|
||||
run: |
|
||||
. packaging/env.sh
|
||||
if ! printf '%s' "$VERSION" | grep -Eq '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$'; then
|
||||
echo "::error::Cargo.toml must contain a version like 1.2.3 or 1.2.3-rc.1, not '$VERSION'"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$REQUEST_DRAFT" = true ] && [ "$GITHUB_REF" != refs/heads/main ]; then
|
||||
echo "::error::Draft releases are built from main only"
|
||||
exit 1
|
||||
fi
|
||||
case "$VERSION" in *-*) pre=true ;; *) pre=false ;; esac
|
||||
{ echo "version=$VERSION"; echo "prerelease=$pre"; echo "draft=$REQUEST_DRAFT"; } >>"$GITHUB_OUTPUT"
|
||||
echo "### FieldCraft v$VERSION (\`${GITHUB_SHA::9}\`)" >>"$GITHUB_STEP_SUMMARY"
|
||||
|
||||
# The full gate, the heavy tests and the oracle on Linux and macOS: the `release` job needs them,
|
||||
# so a release never drafts or signs a commit that fails any of them. Each restores the cache of
|
||||
# its ci.yml counterpart on develop (same shared-key), so it starts warm.
|
||||
verify-ci:
|
||||
name: Verify (cargo xtask ci)
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt, clippy
|
||||
targets: wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
shared-key: ci
|
||||
# Release runs are rare: only release-branch runs keep a cache, so dry runs don't evict CI's.
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- run: cargo xtask ci
|
||||
|
||||
verify-heavy:
|
||||
name: Verify (cargo xtask test --heavy)
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
shared-key: heavy
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- run: cargo xtask test --heavy
|
||||
|
||||
verify-oracle:
|
||||
name: Verify (cargo xtask oracle, ${{ matrix.os }})
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-24.04
|
||||
cache: oracle
|
||||
- os: macos-14
|
||||
cache: oracle-macos
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
shared-key: ${{ matrix.cache }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- run: cargo xtask oracle
|
||||
- name: docs/oracle.md is current
|
||||
run: git diff --exit-code docs/oracle.md
|
||||
|
||||
macos:
|
||||
name: macOS universal
|
||||
needs: version
|
||||
# Signing, notarization and analytics secrets live in the `release` environment, which only
|
||||
# `main` may use; a dry run on another branch asks for no environment and builds unsigned.
|
||||
environment: ${{ github.ref == 'refs/heads/main' && 'release' || '' }}
|
||||
runs-on: macos-14
|
||||
timeout-minutes: 120
|
||||
outputs:
|
||||
signing: ${{ steps.package.outputs.signing }}
|
||||
env:
|
||||
FIELDCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: aarch64-apple-darwin, x86_64-apple-darwin
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: release-macos-universal
|
||||
# Release runs are rare: only release-branch runs keep a cache, so dry runs don't evict CI's.
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: Generate THIRD-PARTY-LICENSES.txt (fails when a crate's licence can't be resolved)
|
||||
run: cargo xtask licences
|
||||
- name: Import the signing certificate (skipped without APPLE_CERTIFICATE)
|
||||
env:
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
||||
run: packaging/macos/import-cert.sh
|
||||
- name: Build, sign, notarize and package (the dmg is mounted and checked)
|
||||
id: package
|
||||
env:
|
||||
# The analytics project key (docs/privacy.md), built in; never printed.
|
||||
FIELDCRAFT_POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
run: packaging/macos/package.sh --arch universal
|
||||
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: release-macos
|
||||
path: dist/release/*
|
||||
if-no-files-found: error
|
||||
- name: Remove the temporary keychain
|
||||
if: always()
|
||||
run: |
|
||||
if [ -n "${MACOS_KEYCHAIN:-}" ]; then security delete-keychain "$MACOS_KEYCHAIN" || true; fi
|
||||
|
||||
linux:
|
||||
name: Linux x86_64
|
||||
needs: version
|
||||
# The oldest GitHub-hosted image, so the binaries need only glibc >= 2.35.
|
||||
# Signing, notarization and analytics secrets live in the `release` environment, which only
|
||||
# `main` may use; a dry run on another branch asks for no environment and builds unsigned.
|
||||
environment: ${{ github.ref == 'refs/heads/main' && 'release' || '' }}
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
FIELDCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
||||
# The .deb and .rpm builders (both MIT), pinned.
|
||||
CARGO_DEB_VERSION: 3.8.0
|
||||
CARGO_GENERATE_RPM_VERSION: 0.21.0
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Install packaging tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends desktop-file-utils lintian rpm
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: release-linux-x86_64
|
||||
# Release runs are rare: only release-branch runs keep a cache, so dry runs don't evict CI's.
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: Generate THIRD-PARTY-LICENSES.txt (fails when a crate's licence can't be resolved)
|
||||
run: cargo xtask licences
|
||||
- name: Install cargo-deb and cargo-generate-rpm
|
||||
run: |
|
||||
cargo install --locked --version "$CARGO_DEB_VERSION" cargo-deb
|
||||
cargo install --locked --version "$CARGO_GENERATE_RPM_VERSION" cargo-generate-rpm
|
||||
- name: Build and package (tar.gz, AppImage, deb, rpm)
|
||||
env:
|
||||
# The analytics project key (docs/privacy.md), built in; never printed.
|
||||
FIELDCRAFT_POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
|
||||
run: packaging/linux/package.sh
|
||||
- name: Check the packages
|
||||
run: |
|
||||
. packaging/env.sh
|
||||
base="fieldcraft-$VERSION-linux-x86_64"
|
||||
deb="dist/release/fieldcraft_${VERSION}_amd64.deb"
|
||||
rpm="dist/release/fieldcraft-$VERSION.x86_64.rpm"
|
||||
list="$(tar -tzf "dist/release/$base.tar.gz")"
|
||||
for f in bin/fieldcraft bin/fieldcraft-cli "share/applications/$APP_ID.desktop" \
|
||||
share/doc/fieldcraft/THIRD-PARTY-LICENSES.txt; do
|
||||
grep -Fx -- "$base/$f" <<<"$list"
|
||||
done
|
||||
# The .deb and .rpm carry the tar.gz's files under /usr (the .deb adds its copyright
|
||||
# file); their lists live in apps/fieldcraft/Cargo.toml, so a drift fails here.
|
||||
files() { grep -v '/$' | sed -E "s#^(\./|/|$base/)##; s#^usr/##" | grep -vx 'share/doc/fieldcraft/copyright' | sort; }
|
||||
diff <(files <<<"$list") <(dpkg-deb --fsys-tarfile "$deb" | tar -t | files)
|
||||
diff <(files <<<"$list") <(rpm -qpl "$rpm" | files)
|
||||
dpkg-deb --info "$deb"
|
||||
rpm -qpi "$rpm"
|
||||
echo "rpm requires:"; rpm -qpR "$rpm"
|
||||
# The release notes live on GitHub, so the package has no Debian changelog (a native
|
||||
# package built upstream, not by a distribution).
|
||||
lintian --info --fail-on error --suppress-tags no-changelog "$deb"
|
||||
ldd target/release/fieldcraft
|
||||
for appimage in dist/release/*.AppImage; do
|
||||
chmod +x "$appimage"
|
||||
APPIMAGE_EXTRACT_AND_RUN=1 "$appimage" --version
|
||||
done
|
||||
- name: Install the .deb and .rpm in containers
|
||||
run: |
|
||||
# The oldest supported Ubuntu (the build's glibc) and a current Fedora. Recommended and
|
||||
# weak dependencies (GPU drivers) are skipped to keep it quick; the hard ones must resolve.
|
||||
# Both images leave out documentation when installing (dpkg path-exclude, dnf nodocs), so
|
||||
# the licences are checked in the package's file list rather than on disk.
|
||||
docker run --rm -v "$PWD/dist/release:/dist:ro" ubuntu:22.04 bash -euxc '
|
||||
apt-get update -qq
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends /dist/*.deb
|
||||
fieldcraft-cli --version
|
||||
fieldcraft --version
|
||||
dpkg -L fieldcraft | grep -Fx /usr/share/doc/fieldcraft/THIRD-PARTY-LICENSES.txt'
|
||||
docker run --rm -v "$PWD/dist/release:/dist:ro" fedora:44 bash -euxc '
|
||||
dnf install -y -q --setopt=install_weak_deps=False /dist/*.rpm
|
||||
fieldcraft-cli --version
|
||||
fieldcraft --version
|
||||
rpm -ql fieldcraft | grep -Fx /usr/share/doc/fieldcraft/THIRD-PARTY-LICENSES.txt
|
||||
dnf install -y -q rpmlint
|
||||
rpmlint /dist/*.rpm || true'
|
||||
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: release-linux-x86_64
|
||||
path: dist/release/*
|
||||
if-no-files-found: error
|
||||
|
||||
windows:
|
||||
name: Windows x64
|
||||
needs: version
|
||||
# Signing, notarization and analytics secrets live in the `release` environment, which only
|
||||
# `main` may use; a dry run on another branch asks for no environment and builds unsigned.
|
||||
environment: ${{ github.ref == 'refs/heads/main' && 'release' || '' }}
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
FIELDCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: x86_64-pc-windows-msvc
|
||||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||||
with:
|
||||
key: release-windows-x64
|
||||
# Release runs are rare: only release-branch runs keep a cache, so dry runs don't evict CI's.
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: Generate THIRD-PARTY-LICENSES.txt (fails when a crate's licence can't be resolved)
|
||||
run: cargo xtask licences
|
||||
- name: Build and package (zip)
|
||||
shell: pwsh
|
||||
env:
|
||||
# The analytics project key (docs/privacy.md), built in; never printed.
|
||||
FIELDCRAFT_POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
|
||||
run: ./packaging/windows/package.ps1
|
||||
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: release-windows-x64
|
||||
path: dist/release/*
|
||||
if-no-files-found: error
|
||||
|
||||
release:
|
||||
name: Draft release (or dry-run artifacts)
|
||||
# The only job that publishes anything: it waits for every verify and packaging job.
|
||||
needs: [version, verify-ci, verify-heavy, verify-oracle, macos, linux, windows]
|
||||
# Signing, notarization and analytics secrets live in the `release` environment, which only
|
||||
# `main` may use; a dry run on another branch asks for no environment and builds unsigned.
|
||||
environment: ${{ github.ref == 'refs/heads/main' && 'release' || '' }}
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
VERSION: ${{ needs.version.outputs.version }}
|
||||
PRERELEASE: ${{ needs.version.outputs.prerelease }}
|
||||
MACOS_SIGNING: ${{ needs.macos.outputs.signing }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
||||
with:
|
||||
pattern: release-*
|
||||
path: artifacts
|
||||
merge-multiple: true
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
# The same file every package carries, attached to the release on its own.
|
||||
- name: Generate THIRD-PARTY-LICENSES.txt
|
||||
run: cargo xtask licences && cp THIRD-PARTY-LICENSES.txt artifacts/
|
||||
- name: Check the set and write the checksums
|
||||
run: |
|
||||
for f in "fieldcraft-$VERSION-macos-universal.dmg" "fieldcraft-cli-$VERSION-macos-universal.zip" \
|
||||
"fieldcraft-$VERSION-linux-x86_64.tar.gz" "fieldcraft-$VERSION-linux-x86_64.AppImage" \
|
||||
"fieldcraft_${VERSION}_amd64.deb" "fieldcraft-$VERSION.x86_64.rpm" \
|
||||
"fieldcraft-$VERSION-windows-x64.zip" THIRD-PARTY-LICENSES.txt; do
|
||||
test -s "artifacts/$f" || { echo "::error::missing $f"; exit 1; }
|
||||
done
|
||||
(cd artifacts && sha256sum -- * | tee SHA256SUMS.txt)
|
||||
- name: Sign SHA256SUMS.txt with the release key (skipped without FIELDCRAFT_SIGNING_KEY)
|
||||
id: sign
|
||||
env:
|
||||
FIELDCRAFT_SIGNING_KEY: ${{ secrets.FIELDCRAFT_SIGNING_KEY }}
|
||||
# rsign2 (MIT), a minisign-compatible signer, pinned.
|
||||
RSIGN_VERSION: 0.6.7
|
||||
run: |
|
||||
if [ -z "$FIELDCRAFT_SIGNING_KEY" ]; then
|
||||
echo "::warning::FIELDCRAFT_SIGNING_KEY is not set: SHA256SUMS.txt is not signed, so the app cannot install this release itself (docs/releasing.md)"
|
||||
echo "signed=false" >>"$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
cargo install --locked --quiet --root "$RUNNER_TEMP/rsign" rsign2 --version "$RSIGN_VERSION"
|
||||
rsign="$RUNNER_TEMP/rsign/bin/rsign"
|
||||
key="$RUNNER_TEMP/release.key"
|
||||
(umask 077 && printf '%s\n' "$FIELDCRAFT_SIGNING_KEY" >"$key")
|
||||
"$rsign" sign -W -s "$key" -t "FieldCraft v$VERSION SHA256SUMS.txt" -x artifacts/SHA256SUMS.txt.minisig artifacts/SHA256SUMS.txt
|
||||
rm -f "$key"
|
||||
# The signature must verify with the public key the app is built with.
|
||||
public="$(sed -n 's/^pub const PUBLIC_KEY: &str = "\(.*\)";$/\1/p' crates/update/src/lib.rs)"
|
||||
"$rsign" verify -q -P "$public" -x artifacts/SHA256SUMS.txt.minisig artifacts/SHA256SUMS.txt
|
||||
echo "signed=true" >>"$GITHUB_OUTPUT"
|
||||
- name: Write the notes
|
||||
env:
|
||||
SIGNED: ${{ steps.sign.outputs.signed }}
|
||||
run: |
|
||||
# The macOS paragraph says how the app was signed (packaging/macos/package.sh).
|
||||
case "$MACOS_SIGNING" in
|
||||
notarized) macos="The macOS app is signed with a Developer ID and notarized by Apple." ;;
|
||||
signed) macos="The macOS app is signed with a Developer ID but **not notarized**: the first time, right-click \`FieldCraft.app\` in Applications and choose **Open**." ;;
|
||||
*) macos="The macOS app is **ad-hoc signed and not notarized**: macOS blocks the first launch. Right-click \`FieldCraft.app\` in Applications and choose **Open** (then **Open** again), or run \`xattr -dr com.apple.quarantine /Applications/FieldCraft.app\`." ;;
|
||||
esac
|
||||
# The updates paragraph says whether the app can install this release itself.
|
||||
if [ "$SIGNED" = true ]; then
|
||||
updates="\`SHA256SUMS.txt\` is signed with the FieldCraft release key (\`SHA256SUMS.txt.minisig\`), so FieldCraft's Help › Check for Updates can verify and install this release."
|
||||
else
|
||||
updates="This release's \`SHA256SUMS.txt\` is **not signed**: FieldCraft's in-app updates can't verify it and won't install it; download it from this page instead."
|
||||
fi
|
||||
awk -v v="$VERSION" -v m="$macos" -v u="$updates" '{ gsub(/\{version\}/, v); gsub(/\{macos_signing\}/, m); gsub(/\{update_signing\}/, u); print }' \
|
||||
.github/release-notes.md >notes.md
|
||||
cat notes.md >>"$GITHUB_STEP_SUMMARY"
|
||||
- name: Upload the dry-run artifacts
|
||||
if: needs.version.outputs.draft != 'true'
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: release-assets
|
||||
path: |
|
||||
artifacts/*
|
||||
notes.md
|
||||
if-no-files-found: error
|
||||
- name: Create or update the draft release
|
||||
if: needs.version.outputs.draft == 'true'
|
||||
working-directory: artifacts
|
||||
run: |
|
||||
tag="v$VERSION"
|
||||
title="FieldCraft v$VERSION"
|
||||
if gh api "repos/$GH_REPO/git/matching-refs/tags/$tag" --jq '.[].ref' | grep -Fxq "refs/tags/$tag"; then
|
||||
echo "::error::Tag $tag already exists. Choose a new version; never move a release tag."
|
||||
exit 1
|
||||
fi
|
||||
if draft="$(gh release view "$tag" --json isDraft --jq .isDraft 2>/dev/null)"; then
|
||||
if [ "$draft" != true ]; then
|
||||
echo "::error::Release $tag is already published. Bump the version before releasing."
|
||||
exit 1
|
||||
fi
|
||||
# A rebuild of the same version replaces the draft's assets (removing names it no longer
|
||||
# makes); its notes are kept.
|
||||
assets="$(gh release view "$tag" --json assets --jq '.assets[].name')"
|
||||
while IFS= read -r asset; do
|
||||
if [ -n "$asset" ]; then gh release delete-asset "$tag" "$asset" --yes; fi
|
||||
done <<<"$assets"
|
||||
gh release edit "$tag" --draft --title "$title" --target "$GITHUB_SHA" --prerelease="$PRERELEASE"
|
||||
gh release upload "$tag" -- *
|
||||
else
|
||||
gh release create "$tag" --draft --title "$title" --target "$GITHUB_SHA" \
|
||||
--notes-file ../notes.md --generate-notes --prerelease="$PRERELEASE" -- *
|
||||
fi
|
||||
echo "Draft release **$title** at \`${GITHUB_SHA::9}\`: $(gh release view "$tag" --json url --jq .url)" >>"$GITHUB_STEP_SUMMARY"
|
||||
@@ -1,86 +0,0 @@
|
||||
# Labels new issues and PRs from fixed rules, `/triage` for maintainers, asks short bug reports
|
||||
# for reproduction steps (`needs-info`, closed after 14 days unanswered), and a manual backfill
|
||||
# (CONTRIBUTING.md, "Labels and triage"). The logic is in scripts/triage/ (tested in ci.yml).
|
||||
name: Triage
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, labeled, edited]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
# pull_request_target so PRs from forks can be labelled. It runs the base branch's
|
||||
# scripts/triage and only lists the PR's changed paths; the PR's code is never checked out.
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
schedule:
|
||||
- cron: '41 5 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: 'Only report what would be labelled'
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
permissions:
|
||||
contents: read # to check out scripts/triage
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: triage-${{ github.event.issue.number || github.event.pull_request.number || 'repo' }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
name: Triage bot
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 30
|
||||
# Filtered here so the common events (every comment, every label) start no runner.
|
||||
if: >-
|
||||
(github.event_name == 'issue_comment' &&
|
||||
github.event.comment.user.type != 'Bot' && startsWith(github.event.comment.body, '/triage')) ||
|
||||
((github.event_name == 'issue_comment' || github.event.action == 'edited') &&
|
||||
contains(github.event.issue.labels.*.name, 'needs-info') &&
|
||||
github.event.sender.login == github.event.issue.user.login) ||
|
||||
(github.event.action == 'labeled' && startsWith(github.event.label.name, 'priority') &&
|
||||
contains(github.event.issue.labels.*.name, 'needs-triage')) ||
|
||||
(github.event_name != 'issue_comment' && github.event.action != 'labeled' && github.event.action != 'edited')
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
sparse-checkout: scripts/triage
|
||||
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
env:
|
||||
DRY_RUN: ${{ inputs.dry_run }}
|
||||
with:
|
||||
script: |
|
||||
const triage = require('./scripts/triage/triage.js');
|
||||
const info = require('./scripts/triage/needs-info.js');
|
||||
const event = context.eventName;
|
||||
const action = context.payload.action;
|
||||
const esc = (t) => String(t).replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]);
|
||||
const th = (...names) => names.map((data) => ({ data, header: true }));
|
||||
if (event === 'issue_comment') {
|
||||
core.info(await triage.handleComment({ github, context }));
|
||||
await info.handleReply({ github, context });
|
||||
} else if (event === 'issues' && action === 'labeled') {
|
||||
await triage.handleLabeled({ github, context });
|
||||
} else if (event === 'issues' && action === 'edited') {
|
||||
await info.handleReply({ github, context });
|
||||
} else if (event === 'schedule') {
|
||||
const closed = await info.sweep({ github, context });
|
||||
core.info(`Closed for missing information: ${closed.join(', ') || 'none'}`);
|
||||
} else if (event === 'workflow_dispatch') {
|
||||
const dryRun = process.env.DRY_RUN !== 'false';
|
||||
const plan = await triage.backfill({ github, context, dryRun });
|
||||
const asks = await info.backfill({ github, context, dryRun });
|
||||
await core.summary
|
||||
.addHeading(dryRun ? `Backfill dry run: ${plan.length} unlabelled` : `Backfill: ${plan.length} labelled`)
|
||||
.addTable([th('Item', 'Kind', 'Labels', 'Title'), ...plan.map((p) => [`#${p.number}`, p.pull ? 'PR' : 'issue', esc(p.labels.join(', ') || '—'), esc(p.title)])])
|
||||
.addHeading(dryRun ? `needs-info dry run: ${asks.length} would be asked` : `needs-info: ${asks.length} asked`)
|
||||
.addTable([th('Issue', 'Asks', 'Title'), ...asks.map((p) => [`#${p.number}`, esc(p.asks.join(', ')), esc(p.title)])])
|
||||
.write();
|
||||
} else {
|
||||
core.info(`Labelled: ${(await triage.handleOpened({ github, context })).join(', ') || 'nothing'}`);
|
||||
if (event === 'issues') await info.handleOpened({ github, context });
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/target*/
|
||||
/plan/
|
||||
/dist/
|
||||
/.agent-files/
|
||||
/worktrees/
|
||||
*~
|
||||
.DS_Store
|
||||
*.swp
|
||||
__pycache__/
|
||||
/THIRD-PARTY-LICENSES.txt
|
||||
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 295 KiB |
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 220 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 161 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 213 KiB |
|
After Width: | Height: | Size: 204 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 92 KiB |