Skip to content

Commit 4f03427

Browse files
committed
chore: fix resticinstaller for windows tests
1 parent 74eb869 commit 4f03427

File tree

4 files changed

+69
-58
lines changed

4 files changed

+69
-58
lines changed

Dockerfile.alpine

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ RUN apk --no-cache add tini ca-certificates curl bash rclone openssh tzdata dock
44
RUN mkdir -p /tmp
55
COPY backrest /backrest
66
RUN /backrest --install-deps-only
7-
RUN mkdir -p /bin && mv /root/.local/share/backrest/* /bin
7+
RUN mkdir -p /bin && mv /root/.local/share/backrest/restic /bin/restic
88

99
ENTRYPOINT ["/sbin/tini", "--"]
1010
CMD ["/backrest", "--bind-address", ":9898"]

Dockerfile.scratch

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ RUN apk add --no-cache ca-certificates tini-static
33
RUN mkdir /tmp-orig
44
COPY backrest /backrest
55
RUN /backrest --install-deps-only
6-
RUN mkdir -p /bin && mv /root/.local/share/backrest/* /bin
6+
RUN mkdir -p /bin && mv /root/.local/share/backrest/restic /bin/restic
77

88
FROM scratch
99
LABEL org.opencontainers.image.source="https://github.com/garethgeorge/backrest"

README.md

+10-15
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ Download options
6262
Backrest is accessible from a web browser. By default it binds to `127.0.0.1:9898` and can be accessed at `http://localhost:9898`. Change the port with the `BACKREST_PORT` environment variable e.g. `BACKREST_PORT=0.0.0.0:9898 backrest` to listen on all network interfaces. On first startup backrest will prompt you to create a default username and password, this can be changed later in the settings page.
6363

6464
> [!Note]
65-
> Backrest installs a specific restic version to ensure that it is compatible. If you wish to use a different version of restic OR if you would prefer to install restic manually, use the `BACKREST_RESTIC_COMMAND` environment variable to specify the path of your restic install.
65+
> Backrest will use your system install of restic if it is available and matches Backrest's required version. Otherwise it will download and install a compatible version of restic in its data directory. Backrest will keep restic up to date with the latest version. You force use of a specific restic binary (or non-standard version) by setting the `BACKREST_RESTIC_COMMAND` environment variable to the path of your restic binary.
6666
6767
## Running with Docker Compose
6868

@@ -142,7 +142,7 @@ Create a systemd service file at `/etc/systemd/system/backrest.service` with the
142142

143143
```ini
144144
[Unit]
145-
Description=ResticWeb
145+
Description=Backrest
146146
After=network.target
147147

148148
[Service]
@@ -189,15 +189,14 @@ Backrest is provided as a [homebrew tap](https://github.com/garethgeorge/homebre
189189
brew tap garethgeorge/homebrew-backrest-tap
190190
brew install backrest
191191
brew services start backrest
192+
# optionally, install restic
193+
brew install restic
192194
```
193195

194196
This tap uses [Brew services](https://github.com/Homebrew/homebrew-services) to launch and manage Backrest's lifecycle. Backrest will launch on startup and run on port ':9898` by default.
195197

196198
> [!NOTE]
197-
> You may need to grant Full Disk Access to your restic install. To do this, go to `System Preferences > Security & Privacy > Privacy > Full Disk Access` and add the path to your restic install which is typically ~/.local/share/backrest/restic .
198-
199-
> [!NOTE]
200-
> You may optionally install `restic` through homebrew as well, but you may need to regrant Full Disk Access to the homebrew managed binary on each update. You should ensure that you update backrest and restic together if using homebrew to manage both dependencies.
199+
> You may need to grant Full Disk Access to backrest. To do this, go to `System Preferences > Security & Privacy > Privacy > Full Disk Access` and add the path to backrest (typically /usr/local/bin/backrest).
201200
202201
#### Manually using the install script
203202

@@ -220,19 +219,15 @@ The install script will:
220219

221220
Read the script before running it to make sure you are comfortable with these operations.
222221

223-
#### Manually
224-
225-
If setting up Backrest manually, it is recommended to install the binary to `/usr/local/bin` and run it manually. You can also create a launch agent to run it on startup or may run it manually when needed.
226-
227222
## Running on Windows
228223

229-
Download a Windows release from the [releases page](https://github.com/garethgeorge/backrest/releases) and install it to `C:\Program Files\Backrest\backrest.exe` (create the path if it does not exist). The binary should be run as administrator on first launch, otherwise the restic installation will fail and the process will terminate.
224+
#### Windows Installer
225+
226+
Download a the Windows installer for your architecture from the [releases page](https://github.com/garethgeorge/backrest/releases). The installer is named Backrest-setup-[arch].exe. Run the installer and follow the prompts.
230227

231-
To run the binary on login, create a shortcut to the binary and place it in the `shell:startup` folder. See [this windows support article](https://support.microsoft.com/en-us/windows/add-an-app-to-run-automatically-at-startup-in-windows-10-150da165-dcd9-7230-517b-cf3c295d89dd) for more details.
228+
The installer will place backrest and a GUI tray application to monitor backrest in `%localappdata%\Programs\Backrest\`. The GUI tray application will start on login by default.
232229

233-
> [!WARNING]
234-
> * If you receive filesystem errors, you may need to run Backrest as an administrator for full filesystem access.
235-
> * Backrest is **not** tested on Windows to the same extent as Linux and macOS. Some features may not work as expected.
230+
> [!NOTE] You can optionally override the default port of the installation by using PowerShell to run the installer with the `BACKREST_PORT` environment variable set to the desired port. E.g. to run backrest on port 8080, run the following command in PowerShell: `BACKREST_PORT=:8080 .\Backrest-setup-x86_64.exe`
236231
237232

238233
# Configuration

internal/resticinstaller/resticinstaller.go

+57-41
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ var (
2424
var (
2525
RequiredResticVersion = "0.17.3"
2626

27-
findResticMu sync.Mutex
28-
didTryInstall bool
27+
tryFindRestic sync.Once
28+
findResticErr error
29+
foundResticPath string
2930
)
3031

3132
func getResticVersion(binary string) (string, error) {
@@ -94,12 +95,6 @@ func verify(sha256 string) error {
9495
}
9596

9697
func installResticIfNotExists(resticInstallPath string) error {
97-
lock := flock.New(filepath.Join(filepath.Dir(resticInstallPath), "install.lock"))
98-
if err := lock.Lock(); err != nil {
99-
return fmt.Errorf("lock %v: %w", lock.Path(), err)
100-
}
101-
defer lock.Unlock()
102-
10398
if _, err := os.Stat(resticInstallPath); err == nil {
10499
// file is now installed, probably by another process. We can return.
105100
return nil
@@ -148,25 +143,40 @@ func removeOldVersions(installDir string) {
148143
}
149144
}
150145

151-
// FindOrInstallResticBinary first tries to find the restic binary if provided as an environment variable. Otherwise it downloads restic if not already installed.
152-
func FindOrInstallResticBinary() (string, error) {
153-
findResticMu.Lock()
154-
defer findResticMu.Unlock()
146+
func installResticHelper(resticInstallPath string) {
147+
if _, err := os.Stat(resticInstallPath); err == nil {
148+
zap.S().Infof("replacing restic binary in data dir due to failed check: %w", err)
149+
if err := os.Remove(resticInstallPath); err != nil {
150+
zap.S().Errorf("failed to remove old restic binary %v: %v", resticInstallPath, err)
151+
}
152+
}
153+
154+
zap.S().Infof("downloading restic %v to %v...", RequiredResticVersion, resticInstallPath)
155+
if err := installResticIfNotExists(resticInstallPath); err != nil {
156+
zap.S().Errorf("failed to install restic %v: %v", RequiredResticVersion, err)
157+
return
158+
}
159+
zap.S().Infof("installed restic %v", RequiredResticVersion)
160+
161+
// TODO: this check is no longer needed, remove it after a few releases.
162+
removeOldVersions(path.Dir(resticInstallPath))
163+
}
155164

165+
func tryFindOrInstall() (string, error) {
156166
// Check if restic is provided.
157-
resticBin := env.ResticBinPath()
158-
if resticBin != "" {
159-
if err := assertResticVersion(resticBin); err != nil {
160-
zap.S().Warnf("restic binary %q may not be supported by backrest", resticBin, err)
167+
resticBinOverride := env.ResticBinPath()
168+
if resticBinOverride != "" {
169+
if err := assertResticVersion(resticBinOverride); err != nil {
170+
zap.S().Warnf("restic binary %q may not be supported by backrest: %v", resticBinOverride, err)
161171
}
162172

163-
if _, err := os.Stat(resticBin); err != nil {
173+
if _, err := os.Stat(resticBinOverride); err != nil {
164174
if !errors.Is(err, os.ErrNotExist) {
165-
return "", fmt.Errorf("stat(%v): %w", resticBin, err)
175+
return "", fmt.Errorf("check if restic binary exists at %v: %v", resticBinOverride, err)
166176
}
167-
return "", fmt.Errorf("no binary found at path %v: %w", resticBin, ErrResticNotFound)
177+
return "", fmt.Errorf("no restic binary found at %v", resticBinOverride)
168178
}
169-
return resticBin, nil
179+
return resticBinOverride, nil
170180
}
171181

172182
// Search the PATH for the specific restic version.
@@ -180,40 +190,46 @@ func FindOrInstallResticBinary() (string, error) {
180190
}
181191

182192
// Check for restic installation in data directory.
183-
resticInstallPath := path.Join(env.DataDir(), "restic")
193+
var resticInstallPath string
184194
if runtime.GOOS == "windows" {
185195
// on windows use a path relative to the executable.
186-
resticInstallPath, _ = filepath.Abs(path.Join(path.Dir(os.Args[0]), "restic"))
196+
resticInstallPath, _ = filepath.Abs(path.Join(path.Dir(os.Args[0]), "restic.exe"))
197+
} else {
198+
resticInstallPath = filepath.Join(env.DataDir(), "restic")
187199
}
188-
189-
if err := os.MkdirAll(path.Dir(resticInstallPath), 0700); err != nil {
200+
if err := os.MkdirAll(filepath.Dir(resticInstallPath), 0700); err != nil {
190201
return "", fmt.Errorf("create restic install directory %v: %w", path.Dir(resticInstallPath), err)
191202
}
192203

193204
// Install restic if not found OR if the version is not the required version
194205
if err := assertResticVersion(resticInstallPath); err != nil {
195-
if _, err := os.Stat(resticInstallPath); err == nil {
196-
zap.S().Infof("reinstalling restic binary in data dir due to failed checks: %w", err)
197-
if err := os.Remove(resticInstallPath); err != nil {
198-
return "", fmt.Errorf("remove old restic binary %v: %w", resticInstallPath, err)
199-
}
200-
}
201-
202-
if didTryInstall {
203-
return "", fmt.Errorf("already tried to install: %w", ErrResticNotFound)
206+
lock := flock.New(filepath.Join(filepath.Dir(resticInstallPath), "install.lock"))
207+
if err := lock.Lock(); err != nil {
208+
return "", fmt.Errorf("acquire lock on restic install dir %v: %v", lock.Path(), err)
204209
}
205-
didTryInstall = true
210+
defer lock.Unlock()
206211

207-
zap.S().Infof("downloading restic %v to %v...", RequiredResticVersion, resticInstallPath)
208-
if err := installResticIfNotExists(resticInstallPath); err != nil {
209-
return "", fmt.Errorf("install restic: %w", err)
212+
// Check again after acquiring the lock.
213+
if err := assertResticVersion(resticInstallPath); err != nil {
214+
installResticHelper(resticInstallPath)
210215
}
211-
zap.S().Infof("installed restic %v", RequiredResticVersion)
212-
213-
// TODO: this check is no longer needed, remove it after a few releases.
214-
removeOldVersions(path.Dir(resticInstallPath))
215216
}
216217

217218
zap.S().Infof("restic binary %v in data dir will be used as no system install matching required version %v is found", resticInstallPath, RequiredResticVersion)
218219
return resticInstallPath, nil
219220
}
221+
222+
// FindOrInstallResticBinary first tries to find the restic binary if provided as an environment variable. Otherwise it downloads restic if not already installed.
223+
func FindOrInstallResticBinary() (string, error) {
224+
tryFindRestic.Do(func() {
225+
foundResticPath, findResticErr = tryFindOrInstall()
226+
})
227+
228+
if findResticErr != nil {
229+
return "", findResticErr
230+
}
231+
if foundResticPath == "" {
232+
return "", ErrResticNotFound
233+
}
234+
return foundResticPath, nil
235+
}

0 commit comments

Comments
 (0)