@foss.global/codefeed 5.4.0 → 5.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/changelog.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026-11-15 - 5.6.0
4
+
5
+ ### Features
6
+
7
+ - read a Gitea source's quiescence facts and send its HTTP freeze canaries for the migration's freeze proof (giteasourcefreeze)
8
+ - `GiteaQuiescenceReader.read()` returns an `IForgeSourceQuiescenceReport` (`@foss.global/interfaces` 3.5) with the connection's read-only site administrator token and GET requests only (S11 ruling D1): every runner of `GET /admin/actions/runners?disabled=false`, page after page at `limit=50` until the rows cover `total_count`, sorted by numeric id, with Gitea's `status` and `busy` as evidence; `total_count` of the `in_progress` jobs with `blindSpot: 'cancelling'`; every cron task with its schedule verbatim, sorted by name; and the runs Gitea lists as `queued` or `pending`, sorted by numeric id, or `overBound` with Gitea's count past `forgeSourceQuiescenceQueuedRunMaxRecords`. A part whose read does not finish is null with `<part>: <code>` in `incompleteReasons`, never an empty list; a listing whose filter Gitea ignored fails its part (`runner-filter-ignored`, `job-filter-ignored`, `run-filter-ignored`), as do a total that changes between pages, a row listed twice and rows that do not add up to the total. The reader decides nothing: the consumer calls the source quiescent.
9
+ - `GiteaFreezeProbe.run({ origins, canaries, signal })` sends the HTTP canaries with the canary credential only and returns one `IForgeSourceFreezeCanaryOutcome` per origin and canary (S11 ruling D2, G14; canary addendum 1): `receive-pack-discovery`, `archive-get`, `issue-post`, `release-post`, `generic-package-put`, `oci-blob-upload` (the registry token `GET`, then the upload `POST`), `lfs-put` (the batch request, then the object `PUT` on the same origin) and `lfs-off` (the batch `upload` request alone). Before any write it reads `/user`, `/user/orgs` and `/user/repos` through every origin and rejects an administrator, an unrestricted account or one that reaches more than the canary organization and repository with `canary-credential-overbroad` (S11 ruling D3), one that reaches neither with `canary-credential-mismatch`, and origins that show another account or canary repository id with `canary-origin-mismatch`. Every write carries a fresh nonce. An outcome reports the status of the canary's own request, its `X-Gitea-Freeze` header verbatim and what a 2xx created; redirects are reported and never followed, failures are codes without a status, and no outcome or error carries a body or a credential. The probe never judges the marker.
10
+ - An origin's `connectAddress` sends its connections to that IP address on `baseUrl`'s port, while the Host header, the TLS server name and the certificate check stay `baseUrl`'s host (`createNodeAgents` takes it as its third argument).
11
+ - Gitea 28.2.0 facts the reader relies on, from its source: a run's own `repository_id` reads 0, so the repository comes from the run's embedded record; `head_branch` is omitted for a run on a tag (`ref` reads `''`); the `pending` filter selects runs shown as `pending`, `requested` or `waiting`; `/user/orgs` and `/user/repos` carry `X-Total-Count`; an LFS upload action asks for `Transfer-Encoding: chunked`, which the probe replaces with the object's length.
12
+
13
+ ## 2026-11-14 - 5.5.0
14
+
15
+ ### Features
16
+
17
+ - record each repository's issues with their comments, labels and milestones in a collaboration unit, split the collaboration measures by kind and count project boards (giteasourceinventory)
18
+ - `discover(signal, { classes: ['collaboration'] })` sets discovery's `collaboration: true`, and `units()` then adds one `{ kind: 'collaboration', owner, name, id }` unit per repository, keyed by its discovered id; a discovery that selects collaboration while a repository has no id rejects with `repository-id-missing` (S8 ruling D12, U3; S8 U1 ruling 1).
19
+ - A collaboration unit is an `IForgeSourceCollaborationInventory` (`@foss.global/interfaces` 3.4): `highestNumber`, the highest number Gitea lists among the repository's issues and pull requests (not measured when it lists neither), every repository label and milestone, and every issue sorted by number with its comments, all verbatim: title, body, `ref`, times, poster and original author, assignees, label, milestone and project ids, `locked`, `pinOrder`, `dueDate`, `timeEstimate`, and the counts of tracked times, dependencies, attachments and reactions. Gitea's `content_version` is not recorded. A list is `notMeasured`, without a read of it, while Gitea's API hides it: the issues when the issue unit is disabled or an external tracker, or when time tracking (for the repository or the instance) or dependencies are switched off, since Gitea then refuses every issue's tracked times or dependencies with 404 and a refused read must never count as none. Any failed read fails the unit, and so do an issue naming a milestone the listing lacks (`issue-milestone-unlisted`), a number listed twice (`duplicate-number`) and a result over `forgeSourceInventoryUnitMaxBytes` (`unit-over-bound`); records are never partial or truncated. Every read is a GET with the administrator's token, without `Sudo`.
20
+ - Gitea 28.2.0's REST API never reports `original_author` or `original_author_id`, so `originalAuthor` is null for every record; content Gitea imported from another service keeps its source authors only in Gitea's database and reads as posted by the importing account, which the Docker Gitea test shows with an import. An imported repository's issues are therefore `notMeasured` whenever its record's `original_url` is set and the listing holds an issue, with the reason "Gitea imported this repository (original_url is set); its API names the importing account as poster and never reports the original author.", and none of its issues is read further; an imported repository without issues keeps a complete empty list, and the rule does not depend on `mirror` (S8 U3 ruling, Q1). `original_url` is never recorded. A forge that fills the original author still has it recorded.
21
+ - Each organization owner records `labelRecords`, its labels sorted by id (null while `labels` is not complete); the composed inventory keeps them, and carries `collaboration` sorted by owner and name, exactly when discovery selects collaboration. A collaboration unit that failed, was not measured or answers for another name is a reason (`collaboration: unit failed: <code>`), and a list that is not measured is a reason by its path (`collaboration.issues: <reason>`).
22
+ - A repository's `collaboration.byKind` splits comments, reactions, attachments and attachment bytes into their issue and pull request kinds and counts open, closed and merged pull requests; each mixed measure is the sum of its kinds (S8 ruling D13, D14). `collaboration.projects` counts the repository's project boards, which Gitea serves while the projects unit is disabled, and is not measured while the unit allows only owner boards; each owner's `projects` counts its own boards, read with the administrator's token for private owners too (S8 U1 ruling 8).
23
+ - Compatibility: repository and owner units each read one more listing (`/projects`); a repository record whose `projects_mode` is not `repo`, `owner` or `all` (`invalid-projects-mode`; `invalid-string` when missing), or whose `has_issues` disagrees with its trackers (`inconsistent-issue-unit`), fails the repository unit, as Gitea 28.2.0 cannot produce either state (S8 U3 ruling, Q3). A collaboration unit whose repository record lacks `original_url` as a string fails with `invalid-string`. Time tracking or dependencies switched off leave every affected repository's issues `notMeasured`, for the owner's per-repository decision (S8 ruling D13). Composing a stored result from a reader before this version names the missing `labelRecords`, `projects` and `byKind` as reasons, so the inventory stays incomplete until those units are measured again.
24
+ - Dependencies: `@foss.global/interfaces` ^3.5.1, which defines the collaboration records (3.4.0) and documents the original-author and `labelRecords` rules above (3.5.1); for the Docker Gitea test, `@foss.global/forgefixtures` ^1.2.0, whose default seed holds the issue cases and records their fields.
25
+
26
+ ### Fixes
27
+
28
+ - measure a repository's issues while only its pull request unit is disabled, and the reverse (giteasourceinventory)
29
+ - Gitea 28.2.0 lists every issue and issue comment while only the pull request unit is disabled (`ListIssues` and `ListRepoIssueComments` filter by the readable unit), but 5.4.0 reported every issue, pull request, comment, review, reaction and attachment measure as `notMeasured` when either unit was off (S8 ruling, G2). Each kind is now measured while its own unit is readable; only the measures of the hidden kind, and the mixed measures that include it, are `notMeasured`.
30
+ - An issue unit configured as an external tracker keeps Gitea's `has_issues` true while its API lists none of the repository's issues; 5.4.0 counted them as a complete zero. They are now `notMeasured` with the reason that the issues unit is an external tracker.
31
+
3
32
  ## 2026-11-13 - 5.4.0
