diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml new file mode 100644 index 0000000..ee793b2 --- /dev/null +++ b/.github/workflows/cicd.yml @@ -0,0 +1,143 @@ +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.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_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