diff --git a/.gitea/scripts/replaceTokens.js b/.gitea/scripts/replaceTokens.js new file mode 100644 index 0000000..5b337c6 --- /dev/null +++ b/.gitea/scripts/replaceTokens.js @@ -0,0 +1,81 @@ +// Replaces #{TOKEN-NAME}# with values from SECRETS/VARIABLES +// Converts: #{api-base-url}# -> API_BASE_URL (uppercase, - to _) +// Special: #{buildid}# -> RUN_ID + +const fs = require('fs'); +const path = require('path'); + +function replaceInFile(file, mapToken) { + let data = fs.readFileSync(file, 'utf8'); + const re = /#\{(.*?)\}#/g; + let changed = false; + data = data.replace(re, (_, raw) => { + const token = (raw || '').replace(/-/g, '_').toUpperCase(); + const val = mapToken(token); + if (val == null || val === '') { + return `#{${raw}}#`; // leave unchanged, will error later + } + changed = true; + return String(val); + }); + if (changed) { + fs.writeFileSync(file, data, 'utf8'); + } + return changed; +} + +(async () => { + const secrets = JSON.parse(process.env.SECRETS || '{}'); + const variables = JSON.parse(process.env.VARIABLES || '{}'); + const RUN_ID = process.env.RUN_ID || process.env.GITHUB_RUN_ID || ''; + + const mapToken = (token) => { + if (token === 'BUILDID') return RUN_ID; + // Try variables first, then secrets + return variables[token] ?? secrets[token]; + }; + + const files = [ + 'artifacts/api/appsettings.Production.json', + 'artifacts/ui/appsettings.Production.json' + ].map(f => path.resolve(f)).filter(f => fs.existsSync(f)); + + if (files.length === 0) { + console.error('āŒ No appsettings.Production.json files found in artifacts/'); + process.exit(1); + } + + console.log(` Tokenizing ${files.length} file(s):`); + files.forEach(f => console.log(` - ${f}`)); + + const missing = new Set(); + + // First pass: replace tokens + for (const file of files) { + console.log(`\n Processing: ${file}`); + replaceInFile(file, mapToken); + } + + // Second pass: check for remaining tokens + for (const file of files) { + const content = fs.readFileSync(file, 'utf8'); + const reLeft = /#\{(.*?)\}#/g; + let m; + while ((m = reLeft.exec(content))) { + const token = (m[1] || '').replace(/-/g, '_').toUpperCase(); + missing.add(token); + } + } + + if (missing.size > 0) { + console.error(`\nāŒ Missing values for tokens: ${Array.from(missing).join(', ')}`); + console.error('\nMake sure these secrets/variables are configured in Gitea repo settings.'); + process.exit(1); + } + + console.log('\nāœ… Tokenization complete. All placeholders replaced.'); +})().catch(err => { + console.error('āŒ Error during tokenization:'); + console.error(err.stack || err.message || String(err)); + process.exit(1); +}); \ No newline at end of file diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..d6690ae --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,131 @@ +name: Release Docker Images + +on: + workflow_dispatch: + inputs: + build_id: + description: 'Build ID (leave empty for latest successful build)' + required: false + type: string + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: https://github.com/actions/checkout@v4 + + - name: Setup Node.js + uses: https://github.com/actions/setup-node@v4 + with: + node-version: 20 + + - name: Determine build ID + id: build + run: | + BUILD_ID="${{ github.event.inputs.build_id }}" + if [ -z "$BUILD_ID" ]; then + echo "No build_id provided, will use latest from packages" + # Fallback: you can implement logic to find latest build tag + echo "ERROR: Please provide build_id for now" + exit 1 + fi + echo "build_id=$BUILD_ID" >> "$GITHUB_OUTPUT" + echo "Using build_id: $BUILD_ID" + + - name: Log in to Gitea Container Registry + run: | + echo "${{ secrets.REGISTRY_TOKEN }}" | docker login code.bim-it.pl -u "${{ secrets.REGISTRY_USER }}" --password-stdin + + - name: Pull build artifacts (images) + run: | + docker pull code.bim-it.pl/mz/bimai-api:build-${{ steps.build.outputs.build_id }} + docker pull code.bim-it.pl/mz/bimai-ui:build-${{ steps.build.outputs.build_id }} + + - name: Extract appsettings from images + run: | + mkdir -p artifacts/api artifacts/ui + + # Extract from API image + docker create --name temp-api code.bim-it.pl/mz/bimai-api:build-${{ steps.build.outputs.build_id }} + docker cp temp-api:/app/appsettings.Production.json artifacts/api/ + docker rm temp-api + + # Extract from UI image + docker create --name temp-ui code.bim-it.pl/mz/bimai-ui:build-${{ steps.build.outputs.build_id }} + docker cp temp-ui:/app/appsettings.Production.json artifacts/ui/ + docker rm temp-ui + + - name: Show extracted configs (before tokenization) + run: | + echo "::group::API appsettings.Production.json" + cat artifacts/api/appsettings.Production.json + echo "::endgroup::" + echo "::group::UI appsettings.Production.json" + cat artifacts/ui/appsettings.Production.json + echo "::endgroup::" + + - name: Tokenize appsettings + env: + SECRETS: ${{ toJson(secrets) }} + VARIABLES: ${{ toJson(vars) }} + RUN_ID: ${{ steps.build.outputs.build_id }} + run: | + node .gitea/scripts/replaceTokens.js + + - name: Show tokenized configs (after tokenization) + run: | + echo "::group::API appsettings.Production.json (tokenized)" + cat artifacts/api/appsettings.Production.json | sed 's/Password=[^;]*/Password=***/g' + echo "::endgroup::" + echo "::group::UI appsettings.Production.json (tokenized)" + cat artifacts/ui/appsettings.Production.json + echo "::endgroup::" + + - name: Rebuild images with tokenized configs + run: | + # API + cat > Dockerfile.release.api <<'EOF' + FROM code.bim-it.pl/mz/bimai-api:build-${{ steps.build.outputs.build_id }} + COPY artifacts/api/appsettings.Production.json /app/ + EOF + docker build -f Dockerfile.release.api \ + -t code.bim-it.pl/mz/bimai-api:prod \ + -t code.bim-it.pl/mz/bimai-api:release-${{ steps.build.outputs.build_id }} \ + . + + # UI + cat > Dockerfile.release.ui <<'EOF' + FROM code.bim-it.pl/mz/bimai-ui:build-${{ steps.build.outputs.build_id }} + COPY artifacts/ui/appsettings.Production.json /app/ + EOF + docker build -f Dockerfile.release.ui \ + -t code.bim-it.pl/mz/bimai-ui:prod \ + -t code.bim-it.pl/mz/bimai-ui:release-${{ steps.build.outputs.build_id }} \ + . + + - name: Push final images + run: | + docker push code.bim-it.pl/mz/bimai-api:prod + docker push code.bim-it.pl/mz/bimai-api:release-${{ steps.build.outputs.build_id }} + docker push code.bim-it.pl/mz/bimai-ui:prod + docker push code.bim-it.pl/mz/bimai-ui:release-${{ steps.build.outputs.build_id }} + + - name: Output release info + run: | + echo "## Docker Images Released" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Build ID:** ${{ steps.build.outputs.build_id }}" >> $GITHUB_STEP_SUMMARY + echo "**Release ID:** ${{ github.run_id }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Pull commands:" >> $GITHUB_STEP_SUMMARY + echo '```bash' >> $GITHUB_STEP_SUMMARY + echo "# Production (latest)" >> $GITHUB_STEP_SUMMARY + echo "docker pull code.bim-it.pl/mz/bimai-api:prod" >> $GITHUB_STEP_SUMMARY + echo "docker pull code.bim-it.pl/mz/bimai-ui:prod" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "# Specific release" >> $GITHUB_STEP_SUMMARY + echo "docker pull code.bim-it.pl/mz/bimai-api:release-${{ steps.build.outputs.build_id }}" >> $GITHUB_STEP_SUMMARY + echo "docker pull code.bim-it.pl/mz/bimai-ui:release-${{ steps.build.outputs.build_id }}" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY \ No newline at end of file