Skip to content

Commit

Permalink
feat: add ssp action
Browse files Browse the repository at this point in the history
  • Loading branch information
Dafnik committed May 21, 2024
1 parent 9206ac9 commit 7e8c42e
Show file tree
Hide file tree
Showing 8 changed files with 406 additions and 1 deletion.
17 changes: 17 additions & 0 deletions .github/SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Security Policy

## Supported Versions

| Version | Supported |
|---------|--------------------|
| 1.x.x | :white_check_mark: |

## Reporting a Vulnerability

If you believe you've identified a security vulnerability in `ssp` (a bug that allows something to happen that shouldn't be possible), you can reach us at <security@dafnik.me>.

You will receive a response from us within 48 hours. If the issue is confirmed, we will release a patch as soon as possible depending on complexity but likely within a few days.

## Scope

A "vulnerability in ssp" is a vulnerability in the code distributed through our main source code repository on GitHub. Vulnerabilities that are specific to a given installation (e.g. misconfiguration) should be reported to the owner of that installation and not us.
40 changes: 40 additions & 0 deletions .github/release-drafter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
name-template: "v$RESOLVED_VERSION"
tag-template: "v$RESOLVED_VERSION"
template: |
# Changelog
$CHANGES
See details of [all code changes](https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION) since previous release.
categories:
- title: "🚀 Features"
labels:
- "feature"
- "enhancement"
- title: "🐛 Bug Fixes"
labels:
- "fix"
- "bugfix"
- "bug"
- title: "🧰 Maintenance"
labels:
- "infrastructure"
- "automation"
- "documentation"
- title: "🏎 Performance"
label: "performance"
change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
version-resolver:
major:
labels:
- "type: breaking"
minor:
labels:
- "type: enhancement"
patch:
labels:
- "type: bug"
- "type: maintenance"
- "type: documentation"
default: patch
23 changes: 23 additions & 0 deletions .github/workflows/draft-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Draft release
on:
push:
branches:
- main

permissions:
contents: read

jobs:
draft-release:
permissions:
# write permission is required to create a github release
contents: write
# write permission is required for autolabeler
# otherwise, read permission is required at least
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: release-drafter/release-drafter@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
29 changes: 29 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Release
on:
release:
types: [released]
workflow_dispatch:
inputs:
TAG_NAME:
description: "Tag name that the major tag will point to"
required: true

env:
TAG_NAME: ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }}

permissions:
contents: write

