mirror of
https://github.com/navidrome/navidrome.git
synced 2025-03-31 11:07:36 +03:00
build: add packages for deb and rpm to release (#3202)
* support packing deb/rpm/archlinux * .-. * initial test * fix postinstall, remove execstop * bash -> sh, create toml manually if it doesn't exist (thanks debian) * don't forget that newline * postrm * comments, contrib -> packaging/linux * contrib > packaging in .goreleaser * actually add toml * openrc/sysv templates * add apk. nothing else yet * wait, we have a ntive uninstall * fix: merge errors, move packaging to release * chore: remove old goreleaser conf * ci: remove `release` dependency on `docker push` * ci: fix release version * ci: upload packages * ci: try to fix json file list * ci: replace the json file list with a txt artifact * postremove -> preremove, skip install/remove error * actually do preremove * better preremove * ci: fix * ci: fix? * ci: clean-up * ci: try to change labels and filenames * ci: fix? * ci: fix? * ci: add `make package` target * ci: make labels more readable hope it doesn't break the pipeline again * build: remove alpine and archlinux packages, for now. --------- Co-authored-by: Deluan <deluan@navidrome.org>
This commit is contained in:
parent
69e2a6d620
commit
154e13f7c9
10 changed files with 235 additions and 10 deletions
63
.github/workflows/pipeline.yml
vendored
63
.github/workflows/pipeline.yml
vendored
|
@ -332,7 +332,6 @@ jobs:
|
|||
sudo GIT_TAG=$GIT_TAG release/wix/build_msi.sh ${GITHUB_WORKSPACE} amd64
|
||||
du -h binaries/msi/*.msi
|
||||
|
||||
|
||||
- name: Upload MSI files
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
@ -341,11 +340,16 @@ jobs:
|
|||
retention-days: 7
|
||||
|
||||
release:
|
||||
name: Release
|
||||
needs: [build, msi, push-manifest]
|
||||
name: Package/Release
|
||||
needs: [build, msi]
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
package_list: ${{ steps.set-package-list.outputs.package_list }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
|
@ -366,3 +370,56 @@ jobs:
|
|||
args: "release --clean -f release/goreleaser.yml ${{ env.RELEASE_FLAGS }}"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Remove build artifacts
|
||||
run: |
|
||||
ls -l ./dist
|
||||
rm ./dist/*.tar.gz ./dist/*.zip
|
||||
|
||||
- name: Upload all-packages artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: packages
|
||||
path: dist/navidrome_v*
|
||||
|
||||
- id: set-package-list
|
||||
name: Export list of generated packages
|
||||
run: |
|
||||
cd dist
|
||||
set +x
|
||||
ITEMS=$(ls navidrome_v* | sed 's/^navidrome_v[^_]*_linux_//' | jq -R -s -c 'split("\n")[:-1]')
|
||||
echo $ITEMS
|
||||
echo "package_list=${ITEMS}" >> $GITHUB_OUTPUT
|
||||
|
||||
upload-packages:
|
||||
name: Upload Linux PKG
|
||||
runs-on: ubuntu-latest
|
||||
needs: [release]
|
||||
strategy:
|
||||
matrix:
|
||||
item: ${{ fromJson(needs.release.outputs.package_list) }}
|
||||
steps:
|
||||
- name: Download all-packages artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: packages
|
||||
path: ./dist
|
||||
|
||||
- name: Upload all-packages artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: navidrome_linux_${{ matrix.item }}
|
||||
path: dist/navidrome_v*_linux_${{ matrix.item }}
|
||||
|
||||
# delete-artifacts:
|
||||
# name: Delete unused artifacts
|
||||
# runs-on: ubuntu-latest
|
||||
# needs: [upload-packages]
|
||||
# steps:
|
||||
# - name: Delete all-packages artifact
|
||||
# env:
|
||||
# GH_TOKEN: ${{ github.token }}
|
||||
# run: |
|
||||
# for artifact in $(gh api repos/${{ github.repository }}/actions/artifacts | jq -r '.artifacts[] | select(.name | startswith("packages")) | .id'); do
|
||||
# gh api --method DELETE repos/${{ github.repository }}/actions/artifacts/$artifact
|
||||
# done
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -11,6 +11,7 @@ wiki
|
|||
TODO.md
|
||||
var
|
||||
navidrome.toml
|
||||
!release/linux/navidrome.toml
|
||||
master.zip
|
||||
testDB
|
||||
cache/*
|
||||
|
|
5
Makefile
5
Makefile
|
@ -144,6 +144,11 @@ docker-msi: ##@Cross_Compilation Build MSI installer for Windows
|
|||
@du -h binaries/msi/*.msi
|
||||
.PHONY: docker-msi
|
||||
|
||||
package: docker-build ##@Cross_Compilation Create binaries and packages for ALL supported platforms
|
||||
@if [ -z `which goreleaser` ]; then echo "Please install goreleaser first: https://goreleaser.com/install/"; exit 1; fi
|
||||
goreleaser release -f release/goreleaser.yml --clean --skip=publish --snapshot
|
||||
.PHONY: package
|
||||
|
||||
get-music: ##@Development Download some free music from Navidrome's demo instance
|
||||
mkdir -p music
|
||||
( cd music; \
|
||||
|
|
57
cmd/svc.go
57
cmd/svc.go
|
@ -20,6 +20,9 @@ var (
|
|||
service.StatusStopped: "Stopped",
|
||||
service.StatusRunning: "Running",
|
||||
}
|
||||
|
||||
installUser string
|
||||
workingDirectory string
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -70,9 +73,11 @@ func (p *svcControl) Stop(service.Service) error {
|
|||
|
||||
var svcInstance = sync.OnceValue(func() service.Service {
|
||||
options := make(service.KeyValue)
|
||||
options["Restart"] = "on-success"
|
||||
options["Restart"] = "on-failure"
|
||||
options["SuccessExitStatus"] = "1 2 8 SIGKILL"
|
||||
options["UserService"] = false
|
||||
options["LogDirectory"] = conf.Server.DataFolder
|
||||
options["SystemdScript"] = systemdScript
|
||||
if conf.Server.LogFile != "" {
|
||||
options["LogOutput"] = false
|
||||
} else {
|
||||
|
@ -80,12 +85,13 @@ var svcInstance = sync.OnceValue(func() service.Service {
|
|||
options["LogDirectory"] = conf.Server.DataFolder
|
||||
}
|
||||
svcConfig := &service.Config{
|
||||
UserName: installUser,
|
||||
Name: "navidrome",
|
||||
DisplayName: "Navidrome",
|
||||
Description: "Your Personal Streaming Service",
|
||||
Dependencies: []string{
|
||||
"Requires=",
|
||||
"After="},
|
||||
"After=remote-fs.target network.target",
|
||||
},
|
||||
WorkingDirectory: executablePath(),
|
||||
Option: options,
|
||||
}
|
||||
|
@ -108,6 +114,10 @@ func runServiceCmd(cmd *cobra.Command, _ []string) {
|
|||
}
|
||||
|
||||
func executablePath() string {
|
||||
if workingDirectory != "" {
|
||||
return workingDirectory
|
||||
}
|
||||
|
||||
ex, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -141,11 +151,15 @@ func buildInstallCmd() *cobra.Command {
|
|||
println("Service installed. Use 'navidrome svc start' to start it.")
|
||||
}
|
||||
|
||||
return &cobra.Command{
|
||||
cmd := &cobra.Command{
|
||||
Use: "install",
|
||||
Short: "Install Navidrome service.",
|
||||
Run: runInstallCmd,
|
||||
}
|
||||
cmd.Flags().StringVarP(&installUser, "user", "u", "", "user to run service")
|
||||
cmd.Flags().StringVarP(&workingDirectory, "working-directory", "w", "", "working directory of service")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func buildUninstallCmd() *cobra.Command {
|
||||
|
@ -216,3 +230,38 @@ func buildExecuteCmd() *cobra.Command {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
const systemdScript = `[Unit]
|
||||
Description={{.Description}}
|
||||
ConditionFileIsExecutable={{.Path|cmdEscape}}
|
||||
{{range $i, $dep := .Dependencies}}
|
||||
{{$dep}} {{end}}
|
||||
|
||||
[Service]
|
||||
StartLimitInterval=5
|
||||
StartLimitBurst=10
|
||||
ExecStart={{.Path|cmdEscape}}{{range .Arguments}} {{.|cmd}}{{end}}
|
||||
{{if .WorkingDirectory}}WorkingDirectory={{.WorkingDirectory|cmdEscape}}{{end}}
|
||||
{{if .UserName}}User={{.UserName}}{{end}}
|
||||
{{if .Restart}}Restart={{.Restart}}{{end}}
|
||||
{{if .SuccessExitStatus}}SuccessExitStatus={{.SuccessExitStatus}}{{end}}
|
||||
TimeoutStopSec=20
|
||||
RestartSec=120
|
||||
EnvironmentFile=-/etc/sysconfig/{{.Name}}
|
||||
|
||||
DevicePolicy=closed
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
ProtectControlGroups=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelTunables=yes
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
RestrictNamespaces=yes
|
||||
RestrictRealtime=yes
|
||||
SystemCallFilter=~@clock @debug @module @mount @obsolete @reboot @setuid @swap
|
||||
{{if .WorkingDirectory}}ReadWritePaths={{.WorkingDirectory|cmdEscape}}{{end}}
|
||||
ProtectSystem=full
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
`
|
||||
|
|
|
@ -11,15 +11,13 @@ WantedBy=multi-user.target
|
|||
User=navidrome
|
||||
Group=navidrome
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/navidrome
|
||||
ExecStart=/usr/bin/navidrome --configfile "/etc/navidrome/navidrome.toml"
|
||||
StateDirectory=navidrome
|
||||
WorkingDirectory=/var/lib/navidrome
|
||||
TimeoutStopSec=20
|
||||
KillMode=process
|
||||
Restart=on-failure
|
||||
|
||||
EnvironmentFile=-/etc/sysconfig/navidrome
|
||||
|
||||
# See https://www.freedesktop.org/software/systemd/man/systemd.exec.html
|
||||
CapabilityBoundingSet=
|
||||
DevicePolicy=closed
|
||||
|
|
|
@ -33,6 +33,58 @@ checksum:
|
|||
snapshot:
|
||||
version_template: "{{ .Tag }}-SNAPSHOT"
|
||||
|
||||
nfpms:
|
||||
- id: navidrome
|
||||
package_name: navidrome
|
||||
|
||||
homepage: https://navidrome.org
|
||||
description: |-
|
||||
🎧☁ Your Personal Streaming Service
|
||||
|
||||
maintainer: Deluan Quintão <deluan at navidrome.org>
|
||||
|
||||
license: GPL-3.0
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
|
||||
dependencies:
|
||||
- ffmpeg
|
||||
|
||||
suggests:
|
||||
- mpv
|
||||
|
||||
overrides:
|
||||
rpm:
|
||||
dependencies:
|
||||
- "(ffmpeg or ffmpeg-free)"
|
||||
|
||||
contents:
|
||||
- src: release/linux/navidrome.toml
|
||||
dst: /etc/navidrome/navidrome.toml
|
||||
type: "config|noreplace"
|
||||
file_info:
|
||||
mode: 0644
|
||||
owner: navidrome
|
||||
group: navidrome
|
||||
|
||||
- dst: /var/lib/navidrome
|
||||
type: dir
|
||||
file_info:
|
||||
owner: navidrome
|
||||
group: navidrome
|
||||
|
||||
- dst: /opt/navidrome/music
|
||||
type: dir
|
||||
file_info:
|
||||
owner: navidrome
|
||||
group: navidrome
|
||||
|
||||
scripts:
|
||||
preinstall: "release/linux/preinstall.sh"
|
||||
postinstall: "release/linux/postinstall.sh"
|
||||
preremove: "release/linux/preremove.sh"
|
||||
|
||||
release:
|
||||
draft: true
|
||||
mode: append
|
||||
|
|
2
release/linux/navidrome.toml
Normal file
2
release/linux/navidrome.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
DataFolder = "/var/lib/navidrome"
|
||||
MusicFolder = "/opt/navidrome/music"
|
25
release/linux/postinstall.sh
Normal file
25
release/linux/postinstall.sh
Normal file
|
@ -0,0 +1,25 @@
|
|||
#!/bin/sh
|
||||
|
||||
# It is possible for a user to delete the configuration file in such a way that
|
||||
# the package manager (in particular, deb) thinks that the file exists, while it is
|
||||
# no longer on disk. Specifically, doing a `rm /etc/navidrome/navidrome.toml`
|
||||
# without something like `apt purge navidrome` will result in the system believing that
|
||||
# the file still exists. In this case, during isntall it will NOT extract the configuration
|
||||
# file (as to not override it). Since `navidrome service install` depends on this file existing,
|
||||
# we will create it with the defaults anyway.
|
||||
if [ ! -f /etc/navidrome/navidrome.toml ]; then
|
||||
printf "No navidrome.toml detected, creating in postinstall\n"
|
||||
printf "DataFolder = \"/var/lib/navidrome\"\n" > /etc/navidrome/navidrome.toml
|
||||
printf "MusicFolder = \"/opt/navidrome/music\"\n" >> /etc/navidrome/navidrome.toml
|
||||
fi
|
||||
|
||||
postinstall_flag="/var/lib/navidrome/.installed"
|
||||
|
||||
if [ ! -f "$postinstall_flag" ]; then
|
||||
# The primary reason why this would fail is if the service was already installed AND
|
||||
# someone manually removed the .installed flag. In this case, ignore the error
|
||||
navidrome service install --user navidrome --working-directory /var/lib/navidrome --configfile /etc/navidrome/navidrome.toml || :
|
||||
touch "$postinstall_flag"
|
||||
fi
|
||||
|
||||
|
6
release/linux/preinstall.sh
Executable file
6
release/linux/preinstall.sh
Executable file
|
@ -0,0 +1,6 @@
|
|||
#!/bin/sh
|
||||
|
||||
if ! getent passwd navidrome > /dev/null 2>&1; then
|
||||
printf "Creating default Navidrome user\n"
|
||||
useradd --home-dir /var/lib/navidrome --create-home --system --user-group navidrome
|
||||
fi
|
30
release/linux/preremove.sh
Normal file
30
release/linux/preremove.sh
Normal file
|
@ -0,0 +1,30 @@
|
|||
#!/bin/sh
|
||||
|
||||
action=$1
|
||||
|
||||
remove() {
|
||||
postinstall_flag="/var/lib/navidrome/.installed"
|
||||
|
||||
if [ -f "$postinstall_flag" ]; then
|
||||
# If this fails, ignore it
|
||||
navidrome service uninstall || :
|
||||
rm "$postinstall_flag"
|
||||
|
||||
printf "The following may still be present (especially if you have not done a purge):\n"
|
||||
printf "1. /etc/navidrome/navidrome.toml (configuration file)\n"
|
||||
printf "2. /var/lib/navidrome (database/cache)\n"
|
||||
printf "3. /opt/navidrome (default location for music)\n"
|
||||
printf "4. The Navidrome user (user name navidrome)\n"
|
||||
fi
|
||||
}
|
||||
|
||||
case "$action" in
|
||||
"1" | "upgrade")
|
||||
# For an upgrade, do nothing
|
||||
# Leave the service file untouched
|
||||
# This is relevant for RPM/DEB-based installs
|
||||
;;
|
||||
*)
|
||||
remove
|
||||
;;
|
||||
esac
|
Loading…
Add table
Add a link
Reference in a new issue