Feat: add actions for secure rebuild ds images
This commit is contained in:
parent
1a21bc0154
commit
3cdec1156e
7 changed files with 1338 additions and 321 deletions
218
.github/scripts/update-secure-rebuild-config.sh
vendored
Normal file
218
.github/scripts/update-secure-rebuild-config.sh
vendored
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
#!/usr/bin/env bash
|
||||
# Usage:
|
||||
# update-secure-rebuild-config.sh \
|
||||
# <source_tag> \
|
||||
# <release_tag> \
|
||||
# <release_number> \
|
||||
# <editions_json>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -ne 4 ]; then
|
||||
echo "Usage: $0 <source_tag> <release_tag> <release_number> <editions_json>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
SOURCE_TAG="$1"
|
||||
RELEASE_TAG="$2"
|
||||
RELEASE_NUMBER="$3"
|
||||
EDITIONS_JSON="$4"
|
||||
|
||||
CONFIG="${CONFIG:-.github/secure-rebuild.json}"
|
||||
|
||||
if ! [[ "${RELEASE_NUMBER}" =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo "Error: release_number must be a positive integer" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if ! [[ "${RELEASE_TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "Error: release_tag must have format X.Y.Z.N" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ ! -f "${CONFIG}" ]; then
|
||||
echo "Error: config not found: ${CONFIG}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Expected format:
|
||||
# {
|
||||
# "-ee": { "latest": true },
|
||||
# "-de": { "latest": true }
|
||||
# }
|
||||
if ! jq -e '
|
||||
type == "object"
|
||||
and all(
|
||||
.[];
|
||||
type == "object"
|
||||
and has("latest")
|
||||
and (.latest | type == "boolean")
|
||||
)
|
||||
' >/dev/null 2>&1 <<< "${EDITIONS_JSON}"; then
|
||||
echo 'Error: editions_json must look like {"-ee":{"latest":true}}' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if ! jq -e '
|
||||
type == "object"
|
||||
and (.releases | type == "object")
|
||||
' "${CONFIG}" >/dev/null 2>&1; then
|
||||
echo "Error: ${CONFIG} must contain a releases object" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# For release_tag 9.6.1.1:
|
||||
# MINOR = 9.6.1
|
||||
# SHORTER = 9.6
|
||||
# MAJOR = 9
|
||||
MINOR="${RELEASE_TAG%.*}"
|
||||
SHORTER="${MINOR%.*}"
|
||||
MAJOR="${RELEASE_TAG%%.*}"
|
||||
MAJOR_KEY="v${MAJOR}"
|
||||
|
||||
TMP_FILE="$(mktemp "${CONFIG}.tmp.XXXXXX")"
|
||||
|
||||
cleanup() {
|
||||
rm -f "${TMP_FILE}"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
jq \
|
||||
--arg key "${MAJOR_KEY}" \
|
||||
--arg src "${SOURCE_TAG}" \
|
||||
--arg rel "${RELEASE_TAG}" \
|
||||
--arg s1 "${MINOR}" \
|
||||
--arg s2 "${SHORTER}" \
|
||||
--argjson release_number "${RELEASE_NUMBER}" \
|
||||
--argjson eds "${EDITIONS_JSON}" \
|
||||
'
|
||||
# Strips latest only for editions present in the new $eds.
|
||||
# Uses exact key comparison via index() so that empty key ""
|
||||
# does not match "-ee" or "-de".
|
||||
def strip_latest_for_editions($eds):
|
||||
($eds | keys) as $edition_keys
|
||||
|
|
||||
map(
|
||||
.editions |= with_entries(
|
||||
.key as $edition_key
|
||||
|
|
||||
if ($edition_keys | index($edition_key)) != null
|
||||
then .value.latest = false
|
||||
else .
|
||||
end
|
||||
)
|
||||
);
|
||||
|
||||
# Removes a short tag by exact match.
|
||||
def remove_exact_short_tag($tag):
|
||||
map(
|
||||
.short_tags |= map(
|
||||
select(. != $tag)
|
||||
)
|
||||
);
|
||||
|
||||
# For subsequent rebuild releases, removes short tags
|
||||
# from the previous record of the same patch version.
|
||||
# Example: when adding 9.6.1.2, removes "9.6.1" and "9.6" from 9.6.1.1.
|
||||
def remove_patch_short_tags($s1; $s2):
|
||||
map(
|
||||
if (.short_tags | index($s1)) != null
|
||||
then
|
||||
.short_tags |= map(
|
||||
select(. != $s1 and . != $s2)
|
||||
)
|
||||
else
|
||||
.
|
||||
end
|
||||
);
|
||||
|
||||
def new_record($src; $rel; $s1; $s2; $eds):
|
||||
{
|
||||
source_tag: $src,
|
||||
release_tag: $rel,
|
||||
short_tags: [$s1, $s2],
|
||||
editions: $eds
|
||||
};
|
||||
|
||||
# Remember whether the major branch existed before changes.
|
||||
(.releases | has($key)) as $major_existed
|
||||
|
|
||||
|
||||
# Idempotency guard: remove any existing record with the same release_tag
|
||||
# to avoid duplicates on re-run.
|
||||
.releases |= with_entries(
|
||||
.value |= map(
|
||||
select(.release_tag != $rel)
|
||||
)
|
||||
)
|
||||
|
|
||||
|
||||
if $major_existed then
|
||||
|
||||
# Major branch already exists (e.g. v9).
|
||||
.releases[$key] = (
|
||||
.releases[$key]
|
||||
|
||||
# First release of a new minor (release_number == 1):
|
||||
# remove the short minor tag (e.g. "9.6") from the previous record.
|
||||
# Subsequent rebuild release:
|
||||
# remove both "9.6.1" and "9.6" from the previous record
|
||||
# of the same patch version.
|
||||
|
|
||||
if $release_number == 1
|
||||
then remove_exact_short_tag($s2)
|
||||
else remove_patch_short_tags($s1; $s2)
|
||||
end
|
||||
|
||||
# Strip latest only for editions included in the new release.
|
||||
|
|
||||
strip_latest_for_editions($eds)
|
||||
)
|
||||
|
|
||||
|
||||
# Prepend the new release record.
|
||||
.releases[$key] = [
|
||||
new_record($src; $rel; $s1; $s2; $eds)
|
||||
] + .releases[$key]
|
||||
|
||||
else
|
||||
|
||||
# A new major branch is being created (e.g. first v10).
|
||||
# Strip latest for the corresponding editions across all existing major branches.
|
||||
.releases |= with_entries(
|
||||
.value |= strip_latest_for_editions($eds)
|
||||
)
|
||||
|
|
||||
|
||||
.releases[$key] = [
|
||||
new_record($src; $rel; $s1; $s2; $eds)
|
||||
]
|
||||
|
|
||||
|
||||
# Keep only the two most recent major branches.
|
||||
.releases = (
|
||||
.releases
|
||||
| to_entries
|
||||
| sort_by(
|
||||
.key
|
||||
| ltrimstr("v")
|
||||
| tonumber
|
||||
)
|
||||
| reverse
|
||||
| .[0:2]
|
||||
| from_entries
|
||||
)
|
||||
|
||||
end
|
||||
' "${CONFIG}" > "${TMP_FILE}"
|
||||
|
||||
# Validate that the result is valid JSON.
|
||||
jq -e . "${TMP_FILE}" >/dev/null
|
||||
|
||||
mv "${TMP_FILE}" "${CONFIG}"
|
||||
trap - EXIT
|
||||
|
||||
echo "Updated ${CONFIG}:"
|
||||
cat "${CONFIG}"
|
||||
|
||||
195
.github/secure-rebuild.json
vendored
Normal file
195
.github/secure-rebuild.json
vendored
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
{
|
||||
"releases": {
|
||||
"v9": [
|
||||
{
|
||||
"source_tag": "9.4.1.15",
|
||||
"release_tag": "9.4.1.2",
|
||||
"short_tags": [
|
||||
"9.4.1",
|
||||
"9.4"
|
||||
],
|
||||
"editions": {
|
||||
"-ee": {
|
||||
"latest": true
|
||||
},
|
||||
"-de": {
|
||||
"latest": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_tag": "9.4.1.15",
|
||||
"release_tag": "9.4.1.1",
|
||||
"short_tags": [],
|
||||
"editions": {
|
||||
"-ee": {
|
||||
"latest": false
|
||||
},
|
||||
"-de": {
|
||||
"latest": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_tag": "9.4.0.129",
|
||||
"release_tag": "9.4.0.2",
|
||||
"short_tags": [
|
||||
"9.4.0"
|
||||
],
|
||||
"editions": {
|
||||
"-ee": {
|
||||
"latest": false
|
||||
},
|
||||
"-de": {
|
||||
"latest": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_tag": "9.4.0.129",
|
||||
"release_tag": "9.4.0.1",
|
||||
"short_tags": [],
|
||||
"editions": {
|
||||
"": {
|
||||
"latest": true
|
||||
},
|
||||
"-ee": {
|
||||
"latest": false
|
||||
},
|
||||
"-de": {
|
||||
"latest": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_tag": "9.3.1.10",
|
||||
"release_tag": "9.3.1.2",
|
||||
"short_tags": [
|
||||
"9.3.1",
|
||||
"9.3"
|
||||
],
|
||||
"editions": {
|
||||
"": {
|
||||
"latest": false
|
||||
},
|
||||
"-ee": {
|
||||
"latest": false
|
||||
},
|
||||
"-de": {
|
||||
"latest": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_tag": "9.3.1.8",
|
||||
"release_tag": "9.3.1.1",
|
||||
"short_tags": [],
|
||||
"editions": {
|
||||
"": {
|
||||
"latest": false
|
||||
},
|
||||
"-ee": {
|
||||
"latest": false
|
||||
},
|
||||
"-de": {
|
||||
"latest": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_tag": "9.3.0.138",
|
||||
"release_tag": "9.3.0.1",
|
||||
"short_tags": [
|
||||
"9.3.0"
|
||||
],
|
||||
"editions": {
|
||||
"": {
|
||||
"latest": false
|
||||
},
|
||||
"-ee": {
|
||||
"latest": false
|
||||
},
|
||||
"-de": {
|
||||
"latest": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_tag": "9.2.1.8",
|
||||
"release_tag": "9.2.1.1",
|
||||
"short_tags": [
|
||||
"9.2.1",
|
||||
"9.2"
|
||||
],
|
||||
"editions": {
|
||||
"": {
|
||||
"latest": false
|
||||
},
|
||||
"-ee": {
|
||||
"latest": false
|
||||
},
|
||||
"-de": {
|
||||
"latest": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_tag": "9.2.0.98",
|
||||
"release_tag": "9.2.0.1",
|
||||
"short_tags": [
|
||||
"9.2.0"
|
||||
],
|
||||
"editions": {
|
||||
"": {
|
||||
"latest": false
|
||||
},
|
||||
"-ee": {
|
||||
"latest": false
|
||||
},
|
||||
"-de": {
|
||||
"latest": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_tag": "9.1.0.168",
|
||||
"release_tag": "9.1.0.1",
|
||||
"short_tags": [
|
||||
"9.1.0",
|
||||
"9.1"
|
||||
],
|
||||
"editions": {
|
||||
"": {
|
||||
"latest": false
|
||||
},
|
||||
"-ee": {
|
||||
"latest": false
|
||||
},
|
||||
"-de": {
|
||||
"latest": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"source_tag": "9.0.4.50",
|
||||
"release_tag": "9.0.4.1",
|
||||
"short_tags": [
|
||||
"9.0.4",
|
||||
"9.0"
|
||||
],
|
||||
"editions": {
|
||||
"": {
|
||||
"latest": false
|
||||
},
|
||||
"-ee": {
|
||||
"latest": false
|
||||
},
|
||||
"-de": {
|
||||
"latest": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
224
.github/workflows/rebuild.yml
vendored
224
.github/workflows/rebuild.yml
vendored
|
|
@ -1,224 +0,0 @@
|
|||
---
|
||||
name: Rebuild Docker-Documentserver
|
||||
|
||||
run-name: >
|
||||
Rebuild DocumentServer with secure updates for repo: ${{ github.event.inputs.repo }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
repo:
|
||||
type: choice
|
||||
description: Please, choose upload repo..
|
||||
options:
|
||||
- '4test'
|
||||
- 'stable'
|
||||
|
||||
permissions:
|
||||
# All other permissions are set to none
|
||||
contents: read
|
||||
# Technically read access while waiting for images should be more than enough. However,
|
||||
# there is a bug in GitHub Actions/Packages and in case private repositories are used, you get a permission
|
||||
# denied error when attempting to just pull private image, changing the token permission to write solves the
|
||||
# issue. This is not dangerous, because if it is for "ONLYOFFICE/Docker-DocumentServer", only maintainers can use ds-rebuild.yaml
|
||||
# If it is for a fork, then the token is read-only anyway.
|
||||
packages: read
|
||||
|
||||
env:
|
||||
COMPANY_NAME: "onlyoffice"
|
||||
PRODUCT_NAME: "documentserver"
|
||||
REGISTRY_URL: "https://hub.docker.com/v2/repositories"
|
||||
|
||||
jobs:
|
||||
rebuild-info:
|
||||
name: "Rebuild-info"
|
||||
runs-on: "ubuntu-22.04"
|
||||
env:
|
||||
REPO_INPUTS: ${{ github.event.inputs.repo }}
|
||||
EVENT: ${{ github.event_name }}
|
||||
outputs:
|
||||
stable-versions: ${{ steps.selective-checks.outputs.stable-versions }}
|
||||
ucs-versions: ${{ steps.selective-checks.outputs.ucs-versions }}
|
||||
minor-tags: ${{ steps.selective-checks.outputs.minor-tags }}
|
||||
ucs-rebuild-condition: ${{ steps.selective-checks.outputs.ucs-rebuild-condition }}
|
||||
prefix-name: ${{ steps.selective-checks.outputs.prefix-name }}
|
||||
repo: ${{ steps.selective-checks.outputs.repo }}
|
||||
steps:
|
||||
- name: Selective checks
|
||||
id: selective-checks
|
||||
run: |
|
||||
set -e
|
||||
|
||||
REPO=${REPO_INPUTS:-"4test"}
|
||||
|
||||
if [ "${REPO}" == "stable" ]; then
|
||||
UCS_REBUILD=true
|
||||
UCS_VERSIONS=($(curl -s -H -X ${REGISTRY_URL}/${COMPANY_NAME}/${PRODUCT_NAME}-ucs/tags/?page_size=100 | \
|
||||
jq -r '.results|.[]|.name' | grep -oxE '[0-9]{1,}.[0-9]{1,}.[0-9]{1,}.1' || true))
|
||||
echo "ucs-versions=$(jq -c -n '$ARGS.positional' --args "${UCS_VERSIONS[@]}")" >> "$GITHUB_OUTPUT"
|
||||
elif
|
||||
[ "${REPO}" == "4test" ]; then
|
||||
UCS_REBUILD=false
|
||||
PREFIX_NAME=4testing-
|
||||
fi
|
||||
|
||||
STABLE_VERSIONS=($(curl -s -H -X ${REGISTRY_URL}/${COMPANY_NAME}/${PRODUCT_NAME}/tags/?page_size=100 | \
|
||||
jq -r '.results|.[]|.name' | grep -oxE '[0-9]{1,}.[0-9]{1,}.[0-9]{1,}.1' || true))
|
||||
|
||||
# When rebuilding stable versions of the document server,
|
||||
# it is necessary to determine the version from which the
|
||||
# minor x.x tag will need to be pushed.
|
||||
|
||||
VERSIONS=(${STABLE_VERSIONS[@]})
|
||||
for i in {1..10}; do
|
||||
if [ -z "${VERSIONS}" ]; then
|
||||
break
|
||||
else
|
||||
TEMPLATE=${VERSIONS[0]%.*.*}
|
||||
TEMPLATE_MINOR=$(printf -- '%s\n' "${VERSIONS[@]}" | grep -o -m 1 "${VERSIONS[0]%.*.*}.[0-9].[0-9]")
|
||||
MINOR_TAGS+=(${TEMPLATE_MINOR%.*})
|
||||
|
||||
for v in ${MINOR_TAGS[@]}; do
|
||||
VERSIONS=(${VERSIONS[@]//${v%.*}.*.*})
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Stable releases that will be rebuilded"
|
||||
echo "--------------------------------------"
|
||||
echo "${STABLE_VERSIONS[@]}"
|
||||
echo
|
||||
echo
|
||||
echo "Ucs releases that will be rebuilded"
|
||||
echo "-----------------------------------"
|
||||
echo "${UCS_VERSIONS[@]}"
|
||||
|
||||
echo "stable-versions=$(jq -c -n '$ARGS.positional' --args "${STABLE_VERSIONS[@]}")" >> "$GITHUB_OUTPUT"
|
||||
echo "minor-tags=${MINOR_TAGS[@]}" >> "$GITHUB_OUTPUT"
|
||||
echo "ucs-rebuild-condition=${UCS_REBUILD}" >> "$GITHUB_OUTPUT"
|
||||
echo "prefix-name=${PREFIX_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "repo=${REPO}" >> "$GITHUB_OUTPUT"
|
||||
shell: bash
|
||||
|
||||
re-build-stable:
|
||||
name: "Rebuild stable:${{ matrix.version }} ${{ matrix.edition }}"
|
||||
needs: [rebuild-info]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
type: ["stable"]
|
||||
edition: ["", "-ee", "-de"]
|
||||
version: ${{fromJSON(needs.rebuild-info.outputs.stable-versions)}}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
# Determines the new build number based
|
||||
# on data from the hub.docker registry
|
||||
- name: Declare release number
|
||||
id: release-number
|
||||
env:
|
||||
REBUILD_VERSION: ${{ matrix.version }}
|
||||
run: |
|
||||
MINOR_VERSION=${REBUILD_VERSION%.*}
|
||||
LAST_RELEASE=$(curl -s -H -X ${REGISTRY_URL}/${COMPANY_NAME}/${PRODUCT_NAME}/tags/?page_size=100 \
|
||||
| jq -r '.results|.[]|.name' | grep -Eo -m1 "${MINOR_VERSION}.[0-9]{1,}")
|
||||
LAST_RELEASE=${LAST_RELEASE#*.*.*.}
|
||||
echo "release-number=$((LAST_RELEASE+1))" >> "$GITHUB_OUTPUT"
|
||||
shell: bash
|
||||
# Note: Rebuilding images with an
|
||||
# extra layer to update security and
|
||||
# all dependencies. Update tags got +1 to previous release.
|
||||
- name: Re-build documentserver-stable
|
||||
env:
|
||||
MINOR_TAGS_ST: ${{ needs.rebuild-info.outputs.minor-tags }}
|
||||
VERSION: ${{ matrix.version }}
|
||||
RELEASE_NUMBER: ${{ steps.release-number.outputs.release-number }}
|
||||
PREFIX_NAME: ${{ needs.rebuild-info.outputs.prefix-name }}
|
||||
REPO: ${{ needs.rebuild-info.outputs.repo }}
|
||||
PRODUCT_EDITION: ${{ matrix.edition }}
|
||||
run: |
|
||||
set -eux
|
||||
export PULL_TAG=${VERSION}
|
||||
export TAG=${VERSION%.*}.${RELEASE_NUMBER}
|
||||
export SHORTER_TAG=${VERSION%.*}
|
||||
export SHORTEST_TAG=${VERSION%.*.*}
|
||||
|
||||
if [ "${REPO}" == "stable" ]; then
|
||||
MINOR_TAGS=(${MINOR_TAGS_ST})
|
||||
for v in ${MINOR_TAGS[@]}; do
|
||||
if [ "${SHORTER_TAG}" == "${v}" ]; then
|
||||
export PUSH_MAJOR="true"
|
||||
fi
|
||||
done
|
||||
if [ "${SHORTER_TAG}" == "${MINOR_TAGS[0]}" ]; then
|
||||
export LATEST="true"
|
||||
fi
|
||||
fi
|
||||
docker buildx bake -f docker-bake.hcl documentserver-stable-rebuild --push
|
||||
shell: bash
|
||||
re-build-ucs:
|
||||
name: "Rebuild ucs: ${{ matrix.version }} ${{ matrix.edition }}"
|
||||
if: needs.rebuild-info.outputs.ucs-rebuild-condition == 'true'
|
||||
needs: [rebuild-info]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
type: ["ucs"]
|
||||
edition: ["", "-ee"]
|
||||
version: ${{fromJSON(needs.rebuild-info.outputs.ucs-versions)}}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
# Determines the new build number based
|
||||
# on data from the hub.docker registry
|
||||
- name: Declare release number
|
||||
id: release-number
|
||||
env:
|
||||
REBUILD_VERSION: ${{ matrix.version }}
|
||||
run: |
|
||||
MINOR_VERSION=${REBUILD_VERSION%.*}
|
||||
LAST_RELEASE=$(curl -s -H -X ${REGISTRY_URL}/${COMPANY_NAME}/${PRODUCT_NAME}/tags/?page_size=100 \
|
||||
| jq -r '.results|.[]|.name' | grep -Eo -m1 "${MINOR_VERSION}.[0-9]{1,}")
|
||||
LAST_RELEASE=${LAST_RELEASE#*.*.*.}
|
||||
echo "release-number=$((LAST_RELEASE+1))" >> "$GITHUB_OUTPUT"
|
||||
shell: bash
|
||||
# Note: Rebuilding images with an
|
||||
# extra layer to update security and
|
||||
# all dependencies. Update tags +1 to previous release.
|
||||
- name: Re-build documentserver-ucs
|
||||
env:
|
||||
VERSION: ${{ matrix.version }}
|
||||
RELEASE_NUMBER: ${{ steps.release-number.outputs.release-number }}
|
||||
PRODUCT_EDITION: ${{ matrix.edition }}
|
||||
run: |
|
||||
set -eux
|
||||
export PULL_TAG=${VERSION}
|
||||
export TAG=${VERSION%.*}.${RELEASE_NUMBER}
|
||||
export SHORTER_TAG=${VERSION%.*}
|
||||
export SHORTEST_TAG=${VERSION%.*.*}
|
||||
|
||||
export UCS_REBUILD=true
|
||||
export UCS_PREFIX=-ucs
|
||||
|
||||
docker buildx bake -f docker-bake.hcl documentserver-stable-rebuild --push
|
||||
shell: bash
|
||||
565
.github/workflows/secure-rebuild.yml
vendored
Normal file
565
.github/workflows/secure-rebuild.yml
vendored
Normal file
|
|
@ -0,0 +1,565 @@
|
|||
name: Secure rebuild
|
||||
|
||||
run-name: >-
|
||||
Secure rebuild [
|
||||
${{ github.event_name == 'workflow_dispatch' && inputs.dry_run && 'DRY-RUN' || 'PUBLISH' }}
|
||||
]
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 9 1,15 * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
type: boolean
|
||||
description: "Dry run (build & test, do not retag release)"
|
||||
required: false
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
group: secure-rebuild
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
COMPANY_NAME: "onlyoffice"
|
||||
PRODUCT_NAME: "documentserver"
|
||||
REBUILD_CONFIG: ".github/secure-rebuild.json"
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
name: Prepare rebuild matrices
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
build_matrix: ${{ steps.matrix.outputs.build_matrix }}
|
||||
retag_matrix: ${{ steps.matrix.outputs.retag_matrix }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Validate rebuild config
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [ ! -f "${REBUILD_CONFIG}" ]; then
|
||||
echo "Rebuild config was not found: ${REBUILD_CONFIG}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
jq -e '
|
||||
type == "object"
|
||||
and (.releases | type == "object")
|
||||
and all(
|
||||
.releases[];
|
||||
type == "array"
|
||||
and all(
|
||||
.[];
|
||||
(.source_tag | type == "string")
|
||||
and (.source_tag | length > 0)
|
||||
and (.release_tag | type == "string")
|
||||
and (.release_tag | length > 0)
|
||||
and (.short_tags | type == "array")
|
||||
and all(.short_tags[]; type == "string")
|
||||
and (.editions | type == "object")
|
||||
and (.editions | length > 0)
|
||||
and all(
|
||||
.editions[];
|
||||
type == "object"
|
||||
and has("latest")
|
||||
and (.latest | type == "boolean")
|
||||
)
|
||||
)
|
||||
)
|
||||
' "${REBUILD_CONFIG}" >/dev/null
|
||||
|
||||
- name: Create build and retag matrices
|
||||
id: matrix
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
BUILD="$(jq -c '
|
||||
{
|
||||
include: (
|
||||
[
|
||||
.releases
|
||||
| to_entries[]
|
||||
| .value[]
|
||||
| . as $release
|
||||
| $release.editions
|
||||
| to_entries[]
|
||||
| {
|
||||
source_tag: $release.source_tag,
|
||||
test_tag: (
|
||||
$release.release_tag
|
||||
| split(".")
|
||||
| .[0:3]
|
||||
| join(".")
|
||||
),
|
||||
edition: .key
|
||||
}
|
||||
]
|
||||
| unique_by([
|
||||
.source_tag,
|
||||
.edition
|
||||
])
|
||||
)
|
||||
}
|
||||
' "${REBUILD_CONFIG}")"
|
||||
|
||||
RETAG="$(jq -c '
|
||||
{
|
||||
include: [
|
||||
.releases
|
||||
| to_entries[]
|
||||
| .value[]
|
||||
| . as $release
|
||||
| $release.editions
|
||||
| to_entries[]
|
||||
| {
|
||||
source_tag: $release.source_tag,
|
||||
release_tag: $release.release_tag,
|
||||
short_tags: (
|
||||
$release.short_tags
|
||||
| join(",")
|
||||
),
|
||||
is_latest: (
|
||||
.value.latest
|
||||
| tostring
|
||||
),
|
||||
edition: .key
|
||||
}
|
||||
]
|
||||
}
|
||||
' "${REBUILD_CONFIG}")"
|
||||
|
||||
BUILD_COUNT="$(jq '.include | length' <<< "${BUILD}")"
|
||||
RETAG_COUNT="$(jq '.include | length' <<< "${RETAG}")"
|
||||
|
||||
if [ "${BUILD_COUNT}" -eq 0 ]; then
|
||||
echo "Build matrix is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "${RETAG_COUNT}" -eq 0 ]; then
|
||||
echo "Retag matrix is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Build matrix contains ${BUILD_COUNT} entries:"
|
||||
jq . <<< "${BUILD}"
|
||||
|
||||
echo "Retag matrix contains ${RETAG_COUNT} entries:"
|
||||
jq . <<< "${RETAG}"
|
||||
|
||||
echo "build_matrix=${BUILD}" >> "${GITHUB_OUTPUT}"
|
||||
echo "retag_matrix=${RETAG}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
rebuild:
|
||||
name: "Rebuild ${{ matrix.source_tag }}${{ matrix.edition }}"
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJSON(needs.prepare.outputs.build_matrix) }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: "20"
|
||||
|
||||
- name: Checkout tests
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
TEST_TAG: ${{ matrix.test_tag }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
git clone \
|
||||
"https://${GITEA_TOKEN}@git.onlyoffice.com/ONLYOFFICE-QA/Dep.Tests.git" \
|
||||
dep-tests
|
||||
|
||||
cd dep-tests
|
||||
|
||||
if git checkout "refs/tags/${TEST_TAG}" 2>/dev/null; then
|
||||
echo "Checked out test tag ${TEST_TAG}"
|
||||
else
|
||||
echo "Test tag ${TEST_TAG} was not found" >&2
|
||||
echo "Using fallback commit aa2d423f18" >&2
|
||||
git checkout aa2d423f18
|
||||
fi
|
||||
|
||||
df -h
|
||||
|
||||
- name: Initialize submodules
|
||||
working-directory: dep-tests
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
git config --global \
|
||||
url."https://${GITHUB_TOKEN}@github.com/".insteadOf \
|
||||
"https://github.com/"
|
||||
|
||||
git submodule update \
|
||||
--init
|
||||
|
||||
git config --global --unset url."https://${GITHUB_TOKEN}@github.com/".insteadOf
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: dep-tests/puppeteer/engine
|
||||
run: |
|
||||
df -h
|
||||
npm install --silent --no-progress > /dev/null 2>&1 || {
|
||||
echo "Dependency installation failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
- name: mv Dep.Tests under context
|
||||
run: |
|
||||
mv dep-tests ../
|
||||
|
||||
- name: Build secure-patched image
|
||||
id: build
|
||||
env:
|
||||
SOURCE_TAG: ${{ matrix.source_tag }}
|
||||
EDITION: ${{ matrix.edition }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
SOURCE_IMAGE="${COMPANY_NAME}/4testing-${PRODUCT_NAME}${EDITION}:${SOURCE_TAG}"
|
||||
PATCHED_IMAGE="${COMPANY_NAME}/4testing-${PRODUCT_NAME}${EDITION}:${SOURCE_TAG}-secure"
|
||||
|
||||
echo "Source image: ${SOURCE_IMAGE}"
|
||||
echo "Patched image: ${PATCHED_IMAGE}"
|
||||
|
||||
docker buildx imagetools inspect "${SOURCE_IMAGE}"
|
||||
|
||||
docker buildx build \
|
||||
--platform "linux/amd64,linux/arm64" \
|
||||
--build-arg "SOURCE_IMAGE=${SOURCE_IMAGE}" \
|
||||
--tag "${PATCHED_IMAGE}" \
|
||||
--file Dockerfile.secure \
|
||||
--push \
|
||||
.
|
||||
|
||||
echo "patched_image=${PATCHED_IMAGE}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Return dep.tests
|
||||
run: |
|
||||
mv ../dep-tests ./
|
||||
|
||||
- name: Clean-up
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
docker rm --force documentserver 2>/dev/null || true
|
||||
docker buildx prune -af
|
||||
docker image prune -af
|
||||
docker builder prune -af
|
||||
|
||||
- name: Pull test image
|
||||
env:
|
||||
IMAGE: ${{ steps.build.outputs.patched_image }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
for attempt in $(seq 1 30); do
|
||||
if docker pull "${IMAGE}"; then
|
||||
echo "Image pulled successfully"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "${attempt}" -eq 30 ]; then
|
||||
echo "Image was not available after 5 minutes" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Image is not available yet, retrying in 10 seconds"
|
||||
sleep 10
|
||||
done
|
||||
|
||||
- name: Start test container
|
||||
env:
|
||||
IMAGE: ${{ steps.build.outputs.patched_image }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
docker run \
|
||||
--detach \
|
||||
--publish 80:80 \
|
||||
--name documentserver \
|
||||
"${IMAGE}"
|
||||
|
||||
- name: Wait for healthcheck
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
for attempt in $(seq 1 30); do
|
||||
RESULT="$(
|
||||
curl \
|
||||
--fail \
|
||||
--silent \
|
||||
--show-error \
|
||||
http://localhost/healthcheck \
|
||||
2>/dev/null \
|
||||
|| true
|
||||
)"
|
||||
|
||||
echo "Healthcheck response: ${RESULT}"
|
||||
|
||||
if [ "${RESULT}" = "true" ]; then
|
||||
echo "Healthcheck passed"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "${attempt}" -eq 30 ]; then
|
||||
echo "Healthcheck failed after 5 minutes" >&2
|
||||
docker logs documentserver || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sleep 10
|
||||
done
|
||||
|
||||
- name: Start example service
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
docker exec documentserver \
|
||||
supervisorctl start ds:example
|
||||
|
||||
for attempt in $(seq 1 30); do
|
||||
if curl \
|
||||
--fail \
|
||||
--silent \
|
||||
--show-error \
|
||||
http://localhost/example/ \
|
||||
>/dev/null 2>&1; then
|
||||
echo "Example service is ready"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "${attempt}" -eq 30 ]; then
|
||||
echo "Example service did not become ready" >&2
|
||||
docker logs documentserver || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sleep 5
|
||||
done
|
||||
|
||||
- name: Start Xvfb
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
Xvfb :99 \
|
||||
-screen 0 1920x1080x24 \
|
||||
>"${RUNNER_TEMP}/xvfb.log" \
|
||||
2>&1 &
|
||||
|
||||
sleep 60
|
||||
echo "DISPLAY=:99" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Install Chrome
|
||||
uses: browser-actions/setup-chrome@v2
|
||||
with:
|
||||
chrome-version: "120"
|
||||
|
||||
- name: Run tests with retryes
|
||||
uses: nick-fields/retry@v4
|
||||
with:
|
||||
timeout_minutes: 20
|
||||
max_attempts: 3
|
||||
retry_wait_seconds: 120
|
||||
command: |
|
||||
set -uo pipefail
|
||||
cd dep-tests/puppeteer/engine
|
||||
OUTPUT_FILE="../test-output.log"
|
||||
RESULT=0
|
||||
|
||||
: > "${OUTPUT_FILE}"
|
||||
|
||||
tests=(
|
||||
"tests/desktop/word/smoke/open-close-file-tab.js"
|
||||
"tests/desktop/word/smoke/check_search_while_drawing.js"
|
||||
"tests/desktop/spreadsheets/smoke/check_replace_all.js"
|
||||
"tests/desktop/spreadsheets/smoke/data_entry_use_tab_button.js"
|
||||
"tests/desktop/pdf/smoke/check_find_for_a_specific_pdf.js"
|
||||
"tests/desktop/pdf/smoke/check_undo_after_add_text_comment.js"
|
||||
)
|
||||
|
||||
for test in "${tests[@]}"; do
|
||||
echo "Running ${test}" \
|
||||
| tee -a "${OUTPUT_FILE}"
|
||||
|
||||
python3 run.py \
|
||||
--config config_chrome_linux.json \
|
||||
"${test}" \
|
||||
--config_params \
|
||||
"runOptions.disableAnimation=true" \
|
||||
"testOptions.url=http://localhost/example" \
|
||||
'puppeteerOptions.browserArgs.args=["--no-sandbox","--disable-setuid-sandbox"]' \
|
||||
2>&1 \
|
||||
| tee -a "${OUTPUT_FILE}"
|
||||
|
||||
TEST_RESULT="${PIPESTATUS[0]}"
|
||||
|
||||
if [ "${TEST_RESULT}" -ne 0 ]; then
|
||||
echo "Test failed with exit code ${TEST_RESULT}: ${test}" \
|
||||
| tee -a "${OUTPUT_FILE}"
|
||||
RESULT=1
|
||||
fi
|
||||
done
|
||||
|
||||
exit "${RESULT}"
|
||||
|
||||
- name: Print test output
|
||||
if: always()
|
||||
run: |
|
||||
if [ -f dep-tests/puppeteer/test-output.log ]; then
|
||||
cat dep-tests/puppeteer/test-output.log
|
||||
else
|
||||
echo "Test output file was not created"
|
||||
fi
|
||||
|
||||
- name: Print test reports
|
||||
if: always()
|
||||
run: |
|
||||
if [ ! -d dep-tests/puppeteer/out ]; then
|
||||
echo "Test report directory was not created"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
find dep-tests/puppeteer/out \
|
||||
-type f \
|
||||
-name "report*.html" \
|
||||
-print0 \
|
||||
| sort -z \
|
||||
| while IFS= read -r -d "" report; do
|
||||
echo "=== ${report} ==="
|
||||
cat "${report}"
|
||||
echo
|
||||
done
|
||||
|
||||
- name: Upload test artifacts
|
||||
if: ${{ false }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: smoke-tests-${{ matrix.source_tag }}${{ matrix.edition }}
|
||||
path: |
|
||||
dep-tests/puppeteer/test-output.log
|
||||
dep-tests/puppeteer/out/
|
||||
if-no-files-found: warn
|
||||
retention-days: 14
|
||||
|
||||
- name: Remove test container
|
||||
if: ${{ false }}
|
||||
run: |
|
||||
docker rm \
|
||||
--force \
|
||||
documentserver \
|
||||
2>/dev/null \
|
||||
|| true
|
||||
|
||||
retag:
|
||||
name: "Retag ${{ matrix.release_tag }}${{ matrix.edition }}"
|
||||
needs:
|
||||
- prepare
|
||||
- rebuild
|
||||
|
||||
if: >-
|
||||
${{
|
||||
github.event_name != 'workflow_dispatch'
|
||||
|| !inputs.dry_run
|
||||
}}
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJSON(needs.prepare.outputs.retag_matrix) }}
|
||||
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Create multi-arch manifest with release tags
|
||||
uses: nick-fields/retry@v4
|
||||
env:
|
||||
SOURCE_TAG: ${{ matrix.source_tag }}
|
||||
RELEASE_TAG: ${{ matrix.release_tag }}
|
||||
SHORT_TAGS: ${{ matrix.short_tags }}
|
||||
IS_LATEST: ${{ matrix.is_latest }}
|
||||
EDITION: ${{ matrix.edition }}
|
||||
PRODUCT_NAME: ${{ env.PRODUCT_NAME }}
|
||||
with:
|
||||
timeout_minutes: 20
|
||||
max_attempts: 3
|
||||
retry_wait_seconds: 60
|
||||
command: |
|
||||
set -euo pipefail
|
||||
|
||||
RELEASE_IMAGE="${COMPANY_NAME}/${PRODUCT_NAME}${EDITION}"
|
||||
SRC_IMAGE="${COMPANY_NAME}/4testing-${PRODUCT_NAME}${EDITION}:${SOURCE_TAG}-secure"
|
||||
|
||||
echo "Source image: ${SRC_IMAGE}"
|
||||
echo "Release image: ${RELEASE_IMAGE}"
|
||||
echo "Release tag: ${RELEASE_TAG}"
|
||||
echo "Short tags: ${SHORT_TAGS}"
|
||||
echo "Latest: ${IS_LATEST}"
|
||||
|
||||
docker buildx imagetools inspect "${SRC_IMAGE}"
|
||||
|
||||
PUSH_TAGS=(
|
||||
--tag "${RELEASE_IMAGE}:${RELEASE_TAG}"
|
||||
)
|
||||
|
||||
if [ -n "${SHORT_TAGS}" ]; then
|
||||
IFS=',' read -r -a SHORTS <<< "${SHORT_TAGS}"
|
||||
|
||||
for tag in "${SHORTS[@]}"; do
|
||||
if [ -n "${tag}" ]; then
|
||||
PUSH_TAGS+=(
|
||||
--tag "${RELEASE_IMAGE}:${tag}"
|
||||
)
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "${IS_LATEST}" = "true" ]; then
|
||||
PUSH_TAGS+=(
|
||||
--tag "${RELEASE_IMAGE}:latest"
|
||||
)
|
||||
fi
|
||||
|
||||
docker buildx imagetools create \
|
||||
"${PUSH_TAGS[@]}" \
|
||||
"${SRC_IMAGE}"
|
||||
|
||||
docker buildx imagetools inspect \
|
||||
"${RELEASE_IMAGE}:${RELEASE_TAG}"
|
||||
428
.github/workflows/stable-build.yml
vendored
428
.github/workflows/stable-build.yml
vendored
|
|
@ -31,9 +31,9 @@ on:
|
|||
env:
|
||||
COMPANY_NAME: "onlyoffice"
|
||||
PRODUCT_NAME: "documentserver"
|
||||
VERSION: ${{ github.event.inputs.tag }}
|
||||
RELEASE_NUMBER: ${{ github.event.inputs.release_number }}
|
||||
LATEST: ${{ github.event.inputs.latest }}
|
||||
VERSION: ${{ inputs.tag }}
|
||||
RELEASE_NUMBER: ${{ inputs.release_number }}
|
||||
LATEST: ${{ inputs.latest }}
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
|
|
@ -41,26 +41,32 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
editions: ${{ steps.set.outputs.editions }}
|
||||
editions_ucs: ${{ steps.set.outputs.editions_ucs }}
|
||||
steps:
|
||||
- name: Build editions list
|
||||
id: set
|
||||
shell: bash
|
||||
env:
|
||||
INPUT_C: ${{ github.event.inputs.community }}
|
||||
INPUT_E: ${{ github.event.inputs.enterprise }}
|
||||
INPUT_C: ${{ inputs.community }}
|
||||
INPUT_E: ${{ inputs.enterprise }}
|
||||
run: |
|
||||
set -eux
|
||||
editions=$(jq -nc \
|
||||
--argjson c "$INPUT_C" \
|
||||
--argjson e "$INPUT_E" \
|
||||
'[ if $c then "" else empty end,
|
||||
if $e then ("-ee", "-de") else empty end ]')
|
||||
editions_ucs=$(echo "$editions" | jq -c 'map(select(. != "-de"))')
|
||||
echo "editions=$editions" >> "$GITHUB_OUTPUT"
|
||||
echo "editions_ucs=$editions_ucs" >> "$GITHUB_OUTPUT"
|
||||
echo "Matrix editions: $editions"
|
||||
echo "Matrix editions (UCS): $editions_ucs"
|
||||
set -euo pipefail
|
||||
|
||||
editions="$(jq -nc \
|
||||
--argjson community "${INPUT_C}" \
|
||||
--argjson enterprise "${INPUT_E}" \
|
||||
'[
|
||||
if $community then "" else empty end,
|
||||
if $enterprise then "-ee", "-de" else empty end
|
||||
]'
|
||||
)"
|
||||
|
||||
if [ "$(jq 'length' <<< "${editions}")" -eq 0 ]; then
|
||||
echo "At least one edition must be selected" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "editions=${editions}" >> "${GITHUB_OUTPUT}"
|
||||
echo "Matrix editions: ${editions}"
|
||||
|
||||
build:
|
||||
name: "Release image: DocumentServer${{ matrix.edition }}"
|
||||
|
|
@ -74,35 +80,33 @@ jobs:
|
|||
edition: ${{ fromJSON(needs.setup.outputs.editions) }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
||||
- name: Build documentserver-release
|
||||
env:
|
||||
TARGET: ${{ matrix.images }}
|
||||
PRODUCT_EDITION: ${{ matrix.edition }}
|
||||
run: |
|
||||
set -eux
|
||||
TESTING_IMAGE=${COMPANY_NAME}/4testing-${PRODUCT_NAME}${PRODUCT_EDITION}
|
||||
export PRODUCT_EDITION
|
||||
export PULL_TAG=${VERSION}
|
||||
set -eux
|
||||
export SOURCE_IMAGE=${COMPANY_NAME}/4testing-${PRODUCT_NAME}${PRODUCT_EDITION}:${VERSION}
|
||||
export RELEASE_IMAGE=${COMPANY_NAME}/${PRODUCT_NAME}${PRODUCT_EDITION}
|
||||
export TAG=${VERSION%.*}.${RELEASE_NUMBER}
|
||||
export SHORTER_TAG=${VERSION%.*}
|
||||
export SHORTEST_TAG=${VERSION%.*.*}
|
||||
docker buildx bake --sbom=true --provenance=mode=max -f docker-bake.hcl "${TARGET}" --push
|
||||
echo "DONE: Build success >> exit with 0"
|
||||
exit 0
|
||||
PUSH_TAGS=(
|
||||
-t "${RELEASE_IMAGE}:${TAG}"
|
||||
-t "${RELEASE_IMAGE}:${SHORTER_TAG}"
|
||||
-t "${RELEASE_IMAGE}:${SHORTEST_TAG}"
|
||||
)
|
||||
if [ "${LATEST}" = "true" ]; then
|
||||
PUSH_TAGS+=( "-t" "$RELEASE_IMAGE:latest" )
|
||||
fi
|
||||
docker buildx imagetools create "${PUSH_TAGS[@]}" "${SOURCE_IMAGE}"
|
||||
shell: bash
|
||||
|
||||
# Disable for now
|
||||
|
|
@ -110,7 +114,7 @@ jobs:
|
|||
# https://github.com/peter-evans/dockerhub-description/issues/294
|
||||
#
|
||||
#- name: Update Docker Hub README
|
||||
# uses: peter-evans/dockerhub-description@v4
|
||||
# uses: peter-evans/dockerhub-description@v5
|
||||
# with:
|
||||
# username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
# password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
|
@ -123,26 +127,25 @@ jobs:
|
|||
if: ${{ github.event.inputs.enterprise == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME_ENTERPRISE }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN_ENTERPRISE }}
|
||||
|
||||
- name: release 4enterpise
|
||||
shell: bash
|
||||
run: |
|
||||
export TAG=${VERSION%.*}.${RELEASE_NUMBER}
|
||||
docker buildx imagetools create --tag onlyoffice4enterprise/documentserver-ee:${TAG} \
|
||||
onlyoffice/4testing-documentserver-ee:${VERSION}
|
||||
- name: Create release image tag
|
||||
uses: nick-fields/retry@v4
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
retry_wait_seconds: 60
|
||||
command: |
|
||||
export TAG=${VERSION%.*}.${RELEASE_NUMBER}
|
||||
docker buildx imagetools create \
|
||||
--tag onlyoffice4enterprise/documentserver-ee:${TAG} \
|
||||
onlyoffice/4testing-documentserver-ee:${VERSION}
|
||||
|
||||
build-nonexample:
|
||||
name: "Release image: DocumentServer${{ matrix.edition }}-nonExample"
|
||||
|
|
@ -156,16 +159,16 @@ jobs:
|
|||
edition: ${{ fromJSON(needs.setup.outputs.editions) }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
|
|
@ -181,41 +184,298 @@ jobs:
|
|||
docker buildx bake --sbom=true --provenance=mode=max -f docker-bake.hcl "${TARGET}" --push
|
||||
shell: bash
|
||||
|
||||
build-ucs-ubuntu20:
|
||||
name: "Release image: DocumentServer${{ matrix.edition }}-ucs"
|
||||
update-secure-rebuild-config:
|
||||
name: "Update secure-rebuild config"
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ needs.setup.outputs.editions_ucs != '[]' }}
|
||||
needs: [setup]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
edition: ${{ fromJSON(needs.setup.outputs.editions_ucs) }}
|
||||
platform: ["amd64"]
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
needs: [build]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: build UCS
|
||||
- name: Compute release metadata
|
||||
id: release
|
||||
env:
|
||||
PACKAGE_BASEURL: ${{ secrets.REPO_BASEURL }}
|
||||
PRODUCT_EDITION: ${{ matrix.edition }}
|
||||
PLATFORM: linux/${{ matrix.platform }}
|
||||
VERSION: ${{ inputs.tag }}
|
||||
RELEASE_NUMBER: ${{ inputs.release_number }}
|
||||
run: |
|
||||
set -eux
|
||||
export DOCKERFILE=Dockerfile
|
||||
export BASE_VERSION=20.04
|
||||
export PG_VERSION=12
|
||||
export PACKAGE_SUFFIX=
|
||||
export TAG=${VERSION%.*}.${RELEASE_NUMBER}
|
||||
export PACKAGE_VERSION=$( echo ${VERSION} | sed -E 's/(.*)\./\1-/')
|
||||
docker buildx bake --sbom=true --provenance=mode=max -f docker-bake.hcl documentserver-ucs --push
|
||||
shell: bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE="${VERSION%.*}"
|
||||
|
||||
echo "source_tag=${VERSION}" >> "${GITHUB_OUTPUT}"
|
||||
echo "release_tag=${BASE}.${RELEASE_NUMBER}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Update JSON
|
||||
env:
|
||||
COMMUNITY: ${{ inputs.community }}
|
||||
ENTERPRISE: ${{ inputs.enterprise }}
|
||||
LATEST: ${{ inputs.latest }}
|
||||
SOURCE_TAG: ${{ steps.release.outputs.source_tag }}
|
||||
RELEASE_TAG: ${{ steps.release.outputs.release_tag }}
|
||||
RELEASE_NUMBER: ${{ inputs.release_number }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
EDITIONS_JSON="$(jq -cn \
|
||||
--argjson community "${COMMUNITY}" \
|
||||
--argjson enterprise "${ENTERPRISE}" \
|
||||
--argjson latest "${LATEST}" \
|
||||
'{
|
||||
editions: (
|
||||
{}
|
||||
+ (
|
||||
if $community
|
||||
then {"": {latest: $latest}}
|
||||
else {}
|
||||
end
|
||||
)
|
||||
+ (
|
||||
if $enterprise
|
||||
then {
|
||||
"-ee": {latest: $latest},
|
||||
"-de": {latest: $latest}
|
||||
}
|
||||
else {}
|
||||
end
|
||||
)
|
||||
)
|
||||
}
|
||||
| .editions'
|
||||
)"
|
||||
|
||||
if [ "$(jq 'length' <<< "${EDITIONS_JSON}")" -eq 0 ]; then
|
||||
echo "No editions were selected" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
chmod +x .github/scripts/update-secure-rebuild-config.sh
|
||||
|
||||
.github/scripts/update-secure-rebuild-config.sh \
|
||||
"${SOURCE_TAG}" \
|
||||
"${RELEASE_TAG}" \
|
||||
"${RELEASE_NUMBER}" \
|
||||
"${EDITIONS_JSON}"
|
||||
|
||||
- name: Push branch and open PR in Gitea
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
GITEA_URL: ${{ secrets.GITEA_URL }}
|
||||
GITEA_REPO: ${{ secrets.GITEA_REPO }}
|
||||
RELEASE_TAG: ${{ steps.release.outputs.release_tag }}
|
||||
SOURCE_TAG: ${{ steps.release.outputs.source_tag }}
|
||||
GITHUB_SOURCE_BRANCH: ${{ github.ref_name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
CONFIG_FILE=".github/secure-rebuild.json"
|
||||
BRANCH="secure-rebuild-config/${RELEASE_TAG}"
|
||||
WORK_DIR="${RUNNER_TEMP}/gitea-repo"
|
||||
RESPONSE_FILE="$(mktemp)"
|
||||
|
||||
cleanup() {
|
||||
rm -f "${RESPONSE_FILE}"
|
||||
rm -rf "${WORK_DIR}"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
if [ ! -f "${CONFIG_FILE}" ]; then
|
||||
echo "Config file was not found: ${CONFIG_FILE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${GITEA_TOKEN}" ]; then
|
||||
echo "GITEA_TOKEN is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${GITEA_URL}" ]; then
|
||||
echo "GITEA_URL is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${GITEA_REPO}" ]; then
|
||||
echo "GITEA_REPO is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GITEA_URL="${GITEA_URL%/}"
|
||||
GITEA_HOST="${GITEA_URL#https://}"
|
||||
GITEA_HOST="${GITEA_HOST#http://}"
|
||||
|
||||
AUTHENTICATED_REPO_URL="https://x-token:${GITEA_TOKEN}@${GITEA_HOST}/${GITEA_REPO}.git"
|
||||
PUBLIC_REPO_URL="${GITEA_URL}/${GITEA_REPO}.git"
|
||||
API_URL="${GITEA_URL}/api/v1/repos/${GITEA_REPO}/pulls"
|
||||
|
||||
BASE_BRANCH="${GITHUB_SOURCE_BRANCH#refs/heads/}"
|
||||
|
||||
if [ -z "${BASE_BRANCH}" ]; then
|
||||
BASE_BRANCH="master"
|
||||
fi
|
||||
|
||||
if git ls-remote \
|
||||
--exit-code \
|
||||
--heads \
|
||||
"${AUTHENTICATED_REPO_URL}" \
|
||||
"refs/heads/${BASE_BRANCH}" \
|
||||
>/dev/null 2>&1; then
|
||||
echo "Using matching Gitea branch as PR base: ${BASE_BRANCH}"
|
||||
else
|
||||
echo "Branch ${BASE_BRANCH} does not exist in Gitea"
|
||||
BASE_BRANCH="master"
|
||||
echo "Using fallback PR base: ${BASE_BRANCH}"
|
||||
fi
|
||||
|
||||
if ! git ls-remote \
|
||||
--exit-code \
|
||||
--heads \
|
||||
"${AUTHENTICATED_REPO_URL}" \
|
||||
"refs/heads/${BASE_BRANCH}" \
|
||||
>/dev/null 2>&1; then
|
||||
echo "Base branch does not exist in Gitea: ${BASE_BRANCH}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -rf "${WORK_DIR}"
|
||||
|
||||
git clone \
|
||||
--branch "${BASE_BRANCH}" \
|
||||
--single-branch \
|
||||
"${AUTHENTICATED_REPO_URL}" \
|
||||
"${WORK_DIR}"
|
||||
|
||||
cd "${WORK_DIR}"
|
||||
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
git checkout -B "${BRANCH}"
|
||||
|
||||
mkdir -p "$(dirname "${CONFIG_FILE}")"
|
||||
|
||||
cp \
|
||||
"${GITHUB_WORKSPACE}/${CONFIG_FILE}" \
|
||||
"${CONFIG_FILE}"
|
||||
|
||||
jq -e . "${CONFIG_FILE}" >/dev/null
|
||||
|
||||
git add "${CONFIG_FILE}"
|
||||
|
||||
if git diff --cached --quiet; then
|
||||
echo "The secure rebuild config is already up to date"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git commit \
|
||||
-m "chore: update secure-rebuild config for ${RELEASE_TAG}"
|
||||
|
||||
git push \
|
||||
origin \
|
||||
"HEAD:refs/heads/${BRANCH}" \
|
||||
--force
|
||||
|
||||
HTTP_CODE="$(curl \
|
||||
--silent \
|
||||
--show-error \
|
||||
--output "${RESPONSE_FILE}" \
|
||||
--write-out "%{http_code}" \
|
||||
--get "${API_URL}" \
|
||||
--header "Accept: application/json" \
|
||||
--header "Authorization: token ${GITEA_TOKEN}" \
|
||||
--data-urlencode "state=open" \
|
||||
--data-urlencode "limit=50"
|
||||
)"
|
||||
|
||||
if [ "${HTTP_CODE}" -lt 200 ] ||
|
||||
[ "${HTTP_CODE}" -ge 300 ]; then
|
||||
echo "Failed to query existing pull requests. HTTP status: ${HTTP_CODE}" >&2
|
||||
jq . "${RESPONSE_FILE}" >&2 || cat "${RESPONSE_FILE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
EXISTING_PR_URL="$(jq -r \
|
||||
--arg branch "${BRANCH}" \
|
||||
--arg base "${BASE_BRANCH}" \
|
||||
'
|
||||
map(
|
||||
select(
|
||||
.head.ref == $branch
|
||||
and .base.ref == $base
|
||||
)
|
||||
)
|
||||
| first
|
||||
| .html_url // empty
|
||||
' "${RESPONSE_FILE}"
|
||||
)"
|
||||
|
||||
if [ -n "${EXISTING_PR_URL}" ]; then
|
||||
echo "Pull request already exists: ${EXISTING_PR_URL}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
PAYLOAD="$(jq -n \
|
||||
--arg title "chore: update secure-rebuild config for ${RELEASE_TAG}" \
|
||||
--arg head "${BRANCH}" \
|
||||
--arg base "${BASE_BRANCH}" \
|
||||
--arg body "Auto-generated after release of ${RELEASE_TAG} from source image tag ${SOURCE_TAG}." \
|
||||
'{
|
||||
title: $title,
|
||||
head: $head,
|
||||
base: $base,
|
||||
body: $body
|
||||
}'
|
||||
)"
|
||||
|
||||
HTTP_CODE="$(curl \
|
||||
--silent \
|
||||
--show-error \
|
||||
--output "${RESPONSE_FILE}" \
|
||||
--write-out "%{http_code}" \
|
||||
--request POST "${API_URL}" \
|
||||
--header "Accept: application/json" \
|
||||
--header "Content-Type: application/json" \
|
||||
--header "Authorization: token ${GITEA_TOKEN}" \
|
||||
--data "${PAYLOAD}"
|
||||
)"
|
||||
|
||||
if [ "${HTTP_CODE}" -lt 200 ] ||
|
||||
[ "${HTTP_CODE}" -ge 300 ]; then
|
||||
echo "Failed to create pull request. HTTP status: ${HTTP_CODE}" >&2
|
||||
jq . "${RESPONSE_FILE}" >&2 || cat "${RESPONSE_FILE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PR_NUMBER="$(jq -r '.number // empty' "${RESPONSE_FILE}")"
|
||||
PR_URL="$(jq -r '.html_url // empty' "${RESPONSE_FILE}")"
|
||||
|
||||
echo "Pull request created: #${PR_NUMBER}"
|
||||
echo "Pull request base branch: ${BASE_BRANCH}"
|
||||
|
||||
if [ -n "${PR_URL}" ]; then
|
||||
echo "Pull request URL: ${PR_URL}"
|
||||
else
|
||||
echo "Repository URL: ${PUBLIC_REPO_URL}"
|
||||
fi
|
||||
|
||||
# The same step for GITHUB.
|
||||
# Uncomment if need to open PR in github repository.
|
||||
- name: Open PR Github
|
||||
if: ${{ false }}
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: "secure-rebuild-config/${{ steps.release.outputs.release_tag }}"
|
||||
base: ${{ github.ref_name }}
|
||||
commit-message: "chore: update secure-rebuild config for ${{ steps.release.outputs.release_tag }}"
|
||||
title: "Update secure-rebuild config for ${{ steps.release.outputs.release_tag }}"
|
||||
body: |
|
||||
Auto-generated after release of `${{ steps.release.outputs.release_tag }}`
|
||||
(source 4testing tag: `${{ steps.release.outputs.source_tag }}`,
|
||||
release number: `${{ github.event.inputs.release_number }}`).
|
||||
|
||||
Please review and merge so next scheduled secure-rebuild picks up the new source.
|
||||
delete-branch: true
|
||||
|
|
|
|||
16
Dockerfile.secure
Normal file
16
Dockerfile.secure
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
ARG SOURCE_IMAGE
|
||||
FROM ${SOURCE_IMAGE}
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV ACCEPT_EULA=Y
|
||||
|
||||
RUN set -eux; \
|
||||
printf '#!/bin/sh\nexit 101\n' > /usr/sbin/policy-rc.d; \
|
||||
chmod +x /usr/sbin/policy-rc.d; \
|
||||
apt-get update; \
|
||||
apt-get -o Dpkg::Options::="--force-confold" \
|
||||
-o Dpkg::Options::="--force-confdef" \
|
||||
-y upgrade; \
|
||||
rm -f /usr/sbin/policy-rc.d; \
|
||||
apt-get clean; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
|
@ -2,19 +2,6 @@
|
|||
ARG PULL_TAG=latest
|
||||
ARG COMPANY_NAME=onlyoffice
|
||||
ARG PRODUCT_EDITION=
|
||||
### Rebuild arguments
|
||||
ARG UCS_PREFIX=
|
||||
ARG IMAGE=${COMPANY_NAME}/documentserver${PRODUCT_EDITION}${UCS_PREFIX}:${PULL_TAG}
|
||||
|
||||
### Build main-release ###
|
||||
|
||||
FROM ${COMPANY_NAME}/4testing-documentserver${PRODUCT_EDITION}:${PULL_TAG} as documentserver-stable
|
||||
|
||||
### Rebuild stable images with secure updates
|
||||
FROM ${IMAGE} as documentserver-stable-rebuild
|
||||
RUN echo "This is rebuild" \
|
||||
&& apt-get update -y \
|
||||
&& apt-get upgrade -y
|
||||
|
||||
### Build nonexample ###
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue