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
32 changes: 32 additions & 0 deletions .github/actions/install-kustomize/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Install kustomize
description: Install the standalone kustomize CLI with a pinned version + sha256 checksum.

inputs:
version:
description: "kustomize version (without leading 'v'), e.g. 5.8.1."
required: false
default: "5.8.1"
sha256:
description: "Expected sha256 of the linux_amd64 tarball. Lookup at https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv<version>/checksums.txt"
required: false
default: "029a7f0f4e1932c52a0476cf02a0fd855c0bb85694b82c338fc648dcb53a819d"

runs:
using: composite
steps:
- name: Install kustomize
shell: bash
env:
VERSION: ${{ inputs.version }}
SHA256: ${{ inputs.sha256 }}
run: |
set -eu
# Pinned + checksum-verified so a compromised installer script can't
# silently swap the binary.
URL="https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv${VERSION}/kustomize_v${VERSION}_linux_amd64.tar.gz"
curl -fsSL -o /tmp/kustomize.tgz "$URL"
echo "${SHA256} /tmp/kustomize.tgz" | sha256sum -c -
tar -xzf /tmp/kustomize.tgz -C /tmp kustomize
sudo install -m 0755 /tmp/kustomize /usr/local/bin/kustomize
rm -f /tmp/kustomize.tgz /tmp/kustomize
kustomize version
30 changes: 30 additions & 0 deletions .github/actions/install-yq/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Install yq
description: Install the yq CLI (mikefarah/yq) with a pinned version + sha256 checksum.

inputs:
version:
description: "yq version (without leading 'v'), e.g. 4.53.2."
required: false
default: "4.53.2"
sha256:
description: "Expected sha256 of the linux_amd64 binary. Lookup at https://github.com/mikefarah/yq/releases/download/v<version>/checksums (SHA-256 row — see checksums_hashes_order)."
required: false
default: "d56bf5c6819e8e696340c312bd70f849dc1678a7cda9c2ad63eebd906371d56b"

runs:
using: composite
steps:
- name: Install yq
shell: bash
env:
VERSION: ${{ inputs.version }}
SHA256: ${{ inputs.sha256 }}
run: |
set -eu
# Pinned + checksum-verified so a compromised release can't silently swap the binary.
URL="https://github.com/mikefarah/yq/releases/download/v${VERSION}/yq_linux_amd64"
curl -fsSL -o /tmp/yq "$URL"
Comment thread
vklimontovich marked this conversation as resolved.
echo "${SHA256} /tmp/yq" | sha256sum -c -
sudo install -m 0755 /tmp/yq /usr/local/bin/yq
rm -f /tmp/yq
yq --version
118 changes: 118 additions & 0 deletions .github/actions/slack-notify/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
name: Send Slack Notification
description: Send a formatted notification to Slack with optional bullet blocks

inputs:
slack_webhook_url:
description: "Slack webhook URL (override). Normally leave empty and set SLACK_WEBHOOK_URL env in the job from secrets.CI_SLACK_WEBHOOK; this input only takes effect if that env var is unset (useful for ad-hoc testing)."
required: false
default: ""
color:
description: "Attachment color: good, warning, danger, or hex (e.g. #36a64f)."
required: false
default: "good"
header:
description: "Header text for the notification (required)."
required: true
blocks:
description: "YAML array of block objects with title, value, url (optional), is_code (optional). Omit to send the header alone."
required: false
default: ""

runs:
using: composite
steps:
- name: 🔄 Convert YAML to JSON
id: convert
if: ${{ inputs.blocks != '' }}
shell: bash
env:
BLOCKS_YAML: ${{ inputs.blocks }}
YQ_VERSION: "4.53.2"
YQ_SHA256: "d56bf5c6819e8e696340c312bd70f849dc1678a7cda9c2ad63eebd906371d56b"
run: |
set -eu
# Pinned + checksum-verified yq install. Keep in sync with .github/actions/install-yq
# (we inline here so this action stays self-contained for external callers).
URL="https://github.com/mikefarah/yq/releases/download/v${YQ_VERSION}/yq_linux_amd64"
curl -fsSL -o /tmp/yq "$URL"
echo "${YQ_SHA256} /tmp/yq" | sha256sum -c -
chmod +x /tmp/yq
BLOCKS_JSON=$(/tmp/yq -o=json '.' <<< "$BLOCKS_YAML")
echo "blocks_json<<EOF" >> $GITHUB_OUTPUT
echo "$BLOCKS_JSON" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: 💬 Send Slack notification
uses: actions/github-script@v9
env:
# Webhook precedence: SLACK_WEBHOOK_URL env var wins (typically set at
# job level from secrets.CI_SLACK_WEBHOOK — composite actions can't read
# org secrets directly). The `slack_webhook_url` input is a fallback for
# ad-hoc testing or direct invocation.
WEBHOOK_INPUT: ${{ inputs.slack_webhook_url }}
COLOR: ${{ inputs.color }}
HEADER: ${{ inputs.header }}
BLOCKS_JSON: ${{ steps.convert.outputs.blocks_json }}
with:
script: |
const webhook = process.env.SLACK_WEBHOOK_URL || process.env.WEBHOOK_INPUT;
if (!webhook) {
throw new Error(
"slack-notify: no webhook URL. Set SLACK_WEBHOOK_URL env in the " +
"job (from secrets.CI_SLACK_WEBHOOK) or pass `slack_webhook_url` input."
);
}

const blocksJson = process.env.BLOCKS_JSON;
const blocks = blocksJson ? JSON.parse(blocksJson) : [];

