Skip to content
Closed
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
78 changes: 73 additions & 5 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,87 @@ on:
type: boolean
default: true

permissions:
contents: read

concurrency:
group: ksefnik-docs
cancel-in-progress: false

env:
IMAGE: ghcr.io/codeformers-it/ksefnik-docs
PLATFORM: linux/amd64

jobs:
build:
name: Build & push image
runs-on: [self-hosted, codeformers]
timeout-minutes: 20
permissions:
contents: read
packages: write
outputs:
sha: ${{ steps.meta.outputs.sha }}

steps:
- uses: actions/checkout@v4

- name: Build & push Docker image
run: ./scripts/build-and-push.sh
- id: meta
run: echo "sha=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"

- name: Log in to GHCR
run: echo '${{ secrets.GITHUB_TOKEN }}' | docker login ghcr.io -u '${{ github.actor }}' --password-stdin

- name: Ensure buildx builder exists
run: |
docker buildx inspect ksefnik-builder >/dev/null 2>&1 \
|| docker buildx create --name ksefnik-builder --driver docker-container --bootstrap

- name: Build & push
env:
PRO_REPO_TOKEN: ${{ secrets.PRO_REPO_TOKEN }}
run: |
BUILD_ARGS=""
if [ -n "$PRO_REPO_TOKEN" ]; then
BUILD_ARGS="--build-arg PRO_REPO_TOKEN=${PRO_REPO_TOKEN}"
fi
docker buildx build \
--builder ksefnik-builder \
--platform "${PLATFORM}" \
--push \
-f docker/docs.Dockerfile \
-t "${IMAGE}:latest" \
-t "${IMAGE}:${{ steps.meta.outputs.sha }}" \
$BUILD_ARGS \
.

- name: Logout from GHCR
if: always()
run: docker logout ghcr.io || true

deploy:
name: Deploy docs on VPS
needs: build
if: github.event_name == 'workflow_dispatch' && inputs.deploy
runs-on: [self-hosted, codeformers]
runs-on: [self-hosted, vps-codeformers]
timeout-minutes: 5

steps:
- name: Deploy to VPS
run: ssh codeformers "cd /home/ubuntu/ksefnik.pl && ./scripts/deploy.sh"
- uses: actions/checkout@v4

- name: Sync compose + deploy script into deploy dir
run: |
set -e
DEPLOY_DIR=/home/ubuntu/ksefnik.pl
install -m 644 docker-compose.prod.yml "${DEPLOY_DIR}/docker-compose.prod.yml"
install -m 755 scripts/deploy.sh "${DEPLOY_DIR}/scripts/deploy.sh"

- name: Run deploy.sh
run: /home/ubuntu/ksefnik.pl/scripts/deploy.sh

