Skip to content

Commit e5da1b9

Browse files
authored
derive fork release versions in workflow (#24)
1 parent 05ad0e6 commit e5da1b9

6 files changed

Lines changed: 153 additions & 34 deletions

File tree

.github/workflows/release.yml

Lines changed: 93 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -101,50 +101,117 @@ jobs:
101101
DISPATCH_VERSION: ${{ github.event.inputs.version }}
102102
NIGHTLY_DATE: ${{ github.run_started_at }}
103103
NIGHTLY_SHA: ${{ github.sha }}
104-
NIGHTLY_RUN_NUMBER: ${{ github.run_number }}
105104
run: |
106-
if [[ "${GITHUB_EVENT_NAME}" == "schedule" || ( "${GITHUB_EVENT_NAME}" == "workflow_dispatch" && "${DISPATCH_CHANNEL:-stable}" == "nightly" ) ]]; then
107-
nightly_date="$(date -u -d "$NIGHTLY_DATE" +%Y%m%d)"
105+
fork_marker="tarik02"
106+
release_date="$(date -u -d "$NIGHTLY_DATE" +%Y%m%d)"
107+
release_year="${release_date:0:4}"
108+
release_month=$((10#${release_date:4:2}))
109+
release_day=$((10#${release_date:6:2}))
110+
111+
trim() {
112+
local value="$1"
113+
value="${value#"${value%%[![:space:]]*}"}"
114+
value="${value%"${value##*[![:space:]]}"}"
115+
printf '%s' "$value"
116+
}
117+
118+
next_stable_version() {
119+
local day_slot=$((release_day * 100))
120+
local max_sequence=0
121+
local regex="^v?${release_year}\.${release_month}\.([0-9]+)\+${fork_marker}(\.[0-9]+)?$"
122+
123+
for tag in $(git tag --list); do
124+
if [[ "$tag" =~ $regex ]]; then
125+
patch="${BASH_REMATCH[1]}"
126+
if (( patch == release_day || patch == day_slot || patch == day_slot + 1 )); then
127+
sequence=1
128+
elif (( patch > day_slot + 1 && patch < day_slot + 100 )); then
129+
sequence=$((patch - day_slot))
130+
else
131+
continue
132+
fi
133+
if (( sequence > max_sequence )); then
134+
max_sequence="$sequence"
135+
fi
136+
fi
137+
done
138+
139+
local next_sequence=$((max_sequence + 1))
140+
local patch="$day_slot"
141+
if (( next_sequence > 1 )); then
142+
patch=$((day_slot + next_sequence))
143+
fi
144+
printf '%s.%s.%s+%s' "$release_year" "$release_month" "$patch" "$fork_marker"
145+
}
146+
147+
next_nightly_version() {
148+
local max_sequence=0
149+
local regex="^v?${release_year}\.${release_month}\.${release_day}-nightly\.${release_date}(\.([0-9]+))?\+${fork_marker}$"
150+
151+
for tag in $(git tag --list); do
152+
if [[ "$tag" =~ $regex ]]; then
153+
sequence="${BASH_REMATCH[2]:-1}"
154+
if (( sequence > max_sequence )); then
155+
max_sequence="$sequence"
156+
fi
157+
fi
158+
done
159+
160+
local next_sequence=$((max_sequence + 1))
161+
local sequence_suffix=""
162+
if (( next_sequence > 1 )); then
163+
sequence_suffix=".$next_sequence"
164+
fi
165+
printf '%s.%s.%s-nightly.%s%s+%s' \
166+
"$release_year" "$release_month" "$release_day" "$release_date" "$sequence_suffix" "$fork_marker"
167+
}
108168
109-
node scripts/resolve-nightly-release.ts \
110-
--date "$nightly_date" \
111-
--run-number "$NIGHTLY_RUN_NUMBER" \
112-
--sha "$NIGHTLY_SHA" \
113-
--github-output
169+
normalize_stable_version() {
170+
local raw
171+
raw="$(trim "$1")"
172+
raw="${raw#v}"
173+
if [[ "$raw" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
174+
printf '%s+%s' "$raw" "$fork_marker"
175+
elif [[ "$raw" =~ ^[0-9]+\.[0-9]+\.[0-9]+\+tarik02(\.[0-9]+)?$ ]]; then
176+
printf '%s' "$raw"
177+
else
178+
echo "Invalid stable release version: $1" >&2
179+
exit 1
180+
fi
181+
}
182+
183+
if [[ "${GITHUB_EVENT_NAME}" == "schedule" || ( "${GITHUB_EVENT_NAME}" == "workflow_dispatch" && "${DISPATCH_CHANNEL:-stable}" == "nightly" ) ]]; then
184+
version="$(next_nightly_version)"
185+
short_sha="${NIGHTLY_SHA:0:12}"
114186
115187
echo "release_channel=nightly" >> "$GITHUB_OUTPUT"
188+
echo "version=$version" >> "$GITHUB_OUTPUT"
189+
echo "tag=v$version" >> "$GITHUB_OUTPUT"
190+
echo "name=T3 Code Nightly $version ($short_sha)" >> "$GITHUB_OUTPUT"
191+
echo "short_sha=$short_sha" >> "$GITHUB_OUTPUT"
116192
echo "cli_dist_tag=nightly" >> "$GITHUB_OUTPUT"
117193
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
118194
echo "make_latest=false" >> "$GITHUB_OUTPUT"
119195
else
120196
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
121-
raw="${DISPATCH_VERSION}"
122-
if [[ -z "$raw" ]]; then
123-
echo "workflow_dispatch stable releases require the version input." >&2
124-
exit 1
197+
raw="$(trim "${DISPATCH_VERSION:-}")"
198+
if [[ -n "$raw" ]]; then
199+
version="$(normalize_stable_version "$raw")"
200+
else
201+
version="$(next_stable_version)"
125202
fi
126203
else
127-
raw="${GITHUB_REF_NAME}"
128-
fi
129-
130-
version="${raw#v}"
131-
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
132-
echo "Invalid release version: $raw" >&2
133-
exit 1
204+
version="$(normalize_stable_version "$GITHUB_REF_NAME")"
134205
fi
135206
136207
echo "release_channel=stable" >> "$GITHUB_OUTPUT"
137208
echo "version=$version" >> "$GITHUB_OUTPUT"
138209
echo "tag=v$version" >> "$GITHUB_OUTPUT"
139210
echo "name=T3 Code v$version" >> "$GITHUB_OUTPUT"
211+
echo "short_sha=" >> "$GITHUB_OUTPUT"
140212
echo "cli_dist_tag=latest" >> "$GITHUB_OUTPUT"
141-
if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-tarik02\.[0-9]+)?$ ]]; then
142-
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
143-
echo "make_latest=true" >> "$GITHUB_OUTPUT"
144-
else
145-
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
146-
echo "make_latest=false" >> "$GITHUB_OUTPUT"
147-
fi
213+
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
214+
echo "make_latest=true" >> "$GITHUB_OUTPUT"
148215
fi
149216
150217
- name: Check

AGENTS.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ If a tradeoff is required, choose correctness and robustness over short-term con
2424

2525
Long term maintainability is a core priority. If you add new functionality, first check if there is shared logic that can be extracted to a separate module. Duplicate logic across multiple files is a code smell and should be avoided. Don't be afraid to change existing code. Don't take shortcuts by just adding local logic to solve a problem.
2626

27+
## Fork Notes
28+
29+
- `FORK.md` documents behavior that intentionally differs from upstream.
30+
- When making fork-only changes, update `FORK.md` in the same change so future upstream merges have current context.
31+
- Keep workflow-only fork changes narrow and prefer job-level disables over broad refactors.
32+
- Do not commit package version bumps solely to represent fork releases.
33+
- Re-check Electron updater channel behavior when changing version strings, release metadata, or desktop packaging.
34+
- Keep fork-only storage in `state-tarik02.sqlite` unless intentionally upstreaming it.
35+
- When preparing fork PRs, branch from `origin/main` and target `tarik02/t3code:main`.
36+
- If a fork PR branch accidentally includes upstream history, rebuild it from `origin/main` and replay only the intended diff.
37+
2738
## Package Roles
2839

2940
- `apps/server`: Node.js WebSocket server. Wraps Codex app-server (JSON-RPC over stdio), serves the React web app, and manages provider sessions.

FORK.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Fork Notes
2+
3+
This repository is a fork of `pingdotgg/t3code`. Keep this file focused on fork behavior that intentionally differs from upstream.
4+
5+
## Changes
6+
7+
### Release And CI
8+
9+
- Fork workflows disable scheduled releases, relay jobs, hosted deploys, and fork-unsafe publish paths.
10+
- Release builds publish updater metadata against the fork repository.
11+
- Fork release versions are derived in the release workflow so package manifests stay close to upstream.
12+
13+
### Desktop Updater Channels
14+
15+
- Stable builds use `latest`; nightly builds use `nightly`.
16+
- Nightly detection accepts fork release metadata while preserving the upstream channel split.
17+
18+
### Fork Persistence
19+
20+
- Fork-only goal persistence is stored in a sidecar database named `state-tarik02.sqlite`.
21+
22+
### Goals UI
23+
24+
- The fork adds thread goal support, goal activity rendering, and goal sidebar/panel UI.
25+
26+
### Provider Launch Environment
27+
28+
- Provider sessions use a shared launch environment pipeline instead of ad hoc environment assembly.
29+
30+
### Base Path And Remote URLs
31+
32+
- The fork includes base-path handling for served web assets and remote URL normalization.
33+
34+
### UX Changes
35+
36+
- Desktop context-menu style is configurable.
37+
- Threads can be archived with middle click.
38+
- Terminal selection has a copy action.

apps/desktop/src/updates/updateChannels.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { DesktopUpdateChannel } from "@t3tools/contracts";
22

3-
const NIGHTLY_VERSION_PATTERN = /-nightly\.\d{8}\.\d+$/;
3+
const NIGHTLY_VERSION_PATTERN = /-nightly\.\d{8}(?:\.\d+)?(?:\+[0-9A-Za-z.-]+)?$/;
44

55
export function isNightlyDesktopVersion(version: string): boolean {
66
return NIGHTLY_VERSION_PATTERN.test(version);

scripts/build-desktop-artifact.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ function resolveGitHubPublishConfig(updateChannel: "latest" | "nightly"):
650650
}
651651

652652
export function resolveDesktopUpdateChannel(version: string): "latest" | "nightly" {
653-
return /-nightly\.\d{8}\.\d+$/.test(version) ? "nightly" : "latest";
653+
return /-nightly\.\d{8}(?:\.\d+)?(?:\+[0-9A-Za-z.-]+)?$/.test(version) ? "nightly" : "latest";
654654
}
655655

656656
export function resolveDesktopBuildIconAssets(version: string): DesktopBuildIconAssets {

scripts/resolve-previous-release-tag.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ interface NightlyVersion {
2727
readonly minor: number;
2828
readonly patch: number;
2929
readonly date: number;
30-
readonly runNumber: number;
30+
readonly sequence: number;
3131
}
3232

3333
const parseNumericIdentifier = (identifier: string): number | undefined =>
@@ -100,24 +100,27 @@ const compareNightlyVersions = (left: NightlyVersion, right: NightlyVersion): nu
100100
if (left.minor !== right.minor) return left.minor - right.minor;
101101
if (left.patch !== right.patch) return left.patch - right.patch;
102102
if (left.date !== right.date) return left.date - right.date;
103-
return left.runNumber - right.runNumber;
103+
return left.sequence - right.sequence;
104104
};
105105

106106
const parseNightlyTag = (tag: string): NightlyVersion | undefined => {
107107
// Accept both the current `v<semver>` format and the legacy `nightly-v<semver>`
108108
// format so release note diffs keep working across the tag-format transition.
109-
const match = /^(?:nightly-)?v(\d+)\.(\d+)\.(\d+)-nightly\.(\d{8})\.(\d+)$/.exec(tag);
109+
const match =
110+
/^(?:nightly-)?v(\d+)\.(\d+)\.(\d+)-nightly\.(\d{8})(?:\.(\d+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(
111+
tag,
112+
);
110113
if (!match) return undefined;
111114

112-
const [, major, minor, patch, date, runNumber] = match;
113-
if (!major || !minor || !patch || !date || !runNumber) return undefined;
115+
const [, major, minor, patch, date, sequence] = match;
116+
if (!major || !minor || !patch || !date) return undefined;
114117

115118
return {
116119
major: Number(major),
117120
minor: Number(minor),
118121
patch: Number(patch),
119122
date: Number(date),
120-
runNumber: Number(runNumber),
123+
sequence: sequence ? Number(sequence) : 1,
121124
};
122125
};
123126

0 commit comments

Comments
 (0)