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
33 changes: 33 additions & 0 deletions .githooks/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
# shellcheck shell=bash
set -euo pipefail

# Rejects pushing non-standard tags. Release tags must be SemVer with a 'v' prefix:
# vX.Y.Z (production, e.g. v0.5.0)
# vX.Y.Z-<pre> (pre-release, e.g. v0.5.0-beta.1 — normally created by CI)
# Examples REJECTED: X.Y.Z, X.Y, vX.Y, 1.0, v1.
#
# Enable once per clone: git config core.hooksPath .githooks

pattern='^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?$'
zero='0000000000000000000000000000000000000000'
status=0

while read -r _local_ref _local_sha remote_ref remote_sha; do
case "$remote_ref" in
refs/tags/*)
# Allow tag deletions (remote_sha stays, local sha is zero on delete is the other way;
# we only validate creations/updates).
[ "$remote_sha" = "$zero" ] && continue || true
tag="${remote_ref#refs/tags/}"
if ! printf '%s' "$tag" | grep -qE "$pattern"; then
echo "✖ Refused to push tag '$tag'." >&2
echo " Release tags must be SemVer with a 'v' prefix, e.g. v0.5.0 (or v0.5.0-beta.1)." >&2
echo " Rejected forms: X.Y.Z, X.Y, vX.Y, bare numbers." >&2
status=1
fi
;;
esac
done

exit $status
68 changes: 68 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Release
on:
push:
branches:
- main
- 'release/**'

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-node@v4
with:
node-version: 20

- name: Build bundle (smoke test)
run: |
npm ci
npm run package

- name: Release
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
BRANCH="${GITHUB_REF_NAME}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

# Version source of truth = latest stable tag vX.Y.Z (pushed manually).
# Used for BOTH production and beta — the branch only selects the channel.
STABLE="$(git tag --list 'v*' | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -1)"
if [ -z "$STABLE" ]; then
echo "::error::No stable vX.Y.Z tag found. Push the release tag (e.g. v0.5.0) first."
exit 1
fi
echo "Latest stable tag: $STABLE"

if [ "$BRANCH" = "main" ]; then
if gh release view "$STABLE" >/dev/null 2>&1; then
echo "Release $STABLE already exists — nothing to do."
exit 0
fi
echo "Production release: $STABLE"
gh release create "$STABLE" --title "$STABLE" --generate-notes --latest
MAJOR="${STABLE%%.*}"
git tag -f "$MAJOR" "$STABLE"
git push origin -f "refs/tags/$MAJOR"
echo "Moved major alias $MAJOR -> $STABLE"
elif [[ "$BRANCH" == release/* ]]; then
# Beta of the latest stable tag; auto-increment N from existing beta tags.
LAST="$(git tag --list "${STABLE}-beta.*" | sed -E 's/.*-beta\.([0-9]+)$/\1/' | sort -n | tail -1)"
N=$(( ${LAST:-0} + 1 ))
TAG="${STABLE}-beta.${N}"
echo "Beta release: $TAG (base $STABLE)"
git tag "$TAG"
git push origin "refs/tags/$TAG"
gh release create "$TAG" --title "$TAG" --generate-notes --prerelease
else
echo "Branch '$BRANCH' is not configured for release — skipping."
fi
54 changes: 27 additions & 27 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
name: CI Pipeline
# name: CI Pipeline

on:
push:
branches:
- main
pull_request:
branches:
- main
# on:
# push:
# branches:
# - main
# pull_request:
# branches:
# - main

workflow_dispatch:
# workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest
# jobs:
# build:
# runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3
# steps:
# - name: Checkout code
# uses: actions/checkout@v3

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
# - name: Set up Node.js
# uses: actions/setup-node@v3
# with:
# node-version: '16'

- name: Test Local Action
id: test-action
uses: ./
with:
accessToken: ${{ secrets.AC_ACCESS_TOKEN }}
profileID: ${{ secrets.AC_PROFILE_ID }}
appPath: ${{ secrets.APP_PATH }}
message: ${{ secrets.MESSAGE }}
# - name: Test Local Action
# id: test-action
# uses: ./
# with:
# accessToken: ${{ secrets.AC_ACCESS_TOKEN }}
# profileID: ${{ secrets.AC_PROFILE_ID }}
# appPath: ${{ secrets.APP_PATH }}
# message: ${{ secrets.MESSAGE }}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,7 @@ __tests__/runner/*
.idea
.vscode
*.code-workspace

# Test Script

test.sh
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,9 @@ leading to better products and faster delivery times.
- macos-14 (arm64)
- Ubuntu-22.04

Note: Currently, plugins are only compatible to use with **Appcircle Cloud**.
**Self-hosted** support will be available in future releases.
Note: Both **Appcircle Cloud** and **self-hosted** Appcircle installations are
supported. See [Self-Hosted Appcircle](#self-hosted-appcircle) below to configure
custom endpoints.

### Testing Distribution

Expand Down Expand Up @@ -127,6 +128,39 @@ Local Action' with the appropriate information.
- `message`: Your message to testers, ensuring they receive important updates
and information regarding the application.

### Self-Hosted Appcircle

If you run a self-hosted Appcircle installation, point the action to your own
servers with the optional `authEndpoint` and `apiEndpoint` inputs. When omitted,
they default to the Appcircle cloud (`https://auth.appcircle.io` and
`https://api.appcircle.io`), so existing cloud workflows keep working without any
change.

```yaml
- name: Publish App to Appcircle
uses: appcircleio/appcircle-testing-distribution-githubaction
with:
personalAPIToken: ${{ secrets.AC_PROFLE_API_TOKEN }}
profileName: ${{ secrets.AC_PROFILE_NAME }}
createProfileIfNotExists: ${{ secrets.CREATE_PROFILE_IF_NOT_EXISTS }}
appPath: ${{ secrets.APP_PATH }}
message: ${{ secrets.MESSAGE }}
authEndpoint: https://auth.your-appcircle-domain.com
apiEndpoint: https://api.your-appcircle-domain.com
```

- `authEndpoint`: Base URL of the self-hosted Appcircle authentication server.
Optional; defaults to `https://auth.appcircle.io`.
- `apiEndpoint`: Base URL of the self-hosted Appcircle API server. Optional;
defaults to `https://api.appcircle.io`.

> **Self-signed or private CA certificates:** If your self-hosted Appcircle server
> uses a self-signed certificate (or one issued by a private/internal CA), requests
> will fail certificate validation. The action does not disable TLS verification.
> Trust the server's CA on the runner — set the `NODE_EXTRA_CA_CERTS` environment
> variable to a PEM file containing the CA certificate, or add the CA to the system
> certificate store.

### Leveraging Environment Variables

Utilize environment variables seamlessly by substituting the parameters with
Expand Down
12 changes: 12 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ inputs:
description:
'Provide Appcircle Personal API Token to authenticate Appcircle services.'
required: true
authEndpoint:
description:
'Optional: Authentication endpoint URL for self-hosted Appcircle
installations. Defaults to the Appcircle cloud.'
required: false
default: 'https://auth.appcircle.io'
apiEndpoint:
description:
'Optional: API endpoint URL for self-hosted Appcircle installations.
Defaults to the Appcircle cloud.'
required: false
default: 'https://api.appcircle.io'
profileName:
description:
'Enter the profile name of the Appcircle distribution profile. This name
Expand Down
Loading
Loading