jobs:
update_tag:
name: Update the major tag to include the ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }} changes
runs-on: ubuntu-latest
environment:
# Note: this environment is protected
name: Release
steps:
- name: Update the ${{ env.TAG_NAME }} tag
id: update-major-tag
uses: actions/publish-action@v0.3.0
with:
source-tag: ${{ env.TAG_NAME }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/.idea
/.idea/**
163 changes: 162 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,163 @@
# ssp
# Static Site Preview (SSP)
Deploy static site previews to a self-hosted server via ssh

## Please note that this is in no way secure a solution for hosting static site previews.
- You should only use this in repositories you **trust**.
- You should only use this on a server
- you **DO NOT** care about.
- where you **DO NOT** have sensitive information stored.
- where you **DO NOT** run other sensitive services.
- Unless you jail the specific ssh user, you are allowing a GitHub Action full ssh access to your server.
- You allow any other repository with access to the same preview server to overwrite each others previews. (This should not happen under normal circumstances, as the GitHub Action uses a hash of repository name + pull request number)

## Usage

### GitHub Action
```yml
name: preview

on: [pull_request]

jobs:
deploy:
runs-on: ubuntu-latest
permissions:
pull-requests: write
actions: read

# Deploy to the preview environment
environment:
name: preview-${{ github.event.number }}
url: ${{ steps.deploy-preview.outputs.url }}
steps:
- uses: actions/download-artifact@v4
with:
path: ./dist

- name: deploy preview
id: deploy-preview
uses: dafnik/ssp@v1
# with:
# source: dist/*
# target: /var/www/preview
# host: preview.yxz.abc
# port: 22
# username: ubuntu
# key: ${{ secrets.PREVIEW_SSH_PRIVATE_KEY }}
# strip_components: 0
```

| Inputs | Default value | Required | Description |
|--------------------|---------------|----------|--------------------------------------------------------------------------------|
| `source` | | x | Path to the files which should be deployed |
| `target` | | x | Preview server target path, must be a directory path. |
| `host` | | x | Preview server domain |
| `port` | `22` | | Preview server ssh port |
| `username` | | x | Preview server ssh username |
| `key` | | x | Preview server ssh key content of private key. ex raw content of ~/.ssh/id_rsa |
| `strip_components` | `0` | | remove the specified number of leading path elements |


Furthermore, see [action.yml](action.yml)

### NGINX Configuration
Preview are going to stored in the `/var/www/preview` directory.
```
/etc/nginx/site-enabled/preview
```

```
server {
listen 80;
server_name *.preview.xyz.abc;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name ~^(?P<sub>.+)\.preview\.xyz\.abc$;
ssl_certificate /etc/letsencrypt/live/preview.xyz.abc/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/preview.xyz.abc/privkey.pem;
# GZIP
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types text/xml text/javascript font/ttf font/eot font/otf application/rdf+xml application/x-javascript application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
# Remove X-Powered-By, which is an information leak
fastcgi_hide_header X-Powered-By;
# Do not send nginx server header
server_tokens off;
add_header Access-Control-Allow-Origin *;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header Referrer-Policy "strict-origin" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
access_log on;
error_log off;
root /var/www/preview/$sub;
location / {
index index.html;
}
}
```

### Cleanup cron job
Delete previews with not activity in the last 30 days.
```bash
# /home/ubuntu/cronDeleteUnusedPreviews.sh

# Define the directory to search in. Modify this variable to suit your needs.
SEARCH_DIR="/var/www/preview"

# Find and delete directories not modified in the last 30 days.
find "$SEARCH_DIR" -type d -mtime +30 -exec rm -rf {} +

# Explanation:
# - `find "$SEARCH_DIR"`: Start searching in the specified directory.
# - `-type d`: Only look for directories.
# - `-mtime +30`: Find directories that were last modified more than 30 days ago.
# - `-exec rm -rf {} +`: Delete each directory found ({} is replaced by the found directory name).
```

Crontab example:
```bash
@daily /home/ubuntu/cronDeleteUnusedPreviews.sh
```

## Release instructions

In order to release a new version of this action:

1. Locate the semantic version of the [upcoming release][release-list] (a draft is maintained by the [`draft-release` workflow][draft-release]).

2. Publish the draft release from the `main` branch with semantic version as the tag name, _with_ the checkbox to publish to the GitHub Marketplace checked. :ballot_box_with_check:

3. After publishing the release, the [`release` workflow][release] will automatically run to create/update the corresponding the major version tag such as `v0`.

⚠️ Environment approval is required. Check the [Release workflow run list][release-workflow-runs].

## License

The scripts and documentation in this project are released under the [MIT License](LICENSE).

<!-- references -->
[release-list]: https://github.com/dafnik/ssp/releases
[draft-release]: .github/workflows/draft-release.yml
[release]: .github/workflows/release.yml
[release-workflow-runs]: https://github.com/dafnik/ssp/actions/workflows/release.yml
103 changes: 103 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: 'Static Site Preview (SSP)'
description: 'Deploy static site previews to a self-hosted server via ssh'
author: "Dafnik"
branding:
icon: 'arrow-down-circle'
color: 'purple'

inputs:
source:
required: true
description: 'Path to the files which should be deployed'
target:
required: true
description: 'Preview server target path, must be a directory path.'
host:
required: true
description: 'Preview server domain'
port:
description: 'Preview server ssh port'
default: '22'
username:
required: true
description: 'Preview server ssh username'
key:
description: 'Preview server ssh key content of private key. ex raw content of ~/.ssh/id_rsa'
strip_components:
description: 'remove the specified number of leading path elements'
default: '0'
delete_treshhold_days:
default: '30'
description: 'Number of days after inactive previews are deleted'

runs:
using: 'composite'
steps:
- name: Check if run from pull request
shell: bash
if: github.event_name != 'pull_request'
run : |
echo "static-site-preview can only be run from pull request"
echo "Check for pull request like this"
echo "if: github.event_name == 'pull_request'"
exit 1
- id: setup
shell: bash
name: Setup hash identifier & date
run: |
echo "name=${{ github.event.repository.name }}-${{ github.event.number }}" >> "$GITHUB_OUTPUT"
current_date=$(date '+%Y-%m-%d %H:%M:%S (%Z)')
echo "current_date=$current_date" >> "$GITHUB_OUTPUT"
future_date=$(date -d "+${{ inputs.delete_treshhold_days }} days" '+%Y-%m-%d')
echo "future_date=$future_date" >> "$GITHUB_OUTPUT"
- id: hash
shell: bash
name: Create hash
run: |
echo Hash identifier: "${{ steps.setup.outputs.name }}"
hash=$(echo -n "${{ steps.setup.outputs.name }}" | md5sum | awk '{print $1}')
short_hash=${hash:0:10}
echo "md5=$short_hash" >> "$GITHUB_OUTPUT"
echo "url=https://$short_hash.${{ inputs.host }}" >> "$GITHUB_OUTPUT"
- name: Check if hash is empty
shell: bash
run: |
if [ -z "${{ steps.hash.outputs.md5 }}" ]; then
echo "MD5 hash is empty. Failing the workflow."
exit 1
else
echo "MD5 hash: ${{ steps.hash.outputs.md5 }}"
fi
- name: Copy source files to target server
uses: appleboy/scp-action@v0.1.7
with:
overwrite: true
host: ${{ inputs.host }}
username: ${{ inputs.username }}
key: ${{ inputs.key }}
port: ${{ inputs.port }}
source: ${{ inputs.source }}
target: "${{ inputs.target }}/${{ steps.hash.outputs.md5 }}"
strip_components: ${{ inputs.strip_components }}

- name: Create or update sticky pull request comment
uses: marocchino/sticky-pull-request-comment@v2
with:
message: |
This pull request has been automatically deployed using Dafnik's static site preview (SSP) service.
[Learn more](https://github.com/Dafnik/ssp).
🔎 **Commit:** ${{ github.sha }}
📅 **Updated at:** ${{ steps.setup.outputs.current_date }}
✅ **Preview URL:** <${{ steps.hash.outputs.url }}>
**Note:** If there is no further activity, this preview will be deleted **${{ inputs.delete_treshhold_days }} day(s) from now** on ${{ steps.setup.outputs.future_date }}.
outputs:
url:
description: Deployment url
value: ${{ steps.hash.outputs.url }}
Loading

0 comments on commit 7e8c42e

Please sign in to comment.