# ==================================================================================================================== # # Authors: # # Patrick Lehmann # # # # ==================================================================================================================== # # Copyright 2020-2024 The pyTooling Authors # # # # Licensed under the Apache License, Version 2.0 (the "License"); # # you may not use this file except in compliance with the License. # # You may obtain a copy of the License at # # # # http://www.apache.org/licenses/LICENSE-2.0 # # # # Unless required by applicable law or agreed to in writing, software # # distributed under the License is distributed on an "AS IS" BASIS, # # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # See the License for the specific language governing permissions and # # limitations under the License. # # # # SPDX-License-Identifier: Apache-2.0 # # ==================================================================================================================== # name: Nightly on: workflow_call: inputs: ubuntu_image: description: 'Name of the Ubuntu image.' required: false default: 'ubuntu-24.04' type: string nightly_name: description: 'Name of the nightly release.' required: false default: 'nightly' type: string nightly_title: description: 'Title of the nightly release.' required: false default: '' type: string nightly_description: description: 'Description of the nightly release.' required: false default: 'Release of artifacts from latest CI pipeline.' type: string draft: description: 'Specify if this is a draft.' required: false default: false type: boolean prerelease: description: 'Specify if this is a pre-release.' required: false default: false type: boolean latest: description: 'Specify if this is the latest release.' required: false default: false type: boolean replacements: description: 'Multi-line string containing search=replace patterns.' required: false default: '' type: string assets: description: 'Multi-line string containing artifact:file:title asset descriptions.' required: true type: string tarball-name: type: string required: false default: '__pyTooling_upload_artifact__.tar' jobs: Release: name: 📝 Update 'Nightly Page' on GitHub runs-on: ${{ inputs.ubuntu_image }} permissions: contents: write actions: write # attestations: write steps: - name: ⏬ Checkout repository uses: actions/checkout@v4 with: # The command 'git describe' (used for version) needs the history. fetch-depth: 0 - name: 🔧 Install zstd run: sudo apt-get install -y --no-install-recommends zstd - name: 📑 Delete (old) Release Page id: deleteReleasePage run: | set +e ANSI_LIGHT_RED="\e[91m" ANSI_LIGHT_GREEN="\e[92m" ANSI_LIGHT_YELLOW="\e[93m" ANSI_NOCOLOR="\e[0m" export GH_TOKEN=${{ github.token }} echo -n "Deleting release '${{ inputs.nightly_name }}' ... " message="$(gh release delete ${{ inputs.nightly_name }} --yes 2>&1)" if [[ $? -eq 0 ]]; then echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" elif [[ "${message}" == "release not found" ]]; then echo -e "${ANSI_LIGHT_YELLOW}[NOT FOUND]${ANSI_NOCOLOR}" else echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}" echo -e "${ANSI_LIGHT_RED}Couldn't delete release '${{ inputs.nightly_name }}' -> Error: '${message}'.${ANSI_NOCOLOR}" echo "::error title=InternalError::Couldn't delete release '${{ inputs.nightly_name }}' -> Error: '${message}'." exit 1 fi - name: 📑 (Re)create (new) Release Page id: createReleasePage run: | set +e ANSI_LIGHT_RED="\e[91m" ANSI_LIGHT_GREEN="\e[92m" ANSI_NOCOLOR="\e[0m" export GH_TOKEN=${{ github.token }} addDraft="--draft" if ${{ inputs.prerelease }}; then addPreRelease="--prerelease" fi if ! ${{ inputs.latest }}; then addLatest="--latest=false" fi if [[ "${{ inputs.nightly_title }}" != "" ]]; then addTitle=("--title" "${{ inputs.nightly_title }}") fi cat <<'EOF' > __NoTeS__.md ${{ inputs.nightly_description }} EOF if [[ -s __NoTeS__.md ]]; then addNotes=("--notes-file" "__NoTeS__.md") fi # Apply replacements while IFS=$'\r\n' read -r patternLine; do # skip empty lines [[ "$patternLine" == "" ]] && continue pattern="${patternLine%%=*}" replacement="${patternLine#*=}" sed -i -e "s/%$pattern%/$replacement/g" "__NoTeS__.md" done <<<'${{ inputs.replacements }}' # Add footer line cat <> __NoTeS__.md -------- Published from [${{ github.workflow }}](https://github.com/Paebbels/ghdl/actions/runs/${{ github.run_id }}) workflow triggered by @${{ github.actor }} on $(date '+%Y-%m-%d %H:%M:%S'). EOF echo "Creating release '${{ inputs.nightly_name }}' ... " message="$(gh release create "${{ inputs.nightly_name }}" --verify-tag $addDraft $addPreRelease $addLatest "${addTitle[@]}" "${addNotes[@]}" 2>&1)" if [[ $? -eq 0 ]]; then echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" else echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}" echo -e "${ANSI_LIGHT_RED}Couldn't create release '${{ inputs.nightly_name }}' -> Error: '${message}'.${ANSI_NOCOLOR}" echo "::error title=InternalError::Couldn't create release '${{ inputs.nightly_name }}' -> Error: '${message}'." exit 1 fi - name: 📥 Download artifacts and upload as assets id: uploadAssets run: | set +e ANSI_LIGHT_RED="\e[91m" ANSI_LIGHT_GREEN="\e[92m" ANSI_LIGHT_YELLOW="\e[93m" ANSI_NOCOLOR="\e[0m" export GH_TOKEN=${{ github.token }} Replace() { line="$1" while IFS=$'\r\n' read -r patternLine; do # skip empty lines [[ "$patternLine" == "" ]] && continue pattern="${patternLine%%=*}" replacement="${patternLine#*=}" line="${line//"%$pattern%"/"$replacement"}" done <<<'${{ inputs.replacements }}' echo "$line" } ERRORS=0 # A dictionary of 0/1 to avoid duplicate downloads declare -A downloadedArtifacts # A dictionary to check for duplicate asset files in release declare -A assetFilenames while IFS=$'\r\n' read -r assetLine; do if [[ "${assetLine}" == "" ]]; then continue fi # split assetLine colon separated triple: artifact:asset:title artifact="${assetLine%%:*}" remaining="${assetLine#*:}" asset="${remaining%%:*}" title="${remaining##*:}" # remove leading whitespace asset="${asset#"${asset%%[![:space:]]*}"}" title="${title#"${title%%[![:space:]]*}"}" # apply replacements asset="$(Replace "${asset}")" title="$(Replace "${title}")" echo "Publish asset '${asset}' from artifact '${artifact}' with title '${title}'" echo -n " Checked asset for duplicates ... " if [[ -n "${assetFilenames[$asset]}" ]]; then echo -e "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}" echo "::error title=DuplicateAsset::Asset '${asset}' from artifact '${artifact}' was already uploaded to release '${{ inputs.nightly_name }}'." ERRORS=1 continue else echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" assetFilenames[$asset]=1 fi # Download artifact by artifact name if [[ -n "${downloadedArtifacts[$artifact]}" ]]; then echo -e " downloading '${artifact}' ... ${ANSI_LIGHT_YELLOW}[SKIPPED]${ANSI_NOCOLOR}" else echo " downloading '${artifact}' ... " echo -n " gh run download $GITHUB_RUN_ID --dir \"${artifact}\" --name \"${artifact}\" " gh run download $GITHUB_RUN_ID --dir "${artifact}" --name "${artifact}" if [[ $? -eq 0 ]]; then echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" else echo -e "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}" echo -e "${ANSI_LIGHT_RED}Couldn't download artifact '${artifact}'.${ANSI_NOCOLOR}" echo "::error title=ArtifactNotFound::Couldn't download artifact '${artifact}'." ERRORS=1 continue fi downloadedArtifacts[$artifact]=1 echo -n " Checking for embedded tarball ... " if [[ -f "${artifact}/${{ inputs.tarball-name }}" ]]; then echo -e "${ANSI_LIGHT_GREEN}[FOUND]${ANSI_NOCOLOR}" pushd "${artifact}" > /dev/null echo -n " Extracting embedded tarball ... " tar -xf "${{ inputs.tarball-name }}" if [[ $? -ne 0 ]]; then echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}" else echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" fi echo -n " Removing temporary tarball ... " rm -f "${{ inputs.tarball-name }}" if [[ $? -ne 0 ]]; then echo -e "${ANSI_LIGHT_RED}[FAILED]${ANSI_NOCOLOR}" else echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" fi popd > /dev/null else echo -e "${ANSI_LIGHT_YELLOW}[SKIPPED]${ANSI_NOCOLOR}" fi fi # Check if artifact should be compressed (zip, tgz) or if asset was part of the downloaded artifact. echo -n " checking asset '${artifact}/${asset}' ... " if [[ "${asset}" == !*.zip ]]; then echo -e "${ANSI_LIGHT_GREEN}[ZIP]${ANSI_NOCOLOR}" asset="${asset##*!}" echo " Compressing artifact '${artifact}' to '${asset}' ..." ( cd "${artifact}" && \ zip -r "../${asset}" * ) if [[ $? -eq 0 ]]; then echo -e " Compression ${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" uploadFile="${asset}" else echo -e " Compression ${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}" echo -e "${ANSI_LIGHT_RED}Couldn't compress '${artifact}' to zip file '${asset}'.${ANSI_NOCOLOR}" echo "::error title=CompressionError::Couldn't compress '${artifact}' to zip file '${asset}'." ERRORS=1 continue fi elif [[ "${asset}" == !*.tgz || "${asset}" == !*.tar.gz || "${asset}" == \$*.tgz || "${asset}" == \$*.tar.gz ]]; then echo -e "${ANSI_LIGHT_GREEN}[TAR/GZ]${ANSI_NOCOLOR}" if [[ "${asset:0:1}" == "\$" ]]; then asset="${asset##*$}" dirName="${asset%.*}" echo " Compressing artifact '${artifact}' to '${asset}' ..." tar -c --gzip --owner=0 --group=0 --file="${asset}" --directory="${artifact}" --transform "s|^\.|${dirName%.tar}|" . retCode=$? else asset="${asset##*!}" echo " Compressing artifact '${artifact}' to '${asset}' ..." ( cd "${artifact}" && \ tar -c --gzip --owner=0 --group=0 --file="../${asset}" * ) retCode=$? fi if [[ $retCode -eq 0 ]]; then echo -e " Compression ${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" uploadFile="${asset}" else echo -e " Compression ${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}" echo -e "${ANSI_LIGHT_RED}Couldn't compress '${artifact}' to tgz file '${asset}'.${ANSI_NOCOLOR}" echo "::error title=CompressionError::Couldn't compress '${artifact}' to tgz file '${asset}'." ERRORS=1 continue fi elif [[ "${asset}" == !*.tzst || "${asset}" == !*.tar.zst || "${asset}" == \$*.tzst || "${asset}" == \$*.tar.zst ]]; then echo -e "${ANSI_LIGHT_GREEN}[ZST]${ANSI_NOCOLOR}" if [[ "${asset:0:1}" == "\$" ]]; then asset="${asset##*$}" dirName="${asset%.*}" echo " Compressing artifact '${artifact}' to '${asset}' ..." tar -c --zstd --owner=0 --group=0 --file="${asset}" --directory="${artifact}" --transform "s|^\.|${dirName%.tar}|" . retCode=$? else asset="${asset##*!}" echo " Compressing artifact '${artifact}' to '${asset}' ..." ( cd "${artifact}" && \ tar -c --zstd --owner=0 --group=0 --file="../${asset}" * ) retCode=$? fi if [[ $retCode -eq 0 ]]; then echo -e " Compression ${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" uploadFile="${asset}" else echo -e " Compression ${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}" echo -e "${ANSI_LIGHT_RED}Couldn't compress '${artifact}' to zst file '${asset}'.${ANSI_NOCOLOR}" echo "::error title=CompressionError::Couldn't compress '${artifact}' to zst file '${asset}'." ERRORS=1 continue fi elif [[ -e "${artifact}/${asset}" ]]; then echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" uploadFile="${artifact}/${asset}" else echo -e "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}" echo -e "${ANSI_LIGHT_RED}Couldn't find asset '${asset}' in artifact '${artifact}'.${ANSI_NOCOLOR}" echo "::error title=FileNotFound::Couldn't find asset '${asset}' in artifact '${artifact}'." ERRORS=1 continue fi # Upload asset to existing release page echo -n " uploading asset '${asset}' from '${uploadFile}' with title '${title}' ... " gh release upload ${{ inputs.nightly_name }} "${uploadFile}#${title}" --clobber if [[ $? -eq 0 ]]; then echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" else echo -e "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}" echo -e "${ANSI_LIGHT_RED}Couldn't upload asset '${asset}' from '${uploadFile}' to release '${{ inputs.nightly_name }}'.${ANSI_NOCOLOR}" echo "::error title=UploadError::Couldn't upload asset '${asset}' from '${uploadFile}' to release '${{ inputs.nightly_name }}'." ERRORS=1 continue fi done <<<'${{ inputs.assets }}' echo "Inspecting downloaded artifacts ..." tree -L 3 . if [[ $ERROR -ne 0 ]]; then echo -e "${ANSI_LIGHT_RED}Errors detected in previous steps.${ANSI_NOCOLOR}" exit 1 fi - name: 📑 Remove draft state from Release Page if: ${{ ! inputs.draft }} run: | set +e ANSI_LIGHT_RED="\e[91m" ANSI_LIGHT_GREEN="\e[92m" ANSI_NOCOLOR="\e[0m" export GH_TOKEN=${{ github.token }} # Remove draft-state from release page echo -n "Remove draft-state from release '${title}' ... " gh release edit --draft=false "${{ inputs.nightly_name }}" if [[ $? -eq 0 ]]; then echo -e "${ANSI_LIGHT_GREEN}[OK]${ANSI_NOCOLOR}" else echo -e "${ANSI_LIGHT_RED}[ERROR]${ANSI_NOCOLOR}" echo -e "${ANSI_LIGHT_RED}Couldn't remove draft-state from release '${{ inputs.nightly_name }}'.${ANSI_NOCOLOR}" echo "::error title=ReleasePage::Couldn't remove draft-state from release '${{ inputs.nightly_name }}'." fi