Skip to content

Commit dfe0f87

Browse files
committed
Merge branch 'develop' into phale/W-21567195-fix-org-logout
2 parents e3c9530 + 22d2cbb commit dfe0f87

11 files changed

Lines changed: 139 additions & 28 deletions

File tree

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
name: feature-branch
3+
description: Create feature branches for all work. Use when creating branches, checking out, or pushing. Prevents accidental push to develop.
4+
---
5+
6+
# Feature Branch
7+
8+
All work must be on feature branches. Never commit directly to develop or main.
9+
10+
## Do
11+
12+
```bash
13+
git fetch origin develop
14+
git checkout develop
15+
git pull
16+
git checkout -b feature/W-XXXXX
17+
# ... work, commit ...
18+
git push -u origin feature/W-XXXXX
19+
```
20+
21+
Or, branch from remote without tracking it:
22+
23+
```bash
24+
git fetch origin develop
25+
git checkout -b feature/W-XXXXX origin/develop --no-track
26+
```
27+
28+
## Don't
29+
30+
**Never** `git checkout -b feature/W-XXXXX origin/develop` without `--no-track`.
31+
32+
That sets the new branch to track `origin/develop`. A bare `git push` would then push to develop instead of creating a remote feature branch.
33+
34+
## Summary
35+
36+
- All work on feature branches
37+
- Use `--no-track` when branching from `origin/<base>`, or branch from local `<base>` after pull
38+
- Always push with explicit branch: `git push -u origin feature/W-XXXXX`

.claude/skills/pr-draft/SKILL.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Draft PR titles and bodies per salesforcedx-vscode conventions. Requires a Gus w
1616
1. If branch name contains `W-XXXXX`, confirm it exists in GUS, resembles the work done on the branch, and use that
1717
2. Else ask: "Do you have a Gus work item (W-XXXXX) for this PR?"
1818
3. If yes → try to find it using [gus-cli/SKILL.md](../gus-cli/SKILL.md). Confirm with the user that you got it right, or ask them to choose if several could be right.
19-
4. If no → offer to create via Gus. Follow [gus-cli/SKILL.md](../gus-cli/SKILL.md). **Before creating:** show user Subject, Epic, Details, assignee. Ask: "Create this work item?" Do not run `sf data create record` until user says yes.
19+
4. If no → offer to create via Gus. Follow [gus-cli/SKILL.md](../gus-cli/SKILL.md). **Before creating:** show user Subject, Epic, Details, assignee. Ask: "Create this work item?" Do not run `sf data create record` until user says yes. If user declines creation and still wants to proceed with the PR, include `[skip-validate-pr]` in the PR body.
2020
5. Before creating PR: push current branch to remote if it doesn't already exist (`git push -u origin $(git branch --show-current)` or equivalent). Never push to `develop`/`main`
2121
6. After PR created: update work item `Details__c` with PR link. Query current `Details__c`, append `"\nPR: <url>"` (or prepend if empty). **Before updating:** show user the new Details\_\_c. Ask: "Update work item with PR link?" Do not run `sf data update record` until user says yes.
2222
7. After PR created: offer Ready for Review. Ask: "Put WI in Ready for Review? Who should review?" Choices:
@@ -43,6 +43,7 @@ Draft PR titles and bodies per salesforcedx-vscode conventions. Requires a Gus w
4343
- Write body content per [concise/SKILL.md](../concise/SKILL.md)
4444
- Include `@W-XXXXX@` in "What issues does this PR fix or reference?" per [.github/PULL_REQUEST_TEMPLATE.md](../../../.github/PULL_REQUEST_TEMPLATE.md):
4545
- Delete the before/after section if you have nothing to say there
46+
- **User declined WI:** Only when user explicitly declines to create a work item and still wants the PR, include `[skip-validate-pr]` in the PR body (e.g. at end of body)
4647