- name: Smoke test
run: |
set -e
status=$(curl -sS -o /dev/null -w '%{http_code}' -m 10 http://127.0.0.1:7020/)
echo "http://127.0.0.1:7020/ -> HTTP ${status}"
[ "${status}" = "200" ] || exit 1
196 changes: 163 additions & 33 deletions .github/workflows/prod-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,53 @@ name: Release to npm (prod)

# Merging into prod bumps the minor version of all @ksefnik/* packages,
# builds everything, publishes to npm with provenance, tags the release,
# and opens a sync PR back to main.
# builds the docs Docker image, deploys it to the VPS, and opens a sync PR
# back to main.
#
# The merge to prod IS the "I want to release" decision.
# Only @luke-cf can approve and merge PRs to prod.
#
# Pipeline shape (4 atomic jobs + sync-back):
# verify - install/build/typecheck/test on the current SHA; uploads dist artifacts.
# Safe to re-run - mutates nothing.
# publish-npm - downloads dist, bumps versions, publishes to npm, commits + tags + GH release.
# After this job the new vX.Y.Z tag exists on origin/prod.
# build-docs - checks out the v${VERSION} tag, builds the docs Docker image, pushes to GHCR.
# Re-runnable: same tag → same image content; tag overwrite is safe.
# deploy - pulls the new image on the VPS, recreates the container, smoke-tests.
# Re-runnable: just pulls latest again.
# sync-back - opens a PR prod → main so version bumps land back in main.

on:
push:
branches: [prod]
workflow_dispatch:
inputs:
dry_run:
description: 'Verify only (no npm publish / git tag / docker push / deploy)'
type: boolean
default: true

permissions:
contents: write
id-token: write
pull-requests: write
contents: read

concurrency:
group: ksefnik-release
cancel-in-progress: false

env:
IMAGE: ghcr.io/codeformers-it/ksefnik-docs
PLATFORM: linux/amd64

jobs:
release:
verify:
name: Verify (install / build / typecheck / test)
if: github.actor != 'github-actions[bot]'
runs-on: [self-hosted, codeformers]
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0

- uses: pnpm/action-setup@v4
Expand All @@ -32,7 +57,6 @@ jobs:
with:
node-version: 22
cache: pnpm
registry-url: https://registry.npmjs.org

- run: pnpm install --frozen-lockfile

Expand All @@ -47,10 +71,61 @@ jobs:
env:
CI: 'true'

- name: Upload dist artifacts
# publish-npm consumes these instead of rebuilding from scratch.
uses: actions/upload-artifact@v4
with:
name: packages-dist
path: packages/*/dist
if-no-files-found: error
retention-days: 1

publish-npm:
name: Publish to npm + tag release
needs: verify
# Skip when dispatched manually with dry_run=true. push:prod events have no
# 'inputs' object, so the check evaluates to false and the job runs as normal.
if: github.event_name != 'workflow_dispatch' || inputs.dry_run == false
runs-on: [self-hosted, codeformers]
timeout-minutes: 15
permissions:
contents: write
id-token: write
outputs:
version: ${{ steps.tag.outputs.version }}
sha: ${{ steps.tag.outputs.sha }}
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
registry-url: https://registry.npmjs.org

# We need node_modules so pnpm publish can resolve workspace:* refs into
# concrete versions when it rewrites package.json files on publish.
- run: pnpm install --frozen-lockfile

- name: Download dist artifacts
uses: actions/download-artifact@v4
with:
name: packages-dist
path: packages

- name: Bump minor versions in all packages
run: node scripts/bump-version.mjs minor

- name: Publish to npm
# --no-git-checks lets pnpm publish from a dirty tree (we just bumped versions).
# If a partial publish fails mid-way (some packages on npm, some not), the next
# rerun will skip already-published versions thanks to npm's own dedup, but the
# version bump commit will not be in git yet - rerun is safe.
run: pnpm -r publish --access public --provenance --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Expand All @@ -69,6 +144,7 @@ jobs:
git tag -f "v$VERSION"
git push origin prod
git push origin "v$VERSION" --force
echo "sha=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
Expand All @@ -77,47 +153,101 @@ jobs:
name: v${{ steps.tag.outputs.version }}
generate_release_notes: true

- name: Build docs Docker image (tagged with npm version)
build-docs:
name: Build & push docs Docker image
needs: publish-npm
runs-on: [self-hosted, codeformers]
timeout-minutes: 20
permissions:
contents: read
packages: write
steps:
- name: Checkout published tag
# Building from the exact tag publish-npm just pushed guarantees the docs image
# version matches the npm release. If this job is rerun, the same tag is checked
# out and the same image content is produced.
uses: actions/checkout@v4
with:
ref: v${{ needs.publish-npm.outputs.version }}

- name: Log in to GHCR
run: echo '${{ secrets.GITHUB_TOKEN }}' | docker login ghcr.io -u '${{ github.actor }}' --password-stdin

- name: Ensure buildx builder exists
run: |
VERSION="${{ steps.tag.outputs.version }}"
GIT_SHA=$(git rev-parse --short HEAD)

DOCKER_CONFIG_DIR="$(mktemp -d)"
trap 'rm -rf "$DOCKER_CONFIG_DIR"' EXIT
AUTH_TOKEN=$(printf 'codeformers-it:%s' "${{ secrets.REPO_TOKEN_GITHUB_PAT }}" | base64)
printf '{"auths":{"ghcr.io":{"auth":"%s"}},"credsStore":""}' "$AUTH_TOKEN" > "$DOCKER_CONFIG_DIR/config.json"
if [ -d "$HOME/.docker/cli-plugins" ]; then
ln -sf "$HOME/.docker/cli-plugins" "$DOCKER_CONFIG_DIR/cli-plugins"
fi
export DOCKER_CONFIG="$DOCKER_CONFIG_DIR"
docker buildx inspect ksefnik-builder >/dev/null 2>&1 \
|| docker buildx create --name ksefnik-builder --driver docker-container --bootstrap

- name: Build & push
env:
PRO_REPO_TOKEN: ${{ secrets.PRO_REPO_TOKEN }}
run: |
BUILD_ARGS=""
if [ -n "${PRO_REPO_TOKEN:-}" ]; then
if [ -n "$PRO_REPO_TOKEN" ]; then
BUILD_ARGS="--build-arg PRO_REPO_TOKEN=${PRO_REPO_TOKEN}"
fi

docker buildx build \
--platform linux/amd64 \
--builder ksefnik-builder \
--platform "${PLATFORM}" \
--push \
-f docker/docs.Dockerfile \
-t "ghcr.io/codeformers-it/ksefnik-docs:latest" \
-t "ghcr.io/codeformers-it/ksefnik-docs:v${VERSION}" \
-t "ghcr.io/codeformers-it/ksefnik-docs:${GIT_SHA}" \
-t "${IMAGE}:latest" \
-t "${IMAGE}:v${{ needs.publish-npm.outputs.version }}" \
-t "${IMAGE}:${{ needs.publish-npm.outputs.sha }}" \
$BUILD_ARGS \
.
env:
PRO_REPO_TOKEN: ${{ secrets.PRO_REPO_TOKEN }}

- name: Deploy docs to VPS
run: ssh codeformers "cd /home/ubuntu/ksefnik.pl && ./scripts/deploy.sh"
- name: Logout from GHCR
if: always()
run: docker logout ghcr.io || true

deploy:
name: Deploy docs on VPS
needs: [publish-npm, build-docs]
runs-on: [self-hosted, vps-codeformers]
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
with:
ref: v${{ needs.publish-npm.outputs.version }}

- name: Sync compose + deploy script into deploy dir
run: |
set -e
DEPLOY_DIR=/home/ubuntu/ksefnik.pl
install -m 644 docker-compose.prod.yml "${DEPLOY_DIR}/docker-compose.prod.yml"
install -m 755 scripts/deploy.sh "${DEPLOY_DIR}/scripts/deploy.sh"

- name: Run deploy.sh
run: /home/ubuntu/ksefnik.pl/scripts/deploy.sh

- name: Smoke test
run: |
set -e
status=$(curl -sS -o /dev/null -w '%{http_code}' -m 10 http://127.0.0.1:7020/)
echo "http://127.0.0.1:7020/ -> HTTP ${status}"
[ "${status}" = "200" ] || exit 1

sync-back:
name: Open sync PR prod → main
needs: publish-npm
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Open sync PR prod → main
- name: Create sync PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ needs.publish-npm.outputs.version }}
run: |
gh pr create \
--base main \
--head prod \
--title "chore: sync versions from prod v${{ steps.tag.outputs.version }}" \
--body "Automated version sync after npm release of v${{ steps.tag.outputs.version }}. Merge to bring main in line with published versions." \
|| echo "sync PR already exists or no diff skipping"
--title "chore: sync versions from prod v${VERSION}" \
--body "Automated version sync after npm release of v${VERSION}. Merge to bring main in line with published versions." \
|| echo "sync PR already exists or no diff - skipping"
1 change: 1 addition & 0 deletions packages/core/src/ksef/ksef.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class KsefAdapterImpl implements KsefAdapter {
subjectType,
pageSize: opts.pageSize,
pageOffset: opts.pageOffset,
includeXml: opts.includeXml,
})

return result.invoices.map((raw) => ({
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/ksef/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export interface KsefClient {
subjectType?: KsefSubjectType
pageSize?: number
pageOffset?: number
includeXml?: boolean
}): Promise<{ invoices: KsefRawInvoice[]; total: number }>
sendInvoice(params: {
token: string
Expand Down
Loading