Skip to content

Commit cd7865a

Browse files
committed
Add minimum release age setting to delay update availability and expose it in settings UI
- in settings > updates, add 'supply chain policy'. It should accept number of days that must pass to consider a version eligible for installation. - use other wording: 'minimum release age' - respect [Pasted text] when automatically checking for updates - does it make sense? we want something like pnpm has [Changes] do we take advantage of CalVer versioning? date of each release is in its version, e.g. [Pasted text] - remove redundant comments ├── apps │ └── editor │ ├── package.json │ └── src │ ├── commands │ │ └── check-for-updates-command.ts │ ├── constants │ │ └── state-keys.ts │ └── views │ ├── prompt │ │ └── backend │ │ └── utils │ │ └── check-for-updates.ts │ └── settings │ ├── backend │ │ ├── message-handlers │ │ │ ├── handle-get-minimum-release-age.ts │ │ │ ├── handle-get-update-check-frequency.ts │ │ │ ├── handle-update-minimum-release-age.ts │ │ │ ├── handle-update-update-check-frequency.ts │ │ │ └── index.ts │ │ └── settings-view-provider.ts │ ├── frontend │ │ ├── Home │ │ │ ├── Home.tsx │ │ │ └── sections │ │ │ └── GeneralSection.tsx │ │ ├── Settings.tsx │ │ ├── hooks │ │ │ └── use-settings.ts │ │ └── i18n │ │ └── translations │ │ └── general.ts │ └── types │ └── messages.ts └── packages └── ui └── src └── components └── editor └── common └── Input ├── Input.tsx └── index.ts
1 parent c805199 commit cd7865a

12 files changed

Lines changed: 221 additions & 31 deletions

File tree