// Build single text with all blocks as bullet points
const lines = blocks.map(block => {
let line = `• *${block.title}:* `;

if (block.is_code) {
line += `\n\`\`\`${block.value}\`\`\``;
} else if (block.url) {
line += `<${block.url}|${block.value}>`;
} else {
line += block.value;
}

return line;
});

const text = lines.join('\n');

// Build complete payload. `text` (top-level) and `fallback` (attachment) drive
// Slack's notification preview / link-unfurl summary; without them the unfurl
// shows "[no preview available]". Top-level `text` also renders above the
// attachment, so we drop the in-attachment header block to avoid duplication.
const attachment = {
color: process.env.COLOR,
fallback: process.env.HEADER,
};
if (text) {
attachment.blocks = [
{ type: "section", text: { type: "mrkdwn", text: text } }
];
}
const payload = {
text: process.env.HEADER,
attachments: [attachment],
};

// Send to Slack
const response = await fetch(webhook, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});

if (!response.ok) {
const text = await response.text();
throw new Error(`Slack webhook failed: ${response.statusText} - ${text}`);
}

console.log('✅ Slack notification sent successfully');
57 changes: 57 additions & 0 deletions .github/workflows/slack-notify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Slack notify (reusable)

# Thin wrapper around .github/actions/slack-notify. The composite action can't
# read org secrets directly, so this reusable workflow pulls
# secrets.CI_SLACK_WEBHOOK and passes it through. Two use cases:
#
# 1. Manual testing of the slack-notify action via the Actions tab
# (workflow_dispatch).
# 2. Callers that prefer `secrets: inherit` over setting SLACK_WEBHOOK_URL
# at job level. Note the trade-off: each call spins up its own runner.
# For inline notifications inside an existing job, use the composite
# action directly.

on:
workflow_call:
inputs:
header:
type: string
required: true
color:
type: string
required: false
default: "good"
blocks:
type: string
required: false
default: ""
workflow_dispatch:
inputs:
header:
type: string
required: true
default: "Test notification from slack-notify"
color:
type: string
required: false
default: "good"
blocks:
type: string
required: false
default: ""

jobs:
send:
runs-on: ubuntu-latest
steps:
# Composite action is pinned to @main — `actions/checkout` in a reusable
# workflow checks out the *caller's* repo, so we can't use a `./` path
# without an extra clone of jitsucom/github-workflows. If this wrapper
# ever ships in tagged releases, bump this @ref alongside the tag.
- uses: jitsucom/github-workflows/.github/actions/slack-notify@main
env:
SLACK_WEBHOOK_URL: ${{ secrets.CI_SLACK_WEBHOOK }}
with:
header: ${{ inputs.header }}
color: ${{ inputs.color }}
blocks: ${{ inputs.blocks }}
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,75 @@ Use the `review_instructions` input to focus the review on what matters for your
#### Updating

All consuming repos pick up changes automatically on the next run — no changes needed per repo.

## Composite actions

Reusable composite actions live under `.github/actions/`. Consume them by path:

```yaml
- uses: jitsucom/github-workflows/.github/actions/<name>@<tag-or-main>
```

### `slack-notify` — Slack webhook notification

Sends a formatted notification to Slack with title + optional bullet blocks.
Used by the deploy workflows.

Inputs:

- `header` — required.
- `color` — optional, defaults to `good` (green). Accepts `good`, `warning`,
`danger`, or a hex color like `#36a64f`.
- `blocks` — optional. YAML array of block objects with `title`, `value`,
`url` (optional), `is_code` (optional). Omit to send the header alone.
- `slack_webhook_url` — optional override. Normally leave empty; the action
reads `SLACK_WEBHOOK_URL` env first (see below) and only uses this input as
a fallback, mainly for ad-hoc testing.

The composite action can't read org secrets directly. Standard pattern: set
`SLACK_WEBHOOK_URL` once at the job level from `secrets.CI_SLACK_WEBHOOK`.

```yaml
jobs:
notify:
env:
SLACK_WEBHOOK_URL: ${{ secrets.CI_SLACK_WEBHOOK }}
steps:
- uses: jitsucom/github-workflows/.github/actions/slack-notify@main
with:
header: "Deploy started"
```

See [`action.yml`](.github/actions/slack-notify/action.yml).

#### Reusable-workflow wrapper

For callers that prefer `secrets: inherit` over wiring the env var, or for
testing the action directly from the GitHub UI (`workflow_dispatch`), there's
a thin wrapper at [`.github/workflows/slack-notify.yml`](.github/workflows/slack-notify.yml):

```yaml
jobs:
notify:
uses: jitsucom/github-workflows/.github/workflows/slack-notify.yml@main
secrets: inherit
with:
header: "Deploy started"
```

Trade-off: each invocation runs as its own job on a fresh runner (~30–60s
startup) and can't share workspace state with sibling steps. For inline
notifications inside an existing deploy job, use the composite action directly.

### `install-yq` — Install the yq CLI

Installs `mikefarah/yq` to `/usr/local/bin` with a pinned version + sha256
checksum. Inputs: `version`, `sha256` (both have safe defaults). See
[`action.yml`](.github/actions/install-yq/action.yml).

### `install-kustomize` — Install the kustomize CLI

Installs the standalone kustomize CLI with a pinned version + sha256 checksum.
Inputs: `version`, `sha256` (both have safe defaults). See
[`action.yml`](.github/actions/install-kustomize/action.yml).

Loading