@shipfox/api-definitions 9.0.3 → 9.2.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipfox/api-definitions",
3
3
  "license": "MIT",
4
- "version": "9.0.3",
4
+ "version": "9.2.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -27,10 +27,10 @@
27
27
  "yaml": "^2.8.3",
28
28
  "zod": "^4.4.3",
29
29
  "@shipfox/api-agent-dto": "9.0.2",
30
- "@shipfox/api-auth-context": "9.0.3",
30
+ "@shipfox/api-auth-context": "9.2.0",
31
31
  "@shipfox/api-definitions-dto": "9.0.2",
32
32
  "@shipfox/api-integration-core-dto": "9.0.2",
33
- "@shipfox/api-projects-dto": "9.0.2",
33
+ "@shipfox/api-projects-dto": "9.2.0",
34
34
  "@shipfox/api-secrets-dto": "9.0.2",
35
35
  "@shipfox/config": "1.2.4",
36
36
  "@shipfox/expression": "1.1.5",
package/src/config.ts CHANGED
@@ -6,6 +6,10 @@ export const config = createConfig({
6
6
  desc: 'Default runner label(s) applied to workflow jobs that do not declare a "runner" at the job or workflow level. Set it to a comma-separated list, for example ubuntu-latest or ubuntu-latest,node-22. Leave it empty to require every workflow job to declare runner labels explicitly; with no value set, a job without a runner fails definition validation.',
7
7
  default: '',
8
8
  }),
9
+ DEFINITION_WORKFLOW_PATH: str({
10
+ desc: 'Repository-relative path that contains workflow YAML files. Set a different path for each Shipfox instance when staging and production share a repository.',
11
+ default: '.shipfox/workflows/',
12
+ }),
9
13
  });
10
14
 
