= SDP Developer Guide
:revnumber: v2026.1
:revdate: 2026-09-08
:doctype: book
:icons: font
:toc:
:toclevels: 5
:sectnumlevels: 4
:xrefstyle: full
// Attribute for ifdef usage
:unix_doc: true
== DRAFT NOTICE
WARNING: This document is in DRAFT status and should not be relied on yet. It is a preview of a document to be completed in a future release.
== Preface
This guide is to aid people who contribute to the P4 Server Deployment Package (P4SDP). This includes Perforce Staff and contributors from the general public.
== Terminology
* _Emergency Bug Fix_: A direct edit made in an already-cut release stream that changes something which actually ships in that stream's tarball -- a script, not just a doc page -- bypassing the normal `dev` -> `main` -> release-stream flow on purpose, to get a critical fix out faster than a full new Patch Release would allow. Always requires bumping that release stream's `Version` file and republishing its tarball. This is exceptional by design; if it starts feeling routine, that's a signal to cut a real Patch Release instead. See the link:ReleaseProcessOverview.md[Release Process Overview]'s "Hot Fixes and Emergency Bug Fixes" section.
* _GA Release_: A General Availability (GA) release is the first release of a new major version. It includes a new SDP tarball as well as updates to files available on the web site with standard/published links.
* _Hot Fix_: A direct edit made in a release stream (or in `main`, ahead of the next Copy Up) that touches only web-facing, non-shipped content -- e.g. doc pages or `README.md` -- without generating a new SDP tarball. This is the preferred, lower-risk way to make an out-of-band correction, and should be the common case; it's not forbidden to go further, but if the fix needs to touch a script that actually ships, see _Emergency Bug Fix_ instead.
* _Merge Down, Copy Up_: A mantra for a well-designed release process. See the link:https://workshop.perforce.com/projects/perforce_software-pds[Perforce Directory Standard (PDS)] for information about this.
* _Patch Release_: A Patch Release is functionally identical to a GA release, in that it includes a new SDP tarball and updates to files on the web site. Any SDP release between major version releases of the P4 Server is a Patch.
* _Ready 5_: The property of a release process that ensures the team can always shift priorities and ship a patch quickly, even as various development tasks are in different states of readiness. Doing work properly and keeping the `mainline` clear of work that is not ready to ship is key to maintaining readiness.
== Choosing an Emergency Bug Fix vs. a Patch Release
Once a bug is found in an already-shipped release, the question is whether to fix it with an Emergency Bug Fix (patch the existing release stream in place, bump `Version`'s changelist number, and add an entry to that release's existing Release Notes section) or to cut a real Patch Release (a new, independently-numbered release with its own Release Notes section). This section covers the *policy* -- when each is appropriate; see the link:ReleaseProcessOverview.md[Release Process Overview]'s "Hot Fixes and Emergency Bug Fixes" section for the *mechanics* of actually doing an Emergency Bug Fix.
An Emergency Bug Fix is a deliberate exception to the normal release flow, not a lightweight alternative to one. It trades the safety of the normal `dev` -> `main` -> release-stream flow (with its usual review and regression testing cadence) for speed, and it does so by editing history that customers may have already started relying on -- anyone who downloaded the tarball before the fix landed has a subtly different artifact than anyone who downloads it after, even though both believe they have "SDP 2026.1 GA." That trade is worth making only when the fix is needed urgently and the fix itself is small and well-understood enough not to need the normal cadence. Three factors govern the call:
* *How much changed.* A one- or two-line fix confined to a single, well-understood root cause (e.g. a hardcoded path, an off-by-one condition) is a reasonable Emergency Bug Fix candidate. A fix that touches several files, requires design judgment, or has ripple effects into other scripts is a signal to route it through the normal flow and a real Patch Release instead, even if it feels urgent -- the whole point of the normal flow's review and regression testing is to catch exactly the kind of ripple effect a rushed fix is prone to introducing.
* *How severe the bug is.* Data loss, security exposure, or a broken install/upgrade path (something that would give every affected customer a bad first experience, or actively damage their environment) justifies the urgency an Emergency Bug Fix trades for. A cosmetic issue, a misleading but harmless message, or a narrow edge case affecting few customers does not -- let it accumulate toward the next Patch Release instead.
* *How long it's been since the release shipped.* This is a proxy for how many customers have already taken the affected artifact, and for how "fresh" the release still feels as a single coherent unit of work. A bug caught within a day or two of shipping, before most customers have even had a chance to download it, is a very different situation from one caught weeks or months later, after the release has been in the field and other work has moved on. As more time passes, prefer a real Patch Release -- patching a release stream that's been stable for a month erodes the meaning of "the same release" for anyone tracking it by changelist number, in a way that patching a release from yesterday does not.
None of these factors is individually decisive, and there's no fixed threshold (e.g. no fixed "N hours" cutoff) -- weigh them together. A severe bug caught within hours, with a small and well-understood fix, is the clearest case for an Emergency Bug Fix. Anything that fails more than one of these tests -- a sprawling fix, a minor bug, or one caught long after release -- should default to a real Patch Release instead. When genuinely unsure, prefer the Patch Release: it costs more time up front, but it doesn't compromise the normal review/testing cadence or the stability of the release history.
== Script Versioning
Many individual SDP scripts are versioned using the `+k` "keyword expansion" file type modifier. This file type modifier causes the P4 Server to update keywords in the content of the versioned file, e.g. our scripts, with a new version identifier each time the script is submitted.
Scripts that use this versioning method support the `-V` (version check) option, which gives results like these examples:
$ ccheck.sh -V
ccheck.sh version r26.1.0.33441
$ ccheck.sh -V
ccheck.sh version DEV_C2S.31580
Due to the way we are using Streams, the path to the versioned file contains meaningful information about which version of SDP the script is released with. For example, if the script path starts with `//p4-sdp/r26.1.0`, that script is part of the SDP 2026.1 GA release. Released versions of SDP align with the P4 Server format of `rXX.Y`, with an additional `.Z` patch digit that's always present (never omitted), where `XX` is a year identifier, `Y` increments with each major release in a year, and `Z` is `0` for the GA release and increments with each patch after it (e.g. `r26.1.0` for GA, `r26.1.1` for the first patch). Unreleasd versions, such as those being developed and tested, use an ALL-UPPERCASE form of the development stream name, .e.g. DEV_C2S relates to the `//p4-sdp/dev_c2s` development stream. The uppercase is used to emphasize that version is not released.
The number at the end is the changelist number of the individual change that produced that latest version of the script. For released versions, this changelist will be due to release-process related activities rather the development code changes.
The following standard block of code (bash in this example) illustrates how the keyword is used to automatically update the version number with each submit.
# Version ID Block. Relies on +k filetype modifier.
#------------------------------------------------------------------------------
# shellcheck disable=SC2016
declare VersionID='$Id: //p4-sdp/r26.1.0/doc/gen/gen_script_man_pages.sh#2 $ $Change: 31472 $'
declare VersionStream=${VersionID#*//}; VersionStream=${VersionStream#*/}; VersionStream=${VersionStream%%/*};
declare VersionCL=${VersionID##*: }; VersionCL=${VersionCL%% *}
declare Version=${VersionStream}.${VersionCL}
[[ "$VersionStream" == r* ]] || Version="${Version^^}"
The line that defines `VersionID` is modified by the P4 Server upon submit, with the '$Id:$` and `$Change:$` tags being replaced. This ensures that the version is reliably updated each time the script changes. Note that in this bash example, single quotes are used rather than double quotes to prevent the bash shell from interpreting `$Id` and `$Change` as bash script variables. The `shellcheck disable=SC2016` comment silences as ShellCheck warning about accidental usage of single quotes suppressing expansion of variables. In this case, that is exactly the intent.
== Working in Streams
Different types of work are done in different streams:
// [%autowidth,cols="a,a,a",options="header",] <-- The autowidth looked ugly.
[cols="24%a,15%a,61%a",options="header",]
|===
|Stream Name|Type|Description of Work
|`//p4-sdp/r*` +
{empty}
Examples: +
{empty}
`//p4-sdp/r26.1.0` +
`//p4-sdp/r26.1.1` | `release` | Release process activities are done in release streams, such as updating the Version file and generating final versions of docs and Release Notes. Each release -- the initial GA and every subsequent patch -- gets its own freshly-cut release stream from `main`; release streams are never patched or otherwise modified in place after they ship. See `doc/ReleaseProcessOverview.md` for the full process.
|`//p4-sdp/main` | `mainline` | Reflects whatever was most recently released. Regression test suites target `dev` (the release candidate), not `main` -- `main` only receives content via Copy Up from `dev` as part of cutting a release. Humans should do very little direct work in this stream outside of the release process itself.
| `//p4-sdp/dev` | `development` | This is the default development stream. Work on features that are committed to be in the next release can be done directly in this stream, such as straightforward bug fixes and small, low-risk features. Anything submitted to this stream _must_ be in a state where, if released today due to a need to ship an urgent patch possibly unrelated to the current change being submitted, it would be a Good Thing. Submitting something to the default dev branch is in effect saying, "Pending verification by regression test suites, this change is good enough to be shipped." Don't submit something in the default dev stream unless you intend to do any necessary iteration (e.g. based on regression test suite results) in short order.
| `//p4-sdp/dev_*` +
{empty}
Examples: +
{empty}
`//p4-sdp/dev_c2s` +
`//p4-sdp/dev_rebrand` +
`//p4-sdp/dev_SDP-1265` | `development` +
or +
`sparsedev`| Development tasks are done in feature streams if it is not certain they are ready or whether they will be included in the next release. The `tag` is a short tag name referencing the work, e.g. "c2s" for "Classic to Streams development work", or the tag can even be a JIRA issue tag, e.g SDP-1265. Types of work done in development streams may include:
* Projects with uncertainty in their development time frames, possibly large projects and/or those requiring significant iteration.
* Prototype or Research and Development work that may never be released. Work in dev* streams can terminate stream and never be promoted, or deferred indefinitely.
When choosing `development` vs. sparsedev stream type, consider these factors:
* Use `development` streams if a plan on using push/fetch to work offline; as fetching doesn't a `sparsedev` stream only fetches stream-resdient files, not the whole workspace.
* Use `sparsedev` if you're working on focused changes, e.g. to just a few scripts or files.
|===
== Documentation Builds
HTML and PDF documentation are generated from AsciiDoc/Markdown sources via `make` in the `doc/` directory (see the link:ReleaseProcessOverview.md[Release Process Overview]'s doc-generation steps).
IMPORTANT: Regenerate PDFs only as part of the release process itself, or when specifically checking PDF rendering/formatting -- never as a routine step alongside an ordinary doc edit. PDFs are large, heavy files with almost no incremental value during development; regenerating one for every small doc change wastes disk space and adds bulk to changelists for no benefit. HTML is cheap to regenerate and should be kept current with its source; PDF generation is deliberately deferred to release time.
== Code Reviews
Code reviews can occur in any stream. Both pre- and post-commit reviews are allowed. Reviews in the default dev stream can be done for changes initiated directly in the default dev stream, as well as Copy Up changes from dev* streams, thus reviewing the sum of a series of iterative changes in a lower dev* stream in a single review. More granular reviews can also occur in directly in dev* streams.
== P4 Code Review and Stream Paths
[appendix]
== Other Documentation
See Also:
* link:ReleaseProcessOverview.html[SDP Release Process Overview].
[appendix]
== DRAFT NOTICE
WARNING: This document is in DRAFT status and should not be relied on yet. It is a preview of a document to be completed in a future release.
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #6 | 33703 | Claude (AI Agent by Anthropic) | Copy Up from dev: SDP_DeveloperGuide.adoc EBF-vs-Patch-Release policy section + ReleaseProcessOverview.md cross-reference, plus outstanding backlog (ReadMe.md, parse_sysctl.pl, Changing_auth.id.md, SysConfig.adoc). | ||
| #5 | 33555 | Claude (AI Agent by Anthropic) | Updated revnumber and date for release. | ||
| #4 | 33541 | Claude (AI Agent by Anthropic) |
Copy Up: document the PDF-regeneration policy (release-time only), from dev@33540. Regenerated SDP_DeveloperGuide.html and ReleaseProcessOverview.html; PDFs intentionally not touched. Agent: Claude (Sonnet 5), on behalf of Tom Tyler. |
||
| #3 | 33475 | Claude (AI Agent by Anthropic) |
Copy Up: Hot Fix vs. Emergency Bug Fix policy docs (see dev change 33474). Agent: Claude Sonnet 5 (claude-sonnet-5), via Claude Code. |
||
| #2 | 33434 | Claude (AI Agent by Anthropic) |
Updated revnumber and revdate fields in adoc files for release (release process Step 11, major releases only). All 15 .adoc files under doc/ and Unsupported/doc/ bumped to v2026.1, 2026-09-03. SDP_DeveloperGuide.adoc was on a stale v2025.1 (had missed at least one prior release cycle); now consistent with everything else. Agent: Claude Code, Model: Claude Sonnet 5 (claude-sonnet-5), operating as bot_Claude_Anthropic. |
||
| #1 | 33433 | Claude (AI Agent by Anthropic) |
Copy Up from //p4-sdp/dev into //p4-sdp/main. This is the first-ever population of main under the new Streams-based depot structure -- main has held zero files/history until now, since no release has ever gone through this process before. 463 files, covering the entire 2026.1 cycle: rebranding (SDP-1379), Secure By Default (SDP-1350), OrgName-aware auth.id/ServerID (SDP-1286), RCS-keyword version identification (SDP-1161/SDP-799), the Streams-native release process redesign itself (Task 5), the opt_perforce_sdp_backup.sh false-error fix, the P4D 2026.1 test-suite targeting, refreshed P4*.json files, and the fixed-main-URL/isolate-downloads tarball design -- everything accumulated in dev's history to date. Isolated paths (ai_dev_support/, Version, doc/*.html, doc/*.pdf, doc/gen/*.man.txt, doc/gen/sdp_install.cfg, Unsupported/doc/*.html, Unsupported/doc/*.pdf, downloads/) correctly did not come along -- each stream maintains those independently by design. Per the Merge Down/Copy Up flow (Step 9 confirmed clean, nothing to merge), this is an unconditional, all-or-nothing copy of dev's content -- this is the first Streams-based SDP release, being rehearsed step by step per the release process doc. Agent: Claude Code, Model: Claude Sonnet 5 (claude-sonnet-5), operating as bot_Claude_Anthropic. |
||
| //p4-sdp/dev/doc/SDP_DeveloperGuide.adoc | |||||
| #1 | 33409 | Claude (AI Agent by Anthropic) |
Copy Up from //p4-sdp/dev_rebrand into //p4-sdp/dev. This is the first promotion of dev_rebrand's work into dev since dev_rebrand was created (2025-05-24) -- 303 files, covering the entire 2026.1 rebranding effort (SDP-1379), the Secure By Default adaptation (SDP-1350), OrgName-aware auth.id/ServerID (SDP-1286), RCS-keyword version identification (SDP-1161/SDP-799), and the Streams-native release process redesign (Task 5) done this session, plus everything else accumulated in dev_rebrand's history before this session. Per the Merge Down/Copy Up flow, this is intentionally a full, unconditional blast-replace of dev's content from dev_rebrand -- all selectivity/care happened in the preceding Merge Down (dev -> dev_rebrand, changes 33407-33408), which absorbed Robert Cowham's independent dev-side work first so nothing of his is lost by this Copy Up. Two files are worth calling out since they might look alarming in isolation: - tools/mdcu.sh is deleted -- intentional, retired this session in favor of the two direct Streams commands now documented in doc/ReleaseProcessOverview.md. - tools/ReleaseProcessOverview.md is deleted -- this is a stale relic of a file move dev_rebrand made back in 2025-05-24 (tools/ -> doc/) that was never previously propagated to dev; the current, fully-rewritten doc/ReleaseProcessOverview.md is added/updated correctly by this same changelist. |
||
| //p4-sdp/dev_rebrand/doc/SDP_DeveloperGuide.adoc | |||||
| #1 | 31752 | C. Thomas Tyler |
In dev_rebrand, bringing in changes from dev_c2s ala: p4 merge --from dev_c2s p4 resolve -am No interactive resolve was needed. |
||
| //p4-sdp/dev_c2s/doc/SDP_DeveloperGuide.adoc | |||||
| #1 | 31696 | C. Thomas Tyler |
Added preliminary SDP Developer Guide. WIP. |
||