4
33
 
5
34
  ### Fixes
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@foss.global/codefeed',
6
- version: '5.4.0',
6
+ version: '5.6.0',
7
7
  description: 'The @foss.global/codefeed module generates commit/activity feeds from forge providers including Gitea and GitManager.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx1QkFBdUI7SUFDN0IsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHVIQUF1SDtDQUNySSxDQUFBIn0=
@@ -0,0 +1,99 @@
1
+ import * as plugins from './plugins.js';
2
+ type IQuiescenceReport = plugins.fossInterfaces.data.IForgeSourceQuiescenceReport;
3
+ type IFreezeOrigin = plugins.fossInterfaces.data.IForgeSourceFreezeOrigin;
4
+ type TFreezeCanary = plugins.fossInterfaces.data.TForgeSourceFreezeHttpCanary;
5
+ type ICanaryOutcome = plugins.fossInterfaces.data.IForgeSourceFreezeCanaryOutcome;
6
+ export interface IGiteaQuiescenceReaderOptions {
7
+ /** Operator-assigned identity of the source instance. */
8
+ instanceId: string;
9
+ /** Instance root, including an optional deployment subpath. HTTPS is required. */
10
+ baseUrl: string;
11
+ /**
12
+ * A site administrator's token, the connection's read-only one, retained
13
+ * only in this reader's memory. It is sent only to `baseUrl`, only in GET
14
+ * requests, and never appears in reports, reasons or errors.
15
+ */
16
+ token: string;
17
+ /** PEM certificates that replace the default trust store for this reader's connections. */
18
+ caCertificates?: string[];
19
+ /** Deadline of one request; defaults to 60 s, at most 300 s. */
20
+ requestTimeoutMs?: number;
21
+ }
22
+ /**
23
+ * Reads the writers of a Gitea source for a freeze proof, with GET requests
24
+ * and a site administrator's token only: every enabled Actions runner, the
25
+ * jobs in progress, the cron tasks and the runs waiting to start. It reports
26
+ * facts only; whether the source is quiescent is the consumer's decision.
27
+ */
28
+ export declare class GiteaQuiescenceReader {
29
+ #private;
30
+ constructor(optionsArg: IGiteaQuiescenceReaderOptions);
31
+ /** Validated, normalized instance root; contains no credentials or API suffix. */
32
+ get baseUrl(): string;
33
+ /**
34
+ * Reads the report. Rejects unless the token belongs to a site
35
+ * administrator (`reader-not-site-admin`) and the forge reports a version;
36
+ * a part whose read does not finish is null, with `<part>: <code>` in
37
+ * `incompleteReasons`. Queued runs past
38
+ * `forgeSourceQuiescenceQueuedRunMaxRecords` are `overBound` with the
39
+ * forge's count and are not read further. An abort rejects with
40
+ * `request-aborted`.
41
+ */
42
+ read(signalArg?: AbortSignal): Promise<IQuiescenceReport>;
43
+ }
44
+ export interface IGiteaFreezeProbeOptions {
45
+ /**
46
+ * The canary credential's token, never the connection's administrator
47
+ * token, retained only in this probe's memory: a restricted,
48
+ * non-administrator account whose only organization is the canary
49
+ * repository's owner and whose only repository is the canary repository.
50
+ * It is sent only to the origins of a run and never appears in outcomes or
51
+ * errors.
52
+ */
53
+ token: string;
54
+ /** The canary repository; its owner is the canary organization, and its generic and container packages carry its name. */
55
+ repository: {
56
+ owner: string;
57
+ name: string;
58
+ };
59
+ /** PEM certificates that replace the default trust store for the probe's connections. */
60
+ caCertificates?: string[];
61
+ /** Deadline of one request, its response body included; defaults to 60 s, at most 300 s. */
62
+ requestTimeoutMs?: number;
63
+ }
64
+ export interface IGiteaFreezeProbeRunOptions {
65
+ /** The network paths to send the canaries through, each at most once; all must reach the same instance. */
66
+ origins: IFreezeOrigin[];
67
+ /** The canaries each origin gets, in order, each at most once: the consumer's canary configuration. */
68
+ canaries: TFreezeCanary[];
69
+ signal?: AbortSignal;
70
+ }
71
+ /**
72
+ * Sends a Gitea source's HTTP freeze canaries for a freeze proof, with the
73
+ * canary credential only. Before any write it reads `/user`, `/user/orgs` and
74
+ * `/user/repos` through every origin of the run and refuses a credential that
75
+ * reaches more than the canary repository (`canary-credential-overbroad`).
76
+ * Each canary targets the canary repository or its packages, and every write
77
+ * carries a fresh nonce. It reports what each canary's own request answered:
78
+ * the status, the `X-Gitea-Freeze` header verbatim and what a 2xx created. It
79
+ * never returns a response body or a credential, never follows a redirect and
80
+ * never judges the marker.
81
+ */
82
+ export declare class GiteaFreezeProbe {
83
+ #private;
84
+ constructor(optionsArg: IGiteaFreezeProbeOptions);
85
+ /**
86
+ * Runs the preflight through every origin, then sends each canary through
87
+ * each origin, origins and canaries in the order given, and returns one
88
+ * outcome per pair. A preflight that does not pass rejects before any
89
+ * canary is sent: `canary-credential-overbroad` when the account is an
90
+ * administrator, is not restricted, or reaches another organization or
91
+ * repository; `canary-credential-mismatch` when it reaches no organization
92
+ * or repository at all; `canary-origin-mismatch` when two origins name a
93
+ * different account or canary repository id; a failed read with its code.
94
+ * An abort rejects with `request-aborted`; every other failure is an
95
+ * outcome's `errorCode`.
96
+ */
97
+ run(optionsArg: IGiteaFreezeProbeRunOptions): Promise<ICanaryOutcome[]>;
98
+ }
99
+ export {};