name: Psiphon Docker CI/CD # 1. Scheduled Trigger: Run daily to check for new Psiphon releases on: schedule: # Run once daily at 00:00 UTC - cron: '0 0 * * *' # 2. Manual Trigger: Allows you to run the workflow manually, optionally providing a version workflow_dispatch: inputs: psiphon_version: description: 'Psiphon Tunnel Core Version (e.g., 1.2.3). Leave empty to fetch latest.' required: false targets: description: 'Build Targets (comma-separated, e.g., "linux/amd64,linux/arm64"). Leave empty for default.' required: false dry_run: description: 'Dry Run (Build only, do not push)' required: false type: boolean default: false jobs: build-and-push: runs-on: ubuntu-latest environment: Production env: # Define the target Docker image name DOCKER_IMAGE_NAME: swarupsengupta2007/psiphon-docker # Define the external repository to track releases from GH_REPO: Psiphon-Labs/psiphon-tunnel-core # Default targets for scheduled runs DEFAULT_TARGETS: "linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6" steps: - name: Checkout repository uses: actions/checkout@v4 - name: Determine Versions & Targets id: prep run: | # --- 1. Determine Psiphon Version --- VERSION="" TAG_TO_FETCH="" if [[ "${{ github.event.inputs.psiphon_version }}" != "" ]]; then # Manual trigger with specific version VERSION="${{ github.event.inputs.psiphon_version }}" TAG_TO_FETCH="v$VERSION" echo "Using manually specified version: $VERSION" else # Automatic: Fetch latest release from GitHub API echo "Fetching latest Psiphon release from $GH_REPO..." LATEST_TAG=$(curl -s "https://api.github.com/repos/$GH_REPO/releases/latest" | jq -r '.tag_name') if [[ -z "$LATEST_TAG" ]] || [[ "$LATEST_TAG" == "null" ]]; then echo "Error: Could not retrieve the latest release tag. Stopping." exit 1 fi TAG_TO_FETCH="$LATEST_TAG" VERSION=$(echo $LATEST_TAG | sed 's/^v//') echo "Latest Psiphon version found: $VERSION (Tag: $TAG_TO_FETCH)" fi # --- 2. Determine Go Version from go.mod --- GO_MOD_URL="https://raw.githubusercontent.com/$GH_REPO/$TAG_TO_FETCH/go.mod" echo "Fetching Go version from: $GO_MOD_URL" GO_VERSION=$(curl -s $GO_MOD_URL | grep "^go " | awk '{print $2}') if [[ -z "$GO_VERSION" ]]; then echo "Warning: Could not determine Go version from go.mod. Defaulting to 'latest'." GO_VERSION="latest" else echo "Detected required Go version: $GO_VERSION" fi # --- 3. Determine Build Targets --- # Use input if provided, otherwise fall back to env.DEFAULT_TARGETS RAW_TARGETS="${{ github.event.inputs.targets }}" if [[ -z "$RAW_TARGETS" ]]; then RAW_TARGETS="$DEFAULT_TARGETS" fi # Docker Buildx expects comma-separated (e.g., linux/amd64,linux/arm64) PLATFORMS=$(echo "$RAW_TARGETS" | tr ' ' ',') # Build script might expect space-separated (e.g., linux/amd64 linux/arm64) # We convert commas to spaces for the build-arg to be safe SCRIPT_TARGETS=$(echo "$RAW_TARGETS" | tr ',' ' ') echo "Using platforms (Buildx): $PLATFORMS" echo "Using targets (Script): $SCRIPT_TARGETS" # --- 4. Determine Push Status --- # Default to true, unless dry_run input is explicitly 'true' SHOULD_PUSH="true" if [[ "${{ github.event.inputs.dry_run }}" == "true" ]]; then SHOULD_PUSH="false" echo "Dry Run selected. Skipping push." else echo "Pushing enabled." fi # Export variables to GitHub Actions outputs echo "psiphon_version=$VERSION" >> $GITHUB_OUTPUT echo "go_version=$GO_VERSION" >> $GITHUB_OUTPUT echo "platforms=$PLATFORMS" >> $GITHUB_OUTPUT echo "script_targets=$SCRIPT_TARGETS" >> $GITHUB_OUTPUT echo "should_push=$SHOULD_PUSH" >> $GITHUB_OUTPUT - name: Set up QEMU # Required for multi-arch builds uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Docker Hub # Only log in if we are actually pushing. if: steps.prep.outputs.should_push == 'true' uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push Docker image uses: docker/build-push-action@v5 with: context: . # Enable multi-arch support using the determined platforms platforms: ${{ steps.prep.outputs.platforms }} # Pass Psiphon version, Go version, and TARGETS as build arguments build-args: | PSIPHON_VERSION=${{ steps.prep.outputs.psiphon_version }} GO_VERSION=${{ steps.prep.outputs.go_version }} TARGETS=${{ steps.prep.outputs.script_targets }} # Use the determined push flag push: ${{ steps.prep.outputs.should_push }} tags: | ${{ env.DOCKER_IMAGE_NAME }}:${{ steps.prep.outputs.psiphon_version }} ${{ env.DOCKER_IMAGE_NAME }}:latest