‎apps/editor/package.json‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,11 @@
10301030
],
10311031
"default": "hourly"
10321032
},
1033+
"codeWebChat.minimumReleaseAge": {
1034+
"type": "integer",
1035+
"scope": "application",
1036+
"default": 0
1037+
},
10331038
"codeWebChat.ignorePatterns": {
10341039
"type": "array",
10351040
"scope": "resource",

‎apps/editor/src/views/prompt/backend/utils/check-for-updates.ts‎

Lines changed: 79 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,50 @@ const is_newer = (latest: string, current: string) => {
1616
return false
1717
}
1818

19+
const get_version_age_days = async (
20+
version: string,
21+
tag_name?: string
22+
): Promise<number> => {
23+
const parts = version.split('.')
24+
if (parts.length >= 2) {
25+
const year = parseInt(parts[0], 10)
26+
const mmdd = parts[1]
27+
if (!isNaN(year) && mmdd.length === 4) {
28+
const month = parseInt(mmdd.substring(0, 2), 10) - 1
29+
const day = parseInt(mmdd.substring(2, 4), 10)
30+
if (!isNaN(month) && !isNaN(day)) {
31+
const version_date = Date.UTC(year, month, day)
32+
const age_days = Math.floor(
33+
(Date.now() - version_date) / (1000 * 60 * 60 * 24)
34+
)
35+
return age_days >= 0 ? age_days : 0
36+
}
37+
}
38+
}
39+
40+
if (tag_name) {
41+
try {
42+
const res = await axios.get(
43+
`https://api.github.com/repos/CodeWebChat/CWC/commits/${tag_name}`,
44+
{ validateStatus: () => true }
45+
)
46+
if (res.status === 200) {
47+
const date = res.data.commit?.committer?.date
48+
if (date) {
49+
const age_days = Math.floor(
50+
(Date.now() - new Date(date).getTime()) / (1000 * 60 * 60 * 24)
51+
)
52+
return age_days >= 0 ? age_days : 0
53+
}
54+
}
55+
} catch (e) {
56+
console.error('Failed to fetch commit date:', e)
57+
}
58+
}
59+
60+
return Infinity
61+
}
62+
1963
export const check_for_updates = async (
2064
provider: PromptViewProvider,
2165
params?: { force?: boolean }
@@ -26,13 +70,17 @@ export const check_for_updates = async (
2670
provider.extension_context.extension.packageJSON.version
2771
if (!current_version) return
2872

73+
const config = vscode.workspace.getConfiguration('codeWebChat')
74+
const minimum_release_age = force
75+
? 0
76+
: config.get<number>('minimumReleaseAge', 0)
77+
2978
if (!force) {
3079
const last_check = provider.extension_context.globalState.get<number>(
3180
state_keys.LAST_UPDATE_CHECK_TIMESTAMP_STATE_KEY,
3281
0
3382
)
3483
const now = Date.now()
35-
const config = vscode.workspace.getConfiguration('codeWebChat')
3684
const frequency = config.get<'hourly' | 'daily' | 'weekly' | 'monthly'>(
3785
'updateCheckFrequency',
3886
'hourly'
@@ -73,7 +121,10 @@ export const check_for_updates = async (
73121
}
74122
}
75123

76-
provider.update_info = await fetch_update_info(current_version)
124+
provider.update_info = await fetch_update_info(
125+
current_version,
126+
minimum_release_age
127+
)
77128

78129
provider.extension_context.globalState.update(
79130
state_keys.LAST_UPDATE_CHECK_TIMESTAMP_STATE_KEY,
@@ -91,7 +142,8 @@ export const check_for_updates = async (
91142
}
92143

93144
const fetch_update_info = async (
94-
current_version: string
145+
current_version: string,
146+
minimum_release_age: number
95147
): Promise<UpdateInfo> => {
96148
if (!current_version) return { is_available: false }
97149

@@ -106,38 +158,34 @@ const fetch_update_info = async (
106158
const tags = res.data
107159
if (!Array.isArray(tags) || tags.length === 0) break
108160

109-
const vscode_tag = tags.find((t: any) => t.name?.endsWith('-vscode'))
110-
if (vscode_tag) {
111-
const latest_version = vscode_tag.name.replace('-vscode', '')
112-
if (is_newer(latest_version, current_version)) {
113-
let age_days = 0
114-
try {
115-
const commit_res = await axios.get(
116-
`https://api.github.com/repos/CodeWebChat/CWC/commits/${current_version}-vscode`,
117-
{ validateStatus: () => true }
118-
)
119-
if (commit_res.status === 200) {
120-
const commit_data = commit_res.data
121-
const date = commit_data.commit?.committer?.date
122-
if (date) {
123-
age_days = Math.floor(
124-
(Date.now() - new Date(date).getTime()) /
125-
(1000 * 60 * 60 * 24)
126-
)
161+
for (const tag of tags) {
162+
if (tag.name?.endsWith('-vscode')) {
163+
const latest_version = tag.name.replace('-vscode', '')
164+
if (is_newer(latest_version, current_version)) {
165+
if (minimum_release_age > 0) {
166+
const tag_age_days = await get_version_age_days(
167+
latest_version,
168+
tag.name
169+
)
170+
if (tag_age_days < minimum_release_age) {
171+
continue
127172
}
128173
}
129-
} catch (e) {
130-
console.error('Failed to fetch commit date:', e)
131-
}
132174

133-
return {
134-
is_available: true,
135-
latest_version,
136-
latest_version_tag: vscode_tag.name,
137-
current_version_age_days: age_days
175+
const current_version_age_days = await get_version_age_days(
176+
current_version,
177+
`${current_version}-vscode`
178+
)
179+
180+
return {
181+
is_available: true,
182+
latest_version,
183+
latest_version_tag: tag.name,
184+
current_version_age_days
185+
}
186+
} else {
187+
return { is_available: false }
138188
}
139-
} else {
140-
return { is_available: false }
141189
}
142190
}
143191
page++
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as vscode from 'vscode'
2+
import { SettingsViewProvider } from '../settings-view-provider'
3+
4+
export const handle_get_minimum_release_age = async (
5+
provider: SettingsViewProvider
6+
) => {
7+
const config = vscode.workspace.getConfiguration('codeWebChat')
8+
const value = config.get<number>('minimumReleaseAge', 0)
9+
provider.postMessage({
10+
command: 'MINIMUM_RELEASE_AGE',
11+
value
12+
})
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as vscode from 'vscode'
2+
import { UpdateMinimumReleaseAgeMessage } from '../../types/messages'
3+
4+
export const handle_update_minimum_release_age = async (
5+
message: UpdateMinimumReleaseAgeMessage
6+
) => {
7+
const config = vscode.workspace.getConfiguration('codeWebChat')
8+
await config.update(
9+
'minimumReleaseAge',
10+
message.value,
11+
vscode.ConfigurationTarget.Global
12+
)
13+
}

‎apps/editor/src/views/settings/backend/message-handlers/index.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export * from './handle-get-include-prompts-in-commit-messages'
2323
export * from './handle-update-include-prompts-in-commit-messages'
2424
export * from './handle-get-update-check-frequency'
2525
export * from './handle-update-update-check-frequency'
26+
export * from './handle-get-minimum-release-age'
27+
export * from './handle-update-minimum-release-age'
2628
export * from './handle-get-default-option-for-voice-input'
2729
export * from './handle-update-default-option-for-voice-input'
2830
export * from './handle-get-voice-input-instructions'

‎apps/editor/src/views/settings/backend/settings-view-provider.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ export class SettingsViewProvider {
143143
await handlers.handle_get_update_check_frequency(this)
144144
} else if (message.command === 'UPDATE_UPDATE_CHECK_FREQUENCY') {
145145
await handlers.handle_update_update_check_frequency(message)
146+
} else if (message.command === 'GET_MINIMUM_RELEASE_AGE') {
147+
await handlers.handle_get_minimum_release_age(this)
148+
} else if (message.command === 'UPDATE_MINIMUM_RELEASE_AGE') {
149+
await handlers.handle_update_minimum_release_age(message)
146150
} else if (message.command === 'GET_DEFAULT_OPTION_FOR_VOICE_INPUT') {
147151
await handlers.handle_get_default_option_for_voice_input(this)
148152
} else if (
@@ -260,6 +264,7 @@ export class SettingsViewProvider {
260264
)
261265
void handlers.handle_get_include_prompts_in_commit_messages(this)
262266
void handlers.handle_get_update_check_frequency(this)
267+
void handlers.handle_get_minimum_release_age(this)
263268
void handlers.handle_get_default_option_for_voice_input(this)
264269
void handlers.handle_get_voice_input_instructions(this)
265270
void handlers.handle_get_agentic_search_instructions(this)

‎apps/editor/src/views/settings/frontend/Home/Home.tsx‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ type Props = {
138138
on_update_check_frequency_change: (
139139
value: 'hourly' | 'daily' | 'weekly' | 'monthly'
140140
) => void
141+
minimum_release_age: number
142+
on_minimum_release_age_change: (value: number) => void
141143
default_option_for_voice_input: 'ask' | 'send-request' | 'invoke-agent'
142144
on_default_option_for_voice_input_change: (
143145
value: 'ask' | 'send-request' | 'invoke-agent'
@@ -372,6 +374,8 @@ export const Home: React.FC<Props> = (props) => {
372374
on_update_check_frequency_change={
373375
props.on_update_check_frequency_change
374376
}
377+
minimum_release_age={props.minimum_release_age}
378+
on_minimum_release_age_change={props.on_minimum_release_age_change}
375379
attach_ascii_tree_of_context={props.attach_ascii_tree_of_context}
376380
on_attach_ascii_tree_of_context_change={
377381
props.on_attach_ascii_tree_of_context_change

‎apps/editor/src/views/settings/frontend/Home/sections/GeneralSection.tsx‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Item as UiItem } from '@ui/components/editor/settings-view/Item'
55
import { Group as UiGroup } from '@ui/components/editor/settings-view/Group/Group'
66
import { Section as UiSection } from '@ui/components/editor/settings-view/Section'
77
import { TextButton as UiTextButton } from '@ui/components/editor/common/TextButton'
8+
import { Input as UiInput } from '@ui/components/editor/common/Input'
89
import { Textarea as UiTextarea } from '@ui/components/editor/common/Textarea'
910
import { use_translation } from '../../i18n/use-translation'
1011
import { NavItem } from '../Home'
@@ -26,6 +27,8 @@ type Props = {
2627
on_update_check_frequency_change: (
2728
value: 'hourly' | 'daily' | 'weekly' | 'monthly'
2829
) => void
30+
minimum_release_age: number
31+
on_minimum_release_age_change: (value: number) => void
2932
set_section_ref: (id: NavItem, el: HTMLDivElement | null) => void
3033
attach_ascii_tree_of_context: 'ask' | 'always' | 'never'
3134
on_attach_ascii_tree_of_context_change: (
@@ -181,6 +184,28 @@ export const GeneralSection = forwardRef<HTMLDivElement, Props>(
181184
/>
182185
}
183186
/>
187+
<UiItem
188+
title={t('general.updates.minimum-release-age.title')}
189+
description={t('general.updates.minimum-release-age.description')}
190+
slot_right={
191+
<UiInput
192+
value={props.minimum_release_age}
193+
type="number"
194+
min={0}
195+
on_change={(val) => {
196+
if (val === '') {
197+
props.on_minimum_release_age_change(0)
198+
} else {
199+
const num = parseInt(val, 10)
200+
if (!isNaN(num)) {
201+
props.on_minimum_release_age_change(num)
202+
}
203+
}
204+
}}
205+
width={100}
206+
/>
207+
}
208+
/>
184209
</UiGroup>
185210
</div>
186211

‎apps/editor/src/views/settings/frontend/Settings.tsx‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export const Settings = () => {
102102
settings_hook.select_all_prompts_in_commit_messages_by_default !==
103103
undefined &&
104104
settings_hook.update_check_frequency !== undefined &&
105+
settings_hook.minimum_release_age !== undefined &&
105106
default_option_for_voice_input !== undefined &&
106107
settings_hook.gemini_user_id !== undefined &&
107108
settings_hook.ai_studio_user_id !== undefined &&
@@ -159,6 +160,10 @@ export const Settings = () => {
159160
on_update_check_frequency_change={
160161
settings_hook.handle_update_check_frequency_change
161162
}
163+
minimum_release_age={settings_hook.minimum_release_age!}
164+
on_minimum_release_age_change={
165+
settings_hook.handle_minimum_release_age_change
166+
}
162167
default_option_for_voice_input={default_option_for_voice_input!}
163168
on_default_option_for_voice_input_change={(value) => {
164169
set_default_option_for_voice_input(value)

‎apps/editor/src/views/settings/frontend/hooks/use-settings.ts‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ export const use_settings = (vscode: any) => {
5050
const [update_check_frequency, set_update_check_frequency] = useState<
5151
'hourly' | 'daily' | 'weekly' | 'monthly' | undefined
5252
>(undefined)
53+
const [minimum_release_age, set_minimum_release_age] = useState<
54+
number | undefined
55+
>(undefined)
5356
const [agentic_search_instructions, set_agentic_search_instructions] =
5457
useState<string | undefined>(undefined)
5558
const [
@@ -99,6 +102,7 @@ export const use_settings = (vscode: any) => {
99102
post_message(vscode, { command: 'GET_EDIT_FILES_SYSTEM_INSTRUCTIONS' })
100103
post_message(vscode, { command: 'GET_COMMIT_MESSAGE_INSTRUCTIONS' })
101104
post_message(vscode, { command: 'GET_UPDATE_CHECK_FREQUENCY' })
105+
post_message(vscode, { command: 'GET_MINIMUM_RELEASE_AGE' })
102106
post_message(vscode, { command: 'GET_VOICE_INPUT_INSTRUCTIONS' })
103107
post_message(vscode, { command: 'GET_AGENTIC_SEARCH_INSTRUCTIONS' })
104108
post_message(vscode, { command: 'GET_AGENTIC_SEARCH_INVOCATION_COUNT' })
@@ -142,6 +146,8 @@ export const use_settings = (vscode: any) => {
142146
set_default_commit_message_instructions(message.default_instructions)
143147
} else if (message.command === 'UPDATE_CHECK_FREQUENCY') {
144148
set_update_check_frequency(message.value)
149+
} else if (message.command === 'MINIMUM_RELEASE_AGE') {
150+
set_minimum_release_age(message.value)
145151
} else if (message.command === 'VOICE_INPUT_INSTRUCTIONS') {
146152
set_voice_input_instructions(message.instructions)
147153
set_default_voice_input_instructions(message.default_instructions)
@@ -322,6 +328,14 @@ export const use_settings = (vscode: any) => {
322328
})
323329
}
324330

331+
const handle_minimum_release_age_change = (value: number) => {
332+
set_minimum_release_age(value)
333+
post_message(vscode, {
334+
command: 'UPDATE_MINIMUM_RELEASE_AGE',
335+
value
336+
})
337+
}
338+
325339
const handle_commit_instructions_change = (instructions: string) =>
326340
post_message(vscode, {
327341
command: 'UPDATE_COMMIT_MESSAGE_INSTRUCTIONS',
@@ -509,6 +523,8 @@ export const use_settings = (vscode: any) => {
509523
voice_input_instructions,
510524
update_check_frequency,
511525
handle_update_check_frequency_change,
526+
minimum_release_age,
527+
handle_minimum_release_age_change,
512528
default_voice_input_instructions,
513529
agentic_search_instructions,
514530
default_agentic_search_instructions,

0 commit comments

Comments
 (0)