@bakers-build/addon-deployment 0.0.0-stage → 0.1.0-next.38
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/LICENSE +21 -0
- package/README.md +4 -2
- package/dist/src/deployment.d.ts +51 -0
- package/dist/src/deployment.js +124 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +34 -0
- package/dist/src/preset.d.ts +2 -0
- package/dist/src/preset.js +8 -0
- package/package.json +27 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 keytec GmbH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @bakers-build/addon-deployment
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
bakers.build deployment command plugin: `bakers deployment tickets` — the release batch computed from a git-log range.
|
|
4
|
+
|
|
5
|
+
Part of the bakers.build CLI. Install the meta package `@bakers-build/bakers` to get the `bakers` CLI with all addons. Source: https://git.key-tec.de/bakers-build/bakers-build (bakers-cli/).
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { BbRemote } from '@bakers-build/conductor/contract';
|
|
2
|
+
import type { Command } from 'commander';
|
|
3
|
+
export interface GatherResult {
|
|
4
|
+
range: string;
|
|
5
|
+
resolved: Array<{
|
|
6
|
+
identifier: string;
|
|
7
|
+
uuid: string;
|
|
8
|
+
title: string;
|
|
9
|
+
}>;
|
|
10
|
+
unresolved: string[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Deduped ticket identifiers from a git log, in first-seen order — for any
|
|
14
|
+
* project key, not just `bakers.build` (BB-392 AC-2).
|
|
15
|
+
*
|
|
16
|
+
* The parse admits *keys*, not tokens, in two passes: a key is admitted where
|
|
17
|
+
* it stands in one of the anchored positions above, and every identifier whose
|
|
18
|
+
* key was admitted is then collected — anchored or bare.
|
|
19
|
+
*
|
|
20
|
+
* The second pass is not optional: of the last 300 commits on `develop` only
|
|
21
|
+
* two carry a `ref:` trailer, the rest name the ticket in the subject, and a
|
|
22
|
+
* bare `BB-135` in a body is a real reference. What the rule gives up is one
|
|
23
|
+
* residual case — a delta in which a key appears *only* in bare mid-prose form,
|
|
24
|
+
* anchored nowhere, yields nothing for that key. That is the deliberate trade:
|
|
25
|
+
* such a token is indistinguishable from `AC-1` without hardcoding a project
|
|
26
|
+
* key, which is the defect this replaces. The cheap direction is the other one,
|
|
27
|
+
* a wrongly admitted key, because it surfaces in `unresolved` rather than as a
|
|
28
|
+
* wrong ticket.
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseIdentifiers(gitLog: string): string[];
|
|
31
|
+
/**
|
|
32
|
+
* Compute the release batch from a git-log delta: parse ticket identifiers and
|
|
33
|
+
* resolve each to a ticket. Unresolved identifiers are reported, never fatal
|
|
34
|
+
* (the resolver returning null is not an error) — BB-153 AC-2.
|
|
35
|
+
*/
|
|
36
|
+
export declare function gatherBatch(opts: {
|
|
37
|
+
sinceRef: string;
|
|
38
|
+
toRef: string;
|
|
39
|
+
gitLog: string;
|
|
40
|
+
resolve: (id: string) => Promise<{
|
|
41
|
+
uuid: string;
|
|
42
|
+
title: string;
|
|
43
|
+
} | null>;
|
|
44
|
+
}): Promise<GatherResult>;
|
|
45
|
+
/**
|
|
46
|
+
* Register the `bakers deployment` command group. `tickets` computes the release's
|
|
47
|
+
* tickets (git-log identifier parse + resolve over the given range) and prints
|
|
48
|
+
* them as JSON — pure compute + report, it does not write `referenced_tickets`
|
|
49
|
+
* (the prepare-deployment skill does, via the documented dropsh path).
|
|
50
|
+
*/
|
|
51
|
+
export declare function registerDeployment(program: Command, resolveRemote: () => Promise<BbRemote>, resolveProject: () => Promise<string>, cwd: () => string): void;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { exec } from '@bakers-build/core';
|
|
2
|
+
/**
|
|
3
|
+
* An identifier's shape: a project key plus its number. A key is validated as
|
|
4
|
+
* `/^[A-Z][A-Z0-9]{1,9}$/` and never contains a hyphen (`BbProject`), so this
|
|
5
|
+
* is the whole grammar — and it is also the shape of prose like `AC-1`,
|
|
6
|
+
* `UTF-8` or `SHA-256`, which is why matching it alone is not enough.
|
|
7
|
+
*/
|
|
8
|
+
const IDENTIFIER = '[A-Z][A-Z0-9]{1,9}-\\d+';
|
|
9
|
+
/**
|
|
10
|
+
* What may precede a subject: the `%x1e` record separator and the abbreviated
|
|
11
|
+
* or full commit hash that `bakers deployment tickets` asks `git log` to print in
|
|
12
|
+
* front of `%s`. Both optional, so a bare subject line anchors too.
|
|
13
|
+
*/
|
|
14
|
+
const SUBJECT_HEAD = '^\\x1e?(?:[0-9a-f]{2,40} )?';
|
|
15
|
+
/**
|
|
16
|
+
* The three positions in which an identifier can only be a ticket reference,
|
|
17
|
+
* and which therefore admit its project key:
|
|
18
|
+
*
|
|
19
|
+
* 1. the `ref:` trailer the commit convention prescribes — `ref:SHOP-42`;
|
|
20
|
+
* 2. a conventional-commit scope — `feat(BB-134): …`;
|
|
21
|
+
* 3. the head of a subject line — `BB-375: …`, the legacy form 298 of the
|
|
22
|
+
* last 300 commits on `develop` use.
|
|
23
|
+
*
|
|
24
|
+
* Positions 2 and 3 are deliberately anchored to the head of a line rather than
|
|
25
|
+
* to "any token before a colon" or "any parenthesised token": a body bullet
|
|
26
|
+
* `- AC-2: …` and a body line `(AC-5): parent = …` are exactly those two
|
|
27
|
+
* shapes, and both occur in this repository's real history. Admitting `AC` from
|
|
28
|
+
* them puts six phantom identifiers into a 300-commit release batch.
|
|
29
|
+
*/
|
|
30
|
+
const ANCHORED = new RegExp([
|
|
31
|
+
`ref:[ \\t]*(${IDENTIFIER})\\b`,
|
|
32
|
+
`${SUBJECT_HEAD}[a-z]+!?\\((${IDENTIFIER})\\):`,
|
|
33
|
+
`${SUBJECT_HEAD}(${IDENTIFIER}):`,
|
|
34
|
+
].join('|'), 'gm');
|
|
35
|
+
/**
|
|
36
|
+
* Deduped ticket identifiers from a git log, in first-seen order — for any
|
|
37
|
+
* project key, not just `bakers.build` (BB-392 AC-2).
|
|
38
|
+
*
|
|
39
|
+
* The parse admits *keys*, not tokens, in two passes: a key is admitted where
|
|
40
|
+
* it stands in one of the anchored positions above, and every identifier whose
|
|
41
|
+
* key was admitted is then collected — anchored or bare.
|
|
42
|
+
*
|
|
43
|
+
* The second pass is not optional: of the last 300 commits on `develop` only
|
|
44
|
+
* two carry a `ref:` trailer, the rest name the ticket in the subject, and a
|
|
45
|
+
* bare `BB-135` in a body is a real reference. What the rule gives up is one
|
|
46
|
+
* residual case — a delta in which a key appears *only* in bare mid-prose form,
|
|
47
|
+
* anchored nowhere, yields nothing for that key. That is the deliberate trade:
|
|
48
|
+
* such a token is indistinguishable from `AC-1` without hardcoding a project
|
|
49
|
+
* key, which is the defect this replaces. The cheap direction is the other one,
|
|
50
|
+
* a wrongly admitted key, because it surfaces in `unresolved` rather than as a
|
|
51
|
+
* wrong ticket.
|
|
52
|
+
*/
|
|
53
|
+
export function parseIdentifiers(gitLog) {
|
|
54
|
+
const admitted = new Set();
|
|
55
|
+
for (const [, trailer, scope, subject] of gitLog.matchAll(ANCHORED)) {
|
|
56
|
+
const identifier = trailer ?? scope ?? subject;
|
|
57
|
+
if (identifier !== undefined) {
|
|
58
|
+
admitted.add(keyOf(identifier));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
const out = [];
|
|
63
|
+
for (const [identifier] of gitLog.matchAll(new RegExp(`\\b${IDENTIFIER}\\b`, 'g'))) {
|
|
64
|
+
if (admitted.has(keyOf(identifier)) && !seen.has(identifier)) {
|
|
65
|
+
seen.add(identifier);
|
|
66
|
+
out.push(identifier);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
/** An identifier's project key — everything left of its `-<number>`. */
|
|
72
|
+
function keyOf(identifier) {
|
|
73
|
+
return identifier.slice(0, identifier.indexOf('-'));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Compute the release batch from a git-log delta: parse ticket identifiers and
|
|
77
|
+
* resolve each to a ticket. Unresolved identifiers are reported, never fatal
|
|
78
|
+
* (the resolver returning null is not an error) — BB-153 AC-2.
|
|
79
|
+
*/
|
|
80
|
+
export async function gatherBatch(opts) {
|
|
81
|
+
const resolved = [];
|
|
82
|
+
const unresolved = [];
|
|
83
|
+
for (const identifier of parseIdentifiers(opts.gitLog)) {
|
|
84
|
+
const hit = await opts.resolve(identifier);
|
|
85
|
+
if (hit) {
|
|
86
|
+
resolved.push({ identifier, ...hit });
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
unresolved.push(identifier);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { range: `${opts.sinceRef}..${opts.toRef}`, resolved, unresolved };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Register the `bakers deployment` command group. `tickets` computes the release's
|
|
96
|
+
* tickets (git-log identifier parse + resolve over the given range) and prints
|
|
97
|
+
* them as JSON — pure compute + report, it does not write `referenced_tickets`
|
|
98
|
+
* (the prepare-deployment skill does, via the documented dropsh path).
|
|
99
|
+
*/
|
|
100
|
+
export function registerDeployment(program, resolveRemote, resolveProject, cwd) {
|
|
101
|
+
const dep = program
|
|
102
|
+
.command('deployment')
|
|
103
|
+
.description('release / deployment helpers');
|
|
104
|
+
dep
|
|
105
|
+
.command('tickets')
|
|
106
|
+
.description("the release's tickets (git-log identifier parse + resolve over a range) as JSON")
|
|
107
|
+
.requiredOption('--since <ref>', "the env's currently-deployed ref (git range start)")
|
|
108
|
+
.requiredOption('--to <ref>', 'the post-merge env branch tip (git range end)')
|
|
109
|
+
.action(async (opts) => {
|
|
110
|
+
// Prefix each commit with a record separator (%x1e) so one commit's body
|
|
111
|
+
// cannot run into the next commit's header line; the identifier parse is
|
|
112
|
+
// separator-agnostic, but the boundary keeps the log unambiguous.
|
|
113
|
+
const gitLog = await exec('git', ['log', '--format=%x1e%H %s%n%b', `${opts.since}..${opts.to}`], { cwd: cwd() });
|
|
114
|
+
const remote = await resolveRemote();
|
|
115
|
+
const project = await resolveProject();
|
|
116
|
+
const result = await gatherBatch({
|
|
117
|
+
sinceRef: opts.since,
|
|
118
|
+
toRef: opts.to,
|
|
119
|
+
gitLog,
|
|
120
|
+
resolve: (id) => remote.resolveTicketByIdentifier(project, id),
|
|
121
|
+
});
|
|
122
|
+
console.log(JSON.stringify(result));
|
|
123
|
+
});
|
|
124
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type BbCommandPlugin } from '@bakers-build/core';
|
|
2
|
+
/** The `deployment` command plugin the host mounts (`bakers deployment …`). */
|
|
3
|
+
declare const deploymentCommandPlugin: BbCommandPlugin;
|
|
4
|
+
export default deploymentCommandPlugin;
|
|
5
|
+
export { type GatherResult, gatherBatch, parseIdentifiers, registerDeployment, } from './deployment.js';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { composeConductorConfig, loadConductorConfig, selectRemote, } from '@bakers-build/conductor';
|
|
2
|
+
import { loadBbConfig, resolveConfigPath, } from '@bakers-build/core';
|
|
3
|
+
import { registerDeployment } from './deployment.js';
|
|
4
|
+
// BB-224 (Finding 7): `deployment` is its own COMMAND-surface addon, no longer
|
|
5
|
+
// a side-registration of the `conductor` command plugin. The release-batch
|
|
6
|
+
// gathering (`src/deployment.ts`) was already fully dependency-inverted — it
|
|
7
|
+
// takes the remote, the project and the cwd as thunks — so this module only has
|
|
8
|
+
// to reproduce the thunks the conductor command plugin used to pass, over the
|
|
9
|
+
// same split-config resolution:
|
|
10
|
+
// engine half `conductor.config.js` via `loadConductorConfig`
|
|
11
|
+
// connection half `bakers.config.js` via `loadBbConfig`
|
|
12
|
+
// composed by `composeConductorConfig`, with the remote picked by `selectRemote`.
|
|
13
|
+
//
|
|
14
|
+
// The `@bakers-build/conductor` import is the package MAIN (engine + config loader),
|
|
15
|
+
// not the runtime-light `./contract` subpath — that cost is paid only when a
|
|
16
|
+
// `bakers deployment …` subcommand actually dispatches, because the host mounts
|
|
17
|
+
// this module lazily (the `./preset` accumulator is what `bakers --help` reads).
|
|
18
|
+
/** Resolve + compose the full conductor config (engine + connection halves). */
|
|
19
|
+
async function resolveConfig(host) {
|
|
20
|
+
const engine = await loadConductorConfig(resolveConfigPath());
|
|
21
|
+
const connection = await loadBbConfig(host, {});
|
|
22
|
+
return composeConductorConfig(engine, connection);
|
|
23
|
+
}
|
|
24
|
+
/** The `deployment` command plugin the host mounts (`bakers deployment …`). */
|
|
25
|
+
const deploymentCommandPlugin = {
|
|
26
|
+
kind: 'command',
|
|
27
|
+
name: 'deployment',
|
|
28
|
+
describe: 'release / deployment helpers',
|
|
29
|
+
register(program, host) {
|
|
30
|
+
registerDeployment(program, async () => selectRemote(await resolveConfig(host)), async () => (await resolveConfig(host)).project, () => process.cwd());
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
export default deploymentCommandPlugin;
|
|
34
|
+
export { gatherBatch, parseIdentifiers, registerDeployment, } from './deployment.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bakers-build/addon-deployment",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0-next.38",
|
|
4
|
+
"description": "bakers.build deployment command plugin: `bakers deployment tickets` — the release batch computed from a git-log range.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/src/index.js",
|
|
9
|
+
"./preset": "./dist/src/preset.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist/src"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://git.key-tec.de/bakers-build/bakers-build.git",
|
|
20
|
+
"directory": "bakers-cli/addons/deployment"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"commander": "^12.1.0"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@bakers-build/conductor": "^0.1.0-next.38",
|
|
27
|
+
"@bakers-build/core": "^0.1.0-next.38"
|
|
28
|
+
}
|
|
29
|
+
}
|