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
34 changes: 29 additions & 5 deletions .github/workflows/ca-template-changes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ on:
release:
types: [published]

# Commits the updated template back to the default branch. The repository
# default is read, so the write is granted here and nowhere else.
# Opens a pull request against the default branch. The repository default is
# read, so the writes are granted here and nowhere else.
permissions:
contents: write
pull-requests: write

jobs:
update:
runs-on: ubuntu-latest
steps:
# Commit onto the default branch, not the tag the release points at: a
# Branch from the default branch, not the tag the release points at: a
# tag checkout is detached and the push would go nowhere.
- name: Checkout default branch
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
Expand All @@ -38,17 +39,40 @@ jobs:

# The checkout keeps no credentials, so the token authenticates this one
# push and is never left in the runner's git config.
- name: Commit
#
# A pull request, never a direct push: the default branch requires one.
# Checks do not start on a pull request opened with GITHUB_TOKEN. Merging
# this one needs the ruleset bypass, or a re-run of the checks.
- name: Open a pull request
if: steps.changes.outputs.changed == 'true'
env:
VERSION: ${{ github.event.release.tag_name }}
BRANCH: ${{ github.event.repository.default_branch }}
BASE: ${{ github.event.repository.default_branch }}
REPO: ${{ github.repository }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
BRANCH="docs/ca-template-${VERSION}"
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git checkout -b "${BRANCH}"
git add templates/stackyard.xml
git commit -m "Docs: describe ${VERSION} in the Community Applications template"
git push "https://x-access-token:${GH_TOKEN}@github.com/${REPO}.git" "HEAD:${BRANCH}"
gh pr create \
--base "${BASE}" \
--head "${BRANCH}" \
--title "Docs: describe ${VERSION} in the Community Applications template" \
--label documentation \
--body "$(printf '%s\n' \
'**Summary**' \
"- Copy the ${VERSION} changelog section into the Community Applications template." \
'' \
'**Testing**' \
'The template was validated as well-formed before this branch was pushed.' \
'' \
'**Checklist**' \
'- [x] Stays within the project constraints: one container, no runtime dependencies, no build step (see CONTRIBUTING.md).' \
'- [ ] Tests added or updated for new logic, and the full suite, lint, and typechecks pass.' \
'- [x] Docs updated if behavior or configuration changed.' \
'- [ ] CHANGELOG.md updated under Unreleased if the change is user-visible.')"
13 changes: 13 additions & 0 deletions api/test/ca-template.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@ test('both documents use the repository path with its real capitalisation', () =
}
});

/* The default branch requires a pull request. A workflow that pushes the
template straight at it is rejected by the ruleset and the release notes
never reach the listing. */
test('the template update opens a pull request instead of pushing to the default branch', () => {
const src = fs.readFileSync(path.join(ROOT, '.github', 'workflows', 'ca-template-changes.yml'), 'utf8');
const commands = src
.split('\n')
.filter(line => !/^\s*#/.test(line))
.join('\n');
assert.match(commands, /gh pr create/, 'the update no longer opens a pull request');
assert.doesNotMatch(commands, /git push[^\n]*HEAD:\$\{BASE\}/, 'a direct push to the default branch is rejected');
});

/* The runner image has no xmllint, so a workflow that reaches for it fails at
step one. Both validate through the committed script instead. */
test('the workflows validate with the committed script, not xmllint', () => {
Expand Down
8 changes: 4 additions & 4 deletions api/test/workflow-hardening.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ test('no workflow grants a write scope it does not need', () => {
lets the job mint a short-lived OIDC token, which is what cosign exchanges
for a Sigstore certificate when it signs the released image. Keyless
signing needs it, and it is the reason there is no private key to hold. */
/* The CA template's release notes are committed back to the default branch,
which is the only thing that write scope covers. The checkout still keeps
no credentials: the push authenticates on its own. */
/* The CA template's release notes go to a branch and a pull request, which is
all those two scopes cover. The checkout still keeps no credentials: the
push authenticates on its own. */
const allowed = {
'workflows/release.yml': ['packages: write', 'id-token: write'],
'workflows/codeql.yml': ['security-events: write'],
'workflows/ca-template-changes.yml': ['contents: write'],
'workflows/ca-template-changes.yml': ['contents: write', 'pull-requests: write'],
};
const bad = [];
for (const [f, src] of workflows) {
Expand Down
115 changes: 92 additions & 23 deletions templates/stackyard.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,42 +19,111 @@
<DonateText>If Stackyard is useful to you, you can buy me a coffee.</DonateText>
<DonateLink>https://buymeacoffee.com/sandobserver</DonateLink>
<Changes>
### 1.6.1 - 2026-08-14
### 1.7.0 - 2026-08-16

### Added

- Light theme for the Settings pages, with Settings Display Mode in Settings &gt;
Appearance: System Default, Light or Dark. Stored per device.

- Keep Screen Awake in Settings &gt; Appearance, off by default. Needs HTTPS.

- Show From on a live activity badge, to badge only counts at or above a number.

- Install from Unraid Community Applications, with a template in `templates/`.

### Changed

- Text on both pages now follows one type scale, so some sizes and line spacing
shift slightly.

- Widget colours now match the rest of the app. Reds, blues, greens and greys
that had been left on older values are corrected.

- Settings controls are redrawn: switches are wider with a capsule knob, slider
tracks are thicker, and groups are more rounded.

- The wallpaper brightness slider now has the same handle as the colour sliders.

- Each colour slider handle shows the value it is set to.

- Group headings and their help text now line up with the settings above and
below them.

- The settings sidebar is wider, with taller items and a rounded selection.

- The selected tab on a phone is marked with a pill instead of a coloured
label.

- Widget tiles and dock icons are rounded correctly, and stay so at every icon
and dashboard size.

- Either-or settings are now a segmented control instead of radio dots.

- Buttons in a dialog are taller and rounded.

- A long settings label now keeps its line and the value beside it shortens,
instead of the label breaking in two.

- Unsplash API Key is now just API Key, under a Source row that already names
Unsplash.

- Row separators now start level with the row's label instead of spanning the
full width.

- Settings on a phone use the larger phone text size. On a desktop the panel
keeps its denser size.

- Widgets on a page you are not looking at now poll four times less often, and
refresh when you swipe back to them.

- Badge and health targets that keep failing are contacted less often, so an
offline service no longer slows every other tile on every refresh.

- Scripts and stylesheets are now cached for a year instead of revalidated on
every load. Their URLs change whenever the file does.

- The logo is redrawn, and its arc now follows the accent colour.

### Fixed

- Phone home screen no longer starts under the status bar in a home-screen
install. The first row clears the clock and the island.
- Home screen no longer leaves a dead strip below the dock in a home-screen
install on iOS.

- App icons now use the current logo, and the browser tab icon follows the
light or dark theme.

- Colour and brightness slider tracks draw round ends on a touch device instead
of tapering to a point.

- Settings reserves the status bar area on a phone.
- Hue, saturation and brightness sliders no longer stretch across the row on a
phone.

- Notification badges inside a folder no longer get trimmed at the page edge or
bleed into the next page.
- Slider fills and colour ramps now run the same way as the handle in
right-to-left languages.

- Opening a folder no longer puts a focus ring on the first app, on a phone or
on the desktop.
- Saturation, brightness and wallpaper brightness sliders now draw a thin track
on a phone, matching hue.

- A long error in Admin now stays on screen long enough to read, up to fifteen
seconds.
- Settings no longer flashes the desktop sidebar before the phone layout
appears.

- Backup placeholder no longer moves or reshuffles when a card is hovered or
tapped.
- Dock and the Settings tab bar now sit as close to the bottom of the screen as
to the sides.

- The App, Widget and Folder tiles under Add New show their names instead of
their internal keys.
- Remove the stray Support link from Settings &gt; General.

- The Backup widget shows the job picker for the provider you picked. Kopia
showed the Duplicati picker as well, and Duplicati showed no picker at all.
- App title toggles in Settings &gt; Appearance now persist across a reload.

- A badge or widget field blocked for pointing at a private address now names
`ALLOW_PRIVATE_IPS=true` as the setting that allows it. The advice was added
in 1.6.0 but never appeared.
- Connections map tooltips now use the system font on Android, Windows and
Linux instead of falling back to a serif.

- A widget settings Fetch marks a blocked private address as a warning, the
same as a badge test, instead of a hard error.
- Saving on Settings &gt; Appearance no longer reports "Wallpaper saved" when
something else on the page was saved.

- A badge extract path ending in `count` now reads a JSON field of that
name. It only counts an array when the value is an array.
- The login and set-password screens, the phone tab bar, the logging level and
wallpaper source menus, the version line and the app, widget and widget
configuration forms are now translated instead of always showing English.
</Changes>
<Config Name="WebUI Port" Target="80" Default="8700" Mode="tcp" Description="Port the dashboard is reached on." Type="Port" Display="always" Required="true" Mask="false">8700</Config>
<Config Name="Config Storage" Target="/data" Default="/mnt/user/appdata/stackyard/data" Mode="rw" Description="Dashboard configuration. Keep this, or every setting is lost on update." Type="Path" Display="always" Required="true" Mask="false">/mnt/user/appdata/stackyard/data</Config>
Expand Down
Loading