diff --git a/.github/workflows/deploy-docsify.yml b/.github/workflows/deploy-docsify.yml deleted file mode 100644 index 4ab10e1a..00000000 --- a/.github/workflows/deploy-docsify.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Deploy Docsify to GitHub Pages - -on: - push: - branches: [main] - workflow_dispatch: - -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Pages - uses: actions/configure-pages@v5 - - - name: Upload Docs artifact - uses: actions/upload-pages-artifact@v3 - with: - path: docs - - deploy: - runs-on: ubuntu-latest - needs: build - environment: - name: github-pages - url: ${{ steps.deploy.outputs.page_url }} - steps: - - name: Deploy to GitHub Pages - id: deploy - uses: actions/deploy-pages@v4 - - - name: Show deployed link - run: echo "Docs available at ${{ steps.deploy.outputs.page_url }}" diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..fe46a187 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "dotnet.defaultSolution": "SnakeAid.Api/SnakeAid.Api.sln" +} \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile index 9d4d8749..129b8d76 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,6 +1,6 @@ // ---------- Helpers ---------- def utcCreated() { - return sh(script: "date -u +%Y-%m-%dT%H:%M:%SZ", returnStdout: true).trim() + sh(script: "date -u +%Y-%m-%dT%H:%M:%SZ", returnStdout: true).trim() } def normalizeRepoUrl(String rawRepo) { @@ -8,28 +8,89 @@ def normalizeRepoUrl(String rawRepo) { if (repoUrl.startsWith('git@github.com:')) { repoUrl = repoUrl.replace('git@github.com:', 'https://github.com/') } - return repoUrl.replaceAll(/\.git$/, '') + repoUrl.replaceAll(/\.git$/, '') } def commitUrl(String repoUrl, String commitSha) { - return (repoUrl && commitSha) ? "${repoUrl}/commit/${commitSha}" : (repoUrl ?: '') + (repoUrl && commitSha) ? "${repoUrl}/commit/${commitSha}" : (repoUrl ?: '') +} + +def guessRefName() { + env.CHANGE_ID ? "PR-${env.CHANGE_ID}" : (env.BRANCH_NAME ?: env.GIT_BRANCH ?: 'unknown') +} + +@NonCPS +Map resolveDeployContext(String branchName, String changeId, String changeTarget) { + final boolean isPr = (changeId != null) + + if (isPr) { + if (changeTarget == 'main') return [stage: 'staging', environment: 'preview'] + if (changeTarget == 'dev') return [stage: 'development', environment: 'pr'] + return [stage: 'development', environment: 'pr'] + } + + if (branchName == 'main') return [stage: 'production', environment: 'production'] + if (branchName == 'dev') return [stage: 'staging', environment: 'staging'] + return [stage: 'development', environment: 'development'] } def ociLabelArgs(String tag) { def created = utcCreated() def repoUrl = normalizeRepoUrl(env.GIT_URL ?: '') - def url = commitUrl(repoUrl, env.GIT_COMMIT ?: '') + def url = commitUrl(repoUrl, env.GIT_COMMIT ?: '') + def refName = guessRefName() + def ctx = resolveDeployContext(env.BRANCH_NAME, env.CHANGE_ID, env.CHANGE_TARGET) + + def baseImage = "mcr.microsoft.com/dotnet/aspnet:8.0" + def docsUrl = "https://snake-aid.github.io/SnakeAid.Docs" - // IMPORTANT: must be a SINGLE LINE to avoid Jenkins `sh` interpreting `--label` as a separate command def labels = [ "org.opencontainers.image.source=${repoUrl}", "org.opencontainers.image.revision=${env.GIT_COMMIT ?: ''}", "org.opencontainers.image.url=${url}", "org.opencontainers.image.created=${created}", - "org.opencontainers.image.version=${tag}" - ].collect { "--label ${it}" }.join(' ') + "org.opencontainers.image.version=${tag}", + "org.opencontainers.image.ref.name=${refName}", + + "org.opencontainers.image.title=snakeaid-api", + "org.opencontainers.image.description=SnakeAid Backend API", + "org.opencontainers.image.vendor=SnakeAid", + "org.opencontainers.image.documentation=${docsUrl}", + "org.opencontainers.image.authors=thekhiem7", + + "org.opencontainers.image.base.name=${baseImage}", + "org.opencontainers.image.build.source=jenkins", + "org.opencontainers.image.build.version=${env.JENKINS_VERSION ?: 'unknown'}", + + "org.opencontainers.image.stage=${ctx.stage}", + "org.opencontainers.image.environment=${ctx.environment}", + ].collect { "--label \"${it}\"" }.join(' ') + + return "-f Dockerfile ${labels}" +} + +// ---------- Docker wrapper (AUTO CLEANUP) ---------- +def withDockerImage(String tag, Closure body) { + try { + body() + } finally { + sh "docker rmi ${env.IMAGE}:${tag} --force || true" + } +} + +def dockerBuildOnly(String tag) { + withDockerImage(tag) { + docker.build("${env.IMAGE}:${tag}","${ociLabelArgs(tag)} .") + } +} - return "-f Dockerfile ${labels} ." +def dockerBuildAndPush(String tag) { + withDockerImage(tag) { + def img = docker.build("${env.IMAGE}:${tag}","${ociLabelArgs(tag)} .") + docker.withRegistry(env.REGISTRY_URL, env.REGISTRY_CREDENTIAL) { + img.push(tag) + } + } } // ---------- Pipeline ---------- @@ -42,32 +103,23 @@ pipeline { } environment { - TIME_STAMP_FORMAT = "dd-MM-yyyy HH:mm:ss" - GITHUB_PR_URL = 'https://github.com/Snake-AID/SnakeAid.Backend/pull/' - IMAGE = 'thekhiem7/snakeaid-api' + IMAGE = 'thekhiem7/snakeaid-api' REGISTRY_CREDENTIAL = 'thekhiem7-dockerhub-credentials' - REGISTRY_URL = 'https://index.docker.io/v1/' + REGISTRY_URL = 'https://index.docker.io/v1/' } stages { stage('Checkout') { - steps { - checkout scm - } + steps { checkout scm } } - stage('Build Check (PR to dev)') { + stage('Build Check (PR -> dev)') { when { expression { env.CHANGE_ID != null && env.CHANGE_TARGET == 'dev' } } - steps { script { - def tag = "pr-${env.CHANGE_ID}" - docker.build("${IMAGE}:${tag}", ociLabelArgs(tag)) - - // Cleanup: remove local image after build check - sh "docker rmi ${IMAGE}:${tag} --force || true" + dockerBuildOnly("pr-${env.CHANGE_ID}") } } } @@ -79,18 +131,9 @@ pipeline { not { changeRequest() } } } - steps { script { - def tag = "dev" - def img = docker.build("${IMAGE}:${tag}", ociLabelArgs(tag)) - - docker.withRegistry(REGISTRY_URL, REGISTRY_CREDENTIAL) { - img.push() - } - - // Cleanup - sh "docker rmi ${IMAGE}:${tag} --force || true" + dockerBuildAndPush('dev') } } } @@ -99,18 +142,9 @@ pipeline { when { expression { env.CHANGE_ID != null && env.CHANGE_TARGET == 'main' } } - steps { script { - def tag = "preview-${env.CHANGE_ID}" - def img = docker.build("${IMAGE}:${tag}", ociLabelArgs(tag)) - - docker.withRegistry(REGISTRY_URL, REGISTRY_CREDENTIAL) { - img.push() - } - - // Cleanup - sh "docker rmi ${IMAGE}:${tag} --force || true" + dockerBuildAndPush("preview-${env.CHANGE_ID}") } } } @@ -122,18 +156,9 @@ pipeline { not { changeRequest() } } } - steps { script { - def tag = "latest" - def img = docker.build("${IMAGE}:${tag}", ociLabelArgs(tag)) - - docker.withRegistry(REGISTRY_URL, REGISTRY_CREDENTIAL) { - img.push() - } - - // Cleanup - sh "docker rmi ${IMAGE}:${tag} --force || true" + dockerBuildAndPush('latest') } } } @@ -145,17 +170,13 @@ pipeline { not { changeRequest() } } } - steps { withCredentials([ string(credentialsId: 'portainer-snakeaid-webhook', variable: 'PORTAINER_WEBHOOK') ]) { - sh ''' - set -e - curl -fsS -X POST "$PORTAINER_WEBHOOK" - ''' + sh 'curl -fsS -X POST "$PORTAINER_WEBHOOK"' } } } } -} +} \ No newline at end of file diff --git a/SnakeAid.Docs b/SnakeAid.Docs index 7377ac31..1976c6f4 160000 --- a/SnakeAid.Docs +++ b/SnakeAid.Docs @@ -1 +1 @@ -Subproject commit 7377ac31de0541886c28e33772eaac8df30f1bc5 +Subproject commit 1976c6f4e921976550de5fba46f33197db52923c