Merge branch 'main' into v2
* main: build: use specific token for pushing release tags fix(gui): update `uncamel()` to handle strings like 'IDs' (fixes #10128) (#10131) refactor: use slices package for sort (#10132) build: process for automatic release tags (#10133) chore(gui, man, authors): update docs, translations, and contributors
This commit is contained in:
@@ -162,7 +162,7 @@ jobs:
|
|||||||
|
|
||||||
codesign-windows:
|
codesign-windows:
|
||||||
name: Codesign for Windows
|
name: Codesign for Windows
|
||||||
if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
|
if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
environment: release
|
environment: release
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
needs:
|
needs:
|
||||||
@@ -282,7 +282,7 @@ jobs:
|
|||||||
|
|
||||||
package-macos:
|
package-macos:
|
||||||
name: Package for macOS
|
name: Package for macOS
|
||||||
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
|
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
environment: release
|
environment: release
|
||||||
env:
|
env:
|
||||||
CODESIGN_IDENTITY: ${{ secrets.CODESIGN_IDENTITY }}
|
CODESIGN_IDENTITY: ${{ secrets.CODESIGN_IDENTITY }}
|
||||||
@@ -385,7 +385,7 @@ jobs:
|
|||||||
|
|
||||||
notarize-macos:
|
notarize-macos:
|
||||||
name: Notarize for macOS
|
name: Notarize for macOS
|
||||||
if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
|
if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
environment: release
|
environment: release
|
||||||
needs:
|
needs:
|
||||||
- package-macos
|
- package-macos
|
||||||
@@ -529,7 +529,7 @@ jobs:
|
|||||||
|
|
||||||
sign-for-upgrade:
|
sign-for-upgrade:
|
||||||
name: Sign for upgrade
|
name: Sign for upgrade
|
||||||
if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
|
if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
environment: release
|
environment: release
|
||||||
needs:
|
needs:
|
||||||
- codesign-windows
|
- codesign-windows
|
||||||
@@ -732,6 +732,8 @@ jobs:
|
|||||||
name: Publish release files
|
name: Publish release files
|
||||||
if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/tags/v'))
|
if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
environment: release
|
environment: release
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
needs:
|
needs:
|
||||||
- sign-for-upgrade
|
- sign-for-upgrade
|
||||||
- package-debian
|
- package-debian
|
||||||
@@ -791,13 +793,35 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
args: sync -v objstore:release/${{ env.VERSION }} objstore:release/latest
|
args: sync -v objstore:release/${{ env.VERSION }} objstore:release/latest
|
||||||
|
|
||||||
|
- name: Create GitHub release and push binaries
|
||||||
|
run: |
|
||||||
|
maybePrerelease=""
|
||||||
|
if [[ $VERSION == *-* ]]; then
|
||||||
|
maybePrerelease="--prerelease"
|
||||||
|
fi
|
||||||
|
export GH_PROMPT_DISABLED=1
|
||||||
|
if ! gh release view --json name "$VERSION" >/dev/null 2>&1 ; then
|
||||||
|
gh release create \
|
||||||
|
"$VERSION" \
|
||||||
|
$maybePrerelease \
|
||||||
|
--title "$VERSION" \
|
||||||
|
--notes-from-tag
|
||||||
|
fi
|
||||||
|
gh release upload "$VERSION" \
|
||||||
|
packages/*.asc packages/*.json \
|
||||||
|
packages/syncthing-*.tar.gz \
|
||||||
|
packages/syncthing-*.zip \
|
||||||
|
packages/syncthing*.deb
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Push Debian/APT archive
|
# Push Debian/APT archive
|
||||||
#
|
#
|
||||||
|
|
||||||
publish-apt:
|
publish-apt:
|
||||||
name: Publish APT
|
name: Publish APT
|
||||||
if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
|
if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
environment: release
|
environment: release
|
||||||
needs:
|
needs:
|
||||||
- package-debian
|
- package-debian
|
||||||
@@ -878,7 +902,7 @@ jobs:
|
|||||||
docker-syncthing:
|
docker-syncthing:
|
||||||
name: Build and push Docker images
|
name: Build and push Docker images
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/release-nightly' || github.ref == 'refs/heads/infrastructure' || startsWith(github.ref, 'refs/tags/v'))
|
||||||
environment: docker
|
environment: docker
|
||||||
env:
|
env:
|
||||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
name: Release Syncthing
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- release
|
||||||
|
- release-rc*
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
create-release-tag:
|
||||||
|
name: Create release tag
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment: release
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
|
||||||
|
token: ${{ secrets.STRELEASE_GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: stable
|
||||||
|
|
||||||
|
- name: Get svu
|
||||||
|
run: |
|
||||||
|
go install github.com/caarlos0/svu@latest
|
||||||
|
|
||||||
|
- name: Determine version to release
|
||||||
|
run: |
|
||||||
|
if [[ "$GITHUB_REF_NAME" == "release" ]] ; then
|
||||||
|
next=$(svu next)
|
||||||
|
else
|
||||||
|
next=$(svu prerelease --pre-release rc)
|
||||||
|
fi
|
||||||
|
echo "NEXT=$next" >> $GITHUB_ENV
|
||||||
|
echo "Next version is $next"
|
||||||
|
|
||||||
|
prev=$(git describe --exclude "*-*" --abbrev=0)
|
||||||
|
echo "PREV=$prev" >> $GITHUB_ENV
|
||||||
|
echo "Previous version is $prev"
|
||||||
|
|
||||||
|
- name: Determine release notes
|
||||||
|
run: |
|
||||||
|
go run ./script/relnotes.go --new-ver "$NEXT" --branch "$GITHUB_REF_NAME" --prev-ver "$PREV" > notes.md
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.STRELEASE_GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create and push tag
|
||||||
|
run: |
|
||||||
|
git config --global user.name 'Syncthing Release Automation'
|
||||||
|
git config --global user.email 'release@syncthing.net'
|
||||||
|
git tag -a -F notes.md --cleanup=whitespace "$NEXT"
|
||||||
|
git push origin "$NEXT"
|
||||||
@@ -8,6 +8,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"cmp"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
@@ -15,7 +16,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -177,8 +178,8 @@ func (d *diskStore) inventory() error {
|
|||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
sort.Slice(d.currentFiles, func(i, j int) bool {
|
slices.SortFunc(d.currentFiles, func(a, b currentFile) int {
|
||||||
return d.currentFiles[i].mtime < d.currentFiles[j].mtime
|
return cmp.Compare(a.mtime, b.mtime)
|
||||||
})
|
})
|
||||||
var oldest time.Duration
|
var oldest time.Duration
|
||||||
if len(d.currentFiles) > 0 {
|
if len(d.currentFiles) > 0 {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"sort"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
@@ -338,7 +338,7 @@ func debugFacilities() string {
|
|||||||
maxLen = len(name)
|
maxLen = len(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Strings(names)
|
slices.Sort(names)
|
||||||
|
|
||||||
// Format the choices
|
// Format the choices
|
||||||
b := new(bytes.Buffer)
|
b := new(bytes.Buffer)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"A device with that ID is already added.": "أضيف هذا الجهاز بالفعل.",
|
"A device with that ID is already added.": "أضيف هذا الجهاز بالفعل.",
|
||||||
"A negative number of days doesn't make sense.": "لا يمكن استخدام قيمة سالبة لعدد الأيام.",
|
"A negative number of days doesn't make sense.": "لا يمكن استخدام قيمة سالبة لعدد الأيام.",
|
||||||
"A new major version may not be compatible with previous versions.": "الإصدار الجديد قد لا يتوافق مع الإصدارات السابقة.",
|
"A new major version may not be compatible with previous versions.": "الإصدار الجديد قد لا يتوافق مع الإصدارات السابقة.",
|
||||||
"API Key": "مفتاح API",
|
"API Key": "مفتاح واجهة برمجة التطبيقات \"API\"",
|
||||||
"About": "حول",
|
"About": "حول",
|
||||||
"Action": "إجراء",
|
"Action": "إجراء",
|
||||||
"Actions": "الإجراءات",
|
"Actions": "الإجراءات",
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
"Allowed Networks": "الشبكات المسموح بها",
|
"Allowed Networks": "الشبكات المسموح بها",
|
||||||
"Alphabetic": "أبجدية",
|
"Alphabetic": "أبجدية",
|
||||||
"Altered by ignoring deletes.": "تغير بتجاهل عمليات الحذف.",
|
"Altered by ignoring deletes.": "تغير بتجاهل عمليات الحذف.",
|
||||||
|
"Always turned on when the folder type is \"{%foldertype%}\".": "مفعل دائمًا عندما يكون نوع المجلد هو \"{{foldertype}}\".",
|
||||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "الإصدار يعالج بواسطة أمر خارجي. يجب إزالة الملف من المجلدات المشتركة. إذا كان المسار للتطبيق يحتوي على مسافات، يجب وضعها بين علامتي تنصيص دلالة على الاقتباس.",
|
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "الإصدار يعالج بواسطة أمر خارجي. يجب إزالة الملف من المجلدات المشتركة. إذا كان المسار للتطبيق يحتوي على مسافات، يجب وضعها بين علامتي تنصيص دلالة على الاقتباس.",
|
||||||
"Anonymous Usage Reporting": "تقارير الإستخدام المجهولة",
|
"Anonymous Usage Reporting": "تقارير الإستخدام المجهولة",
|
||||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "هل تريد الانتقال الى التصميم الجديد لتقرير الاستخدام المجهول ؟",
|
"Anonymous usage report format has changed. Would you like to move to the new format?": "هل تريد الانتقال الى التصميم الجديد لتقرير الاستخدام المجهول ؟",
|
||||||
@@ -52,6 +53,7 @@
|
|||||||
"Body:": "جسم:",
|
"Body:": "جسم:",
|
||||||
"Bugs": "أخطاء برمجية",
|
"Bugs": "أخطاء برمجية",
|
||||||
"Cancel": "إلغاء",
|
"Cancel": "إلغاء",
|
||||||
|
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "لا يمكن تفعيله عندما يكون نوع المجلد هو \"{{foldertype}}\".",
|
||||||
"Changelog": "سجل التغيير",
|
"Changelog": "سجل التغيير",
|
||||||
"Clean out after": "نظف بعد",
|
"Clean out after": "نظف بعد",
|
||||||
"Cleaning Versions": "إصدارات نظيفة",
|
"Cleaning Versions": "إصدارات نظيفة",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"Allow Anonymous Usage Reporting?": "Permiteţi raportarea anonimă de folosire a aplicaţiei?",
|
"Allow Anonymous Usage Reporting?": "Permiteţi raportarea anonimă de folosire a aplicaţiei?",
|
||||||
"Allowed Networks": "Rețele permise",
|
"Allowed Networks": "Rețele permise",
|
||||||
"Alphabetic": "Alfabetic",
|
"Alphabetic": "Alfabetic",
|
||||||
|
"Altered by ignoring deletes.": "Modificat prin ignorarea ștergerilor.",
|
||||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "O comandă externă gestionează versiunea. Trebuie să elimine fișierul din mapa partajat. Dacă calea către aplicație conține spații, ar trebui să fie pusă între ghilimele.",
|
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "O comandă externă gestionează versiunea. Trebuie să elimine fișierul din mapa partajat. Dacă calea către aplicație conține spații, ar trebui să fie pusă între ghilimele.",
|
||||||
"Anonymous Usage Reporting": "Raport Anonim despre Folosirea Aplicației",
|
"Anonymous Usage Reporting": "Raport Anonim despre Folosirea Aplicației",
|
||||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Formatul raportului de utilizare anonim s-a schimbat. Doriți să vă mutați în noul format?",
|
"Anonymous usage report format has changed. Would you like to move to the new format?": "Formatul raportului de utilizare anonim s-a schimbat. Doriți să vă mutați în noul format?",
|
||||||
|
|||||||
@@ -1,2 +1,36 @@
|
|||||||
{
|
{
|
||||||
|
"A device with that ID is already added.": "Уређај са тим идентификатором је већ додат.",
|
||||||
|
"A negative number of days doesn't make sense.": "Негативан број дана нема смисла.",
|
||||||
|
"A new major version may not be compatible with previous versions.": "Нова верзија можда неће радити са претходним верзијама.",
|
||||||
|
"API Key": "АПИ кључ",
|
||||||
|
"About": "Информације",
|
||||||
|
"Action": "Радња",
|
||||||
|
"Actions": "Радње",
|
||||||
|
"Active filter rules": "Активна правила филтера",
|
||||||
|
"Add": "Додај",
|
||||||
|
"Add Device": "Додај уређај",
|
||||||
|
"Add Folder": "Додај фасциклу",
|
||||||
|
"Add Remote Device": "Додаај удаљени уређај",
|
||||||
|
"Add devices from the introducer to our device list, for mutually shared folders.": "Додај уређаје од иницијатора на нашу листу уређаја, за међусобно дељене фасцикле.",
|
||||||
|
"Add filter entry": "Додај ставку филтера",
|
||||||
|
"Add ignore patterns": "Додај правила за игнорисање",
|
||||||
|
"Add new folder?": "Додај нову фасциклу?",
|
||||||
|
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Додатно, интервал потпуног поновног скенирања ће бити повећан (60 пута, тј. нови подразумевани интервал од 1 сат). Такође можете ручно да га подесите за сваку фасциклу касније након што изаберете Не.",
|
||||||
|
"Address": "Адреса",
|
||||||
|
"Addresses": "Адресе",
|
||||||
|
"Advanced": "Напредно",
|
||||||
|
"Advanced Configuration": "Напредна конфигурација",
|
||||||
|
"All Data": "Сви подаци",
|
||||||
|
"All Time": "Све време",
|
||||||
|
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Све фасцикле које се деле са овим уређајем морају бити заштићене лозинком, тако да сви послати подаци не могу бити прочитани без дате лозинке.",
|
||||||
|
"Allow Anonymous Usage Reporting?": "Дозволити анонимно слање података о коришћењу?",
|
||||||
|
"Allowed Networks": "Дозвољене мреже",
|
||||||
|
"Alphabetic": "Абецедним редом",
|
||||||
|
"Altered by ignoring deletes.": "Промењено због игнорисања брисања.",
|
||||||
|
"Always turned on when the folder type is \"{%foldertype%}\".": "Увек укључено када је тип фасцикле „{{foldertype}}\".",
|
||||||
|
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Екстерна команда управља верзионирањем. Она мора да уклони фајл из дељене фасцикле. Ако путања до апликације садржи размаке, треба да буде под наводницима.",
|
||||||
|
"Anonymous Usage Reporting": "Анонимно слање података о употреби",
|
||||||
|
"Anonymous usage report format has changed. Would you like to move to the new format?": "Формат анонимног слања података о коришћењу је промењен. Желите ли да пређете на нови формат?",
|
||||||
|
"Applied to LAN": "Важи за локалну мрежу",
|
||||||
|
"Apply": "Примени"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,39 @@
|
|||||||
angular.module('syncthing.core')
|
angular.module('syncthing.core')
|
||||||
.filter('uncamel', function () {
|
.filter('uncamel', function () {
|
||||||
|
const reservedStrings = [
|
||||||
|
'IDs', 'ID', // substrings must come AFTER longer keywords containing them
|
||||||
|
'URL', 'UR',
|
||||||
|
'API', 'QUIC', 'TCP', 'UDP', 'NAT', 'LAN', 'WAN',
|
||||||
|
'KiB', 'MiB', 'GiB', 'TiB'
|
||||||
|
];
|
||||||
return function (input) {
|
return function (input) {
|
||||||
input = input.replace(/(.)([A-Z][a-z]+)/g, '$1 $2').replace(/([a-z0-9])([A-Z])/g, '$1 $2');
|
if (!input || typeof input !== 'string') return '';
|
||||||
var parts = input.split(' ');
|
const placeholders = {};
|
||||||
var lastPart = parts.splice(-1)[0];
|
let counter = 0;
|
||||||
|
reservedStrings.forEach(word => {
|
||||||
|
const placeholder = `__RSV${counter}__`;
|
||||||
|
const re = new RegExp(word, 'g');
|
||||||
|
input = input.replace(re, placeholder);
|
||||||
|
placeholders[placeholder] = word;
|
||||||
|
counter++;
|
||||||
|
});
|
||||||
|
input = input.replace(/([a-z0-9])([A-Z])/g, '$1 $2');
|
||||||
|
Object.entries(placeholders).forEach(([ph, word]) => {
|
||||||
|
input = input.replace(new RegExp(ph, 'g'), ` ${word} `);
|
||||||
|
});
|
||||||
|
let parts = input.split(' ');
|
||||||
|
const lastPart = parts.pop();
|
||||||
switch (lastPart) {
|
switch (lastPart) {
|
||||||
case "S":
|
case 'S': parts.push('(seconds)'); break;
|
||||||
parts.push('(seconds)');
|
case 'M': parts.push('(minutes)'); break;
|
||||||
break;
|
case 'H': parts.push('(hours)'); break;
|
||||||
case "M":
|
case 'Ms': parts.push('(milliseconds)'); break;
|
||||||
parts.push('(minutes)');
|
default: parts.push(lastPart); break;
|
||||||
break;
|
|
||||||
case "H":
|
|
||||||
parts.push('(hours)');
|
|
||||||
break;
|
|
||||||
case "Ms":
|
|
||||||
parts.push('(milliseconds)');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
parts.push(lastPart);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
input = parts.join(' ');
|
parts = parts.map(part => {
|
||||||
return input.charAt(0).toUpperCase() + input.slice(1);
|
const match = reservedStrings.find(w => w.toUpperCase() === part.toUpperCase());
|
||||||
|
return match || part.charAt(0).toUpperCase() + part.slice(1);
|
||||||
|
});
|
||||||
|
return parts.join(' ').replace(/\s+/g, ' ').trim();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ package olddb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"sort"
|
"slices"
|
||||||
|
|
||||||
"github.com/syncthing/syncthing/internal/db/olddb/backend"
|
"github.com/syncthing/syncthing/internal/db/olddb/backend"
|
||||||
"github.com/syncthing/syncthing/lib/sync"
|
"github.com/syncthing/syncthing/lib/sync"
|
||||||
@@ -104,6 +104,6 @@ func (i *smallIndex) Values() []string {
|
|||||||
}
|
}
|
||||||
i.mut.Unlock()
|
i.mut.Unlock()
|
||||||
|
|
||||||
sort.Strings(vals)
|
slices.Sort(vals)
|
||||||
return vals
|
return vals
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-7
@@ -8,6 +8,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"cmp"
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
@@ -24,7 +25,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"sort"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -750,7 +751,7 @@ func (*service) getSystemVersion(w http.ResponseWriter, _ *http.Request) {
|
|||||||
func (*service) getSystemDebug(w http.ResponseWriter, _ *http.Request) {
|
func (*service) getSystemDebug(w http.ResponseWriter, _ *http.Request) {
|
||||||
names := l.Facilities()
|
names := l.Facilities()
|
||||||
enabled := l.FacilityDebugging()
|
enabled := l.FacilityDebugging()
|
||||||
sort.Strings(enabled)
|
slices.Sort(enabled)
|
||||||
sendJSON(w, map[string]interface{}{
|
sendJSON(w, map[string]interface{}{
|
||||||
"facilities": names,
|
"facilities": names,
|
||||||
"enabled": enabled,
|
"enabled": enabled,
|
||||||
@@ -1516,8 +1517,8 @@ func (*service) getLang(w http.ResponseWriter, r *http.Request) {
|
|||||||
langs = append(langs, code)
|
langs = append(langs, code)
|
||||||
}
|
}
|
||||||
// Reorder by descending q value
|
// Reorder by descending q value
|
||||||
sort.SliceStable(langs, func(i, j int) bool {
|
slices.SortStableFunc(langs, func(i, j string) int {
|
||||||
return weights[langs[i]] > weights[langs[j]]
|
return cmp.Compare(weights[j], weights[i])
|
||||||
})
|
})
|
||||||
sendJSON(w, langs)
|
sendJSON(w, langs)
|
||||||
}
|
}
|
||||||
@@ -1803,8 +1804,8 @@ func browseFiles(ffs fs.Filesystem, search string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sort to return matches in deterministic order (don't depend on file system order)
|
// sort to return matches in deterministic order (don't depend on file system order)
|
||||||
sort.Strings(exactMatches)
|
slices.Sort(exactMatches)
|
||||||
sort.Strings(caseInsMatches)
|
slices.Sort(caseInsMatches)
|
||||||
return append(exactMatches, caseInsMatches...)
|
return append(exactMatches, caseInsMatches...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1901,7 +1902,7 @@ func dirNames(dir string) []string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(dirs)
|
slices.Sort(dirs)
|
||||||
return dirs
|
return dirs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -12,7 +12,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -127,7 +127,7 @@ func TagsList() []string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(tags)
|
slices.Sort(tags)
|
||||||
|
|
||||||
// Remove any empty tags, which will be at the front of the list now
|
// Remove any empty tags, which will be at the front of the list now
|
||||||
for len(tags) > 0 && tags[0] == "" {
|
for len(tags) > 0 && tags[0] == "" {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -380,8 +381,8 @@ func (cfg *Configuration) prepareFolders(myID protocol.DeviceID, existingDevices
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Ensure that the folder list is sorted by ID
|
// Ensure that the folder list is sorted by ID
|
||||||
sort.Slice(cfg.Folders, func(a, b int) bool {
|
slices.SortFunc(cfg.Folders, func(a, b FolderConfiguration) int {
|
||||||
return cfg.Folders[a].ID < cfg.Folders[b].ID
|
return strings.Compare(a.ID, b.ID)
|
||||||
})
|
})
|
||||||
return sharedFolders, nil
|
return sharedFolders, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -911,7 +911,7 @@ func TestV14ListenAddressesMigration(t *testing.T) {
|
|||||||
t.Error("Configuration was not converted")
|
t.Error("Configuration was not converted")
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(tc[2])
|
slices.Sort(tc[2])
|
||||||
if !reflect.DeepEqual(cfg.Options.RawListenAddresses, tc[2]) {
|
if !reflect.DeepEqual(cfg.Options.RawListenAddresses, tc[2]) {
|
||||||
t.Errorf("Migration error; actual %#v != expected %#v", cfg.Options.RawListenAddresses, tc[2])
|
t.Errorf("Migration error; actual %#v != expected %#v", cfg.Options.RawListenAddresses, tc[2])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,12 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmp"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -66,8 +67,8 @@ type migrationSet []migration
|
|||||||
func (ms migrationSet) apply(cfg *Configuration) {
|
func (ms migrationSet) apply(cfg *Configuration) {
|
||||||
// Make sure we apply the migrations in target version order regardless
|
// Make sure we apply the migrations in target version order regardless
|
||||||
// of how it was defined.
|
// of how it was defined.
|
||||||
sort.Slice(ms, func(a, b int) bool {
|
slices.SortFunc(ms, func(a, b migration) int {
|
||||||
return ms[a].targetVersion < ms[b].targetVersion
|
return cmp.Compare(a.targetVersion, b.targetVersion)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Apply all migrations.
|
// Apply all migrations.
|
||||||
@@ -354,7 +355,7 @@ func migrateToConfigV14(cfg *Configuration) {
|
|||||||
cfg.Options.DeprecatedRelayServers = nil
|
cfg.Options.DeprecatedRelayServers = nil
|
||||||
|
|
||||||
// For consistency
|
// For consistency
|
||||||
sort.Strings(cfg.Options.RawListenAddresses)
|
slices.Sort(cfg.Options.RawListenAddresses)
|
||||||
|
|
||||||
var newAddrs []string
|
var newAddrs []string
|
||||||
for _, addr := range cfg.Options.RawGlobalAnnServers {
|
for _, addr := range cfg.Options.RawGlobalAnnServers {
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ package config
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"sort"
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/syncthing/syncthing/lib/structutil"
|
"github.com/syncthing/syncthing/lib/structutil"
|
||||||
)
|
)
|
||||||
@@ -84,8 +85,8 @@ func (c *VersioningConfiguration) toInternal() internalVersioningConfiguration {
|
|||||||
for k, v := range c.Params {
|
for k, v := range c.Params {
|
||||||
tmp.Params = append(tmp.Params, internalParam{k, v})
|
tmp.Params = append(tmp.Params, internalParam{k, v})
|
||||||
}
|
}
|
||||||
sort.Slice(tmp.Params, func(a, b int) bool {
|
slices.SortFunc(tmp.Params, func(a, b internalParam) int {
|
||||||
return tmp.Params[a].Key < tmp.Params[b].Key
|
return strings.Compare(a.Key, b.Key)
|
||||||
})
|
})
|
||||||
return tmp
|
return tmp
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
stdsync "sync"
|
stdsync "sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -1151,7 +1150,7 @@ func (s *service) dialParallel(ctx context.Context, deviceID protocol.DeviceID,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sort the priorities so that we dial lowest first (which means highest...)
|
// Sort the priorities so that we dial lowest first (which means highest...)
|
||||||
sort.Ints(priorities)
|
slices.Sort(priorities)
|
||||||
|
|
||||||
sema := semaphore.MultiSemaphore{semaphore.New(dialMaxParallelPerDevice), parentSema}
|
sema := semaphore.MultiSemaphore{semaphore.New(dialMaxParallelPerDevice), parentSema}
|
||||||
for _, prio := range priorities {
|
for _, prio := range priorities {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/thejerf/suture/v4"
|
"github.com/thejerf/suture/v4"
|
||||||
@@ -159,7 +159,7 @@ func (m *manager) Lookup(ctx context.Context, deviceID protocol.DeviceID) (addre
|
|||||||
m.mut.RUnlock()
|
m.mut.RUnlock()
|
||||||
|
|
||||||
addresses = stringutil.UniqueTrimmedStrings(addresses)
|
addresses = stringutil.UniqueTrimmedStrings(addresses)
|
||||||
sort.Strings(addresses)
|
slices.Sort(addresses)
|
||||||
|
|
||||||
l.Debugln("lookup results for", deviceID)
|
l.Debugln("lookup results for", deviceID)
|
||||||
l.Debugln(" addresses: ", addresses)
|
l.Debugln(" addresses: ", addresses)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
@@ -218,7 +218,7 @@ func TestDirNames(t *testing.T) {
|
|||||||
"a",
|
"a",
|
||||||
"bC",
|
"bC",
|
||||||
}
|
}
|
||||||
sort.Strings(testCases)
|
slices.Sort(testCases)
|
||||||
|
|
||||||
for _, sub := range testCases {
|
for _, sub := range testCases {
|
||||||
if err := os.Mkdir(filepath.Join(dir, sub), 0o777); err != nil {
|
if err := os.Mkdir(filepath.Join(dir, sub), 0o777); err != nil {
|
||||||
@@ -229,7 +229,7 @@ func TestDirNames(t *testing.T) {
|
|||||||
if dirs, err := fs.DirNames("."); err != nil || len(dirs) != len(testCases) {
|
if dirs, err := fs.DirNames("."); err != nil || len(dirs) != len(testCases) {
|
||||||
t.Errorf("%s %s %s", err, dirs, testCases)
|
t.Errorf("%s %s %s", err, dirs, testCases)
|
||||||
} else {
|
} else {
|
||||||
sort.Strings(dirs)
|
slices.Sort(dirs)
|
||||||
for i := range dirs {
|
for i := range dirs {
|
||||||
if dirs[i] != testCases[i] {
|
if dirs[i] != testCases[i] {
|
||||||
t.Errorf("%s != %s", dirs[i], testCases[i])
|
t.Errorf("%s != %s", dirs[i], testCases[i])
|
||||||
@@ -321,8 +321,8 @@ func TestGlob(t *testing.T) {
|
|||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
results, err := fs.Glob(testCase.pattern)
|
results, err := fs.Glob(testCase.pattern)
|
||||||
sort.Strings(results)
|
slices.Sort(results)
|
||||||
sort.Strings(testCase.matches)
|
slices.Sort(testCase.matches)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
@@ -628,8 +628,7 @@ func TestXattr(t *testing.T) {
|
|||||||
Value: value,
|
Value: value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
sort.Slice(attrs, func(i, j int) bool { return attrs[i].Name < attrs[j].Name })
|
slices.SortFunc(attrs, func(a, b protocol.Xattr) int { return strings.Compare(a.Name, b.Name) })
|
||||||
|
|
||||||
// Set the xattrs, read them back and compare
|
// Set the xattrs, read them back and compare
|
||||||
if err := tfs.SetXattr("/test", attrs, testXattrFilter{}); err != nil {
|
if err := tfs.SetXattr("/test", attrs, testXattrFilter{}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ package fs
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"slices"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
@@ -69,7 +69,7 @@ func listXattr(path string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(attrs)
|
slices.Sort(attrs)
|
||||||
return attrs, nil
|
return attrs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ package fs
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
@@ -38,6 +38,6 @@ func listXattr(path string) ([]string, error) {
|
|||||||
buf = buf[:size]
|
buf = buf[:size]
|
||||||
attrs := compact(strings.Split(string(buf), "\x00"))
|
attrs := compact(strings.Split(string(buf), "\x00"))
|
||||||
|
|
||||||
sort.Strings(attrs)
|
slices.Sort(attrs)
|
||||||
return attrs, nil
|
return attrs, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -344,7 +344,7 @@ func fakefsForBenchmark(nfiles int, latency time.Duration) (Filesystem, []string
|
|||||||
return nil, nil, errors.New("didn't find enough stuff")
|
return nil, nil, errors.New("didn't find enough stuff")
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(paths)
|
slices.Sort(paths)
|
||||||
|
|
||||||
return fsys, paths, nil
|
return fsys, paths, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -369,8 +369,8 @@ func assertDir(t *testing.T, fs Filesystem, directory string, filenames []string
|
|||||||
if path.Clean(directory) == "/" {
|
if path.Clean(directory) == "/" {
|
||||||
filenames = append(filenames, ".stfolder")
|
filenames = append(filenames, ".stfolder")
|
||||||
}
|
}
|
||||||
sort.Strings(filenames)
|
slices.Sort(filenames)
|
||||||
sort.Strings(got)
|
slices.Sort(got)
|
||||||
|
|
||||||
if len(filenames) != len(got) {
|
if len(filenames) != len(got) {
|
||||||
t.Errorf("want %s, got %s", filenames, got)
|
t.Errorf("want %s, got %s", filenames, got)
|
||||||
|
|||||||
+2
-1
@@ -12,6 +12,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -1362,7 +1363,7 @@ func unifySubs(dirs []string, exists func(dir string) bool) []string {
|
|||||||
if len(dirs) == 0 {
|
if len(dirs) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
sort.Strings(dirs)
|
slices.Sort(dirs)
|
||||||
if dirs[0] == "" || dirs[0] == "." || dirs[0] == string(fs.PathSeparator) {
|
if dirs[0] == "" || dirs[0] == "." || dirs[0] == string(fs.PathSeparator) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -3992,8 +3991,8 @@ func equalStringsInAnyOrder(a, b []string) bool {
|
|||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
sort.Strings(a)
|
slices.Sort(a)
|
||||||
sort.Strings(b)
|
slices.Sort(b)
|
||||||
for i := range a {
|
for i := range a {
|
||||||
if a[i] != b[i] {
|
if a[i] != b[i] {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ func relayAddressesOrder(ctx context.Context, input []string) []string {
|
|||||||
ids = append(ids, id)
|
ids = append(ids, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Ints(ids)
|
slices.Sort(ids)
|
||||||
|
|
||||||
addresses := make([]string, 0, len(input))
|
addresses := make([]string, 0, len(input))
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -436,8 +436,8 @@ func printServiceTree(w io.Writer, sup supervisor, level int) {
|
|||||||
printService(w, sup, level)
|
printService(w, sup, level)
|
||||||
|
|
||||||
svcs := sup.Services()
|
svcs := sup.Services()
|
||||||
sort.Slice(svcs, func(a, b int) bool {
|
slices.SortFunc(svcs, func(a, b suture.Service) int {
|
||||||
return fmt.Sprint(svcs[a]) < fmt.Sprint(svcs[b])
|
return strings.Compare(fmt.Sprint(a), fmt.Sprint(b))
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, svc := range svcs {
|
for _, svc := range svcs {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -160,7 +160,7 @@ func (s *Service) reportData(ctx context.Context, urVersion int, preview bool) (
|
|||||||
l.Warnf("Unhandled versioning type for usage reports: %s", cfg.Versioning.Type)
|
l.Warnf("Unhandled versioning type for usage reports: %s", cfg.Versioning.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Ints(report.RescanIntvs)
|
slices.Sort(report.RescanIntvs)
|
||||||
|
|
||||||
for _, cfg := range s.cfg.Devices() {
|
for _, cfg := range s.cfg.Devices() {
|
||||||
if cfg.Introducer {
|
if cfg.Introducer {
|
||||||
@@ -288,7 +288,7 @@ func (s *Service) reportData(ctx context.Context, urVersion int, preview bool) (
|
|||||||
report.FolderUsesV3.SyncOwnership++
|
report.FolderUsesV3.SyncOwnership++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Ints(report.FolderUsesV3.FsWatcherDelays)
|
slices.Sort(report.FolderUsesV3.FsWatcherDelays)
|
||||||
|
|
||||||
for _, cfg := range s.cfg.Devices() {
|
for _, cfg := range s.cfg.Devices() {
|
||||||
if cfg.Untrusted {
|
if cfg.Untrusted {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ package versioner
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"sort"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ func (v simple) toRemove(versions []string, now time.Time) []string {
|
|||||||
var remove []string
|
var remove []string
|
||||||
|
|
||||||
// The list of versions may or may not be properly sorted.
|
// The list of versions may or may not be properly sorted.
|
||||||
sort.Strings(versions)
|
slices.Sort(versions)
|
||||||
|
|
||||||
// If the amount of elements exceeds the limit: the oldest elements are to be removed.
|
// If the amount of elements exceeds the limit: the oldest elements are to be removed.
|
||||||
if len(versions) > v.keep {
|
if len(versions) > v.keep {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ package versioner
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ func (v *staggered) toRemove(versions []string, now time.Time) []string {
|
|||||||
var remove []string
|
var remove []string
|
||||||
|
|
||||||
// The list of versions may or may not be properly sorted.
|
// The list of versions may or may not be properly sorted.
|
||||||
sort.Strings(versions)
|
slices.Sort(versions)
|
||||||
|
|
||||||
for _, version := range versions {
|
for _, version := range versions {
|
||||||
versionTime, err := time.ParseInLocation(TimeFormat, extractTag(version), time.Local)
|
versionTime, err := time.ParseInLocation(TimeFormat, extractTag(version), time.Local)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ package versioner
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -97,7 +97,7 @@ func TestStaggeredVersioningVersionCount(t *testing.T) {
|
|||||||
"test~20150416-135958", // 365 days 2 seconds ago
|
"test~20150416-135958", // 365 days 2 seconds ago
|
||||||
"test~20150414-140000", // 367 days ago
|
"test~20150414-140000", // 367 days ago
|
||||||
}
|
}
|
||||||
sort.Strings(delete)
|
slices.Sort(delete)
|
||||||
|
|
||||||
cfg := config.FolderConfiguration{
|
cfg := config.FolderConfiguration{
|
||||||
FilesystemType: config.FilesystemTypeBasic,
|
FilesystemType: config.FilesystemTypeBasic,
|
||||||
@@ -111,7 +111,7 @@ func TestStaggeredVersioningVersionCount(t *testing.T) {
|
|||||||
|
|
||||||
v := newStaggered(cfg).(*staggered)
|
v := newStaggered(cfg).(*staggered)
|
||||||
rem := v.toRemove(versionsWithMtime, now)
|
rem := v.toRemove(versionsWithMtime, now)
|
||||||
sort.Strings(rem)
|
slices.Sort(rem)
|
||||||
|
|
||||||
if diff, equal := messagediff.PrettyDiff(delete, rem); !equal {
|
if diff, equal := messagediff.PrettyDiff(delete, rem); !equal {
|
||||||
t.Errorf("Incorrect deleted files; got %v, expected %v\n%v", rem, delete, diff)
|
t.Errorf("Incorrect deleted files; got %v, expected %v\n%v", rem, delete, diff)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -339,7 +339,7 @@ func findAllVersions(fs fs.Filesystem, filePath string) []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
versions = stringutil.UniqueTrimmedStrings(versions)
|
versions = stringutil.UniqueTrimmedStrings(versions)
|
||||||
sort.Strings(versions)
|
slices.Sort(versions)
|
||||||
|
|
||||||
return versions
|
return versions
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "STDISCOSRV" "1" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "STDISCOSRV" "1" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
stdiscosrv \- Syncthing Discovery Server
|
stdiscosrv \- Syncthing Discovery Server
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "STRELAYSRV" "1" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "STRELAYSRV" "1" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
strelaysrv \- Syncthing Relay Server
|
strelaysrv \- Syncthing Relay Server
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "SYNCTHING-BEP" "7" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "SYNCTHING-BEP" "7" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
syncthing-bep \- Block Exchange Protocol v1
|
syncthing-bep \- Block Exchange Protocol v1
|
||||||
.SH INTRODUCTION AND DEFINITIONS
|
.SH INTRODUCTION AND DEFINITIONS
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "SYNCTHING-CONFIG" "5" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "SYNCTHING-CONFIG" "5" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
syncthing-config \- Syncthing Configuration
|
syncthing-config \- Syncthing Configuration
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "SYNCTHING-DEVICE-IDS" "7" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "SYNCTHING-DEVICE-IDS" "7" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
syncthing-device-ids \- Understanding Device IDs
|
syncthing-device-ids \- Understanding Device IDs
|
||||||
.sp
|
.sp
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "SYNCTHING-EVENT-API" "7" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "SYNCTHING-EVENT-API" "7" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
syncthing-event-api \- Event API
|
syncthing-event-api \- Event API
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "SYNCTHING-FAQ" "7" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "SYNCTHING-FAQ" "7" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
syncthing-faq \- Frequently Asked Questions
|
syncthing-faq \- Frequently Asked Questions
|
||||||
.INDENT 0.0
|
.INDENT 0.0
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "SYNCTHING-GLOBALDISCO" "7" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "SYNCTHING-GLOBALDISCO" "7" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
syncthing-globaldisco \- Global Discovery Protocol v3
|
syncthing-globaldisco \- Global Discovery Protocol v3
|
||||||
.SH ANNOUNCEMENTS
|
.SH ANNOUNCEMENTS
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "SYNCTHING-LOCALDISCO" "7" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "SYNCTHING-LOCALDISCO" "7" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
syncthing-localdisco \- Local Discovery Protocol v4
|
syncthing-localdisco \- Local Discovery Protocol v4
|
||||||
.SH MODE OF OPERATION
|
.SH MODE OF OPERATION
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "SYNCTHING-NETWORKING" "7" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "SYNCTHING-NETWORKING" "7" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
syncthing-networking \- Firewall Setup
|
syncthing-networking \- Firewall Setup
|
||||||
.SH ROUTER SETUP
|
.SH ROUTER SETUP
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "SYNCTHING-RELAY" "7" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "SYNCTHING-RELAY" "7" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
syncthing-relay \- Relay Protocol v1
|
syncthing-relay \- Relay Protocol v1
|
||||||
.SH WHAT IS A RELAY?
|
.SH WHAT IS A RELAY?
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "SYNCTHING-REST-API" "7" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "SYNCTHING-REST-API" "7" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
syncthing-rest-api \- REST API
|
syncthing-rest-api \- REST API
|
||||||
.sp
|
.sp
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "SYNCTHING-SECURITY" "7" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "SYNCTHING-SECURITY" "7" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
syncthing-security \- Security Principles
|
syncthing-security \- Security Principles
|
||||||
.sp
|
.sp
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "SYNCTHING-STIGNORE" "5" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "SYNCTHING-STIGNORE" "5" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
syncthing-stignore \- Prevent files from being synchronized to other nodes
|
syncthing-stignore \- Prevent files from being synchronized to other nodes
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "SYNCTHING-VERSIONING" "7" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "SYNCTHING-VERSIONING" "7" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
syncthing-versioning \- Keep automatic backups of deleted files by other nodes
|
syncthing-versioning \- Keep automatic backups of deleted files by other nodes
|
||||||
.sp
|
.sp
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
|||||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||||
..
|
..
|
||||||
.TH "SYNCTHING" "1" "May 15, 2025" "v1.29.6" "Syncthing"
|
.TH "SYNCTHING" "1" "May 25, 2025" "v1.29.6" "Syncthing"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
syncthing \- Syncthing
|
syncthing \- Syncthing
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Release Notes
|
||||||
|
|
||||||
|
Files in this directory constitute manual release notes for a given release.
|
||||||
|
When relevant, they should be created prior to that release so that they can
|
||||||
|
be included in the corresponding tag message, etc.
|
||||||
|
|
||||||
|
To add release notes for a release 1.2.3, create a file named `v1.2.3.md`
|
||||||
|
consisting of an initial H2-level header and further notes as desired. For
|
||||||
|
example:
|
||||||
|
|
||||||
|
```
|
||||||
|
## Major changes in v1.2.3
|
||||||
|
|
||||||
|
- Files are now synchronized twice as fast on Tuesdays
|
||||||
|
```
|
||||||
|
|
||||||
|
The release notes will also be included in candidate releases (e.g.
|
||||||
|
v1.2.3-rc.1).
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
// Copyright (C) 2025 The Syncthing Authors.
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
//go:build ignore
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"cmp"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
githubToken = os.Getenv("GITHUB_TOKEN")
|
||||||
|
githubRepo = cmp.Or(os.Getenv("GITHUB_REPOSITORY"), "syncthing/syncthing")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ver := flag.String("new-ver", "", "New version tag")
|
||||||
|
prevVer := flag.String("prev-ver", "", "Previous version tag")
|
||||||
|
branch := flag.String("branch", "HEAD", "Branch to release from")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
log.SetOutput(os.Stderr)
|
||||||
|
|
||||||
|
if *ver == "" {
|
||||||
|
log.Fatalln("Must set --new-ver")
|
||||||
|
}
|
||||||
|
if githubToken == "" {
|
||||||
|
log.Fatalln("Must set $GITHUB_TOKEN")
|
||||||
|
}
|
||||||
|
|
||||||
|
addl, err := additionalNotes(*ver)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Gathering additional notes:", err)
|
||||||
|
}
|
||||||
|
notes, err := generatedNotes(*ver, *branch, *prevVer)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Gathering github notes:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if addl != "" {
|
||||||
|
fmt.Println(addl)
|
||||||
|
}
|
||||||
|
fmt.Println(notes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load potential additional release notes from within the repo
|
||||||
|
func additionalNotes(newVer string) (string, error) {
|
||||||
|
ver, _, _ := strings.Cut(newVer, "-")
|
||||||
|
bs, err := os.ReadFile(fmt.Sprintf("relnotes/%s.md", ver))
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return string(bs), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load generated release notes (list of pull requests and contributors)
|
||||||
|
// from GitHub.
|
||||||
|
func generatedNotes(newVer, targetCommit, prevVer string) (string, error) {
|
||||||
|
fields := map[string]string{
|
||||||
|
"tag_name": newVer,
|
||||||
|
"target_commitish": targetCommit,
|
||||||
|
"previous_tag_name": prevVer,
|
||||||
|
}
|
||||||
|
bs, err := json.Marshal(fields)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(http.MethodPost, "https://api.github.com/repos/"+githubRepo+"/releases/generate-notes", bytes.NewReader(bs)) //nolint:noctx
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Accept", "application/vnd.github+json")
|
||||||
|
req.Header.Set("Authorization", "Bearer "+githubToken)
|
||||||
|
req.Header.Set("X-Github-Api-Version", "2022-11-28")
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
bs, _ := io.ReadAll(res.Body)
|
||||||
|
log.Print(string(bs))
|
||||||
|
return "", errors.New(res.Status) //nolint:err113
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
var resJSON struct {
|
||||||
|
Body string
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(&resJSON); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return resJSON.Body, nil
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func saveValidLangs(langs []string) {
|
func saveValidLangs(langs []string) {
|
||||||
sort.Strings(langs)
|
slices.Sort(langs)
|
||||||
fd, err := os.Create("valid-langs.js")
|
fd, err := os.Create("valid-langs.js")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ func reformatLanguageCode(origCode string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func saveValidLangs(langs []string) {
|
func saveValidLangs(langs []string) {
|
||||||
sort.Strings(langs)
|
slices.Sort(langs)
|
||||||
fd, err := os.Create("valid-langs.js")
|
fd, err := os.Create("valid-langs.js")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|||||||
Reference in New Issue
Block a user