11
15
  export function parseDefinitionDefaultRunnerLabels(value: string): readonly string[] {
@@ -30,3 +34,5 @@ export function parseDefinitionDefaultRunnerLabels(value: string): readonly stri
30
34
  export const definitionDefaultRunnerLabels = parseDefinitionDefaultRunnerLabels(
31
35
  config.DEFINITION_DEFAULT_RUNNER_LABEL,
32
36
  );
37
+
38
+ export const definitionWorkflowPath = config.DEFINITION_WORKFLOW_PATH;
package/src/core/index.ts CHANGED
@@ -6,6 +6,7 @@ export {
6
6
  export {parseDefinition} from './parse-definition.js';
7
7
  export {
8
8
  classifySyncFailure,
9
+ DEFAULT_WORKFLOW_PATH,
9
10
  type DiscoverWorkflowFilesParams,
10
11
  discoverWorkflowFiles,
11
12
  type FetchAndParseWorkflowsParams,
@@ -18,7 +19,6 @@ export {
18
19
  type SyncFailureClassification,
19
20
  type SyncSourceContext,
20
21
  UNRESOLVED_SYNC_REF,
21
- WORKFLOW_PREFIX,
22
22
  } from './sync-definitions.js';
23
23
  export {DEFAULT_RUN_TIMEOUT_MS} from './workflow-model/constants.js';
24
24
  export {normalizeWorkflowDocument} from './workflow-model/index.js';
@@ -146,6 +146,27 @@ describe('discoverWorkflowFiles', () => {
146
146
  expect(result.paths).toEqual(['.shipfox/workflows/ci.yml', '.shipfox/workflows/deploy.yaml']);
147
147
  });
148
148
 
149
+ it('lists workflows under the configured repository path', async () => {
150
+ const listFiles = vi.fn(() =>
151
+ Promise.resolve({
152
+ files: [{path: '.shipfox/staging/workflows/ci.yml', type: 'file' as const, size: 64}],
153
+ nextCursor: null,
154
+ }),
155
+ );
156
+
157
+ const result = await discoverWorkflowFiles({
158
+ ...baseContext,
159
+ ref: 'main',
160
+ workflowPath: '.shipfox/staging/workflows/',
161
+ sourceControl: sourceControl({listFiles}),
162
+ });
163
+
164
+ expect(listFiles).toHaveBeenCalledWith(
165
+ expect.objectContaining({prefix: '.shipfox/staging/workflows/'}),
166
+ );
167
+ expect(result.paths).toEqual(['.shipfox/staging/workflows/ci.yml']);
168
+ });
169
+
149
170
  it('throws no-workflow-files when nothing matches the yaml extensions', async () => {
150
171
  const result = discoverWorkflowFiles({
151
172
  ...baseContext,
@@ -163,6 +184,21 @@ describe('discoverWorkflowFiles', () => {
163
184
  await expect(result).rejects.toMatchObject({code: 'no-workflow-files'});
164
185
  });
165
186
 
187
+ it('includes the configured repository path when no workflow files are found', async () => {
188
+ const result = discoverWorkflowFiles({
189
+ ...baseContext,
190
+ ref: 'main',
191
+ workflowPath: '.shipfox/production/workflows/',
192
+ sourceControl: sourceControl({
193
+ listFiles: vi.fn(() => Promise.resolve({files: [], nextCursor: null})),
194
+ }),
195
+ });
196
+
197
+ await expect(result).rejects.toThrow(
198
+ 'No workflow files were found under .shipfox/production/workflows/',
199
+ );
200
+ });
201
+
166
202
  it('throws too-many-files when the listing reports more pages', async () => {
167
203
  const result = discoverWorkflowFiles({
168
204
  ...baseContext,
@@ -11,7 +11,7 @@ import {hasAgentStepIntegrations} from './has-agent-step-integrations.js';
11
11
  import type {DefinitionsSourceControl} from './integrations.js';
12
12
  import {parseDefinition} from './parse-definition.js';
13
13
 
14
- export const WORKFLOW_PREFIX = '.shipfox/workflows/';
14
+ export const DEFAULT_WORKFLOW_PATH = '.shipfox/workflows/';
15
15
  export const MAX_WORKFLOW_FILES = 100;
16
16
  export const MAX_WORKFLOW_FILE_BYTES = 1_000_000;
17
17
  export const FILE_FETCH_CONCURRENCY = 4;
@@ -39,17 +39,19 @@ export async function resolveSyncSource(params: SyncSourceContext): Promise<Reso
39
39
 
40
40
  export interface DiscoverWorkflowFilesParams extends SyncSourceContext {
41
41
  ref: string;
42
+ workflowPath?: string | undefined;
42
43
  }
43
44
 
44
45
  export async function discoverWorkflowFiles(
45
46
  params: DiscoverWorkflowFilesParams,
46
47
  ): Promise<{paths: string[]}> {
48
+ const workflowPath = params.workflowPath ?? DEFAULT_WORKFLOW_PATH;
47
49
  const page = await params.sourceControl.listFiles({
48
50
  workspaceId: params.workspaceId,
49
51
  connectionId: params.sourceConnectionId,
50
52
  externalRepositoryId: params.sourceExternalRepositoryId,
51
53
  ref: params.ref,
52
- prefix: WORKFLOW_PREFIX,
54
+ prefix: workflowPath,
53
55
  limit: MAX_WORKFLOW_FILES,
54
56
  });
55
57
  if (page.nextCursor) {
@@ -65,7 +67,7 @@ export async function discoverWorkflowFiles(
65
67
  if (paths.length === 0) {
66
68
  throw new DefinitionSyncPermanentError(
67
69
  'no-workflow-files',
68
- `No workflow files were found under ${WORKFLOW_PREFIX}`,
70
+ `No workflow files were found under ${workflowPath}`,
69
71
  );
70
72
  }
71
73
 
package/src/index.ts CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  onProjectSourceCommitObserved,
25
25
  } from '#presentation/subscribers/index.js';
26
26
  import {createDefinitionSyncActivities, DEFINITIONS_TASK_QUEUE} from '#temporal/index.js';
27
+ import {definitionWorkflowPath} from './config.js';
27
28
 
28
29
  export type {
29
30
  WorkflowDefinition,
@@ -79,7 +80,10 @@ export function createDefinitionsModule({
79
80
  {
80
81
  taskQueue: DEFINITIONS_TASK_QUEUE,
81
82
  workflowsPath,
82
- activities: () => createDefinitionSyncActivities(sourceControl, agent, integrations),
83
+ activities: () =>
84
+ createDefinitionSyncActivities(sourceControl, agent, integrations, {
85
+ workflowPath: definitionWorkflowPath,
86
+ }),
83
87
  workflows: [],
84
88
  },
85
89
  ],
@@ -180,6 +180,35 @@ describe('definition sync activities', () => {
180
180
  });
181
181
  });
182
182
 
183
+ describe('discoverDefinitionWorkflows', () => {
184
+ it('uses the configured workflow path', async () => {
185
+ const source = sourceControl({
186
+ listFiles: vi.fn(() =>
187
+ Promise.resolve({
188
+ files: [{path: '.shipfox/staging/workflows/ci.yml', type: 'file' as const, size: 64}],
189
+ nextCursor: null,
190
+ }),
191
+ ),
192
+ });
193
+ const activities = createDefinitionSyncActivities(source, agent, undefined, {
194
+ workflowPath: '.shipfox/staging/workflows/',
195
+ });
196
+
197
+ const result = await activities.discoverDefinitionWorkflows({
198
+ projectId,
199
+ workspaceId: crypto.randomUUID(),
200
+ sourceConnectionId,
201
+ sourceExternalRepositoryId: 'gitea:gitea-owner/platform',
202
+ sourceRef: 'main',
203
+ });
204
+
205
+ expect(result.paths).toEqual(['.shipfox/staging/workflows/ci.yml']);
206
+ expect(source.listFiles).toHaveBeenCalledWith(
207
+ expect.objectContaining({prefix: '.shipfox/staging/workflows/'}),
208
+ );
209
+ });
210
+ });
211
+
183
212
  describe('fetchAndApplyDefinitionWorkflows', () => {
184
213
  it('upserts workflow definitions and soft-deletes orphans', async () => {
185
214
  const activities = createDefinitionSyncActivities(sourceControl(), agent);
@@ -52,14 +52,22 @@ export interface FetchAndApplyActivityResult {
52
52
  deletedCount: number;
53
53
  }
54
54
 
55
+ export interface DefinitionSyncActivityOptions {
56
+ workflowPath: string;
57
+ }
58
+
55
59
  export function createDefinitionSyncActivities(
56
60
  sourceControl: DefinitionsSourceControl,
57
61
  agent: AgentInterModuleClient,
58
62
  integrations?: IntegrationsModuleClient | undefined,
63
+ options?: DefinitionSyncActivityOptions | undefined,
59
64
  ) {
60
65
  return {
61
66
  prepareDefinitionSync: createPrepareDefinitionSyncActivity(sourceControl),
62
- discoverDefinitionWorkflows: createDiscoverDefinitionWorkflowsActivity(sourceControl),
67
+ discoverDefinitionWorkflows: createDiscoverDefinitionWorkflowsActivity(
68
+ sourceControl,
69
+ options?.workflowPath,
70
+ ),
63
71
  fetchAndApplyDefinitionWorkflows: createFetchAndApplyActivity(
64
72
  sourceControl,
65
73
  agent,
@@ -94,7 +102,10 @@ function createPrepareDefinitionSyncActivity(sourceControl: DefinitionsSourceCon
94
102
  };
95
103
  }
96
104
 
97
- function createDiscoverDefinitionWorkflowsActivity(sourceControl: DefinitionsSourceControl) {
105
+ function createDiscoverDefinitionWorkflowsActivity(
106
+ sourceControl: DefinitionsSourceControl,
107
+ workflowPath?: string | undefined,
108
+ ) {
98
109
  return async function discoverDefinitionWorkflows(
99
110
  input: SyncRefScopedInput,
100
111
  ): Promise<DiscoverWorkflowsActivityResult> {
@@ -103,6 +114,7 @@ function createDiscoverDefinitionWorkflowsActivity(sourceControl: DefinitionsSou
103
114
  ...input,
104
115
  ref: input.sourceCommitSha ?? input.sourceRef,
105
116
  sourceControl,
117
+ workflowPath,
106
118
  });
107
119
  });
108
120
  };