appsok/scripts/upsert-jenkins-certified-job.sh

183 lines
5.4 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
# Default options
APPLY=false
DRY_RUN=true
while [[ $# -gt 0 ]]; do
case $1 in
--apply)
APPLY=true
DRY_RUN=false
shift
;;
--dry-run)
APPLY=false
DRY_RUN=true
shift
;;
*)
echo "Unknown option: $1" >&2
exit 1
;;
esac
done
AGE_KEY="${APPSOK_SOPS_AGE_KEY_FILE:-$HOME/.config/sops/age/appsok-ci-key.txt}"
SECRET_FILE="${APPSOK_CI_SECRET_FILE:-secrets/appsok.ci.sops.json}"
export PATH="$HOME/SDK/flutter/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
cd "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
# SCM URL extraction
SCM_URL=$(git config --get remote.origin.url 2>/dev/null || git remote get-url origin 2>/dev/null || echo "https://github.com/toki-labs/appsok.git")
# Prepare XML template
# Make sure to exactly match the XML template in the PLAN
XML_CONTENT=$(cat <<EOF
<project>
<actions/>
<description>AppSok certified macOS artifact thin launcher (appsok-macos-certified).</description>
<keepDependencies>false</keepDependencies>
<properties/>
<scm class="hudson.plugins.git.GitSCM" plugin="git">
<branches>
<hudson.plugins.git.BranchSpec>
<name>*/main</name>
</hudson.plugins.git.BranchSpec>
</branches>
<userRemoteConfigs>
<hudson.plugins.git.UserRemoteConfig>
<url>${SCM_URL}</url>
</hudson.plugins.git.UserRemoteConfig>
</userRemoteConfigs>
</scm>
<assignedNode>macbook-ultra</assignedNode>
<canRoam>false</canRoam>
<logRotator class="hudson.tasks.LogRotator">
<daysToKeep>-1</daysToKeep>
<numToKeep>10</numToKeep>
<artifactDaysToKeep>-1</artifactDaysToKeep>
<artifactNumToKeep>10</artifactNumToKeep>
</logRotator>
<triggers>
<com.cloudbees.jenkins.GitHubPushTrigger plugin="github">
<spec></spec>
</com.cloudbees.jenkins.GitHubPushTrigger>
</triggers>
<builders>
<hudson.tasks.Shell>
<command>set -euo pipefail
./scripts/build-certified-macos.sh</command>
</hudson.tasks.Shell>
</builders>
<publishers>
<hudson.tasks.ArtifactArchiver>
<artifacts>build/macos/Build/Products/Release/AppSok-certified.zip,build/macos/Build/Products/Release/AppSok-certified.zip.sha256</artifacts>
<allowEmptyArchive>false</allowEmptyArchive>
<onlyIfSuccessful>true</onlyIfSuccessful>
</hudson.tasks.ArtifactArchiver>
</publishers>
<buildWrappers/>
</project>
EOF
)
if [ "$DRY_RUN" = "true" ]; then
echo "$XML_CONTENT"
exit 0
fi
# Apply logic
if [ ! -f "$AGE_KEY" ]; then
echo "Error: missing SOPS age key at $AGE_KEY" >&2
exit 1
fi
if [ ! -f "$SECRET_FILE" ]; then
echo "Error: missing encrypted CI secret file at $SECRET_FILE" >&2
exit 1
fi
export SOPS_AGE_KEY_FILE="$AGE_KEY"
read_secret() {
local key="$1"
sops -d "$SECRET_FILE" | python3 -c "import json, sys; print(json.load(sys.stdin).get('$key', ''))"
}
JENKINS_URL="$(read_secret jenkins_url)"
JENKINS_USER="$(read_secret jenkins_username)"
JENKINS_TOKEN="$(read_secret jenkins_api_token)"
if [ -z "$JENKINS_URL" ] || [ -z "$JENKINS_USER" ] || [ -z "$JENKINS_TOKEN" ]; then
echo "Error: Missing Jenkins configurations in secrets." >&2
exit 1
fi
# Remove trailing slash if present in JENKINS_URL
JENKINS_URL="${JENKINS_URL%/}"
CURL_CONFIG="$(mktemp)"
chmod 600 "$CURL_CONFIG"
trap 'rm -f "$CURL_CONFIG"' EXIT
curl_config_escape() {
local value="$1"
value="${value//\\/\\\\}"
value="${value//\"/\\\"}"
printf '%s' "$value"
}
{
printf 'user = "'
curl_config_escape "$JENKINS_USER"
printf ':'
curl_config_escape "$JENKINS_TOKEN"
printf '"\n'
} >"$CURL_CONFIG"
# 1. Fetch Crumb
CRUMB_HEADER=""
CRUMB_RESPONSE=$(curl -s --config "$CURL_CONFIG" "${JENKINS_URL}/crumbIssuer/api/json" || true)
if [ -n "$CRUMB_RESPONSE" ] && echo "$CRUMB_RESPONSE" | grep -q "crumb"; then
CRUMB_FIELD=$(echo "$CRUMB_RESPONSE" | python3 -c "import json, sys; print(json.load(sys.stdin)['crumbRequestField'])")
CRUMB_VALUE=$(echo "$CRUMB_RESPONSE" | python3 -c "import json, sys; print(json.load(sys.stdin)['crumb'])")
CRUMB_HEADER="${CRUMB_FIELD}:${CRUMB_VALUE}"
fi
# 2. Check if job exists
CHECK_URL="${JENKINS_URL}/job/appsok-macos-certified/config.xml"
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" --config "$CURL_CONFIG" "$CHECK_URL" || echo "500")
CURL_OPTS=(-s -w "%{http_code}" -o /dev/null)
if [ -n "$CRUMB_HEADER" ]; then
CURL_OPTS+=(-H "$CRUMB_HEADER")
fi
if [ "$HTTP_STATUS" = "200" ]; then
echo "Updating existing job appsok-macos-certified..."
UPDATE_URL="${JENKINS_URL}/job/appsok-macos-certified/config.xml"
STATUS=$(curl "${CURL_OPTS[@]}" --config "$CURL_CONFIG" -H "Content-Type: application/xml" --data-binary "$XML_CONTENT" "$UPDATE_URL" || echo "500")
if [ "$STATUS" = "200" ] || [ "$STATUS" = "201" ] || [ "$STATUS" = "302" ]; then
echo "Successfully updated job configuration."
else
echo "Failed to update job. HTTP status: $STATUS" >&2
exit 1
fi
elif [ "$HTTP_STATUS" = "404" ]; then
echo "Creating new job appsok-macos-certified..."
CREATE_URL="${JENKINS_URL}/createItem?name=appsok-macos-certified"
STATUS=$(curl "${CURL_OPTS[@]}" --config "$CURL_CONFIG" -H "Content-Type: application/xml" --data-binary "$XML_CONTENT" "$CREATE_URL" || echo "500")
if [ "$STATUS" = "200" ] || [ "$STATUS" = "201" ] || [ "$STATUS" = "302" ]; then
echo "Successfully created job."
else
echo "Failed to create job. HTTP status: $STATUS" >&2
exit 1
fi
else
echo "Failed to query job status. HTTP status: $HTTP_STATUS" >&2
exit 1
fi