Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package glab for openSUSE:Factory checked in at 2026-02-02 14:57:51 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/glab (Old) and /work/SRC/openSUSE:Factory/.glab.new.1995 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "glab" Mon Feb 2 14:57:51 2026 rev:67 rq:1330343 version:1.82.0 Changes: -------- --- /work/SRC/openSUSE:Factory/glab/glab.changes 2026-01-22 15:18:48.121788414 +0100 +++ /work/SRC/openSUSE:Factory/.glab.new.1995/glab.changes 2026-02-02 14:58:15.698297217 +0100 @@ -1,0 +2,68 @@ +Fri Jan 30 06:20:10 UTC 2026 - Johannes Kastl <[email protected]> + +- Update to version 1.82.0: + * Features + - 5ff014e1: feat(installer): Add dark mode support in + setup_windows.iss (RC Chuah + [email protected]) + - 1304c75f: feat(installer): improve PATH management with + programmatic control (Oldřich Jedlička [email protected]) + - e170e21f: feat: add -d- flag support to create commands (Kai + Armstrong [email protected]) + - e53cde38: feat: support OAuth2 Access Token only + authentication (Timo Furrer [email protected]) + * Bug Fixes + - 7883dc24: fix(glrepo): use Host instead of Hostname in + FromURL function (Swapnaneel Patra [email protected]) + - 3aca775d: fix(stack): reorder spinner is displayed over + terminal-based editor (Sandro Sauer [email protected]) + - ba8e90ef: fix: normalize nil slices to empty arrays in JSON + output (Kai Armstrong [email protected]) + - 9cc43eee: fix: suppress informational messages in JSON output + mode (Kai Armstrong [email protected]) + * Documentation + - e501773c: docs: Remove EOL spaces in doc files - 2026-01-28 + (Marcel Amirault [email protected]) + - e6e2eb2d: docs: Remove trailing dot from auth login URL tip + (Benedikt Reinartz [email protected]) + - 56e9970b: docs: Update SHA description for glab mr merge + (Brendan Lynch [email protected]) + - 4c4e16ea: docs: Update install from source instructions (Tom + Elliff-O'Shea [email protected]) + * Dependencies + - 52d33754: chore(deps): update dependency golangci-lint to + v2.8.0 (GitLab Renovate Bot [email protected]) + - 2d5732d4: chore(deps): update golangci-lint version in CI + (Ahmed Hemdan [email protected]) + - b3f9cc6c: chore(deps): update module github.com/docker/cli to + v29.2.0+incompatible (GitLab Renovate Bot + [email protected]) + - 807598a6: chore(deps): update module + github.com/gdamore/tcell/v2 to v2.13.8 (GitLab Renovate Bot + [email protected]) + - b5de98e9: chore(deps): update module + github.com/golang-jwt/jwt/v5 to v5.3.1 (GitLab Renovate Bot + [email protected]) + * Maintenance + - 614c42a5: chore(ci): remove unnecessary pre-build step from + prepare_go_cache (Kai Armstrong [email protected]) + - dcba5bc1: chore(docs): gen-docs for mr list (Filip Aleksic + [email protected]) + - ab44a3bf: chore: Add fdignore to gitignore file (Gary Holtz + [email protected]) + - 2406d1c7: chore: Adding fdignore to prevent markdown edits + (Gary Holtz [email protected]) + - 7c5b1dfa: chore: clarify sort/order flags and add consistency + improvements (Kai Armstrong [email protected]) + - 4f107308: chore: enable a few linters (Oleksandr Redko + [email protected]) + - 428e1d2b: ci(deps): remove GOLANGCI_LINT_VERSION from CI + configuration (Ahmed Hemdan [email protected]) + * Others + - a8c7c1b6: Revert "Merge branch 'gmh-add-fdignore' into + 'main'" (Gary Holtz [email protected]) + - 21d1ef08: test: fix expires value in + TestClear_WithRevoke_ActiveToken (Oleksandr Redko + [email protected]) + +------------------------------------------------------------------- Old: ---- glab-1.81.0.obscpio New: ---- glab-1.82.0.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ glab.spec ++++++ --- /var/tmp/diff_new_pack.py1E9v/_old 2026-02-02 14:58:17.090356057 +0100 +++ /var/tmp/diff_new_pack.py1E9v/_new 2026-02-02 14:58:17.090356057 +0100 @@ -18,7 +18,7 @@ Name: glab -Version: 1.81.0 +Version: 1.82.0 Release: 0 Summary: A GitLab command line tool License: MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.py1E9v/_old 2026-02-02 14:58:17.138358086 +0100 +++ /var/tmp/diff_new_pack.py1E9v/_new 2026-02-02 14:58:17.146358424 +0100 @@ -3,7 +3,7 @@ <param name="url">https://gitlab.com/gitlab-org/cli.git</param> <param name="scm">git</param> <param name="package-meta">yes</param> - <param name="revision">v1.81.0</param> + <param name="revision">v1.82.0</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.py1E9v/_old 2026-02-02 14:58:17.166359270 +0100 +++ /var/tmp/diff_new_pack.py1E9v/_new 2026-02-02 14:58:17.170359439 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://gitlab.com/gitlab-org/cli.git</param> - <param name="changesrevision">01fd7984c91ff91b54646a16cb4f571d6164911e</param></service></servicedata> + <param name="changesrevision">b932a8d879a24d02e0c094aa178c7442c9a589a5</param></service></servicedata> (No newline at EOF) ++++++ glab-1.81.0.obscpio -> glab-1.82.0.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/.git/HEAD new/glab-1.82.0/.git/HEAD --- old/glab-1.81.0/.git/HEAD 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/.git/HEAD 2026-01-29 09:31:59.000000000 +0100 @@ -1 +1 @@ -01fd7984c91ff91b54646a16cb4f571d6164911e +b932a8d879a24d02e0c094aa178c7442c9a589a5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/.git/ORIG_HEAD new/glab-1.82.0/.git/ORIG_HEAD --- old/glab-1.81.0/.git/ORIG_HEAD 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/.git/ORIG_HEAD 2026-01-29 09:31:59.000000000 +0100 @@ -1 +1 @@ -01fd7984c91ff91b54646a16cb4f571d6164911e +b932a8d879a24d02e0c094aa178c7442c9a589a5 Binary files old/glab-1.81.0/.git/index and new/glab-1.82.0/.git/index differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/.git/logs/HEAD new/glab-1.82.0/.git/logs/HEAD --- old/glab-1.81.0/.git/logs/HEAD 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/.git/logs/HEAD 2026-01-29 09:31:59.000000000 +0100 @@ -1,2 +1,2 @@ -0000000000000000000000000000000000000000 08e5da124858fb9a0296764f3dd182126ec2dee5 kastl <[email protected]> 1768998566 +0100 clone: from https://gitlab.com/gitlab-org/cli.git -08e5da124858fb9a0296764f3dd182126ec2dee5 01fd7984c91ff91b54646a16cb4f571d6164911e kastl <[email protected]> 1768998566 +0100 checkout: moving from main to v1.81.0 +0000000000000000000000000000000000000000 b932a8d879a24d02e0c094aa178c7442c9a589a5 kastl <[email protected]> 1769754009 +0100 clone: from https://gitlab.com/gitlab-org/cli.git +b932a8d879a24d02e0c094aa178c7442c9a589a5 b932a8d879a24d02e0c094aa178c7442c9a589a5 kastl <[email protected]> 1769754009 +0100 checkout: moving from main to v1.82.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/.git/logs/refs/heads/main new/glab-1.82.0/.git/logs/refs/heads/main --- old/glab-1.81.0/.git/logs/refs/heads/main 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/.git/logs/refs/heads/main 2026-01-29 09:31:59.000000000 +0100 @@ -1 +1 @@ -0000000000000000000000000000000000000000 08e5da124858fb9a0296764f3dd182126ec2dee5 kastl <[email protected]> 1768998566 +0100 clone: from https://gitlab.com/gitlab-org/cli.git +0000000000000000000000000000000000000000 b932a8d879a24d02e0c094aa178c7442c9a589a5 kastl <[email protected]> 1769754009 +0100 clone: from https://gitlab.com/gitlab-org/cli.git diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/.git/logs/refs/remotes/origin/HEAD new/glab-1.82.0/.git/logs/refs/remotes/origin/HEAD --- old/glab-1.81.0/.git/logs/refs/remotes/origin/HEAD 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/.git/logs/refs/remotes/origin/HEAD 2026-01-29 09:31:59.000000000 +0100 @@ -1 +1 @@ -0000000000000000000000000000000000000000 08e5da124858fb9a0296764f3dd182126ec2dee5 kastl <[email protected]> 1768998566 +0100 clone: from https://gitlab.com/gitlab-org/cli.git +0000000000000000000000000000000000000000 b932a8d879a24d02e0c094aa178c7442c9a589a5 kastl <[email protected]> 1769754009 +0100 clone: from https://gitlab.com/gitlab-org/cli.git Binary files old/glab-1.81.0/.git/objects/pack/pack-aa19607e5a2439a157fbcfff5545d32e2310d717.idx and new/glab-1.82.0/.git/objects/pack/pack-aa19607e5a2439a157fbcfff5545d32e2310d717.idx differ Binary files old/glab-1.81.0/.git/objects/pack/pack-aa19607e5a2439a157fbcfff5545d32e2310d717.pack and new/glab-1.82.0/.git/objects/pack/pack-aa19607e5a2439a157fbcfff5545d32e2310d717.pack differ Binary files old/glab-1.81.0/.git/objects/pack/pack-aa19607e5a2439a157fbcfff5545d32e2310d717.rev and new/glab-1.82.0/.git/objects/pack/pack-aa19607e5a2439a157fbcfff5545d32e2310d717.rev differ Binary files old/glab-1.81.0/.git/objects/pack/pack-c8e19de87d4f13c07ba63a1f806394cd280ec96a.idx and new/glab-1.82.0/.git/objects/pack/pack-c8e19de87d4f13c07ba63a1f806394cd280ec96a.idx differ Binary files old/glab-1.81.0/.git/objects/pack/pack-c8e19de87d4f13c07ba63a1f806394cd280ec96a.pack and new/glab-1.82.0/.git/objects/pack/pack-c8e19de87d4f13c07ba63a1f806394cd280ec96a.pack differ Binary files old/glab-1.81.0/.git/objects/pack/pack-c8e19de87d4f13c07ba63a1f806394cd280ec96a.rev and new/glab-1.82.0/.git/objects/pack/pack-c8e19de87d4f13c07ba63a1f806394cd280ec96a.rev differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/.git/packed-refs new/glab-1.82.0/.git/packed-refs --- old/glab-1.81.0/.git/packed-refs 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/.git/packed-refs 2026-01-29 09:31:59.000000000 +0100 @@ -12,7 +12,7 @@ 84da1fad46433768ab745dc15943740bd163d1f5 refs/remotes/origin/1209-test-rename-issuable 241ce1b7cc4f331d6b0605187478a30111a897fc refs/remotes/origin/1273-developer-productivity-commands 96443d099cddcc82c9df00d55ddf9dbb04713a3e refs/remotes/origin/7515-implement-ai-gateway-integration-for-git-natural-language-assistant -ff6b38bab8441835ac25e886986233c1062f1d60 refs/remotes/origin/7524-work-items-cli-list-cmd +cbebc6853b13c3b44ed6c825a3dbcfd8a0cd8292 refs/remotes/origin/7524-work-items-cli-list-cmd 2468cad40e311ecbc8bbc6e3b24cfddc3d490d31 refs/remotes/origin/7553-normalize-go-version-and-references-across-codebase a553dab01fef6b6c6474d5dbd12438fb59e89657 refs/remotes/origin/7574-test-and-fix-stacked-diffs-in-forks 69f4bd6388e75e8e25361c04f769f53f618d6625 refs/remotes/origin/7628-add-iteration-filtering-to-list-issues @@ -21,6 +21,7 @@ 7f7cdf3148a37373fade2d963b5a25a4a9eddc39 refs/remotes/origin/7707-convert-mr-checkout-tests-to-newer-stack-git-tests f6098f2f7f05a6c66be9983b9c42cae9cde211bb refs/remotes/origin/7793-markdown-rendering-doesn-t-work-on-light-terminal-backgrounds 1c0aa5eee027f86a3d2d40f0c34f23da922b19c9 refs/remotes/origin/7946-aqualls-add-conda-forge +9bb6d10d6bb78f6ef61bf5aa0b713abedf716a91 refs/remotes/origin/8122-test-to-generate-links 9067893000a88f7082bfb562d18e0eae38310e27 refs/remotes/origin/acalder/fix-hostname-port-stripping 4a0e82370ca135badadf00afbb59b96cffb65386 refs/remotes/origin/add-award-emojis 313b2a437fb024a9febc406469709c5bf12a0b7c refs/remotes/origin/add-build_docs-ci-job @@ -46,12 +47,12 @@ cfa9bc36ce6df76f62ebc4127ce8f453a7ccc7ca refs/remotes/origin/debug-danger-review 3fb2e429ec5f79e2acabab526fb6b90008d0a71e refs/remotes/origin/devcontainer-support 88df561ca57b6f7f9b45f28cfffed6d95c4dcb3c refs/remotes/origin/docs-markdown +c967caf3510b4dac5c6942c3d248c3bb594014c4 refs/remotes/origin/duo-cli-credential-helper f4c4781693a194664201f44a5330b42f9f0cf4da refs/remotes/origin/faleksic-ci-status-default-repo 72bc4589228cfdcb20481dc2bb85b18d0cdeff09 refs/remotes/origin/faleksic-glamour-markdown d3409aee5d5d6d435cc9bf7231b8a63db04928ba refs/remotes/origin/fix-7235-debug 7cb0aa9ea0de6fdfe84497cc0f1523f3fa93a100 refs/remotes/origin/fix-build-test-artifact-size 87b3d6c230773d7e764e285454472ffba87ff7d5 refs/remotes/origin/fix-issue-8084-client-host-resolution -2678759f27e2afce6af07e9cfc3b323b8521c3e4 refs/remotes/origin/fix-json-null-empty-arrays 1acc8e0e590df4842e280a37995f56f2d3927808 refs/remotes/origin/fix-release-grouping-regex ee3108676aec373534c1af0bb02261aa80801e88 refs/remotes/origin/freinink/secure-files-download-all-poc 02388706d5d43148a65a3009fa8ec64eea13b3a5 refs/remotes/origin/fvpotvin-remove-dpop-package @@ -65,7 +66,6 @@ 1879a6bd78f339d50175f07020234e84b2d8b8e5 refs/remotes/origin/github/fork/tommyalatalo/add-group-issue-boards 925c168bade5964fec4a3bf1437406b0ee8879ce refs/remotes/origin/github/fork/zemzale/chore-update-go-gitlab d519cc2717bc66e64e1399bea714e6fc81dc8c7a refs/remotes/origin/gmh-add-date-to-user-events -2406d1c7a85e81d33eae66d930ee1ef9e10c5055 refs/remotes/origin/gmh-add-fdignore 98be9a9060c9d2833a75cdf83471cf651d40c027 refs/remotes/origin/gmh-add-raw-diff 7ea2339304d43f2d85ec784b0b54555178dacd9b refs/remotes/origin/gmh-add-sync-options d304bcc31fedf63865fc807e0a0d01ab398b2311 refs/remotes/origin/gmh-add-threadsafe-httpclient @@ -110,7 +110,7 @@ b28e0676c02c685f8416d477de06258513b60f4d refs/remotes/origin/lint-eol-spaces-2025-12-15-0956 6df24068b7c50b214feda536e6886825bbe5c569 refs/remotes/origin/lint-eol-spaces-2025-12-24-1115 20181019c29ddc98367ba18fa57a089f3a654592 refs/remotes/origin/lint-eol-spaces-2025-12-24-1140 -08e5da124858fb9a0296764f3dd182126ec2dee5 refs/remotes/origin/main +b932a8d879a24d02e0c094aa178c7442c9a589a5 refs/remotes/origin/main 1ca15d4ed9cb19ce355d2a33fba658fd80f0e740 refs/remotes/origin/msj-linux-links 46ba6fefaefbb458bcc36a687e679c5b512f2cf6 refs/remotes/origin/nagyv-agent-bootstrap 4f1a48190a0f3889728aec8d1fb242bf609f7ce8 refs/remotes/origin/new-branch-with-codex @@ -133,12 +133,11 @@ ed1274e956c9280950ff3b88f0495a8562719f24 refs/remotes/origin/refactor-flags-pkg 7f6b43e191282b4f0199aaf34026bed3a765db81 refs/remotes/origin/refactor-repository-resolver 8da748fd27ab2f90b7751ea23fc2428d35de9889 refs/remotes/origin/release-create-debug -614c42a5ee02c4da06c4188137138fe19a1ffe45 refs/remotes/origin/remove-unnecessary-prebuild a400c127d0ada31a255fe98f62e8f2b0d03d22ef refs/remotes/origin/remove-unused-systemStdOut-iostreams fc7de15ef4760d24e506b5ebaa29347ed8692eb4 refs/remotes/origin/renovate/golang 6de52dc9951f00949150594cb04b318efc3d2560 refs/remotes/origin/revert-f114f6fe d74ec68482b39466a19b25e39dbe564f1607bc25 refs/remotes/origin/revert-push-to-main -123da85b10cf074b41099a4dd60d4d8b2654d891 refs/remotes/origin/ryaanwells/mr-note-update +4397719157101ff1a6fd4b88332e9dc036f85b4c refs/remotes/origin/ryaanwells/mr-note-update 39670d0bb66c04b95c9b4cb5a3270becab04bf3e refs/remotes/origin/smoke-test-gitlab-test-client 36e6ad559a50d3d2e9b5bc8d1116b0c0463f508c refs/remotes/origin/spatnaik/agentic_chat 5dee004bc44943a2348b10f5db79730edd68bb8f refs/remotes/origin/spatnaik/claude_code_support @@ -387,5 +386,6 @@ 1ede85f256e43ada099d122140a2ebefd6ac6af8 refs/tags/v1.80.3 f4b518e9120bb54002d9cda816d3011de757feea refs/tags/v1.80.4 01fd7984c91ff91b54646a16cb4f571d6164911e refs/tags/v1.81.0 +b932a8d879a24d02e0c094aa178c7442c9a589a5 refs/tags/v1.82.0 5c4c0925d98906b696844fecdb5f4c1dfd533282 refs/tags/v1.9.0 b742361dffc3e31b45e99b91a4f9a6e907bafa75 refs/tags/v1.9.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/.git/refs/heads/main new/glab-1.82.0/.git/refs/heads/main --- old/glab-1.81.0/.git/refs/heads/main 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/.git/refs/heads/main 2026-01-29 09:31:59.000000000 +0100 @@ -1 +1 @@ -08e5da124858fb9a0296764f3dd182126ec2dee5 +b932a8d879a24d02e0c094aa178c7442c9a589a5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/.gitignore new/glab-1.82.0/.gitignore --- old/glab-1.81.0/.gitignore 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/.gitignore 2026-01-29 09:31:59.000000000 +0100 @@ -74,3 +74,6 @@ # See https://gitlab.com/gitlab-org/cli/-/merge_requests/2387#note_2751915451 /.gitlab-secrets + +# fd utility +.fdignore diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/.gitlab-ci.yml new/glab-1.82.0/.gitlab-ci.yml --- old/glab-1.81.0/.gitlab-ci.yml 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/.gitlab-ci.yml 2026-01-29 09:31:59.000000000 +0100 @@ -1,6 +1,5 @@ variables: GO_VERSION: "1.25.4" - GOLANGCI_LINT_VERSION: "2.7.2" GITLAB_ADVANCED_SAST_ENABLED: "true" .code-patterns: &code-patterns @@ -102,8 +101,6 @@ prefix: prepare script: - go mod download - # Pre-compile standard library and common packages to warm up GOCACHE - - go build -o /dev/null ./... .documentation: stage: documentation @@ -240,7 +237,7 @@ needs: - job: prepare_go_cache optional: true - image: ${GITLAB_DEPENDENCY_PROXY}golangci/golangci-lint:v$GOLANGCI_LINT_VERSION + image: ${GITLAB_DEPENDENCY_PROXY}golangci/golangci-lint:v2.8.0 cache: key: prefix: lint diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/.golangci.yml new/glab-1.82.0/.golangci.yml --- old/glab-1.81.0/.golangci.yml 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/.golangci.yml 2026-01-29 09:31:59.000000000 +0100 @@ -6,12 +6,16 @@ linters: default: none enable: + - copyloopvar - depguard - dupword - errcheck - govet - ineffassign + - makezero + - mirror - modernize + - nilnesserr - nonamedreturns - staticcheck - unconvert diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/.tool-versions new/glab-1.82.0/.tool-versions --- old/glab-1.81.0/.tool-versions 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/.tool-versions 2026-01-29 09:31:59.000000000 +0100 @@ -1,7 +1,7 @@ golang 1.25.4 # For linting and formatting Go code (includes gofumpt and goimports) -golangci-lint 2.7.2 +golangci-lint 2.8.0 # For linting shell scripts shellcheck 0.11.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/README.md new/glab-1.82.0/README.md --- old/glab-1.81.0/README.md 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/README.md 2026-01-29 09:31:59.000000000 +0100 @@ -164,13 +164,14 @@ To build from source: -1. Run the command `go version` to verify that you have the minimum required Go version. - If `go` is not installed, follow instructions on [the Go website](https://go.dev/doc/install). -1. Run the `go install gitlab.com/gitlab-org/cli/cmd/glab@main` to install `glab` cmd in `$GOPATH/bin`. -1. The sources of `glab` will be in `$GOPATH/src/gitlab.com/gitlab-org/cli`. -1. If you do not have `$GOPATH/bin` or `$GOBIN` in your `$PATH`, run `export PATH=$PWD/bin:$PATH` - to update your PATH with the newly compiled project. -1. Run `glab version` to confirm that it worked. +1. Run `go version` to verify that you have the minimum required Go version. + If Go is not installed, see [Download and install](https://go.dev/doc/install). +1. Clone the repository: `git clone https://gitlab.com/gitlab-org/cli.git` +1. Build the binary: `make build` +1. Install `glab` in `$GOPATH/bin`: `make install` +1. Optional. If `$GOPATH/bin` or `$GOBIN` is not in your `$PATH`, + run `export PATH=$PWD/bin:$PATH`. +1. Confirm the installation: `glab version` ## Authentication diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/docs/source/auth/status.md new/glab-1.82.0/docs/source/auth/status.md --- old/glab-1.81.0/docs/source/auth/status.md 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/docs/source/auth/status.md 2026-01-29 09:31:59.000000000 +0100 @@ -16,9 +16,9 @@ Verifies and displays information about your authentication state. -By default, this command checks the authentication state of the GitLab instance -determined by your current context (`git remote`, `GITLAB_HOST` environment variable, -or configuration). Use `--all` to check all configured instances, or `--hostname` to +By default, this command checks the authentication state of the GitLab instance +determined by your current context (`git remote`, `GITLAB_HOST` environment variable, +or configuration). Use `--all` to check all configured instances, or `--hostname` to check a specific instance. ```plaintext diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/docs/source/ci/list.md new/glab-1.82.0/docs/source/ci/list.md --- old/glab-1.81.0/docs/source/ci/list.md 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/docs/source/ci/list.md 2026-01-29 09:31:59.000000000 +0100 @@ -28,14 +28,14 @@ ```plaintext -n, --name string Return only pipelines with the given name. - -o, --orderBy string Order pipelines by this field. Options: id, status, ref, updated_at, user_id. (default "id") + -o, --order string Order pipelines by this field. Options: id, status, ref, updated_at, user_id. (default "id") -F, --output string Format output. Options: text, json. (default "text") -p, --page int Page number. (default 1) -P, --per-page int Number of items to list per page. (default 30) -r, --ref string Return only pipelines for given ref. --scope string Return only pipelines with the given scope: {running|pending|finished|branches|tags} --sha string Return only pipelines with the given SHA. - --sort string Sort pipelines. Options: asc, desc. (default "desc") + --sort string Sort direction for --order field: asc or desc. (default "desc") --source string Return only pipelines triggered via the given source. See https://docs.gitlab.com/ci/jobs/job_rules/#ci_pipeline_source-predefined-variable for full list. Commonly used options: {merge_request_event|parent_pipeline|pipeline|push|trigger} -s, --status string Get pipeline with this status. Options: running, pending, success, failed, canceled, skipped, created, manual, waiting_for_resource, preparing, scheduled -a, --updated-after string Return only pipelines updated after the specified date. Expected in ISO 8601 format (2019-03-15T08:00:00Z). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/docs/source/incident/list.md new/glab-1.82.0/docs/source/incident/list.md --- old/glab-1.81.0/docs/source/incident/list.md 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/docs/source/incident/list.md 2026-01-29 09:31:59.000000000 +0100 @@ -55,7 +55,7 @@ -P, --per-page int Number of items to list per page. (default 30) -R, --repo OWNER/REPO Select another repository. Can use either OWNER/REPO or `GROUP/NAMESPACE/REPO` format. Also accepts full URL or Git URL. --search string Search <string> in the fields defined by '--in'. - --sort string Return incident sorted in asc or desc order. (default "desc") + -s, --sort string Sort direction for --order field: asc or desc. (default "desc") ``` ## Options inherited from parent commands diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/docs/source/issue/create.md new/glab-1.82.0/docs/source/issue/create.md --- old/glab-1.81.0/docs/source/issue/create.md 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/docs/source/issue/create.md 2026-01-29 09:31:59.000000000 +0100 @@ -38,7 +38,7 @@ ```plaintext -a, --assignee usernames Assign issue to people by their usernames. Multiple usernames can be comma-separated or specified by repeating the flag. -c, --confidential Set an issue to be confidential. (default false) - -d, --description string Issue description. + -d, --description string Issue description. Set to "-" to open an editor. --due-date string A date in 'YYYY-MM-DD' format. --epic int ID of the epic to add the issue to. -l, --label strings Add label by name. Multiple labels can be comma-separated or specified by repeating the flag. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/docs/source/issue/list.md new/glab-1.82.0/docs/source/issue/list.md --- old/glab-1.81.0/docs/source/issue/list.md 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/docs/source/issue/list.md 2026-01-29 09:31:59.000000000 +0100 @@ -57,7 +57,7 @@ -P, --per-page int Number of items to list per page. (default 30) -R, --repo OWNER/REPO Select another repository. Can use either OWNER/REPO or `GROUP/NAMESPACE/REPO` format. Also accepts full URL or Git URL. --search string Search <string> in the fields defined by '--in'. - --sort string Return issue sorted in asc or desc order. (default "desc") + -s, --sort string Sort direction for --order field: asc or desc. (default "desc") ``` ## Options inherited from parent commands diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/docs/source/mr/create.md new/glab-1.82.0/docs/source/mr/create.md --- old/glab-1.81.0/docs/source/mr/create.md 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/docs/source/mr/create.md 2026-01-29 09:31:59.000000000 +0100 @@ -40,7 +40,7 @@ -a, --assignee usernames Assign merge request to people by their usernames. Multiple usernames can be comma-separated or specified by repeating the flag. --copy-issue-labels Copy labels from issue to the merge request. Used with --related-issue. --create-source-branch Create a source branch if it does not exist. - -d, --description string Supply a description for the merge request. + -d, --description string Supply a description for the merge request. Set to "-" to open an editor. --draft Mark merge request as a draft. -f, --fill push Do not prompt for title or description, and just use commit info. Sets push to `true`, and pushes the branch. --fill-commit-body Fill description with each commit body when multiple commits. Can only be used with --fill. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/docs/source/mr/list.md new/glab-1.82.0/docs/source/mr/list.md --- old/glab-1.81.0/docs/source/mr/list.md 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/docs/source/mr/list.md 2026-01-29 09:31:59.000000000 +0100 @@ -47,6 +47,8 @@ -a, --assignee strings Get only merge requests assigned to users. Multiple users can be comma-separated or specified by repeating the flag. --author string Filter merge request by author <username>. -c, --closed Get only closed merge requests. + --created-after time Filter merge requests created after a certain date (ISO 8601 format). + --created-before time Filter merge requests created after a certain date (ISO 8601 format). -d, --draft Filter by draft merge requests. -g, --group string Select a group/subgroup. This option is ignored if a repo argument is set. -l, --label strings Filter merge request by label <name>. Multiple labels can be comma-separated or specified by repeating the flag. @@ -61,7 +63,7 @@ -R, --repo OWNER/REPO Select another repository. Can use either OWNER/REPO or `GROUP/NAMESPACE/REPO` format. Also accepts full URL or Git URL. -r, --reviewer strings Get only merge requests with users as reviewer. Multiple users can be comma-separated or specified by repeating the flag. --search string Filter by <string> in title and description. - -S, --sort string Sort merge requests by <field>. Sort options: asc, desc. + -S, --sort string Sort direction for --order field: asc or desc. -s, --source-branch string Filter by source branch <name>. -t, --target-branch string Filter by target branch <name>. ``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/docs/source/mr/merge.md new/glab-1.82.0/docs/source/mr/merge.md --- old/glab-1.81.0/docs/source/mr/merge.md 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/docs/source/mr/merge.md 2026-01-29 09:31:59.000000000 +0100 @@ -41,7 +41,7 @@ -m, --message string Custom merge commit message. -r, --rebase Rebase the commits onto the base branch. -d, --remove-source-branch Remove source branch on merge. - --sha string Merge commit SHA. + --sha string Merge only if the HEAD of the source branch matches this SHA. Use to ensure that only reviewed commits are merged. -s, --squash Squash commits on merge. --squash-message string Custom squash commit message. -y, --yes Skip submission confirmation prompt. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/docs/source/repo/contributors.md new/glab-1.82.0/docs/source/repo/contributors.md --- old/glab-1.81.0/docs/source/repo/contributors.md 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/docs/source/repo/contributors.md 2026-01-29 09:31:59.000000000 +0100 @@ -40,7 +40,7 @@ -p, --page int Page number. (default 1) -P, --per-page int Number of items to list per page. (default 30) -R, --repo OWNER/REPO Select another repository. Can use either OWNER/REPO or `GROUP/NAMESPACE/REPO` format. Also accepts full URL or Git URL. - -s, --sort string Return contributors. Sort options: asc, desc. + -s, --sort string Sort direction for --order field: asc or desc. ``` ## Options inherited from parent commands diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/docs/source/repo/create.md new/glab-1.82.0/docs/source/repo/create.md --- old/glab-1.81.0/docs/source/repo/create.md 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/docs/source/repo/create.md 2026-01-29 09:31:59.000000000 +0100 @@ -50,7 +50,7 @@ ```plaintext --defaultBranch master Default branch of the project. Defaults to master if not provided. - -d, --description string Description of the new project. + -d, --description string Description of the new project. Set to "-" to open an editor. -g, --group string Namespace or group for the new project. Defaults to the current user's namespace. --internal Make project internal: visible to any authenticated user. Default. -n, --name string Name of the new project. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/docs/source/repo/list.md new/glab-1.82.0/docs/source/repo/list.md --- old/glab-1.81.0/docs/source/repo/list.md 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/docs/source/repo/list.md 2026-01-29 09:31:59.000000000 +0100 @@ -42,7 +42,7 @@ -F, --output string Format output as: text, json. (default "text") -p, --page int Page number. (default 1) -P, --per-page int Number of items to list per page. (default 30) - -s, --sort string Return repositories sorted in asc or desc order. + -s, --sort string Sort direction for --order field: asc or desc. --starred List only starred projects. -u, --user string List user projects. ``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/docs/source/snippet/create.md new/glab-1.82.0/docs/source/snippet/create.md --- old/glab-1.81.0/docs/source/snippet/create.md 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/docs/source/snippet/create.md 2026-01-29 09:31:59.000000000 +0100 @@ -37,7 +37,7 @@ ## Options ```plaintext - -d, --description string Description of the snippet. + -d, --description string Description of the snippet. Set to "-" to open an editor. -f, --filename string Filename of the snippet in GitLab. -p, --personal Create a personal snippet. -t, --title string (required) Title of the snippet. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/go.mod new/glab-1.82.0/go.mod --- old/glab-1.81.0/go.mod 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/go.mod 2026-01-29 09:31:59.000000000 +0100 @@ -17,11 +17,11 @@ github.com/charmbracelet/glamour v0.10.0 github.com/charmbracelet/huh v0.8.0 github.com/coder/websocket v1.8.14 - github.com/docker/cli v29.1.5+incompatible + github.com/docker/cli v29.2.0+incompatible github.com/docker/docker-credential-helpers v0.9.5 github.com/dustin/go-humanize v1.0.1 - github.com/gdamore/tcell/v2 v2.13.7 - github.com/golang-jwt/jwt/v5 v5.3.0 + github.com/gdamore/tcell/v2 v2.13.8 + github.com/golang-jwt/jwt/v5 v5.3.1 github.com/google/renameio/v2 v2.0.2 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/uuid v1.6.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/go.sum new/glab-1.82.0/go.sum --- old/glab-1.81.0/go.sum 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/go.sum 2026-01-29 09:31:59.000000000 +0100 @@ -98,8 +98,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/cli v29.1.5+incompatible h1:GckbANUt3j+lsnQ6eCcQd70mNSOismSHWt8vk2AX8ao= -github.com/docker/cli v29.1.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v29.2.0+incompatible h1:9oBd9+YM7rxjZLfyMGxjraKBKE4/nVyvVfN4qNl9XRM= +github.com/docker/cli v29.2.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= github.com/dop251/goja v0.0.0-20240927123429-241b342198c2 h1:Ux9RXuPQmTB4C1MKagNLme0krvq8ulewfor+ORO/QL4= @@ -120,8 +120,8 @@ github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw= github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo= -github.com/gdamore/tcell/v2 v2.13.7 h1:yfHdeC7ODIYCc6dgRos8L1VujQtXHmUpU6UZotzD6os= -github.com/gdamore/tcell/v2 v2.13.7/go.mod h1:+Wfe208WDdB7INEtCsNrAN6O2m+wsTPk1RAovjaILlo= +github.com/gdamore/tcell/v2 v2.13.8 h1:Mys/Kl5wfC/GcC5Cx4C2BIQH9dbnhnkPgS9/wF3RlfU= +github.com/gdamore/tcell/v2 v2.13.8/go.mod h1:+Wfe208WDdB7INEtCsNrAN6O2m+wsTPk1RAovjaILlo= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= @@ -136,8 +136,8 @@ github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= -github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= -github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/api/auth_sources.go new/glab-1.82.0/internal/api/auth_sources.go --- old/glab-1.81.0/internal/api/auth_sources.go 1970-01-01 01:00:00.000000000 +0100 +++ new/glab-1.82.0/internal/api/auth_sources.go 2026-01-29 09:31:59.000000000 +0100 @@ -0,0 +1,21 @@ +package api + +import ( + "context" + + gitlab "gitlab.com/gitlab-org/api/client-go" +) + +var _ gitlab.AuthSource = (*oauth2AccessTokenOnlyAuthSource)(nil) + +type oauth2AccessTokenOnlyAuthSource struct { + token string +} + +func (as oauth2AccessTokenOnlyAuthSource) Init(context.Context, *gitlab.Client) error { + return nil +} + +func (as oauth2AccessTokenOnlyAuthSource) Header(_ context.Context) (string, string, error) { + return "Authorization", "Bearer " + as.token, nil +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/api/client.go new/glab-1.82.0/internal/api/client.go --- old/glab-1.81.0/internal/api/client.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/api/client.go 2026-01-29 09:31:59.000000000 +0100 @@ -311,6 +311,17 @@ var newAuthSource newAuthSource switch { case isOAuth2Cfg == "true": + if v, _ := cfg.Get(repoHost, "oauth2_refresh_token"); v == "" { + if token == "" { + return nil, errors.New("with OAuth2 is enabled and when no Refresh Token is available an OAuth2 Access Token is required") + } + + newAuthSource = func(client *http.Client) (gitlab.AuthSource, error) { + return oauth2AccessTokenOnlyAuthSource{token: token}, nil + } + break + } + newAuthSource = func(client *http.Client) (gitlab.AuthSource, error) { ts, err := oauth2.NewConfigTokenSource(cfg, client, glinstance.DefaultProtocol, repoHost) if err != nil { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/api/client_test.go new/glab-1.82.0/internal/api/client_test.go --- old/glab-1.81.0/internal/api/client_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/glab-1.82.0/internal/api/client_test.go 2026-01-29 09:31:59.000000000 +0100 @@ -0,0 +1,61 @@ +package api + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + gitlab "gitlab.com/gitlab-org/api/client-go" + + "gitlab.com/gitlab-org/cli/internal/config" +) + +func TestClient_OAuth2AccessTokenOnly(t *testing.T) { + t.Setenv("GITLAB_TOKEN", "oauth2-access-token") + t.Setenv("GLAB_IS_OAUTH2", "true") + + client, err := NewClientFromConfig( + "example.gitlab.com", + config.NewBlankConfig(), + false, + "dummy user agent", + ) + require.NoError(t, err) + + key, value, err := client.AuthSource().Header(t.Context()) + require.NoError(t, err) + assert.Equal(t, "Authorization", key) + assert.Equal(t, "Bearer oauth2-access-token", value) +} + +func TestClient_OAuth2AccessTokenOnly_NoToken(t *testing.T) { + t.Setenv("GITLAB_TOKEN", "") + t.Setenv("GLAB_IS_OAUTH2", "true") + + _, err := NewClientFromConfig( + "example.gitlab.com", + config.NewBlankConfig(), + false, + "dummy user agent", + ) + require.Error(t, err) +} + +func TestClient_PATAuth(t *testing.T) { + t.Setenv("GITLAB_TOKEN", "some-pat") + t.Setenv("GLAB_IS_OAUTH2", "") + + client, err := NewClientFromConfig( + "example.gitlab.com", + config.NewBlankConfig(), + false, + "dummy user agent", + ) + require.NoError(t, err) + + key, value, err := client.AuthSource().Header(t.Context()) + require.NoError(t, err) + assert.Equal(t, gitlab.AccessTokenHeaderName, key) + assert.Equal(t, "some-pat", value) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/auth/login/login.go new/glab-1.82.0/internal/commands/auth/login/login.go --- old/glab-1.81.0/internal/commands/auth/login/login.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/auth/login/login.go 2026-01-29 09:31:59.000000000 +0100 @@ -583,8 +583,8 @@ func getAccessTokenTip(hostname string) string { return fmt.Sprintf(` - Tip: generate a personal access token at https://%s/-/user_settings/personal_access_tokens?scopes=api,write_repository. - The minimum required scopes are 'api' and 'write_repository'.`, hostname) + The minimum required scopes are 'api' and 'write_repository'. + Generate a personal access token at https://%s/-/user_settings/personal_access_tokens?scopes=api,write_repository`, hostname) } func showTokenPrompt(ctx context.Context, io *iostreams.IOStreams, hostname string) (string, error) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/auth/status/status.go new/glab-1.82.0/internal/commands/auth/status/status.go --- old/glab-1.81.0/internal/commands/auth/status/status.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/auth/status/status.go 2026-01-29 09:31:59.000000000 +0100 @@ -42,9 +42,9 @@ Short: "View authentication status.", Long: heredoc.Docf(`Verifies and displays information about your authentication state. - By default, this command checks the authentication state of the GitLab instance - determined by your current context (%[1]sgit remote%[1]s, %[1]sGITLAB_HOST%[1]s environment variable, - or configuration). Use %[1]s--all%[1]s to check all configured instances, or %[1]s--hostname%[1]s to + By default, this command checks the authentication state of the GitLab instance + determined by your current context (%[1]sgit remote%[1]s, %[1]sGITLAB_HOST%[1]s environment variable, + or configuration). Use %[1]s--all%[1]s to check all configured instances, or %[1]s--hostname%[1]s to check a specific instance. `, "`"), Annotations: map[string]string{ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/ci/get/get.go new/glab-1.82.0/internal/commands/ci/get/get.go --- old/glab-1.81.0/internal/commands/ci/get/get.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/ci/get/get.go 2026-01-29 09:31:59.000000000 +0100 @@ -1,7 +1,6 @@ package get import ( - "encoding/json" "fmt" "io" "strconv" @@ -128,12 +127,11 @@ outputFormat, _ := cmd.Flags().GetString("output-format") output, _ := cmd.Flags().GetString("output") if output == "json" || outputFormat == "json" { - printJSON(*mergedPipelineObject, f.IO().StdOut) - } else { - showJobDetails, _ := cmd.Flags().GetBool("with-job-details") - printTable(*mergedPipelineObject, f.IO().StdOut, showJobDetails) + return f.IO().PrintJSON(*mergedPipelineObject) } + showJobDetails, _ := cmd.Flags().GetBool("with-job-details") + printTable(*mergedPipelineObject, f.IO().StdOut, showJobDetails) return nil }, } @@ -150,11 +148,6 @@ return pipelineGetCmd } -func printJSON(p PipelineMergedResponse, dest io.Writer) { - JSONStr, _ := json.Marshal(p) - fmt.Fprintln(dest, string(JSONStr)) -} - func printTable(p PipelineMergedResponse, dest io.Writer, showJobDetails bool) { printPipelineTable(p, dest) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/ci/get/testdata/ci_get.result new/glab-1.82.0/internal/commands/ci/get/testdata/ci_get.result --- old/glab-1.81.0/internal/commands/ci/get/testdata/ci_get.result 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/ci/get/testdata/ci_get.result 2026-01-29 09:31:59.000000000 +0100 @@ -1 +1 @@ -{"id":452959326,"iid":14,"project_id":29316529,"status":"success","source":"push","ref":"1-fake-issue-3","name":"","sha":"44eb489568f7cb1a5a730fce6b247cd3797172ca","before_sha":"001eb421e586a3f07f90aea102c8b2d4068ab5b6","tag":false,"yaml_errors":"","user":{"id":8814129,"username":"OWNER","name":"Some User","state":"active","locked":false,"created_at":null,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/8814129/avatar.png","web_url":"https://gitlab.com/OWNER"},"updated_at":"2022-01-20T21:47:31.358Z","created_at":"2022-01-20T21:47:16.276Z","started_at":"2022-01-20T21:47:17.448Z","finished_at":"2022-01-20T21:47:31.35Z","committed_at":null,"duration":14,"queued_duration":1,"coverage":"","web_url":"https://gitlab.com/OWNER/REPO/-/pipelines/452959326","detailed_status":{"icon":"status_success","text":"Passed","label":"passed","group":"success","tooltip":"passed","has_details":true,"details_path":"/OWNER/REPO/-/pipelines/452959326","illustration":{"image":""},"favicon":"/asse ts/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"},"jobs":[{"commit":{"id":"44eb489568f7cb1a5a730fce6b247cd3797172ca","short_id":"44eb4895","title":"Add new file","author_name":"Some User","author_email":"[email protected]","authored_date":"2022-01-20T21:47:15Z","committer_name":"Some User","committer_email":"[email protected]","committed_date":"2022-01-20T21:47:15Z","created_at":"2022-01-20T21:47:15Z","message":"Add new file","parent_ids":["001eb421e586a3f07f90aea102c8b2d4068ab5b6"],"stats":null,"status":null,"last_pipeline":null,"project_id":0,"trailers":{},"extended_trailers":{},"web_url":"https://gitlab.com/OWNER/REPO/-/commit/44eb489568f7cb1a5a730fce6b247cd3797172ca"},"coverage":0,"allow_failure":false,"created_at":"2022-01-20T21:47:16.291Z","started_at":"2022-01-20T21:47:16.693Z","finished_at":"2022-01-20T21:47:31.274Z","erased_at":null,"duration":14.580467,"queued_duration":0.211715,"artifacts_expire_at":null,"tag_list": [],"id":1999017704,"name":"test_vars","pipeline":{"id":452959326,"project_id":29316529,"ref":"1-fake-issue-3","sha":"44eb489568f7cb1a5a730fce6b247cd3797172ca","status":"success"},"ref":"1-fake-issue-3","artifacts":[{"file_type":"trace","filename":"job.log","size":2770,"file_format":""}],"artifacts_file":{"filename":"","size":0},"runner":{"id":12270859,"description":"5-green.saas-linux-small-amd64.runners-manager.gitlab.com/default","active":true,"is_shared":true,"name":"gitlab-runner"},"stage":"test","status":"success","failure_reason":"","tag":false,"web_url":"https://gitlab.com/OWNER/REPO/-/jobs/1999017704","project":{"id":0,"description":"","default_branch":"","visibility":"","ssh_url_to_repo":"","http_url_to_repo":"","web_url":"","readme_url":"","topics":null,"owner":null,"name":"","name_with_namespace":"","path":"","path_with_namespace":"","open_issues_count":0,"resolve_outdated_diff_discussions":false,"resource_group_default_process_mode": "", "container_registry_access_level" :"","creator_id":0,"namespace":null,"permissions":null,"marked_for_deletion_on":null,"max_artifacts_size":0,"empty_repo":false,"archived":false,"avatar_url":"","license_url":"","license":null,"shared_runners_enabled":false,"group_runners_enabled":false,"runner_token_expiration_interval":0,"forks_count":0,"star_count":0,"runners_token":"","allow_merge_on_skipped_pipeline":false,"allow_pipeline_trigger_approve_deployment":false,"only_allow_merge_if_pipeline_succeeds":false,"only_allow_merge_if_all_discussions_are_resolved":false,"remove_source_branch_after_merge":false,"prevent_merge_without_jira_issue":false,"printing_merge_request_link_enabled":false,"lfs_enabled":false,"repository_storage":"","request_access_enabled":false,"merge_method":"","can_create_merge_request_in":false,"forked_from_project":null,"mirror":false,"mirror_user_id":0,"mirror_trigger_builds":false,"only_mirror_protected_branches":false,"mirror_overwrites_diverged_branches":false,"packages_enabled":false,"service_d esk_enabled":false,"service_desk_address":"","issues_access_level":"","repository_access_level":"","merge_requests_access_level":"","forking_access_level":"","wiki_access_level":"","builds_access_level":"","snippets_access_level":"","pages_access_level":"","operations_access_level":"","analytics_access_level":"","environments_access_level":"","feature_flags_access_level":"","infrastructure_access_level":"","monitor_access_level":"","autoclose_referenced_issues":false,"suggestion_commit_message":"","squash_option":"","shared_with_groups":null,"statistics":null,"import_url":"","import_type":"","import_status":"","import_error":"","ci_default_git_depth":0,"ci_forward_deployment_enabled":false,"ci_forward_deployment_rollback_allowed":false,"ci_id_token_sub_claim_components":null,"ci_separated_caches":false,"ci_job_token_scope_enabled":false,"ci_opt_in_jwt":false,"ci_allow_fork_pipelines_to_run_in_parent_project":false,"ci_restrict_pipeline_cancellation_role":"","public_jobs":false,"buil d_timeout":0,"auto_cancel_pending_pipelines":"","ci_config_path":"","custom_attributes":null,"compliance_frameworks":null,"build_coverage_regex":"","issues_template":"","merge_requests_template":"","issue_branch_template":"","keep_latest_artifact":false,"merge_pipelines_enabled":false,"merge_trains_enabled":false,"merge_trains_skip_train_allowed":false,"ci_pipeline_variables_minimum_override_role":"","ci_push_repository_for_job_token_allowed":false,"merge_commit_template":"","squash_commit_template":"","auto_devops_deploy_strategy":"","auto_devops_enabled":false,"auto_duo_code_review_enabled":false,"build_git_strategy":"","emails_enabled":false,"external_authorization_classification_label":"","requirements_enabled":false,"requirements_access_level":"","security_and_compliance_enabled":false,"security_and_compliance_access_level":"","mr_default_target_self":false,"model_experiments_access_level":"","model_registry_access_level":"","pre_receive_secret_detection_enabled":false,"tag_lis t":null,"issues_enabled":false,"merge_requests_enabled":false,"approvals_before_merge":0,"jobs_enabled":false,"wiki_enabled":false,"snippets_enabled":false,"container_registry_enabled":false,"marked_for_deletion_at":null,"restrict_user_defined_variables":false,"emails_disabled":false,"public_builds":false},"user":{"id":8814129,"username":"OWNER","email":"","name":"Some User","state":"active","web_url":"https://gitlab.com/OWNER","created_at":"2021-05-03T14:58:50.059Z","bio":"","bot":false,"location":"Canada","public_email":"","skype":"","linkedin":"","twitter":"","website_url":"","organization":"GitLab","job_title":"Sr Backend Engineer","extern_uid":"","provider":"","theme_id":0,"last_activity_on":null,"color_scheme_id":0,"is_admin":false,"is_auditor":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/8814129/avatar.png","can_create_group":false,"can_create_organization":false,"can_create_project":false,"projects_limit":0,"current_sign_in_at":null,"current_sign_in_ip ":null,"last_sign_in_at":null,"last_sign_in_ip":null,"confirmed_at":null,"two_factor_enabled":false,"note":"","identities":null,"external":false,"private_profile":false,"shared_runners_minutes_limit":0,"extra_shared_runners_minutes_limit":0,"using_license_seat":false,"custom_attributes":null,"namespace_id":0,"locked":false,"created_by":null}}],"variables":null} +{"id":452959326,"iid":14,"project_id":29316529,"status":"success","source":"push","ref":"1-fake-issue-3","name":"","sha":"44eb489568f7cb1a5a730fce6b247cd3797172ca","before_sha":"001eb421e586a3f07f90aea102c8b2d4068ab5b6","tag":false,"yaml_errors":"","user":{"id":8814129,"username":"OWNER","name":"Some User","state":"active","locked":false,"created_at":null,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/8814129/avatar.png","web_url":"https://gitlab.com/OWNER"},"updated_at":"2022-01-20T21:47:31.358Z","created_at":"2022-01-20T21:47:16.276Z","started_at":"2022-01-20T21:47:17.448Z","finished_at":"2022-01-20T21:47:31.35Z","committed_at":null,"duration":14,"queued_duration":1,"coverage":"","web_url":"https://gitlab.com/OWNER/REPO/-/pipelines/452959326","detailed_status":{"icon":"status_success","text":"Passed","label":"passed","group":"success","tooltip":"passed","has_details":true,"details_path":"/OWNER/REPO/-/pipelines/452959326","illustration":{"image":""},"favicon":"/asse ts/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"},"jobs":[{"commit":{"id":"44eb489568f7cb1a5a730fce6b247cd3797172ca","short_id":"44eb4895","title":"Add new file","author_name":"Some User","author_email":"[email protected]","authored_date":"2022-01-20T21:47:15Z","committer_name":"Some User","committer_email":"[email protected]","committed_date":"2022-01-20T21:47:15Z","created_at":"2022-01-20T21:47:15Z","message":"Add new file","parent_ids":["001eb421e586a3f07f90aea102c8b2d4068ab5b6"],"stats":null,"status":null,"last_pipeline":null,"project_id":0,"trailers":{},"extended_trailers":{},"web_url":"https://gitlab.com/OWNER/REPO/-/commit/44eb489568f7cb1a5a730fce6b247cd3797172ca"},"coverage":0,"allow_failure":false,"created_at":"2022-01-20T21:47:16.291Z","started_at":"2022-01-20T21:47:16.693Z","finished_at":"2022-01-20T21:47:31.274Z","erased_at":null,"duration":14.580467,"queued_duration":0.211715,"artifacts_expire_at":null,"tag_list": [],"id":1999017704,"name":"test_vars","pipeline":{"id":452959326,"project_id":29316529,"ref":"1-fake-issue-3","sha":"44eb489568f7cb1a5a730fce6b247cd3797172ca","status":"success"},"ref":"1-fake-issue-3","artifacts":[{"file_type":"trace","filename":"job.log","size":2770,"file_format":""}],"artifacts_file":{"filename":"","size":0},"runner":{"id":12270859,"description":"5-green.saas-linux-small-amd64.runners-manager.gitlab.com/default","active":true,"is_shared":true,"name":"gitlab-runner"},"stage":"test","status":"success","failure_reason":"","tag":false,"web_url":"https://gitlab.com/OWNER/REPO/-/jobs/1999017704","project":{"id":0,"description":"","default_branch":"","visibility":"","ssh_url_to_repo":"","http_url_to_repo":"","web_url":"","readme_url":"","topics":[],"owner":null,"name":"","name_with_namespace":"","path":"","path_with_namespace":"","open_issues_count":0,"resolve_outdated_diff_discussions":false,"resource_group_default_process_mode":"","container_registry_access_level":"", "creator_id":0,"namespace":null,"permissions":null,"marked_for_deletion_on":null,"empty_repo":false,"archived":false,"avatar_url":"","license_url":"","license":null,"shared_runners_enabled":false,"group_runners_enabled":false,"runner_token_expiration_interval":0,"forks_count":0,"star_count":0,"runners_token":"","allow_merge_on_skipped_pipeline":false,"allow_pipeline_trigger_approve_deployment":false,"only_allow_merge_if_pipeline_succeeds":false,"only_allow_merge_if_all_discussions_are_resolved":false,"remove_source_branch_after_merge":false,"prevent_merge_without_jira_issue":false,"printing_merge_request_link_enabled":false,"lfs_enabled":false,"repository_storage":"","request_access_enabled":false,"merge_method":"","can_create_merge_request_in":false,"forked_from_project":null,"mirror":false,"mirror_user_id":0,"mirror_trigger_builds":false,"only_mirror_protected_branches":false,"mirror_overwrites_diverged_branches":false,"packages_enabled":false,"service_desk_enabled":false,"service _desk_address":"","issues_access_level":"","repository_access_level":"","merge_requests_access_level":"","forking_access_level":"","wiki_access_level":"","builds_access_level":"","snippets_access_level":"","pages_access_level":"","operations_access_level":"","analytics_access_level":"","environments_access_level":"","feature_flags_access_level":"","infrastructure_access_level":"","monitor_access_level":"","autoclose_referenced_issues":false,"suggestion_commit_message":"","squash_option":"","shared_with_groups":[],"statistics":null,"import_url":"","import_type":"","import_status":"","import_error":"","ci_default_git_depth":0,"ci_forward_deployment_enabled":false,"ci_forward_deployment_rollback_allowed":false,"ci_id_token_sub_claim_components":[],"ci_separated_caches":false,"ci_job_token_scope_enabled":false,"ci_opt_in_jwt":false,"ci_allow_fork_pipelines_to_run_in_parent_project":false,"ci_restrict_pipeline_cancellation_role":"","public_jobs":false,"build_timeout":0,"auto_cancel_pendi ng_pipelines":"","ci_config_path":"","custom_attributes":[],"compliance_frameworks":[],"build_coverage_regex":"","issues_template":"","merge_requests_template":"","issue_branch_template":"","keep_latest_artifact":false,"merge_pipelines_enabled":false,"merge_trains_enabled":false,"merge_trains_skip_train_allowed":false,"ci_pipeline_variables_minimum_override_role":"","ci_push_repository_for_job_token_allowed":false,"merge_commit_template":"","squash_commit_template":"","auto_devops_deploy_strategy":"","auto_devops_enabled":false,"auto_duo_code_review_enabled":false,"build_git_strategy":"","emails_enabled":false,"external_authorization_classification_label":"","requirements_enabled":false,"requirements_access_level":"","security_and_compliance_enabled":false,"security_and_compliance_access_level":"","mr_default_target_self":false,"model_experiments_access_level":"","model_registry_access_level":"","pre_receive_secret_detection_enabled":false,"tag_list":[],"issues_enabled":false,"merge _requests_enabled":false,"approvals_before_merge":0,"jobs_enabled":false,"wiki_enabled":false,"snippets_enabled":false,"container_registry_enabled":false,"marked_for_deletion_at":null,"restrict_user_defined_variables":false,"emails_disabled":false,"public_builds":false},"user":{"id":8814129,"username":"OWNER","email":"","name":"Some User","state":"active","web_url":"https://gitlab.com/OWNER","created_at":"2021-05-03T14:58:50.059Z","bio":"","bot":false,"location":"Canada","public_email":"","skype":"","linkedin":"","twitter":"","website_url":"","organization":"GitLab","job_title":"Sr Backend Engineer","extern_uid":"","provider":"","theme_id":0,"last_activity_on":null,"color_scheme_id":0,"is_admin":false,"is_auditor":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/8814129/avatar.png","can_create_group":false,"can_create_organization":false,"can_create_project":false,"projects_limit":0,"current_sign_in_at":null,"current_sign_in_ip":null,"last_sign_in_at":null,"last_s ign_in_ip":null,"confirmed_at":null,"two_factor_enabled":false,"note":"","identities":[],"external":false,"private_profile":false,"shared_runners_minutes_limit":0,"extra_shared_runners_minutes_limit":0,"using_license_seat":false,"custom_attributes":[],"namespace_id":0,"locked":false,"created_by":null}}],"variables":[]} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/ci/list/list.go new/glab-1.82.0/internal/commands/ci/list/list.go --- old/glab-1.81.0/internal/commands/ci/list/list.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/ci/list/list.go 2026-01-29 09:31:59.000000000 +0100 @@ -57,8 +57,15 @@ l.Status = gitlab.Ptr(gitlab.BuildStateValue(m)) titleQualifier = m } - if m, _ := cmd.Flags().GetString("orderBy"); m != "" { - l.OrderBy = gitlab.Ptr(m) + // Support both --order and --orderBy (deprecated) for backward compatibility + var orderByValue string + if cmd.Flags().Changed("orderBy") { + orderByValue, _ = cmd.Flags().GetString("orderBy") + } else { + orderByValue, _ = cmd.Flags().GetString("order") + } + if orderByValue != "" { + l.OrderBy = gitlab.Ptr(orderByValue) } if m, _ := cmd.Flags().GetString("sort"); m != "" { l.Sort = gitlab.Ptr(m) @@ -125,8 +132,10 @@ }, } pipelineListCmd.Flags().StringP("status", "s", "", "Get pipeline with this status. Options: running, pending, success, failed, canceled, skipped, created, manual, waiting_for_resource, preparing, scheduled") - pipelineListCmd.Flags().StringP("orderBy", "o", "id", "Order pipelines by this field. Options: id, status, ref, updated_at, user_id.") - pipelineListCmd.Flags().StringP("sort", "", "desc", "Sort pipelines. Options: asc, desc.") + pipelineListCmd.Flags().StringP("order", "o", "id", "Order pipelines by this field. Options: id, status, ref, updated_at, user_id.") + pipelineListCmd.Flags().String("orderBy", "id", "Deprecated: use --order instead.") + _ = pipelineListCmd.Flags().MarkDeprecated("orderBy", "use --order instead") + pipelineListCmd.Flags().StringP("sort", "", "desc", "Sort direction for --order field: asc or desc.") pipelineListCmd.Flags().IntP("page", "p", 1, "Page number.") pipelineListCmd.Flags().IntP("per-page", "P", 30, "Number of items to list per page.") pipelineListCmd.Flags().StringP("output", "F", "text", "Format output. Options: text, json.") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/ci/view/view_test.go new/glab-1.82.0/internal/commands/ci/view/view_test.go --- old/glab-1.81.0/internal/commands/ci/view/view_test.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/ci/view/view_test.go 2026-01-29 09:31:59.000000000 +0100 @@ -141,7 +141,6 @@ // Set screen to matrix size screen.SetSize(len(test.expected), len(test.expected[0])) - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -235,7 +234,6 @@ test.b1.Draw(screen) test.b2.Draw(screen) - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() @@ -425,7 +423,6 @@ if err != nil { t.Fatal(err) } - test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/cluster/agent/token_cache/clear/clear_command_test.go new/glab-1.82.0/internal/commands/cluster/agent/token_cache/clear/clear_command_test.go --- old/glab-1.81.0/internal/commands/cluster/agent/token_cache/clear/clear_command_test.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/cluster/agent/token_cache/clear/clear_command_test.go 2026-01-29 09:31:59.000000000 +0100 @@ -110,7 +110,7 @@ cacheDir := t.TempDir() setUserCacheDir(t, cacheDir) - expires := gitlab.Ptr(gitlab.ISOTime(time.Now().Add(24 * time.Hour))) + expires := gitlab.Ptr(gitlab.ISOTime(time.Now().UTC().Add(24 * time.Hour))) pat := &gitlab.PersonalAccessToken{ID: 456, Name: "active-token", ExpiresAt: expires, Revoked: false} writeFSToken(t, tc.Client.BaseURL().String(), 10, pat) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/duo/ask/ask.go new/glab-1.82.0/internal/commands/duo/ask/ask.go --- old/glab-1.81.0/internal/commands/duo/ask/ask.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/duo/ask/ask.go 2026-01-29 09:31:59.000000000 +0100 @@ -244,7 +244,7 @@ return nil } - if opts.IO.StartPager() != nil { + if err := opts.IO.StartPager(); err != nil { return fmt.Errorf("failed to start pager: %q", err) } defer opts.IO.StopPager() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/issuable/list/issuable_list.go new/glab-1.82.0/internal/commands/issuable/list/issuable_list.go --- old/glab-1.81.0/internal/commands/issuable/list/issuable_list.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/issuable/list/issuable_list.go 2026-01-29 09:31:59.000000000 +0100 @@ -170,7 +170,7 @@ issueListCmd.Flags().IntVarP(&opts.Epic, "epic", "e", 0, "List issues belonging to a given epic (requires --group, no pagination support).") issueListCmd.MarkFlagsMutuallyExclusive("output", "output-format") issueListCmd.Flags().StringVar(&opts.OrderBy, "order", "created_at", fmt.Sprintf("Order %s by <field>. Order options: created_at, updated_at, priority, due_date, relative_position, label_priority, milestone_due, popularity, weight.", issueType)) - issueListCmd.Flags().StringVar(&opts.Sort, "sort", "desc", fmt.Sprintf("Return %s sorted in asc or desc order.", issueType)) + issueListCmd.Flags().StringVarP(&opts.Sort, "sort", "s", "desc", "Sort direction for --order field: asc or desc.") if issueType == issuable.TypeIssue { issueListCmd.Flags().StringVarP(&opts.IssueType, "issue-type", "t", "", "Filter issue by its type. Options: issue, incident, test_case.") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/issue/create/issue_create.go new/glab-1.82.0/internal/commands/issue/create/issue_create.go --- old/glab-1.81.0/internal/commands/issue/create/issue_create.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/issue/create/issue_create.go 2026-01-29 09:31:59.000000000 +0100 @@ -146,7 +146,7 @@ }, } issueCreateCmd.Flags().StringVarP(&opts.Title, "title", "t", "", "Issue title.") - issueCreateCmd.Flags().StringVarP(&opts.Description, "description", "d", "", "Issue description.") + issueCreateCmd.Flags().StringVarP(&opts.Description, "description", "d", "", "Issue description. Set to \"-\" to open an editor.") issueCreateCmd.Flags().StringSliceVarP(&opts.Labels, "label", "l", []string{}, "Add label by name. Multiple labels can be comma-separated or specified by repeating the flag.") issueCreateCmd.Flags().StringSliceVarP(&opts.Assignees, "assignee", "a", []string{}, "Assign issue to people by their `usernames`. Multiple usernames can be comma-separated or specified by repeating the flag.") issueCreateCmd.Flags().StringVarP(&opts.MilestoneFlag, "milestone", "m", "", "The global ID or title of a milestone to assign.") @@ -201,6 +201,20 @@ } } + // Handle -d- flag to directly open external editor + if opts.Description == "-" { + editor, err := cmdutils.GetEditor(opts.config) + if err != nil { + return err + } + + opts.Description = "" + err = opts.io.DirectEditor(ctx, &opts.Description, "", editor) + if err != nil { + return err + } + } + if opts.isInteractive { // Step 1: Template selection (if not using --no-editor and description is empty) if opts.Description == "" && !opts.noEditor { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/iteration/list/iteration_list.go new/glab-1.82.0/internal/commands/iteration/list/iteration_list.go --- old/glab-1.81.0/internal/commands/iteration/list/iteration_list.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/iteration/list/iteration_list.go 2026-01-29 09:31:59.000000000 +0100 @@ -125,6 +125,7 @@ for _, iteration := range iterations { iterationBuilder.WriteString(formatIterationInfo(iteration.Description, iteration.Title, iteration.WebURL)) } + fmt.Fprintln(o.io.StdOut, utils.Indent(iterationBuilder.String(), " ")) } } else { repo, err := o.baseRepo() @@ -143,9 +144,9 @@ for _, iteration := range iterations { iterationBuilder.WriteString(formatIterationInfo(iteration.Description, iteration.Title, iteration.WebURL)) } + fmt.Fprintln(o.io.StdOut, utils.Indent(iterationBuilder.String(), " ")) } } - fmt.Fprintln(o.io.StdOut, utils.Indent(iterationBuilder.String(), " ")) return nil } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/mr/create/mr_create.go new/glab-1.82.0/internal/commands/mr/create/mr_create.go --- old/glab-1.81.0/internal/commands/mr/create/mr_create.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/mr/create/mr_create.go 2026-01-29 09:31:59.000000000 +0100 @@ -144,7 +144,7 @@ mrCreateCmd.Flags().BoolVarP(&opts.IsWIP, "wip", "", false, "Mark merge request as a draft. Alternative to --draft.") mrCreateCmd.Flags().BoolVarP(&opts.ShouldPush, "push", "", false, "Push committed changes after creating merge request. Make sure you have committed changes.") mrCreateCmd.Flags().StringVarP(&opts.Title, "title", "t", "", "Supply a title for the merge request.") - mrCreateCmd.Flags().StringVarP(&opts.Description, "description", "d", "", "Supply a description for the merge request.") + mrCreateCmd.Flags().StringVarP(&opts.Description, "description", "d", "", "Supply a description for the merge request. Set to \"-\" to open an editor.") mrCreateCmd.Flags().StringSliceVarP(&opts.Labels, "label", "l", []string{}, "Add label by name. Multiple labels can be comma-separated or specified by repeating the flag.") mrCreateCmd.Flags().StringSliceVarP(&opts.Assignees, "assignee", "a", []string{}, "Assign merge request to people by their `usernames`. Multiple usernames can be comma-separated or specified by repeating the flag.") mrCreateCmd.Flags().StringSliceVarP(&opts.Reviewers, "reviewer", "", []string{}, "Request review from users by their `usernames`. Multiple usernames can be comma-separated or specified by repeating the flag.") @@ -374,6 +374,20 @@ return cmdutils.SilentError } + // Handle -d- flag to directly open external editor + if o.Description == "-" { + editor, err := cmdutils.GetEditor(o.config) + if err != nil { + return err + } + + o.Description = "" + err = o.io.DirectEditor(ctx, &o.Description, "", editor) + if err != nil { + return err + } + } + if o.Autofill { if err = mrBodyAndTitle(o); err != nil { return err diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/mr/list/mr_list.go new/glab-1.82.0/internal/commands/mr/list/mr_list.go --- old/glab-1.81.0/internal/commands/mr/list/mr_list.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/mr/list/mr_list.go 2026-01-29 09:31:59.000000000 +0100 @@ -3,6 +3,7 @@ import ( "encoding/json" "fmt" + "time" "github.com/MakeNowJust/heredoc/v2" "github.com/spf13/cobra" @@ -33,17 +34,19 @@ type options struct { // metadata - assignee []string - reviewer []string - author string - labels []string - notLabels []string - milestone string - sourceBranch string - targetBranch string - search string - mine bool - group string + assignee []string + reviewer []string + author string + labels []string + notLabels []string + milestone string + sourceBranch string + targetBranch string + search string + mine bool + group string + createdBefore time.Time + createdAfter time.Time // issue states state string @@ -132,8 +135,10 @@ mrListCmd.Flags().IntVarP(&opts.perPage, "per-page", "P", 30, "Number of items to list per page.") mrListCmd.Flags().StringSliceVarP(&opts.assignee, "assignee", "a", []string{}, "Get only merge requests assigned to users. Multiple users can be comma-separated or specified by repeating the flag.") mrListCmd.Flags().StringSliceVarP(&opts.reviewer, "reviewer", "r", []string{}, "Get only merge requests with users as reviewer. Multiple users can be comma-separated or specified by repeating the flag.") - mrListCmd.Flags().StringVarP(&opts.sort, "sort", "S", "", "Sort merge requests by <field>. Sort options: asc, desc.") + mrListCmd.Flags().StringVarP(&opts.sort, "sort", "S", "", "Sort direction for --order field: asc or desc.") mrListCmd.Flags().StringVarP(&opts.orderBy, "order", "o", "", "Order merge requests by <field>. Order options: created_at, updated_at, merged_at, title, priority, label_priority, milestone_due, and popularity.") + mrListCmd.Flags().TimeVar(&opts.createdAfter, "created-before", time.Time{}, []string{time.RFC3339}, "Filter merge requests created after a certain date (ISO 8601 format).") + mrListCmd.Flags().TimeVar(&opts.createdAfter, "created-after", time.Time{}, []string{time.RFC3339}, "Filter merge requests created after a certain date (ISO 8601 format).") mrListCmd.Flags().BoolP("opened", "O", false, "Get only open merge requests.") _ = mrListCmd.Flags().MarkHidden("opened") @@ -302,6 +307,14 @@ } title := utils.NewListTitle(o.titleQualifier + " merge request") + if !o.createdBefore.IsZero() { + l.CreatedBefore = gitlab.Ptr(o.createdBefore) + } + + if !o.createdAfter.IsZero() { + l.CreatedAfter = gitlab.Ptr(o.createdAfter) + } + if o.group != "" { mergeRequests, err = api.ListGroupMRs(client, o.group, projectListMROptionsToGroup(l), api.WithMRAssignees(assigneeIds), api.WithMRReviewers(reviewerIds)) title.RepoName = o.group diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/mr/merge/mr_merge.go new/glab-1.82.0/internal/commands/mr/merge/mr_merge.go --- old/glab-1.81.0/internal/commands/mr/merge/mr_merge.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/mr/merge/mr_merge.go 2026-01-29 09:31:59.000000000 +0100 @@ -83,7 +83,7 @@ }, } - mrMergeCmd.Flags().StringVarP(&opts.sha, "sha", "", "", "Merge commit SHA.") + mrMergeCmd.Flags().StringVarP(&opts.sha, "sha", "", "", "Merge only if the HEAD of the source branch matches this SHA. Use to ensure that only reviewed commits are merged.") mrMergeCmd.Flags().BoolVarP(&opts.removeSourceBranch, "remove-source-branch", "d", false, "Remove source branch on merge.") mrMergeCmd.Flags().BoolVarP(&opts.setAutoMerge, "auto-merge", "", true, "Set auto-merge.") mrMergeCmd.Flags().StringVarP(&opts.mergeCommitMessage, "message", "m", "", "Custom merge commit message.") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/project/contributors/repo_contributors.go new/glab-1.82.0/internal/commands/project/contributors/repo_contributors.go --- old/glab-1.81.0/internal/commands/project/contributors/repo_contributors.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/project/contributors/repo_contributors.go 2026-01-29 09:31:59.000000000 +0100 @@ -56,7 +56,7 @@ cmdutils.EnableRepoOverride(repoContributorsCmd, f) repoContributorsCmd.Flags().StringVarP(&opts.orderBy, "order", "o", "commits", "Return contributors ordered by name, email, or commits (orders by commit date) fields.") - repoContributorsCmd.Flags().StringVarP(&opts.sort, "sort", "s", "", "Return contributors. Sort options: asc, desc.") + repoContributorsCmd.Flags().StringVarP(&opts.sort, "sort", "s", "", "Sort direction for --order field: asc or desc.") repoContributorsCmd.Flags().IntVarP(&opts.page, "page", "p", 1, "Page number.") repoContributorsCmd.Flags().IntVarP(&opts.perPage, "per-page", "P", 30, "Number of items to list per page.") return repoContributorsCmd diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/project/create/project_create.go new/glab-1.82.0/internal/commands/project/create/project_create.go --- old/glab-1.81.0/internal/commands/project/create/project_create.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/project/create/project_create.go 2026-01-29 09:31:59.000000000 +0100 @@ -88,7 +88,7 @@ projectCreateCmd.Flags().StringP("name", "n", "", "Name of the new project.") projectCreateCmd.Flags().StringP("group", "g", "", "Namespace or group for the new project. Defaults to the current user's namespace.") - projectCreateCmd.Flags().StringP("description", "d", "", "Description of the new project.") + projectCreateCmd.Flags().StringP("description", "d", "", "Description of the new project. Set to \"-\" to open an editor.") projectCreateCmd.Flags().String("defaultBranch", "", "Default branch of the project. Defaults to `master` if not provided.") projectCreateCmd.Flags().String("remoteName", "origin", "Remote name for the Git repository you're in. Defaults to `origin` if not provided.") projectCreateCmd.Flags().StringArrayP("tag", "t", []string{}, "The list of tags for the project.") @@ -213,6 +213,20 @@ description, _ := cmd.Flags().GetString("description") + // Handle -d- flag to directly open external editor + if description == "-" { + editor, err := cmdutils.GetEditor(f.Config) + if err != nil { + return err + } + + description = "" + err = f.IO().DirectEditor(cmd.Context(), &description, "", editor) + if err != nil { + return err + } + } + if internal, _ := cmd.Flags().GetBool("internal"); internal { visibility = gitlab.InternalVisibility } else if private, _ := cmd.Flags().GetBool("private"); private { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/project/list/list.go new/glab-1.82.0/internal/commands/project/list/list.go --- old/glab-1.81.0/internal/commands/project/list/list.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/project/list/list.go 2026-01-29 09:31:59.000000000 +0100 @@ -61,7 +61,7 @@ } repoListCmd.Flags().StringVarP(&opts.orderBy, "order", "o", "last_activity_at", "Return repositories ordered by id, name, path, created_at, updated_at, similarity, star_count, last_activity_at.") - repoListCmd.Flags().StringVarP(&opts.sort, "sort", "s", "", "Return repositories sorted in asc or desc order.") + repoListCmd.Flags().StringVarP(&opts.sort, "sort", "s", "", "Sort direction for --order field: asc or desc.") repoListCmd.Flags().StringVarP(&opts.group, "group", "g", "", "Return repositories in only the given group.") repoListCmd.Flags().BoolVarP(&opts.includeSubgroups, "include-subgroups", "G", false, "Include projects in subgroups of this group. Default is false. Used with the '--group' flag.") repoListCmd.Flags().IntVarP(&opts.page, "page", "p", 1, "Page number.") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/snippet/create/create.go new/glab-1.82.0/internal/commands/snippet/create/create.go --- old/glab-1.81.0/internal/commands/snippet/create/create.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/snippet/create/create.go 2026-01-29 09:31:59.000000000 +0100 @@ -1,6 +1,7 @@ package create import ( + "context" "errors" "fmt" "io" @@ -13,6 +14,7 @@ gitlab "gitlab.com/gitlab-org/api/client-go" "gitlab.com/gitlab-org/cli/internal/cmdutils" + "gitlab.com/gitlab-org/cli/internal/config" "gitlab.com/gitlab-org/cli/internal/dbg" "gitlab.com/gitlab-org/cli/internal/glrepo" "gitlab.com/gitlab-org/cli/internal/iostreams" @@ -29,6 +31,7 @@ files []*gitlab.CreateSnippetFileOptions io *iostreams.IOStreams + config func() config.Config gitlabClient func() (*gitlab.Client, error) baseRepo func() (glrepo.Interface, error) } @@ -52,6 +55,7 @@ func NewCmdCreate(f cmdutils.Factory) *cobra.Command { opts := &options{ io: f.IO(), + config: f.Config, gitlabClient: f.GitLabClient, baseRepo: f.BaseRepo, } @@ -80,13 +84,13 @@ return err } - return opts.run() + return opts.run(cmd.Context()) }, } snippetCreateCmd.Flags().StringVarP(&opts.title, "title", "t", "", "(required) Title of the snippet.") snippetCreateCmd.Flags().StringVarP(&opts.displayFilename, "filename", "f", "", "Filename of the snippet in GitLab.") - snippetCreateCmd.Flags().StringVarP(&opts.description, "description", "d", "", "Description of the snippet.") + snippetCreateCmd.Flags().StringVarP(&opts.description, "description", "d", "", "Description of the snippet. Set to \"-\" to open an editor.") snippetCreateCmd.Flags().StringVarP(&opts.visibility, "visibility", "v", "private", "Limit by visibility: 'public', 'internal', or 'private'") snippetCreateCmd.Flags().BoolVarP(&opts.personal, "personal", "p", false, "Create a personal snippet.") @@ -137,7 +141,21 @@ return nil } -func (o *options) run() error { +func (o *options) run(ctx context.Context) error { + // Handle -d- flag to directly open external editor + if o.description == "-" { + editor, err := cmdutils.GetEditor(o.config) + if err != nil { + return err + } + + o.description = "" + err = o.io.DirectEditor(ctx, &o.description, "", editor) + if err != nil { + return err + } + } + client, err := o.gitlabClient() if err != nil { return err diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/stack/reorder/stack_reorder.go new/glab-1.82.0/internal/commands/stack/reorder/stack_reorder.go --- old/glab-1.81.0/internal/commands/stack/reorder/stack_reorder.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/stack/reorder/stack_reorder.go 2026-01-29 09:31:59.000000000 +0100 @@ -49,9 +49,6 @@ mcpannotations.Destructive: "true", }, RunE: func(cmd *cobra.Command, args []string) error { - opts.io.StartSpinner("Reordering\n") - defer opts.io.StopSpinner("%s Reordering complete\n", f.IO().Color().GreenCheck()) - return opts.run(cmd.Context(), f, getText) }, } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/token/list/list.go new/glab-1.82.0/internal/commands/token/list/list.go --- old/glab-1.81.0/internal/commands/token/list/list.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/token/list/list.go 2026-01-29 09:31:59.000000000 +0100 @@ -1,7 +1,6 @@ package list import ( - "encoding/json" "strconv" "strings" "time" @@ -256,13 +255,10 @@ } if o.outputFormat == "json" { - encoder := json.NewEncoder(o.io.StdOut) - if err := encoder.Encode(apiTokens); err != nil { - return err - } - } else { - table := createTablePrinter(outputTokens) - o.io.LogInfof("%s", table.String()) + return o.io.PrintJSON(apiTokens) } + + table := createTablePrinter(outputTokens) + o.io.LogInfof("%s", table.String()) return nil } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/commands/variable/list/list.go new/glab-1.82.0/internal/commands/variable/list/list.go --- old/glab-1.81.0/internal/commands/variable/list/list.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/commands/variable/list/list.go 2026-01-29 09:31:59.000000000 +0100 @@ -104,7 +104,9 @@ table := tableprinter.NewTablePrinter() if o.group != "" { - o.io.LogInfof("Listing variables for the %s group:\n\n", color.Bold(o.group)) + if o.outputFormat != "json" { + o.io.LogInfof("Listing variables for the %s group:\n\n", color.Bold(o.group)) + } listOpts := &gitlab.ListGroupVariablesOptions{ ListOptions: gitlab.ListOptions{ Page: int64(o.page), @@ -126,7 +128,9 @@ } } } else if o.instance { - o.io.LogInfo("Listing variables for the instance\n\n") + if o.outputFormat != "json" { + o.io.LogInfo("Listing variables for the instance\n\n") + } listOpts := &gitlab.ListInstanceVariablesOptions{ ListOptions: gitlab.ListOptions{ Page: int64(o.page), @@ -152,7 +156,9 @@ if err != nil { return err } - o.io.LogInfof("Listing variables from the %s project:\n\n", color.Bold(repo.FullName())) + if o.outputFormat != "json" { + o.io.LogInfof("Listing variables from the %s project:\n\n", color.Bold(repo.FullName())) + } listOpts := &gitlab.ListProjectVariablesOptions{ ListOptions: gitlab.ListOptions{ Page: int64(o.page), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/config/config_mapping.go new/glab-1.82.0/internal/config/config_mapping.go --- old/glab-1.81.0/internal/config/config_mapping.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/config/config_mapping.go 2026-01-29 09:31:59.000000000 +0100 @@ -98,6 +98,8 @@ return []string{"GIT_REMOTE_URL_VAR", "GIT_REMOTE_ALIAS", "REMOTE_ALIAS", "REMOTE_NICKNAME", "GIT_REMOTE_NICKNAME"} case "client_id": return []string{"GITLAB_CLIENT_ID"} + case "is_oauth2": + return []string{"GLAB_IS_OAUTH2"} default: return []string{strings.ToUpper(key)} } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/glrepo/remote.go new/glab-1.82.0/internal/glrepo/remote.go --- old/glab-1.81.0/internal/glrepo/remote.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/glrepo/remote.go 2026-01-29 09:31:59.000000000 +0100 @@ -28,7 +28,7 @@ // UniqueHosts returns a string of unique hostnames func (r Remotes) UniqueHosts() string { unique := make(map[string]bool, r.Len()) - uniqueHosts := make([]string, len(unique)) + var uniqueHosts []string for _, remote := range r { if !unique[remote.RepoHost()] { uniqueHosts = append(uniqueHosts, remote.RepoHost()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/glrepo/repo.go new/glab-1.82.0/internal/glrepo/repo.go --- old/glab-1.81.0/internal/glrepo/repo.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/glrepo/repo.go 2026-01-29 09:31:59.000000000 +0100 @@ -199,11 +199,11 @@ if repo != "" && pathWithoutRepo != "" { parts := strings.SplitN(pathWithoutRepo, "/", 2) if len(parts) == 1 { - return NewWithHost(parts[0], repo, u.Hostname()), nil + return NewWithHost(parts[0], repo, u.Host), nil } if len(parts) == 2 { - return NewWithGroup(parts[0], parts[1], repo, u.Hostname(), defaultHostname), nil + return NewWithGroup(parts[0], parts[1], repo, u.Host, defaultHostname), nil } } return nil, fmt.Errorf("invalid path: %s", u.Path) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/glrepo/repo_test.go new/glab-1.82.0/internal/glrepo/repo_test.go --- old/glab-1.81.0/internal/glrepo/repo_test.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/glrepo/repo_test.go 2026-01-29 09:31:59.000000000 +0100 @@ -137,7 +137,14 @@ name: "GDK with api_host", input: "https://gdk.test:3443/one/two", result: "one/two", - host: "gdk.test", + host: "gdk.test:3443", + err: nil, + }, + { + name: "non-standard port without api_host", + input: "https://example.com:8443/owner/repo", + result: "owner/repo", + host: "example.com:8443", err: nil, }, { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/iostreams/iostreams.go new/glab-1.82.0/internal/iostreams/iostreams.go --- old/glab-1.81.0/internal/iostreams/iostreams.go 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/internal/iostreams/iostreams.go 2026-01-29 09:31:59.000000000 +0100 @@ -453,7 +453,7 @@ // Write the default content to the temp file if defaultContent != "" { - if _, err := tmpFile.Write([]byte(defaultContent)); err != nil { + if _, err := tmpFile.WriteString(defaultContent); err != nil { _ = tmpFile.Close() // Best effort cleanup return fmt.Errorf("failed to write to temp file: %w", err) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/iostreams/print_json.go new/glab-1.82.0/internal/iostreams/print_json.go --- old/glab-1.81.0/internal/iostreams/print_json.go 1970-01-01 01:00:00.000000000 +0100 +++ new/glab-1.82.0/internal/iostreams/print_json.go 2026-01-29 09:31:59.000000000 +0100 @@ -0,0 +1,26 @@ +package iostreams + +import ( + "encoding/json" + "reflect" +) + +// PrintJSON marshals v to JSON and writes it to stdout. If v is a nil slice, +// it converts it to an empty slice so that JSON marshaling produces [] instead +// of null. This addresses the issue where gitlab.ScanAndCollect returns nil for +// empty results, which would otherwise marshal as null instead of []. +// +// Nested slices within the data structure are left as-is to preserve the +// semantic difference between absent fields (null) and empty arrays ([]) in +// the original API response. +func (s *IOStreams) PrintJSON(v any) error { + // Only normalize if v is a top-level nil slice + rv := reflect.ValueOf(v) + if rv.Kind() == reflect.Slice && rv.IsNil() { + // Create an empty slice of the same type + v = reflect.MakeSlice(rv.Type(), 0, 0).Interface() + } + + encoder := json.NewEncoder(s.StdOut) + return encoder.Encode(v) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/internal/iostreams/print_json_test.go new/glab-1.82.0/internal/iostreams/print_json_test.go --- old/glab-1.81.0/internal/iostreams/print_json_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/glab-1.82.0/internal/iostreams/print_json_test.go 2026-01-29 09:31:59.000000000 +0100 @@ -0,0 +1,94 @@ +package iostreams + +import ( + "bytes" + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPrintJSON_TopLevelNilSliceBecomesEmptyArray(t *testing.T) { + // Test that top-level nil slices (like from gitlab.ScanAndCollect) are + // normalized to [] instead of null + type Token struct { + ID int `json:"id"` + Name string `json:"name"` + } + + buf := &bytes.Buffer{} + io := &IOStreams{StdOut: buf} + + // Simulate what gitlab.ScanAndCollect returns for empty results + var tokens []Token // nil slice + + err := io.PrintJSON(tokens) + require.NoError(t, err) + + // Verify the actual JSON output is [] + assert.Equal(t, "[]\n", buf.String()) +} + +func TestPrintJSON_TopLevelSliceWithData(t *testing.T) { + // Test that top-level slices with data are preserved correctly + type Token struct { + ID int `json:"id"` + Name string `json:"name"` + } + + buf := &bytes.Buffer{} + io := &IOStreams{StdOut: buf} + + tokens := []Token{ + {ID: 1, Name: "token1"}, + {ID: 2, Name: "token2"}, + } + + err := io.PrintJSON(tokens) + require.NoError(t, err) + + // Parse the output + var result []Token + jsonBytes := bytes.TrimSpace(buf.Bytes()) + err = json.Unmarshal(jsonBytes, &result) + require.NoError(t, err) + + assert.Len(t, result, 2) + assert.Equal(t, tokens[0].ID, result[0].ID) + assert.Equal(t, tokens[0].Name, result[0].Name) + assert.Equal(t, tokens[1].ID, result[1].ID) + assert.Equal(t, tokens[1].Name, result[1].Name) +} + +func TestPrintJSON_NestedNilSlicesPreserved(t *testing.T) { + // Test that nested nil slices (from API responses) are preserved as null + // to maintain the semantic difference between absent and empty + type Token struct { + ID int `json:"id"` + Scopes []string `json:"scopes"` // nil should stay null in JSON + } + + buf := &bytes.Buffer{} + io := &IOStreams{StdOut: buf} + + tokens := []Token{ + {ID: 1, Scopes: nil}, // This nil should remain null in the JSON output + } + + err := io.PrintJSON(tokens) + require.NoError(t, err) + + // Verify the nested nil slice is preserved as null + jsonBytes := bytes.TrimSpace(buf.Bytes()) + var result []map[string]any + err = json.Unmarshal(jsonBytes, &result) + require.NoError(t, err) + + require.Len(t, result, 1) + + // The scopes field should be null (present but nil) + scopes, exists := result[0]["scopes"] + assert.True(t, exists, "expected scopes field to exist") + assert.Nil(t, scopes, "expected scopes to be null") +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/glab-1.81.0/scripts/setup_windows.iss new/glab-1.82.0/scripts/setup_windows.iss --- old/glab-1.81.0/scripts/setup_windows.iss 2026-01-20 16:21:36.000000000 +0100 +++ new/glab-1.82.0/scripts/setup_windows.iss 2026-01-29 09:31:59.000000000 +0100 @@ -37,7 +37,7 @@ OutputBaseFilename=glab_{#Version}_Windows_x86_64_installer Compression=lzma SolidCompression=yes -WizardStyle=modern +WizardStyle=modern dynamic [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" @@ -73,25 +73,180 @@ Name: "{group}\{#MyAppName}"; Filename: "{app}\{#ExeName}" [Code] -function NeedsAddPath(Param: string): boolean; +function BoolToStr(B: Boolean; const TrueStr, FalseStr: string): string; +begin + if B then + Result := TrueStr + else + Result := FalseStr; +end; + +procedure GetRegPath(var RegRootKey: Integer; var RegRootKeyStr, RegSubkeyPath: string); +begin + if IsAdminInstallMode then + begin + RegRootKey := HKEY_LOCAL_MACHINE; + RegRootKeyStr := 'HKEY_LOCAL_MACHINE'; + RegSubkeyPath := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'; + end + else + begin + RegRootKey := HKEY_CURRENT_USER; + RegRootKeyStr := 'HKEY_CURRENT_USER'; + RegSubkeyPath := 'Environment'; + end; +end; + +function PosIgnoreCase(SubStr, S: AnyString): Integer; +begin + Result := Pos(AnsiUppercase(SubStr), AnsiUppercase(S)); +end; + +function QuotePathIfNeeded(const Path: string): string; +begin + // If path contains semicolon, enclose it in double quotes + if Pos(';', Path) > 0 then + Result := '"' + Path + '"' + else + Result := Path; +end; + +procedure AddPath; var + Existed: Boolean; + AppPath: string; OrigPath: string; - RegPathKey: string; + NewPath: string; + RegRootKey: Integer; + RegRootKeyStr: string; + RegSubkeyPath: string; begin - if IsAdminInstallMode then - RegPathKey := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + AppPath := QuotePathIfNeeded(ExpandConstant('{app}')); + GetRegPath(RegRootKey, RegRootKeyStr, RegSubkeyPath); + + LogFmt('Adding application path %s to %s PATH', [AppPath, BoolToStr(IsAdminInstallMode, 'system', 'user')]); + + // Read existing PATH value, use empty value if it doesn't exist + Existed := RegValueExists(RegRootKey, RegSubkeyPath, 'Path'); + if Existed then + begin + if not RegQueryStringValue(RegRootKey, RegSubkeyPath, 'Path', OrigPath) then begin + LogFmt('[ERROR] Failed to read registry key %s\%s\Path', [RegRootKeyStr, RegSubkeyPath]); + MsgBox('Failed to read PATH environment variable from registry. ' + + 'You may need to manually add: ' + AppPath, mbError, MB_OK); + exit; + end; + end else - RegPathKey := 'Environment'; + begin + OrigPath := ''; + end; + + // Check if path already contains the application path + if PosIgnoreCase(';' + AppPath + ';', ';' + OrigPath + ';') > 0 then begin + LogFmt('Application path already in registry key %s\%s\Path, skipping update', [RegRootKeyStr, RegSubkeyPath]); + exit; + end; + + if Length(OrigPath) = 0 then + NewPath := AppPath + else + begin + if Copy(OrigPath, Length(OrigPath), 1) = ';' then + NewPath := OrigPath + AppPath + else + NewPath := OrigPath + ';' + AppPath; + end; + + if RegWriteExpandStringValue(RegRootKey, RegSubkeyPath, 'Path', NewPath) then + begin + if Existed then + LogFmt('Registry key %s\%s\Path updated successfully', [RegRootKeyStr, RegSubkeyPath]) + else + LogFmt('Registry key %s\%s\Path created successfully', [RegRootKeyStr, RegSubkeyPath]); + end + else + begin + LogFmt('[ERROR] Failed to write to registry key %s\%s\Path', [RegRootKeyStr, RegSubkeyPath]) + MsgBox('Failed to update PATH environment variable. ' + + 'You may need to manually add: ' + AppPath, mbError, MB_OK); + end; +end; + +procedure RemovePath; +var + AppPathSemicolons: string; + AppPath: string; + OrigPath: string; + NewPath: string; + RegRootKey: Integer; + RegRootKeyStr: string; + RegSubkeyPath: string; + Index: Integer; +begin + AppPath := QuotePathIfNeeded(ExpandConstant('{app}')); + AppPathSemicolons := ';' + AppPath + ';'; + GetRegPath(RegRootKey, RegRootKeyStr, RegSubkeyPath); + + LogFmt('Removing application path %s from %s PATH', [AppPath, BoolToStr(IsAdminInstallMode, 'system', 'user')]); - if not RegQueryStringValue(HKA, RegPathKey, 'Path', OrigPath) then begin - Result := True; + if not RegQueryStringValue(RegRootKey, RegSubkeyPath, 'Path', OrigPath) then begin + LogFmt('[ERROR] Failed to read registry key %s\%s\Path', [RegRootKeyStr, RegSubkeyPath]); exit; end; - // look for the path with leading and trailing semicolon - // Pos() returns 0 if not found - Result := Pos(';' + Param + ';', ';' + OrigPath + ';') = 0; + + NewPath := ';' + OrigPath + ';'; + Index := PosIgnoreCase(AppPathSemicolons, NewPath); + + if Index > 0 then + begin + // The PATH contains application path, remove it with surrounding semicolons + Delete(NewPath, Index, Length(AppPathSemicolons)); + + // Re-balance semicolons in the PATH after deleting ";{app};" from ";PATH;" + if Index = 1 then + begin + // Removed app path from the beginning, so remove only trailing + // semicolon, our leading one has been removed already + if Length(NewPath) > 0 then Delete(NewPath, Length(NewPath), 1); + end + else if (Index > 1) and (Index < Length(NewPath)) then + begin + // Removed app path from the middle of the string, so re-insert now + // missing semicolon and remove both the leading and trailing semicolons + Insert(';', NewPath, Index); + Delete(NewPath, Length(NewPath), 1); + Delete(NewPath, 1, 1); + end + else + begin + // Removed app path from the end, so remove only the leading semicolon + Delete(NewPath, 1, 1); + end; + + if RegWriteExpandStringValue(RegRootKey, RegSubkeyPath, 'Path', NewPath) then + LogFmt('Registry key %s\%s\Path updated successfully', [RegRootKeyStr, RegSubkeyPath]) + else + begin + LogFmt('[ERROR] Failed to write registry key %s\%s\Path', [RegRootKeyStr, RegSubkeyPath]); + MsgBox('Failed to update PATH environment variable during uninstallation. ' + + 'You may need to manually remove: ' + AppPath, mbError, MB_OK); + end; + end + else + LogFmt('Application path not found in registry key %s\%s\Path, no cleanup needed', [RegRootKeyStr, RegSubkeyPath]); end; -[Registry] -Root: HKA; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}"; Check: IsAdminInstallMode and NeedsAddPath(ExpandConstant('{app}')) -Root: HKA; Subkey: "Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}"; Check: not IsAdminInstallMode and NeedsAddPath(ExpandConstant('{app}')) +procedure CurStepChanged(CurStep: TSetupStep); +begin + if CurStep = ssPostInstall then begin + AddPath(); + end; +end; + +procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); +begin + if CurUninstallStep = usUninstall then begin + RemovePath(); + end; +end; \ No newline at end of file ++++++ glab.obsinfo ++++++ --- /var/tmp/diff_new_pack.py1E9v/_old 2026-02-02 14:58:19.466456491 +0100 +++ /var/tmp/diff_new_pack.py1E9v/_new 2026-02-02 14:58:19.474456829 +0100 @@ -1,5 +1,5 @@ name: glab -version: 1.81.0 -mtime: 1768922496 -commit: 01fd7984c91ff91b54646a16cb4f571d6164911e +version: 1.82.0 +mtime: 1769675519 +commit: b932a8d879a24d02e0c094aa178c7442c9a589a5 ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/glab/vendor.tar.gz /work/SRC/openSUSE:Factory/.glab.new.1995/vendor.tar.gz differ: char 133, line 1
