name: Release assets run-name: Building and uploading the release assets # Builds the downloadable artefacts for an existing release and uploads them. # # The release job cuts the tag before it builds, so a failure anywhere between # the semantic-release step and the asset upload leaves a published release with # no files - and that job cannot simply be re-run, because semantic-release # refuses once the tag exists. This workflow rebuilds the artefacts from the tag # and attaches them, so the release can be completed without cutting a new one. on: workflow_dispatch: inputs: tag: description: 'Release tag to build and upload (blank = the latest release)' required: false default: '' env: NODE_VERSION: '24.5.0' permissions: contents: write jobs: assets: runs-on: ubuntu-latest # Two runs uploading the same asset names would race the delete-then-upload # below. concurrency: group: release-assets cancel-in-progress: false steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Check runner size # Builds the same frontend as the release jobs; an undersized runner # fails inside the build - see the script. run: bash .gitea/scripts/check-runner-memory.sh - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} - name: Write .npmrc file run: | printf '%s\n' "$NPMRC" > "$RUNNER_TEMP/npmrc" echo "legacy-peer-deps=true" >> "$RUNNER_TEMP/npmrc" echo "NPM_CONFIG_USERCONFIG=$RUNNER_TEMP/npmrc" >> "$GITHUB_ENV" shell: bash env: NPMRC: ${{ secrets.NPMRC }} - name: Install system packages # jq parses the release JSON below, so it is installed before the tag # resolve. run: | apt-get update apt-get install -y zip jq - name: Resolve the release tag env: GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} REQUESTED_TAG: ${{ inputs.tag }} run: | set -euo pipefail AUTH_HEADER="Authorization: token $GITEA_TOKEN" BASE="${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}" if [ -n "${REQUESTED_TAG}" ]; then TAG="${REQUESTED_TAG}" # Fail here rather than after a ten-minute build if the tag is not # cut yet or has no release. curl --fail-with-body -sS -H "$AUTH_HEADER" "$BASE/releases/tags/${TAG}" > /dev/null else TAG=$(curl --fail-with-body -sS -H "$AUTH_HEADER" "$BASE/releases/latest" | jq -r '.tag_name') fi echo "Building assets for ${TAG}" echo "TAG=${TAG}" >> "$GITHUB_ENV" - name: Check out the tag run: | set -euo pipefail git fetch --force --tags origin "+refs/tags/${TAG}:refs/tags/${TAG}" git checkout --force "refs/tags/${TAG}" - name: Install the SASjs CLI the tag pins run: | cd sas CLI_VERSION=$(node -p "require('./package.json').dependencies['@sasjs/cli']") npm i -g "@sasjs/cli@${CLI_VERSION}" - name: Frontend build run: | cd client # Decrypt and Install sheet echo "$SHEET_PWD" | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg npm ci # client install scripts are off (client/.npmrc); the generator that # creates src/environments/version.ts runs explicitly npm run postinstall npm i webpack npm run build shell: bash env: SHEET_PWD: ${{ secrets.SHEET_PWD }} - name: Build SAS9 EBI Release run: | cd sas npm i sasjs c -t sas9 rm -rf sasjsbuild/tests sasjs b -t sas9 cp sasjsbuild/mysas9deploy.sas ./demostream_sas9.sas # remove streamed component and rebuild SAS 9 services rm -rf sasjsbuild/services/web9 rm sasjsbuild/services/clickme.sas sasjs b -t sas9 cp sasjsbuild/mysas9deploy.sas ./sas9.sas - name: Build SASjs Server Release run: | cd sas cp sasjs/utils/favicon.ico ../client/dist/favicon.ico sasjs c -t server rm -rf sasjsbuild/tests server_apploc="/Public/app/dc" sed -i "s|apploc=\"[^\"]*\"|apploc=\"${server_apploc}\"|g" sasjsbuild/services/web/index.html sasjs b -t server cp sasjsbuild/server.json.zip ./sasjs_server.json.zip - name: Build Viya Release run: | cd sas sasjs c -t viya rm -rf sasjsbuild/tests sed -i -e 's/servertype="SASJS"/servertype="SASVIYA"/g' sasjsbuild/services/DC.html sasjs b -t viya cp sasjsbuild/viya.sas ./viya.sas # compile Viya Full deploy (without web) rm -rf sasjsbuild/services/web rm sasjsbuild/services/DC.html sasjs b -t viya cp sasjsbuild/viya.sas ./viya_noweb.sas cp sasjsbuild/viya.json ./viya_noweb.json - name: Zip Frontend (including viya.json for full viya deploy) run: | cd sas cp sasjsbuild/viya.json ../client/dist/viya.json # Zip from *inside* dist so the archive holds the frontend files at its # root rather than under a client/dist/ prefix (see #147). cd ../client/dist zip -r ../../frontend.zip . - name: Upload assets to the release env: GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} run: | set -euo pipefail AUTH_HEADER="Authorization: token $GITEA_TOKEN" BASE="${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}" RELEASE_JSON=$(curl --fail-with-body -sS -H "$AUTH_HEADER" "$BASE/releases/tags/${TAG}") RELEASE_ID=$(echo "$RELEASE_JSON" | jq -r '.id') RELEASE_BODY=$(echo "$RELEASE_JSON" | jq -r '.body') ASSETS=(frontend.zip sas/demostream_sas9.sas sas/viya.sas sas/sasjs_server.json.zip sas/sas9.sas sas/viya_noweb.sas sas/viya_noweb.json) : > SHA256SUMS for f in "${ASSETS[@]}"; do sum=$(sha256sum "$f" | cut -d' ' -f1) printf '%s %s\n' "$sum" "${f##*/}" >> SHA256SUMS done cat SHA256SUMS # Replacing the assets means removing the ones this workflow uploads, # and a second run must not append the verification section twice. An # asset the release carries from another source is left alone. OWNED=$(printf '%s\n' "${ASSETS[@]##*/}" SHA256SUMS) echo "$RELEASE_JSON" | jq -r '.assets[]? | "\(.id) \(.name)"' | while read -r asset_id asset_name; do if printf '%s\n' "$OWNED" | grep -qxF -- "$asset_name"; then echo "Removing previous ${asset_name}" curl --fail-with-body -sS -X DELETE -H "$AUTH_HEADER" "$BASE/releases/${RELEASE_ID}/assets/${asset_id}" else echo "Leaving ${asset_name} in place: this workflow did not upload it" fi done if echo "$RELEASE_BODY" | grep -q "## Verifying the download"; then echo "Release body already carries the verification section" else VERIFY_SECTION=$(./.gitea/scripts/verify-section.sh) NEW_BODY=$(jq -nr --arg body "$RELEASE_BODY" --arg verify "$VERIFY_SECTION" \ '$body + "\n" + $verify + "\n\nFor installation instructions, please visit https://docs.datacontroller.io/"') curl --fail-with-body -sS -X PATCH \ -H "$AUTH_HEADER" \ -H 'Content-Type: application/json' \ --data "$(jq -n --arg body "$NEW_BODY" '{draft:false, body:$body}')" \ "$BASE/releases/$RELEASE_ID" fi URL="$BASE/releases/${RELEASE_ID}/assets" for f in "${ASSETS[@]}"; do echo "Uploading $f ..." curl --fail-with-body -sS -H "$AUTH_HEADER" "$URL" -F "attachment=@$f" done echo "Uploading SHA256SUMS ..." curl --fail-with-body -sS -H "$AUTH_HEADER" "$URL" -F "attachment=@SHA256SUMS" - name: Remove registry config # written earlier in this job; nothing deletes it on the way out if: always() run: rm -f "$RUNNER_TEMP/npmrc"