docs: document release asset workflow
This commit is contained in:
parent
d1c80db665
commit
1371ec9aa9
4 changed files with 80 additions and 3 deletions
38
README.md
38
README.md
|
|
@ -625,7 +625,8 @@ Calls `GET /api/v1/repos/{owner}/{repo}/releases` and auto-paginates.
|
|||
|
||||
### `stoke release view`
|
||||
|
||||
Show the release for a tag, including its notes.
|
||||
Show the release for a tag, including its notes and attached assets. Each asset
|
||||
line includes its filename, size in bytes and download URL.
|
||||
|
||||
```text
|
||||
Options:
|
||||
|
|
@ -653,15 +654,46 @@ Options:
|
|||
-t, --title <title> release title (default: the tag name)
|
||||
-b, --body <body> release notes (markdown)
|
||||
--body-file <path> read the release notes from a file (wins over -b)
|
||||
--asset <path> attach an asset (repeatable)
|
||||
--asset-name <name> override the uploaded filename (exactly one asset)
|
||||
--draft create as a draft release
|
||||
--prerelease mark as a prerelease
|
||||
```
|
||||
|
||||
```bash
|
||||
stoke release create -o heavy-duty -r stoke --tag v1.3.0 --body-file release-notes.md
|
||||
stoke release create -o heavy-duty -r stoke --tag v1.3.0 \
|
||||
--body-file release-notes.md --asset dist/stoke_1.3.0_all.deb
|
||||
```
|
||||
|
||||
Calls `POST /api/v1/repos/{owner}/{repo}/releases`.
|
||||
The command prints the numeric release id, tag and URL. It calls
|
||||
`POST /api/v1/repos/{owner}/{repo}/releases`, then uploads each asset. If an
|
||||
upload fails, the release and any assets that already landed are kept; every
|
||||
asset is attempted, the command names successes and failures, and exits
|
||||
non-zero.
|
||||
|
||||
### `stoke release upload`
|
||||
|
||||
Attach one or more assets to an existing release. `--asset-name` overrides the
|
||||
uploaded filename and is valid only when exactly one `--asset` is supplied.
|
||||
|
||||
```text
|
||||
Options:
|
||||
-o, --owner <owner> repository owner (required)
|
||||
-r, --repo <repo> repository name (required)
|
||||
--tag <tag> tag name of the existing release (required)
|
||||
--asset <path> asset to upload (required, repeatable)
|
||||
--asset-name <name> override the uploaded filename (exactly one asset)
|
||||
```
|
||||
|
||||
```bash
|
||||
stoke release upload -o heavy-duty -r stoke --tag v1.3.0 \
|
||||
--asset dist/checksums.txt --asset dist/stoke_1.3.0_all.deb
|
||||
```
|
||||
|
||||
The command resolves the tag once with
|
||||
`GET /api/v1/repos/{owner}/{repo}/releases/tags/{tag}`, then uploads each file
|
||||
to the release's numeric-id asset endpoint. It attempts every asset and exits
|
||||
non-zero if any upload fails.
|
||||
|
||||
### `stoke label list`
|
||||
|
||||
|
|
|
|||
1
changelog.d/25.md
Normal file
1
changelog.d/25.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Release commands can now stream asset uploads, rename single assets, report partial failures, print release ids, and list attached files. (#25).
|
||||
|
|
@ -1072,6 +1072,12 @@ release
|
|||
console.log(`Target: ${rel.target_commitish}`);
|
||||
console.log(`Author: ${rel.author?.login || '(unknown)'}`);
|
||||
console.log(`Published: ${rel.published_at}`);
|
||||
if (rel.assets?.length) {
|
||||
console.log('\nAssets:');
|
||||
for (const asset of rel.assets) {
|
||||
console.log(` ${asset.name} (${asset.size} bytes) ${asset.browser_download_url}`);
|
||||
}
|
||||
}
|
||||
if (rel.body) {
|
||||
console.log('\n' + rel.body);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -229,6 +229,44 @@ test('release create keeps the release and reports landed and failed assets', as
|
|||
}
|
||||
});
|
||||
|
||||
test('release view lists attached assets with their sizes and download URLs', async () => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'stoke-release-view-assets-'));
|
||||
const cfg = path.join(dir, 'config.json');
|
||||
const server = http.createServer((req, res) => {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({
|
||||
id: 42,
|
||||
tag_name: 'v1',
|
||||
name: 'Version 1',
|
||||
html_url: 'https://forge.test/o/r/releases/v1',
|
||||
target_commitish: 'main',
|
||||
author: { login: 'bot' },
|
||||
published_at: '2026-08-30T00:00:00Z',
|
||||
body: '',
|
||||
assets: [
|
||||
{ name: 'first.bin', size: 12, browser_download_url: 'https://forge.test/assets/first.bin' },
|
||||
{ name: 'second.bin', size: 2048, browser_download_url: 'https://forge.test/assets/second.bin' },
|
||||
],
|
||||
}));
|
||||
});
|
||||
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
|
||||
fs.writeFileSync(cfg, JSON.stringify({ url: `http://127.0.0.1:${server.address().port}`, token: 'tok' }));
|
||||
|
||||
try {
|
||||
const result = await spawnAsync(
|
||||
['release', 'view', '-o', 'o', '-r', 'r', '--tag', 'v1'],
|
||||
{ STOKE_CONFIG_FILE: cfg },
|
||||
);
|
||||
assert.equal(result.status, 0, result.stderr);
|
||||
assert.match(result.stdout, /Assets:/);
|
||||
assert.match(result.stdout, /first\.bin \(12 bytes\) https:\/\/forge\.test\/assets\/first\.bin/);
|
||||
assert.match(result.stdout, /second\.bin \(2048 bytes\) https:\/\/forge\.test\/assets\/second\.bin/);
|
||||
} finally {
|
||||
await new Promise((resolve) => server.close(resolve));
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('repo create surfaces an organization permission failure and HTTP status', async () => {
|
||||
const cfg = path.join(os.tmpdir(), `stoke-cfg-${process.pid}-repo-create-403.json`);
|
||||
const requests = [];
|
||||
|
|
|
|||
Loading…
Reference in a new issue