@vzn/vx-ci 0.0.643 → 0.0.644

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/README.md CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
  Website: <https://vznjs.github.io/vx/> · this plugin: <https://vznjs.github.io/vx/plugins/vx-ci/> · source: <https://github.com/vznjs/vx>
4
4
 
5
- GitHub Actions integration for [`@vzn/vx`](https://vznjs.github.io/vx/) — a
6
- telemetry plugin that writes every `vx run` as a **job summary** on the
7
- workflow run page.
5
+ GitHub Actions and GitLab CI integration for [`@vzn/vx`](https://vznjs.github.io/vx/):
6
+ `github()` writes every `vx run` as a **job summary** on the workflow run
7
+ page and names the run's cache scope; `gitlab()` names the run's cache
8
+ scope on GitLab CI ([below](#gitlab-ci)).
8
9
 
9
10
  ```sh
10
11
  npm install -D @vzn/vx @vzn/vx-ci # or: pnpm add -D -w · yarn add -D (-W on Yarn 1) · bun add -d
@@ -39,8 +40,8 @@ checked and nothing loads, so declaring it unconditionally is safe.
39
40
 
40
41
  The options type is `GithubPluginOptions`; `renderJobSummary` renders the
41
42
  summary lines the plugin posts (the site's CI guide sample is rendered from it).
42
- The package exports `github`, `renderJobSummary` and the types `GithubPluginOptions`
43
- and `FetchFn`; nothing else. `fetchFn`, `append` and `sizeOf` are test seams:
43
+ The package exports `github`, `gitlab`, `renderJobSummary` and the types
44
+ `GithubPluginOptions`, `GitlabPluginOptions` and `FetchFn`; nothing else. `fetchFn`, `append` and `sizeOf` are test seams:
44
45
  they replace the Checks API transport, the summary writer and its size probe.
45
46
 
46
47
  ```ts
@@ -64,7 +65,10 @@ the default keys). A push to, or a pull request from, a branch names
64
65
  fork's pull request names the fork; so does a `pull_request_target` or
65
66
  `workflow_run` run on a PR's behalf (by `GITHUB_EVENT_NAME`, though its
66
67
  ref is main's), and an `issue_comment` on a PR, which names no branch,
67
- `pr-<n>`. A pull request also names its base commit
68
+ `pr-<n>`. A merge queue's run (`merge_group`) is trusted: no scope,
69
+ whatever its `gh-readonly-queue/...` branch, so it reads and writes only
70
+ the default keys (`x-vx-cache-scope: trusted`), compared with
71
+ `merge_group.base_sha`. A pull request also names its base commit
68
72
  (`pull_request.base.sha`), so vx diffs the merge checkout from it with
69
73
  no git call. A scoped run's changed tasks read the scope's remote entry,
70
74
  the rest the default keys, and every task it builds is saved to the
@@ -76,6 +80,33 @@ real boundary is a cache token the server limits, so give PR jobs one
76
80
  that cannot write the default keys (`docs/security.md` § Cache
77
81
  poisoning).
78
82
 
83
+ ## GitLab CI
84
+
85
+ ```ts
86
+ // .vx/workspace.ts
87
+ import { defineWorkspace } from '@vzn/vx/config'
88
+ import { github, gitlab } from '@vzn/vx-ci'
89
+
90
+ export default defineWorkspace({ plugins: [github(), gitlab()] })
91
+ ```
92
+
93
+ On GitLab CI (`GITLAB_CI=true`) `gitlab()`'s `config` stage names the
94
+ run's branch through `ctx.setBranch`, from GitLab's predefined
95
+ variables, as `github()` does on Actions. The default branch
96
+ (`CI_DEFAULT_BRANCH`) names no scope. Another branch or a tag (`CI_COMMIT_TAG`) names
97
+ `<project path>:<ref>` (`CI_PROJECT_PATH`, `CI_COMMIT_REF_NAME`), so a
98
+ subgroup's project is `group/sub/app:feat-x`, the scope a laptop on
99
+ `feat-x` derives. A merge request pipeline names its source branch
100
+ (`CI_MERGE_REQUEST_SOURCE_BRANCH_NAME`) in the project it lives in (a
101
+ fork's `CI_MERGE_REQUEST_SOURCE_PROJECT_PATH`) and its diff base
102
+ (`CI_MERGE_REQUEST_DIFF_BASE_SHA`), so vx diffs from it with no git
103
+ call. A merge train's pipeline (`CI_MERGE_REQUEST_EVENT_TYPE=merge_train`)
104
+ builds what lands on the target, so it is trusted: no scope, only the
105
+ default keys. `gitlab({ cacheScope: false })` (`GitlabPluginOptions`) turns it off;
106
+ `VX_CACHE_SCOPE` overrides it. Anywhere else `gitlab()` returns
107
+ `undefined`, so declaring both plugins is safe. Merge request comments
108
+ and reports are not part of it yet.
109
+
79
110
  ## How it works
80
111
 
81
112
  `github()` contributes one observe-only telemetry sink through vx's
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@vzn/vx-ci",
3
- "version": "0.0.643",
3
+ "version": "0.0.644",
4
4
  "license": "MIT",
5
- "description": "GitHub Actions integration for vx — a telemetry plugin that writes the run as a GitHub Actions job summary.",
5
+ "description": "GitHub Actions and GitLab CI integration for vx — a job summary on Actions and the branch cache scope on both.",
6
6
  "keywords": [
7
7
  "vx",
8
8
  "vx-plugin",
9
9
  "monorepo",
10
10
  "github-actions",
11
+ "gitlab-ci",
11
12
  "job-summary",
12
13
  "checks"
13
14
  ],
@@ -33,7 +34,7 @@
33
34
  "access": "public"
34
35
  },
35
36
  "peerDependencies": {
36
- "@vzn/vx": "^0.0.643"
37
+ "@vzn/vx": "^0.0.644"
37
38
  },
38
39
  "repository": {
39
40
  "type": "git",
@@ -2,7 +2,7 @@
2
2
  // `setBranch`: the default branch writes the default keys, a branch its own
3
3
  // scope, the one a laptop on that branch reads (owner, 2026-10-17).
4
4
  import { readFile } from 'node:fs/promises'
5
- import { branchCacheScope } from '@vzn/vx'
5
+ import { type BranchFacts, branchCacheScope } from '@vzn/vx'
6
6
 
7
7
  /**
8
8
  * What an Actions run knows of its branch (owner, 2026-10-17). `scope`:
@@ -15,15 +15,21 @@ import { branchCacheScope } from '@vzn/vx'
15
15
  * `workflow_run` a PR started) is the PR's, whatever `GITHUB_REF` says: a
16
16
  * workflow that checks out the PR's head there would otherwise write the
17
17
  * default keys. `issue_comment` names no branch, so it writes `pr-<n>`.
18
+ * A merge queue's run (`merge_group`) is trusted (owner, 2026-10-18): no
19
+ * scope, only the default keys, compared with `merge_group.base_sha`.
18
20
  * `base`: a pull request's base commit (`pull_request.base.sha`), what its
19
21
  * merge checkout is compared with, no git call needed.
20
22
  */
21
23
  export async function githubBranch(
22
24
  env: Readonly<Record<string, string | undefined>>,
23
- ): Promise<{ scope?: string; base?: string }> {
25
+ ): Promise<BranchFacts> {
24
26
  if (env['GITHUB_ACTIONS'] !== 'true') return {}
25
27
  const ref = env['GITHUB_REF'] ?? ''
26
28
  const event = await eventOf(env['GITHUB_EVENT_PATH'])
29
+ if (env['GITHUB_EVENT_NAME'] === 'merge_group') {
30
+ const base = stringOf(event?.merge_group?.base_sha)
31
+ return { trusted: true, ...(base !== undefined ? { base } : {}) }
32
+ }
27
33
  const repo = stringOf(event?.repository?.full_name) ?? env['GITHUB_REPOSITORY']
28
34
  const base = stringOf(event?.pull_request?.base?.sha)
29
35
  const withBase = (scope: string | undefined): { scope?: string; base?: string } => ({
@@ -54,6 +60,7 @@ interface ActionsEvent {
54
60
  base?: { sha?: unknown }
55
61
  }
56
62
  issue?: { number?: unknown; pull_request?: unknown }
63
+ merge_group?: { base_sha?: unknown }
57
64
  workflow_run?: {
58
65
  event?: unknown
59
66
  head_branch?: unknown
package/src/gitlab.ts ADDED
@@ -0,0 +1,63 @@
1
+ // The `gitlab()` plugin (owner, 2026-10-18): on GitLab CI it names the
2
+ // run's branch through `setBranch`, as `github()` does on Actions. Off
3
+ // GitLab CI (`GITLAB_CI` not 'true') the factory returns undefined, which
4
+ // `plugins` skips, so declaring it costs nothing on a laptop.
5
+ import {
6
+ type BranchFacts,
7
+ branchCacheScope,
8
+ definePlugin,
9
+ type PluginOptionKinds,
10
+ refuseUnknownOptions,
11
+ type VxPlugin,
12
+ } from '@vzn/vx'
13
+
14
+ export interface GitlabPluginOptions {
15
+ /**
16
+ * Name the run's cache scope and base commit from GitLab's predefined
17
+ * variables: the default branch and a merge train read and write the
18
+ * default keys, any other branch `<project path>:<branch>`, and a merge
19
+ * request is compared with its diff base. `VX_CACHE_SCOPE` beats it.
20
+ * Default: on; `false` opts out.
21
+ */
22
+ cacheScope?: boolean
23
+ }
24
+
25
+ const GITLAB_PLUGIN_KEYS: PluginOptionKinds<GitlabPluginOptions> = {
26
+ cacheScope: 'boolean',
27
+ }
28
+
29
+ export function gitlab(options: GitlabPluginOptions = {}): VxPlugin | undefined {
30
+ if (process.env['GITLAB_CI'] !== 'true') return undefined
31
+ refuseUnknownOptions('gitlab()', options, GITLAB_PLUGIN_KEYS)
32
+ return definePlugin(import.meta, {
33
+ config(_workspace, ctx) {
34
+ if (options.cacheScope === false) return
35
+ ctx.setBranch(gitlabBranch(process.env))
36
+ },
37
+ })
38
+ }
39
+
40
+ /**
41
+ * What a GitLab CI job knows of its branch. A merge request pipeline names
42
+ * its source branch in the project it lives in (a fork's own,
43
+ * `CI_MERGE_REQUEST_SOURCE_PROJECT_PATH`) and its diff base; a merge
44
+ * train's pipeline builds what lands on the target, so it is trusted; a
45
+ * branch or tag pipeline names its ref, none on the default branch.
46
+ */
47
+ function gitlabBranch(env: Readonly<Record<string, string | undefined>>): BranchFacts {
48
+ const value = (key: string): string | undefined => env[key] || undefined
49
+ const base = value('CI_MERGE_REQUEST_DIFF_BASE_SHA')
50
+ const withBase = (facts: BranchFacts): BranchFacts =>
51
+ base === undefined ? facts : { ...facts, base }
52
+ if (value('CI_MERGE_REQUEST_EVENT_TYPE') === 'merge_train') return withBase({ trusted: true })
53
+ const project = value('CI_PROJECT_PATH')
54
+ const source = value('CI_MERGE_REQUEST_SOURCE_BRANCH_NAME')
55
+ if (source !== undefined) {
56
+ const from = value('CI_MERGE_REQUEST_SOURCE_PROJECT_PATH') ?? project
57
+ return withBase(from === undefined ? {} : { scope: branchCacheScope(from, source) })
58
+ }
59
+ const ref = value('CI_COMMIT_REF_NAME')
60
+ if (project === undefined || ref === undefined) return {}
61
+ if (value('CI_COMMIT_TAG') === undefined && ref === value('CI_DEFAULT_BRANCH')) return {}
62
+ return { scope: branchCacheScope(project, ref) }
63
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- // Public API for @vzn/vx-ci — the GitHub Actions integration plugin.
1
+ // Public API for @vzn/vx-ci — the GitHub Actions and GitLab CI plugins.
2
2
  //
3
3
  // Usage in .vx/workspace.ts:
4
4
  // import { defineWorkspace } from '@vzn/vx'
@@ -9,6 +9,7 @@
9
9
  // appends a job summary: verdict, stats, failures, and the per-task table.
10
10
  // Anywhere else the plugin declines and costs nothing.
11
11
  export { github, type GithubPluginOptions } from './plugin.js'
12
+ export { gitlab, type GitlabPluginOptions } from './gitlab.js'
12
13
  export { renderJobSummary } from './summary.js'
13
14
 
14
15
  // The Checks API helpers are the plugin's own; only the transport type