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
142 changes: 142 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
name: Playwright Tests
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
permissions:
contents: read
pull-requests: write
concurrency:
group: playwright-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7

- name: Set up Go 1.x
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: go.mod
check-latest: true
cache: true

- name: Use Node.js LTS
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: lts/*

- name: Build Argus (web + binary)
run: make build

- name: Start Argus test server
run: make playwright-tests-setup
env:
ARGUS_SERVICE_LATEST_VERSION_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ARGUS_SERVICE_LATEST_VERSION_REQUIRE_DOCKER_REGISTRY_HUB_AUTH_USERNAME: ${{ vars.DOCKER_HUB_USERNAME_R }}
ARGUS_SERVICE_LATEST_VERSION_REQUIRE_DOCKER_REGISTRY_HUB_AUTH_TOKEN: ${{ secrets.DOCKER_HUB_TOKEN_R }}
ARGUS_SERVICE_LATEST_VERSION_REQUIRE_DOCKER_REGISTRY_GHCR_AUTH_TOKEN: ${{ secrets.DOCKER_GHCR_TOKEN_R }}
ARGUS_SERVICE_LATEST_VERSION_REQUIRE_DOCKER_REGISTRY_QUAY_AUTH_TOKEN: ${{ secrets.DOCKER_QUAY_TOKEN_R }}

- name: Install Playwright dependencies
working-directory: web/ui/react-app
run: npm ci

- name: Type-check Playwright tests
working-directory: web/ui/react-app
run: npm run typecheck:e2e

- name: Install Playwright Browsers
working-directory: web/ui/react-app
run: npx playwright install --with-deps chromium firefox webkit

- name: Run Playwright tests
run: make playwright-tests

- name: Upload test results (screenshots & traces)
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: ${{ !cancelled() }}
with:
name: test-results
path: web/ui/react-app/test-results/
retention-days: 30

- name: Upload Playwright HTML report
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: web/ui/react-app/playwright-report/
retention-days: 30

- name: Comment test results on PR
continue-on-error: true
if: ${{ !cancelled() && github.event_name == 'pull_request' }}
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
with:
script: |
const fs = require('fs');
const { owner, repo } = context.repo;
const runUrl = `https://github.com/${owner}/${repo}/actions/runs/${context.runId}`;

let body;
try {
const { stats } = JSON.parse(fs.readFileSync('web/ui/react-app/results.json', 'utf8'));
const icon = stats.unexpected > 0 ? '❌' : '✅';
const rows = [
['✅ Passed', stats.expected],
['❌ Failed', stats.unexpected],
['🔄 Flaky', stats.flaky],
['⏭️ Skipped', stats.skipped],
].map(([label, n]) => `| ${label} | ${n} |`).join('\n');
body = [
`## ${icon} Playwright Test Results`,
'',
'| | Count |',
'|---|---|',
rows,
'',
`Duration: ${(stats.duration / 1000).toFixed(1)}s`,
'',
`[View screenshots & full report](${runUrl})`,
].join('\n');
} catch {
body = `## Playwright Test Results\n\nResults unavailable — [view run](${runUrl}).`;
}

const marker = '<!-- playwright-results -->';
body = `${marker}\n${body}`;
const issue_number = context.payload.pull_request.number;
const { data: comments } = await github.rest.issues.listComments({
owner,
repo,
issue_number,
per_page: 100,
});
const existing = comments.find(
(c) => c.user?.type === 'Bot' && c.body?.includes(marker),
);
if (existing) {
await github.rest.issues.updateComment({
owner,
repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner,
repo,
issue_number,
body,
});
}


- name: Stop Argus test server
if: ${{ !cancelled() }}
run: make playwright-tests-teardown
18 changes: 8 additions & 10 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# MacOS
.DS_Store

# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories
vendor/
node_modules/

# Output of `make build`
/argus
Expand All @@ -20,12 +21,9 @@ vendor/
# Output of `make build-all`
/.build/

# Output of the go coverage tool
*.out

# Ignore personal files
config.yml
argus.db

# React
node_modules

# MacOS
.DS_Store
argus.db
8 changes: 4 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -540,10 +540,10 @@ New mascot! Thanks [@rexapex](https://github.com/rexapex)
### Features

* **command:** apply version var templating to args ([5bf93b7](https://github.com/release-argus/Argus/commit/5bf93b7930978a4c65fe6fb3aaf57f4b9f0b7d56))
* **config:** `active` var to disable a service ([#88](https://github.com/release-argus/argus/issues/88)) ([af756f4](https://github.com/release-argus/Argus/commit/af756f458cfbe9f379a96e338376d20c1702976c))
* **config:** `comment` var for services ([#90](https://github.com/release-argus/argus/issues/90)) ([a6b68eb](https://github.com/release-argus/Argus/commit/a6b68eb72495f8ff450feba1b9cb2f47fe1523db))
* **ui:** icons can be links - `icon_link_to` ([#92](https://github.com/release-argus/argus/issues/92)) ([8c3a9af](https://github.com/release-argus/Argus/commit/8c3a9af5c38a8ea3ce16fdf1c62ab0a730a16359))
* **webhook:** add `gitlab` type ([#95](https://github.com/release-argus/argus/issues/95)) ([5a8ab55](https://github.com/release-argus/Argus/commit/5a8ab551e672f08e6a82fdcbd3e0d3bc8f498c0d))
* **config:** `active` var to disable a service ([#88](https://github.com/release-argus/Argus/issues/88)) ([af756f4](https://github.com/release-argus/Argus/commit/af756f458cfbe9f379a96e338376d20c1702976c))
* **config:** `comment` var for services ([#90](https://github.com/release-argus/Argus/issues/90)) ([a6b68eb](https://github.com/release-argus/Argus/commit/a6b68eb72495f8ff450feba1b9cb2f47fe1523db))
* **ui:** icons can be links - `icon_link_to` ([#92](https://github.com/release-argus/Argus/issues/92)) ([8c3a9af](https://github.com/release-argus/Argus/commit/8c3a9af5c38a8ea3ce16fdf1c62ab0a730a16359))
* **webhook:** add `gitlab` type ([#95](https://github.com/release-argus/Argus/issues/95)) ([5a8ab55](https://github.com/release-argus/Argus/commit/5a8ab551e672f08e6a82fdcbd3e0d3bc8f498c0d))
* **webhook:** apply version var templating to custom headers + url ([e31f51b](https://github.com/release-argus/Argus/commit/e31f51bc9877a40101e74c9cb930f2adf9be564d))


Expand Down
68 changes: 68 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,71 @@ build: web common-build

.PHONY: build-all
build-all: web-build compress-web build-darwin build-freebsd build-linux build-openbsd build-windows

PLAYWRIGHT_DIR = $(UI_PATH)/react-app
PLAYWRIGHT_TESTS_DIR = $(PLAYWRIGHT_DIR)/tests
PLAYWRIGHT_CONFIG_FILE = $(PLAYWRIGHT_TESTS_DIR)/playwright-test-config.yml
PLAYWRIGHT_DB_FILE = $(PLAYWRIGHT_TESTS_DIR)/playwright-tests.db
PLAYWRIGHT_PID_FILE = $(PLAYWRIGHT_TESTS_DIR)/playwright-argus.pid
PLAYWRIGHT_LOG_FILE = $(PLAYWRIGHT_TESTS_DIR)/playwright-test.log
# Port the Makefile-managed Argus server listens on.
PLAYWRIGHT_PORT ?= 8080
# URL the Playwright tests run against; defaults to the managed server.
BASE_URL ?= http://localhost:$(PLAYWRIGHT_PORT)

# wait_for_argus,<base-url>: poll the healthcheck endpoint until it answers (max 60s).
define wait_for_argus
i=0; until curl -sf $(1)/api/v1/healthcheck >/dev/null; do \
i=$$((i+1)); \
if [ $$i -ge 30 ]; then \
echo "Argus not reachable at $(1) after 60s" >&2; \
if [ -f $(PLAYWRIGHT_LOG_FILE) ]; then cat $(PLAYWRIGHT_LOG_FILE) >&2; fi; \
exit 1; \
fi; \
sleep 2; \
done
endef

.PHONY: playwright-tests-setup
playwright-tests-setup:
@if [ -n "$(FRESH)" ]; then \
$(MAKE) playwright-tests-teardown; \
rm -f ./$(OUTPUT_BINARY); \
elif [ -f $(PLAYWRIGHT_PID_FILE) ]; then \
kill $$(cat $(PLAYWRIGHT_PID_FILE)) 2>/dev/null || true; \
rm -f $(PLAYWRIGHT_PID_FILE); \
fi
cp config.yml.example $(PLAYWRIGHT_CONFIG_FILE)
@if [ ! -x ./$(OUTPUT_BINARY) ] || [ -n "$(FORCE)" ]; then $(MAKE) build; fi
./$(OUTPUT_BINARY) \
-config.file $(PLAYWRIGHT_CONFIG_FILE) \
-data.database-file $(PLAYWRIGHT_DB_FILE) \
-web.listen-port $(PLAYWRIGHT_PORT) \
-log.level DEBUG \
> $(PLAYWRIGHT_LOG_FILE) 2>&1 & echo $$! > $(PLAYWRIGHT_PID_FILE)
@$(call wait_for_argus,http://localhost:$(PLAYWRIGHT_PORT))

.PHONY: playwright-tests-teardown
playwright-tests-teardown:
@if [ -f $(PLAYWRIGHT_PID_FILE) ]; then \
kill $$(cat $(PLAYWRIGHT_PID_FILE)) 2>/dev/null || true; \
rm -f $(PLAYWRIGHT_PID_FILE); \
else \
echo "No playwright Argus instance found running ($(PLAYWRIGHT_PID_FILE) missing)"; \
fi
rm -f $(PLAYWRIGHT_CONFIG_FILE) $(PLAYWRIGHT_DB_FILE)* $(PLAYWRIGHT_LOG_FILE)

.PHONY: playwright-tests
playwright-tests:
@echo "Waiting for Argus to be ready at $(BASE_URL)..."
@$(call wait_for_argus,$(BASE_URL))
cd $(PLAYWRIGHT_DIR) && \
BASE_URL=$(BASE_URL) \
PWTEST_CHILD_PROCESS_TIMEOUT=30000 \
npx playwright test $(if $(HEADED),--headed,)

.PHONY: playwright-full
playwright-full: playwright-tests-setup
@$(MAKE) playwright-tests; status=$$?; \
$(MAKE) playwright-tests-teardown; \
exit $$status
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@

Keeping an eye on releases.

[![GitHub](https://img.shields.io/github/license/release-argus/argus)](https://github.com/release-argus/Argus/blob/master/LICENSE)
[![GitHub](https://img.shields.io/github/license/release-argus/Argus)](https://github.com/release-argus/Argus/blob/master/LICENSE)
[![Go Report Card](https://goreportcard.com/badge/github.com/release-argus/Argus)](https://goreportcard.com/report/github.com/release-argus/Argus)
[![GitHub go.mod Go version (subdirectory of monorepo)](https://img.shields.io/github/go-mod/go-version/release-argus/argus?filename=go.mod)](https://go.dev/dl/)
[![GitHub package.json dependency version (subfolder of monorepo)](https://img.shields.io/github/package-json/dependency-version/release-argus/argus/react?filename=web%2Fui%2Freact-app%2Fpackage.json)](https://reactjs.org/)
[![Codecov](https://img.shields.io/codecov/c/github/release-argus/argus)](https://app.codecov.io/gh/release-argus/Argus)
[![GitHub go.mod Go version (subdirectory of monorepo)](https://img.shields.io/github/go-mod/go-version/release-argus/Argus?filename=go.mod)](https://go.dev/dl/)
[![GitHub package.json dependency version (subfolder of monorepo)](https://img.shields.io/github/package-json/dependency-version/release-argus/Argus/react?filename=web%2Fui%2Freact-app%2Fpackage.json)](https://reactjs.org/)
[![Codecov](https://img.shields.io/codecov/c/github/release-argus/Argus)](https://app.codecov.io/gh/release-argus/Argus)
<br>
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/release-argus/Argus/build-binary.yml)](https://github.com/release-argus/Argus/actions/workflows/build-binary.yml)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/release-argus/argus)](https://github.com/release-argus/Argus/releases)
[![GitHub all releases](https://img.shields.io/github/downloads/release-argus/argus/total)](https://github.com/release-argus/Argus/releases)
[![GitHub release (latest by SemVer)](https://img.shields.io/github/downloads/release-argus/argus/latest/total)](https://github.com/release-argus/Argus/releases/latest)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/release-argus/Argus)](https://github.com/release-argus/Argus/releases)
[![GitHub all releases](https://img.shields.io/github/downloads/release-argus/Argus/total)](https://github.com/release-argus/Argus/releases)
[![GitHub release (latest by SemVer)](https://img.shields.io/github/downloads/release-argus/Argus/latest/total)](https://github.com/release-argus/Argus/releases/latest)
<br>
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/release-argus/Argus/build-docker.yml)](https://github.com/release-argus/Argus/actions/workflows/build-docker.yml)
[![Docker Image Version (latest semver)](https://img.shields.io/docker/v/releaseargus/argus?sort=semver)](https://hub.docker.com/r/releaseargus/argus/tags)
Expand All @@ -25,7 +25,7 @@ Keeping an eye on releases.
</div>

Argus will query websites at a user defined interval for new software releases and then trigger Gotify/Slack/Other notification(s) and/or WebHook(s) when one has been found.
For example, you could set it to monitor the Argus repo ([release-argus/argus](https://github.com/release-argus/Argus)). This will query the [GitHub API](https://api.github.com/repos/release-argus/argus/releases) and track the "tag_name" variable. When this variable changes from what it was on a previous query, a GitHub-style WebHook could be sent that triggers something (like AWX) to update Argus on your server.
For example, you could set it to monitor the Argus repo ([release-argus/Argus](https://github.com/release-argus/Argus)). This will query the [GitHub API](https://api.github.com/repos/release-argus/Argus/releases) and track the "tag_name" variable. When this variable changes from what it was on a previous query, a GitHub-style WebHook could be sent that triggers something (like AWX) to update Argus on your server.

##### Table of Contents

Expand Down
4 changes: 2 additions & 2 deletions config.yml.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
service:
release-argus/argus:
release-argus/Argus:
latest_version:
type: github
url: release-argus/argus
url: release-argus/Argus
dashboard:
icon: https://raw.githubusercontent.com/release-argus/Argus/master/web/ui/react-app/public/favicon.svg
icon_link-to: https://release-argus.io
Expand Down
8 changes: 4 additions & 4 deletions config/decode/json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ package decode

import (
"fmt"
"regexp"
"strings"
"testing"

"github.com/release-argus/Argus/internal/test"
"github.com/release-argus/Argus/util"
"github.com/release-argus/Argus/util/errfmt"
)

Expand Down Expand Up @@ -121,7 +121,7 @@ func TestParseKeys(t *testing.T) {

// AND: the error is returned correctly.
e := errfmt.FormatError(err)
if !regexp.MustCompile(tc.errRegex).MatchString(e) {
if !util.RegexCheck(tc.errRegex, e) {
t.Errorf(
"%s error mismatch\ngot: %q\nwant: %q",
prefix, e, tc.errRegex,
Expand Down Expand Up @@ -416,7 +416,7 @@ func TestNavigateJSON(t *testing.T) {

// AND: the error is returned correctly.
e := errfmt.FormatError(err)
if !regexp.MustCompile(tc.errRegex).MatchString(e) {
if !util.RegexCheck(tc.errRegex, e) {
t.Errorf(
"%s error mismatch\ngot: %q\nwant: %q",
prefix, e, tc.errRegex,
Expand Down Expand Up @@ -550,7 +550,7 @@ func TestGetValueByKey(t *testing.T) {

// AND: the error is returned correctly.
e := errfmt.FormatError(err)
if !regexp.MustCompile(tc.errRegex).MatchString(e) {
if !util.RegexCheck(tc.errRegex, e) {
t.Errorf(
"%s error mismatch\ngot: %q\nwant: %q",
prefix, e, tc.errRegex,
Expand Down
6 changes: 3 additions & 3 deletions config/save.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,12 @@ func drainAndDebounce[T any](ctx context.Context, channel chan T, duration time.

// Save writes the configuration to c.File.
func (c *Config) Save() (ok bool) {
// Lock the config.
c.OrderMu.Lock()
defer c.OrderMu.Unlock()
c.OrderMu.RLock()

// Encode to memory (Go-ordered slices, but with an order list for Services).
var buf bytes.Buffer
if err := encodeConfigYAML(&buf, int(c.Settings.Indentation), c); err != nil {
c.OrderMu.RUnlock()
logx.Fatal(
fmt.Sprintf("error encoding config: %v", err),
logx.LogFrom{},
Expand All @@ -112,6 +111,7 @@ func (c *Config) Save() (ok bool) {
// Reorder and clean the YAML in memory.
lines := strings.Split(string(util.NormaliseNewlines(buf.Bytes())), "\n")
lines = c.reorderYAML(lines)
c.OrderMu.RUnlock()

// Open the file.
file, err := openSaveFile(c.File)
Expand Down
4 changes: 2 additions & 2 deletions internal/test/marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ package test

import (
"fmt"
"regexp"
"testing"

"github.com/goccy/go-yaml"
"github.com/release-argus/Argus/util"
"github.com/release-argus/Argus/util/errfmt"
)

Expand Down Expand Up @@ -111,7 +111,7 @@ func TestUnmarshal(t *testing.T) {

// THEN: the error is as expected.
e := errfmt.FormatError(err)
if !util.RegexCheck(tc.errRegex, e) {
if !regexp.MustCompile(tc.errRegex).MatchString(e) {
t.Errorf(
"%s error mismatch\ngot: %q\nwant: %q",
prefix, e, tc.errRegex,
Expand Down
Loading
Loading