Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 55 additions & 4 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Contributing

## Implementations

There are two parallel implementations that must be kept in feature parity:

| File | Platform | Language |
| ------------------- | -------- | ---------- |
| `src/backdrop.sh` | Linux | Bash |
| `src/backdrop.psm1` | Windows | PowerShell |

Changes to commands, sources, config keys, or behaviour should be applied to both files.

## Setup

After cloning, install the Node dev dependencies:
Expand All @@ -10,10 +21,10 @@ npm install

This registers the git hooks via `simple-git-hooks`:

- **pre-commit** - runs Prettier, shfmt, ShellCheck, and the BATS test suite
- **pre-commit** - runs Prettier, shfmt, ShellCheck, and BATS against the bash implementation, and PSScriptAnalyzer and Pester against the PowerShell implementation
- **prepare-commit-msg** - launches an interactive conventional commit prompt (`czg`)

You also need [**shellcheck**](https://github.com/koalaman/shellcheck) and [**shfmt**](https://github.com/mvdan/sh) installed locally for the hooks and `npm run lint` / `npm test` to work:
You also need [**shellcheck**](https://github.com/koalaman/shellcheck) and [**shfmt**](https://github.com/mvdan/sh) installed locally for `npm run lint:sh` / `npm run test:sh` to work:

```bash
# Ubuntu/Debian
Expand All @@ -23,6 +34,14 @@ sudo apt install shellcheck shfmt
brew install shellcheck shfmt
```

For the PowerShell side, you need [**PowerShell 7+**](https://github.com/PowerShell/PowerShell) (`pwsh`) installed. Then install PSScriptAnalyzer and Pester:

```bash
npm run install:ps
```

This is required for `npm run lint:ps` / `npm run test:ps` to work. Since the pre-commit hook runs `npm run lint` and `npm run test` (which cover both implementations), it won't pass without these installed.

## Commits

Commits must follow the [Conventional Commits](https://www.conventionalcommits.org/) spec. The pre-commit hook will guide you through it. You can also run the prompt manually:
Expand All @@ -33,17 +52,35 @@ npm run commit

## Testing

To run the full test suite (ShellCheck + BATS):
To run the full test suite (ShellCheck + BATS, plus Pester for PowerShell):

```bash
npm test
```

To run just one implementation's tests:

```bash
npm run test:sh
```

or

```bash
npm run test:ps
```

Tests live in `test/backdrop.bats` and cover the pure and file-I/O functions in `src/backdrop.sh`; things like config read/write, source validation, image dimension detection, wallpaper option selection, metadata read/write, version comparison, and source rotation. Source resolver functions are tested using a stub `curl` script injected via `PATH`. The `_rotation_index` helper takes a unix timestamp as a parameter rather than calling `date`, so rotation tests run without needing to stub `date`; `get_active_source` integration tests stub `date` via `PATH` in the same way as `curl`.

The PowerShell module (`src/backdrop.psm1`) is tested with [Pester 5](https://pester.dev/). Tests live in `test/backdrop.tests.ps1` and run via `Invoke-Pester test/backdrop.tests.ps1`. The `Get-UnixTimestamp` helper is extracted so rotation tests can mock it without stubbing `[DateTimeOffset]::UtcNow` directly.

## Image metadata

Each `resolve_<source>()` function emits `META_TITLE:`, `META_DESC:`, and `META_URL:` prefixed lines before the image URLs it returns. `apply_wallpaper` strips these out to get the candidate URL list, then stores the values in the `META_TITLE`/`META_DESC`/`META_URL` globals. After a successful download, `_write_meta` saves those values to a `<source>-<date>.meta` file alongside the `.jpg`. The `status` command reads this file via `_meta_get` to display image title, description, and URL. Old `.meta` files are pruned on the same 14-day schedule as wallpaper images.
**Bash (`backdrop.sh`):** each `resolve_<source>()` function emits `META_TITLE:`, `META_DESC:`, and `META_URL:` prefixed lines before the image URLs it returns. `apply_wallpaper` strips these out to get the candidate URL list, then stores the values in the `META_TITLE`/`META_DESC`/`META_URL` globals.

**PowerShell (`backdrop.psm1`):** each `Resolve-<Source>` function returns a hashtable with `Title`, `Desc`, `Url`, and `ImageUrls` keys directly, avoiding the stdout multiplexing used by the bash version.

In both implementations, after a successful download the metadata is saved to a `<source>-<date>.meta` file (key = value format) alongside the `.jpg`. The `status` command reads this file to display image title, description, and URL. Old `.meta` files are pruned on the same 14-day schedule as wallpaper images.

## Linting

Expand All @@ -53,8 +90,22 @@ To check formatting:
npm run lint
```

To check just one implementation:

```bash
npm run lint:sh
```

or

```bash
npm run lint:ps
```

To auto-fix formatting:

```bash
npm run lint:fix
```

`lint:ps:fix` uses `Invoke-ScriptAnalyzer -Fix`, which only corrects rules that support automatic fixes (mostly formatting/whitespace). Violations like `PSAvoidUsingCmdletAliases` still need to be fixed by hand; re-run `npm run lint:ps` afterward to see what's left.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
matrix:
node-version: [22]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
token: ${{ secrets.GHA_SEMANTIC_RELEASE_TOKEN }}
- name: Use Node.js ${{ matrix.node-version }}
Expand Down
21 changes: 18 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
name: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- uses: actions/setup-node@v6
with:
Expand All @@ -25,12 +25,27 @@ jobs:
run: go install mvdan.cc/sh/v3/cmd/shfmt@latest

- name: Lint
run: npm run lint
run: npm run lint:sh

- name: Install ShellCheck v0.11.0
run: |
curl -sSL "https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.linux.x86_64.tar.xz" | tar -xJ
sudo mv shellcheck-v0.11.0/shellcheck /usr/local/bin/shellcheck

- name: Test
run: npm test
run: npm run test:sh

test-ps:
name: test (powershell)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- name: Install PSScriptAnalyzer and Pester
run: npm run install:ps

- name: Lint
run: npm run lint:ps

- name: Test
run: npm run test:ps
85 changes: 60 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,40 @@ Set a new desktop wallpaper every day from various sources.

backdrop fetches a daily image from one of several curated sources and sets it as your desktop wallpaper. It automatically picks the best display mode based on the image dimensions and your screen aspect ratio.

Run it manually or let a systemd timer handle it on a schedule.
Run it manually or let a background timer handle it on a schedule.

## Quick Install

### Linux

```bash
curl -fsSL https://ensl.ee/backdrop | bash
```

### Windows

```powershell
iex (iwr 'https://ensl.ee/backdrop-w' -UseBasicParsing).Content
```

## Requirements

- A supported Linux desktop environment (see below)
### Linux

- A supported desktop environment (see [Desktop Environments](#desktop-environments) below)
- `curl`, `python3` (standard on most distros)
- systemd (for the daily timer)

### Windows

- Windows 10 or later
- PowerShell 5.1 or later (built-in on Windows 10+)

## Desktop Environments

backdrop supports the following Desktop Environments.
**Windows** uses `SystemParametersInfo` (user32.dll) to set the wallpaper, with fill mode stored in the registry under `HKCU:\Control Panel\Desktop`. No additional tools required.

**Linux** supports the following desktop environments:

| Desktop | Method | Notes |
| --------------------- | ----------------------------- | ------------------------------------------------------------------------------- |
Expand All @@ -46,7 +63,9 @@ backdrop supports the following Desktop Environments.

## Installation

Use the [quick install script](#quick-install) or clone the repo and run the installer locally:
Use the [quick install script](#quick-install) or clone the repo and run the installer locally.

### Linux

```bash
git clone https://github.com/aensley/backdrop.git \
Expand All @@ -59,35 +78,44 @@ The installer:
2. Copies `backdrop` to `/usr/local/bin/`
3. Runs `backdrop enable`, which downloads and installs the systemd unit files and starts the daily timer

### Additional users
**Additional users:** with `backdrop` already installed system-wide, additional users can enable it for their own login with `backdrop enable`. This downloads the matching systemd unit files from GitHub and installs them into `~/.config/systemd/user/`.

With `backdrop` already installed, additional users can enable it for their login with:
### Windows

```bash
backdrop enable
Clone the repo and run the installer from PowerShell:

```powershell
git clone https://github.com/aensley/backdrop.git
cd backdrop/src
.\install.ps1
```

This will download the matching systemd unit files from the matching GitHub release and install them into their own `~/.config/systemd/user/` directory.
The installer:

1. Copies `backdrop.psm1` and `backdrop.psd1` to the per-user PowerShell modules directory
2. Runs `backdrop enable`, which registers the scheduled task and applies the wallpaper immediately

The module is auto-imported in every new PowerShell session; no `$PROFILE` changes needed.

## Usage

```
backdrop <command>
```

| Command | Description |
| ------------------------------- | ---------------------------------------------------------------------------- |
| `status` | Show version, active source, last image, and image metadata (default) |
| `update [--force]` | Refresh wallpaper from the active source |
| `set <source...> [--force]` | Switch active source(s) and refresh now; use `all` for all sources |
| `set-time <HH:MM>` | Set the daily run time (24-hour); restarts timer if active |
| `set-rotate-interval <minutes>` | Set rotation interval in minutes; 0 to disable |
| `random [--force]` | Refresh from a randomly chosen source (does not change active) |
| `enable` | Enable the systemd --user timer; downloads unit files from GitHub if missing |
| `disable` | Disable the systemd --user timer |
| `upgrade` | Check for and install the latest version from GitHub |
| `uninstall` | Remove backdrop from this system |
| `help` | Show help |
| Command | Description |
| ------------------------------- | ---------------------------------------------------------------------------------- |
| `status` | Show version, active source, last image, and image metadata (default) |
| `update [--force]` | Refresh wallpaper from the active source |
| `set <source...> [--force]` | Switch active source(s) and refresh now; use `all` for all sources |
| `set-time <HH:MM>` | Set the daily run time (24-hour); restarts timer if active |
| `set-rotate-interval <minutes>` | Set rotation interval in minutes; 0 to disable |
| `random [--force]` | Refresh from a randomly chosen source (does not change active) |
| `enable` | Enable the background timer (Linux: systemd --user timer; Windows: Task Scheduler) |
| `disable` | Disable the background timer |
| `upgrade` | Check for and install the latest version from GitHub |
| `uninstall` | Remove backdrop from this system |
| `help` | Show help |

## Sources

Expand Down Expand Up @@ -147,7 +175,12 @@ When rotation is active, the systemd timer fires at the rotation interval instea

## Configuration

The config file lives at `~/.config/backdrop/config` and is created on first run. You can edit it directly or use the `set` / `set-time` commands.
The config file is created on first run. You can edit it directly or use the `set` / `set-time` commands.

| Platform | Config file | Cached images |
| -------- | --------------------------- | -------------------------- |
| Linux | `~/.config/backdrop/config` | `~/.local/share/backdrop/` |
| Windows | `%APPDATA%\backdrop\config` | `%LOCALAPPDATA%\backdrop\` |

| Key | Default | Description |
| --------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------- |
Expand All @@ -160,12 +193,14 @@ The config file lives at `~/.config/backdrop/config` and is created on first run

## Uninstallation

```bash
```
backdrop uninstall
```

To also remove your config and cached wallpapers:

```bash
```
backdrop uninstall --purge
```

On Linux this removes the script from `/usr/local/bin` and disables the systemd timer. On Windows it unregisters the scheduled task and removes the PowerShell module.
Loading