diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..98bd42850 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,259 @@ +name: Release Agent V3 + +on: + workflow_dispatch: + inputs: + publishPackages: + description: 'Publish packages to up-ap.nginx.com' + required: true + type: boolean + createPullRequest: + description: 'Create pull request back into main' + required: true + type: boolean + uploadJWT: + description: 'Temporary JWT to publish packages to up-ap.nginx.com' + required: true + type: string + +defaults: + run: + shell: bash + +concurrency: + group: ${{ github.ref_name }}-release + cancel-in-progress: true + +permissions: + contents: read + +jobs: + release-draft: + name: Update Release Draft + runs-on: ubuntu-22.04 + outputs: + version: ${{ steps.vars.outputs.VERSION }} + release_id: ${{ steps.vars.outputs.RELEASE_ID }} + steps: + - name: Checkout Repository + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + with: + fetch-depth: 0 + + - name: Setup Golang Environment + uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + with: + go-version-file: go.mod + + - name: Setup Golang Environment + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + + - name: Install semver NPM package + run: npm install semver + + - name: Create Draft Release + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + id: release + with: + script: | + const semver = require('semver'); + const ref = context.ref.split("/")[2] + const version = ref.split("-")[1] + console.log(`The release version is v${version}`) + + const releases = (await github.rest.repos.listReleases({ + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, + per_page: 100, + })).data + + const latest_release = (await github.rest.repos.getLatestRelease({ + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, + })).data.tag_name + + console.log(`The latest release was ${latest_release}`) + + if (latest_release === "v"+version) { + core.setFailed(`A published release already exists for ${latest_release}`) + } else { + const draft = releases.find((r) => r.draft && r.tag_name === "v"+version) + const draft_found = !(draft === undefined) + + let release + if (draft_found){ + console.log("Draft release already exists. Deleting current draft release and recreating it") + release = (await github.rest.repos.deleteRelease({ + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, + release_id: draft.id, + })) + } + + const release_notes = (await github.rest.repos.generateReleaseNotes({ + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, + tag_name: "v"+version, + previous_tag_name: latest_release, + target_commitish: ref, + })) + + const footer = ` + ## Resources + - Documentation -- https://github.com/nginx/agent#readme + ` + + release = (await github.rest.repos.createRelease({ + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, + tag_name: "v"+version, + target_commitish: ref, + name: "v"+version, + body: release_notes.data.body + footer, + draft: true, + })) + + console.log(`Release created: ${release.data.html_url}`) + console.log(`Release ID: ${release.data.id}`) + console.log(`Release notes: ${release_notes.data.body}`) + console.log(`Release Upload URL: ${release.data.upload_url}`) + + return { + version: version, + release_id: release.data.id, + release_upload_url: release.data.upload_url, + } + } + + - name: Set Environment Variables + id: vars + run: | + echo "${{steps.release.outputs.result}}" + echo "VERSION=$(echo '${{steps.release.outputs.result}}' | jq -r '.version')" >> $GITHUB_OUTPUT + echo "RELEASE_ID=$(echo '${{steps.release.outputs.result}}' | jq -r '.release_id')" >> $GITHUB_OUTPUT + + tag-release: + name: Tag Release + runs-on: ubuntu-22.04 + needs: [release-draft] + steps: + - name: Checkout Repository + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + with: + fetch-depth: 0 + + - name: Tag release + run: | + git config --global user.name 'github-actions' + git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' + + git tag -a "v${{ needs.release-draft.outputs.version }}" -m "CI Autogenerated" + git tag -a "sdk/v${{ needs.release-draft.outputs.version }}" -m "CI Autogenerated" + + - name: Push Tags + if: ${{ inputs.publishPackages == true }} + run: | + git push origin "v${{ needs.release-draft.outputs.version }}" + git push origin "sdk/v${{ needs.release-draft.outputs.version }}" + + build-publish-packages: + name: Build & Publish packages + runs-on: ubuntu-22.04 + needs: [release-draft] + steps: + - name: Checkout Repository + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + with: + fetch-depth: 0 + + - name: Setup package build environment + run: | + sudo apt-get update + sudo apt-get install -y gpgv1 monkeysphere + go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.32.0 + + - name: Docker Buildx + uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 + + - name: Build Docker Image + uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0 + with: + file: scripts/packages/packager/Dockerfile + tags: build-signed-packager:1.0.0 + context: '.' + push: false + load: true + no-cache: true + build-args: | + package_type=signed-package + + - name: Build Packages + env: + INDIGO_GPG_AGENT: ${{ secrets.INDIGO_GPG_AGENT }} + NFPM_SIGNING_KEY_FILE: .key.asc + run: | + echo "$INDIGO_GPG_AGENT" | base64 --decode > ${NFPM_SIGNING_KEY_FILE} + make clean package + + - name: Azure Login + uses: azure/login@8c334a195cbb38e46038007b304988d888bf676a # v2.0.0 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Azure Upload Release Packages + uses: azure/CLI@965c8d7571d2231a54e321ddd07f7b10317f34d9 # v2.0.0 + with: + inlineScript: | + for i in ./build/azure/packages/nginx-agent*; do + az storage blob upload --auth-mode=login -f "$i" -c ${{ secrets.AZURE_CONTAINER_NAME }} \ + --account-name ${{ secrets.AZURE_ACCOUNT_NAME }} --overwrite -n nginx-agent/${GITHUB_REF##*/}/${i##*/} + done + + - name: Upload Release Assets + if: ${{ inputs.publishPackages == true }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # clobber overwrites existing assets of the same name + run: | + gh release upload --clobber v${{ needs.release-draft.outputs.version }} \ + $(find ./build/github/packages -type f \( -name "*.deb" -o -name "*.rpm" -o -name "*.pkg" -o -name "*.apk" \)) + + - name: Publish Release Packages + if: ${{ inputs.publishPackages == true }} + env: + TOKEN: ${{ inputs.uploadJWT }} + run: | + make release + + - name: Publish Github Release + if: ${{ inputs.publishPackages == true }} + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const {RELEASE_ID} = process.env + const release = (await github.rest.repos.updateRelease({ + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, + release_id: `${RELEASE_ID}`, + draft: false, + })) + console.log(`Release published: ${release.data.html_url}`) + env: + RELEASE_ID: ${{ needs.release-draft.outputs.release_id }} + + - name: Create Pull Request + if: ${{ inputs.publishPackages == true && inputs.createPullRequest == true }} + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const { repo, owner } = context.repo; + const result = await github.rest.pulls.create({ + title: 'Merge ${{ github.ref_name }} back into v3', + owner, + repo, + head: '${{ github.ref_name }}', + base: 'v3', + body: [ + 'This PR is auto-generated by the release workflow.' + ].join('\n') + });