4748
```
4849
### What issues does this PR fix or reference?

.cursor/hooks.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
{
22
"version": 1,
33
"hooks": {
4+
"beforeShellExecution": [
5+
{
6+
"command": ".cursor/hooks/block-no-verify.sh",
7+
"matcher": "git.*--no-verify"
8+
}
9+
],
410
"stop": [{ "command": ".cursor/hooks/verify-stop.sh" }]
511
}
612
}

.cursor/hooks/block-no-verify.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env bash
2+
# Block git with --no-verify. beforeShellExecution hook (matcher: git.*--no-verify).
3+
input=$(cat)
4+
command=$(echo "$input" | jq -r '.command // empty')
5+
[[ "$command" =~ git.*--no-verify ]] && echo '{"permission":"deny","agent_message":"git with --no-verify is blocked. Run without --no-verify so hooks run."}' || echo '{"permission":"allow"}'

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@
136136
"capture:results": "shx mkdir -p ./test-results && npm run junit:report && npm run coverage:report",
137137
"check:links": "find . -name \\*.md -not -path '*/node_modules/*' -print0 | xargs -0 -n1 npx markdown-link-check --quiet --alive 200,206,429",
138138
"check:dupes": "npx jscpd",
139+
"check:branch": "wireit",
139140
"precommit": "wireit",
140141
"prepush": "wireit",
141142
"vscode:bundle": "wireit",
@@ -371,8 +372,12 @@
371372
],
372373
"output": []
373374
},
375+
"check:branch": {
376+
"command": "node scripts/check-branch.js"
377+
},
374378
"precommit": {
375379
"dependencies": [
380+
"check:branch",
376381
"compile",
377382
"lint",
378383
"check:peer-deps",

packages/salesforcedx-vscode-core/src/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,12 @@ export const activate = async (extensionContext: vscode.ExtensionContext): Promi
263263
CommandEventDispatcher.getInstance()
264264
);
265265

266-
if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) {
267-
// Refresh SObject definitions if there aren't any faux classes
266+
if (
267+
metadataExtension &&
268+
vscode.workspace.workspaceFolders &&
269+
vscode.workspace.workspaceFolders.length > 0
270+
) {
271+
// Refresh SObject definitions if there aren't any faux classes (metadata ext registers the command)
268272
const sobjectRefreshStartup: boolean = vscode.workspace
269273
.getConfiguration(SFDX_CORE_CONFIGURATION_NAME)
270274
.get<boolean>(ENABLE_SOBJECT_REFRESH_ON_STARTUP, false);

packages/salesforcedx-vscode-metadata/package.json

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,6 @@
287287
"command": "sf.metadata.apex.generate.class",
288288
"when": "salesforcedx-vscode-metadata.showSharedCommands"
289289
},
290-
{
291-
"command": "sf.metadata.lightning.generate.lwc",
292-
"when": "salesforcedx-vscode-metadata.showSharedCommands"
293-
},
294290
{
295291
"command": "sf.metadata.delete.source",
296292
"when": "never"
@@ -393,7 +389,7 @@
393389
},
394390
{
395391
"command": "sf.metadata.lightning.generate.lwc",
396-
"when": "explorerResourceIsFolder && resourceFilename == lwc && salesforcedx-vscode-metadata.showSharedCommands && sf:project_opened"
392+
"when": "explorerResourceIsFolder && resourceFilename == lwc && sf:project_opened"
397393
}
398394
]
399395
},

packages/salesforcedx-vscode-metadata/src/commands/projectDeployStart.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,23 @@
77

88
import { ExtensionProviderService } from '@salesforce/effect-ext-utils';
99
import * as Effect from 'effect/Effect';
10+
import * as vscode from 'vscode';
11+
import { nls } from '../messages';
1012
import { deployComponentSet } from '../shared/deploy/deployComponentSet';
1113

1214
/** Deploy local changes to the default org */
1315
export const projectDeployStartCommand = (ignoreConflicts = false) =>
1416
Effect.gen(function* () {
1517
const api = yield* (yield* ExtensionProviderService).getServicesApi;
16-
const componentSet = yield* (yield* api.services.ComponentSetService).ensureNonEmptyComponentSet(
17-
yield* api.services.MetadataDeployService.getComponentSetForDeploy({ ignoreConflicts })
18-
);
18+
const [componentSetService] = yield* Effect.all([api.services.ComponentSetService], { concurrency: 'unbounded' });
19+
const componentSet = yield* api.services.MetadataDeployService.getComponentSetForDeploy({ ignoreConflicts });
1920

20-
yield* deployComponentSet({ componentSet });
21-
});
21+
const nonEmpty = yield* componentSetService.ensureNonEmptyComponentSet(componentSet);
22+
yield* deployComponentSet({ componentSet: nonEmpty });
23+
}).pipe(
24+
Effect.catchTag('EmptyComponentSetError', () =>
25+
Effect.sync(() => {
26+
void vscode.window.showInformationMessage(nls.localize('no_local_changes_to_deploy'));
27+
})
28+
)
29+
);

packages/salesforcedx-vscode-metadata/src/commands/retrieveStart/projectRetrieveStart.ts

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,19 @@
88
import { ExtensionProviderService } from '@salesforce/effect-ext-utils';
99
import type { ComponentSet } from '@salesforce/source-deploy-retrieve';
1010
import * as Effect from 'effect/Effect';
11+
import * as Schema from 'effect/Schema';
12+
import * as vscode from 'vscode';
1113
import { nls } from '../../messages';
1214
import { retrieveComponentSet } from '../../shared/retrieve/retrieveComponentSet';
1315

16+
class SourceTrackingComponentsFailedError extends Schema.TaggedError<SourceTrackingComponentsFailedError>()(
17+
'SourceTrackingComponentsFailedError',
18+
{
19+
message: Schema.String,
20+
cause: Schema.optional(Schema.Unknown)
21+
}
22+
) {}
23+
1424
// Type guard function to ensure result has expected shape
1525
const isApplyResult = (
1626
value: unknown
@@ -26,8 +36,8 @@ export const projectRetrieveStartCommand = (ignoreConflicts: boolean) =>
2636
yield* Effect.annotateCurrentSpan({ ignoreConflicts });
2737

2838
const api = yield* (yield* ExtensionProviderService).getServicesApi;
29-
const [sourceTrackingService, channelService] = yield* Effect.all(
30-
[api.services.SourceTrackingService, api.services.ChannelService],
39+
const [sourceTrackingService, channelService, componentSetService] = yield* Effect.all(
40+
[api.services.SourceTrackingService, api.services.ChannelService, api.services.ComponentSetService],
3141
{ concurrency: 'unbounded' }
3242
);
3343

@@ -48,26 +58,30 @@ export const projectRetrieveStartCommand = (ignoreConflicts: boolean) =>
4858
const result = yield* Effect.tryPromise({
4959
try: () => tracking.maybeApplyRemoteDeletesToLocal(true),
5060
catch: e =>
51-
new Error(nls.localize('error_source_tracking_components_failed', e instanceof Error ? e.message : String(e)))
61+
new SourceTrackingComponentsFailedError({
62+
message: nls.localize('error_source_tracking_components_failed', e instanceof Error ? e.message : String(e)),
63+
cause: e
64+
})
5265
}).pipe(Effect.withSpan('maybeApplyRemoteDeletesToLocal'));
5366

5467
if (!isApplyResult(result)) {
5568
return yield* Effect.fail(
56-
new Error(nls.localize('error_source_tracking_components_failed', 'Invalid result from source tracking'))
69+
new SourceTrackingComponentsFailedError({
70+
message: nls.localize('error_source_tracking_components_failed', 'Invalid result from source tracking')
71+
})
5772
);
5873
}
5974

6075
const componentSet = result.componentSetFromNonDeletes;
61-
62-
const changeCount = componentSet.size;
76+
const nonEmpty = yield* componentSetService.ensureNonEmptyComponentSet(componentSet);
6377
yield* channelService.appendToChannel(
64-
`Found ${changeCount} remote change${changeCount === 1 ? '' : 's'} to retrieve`
78+
`Found ${nonEmpty.size} remote change${nonEmpty.size === 1 ? '' : 's'} to retrieve`
6579
);
66-
67-
if (componentSet.size === 0) {
68-
yield* channelService.appendToChannel('No remote changes to retrieve');
69-
return;
70-
}
71-
72-
yield* retrieveComponentSet({ componentSet, ignoreConflicts: true });
73-
});
80+
yield* retrieveComponentSet({ componentSet: nonEmpty, ignoreConflicts: true });
81+
}).pipe(
82+
Effect.catchTag('EmptyComponentSetError', () =>
83+
Effect.sync(() => {
84+
void vscode.window.showInformationMessage(nls.localize('no_remote_changes_to_retrieve'));
85+
})
86+
)
87+
);

packages/salesforcedx-vscode-metadata/src/messages/i18n.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ export const messages = {
6060
'You can run SFDX: Retrieve This Source from Org only on a source file or directory.',
6161
retrieve_select_manifest: 'You can run SFDX: Retrieve Source in Manifest from Org only on a manifest file.',
6262
retrieve_completed_with_errors_message: 'Retrieve completed with errors. Check output for details.',
63+
no_remote_changes_to_retrieve: 'No remote changes to retrieve.',
64+
no_local_changes_to_deploy: 'No local changes to deploy.',
6365
retrieve_source_conflicts_detected: 'Conflicts detected. Resolve conflicts before retrieving. \n Conflicts: %s',
6466
error_source_tracking_components_failed: 'Failed to retrieve components using source tracking: %s',
6567
delete_source_text: 'SFDX: Delete from Project and Org',

0 commit comments

Comments
 (0)