happyskills 1.7.0 → 1.7.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 +6 -0
- package/package.json +1 -1
- package/src/api/repos.js +3 -0
- package/src/api/repos.test.js +16 -0
- package/src/engine/downloader.js +2 -2
- package/src/engine/installer.js +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.7.1] - 2026-06-04
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Stop double-counting a single install. The install pipeline tries an archive download first and falls back to a JSON clone when the archive is missing or unusable; both hit the clone endpoint, which counts (`repo.clone` + `download_count`) per request. The fallback now sends `?no_count=1` so one install registers exactly once. Only the install fallback sets it — `fork`, `pull`, and normal installs are unchanged. Takes effect against API v5.4.1+ (older APIs ignore the flag and still count both); forward-only.
|
|
15
|
+
|
|
10
16
|
## [1.7.0] - 2026-06-04
|
|
11
17
|
|
|
12
18
|
### Added
|
package/package.json
CHANGED
package/src/api/repos.js
CHANGED
|
@@ -61,6 +61,9 @@ const clone = (owner, repo, ref, options = {}) => catch_errors(`Clone ${owner}/$
|
|
|
61
61
|
if (options.commit) params.set('commit', options.commit)
|
|
62
62
|
else if (ref) params.set('ref', ref)
|
|
63
63
|
if (options.format) params.set('format', options.format)
|
|
64
|
+
// Fallback retries (install archive→JSON) mark themselves so the server
|
|
65
|
+
// counts the install only once (the primary attempt already counted).
|
|
66
|
+
if (options.no_count) params.set('no_count', '1')
|
|
64
67
|
const qs = params.toString() ? `?${params}` : ''
|
|
65
68
|
const [errors, data] = await client.get(`/repos/${owner}/${repo}/clone${qs}`)
|
|
66
69
|
if (errors) throw errors[errors.length - 1]
|
package/src/api/repos.test.js
CHANGED
|
@@ -217,3 +217,19 @@ describe('repos.star / repos.unstar — endpoint + path', () => {
|
|
|
217
217
|
assert.strictEqual(calls[0][1], `/repos/${encodeURIComponent('acme corp')}/${encodeURIComponent('deploy/aws')}/star`)
|
|
218
218
|
})
|
|
219
219
|
})
|
|
220
|
+
|
|
221
|
+
// Bug B fix: a fallback retry marks itself no_count so the server counts the
|
|
222
|
+
// install only once (the primary archive attempt already counted).
|
|
223
|
+
describe('repos.clone — no_count flag (double-count prevention)', () => {
|
|
224
|
+
afterEach(restore)
|
|
225
|
+
|
|
226
|
+
it('adds ?no_count=1 only when options.no_count is set', async () => {
|
|
227
|
+
const { repos, calls } = stub_client_capture()
|
|
228
|
+
await repos.clone('acme', 'deploy', 'refs/tags/v1.0.0', { no_count: true })
|
|
229
|
+
await repos.clone('acme', 'deploy', 'refs/tags/v1.0.0', {})
|
|
230
|
+
await repos.clone('acme', 'deploy', 'refs/tags/v1.0.0', { format: 'archive' })
|
|
231
|
+
assert.match(calls[0][1], /[?&]no_count=1/)
|
|
232
|
+
assert.doesNotMatch(calls[1][1], /no_count/)
|
|
233
|
+
assert.doesNotMatch(calls[2][1], /no_count/) // archive is the primary attempt — counts
|
|
234
|
+
})
|
|
235
|
+
})
|
package/src/engine/downloader.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const { error: { catch_errors, wrap_errors: e } } = require('puffy-core')
|
|
2
2
|
const repos_api = require('../api/repos')
|
|
3
3
|
|
|
4
|
-
const download = (owner, repo, ref) => catch_errors(`Download ${owner}/${repo} failed`, async () => {
|
|
5
|
-
const [errors, data] = await repos_api.clone(owner, repo, ref)
|
|
4
|
+
const download = (owner, repo, ref, options = {}) => catch_errors(`Download ${owner}/${repo} failed`, async () => {
|
|
5
|
+
const [errors, data] = await repos_api.clone(owner, repo, ref, options)
|
|
6
6
|
if (errors) throw e(`Failed to clone ${owner}/${repo}`, errors)
|
|
7
7
|
return data
|
|
8
8
|
})
|
package/src/engine/installer.js
CHANGED
|
@@ -204,8 +204,11 @@ const install = (skill, options = {}) => catch_errors('Install failed', async ()
|
|
|
204
204
|
clone_ref = arch_result.ref
|
|
205
205
|
clone_commit = arch_result.commit
|
|
206
206
|
} else {
|
|
207
|
-
// Fall back to JSON clone
|
|
208
|
-
|
|
207
|
+
// Fall back to JSON clone. The archive attempt above already hit the
|
|
208
|
+
// clone endpoint (and counted the install), so mark this retry
|
|
209
|
+
// no_count to avoid double-counting one install (download_count +
|
|
210
|
+
// repo.clone). See repos.js clone handler.
|
|
211
|
+
const [dl_errors, clone_data] = await download(owner, name, pkg.ref, { no_count: true })
|
|
209
212
|
if (dl_errors) { spinner.fail(`Download failed: ${pkg.skill}`); throw e(`Download ${pkg.skill} failed`, dl_errors) }
|
|
210
213
|
|
|
211
214
|
const [ext_errors] = await extract(clone_data, pkg_tmp)
|