Skip to content

Lay out tickets from the form's labels, and add a dry-run mode - #388

Merged
ramereth merged 8 commits into
masterfrom
field-labels-and-dry-run
Oct 5, 2026
Merged

ramereth merged 8 commits into
masterfrom
field-labels-and-dry-run

Conversation

@ramereth

@ramereth ramereth commented Oct 1, 2026

Copy link
Copy Markdown
Member

The website's reworked request forms send formsender each field's label and where it sits in the form. This lays out
tickets the way the form is, and adds a dry-run mode so form changes can be tested without creating tickets.

Ticket layout

  • Uses the labels from a new hidden field_labels field to head each answer, in form order. Fields without a label
    follow as before, title-cased and sorted.
  • Leaves blank answers out of the body.
  • Puts the answers under headings for the form's sections, lists the chosen options of a checkbox group under its
    question, and adds a "Services requested" summary after the contact details.
  • Indents each answer and wraps it to 80 columns. A line with a very long word, such as an SSH key, isn't wrapped, so it
    can still be copied.
  • Keeps field_labels out of the debug log.

The contact block changes for every form, not only the ones that send labels: NAME: and EMAIL: become Name: and
Email: under a Contact heading, with indented answers.

Testing support

  • DRY_RUN=1 logs each ticket instead of creating it in RT, and makes RT_TOKEN optional.
  • DUPLICATE_CHECK_TIME can now be set from the environment; 0 turns the duplicate check off.
  • A .dockerignore keeps a local conf.py and other local files out of the image.

Deploying

The website's matching PR sends field_labels. Merge and deploy this first. Until then, the current formsender would
put the whole label list into every ticket as a "Field Labels" entry.

Tests pass with 100% coverage, and flake8 is clean.

🤖 Generated with Claude Code

ramereth and others added 8 commits September 30, 2026 18:44
Ticket bodies headed each answer with its field name in title case and sorted
them alphabetically, so the website's new hosting form produced headings like
"Instance Vcpus" and "Ci Cd Arch X86 64" and scattered each service's answers.
The website now sends a hidden field_labels field listing every field's label
in form order, built from its form definitions.

- Write fields named in field_labels first, in that order, under their label
- End a label in a colon unless it is a question, so "Do you have a hard
  deadline?" isn't followed by a stray colon
- Keep field_labels out of the body, and leave fields it doesn't name, or every
  field when it is missing or malformed, titled and sorted as before
- Document field_labels in the form setup guide
- Cover label order and headings, excluded and unlabelled fields, and bad
  field_labels values in the tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lance Albertson <lance@osuosl.org>
Every submission is logged at debug level as a dict of its fields. With the
website now sending field_labels, that line carried the form's whole label
list, about 8 KB of JSON on the hosting form, which buried the answers.

Skip field_labels in that log line alongside the captcha payload, and test
that the labels stay out while the answers are still logged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lance Albertson <lance@osuosl.org>
Every submitted field got a heading in the ticket, answered or not. The new
hosting request form has dozens of optional questions, so a typical ticket
carried a row of headings with nothing under them, such as "Organization or
fiscal sponsor:" and "SSH public key:".

- Skip fields whose value is empty or only whitespace, both for labelled
  fields and for the title-cased fallback
- Mark a blank labelled field as handled so it doesn't reappear under its
  title-cased name
- Keep defaults and zeros such as "Not sure" and "0", which are answers
- Note the behavior in the form setup guide
- Cover blank, whitespace-only, labelled and unlabelled fields in the tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lance Albertson <lance@osuosl.org>
Building the image from a working checkout copied everything in it, so a
developer's own conf.py replaced the conf.py.dist the Dockerfile installs, and
the 120 MB venv, .git and editor state went into the image too. A checkout
whose conf.py predated a new setting built an image that ignored it.

- Exclude conf.py, virtualenvs, .git, caches, coverage output and built docs
  from the build context

CI builds from a fresh clone, so its images are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lance Albertson <lance@osuosl.org>
There was no way to see the ticket a form would create without creating one in
RT, so testing a form change meant filing real tickets or reading the code.
With DRY_RUN set, formsender writes each ticket to its log and never contacts
RT.

- Log the queue, subject, requestor, custom fields, attachment names and body
  from send_ticket when DRY_RUN is set, and skip the RT client
- Read DRY_RUN from the environment in conf.py.dist and make RT_TOKEN optional
- Refuse to start without RT_TOKEN unless DRY_RUN is set, keeping the old
  fail-at-start behavior, and warn at start when DRY_RUN is on
- Document DRY_RUN and Cloudflare's Turnstile test keys in the usage and Docker
  guides
- Cover the dry-run log, the RT_TOKEN check and the DRY_RUN parsing in
  conf.py.dist in the tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lance Albertson <lance@osuosl.org>
Testing a form locally means submitting the same answers more than once,
and formsender rejects a repeat within an hour as a duplicate. The window
can now be set with DUPLICATE_CHECK_TIME, and 0 turns the check off.
It still defaults to an hour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lance Albertson <lance@osuosl.org>
Long request forms made hard-to-scan tickets: each chosen service showed
up as "requested", every checkbox was its own entry, and a question's
answer ran straight on from it. A field_labels entry can now say which
sections the field sits under and which checkbox group it belongs to.

- Writes a heading wherever the section changes, in place of the
  "requested" answer of the checkbox that opened it
- Lists chosen checkbox-group options under the group's question
- Indents each answer under its heading, line by line

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lance Albertson <lance@osuosl.org>
Long hosting requests were still slow to read: the scope only became
clear after scrolling the whole ticket, long answers ran on as single
lines, and the contact block used a different style from the rest.

- Lists the sections the requester opened, such as the services they
  chose, under "Services requested" after the contact details
- Gives the contact block and unsectioned fields the same headings and
  indented answers as everything else
- Wraps answers and list items to 80 columns, but leaves lines with a
  very long word whole so SSH keys and long URLs stay copyable

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lance Albertson <lance@osuosl.org>
@ramereth
ramereth merged commit be62f3c into master Oct 5, 2026
7 checks passed
@ramereth
ramereth deleted the field-labels-and-dry-run branch October 5, 2026 23:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant