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
9 changes: 7 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"editor.inlineSuggest.minShowDelay": 200,
"editor.fontFamily": "'JetBrainsMonoNL Nerd Font Mono', 'monospace'",
"editor.fontSize": 16,
"editor.fontLigatures": true,
"files.autoSaveWhenNoErrors": true,
"files.autoSaveWorkspaceFilesOnly": true,
"terminal.integrated.cursorBlinking": true,
Expand All @@ -66,7 +67,10 @@
"bashIde.includeAllWorkspaceSymbols": true,
"shellcheck.useWorkspaceRootAsCwd": true,
"shellcheck.disableVersionCheck": true,
"shellcheck.enableQuickFix": false
"shellcheck.enableQuickFix": false,
"editor.semanticHighlighting.enabled": true,
"terminal.integrated.minimumContrastRatio": 1,
"window.titleBarStyle": "custom"
}
}
},
Expand All @@ -83,6 +87,7 @@
"source=${localEnv:HOME}/.codex,target=/home/node/.codex,type=bind,consistency=cached",
"source=${localEnv:HOME}/.bash_history,target=/home/node/.bash_history,type=bind,consistency=cached",
"source=${localEnv:HOME}/.gitconfig,target=/home/node/.gitconfig,type=bind,consistency=cached",
"source=${localEnv:HOME}/.git-credentials,target=/home/node/.git-credentials,type=bind,consistency=cached"
"source=${localEnv:HOME}/.git-credentials,target=/home/node/.git-credentials,type=bind,consistency=cached",
"source=${localEnv:HOME}/.docker,target=/home/node/.docker,type=bind,consistency=cached"
]
}
39 changes: 30 additions & 9 deletions .github/actions/validate/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,40 @@ runs:
- name: Install ShellCheck
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y shellcheck
if command -v shellcheck >/dev/null 2>&1; then
echo "ShellCheck already installed; skipping apt-get."
else
sudo apt-get update
sudo apt-get install -y shellcheck
fi

- name: Shellcheck
shell: bash
run: |
shellcheck \
src/helix-url-handler.sh \
.devcontainer/scripts/*.sh \
scripts/devcontainer/*.sh \
scripts/container-cleanup.sh \
tests/run.sh
set -euo pipefail

gather() {
local dir="$1"
if [ -d "$dir" ]; then
while IFS= read -r -d '' file; do
files+=("$file")
done < <(find "$dir" -name '*.sh' -print0)
fi
}

files=()
gather src
gather .devcontainer/scripts
gather scripts
gather tests

if [ "${#files[@]}" -eq 0 ]; then
echo "No shell scripts found to lint." >&2
exit 0
fi

shellcheck "${files[@]}"

- name: Run tests
shell: bash
run: ./tests/run.sh
run: ./tests/smoke-tests.sh
78 changes: 78 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,90 @@ jobs:
dist/helix-url-handler-*.rpm
dist/SHASUMS256.txt

package_test:
name: E2E package tests
needs:
- prepare
- package_tgz
- package_deb
- package_rpm
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- name: Ubuntu 24.04
distro: ubuntu
image: ubuntu:24.04
requires: deb
- name: Debian 12
distro: debian
image: debian:12
requires: deb
- name: Fedora 42
distro: fedora
image: fedora:42
requires: rpm
- name: Rocky Linux 9
distro: rocky
image: rockylinux:9
requires: rpm
steps:
- name: Check artifact availability
id: gate
shell: bash
env:
REQUIRES: ${{ matrix.requires }}
HAS_DEB: ${{ fromJSON(needs.prepare.outputs.formats).deb }}
HAS_RPM: ${{ fromJSON(needs.prepare.outputs.formats).rpm }}
run: |
set -euo pipefail
case "$REQUIRES" in
deb)
if [ "$HAS_DEB" != "true" ]; then
echo "skip=true" >>"$GITHUB_OUTPUT"
exit 0
fi
;;
rpm)
if [ "$HAS_RPM" != "true" ]; then
echo "skip=true" >>"$GITHUB_OUTPUT"
exit 0
fi
;;
esac
echo "skip=false" >>"$GITHUB_OUTPUT"

- name: Skip package test
if: ${{ steps.gate.outputs.skip == 'true' }}
run: echo "Skipping package test for ${{ matrix.name }}; artifact not built."

- name: Checkout
uses: actions/checkout@v5
if: ${{ steps.gate.outputs.skip != 'true' }}

- name: Download build artifacts
uses: actions/download-artifact@v4
if: ${{ steps.gate.outputs.skip != 'true' }}
with:
path: dist
merge-multiple: true

- name: Run package smoke test on ${{ matrix.name }}
if: ${{ steps.gate.outputs.skip != 'true' }}
env:
DIST_DIR: ${{ github.workspace }}/dist
HELIX_VERSION: 25.07.1
run: |
tests/e2e/package-test.sh --distro ${{ matrix.distro }}

publish:
needs:
- prepare
- package_tgz
- package_deb
- package_rpm
- package_test
if: ${{ needs.prepare.outputs.publish_release == 'true' }}
runs-on: ubuntu-latest
permissions:
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
- Shared release logging helpers (`scripts/release/common.sh`) and richer packaging logs.
- Default CODEOWNERS entry to auto-request repository reviews from `@mihailfox`.
- Devcontainer provisioning now writes a Helix configuration preset and ensures terminal helpers/lynx are available.
- Docker-based package smoke tests (`scripts/e2e/package-test.sh`) that install Helix and the handler inside Ubuntu 24.04, Debian 12, Fedora 42, and Rocky Linux 9 containers.

### Changed
- Consolidated the host devcontainer tooling into `scripts/devcontainer/launcher.sh` with dedicated `vscode`, `tui`, and `helper` commands, replacing the separate `code-dev-container.sh` and `scripts/run-devcontainer-launcher.sh` helpers.
- Relocated the helper Dockerfile and container entrypoint beneath `scripts/devcontainer/` to keep devcontainer assets together.
- Parameterised `make devcontainer` so you can launch the helper in `tui` mode (or any other subcommand) by exporting `DEVCONTAINER_MODE`/`DEVCONTAINER_ARGS`.
- Release workflow now runs the package smoke tests across Ubuntu 24.04, Debian 12, Fedora 42, and Rocky Linux 9 before publishing.
- `scripts/release/build-artifacts.sh` calls `fpm` with explicit output types to eliminate runtime warnings.

## [0.1.0-alpha.2] - 2025-10-12
Expand Down
5 changes: 3 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ Thanks for your interest in improving Helix URL Handler! This document outlines
1. Run the local checks:

```bash
./tests/run.sh
shellcheck src/helix-url-handler.sh src/lib/*.sh
make check
```

For end-to-end package validation, build and test the artifacts with `make test-packages VERSION=<x.y.z>` (or `make test-packages-<distro> VERSION=<x.y.z>`).

2. Update `CHANGELOG.md` under **Unreleased** with a short note about the change.
3. Target the pull request at `main`. GitHub Actions will run the automated checks.
4. Use Conventional Commit messages (e.g. `feat:`, `fix:`, `chore:`) to keep the history aligned with releases.
Expand Down
79 changes: 71 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,37 +1,100 @@
SHELL := /usr/bin/env bash

SHELLCHECK_FILES := src/helix-url-handler.sh $(wildcard src/lib/*.sh) scripts/release/common.sh scripts/release/build-artifacts.sh scripts/release/prepare-matrix.sh
SHELLCHECK_DIRS := src .devcontainer/scripts scripts tests
DIST_FORMATS := tgz deb rpm

.PHONY: check lint test devcontainer dist FORCE
.PHONY: check lint test devcontainer dist dist-clean FORCE

check: lint test

lint:
@shellcheck $(SHELLCHECK_FILES)
@set -euo pipefail; \
gather() { \
local dir="$$1"; \
if [ -d "$$dir" ]; then \
while IFS= read -r -d '' file; do \
files+=("$$file"); \
done < <(find "$$dir" -type f -name '*.sh' -print0); \
fi; \
}; \
files=(); \
for dir in $(SHELLCHECK_DIRS); do \
gather "$$dir"; \
done; \
if [ "$${#files[@]}" -eq 0 ]; then \
echo "No shell scripts found to lint."; \
exit 0; \
fi; \
failures=0; \
total=0; \
for file in "$${files[@]}"; do \
total=$$((total + 1)); \
if shellcheck "$$file"; then \
printf '✓ shellcheck %s\n' "$$file"; \
else \
printf '✗ shellcheck %s\n' "$$file"; \
failures=$$((failures + 1)); \
fi; \
done; \
if [ "$$failures" -ne 0 ]; then \
printf '\nShellcheck failed for %d of %d script(s)\n\n' "$$failures" "$$total"; \
exit 1; \
fi; \
printf '\nShellcheck passed for %d script(s)\n\n' "$$total"

smoke-tests:
@./tests/smoke-tests.sh

test:
@./tests/run.sh
test: smoke-tests

DEVCONTAINER_MODE ?= vscode
DEVCONTAINER_ARGS ?=
DEVCONTAINER_ARGS ?= --no-attach

devcontainer:
@scripts/devcontainer/launcher.sh $(DEVCONTAINER_MODE) $(DEVCONTAINER_ARGS)

.PHONY: test-packages test-packages-% clean-packages

TEST_PACKAGES_ARGS ?= --all --refresh-images
test-packages: dist
@tests/e2e/package-test.sh $(TEST_PACKAGES_ARGS)

test-packages-%: FORCE dist
@tests/e2e/package-test.sh --distro $*

clean-packages: dist-clean
@set -euo pipefail; \
if [ "$(CONFIRM)" != "1" ]; then \
echo "Set CONFIRM=1 make clean-packages to remove e2e artifacts" >&2; \
exit 1; \
fi; \
filter="label=helix-url-handler=e2e"; \
echo "Removing e2e containers..."; \
docker ps -aq --filter "$$filter" | xargs -r docker rm -f; \
echo "Removing e2e images..."; \
docker images -aq --filter "$$filter" | xargs -r docker rmi -f || true; \
echo "Removing e2e volumes..."; \
docker volume ls -q --filter "$$filter" | xargs -r docker volume rm || true; \
echo "Removing e2e networks..."; \
docker network ls -q --filter "$$filter" | xargs -r docker network rm || true; \
echo "E2E artifacts removed."

define REQUIRE_VERSION
@if [ -z "$(VERSION)" ]; then \
echo "VERSION is required (e.g., make $@ VERSION=0.1.0)" >&2; \
exit 1; \
fi
endef

dist: FORCE
dist: dist-clean
$(REQUIRE_VERSION)
@scripts/release/build-artifacts.sh "$(VERSION)" all

dist-%: FORCE
$(REQUIRE_VERSION)
@scripts/release/build-artifacts.sh "$(VERSION)" "$*"

dist-clean:
@echo "Removing existing packages..."
@rm -rf dist

FORCE:
35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ GitHub releases publish ready-to-install bundles. Pick the artifact that fits yo

All packages install the tool under `/usr/lib/helix-url-handler` and expose a `helix-url-handler` binary on your `PATH`. After installation, run `helix-url-handler --install` (or `--uninstall`) just like the source checkout. Each release also ships a `SHASUMS256.txt` file so you can verify downloads.

> The DEB declares `libglib2.0-bin`, `xdg-utils`, and `desktop-file-utils` alongside `bash`; the RPM pulls the equivalent `glib2`, `xdg-utils`, and `desktop-file-utils` packages. Your package manager resolves these automatically.

Prefer building locally? Use `make dist VERSION=<x.y.z>` to generate every package, or `make dist-<tgz|deb|rpm> VERSION=<x.y.z>` to build a single format. Need a CI dry run? Trigger the **Release** workflow manually, deselect `publish_release`, and tick only the artifact formats you care about.

### Useful flags
Expand Down Expand Up @@ -167,10 +169,39 @@ The devcontainer binds host files into the container to persist local state acro
A basic smoke-test suite verifies the `hx-url` wrapper’s parsing logic and the installer’s ability to register the handler in an isolated environment.

```bash
./tests/run.sh
make check
```

`make check` runs `shellcheck` across every shell script and executes `./tests/smoke-tests.sh`, which creates temporary directories and stubs desktop/xdg commands so it is safe outside a graphical session. You can launch the suite directly with `./tests/smoke-tests.sh` if you prefer.

### Package end-to-end smoke tests

To exercise the platform packages in clean environments, use the Docker-backed harness:

```bash
# Run the full matrix (Ubuntu 24.04, Debian 12, Fedora 42, Rocky Linux 9)
scripts/e2e/package-test.sh

# Target a single distribution
scripts/e2e/package-test.sh --distro fedora
```

The script builds ephemeral containers, installs Helix (default `HELIX_VERSION=25.07.1`), installs the requested package from `dist/`, and asserts that the installed `hx-url` wrapper invokes Helix with the expected arguments (including exercising `xdg-open`). Docker must be available locally.

The Makefile wraps these flows:

```bash
# Build every package format for VERSION and run the full matrix
make test-packages VERSION=0.1.0

# Target a specific distro (still requires VERSION to build packages)
make test-packages-fedora VERSION=0.1.0

# Remove generated artifacts and labeled test containers/images (requires CONFIRM=1)
make clean-packages CONFIRM=1
```

The tests create temporary directories and stub out desktop/xdg commands so they are safe to run outside of a graphical session.
Each `make test-packages*` invocation depends on `make dist VERSION=…`, so ensure the version matches the artifacts you intend to validate.

## Contributing

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set -euo pipefail

usage() {
cat <<'EOF'
Usage: scripts/container-cleanup.sh
Usage: scripts/devcontainer/container-cleanup.sh

Stops and removes all Docker containers, images, and volumes present on the
current host. Intended for local development environments only.
Expand Down
Loading