@checkstack/gitops-frontend 0.2.0 → 0.2.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @checkstack/gitops-frontend
2
2
 
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 86bab6a: ### GitOps: Fix authentication token handling
8
+
9
+ - Made `authToken` optional in `ReconcileProviderParams` and `ScraperOptions` to support unauthenticated access to public repositories
10
+ - GitHub and GitLab scrapers now conditionally set authentication headers only when a token is provided
11
+ - Sync worker now decrypts the encrypted `authToken` from the database before passing it to scrapers, fixing authentication failures caused by sending encrypted values in HTTP headers
12
+
13
+ ### SLO: Fix premature Nines Club achievement unlock
14
+
15
+ - The "Nines Club" achievement now requires both ≥99.99% availability **and** a 365-day compliance streak, preventing immediate unlock on newly created SLOs with 100% default availability
16
+
17
+ ### SLO: Align frontend achievement descriptions with backend criteria
18
+
19
+ - Fixed mismatched descriptions for Iron Uptime (7-day, not 30), Diamond Uptime (30-day, not 90), Clean Sheet (rolling window, not quarter), Full Coverage (3+ SLOs, not all systems in group), and Nines Club (99.99%)
20
+
21
+ ### SLO: Enrich milestones with system names
22
+
23
+ - The `getRecentMilestones` endpoint now resolves human-readable system names via the Catalog API instead of returning raw system IDs
24
+
25
+ - Updated dependencies [86bab6a]
26
+ - @checkstack/gitops-common@0.1.1
27
+
3
28
  ## 0.2.0
4
29
 
5
30
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/gitops-frontend",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "main": "src/index.tsx",
6
6
  "checkstack": {
@@ -14,8 +14,8 @@
14
14
  "dependencies": {
15
15
  "@checkstack/common": "0.6.5",
16
16
  "@checkstack/frontend-api": "0.3.9",
17
- "@checkstack/gitops-common": "0.0.1",
18
- "@checkstack/ui": "1.3.5",
17
+ "@checkstack/gitops-common": "0.1.0",
18
+ "@checkstack/ui": "1.3.6",
19
19
  "lucide-react": "^0.344.0",
20
20
  "react": "^18.2.0",
21
21
  "react-router-dom": "^6.22.0"
@@ -24,7 +24,7 @@ interface ProviderEditorProps {
24
24
  target: string;
25
25
  pathPattern: string;
26
26
  baseUrl?: string;
27
- authToken?: string;
27
+ authToken?: string | null;
28
28
  syncInterval?: number;
29
29
  deletionPolicy?: "orphan" | "auto";
30
30
  }) => void;
@@ -50,6 +50,7 @@ export const ProviderEditor: React.FC<ProviderEditorProps> = ({
50
50
  const [pathPattern, setPathPattern] = useState(initialData?.pathPattern ?? ".checkstack/**/*.yaml");
51
51
  const [baseUrl, setBaseUrl] = useState(initialData?.baseUrl ?? "");
52
52
  const [authToken, setAuthToken] = useState("");
53
+ const [clearToken, setClearToken] = useState(false);
53
54
  const [syncInterval, setSyncInterval] = useState(String(initialData?.syncInterval ?? 300));
54
55
  const [deletionPolicy, setDeletionPolicy] = useState<"orphan" | "auto">(
55
56
  initialData?.deletionPolicy ?? "orphan",
@@ -62,6 +63,7 @@ export const ProviderEditor: React.FC<ProviderEditorProps> = ({
62
63
  setPathPattern(initialData?.pathPattern ?? ".checkstack/**/*.yaml");
63
64
  setBaseUrl(initialData?.baseUrl ?? "");
64
65
  setAuthToken("");
66
+ setClearToken(false);
65
67
  setSyncInterval(String(initialData?.syncInterval ?? 300));
66
68
  setDeletionPolicy(initialData?.deletionPolicy ?? "orphan");
67
69
  }
@@ -71,12 +73,24 @@ export const ProviderEditor: React.FC<ProviderEditorProps> = ({
71
73
  e.preventDefault();
72
74
  if (!target.trim() || !pathPattern.trim()) return;
73
75
 
76
+ // Determine authToken value:
77
+ // - clearToken=true → null (explicitly remove)
78
+ // - non-empty string → new token
79
+ // - empty string → undefined (keep current)
80
+ let authTokenValue: string | null | undefined;
81
+ if (clearToken) {
82
+ // eslint-disable-next-line unicorn/no-null
83
+ authTokenValue = null;
84
+ } else if (authToken.trim()) {
85
+ authTokenValue = authToken.trim();
86
+ }
87
+
74
88
  onSave({
75
89
  type,
76
90
  target: target.trim(),
77
91
  pathPattern: pathPattern.trim(),
78
92
  baseUrl: baseUrl.trim() || undefined,
79
- authToken: authToken.trim() || undefined,
93
+ authToken: authTokenValue,
80
94
  syncInterval: Number(syncInterval) || 300,
81
95
  deletionPolicy,
82
96
  });
@@ -160,13 +174,45 @@ export const ProviderEditor: React.FC<ProviderEditorProps> = ({
160
174
  <Label htmlFor="provider-auth-token">
161
175
  Auth Token {initialData ? "(leave empty to keep current)" : "(optional)"}
162
176
  </Label>
163
- <Input
164
- id="provider-auth-token"
165
- type="password"
166
- placeholder="ghp_xxxx..."
167
- value={authToken}
168
- onChange={(e) => setAuthToken(e.target.value)}
169
- />
177
+ {clearToken ? (
178
+ <div className="flex items-center gap-2 p-2 rounded-md border border-border bg-muted/50">
179
+ <span className="text-sm text-muted-foreground flex-1">
180
+ Token will be removed on save
181
+ </span>
182
+ <Button
183
+ type="button"
184
+ variant="outline"
185
+ size="sm"
186
+ onClick={() => setClearToken(false)}
187
+ >
188
+ Undo
189
+ </Button>
190
+ </div>
191
+ ) : (
192
+ <div className="flex gap-2">
193
+ <Input
194
+ id="provider-auth-token"
195
+ type="password"
196
+ placeholder="ghp_xxxx..."
197
+ value={authToken}
198
+ onChange={(e) => setAuthToken(e.target.value)}
199
+ className="flex-1"
200
+ />
201
+ {initialData && (
202
+ <Button
203
+ type="button"
204
+ variant="outline"
205
+ size="sm"
206
+ onClick={() => {
207
+ setAuthToken("");
208
+ setClearToken(true);
209
+ }}
210
+ >
211
+ Remove
212
+ </Button>
213
+ )}
214
+ </div>
215
+ )}
170
216
  </div>
171
217
 
172
218
  <div className="grid grid-cols-2 gap-4">
@@ -119,7 +119,7 @@ export const ProviderList = () => {
119
119
  target: string;
120
120
  pathPattern: string;
121
121
  baseUrl?: string;
122
- authToken?: string;
122
+ authToken?: string | null;
123
123
  syncInterval?: number;
124
124
  deletionPolicy?: "orphan" | "auto";
125
125
  }) => {
@@ -136,7 +136,11 @@ export const ProviderList = () => {
136
136
  },
137
137
  });
138
138
  } else {
139
- createMutation.mutate(data);
139
+ // On create, authToken is always string|undefined (Remove button not shown)
140
+ createMutation.mutate({
141
+ ...data,
142
+ authToken: data.authToken ?? undefined,
143
+ });
140
144
  }
141
145
  };
142
146