Merge pull request 'Add apt distribution: deb packaging, registry publish, release automation' (#3) from feat/apt-packaging into main
This commit is contained in:
commit
f30f22daf4
12 changed files with 406 additions and 3 deletions
56
.forgejo/workflows/release.yml
Normal file
56
.forgejo/workflows/release.yml
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Release automation: on every v* tag, build the Debian package, publish it
|
||||
# to the Forgejo Debian registry (owner: heavy-duty) and attach the .deb to
|
||||
# the tag's release page as a fallback for direct `dpkg -i` installs.
|
||||
#
|
||||
# Requirements:
|
||||
# - A Forgejo Actions runner on the instance. Adjust `runs-on` to a label
|
||||
# your runner actually advertises (common: docker, ubuntu-latest).
|
||||
# - A repository/org secret RELEASE_TOKEN: a token with package:write and
|
||||
# repository:write scopes for an account allowed to publish packages
|
||||
# under the heavy-duty org.
|
||||
|
||||
name: release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
deb:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: node:22-bookworm
|
||||
steps:
|
||||
- name: Check out tag
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run tests
|
||||
run: npm ci && npm test
|
||||
|
||||
- name: Build .deb
|
||||
run: bash scripts/build-deb.sh
|
||||
|
||||
- name: Publish to Debian registry
|
||||
env:
|
||||
STOKE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
run: bash scripts/publish-deb.sh dist/stoke_*_all.deb heavy-duty stable main
|
||||
|
||||
- name: Create release and attach .deb
|
||||
env:
|
||||
TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
TAG: ${{ github.ref_name }}
|
||||
API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
DEB=$(ls dist/stoke_*_all.deb)
|
||||
# Create the release if it does not exist yet, then grab its id.
|
||||
RELEASE_ID=$(curl -sf -H "Authorization: token $TOKEN" "$API/releases/tags/$TAG" | node -pe "JSON.parse(require('fs').readFileSync(0,'utf8')).id" 2>/dev/null || true)
|
||||
if [ -z "$RELEASE_ID" ]; then
|
||||
RELEASE_ID=$(curl -sf -X POST -H "Authorization: token $TOKEN" -H 'Content-Type: application/json' \
|
||||
-d "{\"tag_name\":\"$TAG\",\"name\":\"$TAG\",\"draft\":false,\"prerelease\":false}" \
|
||||
"$API/releases" | node -pe "JSON.parse(require('fs').readFileSync(0,'utf8')).id")
|
||||
fi
|
||||
curl -sf -X POST -H "Authorization: token $TOKEN" \
|
||||
-F "attachment=@$DEB" \
|
||||
"$API/releases/$RELEASE_ID/assets?name=$(basename "$DEB")" >/dev/null
|
||||
echo "Attached $(basename "$DEB") to release $TAG"
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
node_modules/
|
||||
dist/
|
||||
*.log
|
||||
.DS_Store
|
||||
|
|
|
|||
78
README.md
78
README.md
|
|
@ -11,6 +11,33 @@ A command-line interface for [Forgejo](https://forgejo.org/), built with [Comman
|
|||
|
||||
## Installation
|
||||
|
||||
### With apt (Debian/Ubuntu — recommended)
|
||||
|
||||
The package is published to the Debian registry of the forge itself. One-time setup:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://forgejo.heavyduty.builders/heavy-duty/stoke/raw/branch/main/scripts/install-apt.sh | bash
|
||||
```
|
||||
|
||||
or manually:
|
||||
|
||||
```bash
|
||||
sudo install -d /etc/apt/keyrings
|
||||
curl -fsSL https://forgejo.heavyduty.builders/api/packages/heavy-duty/debian/repository.key \
|
||||
| sudo tee /etc/apt/keyrings/forgejo-heavy-duty.asc >/dev/null
|
||||
echo "deb [signed-by=/etc/apt/keyrings/forgejo-heavy-duty.asc] https://forgejo.heavyduty.builders/api/packages/heavy-duty/debian stable main" \
|
||||
| sudo tee /etc/apt/sources.list.d/forgejo-heavy-duty.list
|
||||
sudo apt-get update && sudo apt-get install stoke
|
||||
```
|
||||
|
||||
Upgrades then arrive through regular `apt-get upgrade`.
|
||||
|
||||
Note: apt releases that verify OpenPGP with `sqv` (Debian 13+, apt >= 2.9) currently reject the signature Forgejo generates for its Debian registry (an upstream signing bug). `install-apt.sh` detects this and falls back to a `[trusted=yes]` source — integrity then relies on HTTPS to the forge. The script prefers the signed source, so setups heal automatically once the forge is fixed.
|
||||
|
||||
As a fallback, each release also has the `.deb` attached for direct install: `sudo dpkg -i stoke_<version>_all.deb`.
|
||||
|
||||
### From source
|
||||
|
||||
```bash
|
||||
cd stoke
|
||||
npm install
|
||||
|
|
@ -390,6 +417,27 @@ stoke pr create -o heavy-duty -r stoke -t "Fix config handling" \
|
|||
|
||||
Calls `POST /api/v1/repos/{owner}/{repo}/pulls`.
|
||||
|
||||
### `stoke pr merge`
|
||||
|
||||
Merge a pull request.
|
||||
|
||||
```text
|
||||
Options:
|
||||
-o, --owner <owner> repository owner (required)
|
||||
-r, --repo <repo> repository name (required)
|
||||
-n, --number <number> pull request number (required)
|
||||
--method <method> merge, rebase, rebase-merge, squash (default: merge)
|
||||
--title <title> custom merge commit title
|
||||
--message <message> custom merge commit message
|
||||
--delete-branch delete the source branch after merging
|
||||
```
|
||||
|
||||
```bash
|
||||
stoke pr merge -o heavy-duty -r stoke -n 2 --delete-branch
|
||||
```
|
||||
|
||||
Calls `POST /api/v1/repos/{owner}/{repo}/pulls/{number}/merge`.
|
||||
|
||||
### `stoke branch list`
|
||||
|
||||
List branches in a repository.
|
||||
|
|
@ -601,6 +649,12 @@ test/
|
|||
├── cli.test.js # end-to-end CLI behavior (spawned processes)
|
||||
├── api.test.js # API client with a mocked fetch
|
||||
└── config.test.js # config path resolution and persistence
|
||||
scripts/
|
||||
├── build-deb.sh # build dist/stoke_<version>_all.deb
|
||||
├── publish-deb.sh # upload a .deb to the Forgejo Debian registry
|
||||
└── install-apt.sh # consumer-side apt source setup + install
|
||||
.forgejo/workflows/
|
||||
└── release.yml # tag-driven build + publish + release attachment
|
||||
```
|
||||
|
||||
- `cli.js` defines commands and options, handles prompts and prints results.
|
||||
|
|
@ -615,6 +669,30 @@ The test suite uses the Node.js built-in test runner — no extra dependencies:
|
|||
npm test
|
||||
```
|
||||
|
||||
## Packaging and releasing
|
||||
|
||||
The Debian package is a pure-JS `Architecture: all` package that ships the CLI to `/usr/lib/stoke` with a `/usr/bin/stoke` symlink and declares `Depends: nodejs (>= 22.12)`.
|
||||
|
||||
Build locally (needs `dpkg-deb`; runs `lintian` when installed):
|
||||
|
||||
```bash
|
||||
scripts/build-deb.sh # -> dist/stoke_<version>_all.deb
|
||||
```
|
||||
|
||||
Publish to the Forgejo Debian registry (uses `STOKE_TOKEN` or the `stoke auth login` token; the account needs package write access on the owner):
|
||||
|
||||
```bash
|
||||
scripts/publish-deb.sh dist/stoke_<version>_all.deb heavy-duty stable main
|
||||
```
|
||||
|
||||
Releases are automated in `.forgejo/workflows/release.yml`: pushing a `v*` tag runs the tests, builds the `.deb`, publishes it to the `heavy-duty` registry and attaches it to the tag's release page. The workflow needs a Forgejo Actions runner and a `RELEASE_TOKEN` secret (package + repository write for the `heavy-duty` org); adjust `runs-on` to a label your runner advertises.
|
||||
|
||||
Release checklist:
|
||||
|
||||
1. Bump `version` in `package.json` and `package-lock.json`.
|
||||
2. Commit, tag `v<version>`, push the tag.
|
||||
3. CI publishes the package; consumers get it with `apt-get update && apt-get upgrade`.
|
||||
|
||||
## Security notes
|
||||
|
||||
- Tokens are stored on disk with `0600` permissions.
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "stoke",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "stoke",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"commander": "^15.0.0"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "stoke",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"description": "CLI for the heavy-duty forge (Forgejo)",
|
||||
"main": "src/cli.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
104
scripts/build-deb.sh
Executable file
104
scripts/build-deb.sh
Executable file
|
|
@ -0,0 +1,104 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Build a Debian package for stoke.
|
||||
#
|
||||
# Usage: scripts/build-deb.sh
|
||||
#
|
||||
# Produces dist/stoke_<version>_all.deb from a clean staging copy of the
|
||||
# working tree (only src/, package.json and package-lock.json are shipped;
|
||||
# production dependencies are installed fresh with `npm ci --omit=dev`).
|
||||
#
|
||||
# Requirements: bash, node/npm, dpkg-deb, gzip. Runs lintian when available.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
VERSION="$(node -p "require('$ROOT/package.json').version")"
|
||||
MAINTAINER="${DEB_MAINTAINER:-Heavy Duty Builders <equipo@lafamilia.so>}"
|
||||
HOMEPAGE="https://forgejo.heavyduty.builders/heavy-duty/stoke"
|
||||
|
||||
STAGE="$(mktemp -d)"
|
||||
trap 'rm -rf "$STAGE"' EXIT
|
||||
|
||||
PKG="$STAGE/stoke_${VERSION}_all"
|
||||
LIB="$PKG/usr/lib/stoke"
|
||||
DOC="$PKG/usr/share/doc/stoke"
|
||||
|
||||
install -d "$PKG/DEBIAN" "$LIB" "$PKG/usr/bin" "$DOC"
|
||||
|
||||
# --- payload -----------------------------------------------------------------
|
||||
cp -r "$ROOT/src" "$LIB/src"
|
||||
cp "$ROOT/package.json" "$ROOT/package-lock.json" "$LIB/"
|
||||
(cd "$LIB" && npm ci --omit=dev --silent --no-audit --no-fund)
|
||||
|
||||
# Launcher: the CLI has a `#!/usr/bin/env node` shebang and resolves its
|
||||
# node_modules relative to its real path, so a symlink is all we need.
|
||||
ln -s ../lib/stoke/src/cli.js "$PKG/usr/bin/stoke"
|
||||
|
||||
# --- docs (lintian: copyright + Debian changelog) ----------------------------
|
||||
cat > "$DOC/copyright" <<EOF
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: stoke
|
||||
Source: $HOMEPAGE
|
||||
|
||||
Files: *
|
||||
Copyright: $(date +%Y) Heavy Duty Builders
|
||||
License: ISC
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
.
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
EOF
|
||||
|
||||
cat > "$STAGE/changelog" <<EOF
|
||||
stoke ($VERSION) stable; urgency=medium
|
||||
|
||||
* See the repository release page for notes.
|
||||
|
||||
-- $MAINTAINER $(date -R)
|
||||
EOF
|
||||
# Native package (no Debian revision in the version), so plain changelog.gz.
|
||||
gzip -9n -c "$STAGE/changelog" > "$DOC/changelog.gz"
|
||||
|
||||
# Normalize permissions regardless of the builder's umask: no group/other
|
||||
# write anywhere, executable entry point.
|
||||
chmod -R go-w "$PKG/usr"
|
||||
chmod 0755 "$LIB/src/cli.js"
|
||||
|
||||
# --- control -----------------------------------------------------------------
|
||||
INSTALLED_SIZE="$(du -sk --exclude=DEBIAN "$PKG" | cut -f1)"
|
||||
cat > "$PKG/DEBIAN/control" <<EOF
|
||||
Package: stoke
|
||||
Version: $VERSION
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Depends: nodejs (>= 22.12)
|
||||
Installed-Size: $INSTALLED_SIZE
|
||||
Maintainer: $MAINTAINER
|
||||
Homepage: $HOMEPAGE
|
||||
Description: CLI for the heavy-duty forge (Forgejo)
|
||||
stoke manages the Forgejo instance at forgejo.heavyduty.builders from the
|
||||
command line: authentication, repositories, issues, pull requests,
|
||||
branches, collaborators, organizations, teams and users.
|
||||
EOF
|
||||
|
||||
# --- build -------------------------------------------------------------------
|
||||
mkdir -p "$ROOT/dist"
|
||||
dpkg-deb --build --root-owner-group "$PKG" "$ROOT/dist/" >/dev/null
|
||||
DEB="$ROOT/dist/stoke_${VERSION}_all.deb"
|
||||
echo "Built: $DEB"
|
||||
|
||||
if command -v lintian >/dev/null 2>&1; then
|
||||
# binary-without-manpage is a known, accepted gap for now.
|
||||
lintian --suppress-tags binary-without-manpage "$DEB" || true
|
||||
else
|
||||
echo "Note: lintian not installed; skipping package lint."
|
||||
fi
|
||||
63
scripts/install-apt.sh
Executable file
63
scripts/install-apt.sh
Executable file
|
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# One-time setup to install stoke via apt on Debian/Ubuntu.
|
||||
#
|
||||
# Adds the heavy-duty Forgejo Debian registry as an APT source (with its
|
||||
# signing key) and installs the stoke package. Safe to re-run; afterwards
|
||||
# stoke upgrades through regular `apt-get upgrade`.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/install-apt.sh
|
||||
# FORGE_URL=... OWNER=... ./scripts/install-apt.sh # non-default instance
|
||||
#
|
||||
# Run as root or as a user with sudo.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
FORGE_URL="${FORGE_URL:-https://forgejo.heavyduty.builders}"
|
||||
OWNER="${OWNER:-heavy-duty}"
|
||||
DISTRIBUTION="${DISTRIBUTION:-stable}"
|
||||
COMPONENT="${COMPONENT:-main}"
|
||||
|
||||
KEYRING="/etc/apt/keyrings/forgejo-$OWNER.asc"
|
||||
LIST="/etc/apt/sources.list.d/forgejo-$OWNER.list"
|
||||
|
||||
SUDO=""
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
command -v sudo >/dev/null 2>&1 || { echo "error: run as root or install sudo" >&2; exit 1; }
|
||||
SUDO="sudo"
|
||||
fi
|
||||
|
||||
update_only_this_source() {
|
||||
$SUDO apt-get update \
|
||||
-o Dir::Etc::sourcelist="$LIST" \
|
||||
-o Dir::Etc::sourceparts=/dev/null \
|
||||
-o APT::Get::List-Cleanup=0
|
||||
}
|
||||
|
||||
echo "Adding APT source for $FORGE_URL/$OWNER ..."
|
||||
$SUDO install -d -m 0755 /etc/apt/keyrings
|
||||
curl -fsSL "$FORGE_URL/api/packages/$OWNER/debian/repository.key" | $SUDO tee "$KEYRING" >/dev/null
|
||||
echo "deb [signed-by=$KEYRING] $FORGE_URL/api/packages/$OWNER/debian $DISTRIBUTION $COMPONENT" \
|
||||
| $SUDO tee "$LIST" >/dev/null
|
||||
|
||||
# Newer apt verifies with sqv (Sequoia), which rejects the signature Forgejo
|
||||
# currently produces for its Debian registry (malformed Ed25519 MPI encoding
|
||||
# in the upstream signing library). Try the properly signed source first so
|
||||
# this heals automatically once the forge is fixed; otherwise fall back to
|
||||
# [trusted=yes] — package integrity then relies on HTTPS to our own forge.
|
||||
if ! update_only_this_source; then
|
||||
echo
|
||||
echo "WARNING: signature verification failed (known Forgejo registry issue" >&2
|
||||
echo "with sqv-based apt). Falling back to [trusted=yes]; transport" >&2
|
||||
echo "security is provided by HTTPS to $FORGE_URL." >&2
|
||||
echo
|
||||
echo "deb [trusted=yes] $FORGE_URL/api/packages/$OWNER/debian $DISTRIBUTION $COMPONENT" \
|
||||
| $SUDO tee "$LIST" >/dev/null
|
||||
update_only_this_source
|
||||
fi
|
||||
|
||||
$SUDO apt-get install -y stoke
|
||||
|
||||
echo
|
||||
stoke --version >/dev/null && echo "stoke $(stoke --version) installed. Run: stoke auth login"
|
||||
52
scripts/publish-deb.sh
Executable file
52
scripts/publish-deb.sh
Executable file
|
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Publish a .deb to the Forgejo Debian package registry.
|
||||
#
|
||||
# Usage: scripts/publish-deb.sh <path-to-deb> [owner] [distribution] [component]
|
||||
#
|
||||
# owner registry owner (user or org), default: heavy-duty
|
||||
# distribution APT distribution, default: stable
|
||||
# component APT component, default: main
|
||||
#
|
||||
# Authentication (first match wins):
|
||||
# 1. STOKE_TOKEN environment variable
|
||||
# 2. The token stored by `stoke auth login`
|
||||
#
|
||||
# The Forgejo URL defaults to the instance in the stoke config, falling back
|
||||
# to https://forgejo.heavyduty.builders. Override with FORGE_URL.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
DEB="${1:?usage: publish-deb.sh <path-to-deb> [owner] [distribution] [component]}"
|
||||
OWNER="${2:-heavy-duty}"
|
||||
DISTRIBUTION="${3:-stable}"
|
||||
COMPONENT="${4:-main}"
|
||||
|
||||
[ -f "$DEB" ] || { echo "error: no such file: $DEB" >&2; exit 1; }
|
||||
|
||||
CONFIG_JSON="$(node -e "const c = require('$ROOT/src/config').loadConfig(); if (c) process.stdout.write(JSON.stringify(c));" 2>/dev/null || true)"
|
||||
TOKEN="${STOKE_TOKEN:-$(node -pe "(JSON.parse(process.argv[1] || '{}').token) || ''" "$CONFIG_JSON")}"
|
||||
FORGE_URL="${FORGE_URL:-$(node -pe "(JSON.parse(process.argv[1] || '{}').url) || 'https://forgejo.heavyduty.builders'" "$CONFIG_JSON")}"
|
||||
|
||||
[ -n "$TOKEN" ] || { echo "error: no token. Set STOKE_TOKEN or run: stoke auth login" >&2; exit 1; }
|
||||
|
||||
URL="$FORGE_URL/api/packages/$OWNER/debian/pool/$DISTRIBUTION/$COMPONENT/upload"
|
||||
echo "Uploading $(basename "$DEB") to $URL"
|
||||
|
||||
STATUS="$(curl -sS -o /tmp/stoke-publish-response.$$ -w '%{http_code}' \
|
||||
-X PUT -H "Authorization: token $TOKEN" \
|
||||
--upload-file "$DEB" "$URL")"
|
||||
|
||||
case "$STATUS" in
|
||||
201) echo "Published." ;;
|
||||
409) echo "Already published (409): this exact version already exists in the registry." ;;
|
||||
*)
|
||||
echo "error: upload failed with HTTP $STATUS" >&2
|
||||
cat /tmp/stoke-publish-response.$$ >&2 || true
|
||||
rm -f /tmp/stoke-publish-response.$$
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
rm -f /tmp/stoke-publish-response.$$
|
||||
|
|
@ -167,6 +167,10 @@ class ForgejoClient {
|
|||
return this.post(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls`, payload);
|
||||
}
|
||||
|
||||
async mergePullRequest(owner, repo, index, payload) {
|
||||
return this.post(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${index}/merge`, payload);
|
||||
}
|
||||
|
||||
async listBranches(owner, repo, opts = {}) {
|
||||
return this.getAll(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/branches`, opts);
|
||||
}
|
||||
|
|
|
|||
30
src/cli.js
30
src/cli.js
|
|
@ -649,6 +649,36 @@ pr
|
|||
}
|
||||
});
|
||||
|
||||
pr
|
||||
.command('merge')
|
||||
.description('Merge a pull request')
|
||||
.requiredOption('-o, --owner <owner>', 'repository owner')
|
||||
.requiredOption('-r, --repo <repo>', 'repository name')
|
||||
.requiredOption('-n, --number <number>', 'pull request number', parseId)
|
||||
.option('--method <method>', 'merge method: merge, rebase, rebase-merge, squash', 'merge')
|
||||
.option('--title <title>', 'custom merge commit title')
|
||||
.option('--message <message>', 'custom merge commit message')
|
||||
.option('--delete-branch', 'delete the source branch after merging', false)
|
||||
.action(async (options) => {
|
||||
try {
|
||||
const config = loadConfig();
|
||||
const client = ForgejoClient.fromConfig(config);
|
||||
const payload = {
|
||||
Do: options.method,
|
||||
delete_branch_after_merge: options.deleteBranch,
|
||||
};
|
||||
if (options.title) payload.MergeTitleField = options.title;
|
||||
if (options.message) payload.MergeMessageField = options.message;
|
||||
await client.mergePullRequest(options.owner, options.repo, options.number, payload);
|
||||
console.log(`Merged !${options.number} in ${options.owner}/${options.repo} (${options.method}).`);
|
||||
if (options.deleteBranch) console.log('Source branch deleted.');
|
||||
} catch (err) {
|
||||
console.error(`Pull request merge failed: ${err.message}`);
|
||||
if (err.status) console.error(`HTTP status: ${err.status}`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
const branchCmd = program
|
||||
.command('branch')
|
||||
.description('Manage branches');
|
||||
|
|
|
|||
|
|
@ -122,6 +122,15 @@ test('searchUsers unwraps the {data: []} envelope and paginates', async () => {
|
|||
assert.equal(users.at(-1).login, 'last');
|
||||
});
|
||||
|
||||
test('mergePullRequest posts the merge payload to the merge endpoint', async () => {
|
||||
const calls = mockFetch(() => jsonResponse(null, 200));
|
||||
const client = new ForgejoClient('https://forge.test', 'tok');
|
||||
await client.mergePullRequest('owner', 'repo', 7, { Do: 'squash', delete_branch_after_merge: true });
|
||||
assert.equal(calls[0].url, 'https://forge.test/api/v1/repos/owner/repo/pulls/7/merge');
|
||||
assert.equal(calls[0].opts.method, 'POST');
|
||||
assert.deepEqual(JSON.parse(calls[0].opts.body), { Do: 'squash', delete_branch_after_merge: true });
|
||||
});
|
||||
|
||||
test('createIssue and createPullRequest hit the expected endpoints', async () => {
|
||||
const calls = mockFetch(() => jsonResponse({ number: 1 }));
|
||||
const client = new ForgejoClient('https://forge.test', 'tok');
|
||||
|
|
|
|||
|
|
@ -55,6 +55,12 @@ test('invalid --team-id is rejected before any network call', () => {
|
|||
assert.match(res.stderr, /Id must be a positive integer/);
|
||||
});
|
||||
|
||||
test('pr merge validates --number before any network call', () => {
|
||||
const res = run(['pr', 'merge', '-o', 'o', '-r', 'r', '-n', 'seven']);
|
||||
assert.equal(res.status, 1);
|
||||
assert.match(res.stderr, /Id must be a positive integer/);
|
||||
});
|
||||
|
||||
test('issue create --body-file reports unreadable files cleanly', () => {
|
||||
const cfg = path.join(os.tmpdir(), `stoke-cfg-${process.pid}.json`);
|
||||
fs.writeFileSync(cfg, JSON.stringify({ url: 'https://forge.test', token: 'tok' }));
|
||||
|
|
|
|||
Loading…
Reference in a new issue