├── .gitignore ├── .vscode ├── .code-snippets ├── README.md ├── img │ ├── task-compile.gif │ └── vsc-manual-release.gif ├── scripts │ ├── apex_export.sh │ ├── gen_object.sh │ └── run_sql.sh ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── apex └── .gitkeep ├── build ├── README.md └── build.sh ├── data ├── .gitkeep └── README.md ├── docs └── .gitkeep ├── lib └── .gitkeep ├── package.json ├── packages └── .gitkeep ├── release ├── README.md ├── _release.sql ├── all_apex.sql ├── all_data.sql ├── all_packages.sql ├── all_triggers.sql ├── all_views.sql ├── code │ ├── _run_code.sql │ └── backout.sql └── load_env_vars.sql ├── scripts ├── .gitignore ├── .gitkeep ├── README.md ├── apex_disable.sql ├── apex_export.sql ├── apex_install.sql ├── helper.sh └── project-config.sh ├── synonyms └── .gitkeep ├── templates ├── README.md ├── template_data_array.sql ├── template_data_json.sql ├── template_pkg.pkb ├── template_pkg.pks ├── template_table.sql └── template_view.sql ├── triggers └── .gitkeep ├── views └── .gitkeep └── www ├── .gitkeep └── src ├── .gitkeep ├── css └── .gitkeep ├── img └── .gitkeep ├── js └── .gitkeep └── lib └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | # Folders 2 | .DS_Store 3 | bin 4 | node_modules 5 | www/dist 6 | 7 | logs 8 | *.log 9 | *.map 10 | /www/dist/css/app.css.map 11 | apexcl.config.json 12 | 13 | 14 | # Build - Ignore all config files 15 | scripts/user-config.sh 16 | -------------------------------------------------------------------------------- /.vscode/.code-snippets: -------------------------------------------------------------------------------- 1 | // Put project based code snippets here 2 | // Suggested to move templates here for quicker use -------------------------------------------------------------------------------- /.vscode/README.md: -------------------------------------------------------------------------------- 1 | # MS Visual Studio Code Build Tasks 2 | 3 | - [Setup](#setup) 4 | - [`tasks.json`](#tasksjson) 5 | - [Tasks](#tasks) 6 | - [APEX Export](#apex-export) 7 | - [Compiling Code](#compiling-code) 8 | - [Generate Object](#generate-object) 9 | 10 | [Microsoft Visual Studio Code (VSC)](https://code.visualstudio.com/) is a code editor and is recommended for most PL/SQL work. VSC can compile PL/SQL code directly from VSC (see [this blog](https://ora-00001.blogspot.ca/2017/03/using-vs-code-for-plsql-development.html)) for more information. Opening this project folder in VSC will automatically give you the ability to compile PL/SQL code and do APEX backups 11 | 12 | 13 | ## Setup 14 | 15 | The first time you execute this script an error will be shown and `scripts/user-config.sh` will be created with some default values. Modify the variables as necessary. You may also need to modify the [`scripts/project-config.sh`](scripts/project-config.sh) file. See the documenation in the [`scripts`](scripts) folder for more info. 16 | 17 | *Note: Windows users: Please ensure WSL or cmder is configured to run bash as terminal in VSC: [instructions](../README.md#windows-setup)* 18 | 19 | ### `tasks.json` 20 | 21 | This file defines the VSCode task. The first time that anything bash script is run in this template (ex: build, compile, apex export, etc) the task names will automatically be updated to reflect the root project's folder name. 22 | 23 | For example if the project is stored in `~/git/my-project` **after** the first bash script is run (see previous paragraph) the task names will look like: `compile: my-project` etc. 24 | 25 | ## Tasks 26 | 27 | Tasks can be executed with `⌘+shift+B` and selecting the desired task. 28 | 29 | ![Task Compile Demo](img/task-compile.gif) 30 | 31 | ### APEX Export 32 | 33 | If you want to export your APEX applications (defined in `scripts/project-config.sh`) execute the `apex export: ` 34 | 35 | ### Compiling Code 36 | 37 | To compile the current file you're editing execute the `compile: ` task. 38 | 39 | 40 | ### Generate Object 41 | 42 | To quickly create a new package, view, or data file execute the `generate object: ` task. 43 | -------------------------------------------------------------------------------- /.vscode/img/task-compile.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/.vscode/img/task-compile.gif -------------------------------------------------------------------------------- /.vscode/img/vsc-manual-release.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/.vscode/img/vsc-manual-release.gif -------------------------------------------------------------------------------- /.vscode/scripts/apex_export.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Env variables $1, $2, etc are from the tasks.json args array 3 | 4 | # Directory of this file 5 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | 7 | # Load helper 8 | source $SCRIPT_DIR/../../scripts/helper.sh 9 | 10 | # Export APEX applications 11 | export_apex_app 12 | -------------------------------------------------------------------------------- /.vscode/scripts/gen_object.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Env variables $1, $2, etc are from the tasks.json args array 3 | 4 | # Directory of this file 5 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | 7 | # Load helper 8 | source $SCRIPT_DIR/../../scripts/helper.sh 9 | 10 | # Generate object 11 | gen_object $1 $2 12 | -------------------------------------------------------------------------------- /.vscode/scripts/run_sql.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Env variables $1, $2, etc are from the tasks.json args array 3 | 4 | # Directory of this file 5 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | 7 | # Load helper 8 | source $SCRIPT_DIR/../../scripts/helper.sh 9 | 10 | # File can be referenced either as a full path or relative path 11 | FILE_FULL_PATH=$2 12 | FILE_RELATIVE_PATH=$1 13 | 14 | # VSCODE_TASK_COMPILE_FILE should be defined in user-config.sh 15 | if [ -z "$VSCODE_TASK_COMPILE_FILE" ]; then 16 | echo -e "${COLOR_ORANGE} Warning: VSCODE_TASK_COMPILE_FILE is not defined.${COLOR_RESET}\nSet VSCODE_TASK_COMPILE_FILE in $USER_CONFIG_FILE" 17 | echo -e "Defaulting to full path" 18 | VSCODE_TASK_COMPILE_FILE=$FILE_FULL_PATH 19 | fi 20 | # Since VSCODE_TASK_COMPILE_FILE contains the variable reference need to evaluate it here 21 | VSCODE_TASK_COMPILE_FILE=$(eval "echo $VSCODE_TASK_COMPILE_FILE") 22 | 23 | echo -e "Parsing file: ${COLOR_LIGHT_GREEN}$VSCODE_TASK_COMPILE_FILE${COLOR_RESET}" 24 | echo -e "pwd: $PWD" 25 | 26 | # run sqlplus, execute the script, then get the error list and exit 27 | # VSCODE_TASK_COMPILE_BIN is set in the config.sh file (either sqlplus or sqlcl) 28 | $VSCODE_TASK_COMPILE_BIN $DB_CONN << EOF 29 | set define off 30 | -- 31 | -- Set any alter session statements here (examples below) 32 | -- alter session set plsql_ccflags = 'dev_env:true'; 33 | -- alter session set plsql_warnings = 'ENABLE:ALL'; 34 | -- 35 | -- #38: This will raise a warning message in SQL*Plus but worth keeping in to encourage use if using SQLcl to compile 36 | set codescan all 37 | -- 38 | -- Load user specific commands here 39 | $VSCODE_TASK_COMPILE_SQL_PREFIX 40 | -- 41 | -- 42 | -- Run file 43 | @$VSCODE_TASK_COMPILE_FILE 44 | -- 45 | set define on 46 | show errors 47 | exit; 48 | EOF 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.enabledLanguageIds": [ 3 | "c", 4 | "cpp", 5 | "csharp", 6 | "go", 7 | "handlebars", 8 | "javascript", 9 | "javascriptreact", 10 | "json", 11 | "latex", 12 | "markdown", 13 | "php", 14 | "plaintext", 15 | "plsql", 16 | "python", 17 | "restructuredtext", 18 | "text", 19 | "typescript", 20 | "typescriptreact", 21 | "yml" 22 | ], 23 | "files.eol": "\n", 24 | "files.associations": { 25 | "*.pks": "plsql", 26 | "*.pkb": "plsql" 27 | } 28 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "compile: CHANGEME_TASKLABEL", 8 | "type": "shell", 9 | "command": ".vscode/scripts/run_sql.sh", 10 | "args": [ 11 | // "${relativeFile}", 12 | // "${file}" 13 | // Escaped double quotes are required to make compatible with Cmder in Windows 14 | "\"${relativeFile}\"", 15 | "\"${file}\"" 16 | ], 17 | "group": "build", 18 | "presentation": { 19 | "reveal": "always", 20 | "panel": "dedicated" 21 | }, 22 | "options": {}, 23 | "problemMatcher": [] 24 | }, 25 | // EXPORT apex applications 26 | { 27 | "label": "apex export: CHANGEME_TASKLABEL", 28 | "type": "shell", 29 | "command": ".vscode/scripts/apex_export.sh", 30 | "args": [], 31 | "group": "build", 32 | "presentation": { 33 | "reveal": "always", 34 | "panel": "dedicated" 35 | }, 36 | "options": {}, 37 | "problemMatcher": [] 38 | }, 39 | { 40 | "label": "generate object: CHANGEME_TASKLABEL", 41 | "type": "shell", 42 | "command": ".vscode/scripts/gen_object.sh", 43 | "args": [ 44 | "${input:newObjectType}", 45 | "${input:newObjectName}" 46 | ], 47 | "group": "build", 48 | "presentation": { 49 | "reveal": "always", 50 | "panel": "dedicated" 51 | }, 52 | "options": {}, 53 | "problemMatcher": [] 54 | }, 55 | ], 56 | "inputs": [ 57 | { 58 | "type": "pickString", 59 | "id": "newObjectType", 60 | "description": "Testing types to include", 61 | // Note: these options must match what's in scripts/project-config.sh 62 | "options": [ 63 | "package", 64 | "view", 65 | "data_array", 66 | "data_json" 67 | ] 68 | }, 69 | { 70 | "type": "promptString", 71 | "id": "newObjectName", 72 | "description": "Object name" 73 | } 74 | ] 75 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Starter Project Template 3 | 4 | [Template for Oracle PL/SQL and/or APEX development](https://github.com/insum-labs/starter-project-template) projects. This template provides scripts and processes to help speed up your development simplify some of your release processes. 5 | 6 | It's **important** to note this is a **template**. If something doesn't fit your project's need or additional changes are required adjust accordingly. All the included tools are meant to help provide results quickly. If your project doesn't need them, remove them. 7 | 8 | - [Start](#start) 9 | - [Overview](#overview) 10 | - [Setup](#setup) 11 | - [Folder Structure](#folder-structure) 12 | - [Other Info](#other-info) 13 | - [Git](#git) 14 | - [Git Workflows](#git-workflows) 15 | - [Windows Setup](#windows-setup) 16 | - [cmder setup](#cmder-setup) 17 | 18 | ## Start 19 | 20 | In Github simply click the [`Use this template`](https://github.com/insum-labs/starter-project-template/generate) button. 21 | 22 | If using another git platform, start a new project (`git init`) then [**download**](https://github.com/insum-labs/starter-project-template/archive/master.zip) this project (*do not clone or fork*) and unzip into your new project. When copying it's important to copy all hidden files and folders. Example copy command: `cp -r ~/Downloads/starter-project-template-master/. ~/git/my-project`. 23 | 24 | 25 | ## Overview 26 | 27 | This template contains a lot of features that may help with your project. 28 | 29 | - [Build](build/): Scripts to generate the release 30 | - [Folders](#folder-structure): The most common project folder structure is provided with this project. 31 | - [Release](release/): Framework to build and do releases. 32 | - [Visual Studio Code](https://code.visualstudio.com/) (VSC) integration: compile or run your SQL and PL/SQL code right from VSC. More details are provided in the [`.vscode`](.vscode/) folder. 33 | 34 | Once [configured](#setup) the high level process to leverage this template is as follows: 35 | 36 | - **Develop** 37 | - Packages go in [`packages`](packages/), views go into [`views`](views/), etc 38 | - Release specific / non re-runnable code goes into the [`release/code`](release/code) folder (see the [`release`](release) folder for more info on how to name files and list them in your release) 39 | - Each release will start at exactly the same point: [`release/_release.sql`](release/_release.sql). If automating your releases this provides a consistent script to run which can reduce any manual intervention. 40 | - **Build Release** 41 | - Once ready to promote your code run `./build/build.sh `. This will do things such as export your APEX application(s), scrape the views/packages folder for all the files, etc. 42 | - More information about the build process is available in the [`build`](build/) folder 43 | - **Run Release** 44 | - They're various approaches on how to approach a release and tag your code. You need to read through the [release](release/) guidelines to chose an approach that is best for you 45 | - **Clean up Release** 46 | - Once a release is done you "clear" the release specific code (i.e. `release/code` folder will be cleared and reset). A bash script [`reset_release`](scripts/#reset_release) is provided to do this automatically. Examples can be found in the [`release`](release/) folder. 47 | 48 | ## Setup 49 | 50 | - [`scripts/project-config.sh`](scripts/project-config.sh): Configure APEX settings 51 | - [`scripts/user-config.sh`](scripts/user-config.sh): The first time any bash script is executed this file will be generated and needs to be modified with user specific settings. By default this file will not be committed to your git repo as it contains user specific settings and database passwords 52 | - Remove directories that don't apply to your project (ie. data, templates, etc...) 53 | 54 | 55 | ## Folder Structure 56 | 57 | The default folder structure (listed below) provides a set of common folders most projects will use. You're encouraged to add new folders to your projects where necessary. For example if you have ORDS scripts you may want to create a root folder called `ords` to store them. 58 | 59 | | Folder | Description | 60 | |:--|--| 61 | | [`.vscode`](.vscode/) | [Visual Studio Code](https://code.visualstudio.com/) specific settings 62 | | [`apex`](apex/) | Application exports 63 | | [`data`](data/) | Conversion and seed data scripts 64 | | docs | Project documents 65 | | lib | Installation libraries ([OOS Utils](https://github.com/OraOpenSource/oos-utils), [Logger](https://github.com/OraOpenSource/Logger), etc..) 66 | | [`release`](release/) | Current release scripts for changes and patching. Documentation is provided on various ways to do releases. 67 | | [`scripts`](scripts/) | Usually re-runable scripts referenced by a release script 68 | | packages | Packages (`.pks` & `.pkb`), (*If you have triggers, stand alone procedures or functions it's recommend to create a new folder for them*) 69 | | synonyms | Application Synonyms 70 | | triggers | Application Triggers 71 | | views | Application views 72 | | www | Assets that go in the server: images, CSS, and JavaScript 73 | 74 | 75 | 76 | ## Other Info 77 | 78 | ### Git 79 | 80 | If you're new to git check out these resources to help learn more about it: 81 | 82 | - [Visualized Git Commands](https://dev.to/lydiahallie/cs-visualized-useful-git-commands-37p1) 83 | 84 | ### Git Workflows 85 | 86 | They're several concepts of how to manage your Git projects. I.e. is your active development done in the `master` branch or in a `develop` branch? Each concept has their pros and cons and we recommend you review and understand the differences to apply the best method for your project. The most popular workflows are: 87 | 88 | - [`git-flow`](https://www.git-tower.com/learn/git/ebook/en/command-line/advanced-topics/git-flow) 89 | - [GitLab flow](https://docs.gitlab.com/ee/topics/gitlab_flow.html) 90 | - This is a super set of `git-flow` and contains GitLab specific features to help if using [GitLab](https://gitlab.com/) 91 | - Document provides a great comparison of all the different workflow models 92 | - [GitHub Flow](https://guides.github.com/introduction/flow/) 93 | - *Note in the GitLab flow document there's comment on GitHub flow is not recommended unless you deploy to prod frequently. For Oracle projects this comment can usually be ignored* 94 | 95 | Given the simplicity of [GitHub Flow](https://guides.github.com/introduction/flow/) we recommend this concept for most projects. 96 | 97 | 98 | ### Windows Setup 99 | 100 | All the scripts provided in this started template are written in bash for Linux (and macOS) environments. Windows users have several options. They can install [Windows Subsystem for Linux](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux)(WSL) to run Linux in Windows. 101 | 102 | Alternatively users can install [cmder](https://cmder.net/) which is a Linux emulator for Windows. 103 | 104 | #### cmder setup 105 | 106 | To setup cmder [download](https://cmder.net/) the latest version. Unzip and place the folder `cmder` into `c:\`. *Note: cmder can be stored anywhere. For the purpose of these instructions its assumed that it's stored in `c:\cmder`*. 107 | 108 | You can launch cmder anytime by running `c:\cmder\Cmder.exe` (*Hint: the first time you run it pin to your taskbar for quick access*) 109 | 110 | To integrate with VSCode: 111 | 112 | - `File > Preferences > Settings` 113 | - Search for `terminal integrated shell` 114 | - In the results you'll find a link to `Terminal > Integrated > Automation Shell: Windows` and a link to `Edit in settings.json`. Click the edit link and add the following to `settings.json`: 115 | 116 | ```json 117 | "terminal.integrated.shell.windows": "C:\\cmder\\vendor\\git-for-windows\\bin\\bash.exe", 118 | "terminal.integrated.automationShell.linux": "" 119 | ``` -------------------------------------------------------------------------------- /apex/.gitkeep: -------------------------------------------------------------------------------- 1 | # APEX Applications 2 | 3 | By default the build script included in this project will do some "extra" things for your APEX applications. 4 | 5 | - [Summary](#summary) 6 | - [App Version](#app-version) 7 | - [Error Handling Function](#error-handling-function) 8 | 9 | ## Summary 10 | 11 | Provides a brief summary of all changes you'll need to make in your APEX application to take advantage of all the features that build script provides: 12 | 13 | 1. Go to `Application Properties > Version` and set to `Release %RELEASE_VERSION%` (or whatever text your want but `%RELEASE_VERSION%` will be the version number) 14 | 15 | 16 | ## App Version 17 | 18 | Every APEX application has version attribute which is stored in `Application Properties > Version`. By default it's set to `Release 1.0` and shows up in the bottom left footer of your application. Manually changing this value for each release can be cumbersome and error prone. Instead use `%RELEASE_VERSION%` in the `Version` attribute and `%RELEASE_VERSION%` will be replaced automatically with your build version. 19 | 20 | For example set the `Version` to `Release %RELEASE_VERSION%` and when you "build" version `2.0.0` it will show up as `Release 2.0.0` in your application. 21 | 22 | 23 | ## Error Handling Function 24 | 25 | By default APEX will show system error messages as they are raised. Example `ORA-123: Some error message`. APEX allows you to *intercept* these error messages and display your own error message instead. This can be very useful to both provide a more user-friendly error message and log everything about the user's session to help with debugging. 26 | 27 | Below is an example of an error handling function that can easily be modified and augmented for your application. The base code was taken from the [Example of an Error Handling Function](https://docs.oracle.com/en/database/oracle/application-express/20.1/aeapi/Example-of-an-Error-Handling-Function.html#GUID-2CD75881-1A59-4787-B04B-9AAEC14E1A82) in the APEX documentation. *Again it's important to note that you can modify this to your project's needs and tools*. The error function does the following: 28 | 29 | - Provide a generic friendly error message along with a human readable error code for users to reference in support tickets 30 | - This error code can then be referenced in your logs 31 | - The error code will be a combination of the application ID and the current time 32 | - Logs everything about the error as well as all session state values 33 | - Will display the technical error message on screen if APEX is in developer mode (i.e. the developer is logged into the builder) 34 | 35 | Dependencies: 36 | 37 | - [Logger](https://github.com/oraopensource/logger) 38 | - [OOS Utils](https://github.com/OraOpenSource/oos-utils) 39 | 40 | To apply this custom error handling function in APEX go to `Shared Components > Application Definitions > Error Handling` set the option `Error Handling Function` to `pkg_apex.apex_error_handler` (assuming the function below is added to a package called `pkg_apex`). 41 | 42 | 43 | ```sql 44 | function apex_error_handler( 45 | p_error in apex_error.t_error) 46 | return apex_error.t_error_result 47 | as 48 | l_scope logger_logs.scope%type := gc_scope_prefix || 'apex_error_handler'; 49 | l_params logger.tab_param; 50 | 51 | l_return apex_error.t_error_result; 52 | 53 | c_reference_code constant varchar2(255) := to_char(apex_application.g_flow_id) || to_char(systimestamp, '.YY.DDD.SSSSS'); 54 | c_err_msg constant varchar2(255) := apex_string.format('An unexpected error has occurred. Please contact support with reference [%0].', c_reference_code); 55 | 56 | procedure generate_err_msg 57 | as 58 | begin 59 | -- Some situations want to use specific codes but also reference the l_reference error number 60 | l_return.message := c_err_msg; 61 | 62 | -- If exception occurs before a session has been initialized, there will 63 | -- be no session. So don't try to log any items. 64 | if apex_application.g_instance is not null then -- g_instance = session_id 65 | logger.log_apex_items( 66 | p_text => 'APEX unhandled exception (see logger_logs_apex_items)', 67 | p_scope => c_reference_code, 68 | p_level => logger.g_error 69 | ); 70 | end if; 71 | 72 | logger.log_cgi_env( 73 | p_scope => c_reference_code, 74 | p_level => logger.g_error 75 | ); 76 | 77 | logger.log_error(p_error.message, c_reference_code, p_error.additional_info, l_params); 78 | 79 | end generate_err_msg; 80 | 81 | begin 82 | logger.append_param(l_params, 'p_error.message', p_error.message); 83 | logger.append_param(l_params, 'p_error.additional_info', p_error.additional_info); 84 | logger.append_param(l_params, 'p_error.display_location', p_error.display_location); 85 | logger.append_param(l_params, 'p_error.association_type', p_error.association_type); 86 | logger.append_param(l_params, 'p_error.page_item_name', p_error.page_item_name); 87 | logger.append_param(l_params, 'p_error.region_id', p_error.region_id); 88 | logger.append_param(l_params, 'p_error.column_alias', p_error.column_alias); 89 | logger.append_param(l_params, 'p_error.row_num', p_error.row_num); 90 | logger.append_param(l_params, 'p_error.apex_error_code', p_error.apex_error_code); 91 | logger.append_param(l_params, 'p_error.is_internal_error', oos_util_string.to_char(p_error.is_internal_error)); 92 | logger.append_param(l_params, 'p_error.is_common_runtime_error', oos_util_string.to_char(p_error.is_common_runtime_error)); 93 | logger.append_param(l_params, 'p_error.ora_sqlcode', p_error.ora_sqlcode); 94 | logger.append_param(l_params, 'p_error.ora_sqlerrm', p_error.ora_sqlerrm); 95 | logger.append_param(l_params, 'p_error.error_backtrace', p_error.error_backtrace); 96 | logger.append_param(l_params, 'p_error.error_statement', p_error.error_statement); 97 | logger.append_param(l_params, 'p_error.component.type', p_error.component.type); 98 | logger.append_param(l_params, 'p_error.component.id', p_error.component.id); 99 | logger.append_param(l_params, 'p_error.component.name', p_error.component.name); 100 | logger.log('START', l_scope, null, l_params); 101 | 102 | l_return := apex_error.init_error_result (p_error => p_error ); 103 | 104 | -- If it's an internal error raised by APEX, like an invalid statement or 105 | -- code which can't be executed, the error text might contain security sensitive 106 | -- information. To avoid this security problem we can rewrite the error to 107 | -- a generic error message and log the original error message for further 108 | -- investigation by the help desk. 109 | if p_error.is_internal_error then 110 | -- mask all errors that are not common runtime errors (Access Denied 111 | -- errors raised by application / page authorization and all errors 112 | -- regarding session and session state) 113 | if not p_error.is_common_runtime_error then 114 | -- log error for example with an autonomous transaction and return 115 | -- l_reference_id as reference# 116 | -- l_reference_id := log_error ( 117 | -- p_error => p_error ); 118 | -- 119 | 120 | -- Change the message to the generic error message which doesn't expose 121 | -- any sensitive information. 122 | l_return.additional_info := null; 123 | generate_err_msg; 124 | end if; 125 | else 126 | -- Always show the error as inline error 127 | -- Note: If you have created manual tabular forms (using the package 128 | -- apex_item/htmldb_item in the SQL statement) you should still 129 | -- use "On error page" on that pages to avoid loosing entered data 130 | l_return.display_location := 131 | case 132 | when l_return.display_location = apex_error.c_on_error_page then apex_error.c_inline_in_notification 133 | else l_return.display_location 134 | end; 135 | 136 | -- 137 | -- Note: If you want to have friendlier ORA error messages, you can also define 138 | -- a text message with the name pattern APEX.ERROR.ORA-number 139 | -- There is no need to implement custom code for that. 140 | -- 141 | 142 | -- If it's a constraint violation like 143 | -- 144 | -- -) ORA-00001: unique constraint violated 145 | -- -) ORA-02091: transaction rolled back (-> can hide a deferred constraint) 146 | -- -) ORA-02290: check constraint violated 147 | -- -) ORA-02291: integrity constraint violated - parent key not found 148 | -- -) ORA-02292: integrity constraint violated - child record found 149 | -- 150 | -- we try to get a friendly error message from our constraint lookup configuration. 151 | -- If we don't find the constraint in our lookup table we fallback to 152 | -- the original ORA error message. 153 | -- if p_error.ora_sqlcode in (-1, -2091, -2290, -2291, -2292) then 154 | -- l_constraint_name := apex_error.extract_constraint_name (p_error => p_error ); 155 | 156 | -- begin 157 | -- select message 158 | -- into l_result.message 159 | -- from constraint_lookup 160 | -- where constraint_name = l_constraint_name; 161 | -- exception when no_data_found then null; -- not every constraint has to be in our lookup table 162 | -- end; 163 | -- end if; 164 | 165 | -- If an ORA error has been raised, for example a raise_application_error(-20xxx, '...') 166 | -- in a table trigger or in a PL/SQL package called by a process and we 167 | -- haven't found the error in our lookup table, then we just want to see 168 | -- the actual error text and not the full error stack with all the ORA error numbers. 169 | if p_error.ora_sqlcode is not null and l_return.message = p_error.message then 170 | l_return.message := apex_error.get_first_ora_error_text (p_error => p_error ); 171 | generate_err_msg; 172 | end if; 173 | 174 | -- If no associated page item/tabular form column has been set, we can use 175 | -- apex_error.auto_set_associated_item to automatically guess the affected 176 | -- error field by examine the ORA error for constraint names or column names. 177 | if l_return.page_item_name is null and l_return.column_alias is null then 178 | apex_error.auto_set_associated_item ( 179 | p_error => p_error, 180 | p_error_result => l_return); 181 | end if; 182 | end if; 183 | 184 | -- If developer is enabled show additional error information 185 | if 1=1 186 | and l_return.message != p_error.message 187 | and oos_util_apex.is_developer 188 | then 189 | l_return.message := l_return.message || '

*** Technical Details (Developers Only) ***
' || p_error.message; 190 | end if; 191 | 192 | logger.log('END', l_scope); 193 | return l_return; 194 | exception 195 | 196 | when others then 197 | logger.log_error('Unhandled Exception', l_scope, null, l_params); 198 | raise; 199 | end apex_error_handler; 200 | ``` 201 | -------------------------------------------------------------------------------- /build/README.md: -------------------------------------------------------------------------------- 1 | # Build Scripts 2 | 3 | This folder contains scripts to help build a release 4 | 5 | - [Build a Release](#build-a-release) 6 | 7 | ## Build a Release 8 | 9 | To build a release simply run the command below. It is recommended that you build a release each time before running the release. 10 | 11 | ```bash 12 | # Change "version" for your version number 13 | ./build.sh version 14 | ``` 15 | 16 | This script does the following: 17 | - Scrapes the `views` and `packages` folder and generate `release/all_views.sql` and `release/all_packages.sql` 18 | - Generates a script to map some OS environment variables to SQL (`release/load_env_vars.sql`) 19 | - Generates the install commands for all the APEX applications and stores in `release/all_apex.sql` -------------------------------------------------------------------------------- /build/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ./build.sh 4 | # Parameters 5 | # version: This is embedded in the APEX application release. 6 | 7 | if [ -z "$1" ]; then 8 | echo 'Missing version number' 9 | exit 0 10 | fi 11 | 12 | VERSION=$1 13 | 14 | # This is the directory that this file is located in 15 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 16 | # echo "Start Dir: $SCRIPT_DIR\n" 17 | 18 | # Load Helper and config 19 | source $SCRIPT_DIR/../scripts/helper.sh 20 | 21 | 22 | echo -e "*** Listing all views and packages ***\n" 23 | list_all_files views release/all_views.sql $EXT_VIEW 24 | list_all_files packages release/all_packages.sql $EXT_PACKAGE_SPEC,$EXT_PACKAGE_BODY 25 | 26 | # TODO #10 APEX Nitro configuration 27 | # echo -e "*** APEX Nitro Publish ***\n" 28 | # apex-nitro publish gre 29 | 30 | # Export APEX applications, defined in project-config.sh 31 | export_apex_app $VERSION 32 | 33 | # # Generate release support sql files 34 | gen_release_sql -------------------------------------------------------------------------------- /data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/data/.gitkeep -------------------------------------------------------------------------------- /data/README.md: -------------------------------------------------------------------------------- 1 | # Data 2 | 3 | This folder is for re-runnable data scripts. Re-runnable data scripts are most common for List of Value (LOV) / lookup tables. This allows developers to easily manage the LOV data rather than end users. It also prevents having to manually write updates each time one is required. It is recommended to name each file `data_table_name.sql`. When opening files in VSCode using the [Command Palette](https://code.visualstudio.com/docs/getstarted/tips-and-tricks#_quick-open) you can quickly find `data` scripts when using the Command Palette's [Quick Open](https://code.visualstudio.com/docs/getstarted/tips-and-tricks#_quick-open) feature. *Note: other modern text editors have similar functionality* 4 | 5 | *Note: If you have one-off data updates it should be part of a [release](../release/)* 6 | 7 | A [template](../templates/template_data.sql) is provided for data scripts. It uses a `merge` concept that joins LOV values on codes. By joining on codes rather than IDs it removes any differences in sequences that may occur across environments. 8 | 9 | -------------------------------------------------------------------------------- /docs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/docs/.gitkeep -------------------------------------------------------------------------------- /lib/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/lib/.gitkeep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apex-starter", 3 | "version": "1.0.0", 4 | "description": "apex starter project template", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://gitlab.com/insumsolutions/internal_insum/project-templates/apex-starter-project-template.git" 12 | }, 13 | "keywords": [ 14 | "apex", 15 | "starter", 16 | "project", 17 | "template" 18 | ], 19 | "author": "Gino Suarez", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://gitlab.com/insumsolutions/internal_insum/project-templates/apex-starter-project-template/issues" 23 | }, 24 | "homepage": "https://gitlab.com/insumsolutions/internal_insum/project-templates/apex-starter-project-template#readme" 25 | } 26 | -------------------------------------------------------------------------------- /packages/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/packages/.gitkeep -------------------------------------------------------------------------------- /release/README.md: -------------------------------------------------------------------------------- 1 | # Release 2 | 3 | - [Structure](#structure) 4 | - [`_release.sql`](#_releasesql) 5 | - [`all_....sql` files](#all_sql-files) 6 | - [The `code` folder](#the-code-folder) 7 | - [Release Process](#release-process) 8 | - [Concept 1: Code is tagged each time it is run in Production](#concept-1-code-is-tagged-each-time-it-is-run-in-production) 9 | - [Concept 2: Code is tagged each time it leaves Dev](#concept-2-code-is-tagged-each-time-it-leaves-dev) 10 | - [Running a Release in Production](#running-a-release-in-production) 11 | - [Tips](#tips) 12 | - [Run Release Manually](#run-release-manually) 13 | 14 | This folder contains examples on how to structure release along with various options on **how** to do a release. Before proceeding you should have a [Git workflow](../README.md#git-workflows) as some of the `git` examples may need to be altered to cater towards the chosen workflow. 15 | 16 | ## Structure 17 | 18 | Some files are provided by default to help guide your release process 19 | 20 | 21 | ### `_release.sql` 22 | 23 | Example release script. This is the only file that will be run for each release. Inside `_release.sql` it references other files that are required for the release. By having a single consistent file for each release it helps simplify release scripts if using automated scripting tools. 24 | 25 | You must review this file and modify it accordingly for your project needs. Again, every project is different and it's expected that this file should be modified to meet your project's needs. 26 | 27 | ### `all_....sql` files 28 | 29 | They're several `all*.sql` files which are described below. All of the files that are flagged as auto generated are generated during the [build](../build) process. 30 | 31 | File | Auto Generated | Description 32 | --- | --- | --- 33 | [`all_apex.sql`](all_apex.sql) | Yes | Will install all the APEX applications as defined in [`scripts/project-config.sh`](../scripts/project-config.sh) 34 | [`all_data.sql`](all_data.sql) | No | Runs all the re-runnable data scripts. You must manually add references to this file as order matters 35 | [`all_packages.sql`](all_packages.sql) | Yes | References all the packages in the `packages` folder. By default this will run all the `.pks` files first then the `pkb` files next 36 | [`all_views.sql`](all_views.sql) | Yes | References all the views in the `views` folder 37 | [`load_env_vars.sql`](load_env_vars.sql) | Yes | Loads some of the environment variables, defined in [`scripts/project-config.sh`](../scripts/project-config.sh), into SQL session 38 | 39 | 40 | 41 | ### The `code` folder 42 | 43 | This folder stores non-rerunable code specific to each release. It's recommended to create a file per-ticket. Ex: `code/issue-123.sql`. The contents of `code/issue-123.sql` may contain things such as DDL and DML statements. Re-runnable code (such as views, packages, etc) **should not** be in here. Instead, store them in their appropriate folders included with this project. After each release the contents of the `code` folder will be deleted as it is no longer required. 44 | 45 | Each file created in the `code` should be added to `code/_run_release_code.sql`. This file should be cleared after each release. Examples of what `code/_run_release_code.sql` would look like: 46 | 47 | ```sql 48 | @issue-123.sql 49 | @issue-456.sql 50 | ``` 51 | 52 | 53 | ## Release Process 54 | 55 | Oracle releases are very "unforgiving" (i.e. if an error occurs it can ruin the entire rest of release and make it difficult to back out). This can make it very difficult to run standard CI/CD tools to dor releases in Oracle as one error can completely corrupt an environment and it may not be easy to "restore" the schema to a point in time before the release. *Note: Making releases scripts re-runnable or providing roll back support is not impossible however it can be very expensive to do so properly.*, 56 | 57 | The rest of this section assumes the following: 58 | - Release process is not re-runnable. For example if a DDL statement fails there's no way to tell if it's already been run. 59 | - There is no rollback script to undo all the changes in a given release 60 | - Code goes from `Dev > Test > Prod` 61 | - Code can be manually run in the Test environment 62 | 63 | The sections below cover two types of strategies for doing releases. Neither is "better" than the other, they just cover different team and cultural situations. The following assumptions are made for your development process: 64 | 65 | - "Short" development sprints of ~2 weeks 66 | - Development team works on block of code, gets released to Test, and then goes to Production quickly 67 | - Active development occurs in `master` branch 68 | 69 | 70 | Examples below show the process to develop release `1.0.0` of the application. The following variables have been defined to help make these scripts re-runnable: 71 | 72 | ```bash 73 | RELEASE_VERSION=1.0.0 74 | GIT_PRE_RELEASE_BRANCH=pre-release-$RELEASE_VERSION 75 | ``` 76 | 77 | *Note: [semantic versioning](https://semver.org/) is recommended for version numbers. TL;DR: `major.minor.patch` numbering.* 78 | 79 | ### Concept 1: Code is tagged each time it is run in Production 80 | 81 | In this process code is tagged each time it **goes to Prod**. This means that every time a patch is applied in Test the release script must be manually updated. This poses a risk in that the order the patches are applied matters. The positive is that it can make releases simpler but requires more manual diligence. The follow example walks through the process. 82 | 83 | - Once the initial sprint is complete code is tagged in a "pre-release" branch: 84 | 85 | ```bash 86 | git checkout -b $GIT_PRE_RELEASE_BRANCH master 87 | git push --set-upstream origin 88 | ``` 89 | 90 | - [Run the release manually](#run-release-manually) in the Test environment 91 | 92 | If a bug in Test is found and need to be patched the following will occur (assuming one ddl and one view needs to change) 93 | 94 | ```bash 95 | git checkout $GIT_PRE_RELEASE_BRANCH 96 | # edit release/code/issue-123.sql with new DDL statement 97 | # edit views/my_view.sql 98 | 99 | # Connect to TEST environment and manually run ddl statement 100 | # sqlcl 101 | # 102 | # alter table my_table add (new_col varchar2); 103 | # 104 | # @views/my_view.sql 105 | # exit 106 | 107 | 108 | # Commit changes 109 | git add * 110 | git commit -m "patch fix" 111 | git push 112 | 113 | # Merge changes back to master 114 | git checkout master 115 | git merge $GIT_PRE_RELEASE_BRANCH 116 | ``` 117 | 118 | **Important note:** It is critical that when modifying a file with a DDL the DDL is put in the correct order it *should* have been run. 119 | 120 | This patching cycle may happen various times. Once is **100% certified** tag the release: 121 | 122 | ```bash 123 | git checkout $GIT_PRE_RELEASE_BRANCH 124 | git tag $RELEASE_VERSION 125 | git push origin --tags 126 | 127 | # Remove the pre-release branch 128 | git checkout master 129 | git push --delete origin $GIT_PRE_RELEASE_BRANCH 130 | git branch -d $GIT_PRE_RELEASE_BRANCH 131 | 132 | # Cleanup the release folder 133 | # Where 134 | # For example if the project's base folder is in /users/martin/git/starter-project-template than the call would be: reset_release starter-project-template 135 | source scripts/helper.sh 136 | reset_release 137 | ``` 138 | 139 | To run the release in production see [Running in Production](#running-a-release-in-production) below. 140 | 141 | 142 | 143 | ### Concept 2: Code is tagged each time it leaves Dev 144 | 145 | This method is similar to Concept 1 except that it tags code each time it **leaves Dev** (rather than going into production). 146 | 147 | The positive aspect with this approach is that it reduces the risk that releases won't run properly in production. In the first concept when a patch is applied it is manually applied and the release script is altered. There exists possibility that whom ever is patching it may get the order wrong so that when it goes to production it could break. 148 | 149 | The *negative* aspect with this approach is that many releases are created. For example suppose version `1.0.0` is created but a few bugs are found during the testing phase. Versions `1.0.1`, `1.0.2`, and `1.0.3` may also be created for each patch. When going to production all four releases must be deployed. At first glance this sounds like a lot of work but is not much overhead. 150 | 151 | The follow example shows how this concept is done in git: 152 | 153 | - Once initial code is ready to be deploy to Test, fix any issues, merge back changes, and tag release. 154 | 155 | ```bash 156 | git checkout -b $GIT_PRE_RELEASE_BRANCH master 157 | git push --set-upstream origin 158 | ``` 159 | 160 | - [Run the release manually](#run-release-manually) in the Test environment 161 | - Merge changes back into `master` (in case any changes were made), tag code, and clean up release. 162 | 163 | ```bash 164 | # Make sure any changes made/corrected during the release (in the GIT_PRE_RELEASE_BRANCH) have been committed 165 | # Merging changes back into master 166 | git checkout master 167 | # Get latest code changes. 168 | git pull origin master 169 | git merge $GIT_PRE_REL_BRANCH 170 | git push origin master 171 | 172 | 173 | # Tag release 174 | git checkout $GIT_PRE_REL_BRANCH 175 | git tag $RELEASE_VERSION 176 | git push origin --tags 177 | 178 | # Remove pre-release branch (i.e. cleanup) 179 | git checkout master 180 | git push --delete origin $GIT_PRE_REL_BRANCH 181 | git branch -d $GIT_PRE_REL_BRANCH 182 | 183 | # Cleanup the release folder 184 | # Where 185 | # For example if the project's base folder is in /users/martin/git/starter-project-template than the call would be: reset_release starter-project-template 186 | source scripts/helper.sh 187 | reset_release 188 | ``` 189 | 190 | Each time a code needs to go to the Test environment use the same scripts above. I.e. create another release and run in Test. 191 | 192 | 193 | Once everything is ready to to production the [Running in Production](#running-a-release-in-production) section still applies. Instead of running just one release you may need to run multiple releases to upgrade production. In our example at the beginning of this sections `1.0.0`, `1.0.1`, `1.0.2`, and `1.0.3` would all need to be run. 194 | 195 | 196 | 197 | ### Running a Release in Production 198 | 199 | To run a release in Production automatically: 200 | 201 | ```bash 202 | # Assume you're starting in root folder 203 | # Load helper (only required to define $SQLCL variable. Can always hard code the SQLcl executable) 204 | source scripts/helper.sh 205 | # Get tag 206 | git checkout tags/ 207 | cd release 208 | # Release includes "exit" as last line 209 | $SQLCL @_release.sql 210 | # Release does not include "exit" as last line (the default _release.sql contains an exit statement) 211 | # echo exit | $SQLCL @_release.sql 212 | 213 | # Show if any errors occurred (optional) 214 | # Add additional logic that is applicable to handle successful and invalid releases 215 | if [ $? -eq 0 ] ; then 216 | echo "Release successful" 217 | else 218 | echo "Release: **ERRORS** " 219 | fi 220 | ``` 221 | 222 | 223 | ## Tips 224 | 225 | ### Run Release Manually 226 | 227 | Running the release manually can help catch errors quickly and fix them right where they occur. When using the right tools and techniques this can be done very quickly. 228 | 229 | Assumptions: 230 | - Using [Visual Studio Code](https://code.visualstudio.com/) (VSC) (or similar text editor) 231 | - Terminal is set to `bash` (TODO: Link to cmder documentation for windows users) 232 | - Shortcuts setup to quickly navigate between current file and terminal. In VSC `Preferences > Keyboard Shortcuts`: (*Windows users change `cmd` to `ctrl`*) 233 | - Set: `View: Focus First Editor Group` to `cmd+1` 234 | - Set: `Terminal: Focus Terminal` to `cmd+2` 235 | - Now you can toggle between the editor and terminal window using `⌘+1` and `⌘+2`. This is critical to scroll through large files, copy from the editor, then past in the terminal (in a SQLcl session) 236 | 237 | 238 | Open the [Terminal window](https://code.visualstudio.com/docs/editor/integrated-terminal) (`Terminal > New Terminal`) and run: 239 | 240 | ```bash 241 | cd 242 | 243 | cd release 244 | # Open _release.sql file in VSC 245 | code _release.sql 246 | 247 | # Connect to DB 248 | source ../scripts/helper.sh 249 | $SQLCL 250 | ``` 251 | 252 | Once this is done manually run through the sections in `_release.sql` and copy and paste each section(s) of code into the terminal as shown below. When/if the `_release.sql` contains files that are referenced in the `release/code` folder it's recommended you open those up manually and do the same thing (i.e. copy and paste). 253 | 254 | ![VSC-demo](../.vscode/img/vsc-manual-release.gif) -------------------------------------------------------------------------------- /release/_release.sql: -------------------------------------------------------------------------------- 1 | -- If you want to add ASCII Art: https://asciiartgen.now.sh/?style=standard 2 | -- *** DO NOT MODIFY: HEADER SECTION *** 3 | clear screen 4 | 5 | whenever sqlerror exit sql.sqlcode 6 | 7 | prompt loading environment variables 8 | @load_env_vars.sql 9 | 10 | 11 | -- define - Sets the character used to prefix substitution variables 12 | -- Note: if you change this you need to modify every reference of it in this file and any referring files 13 | -- set define '&' 14 | -- verify off prevents the old/new substitution message 15 | set verify off 16 | -- feedback - Displays the number of records returned by a script ON=1 17 | set feedback on 18 | -- timing - Displays the time that commands take to complete 19 | set timing on 20 | -- display dbms_output messages 21 | set serveroutput on 22 | -- disables blank lines in code 23 | set sqlblanklines off; 24 | 25 | 26 | -- Log output of release 27 | define logname = '' -- Name of the log file 28 | 29 | set termout on 30 | column my_logname new_val logname 31 | select 'release_log_'||sys_context( 'userenv', 'service_name' )|| '_' || to_char(sysdate, 'YYYY-MM-DD_HH24-MI-SS')||'.log' my_logname from dual; 32 | -- good to clear column names when done with them 33 | column my_logname clear 34 | set termout on 35 | spool &logname 36 | prompt Log File: &logname 37 | 38 | 39 | 40 | prompt check DB user is expected user 41 | declare 42 | begin 43 | if user != '&env_schema_name' or '&env_schema_name' is null then 44 | raise_application_error(-20001, 'Must be run as &env_schema_name'); 45 | end if; 46 | end; 47 | / 48 | 49 | -- Disable APEX apps 50 | @../scripts/apex_disable.sql &env_apex_app_ids 51 | 52 | 53 | -- *** END: HEADER SECTION *** 54 | 55 | 56 | -- *** Release specific tasks *** 57 | 58 | @code/_run_code.sql 59 | 60 | -- *** DO NOT MODIFY BELOW *** 61 | 62 | 63 | -- Views and packages will be automatically referenced in the files below 64 | -- Can generate these files from the build script 65 | -- Search for "list_all_files" 66 | @all_views.sql 67 | @all_packages.sql 68 | @all_triggers.sql 69 | 70 | 71 | prompt Invalid objects 72 | select object_name, object_type 73 | from user_objects 74 | where status != 'VALID' 75 | order by object_name 76 | ; 77 | 78 | 79 | -- *** DATA **** 80 | 81 | 82 | -- Autogenerated triggers 83 | -- If you have code to automatically generate triggers this is a good place to run in. 84 | -- An example of what it might look like: 85 | 86 | -- declare 87 | -- begin 88 | -- pkg_util.gen_triggers() 89 | -- end; 90 | -- / 91 | 92 | 93 | -- Load any re-runnable data scripts 94 | @all_data.sql 95 | 96 | 97 | -- This needs to be in place after trigger generation as some triggers follow the generated triggers above 98 | prompt recompile invalid schema objects 99 | begin 100 | dbms_utility.compile_schema(schema => user, compile_all => false); 101 | end; 102 | / 103 | 104 | -- *** APEX *** 105 | -- Install all apex applications 106 | @all_apex.sql 107 | 108 | 109 | -- Control Build Options (optional) 110 | -- In some cases you may want to enable / disable various build options for an application depending on the environment 111 | -- An example is provided below on how to enabled a build option 112 | PROMPT *** APEX Build option *** 113 | 114 | -- set serveroutput on size unlimited; 115 | -- declare 116 | -- c_app_id constant apex_applications.application_id%type := CHANGEME_APPLICATION_ID; 117 | -- c_username constant varchar2(30) := user; 118 | 119 | -- l_build_option_id apex_application_build_options.build_option_id%type; 120 | -- begin 121 | -- if pkg_environment.is_dev() then 122 | -- select build_option_id 123 | -- into l_build_option_id 124 | -- from apex_application_build_options 125 | -- where 1=1 126 | -- and application_id = c_app_id 127 | -- and build_option_name='DEV_ONLY'; 128 | 129 | -- -- Session is already active ahead 130 | -- apex_session.create_session ( 131 | -- p_app_id => c_app_id, 132 | -- p_page_id => 1, 133 | -- p_username => c_username ); 134 | 135 | -- apex_util.set_build_option_status( 136 | -- p_application_id => c_app_id, 137 | -- p_id => l_build_option_id, 138 | -- p_build_status=>'INCLUDE'); 139 | -- end if; 140 | 141 | -- end; 142 | -- / 143 | 144 | -- commit; 145 | 146 | 147 | spool off 148 | exit -------------------------------------------------------------------------------- /release/all_apex.sql: -------------------------------------------------------------------------------- 1 | -- GENERATED from build/build.sh DO NOT modify this file. 2 | prompt *** APEX Installation *** 3 | prompt *** App: 4000161 *** -------------------------------------------------------------------------------- /release/all_data.sql: -------------------------------------------------------------------------------- 1 | -- This file must be manually modified as order matters when creating data records 2 | -- Data files are re-runnable data scripts 3 | -- ex: @../data/data_my_table.sql -------------------------------------------------------------------------------- /release/all_packages.sql: -------------------------------------------------------------------------------- 1 | -- GENERATED from build/build.sh DO NOT modify this file directly as all changes will be overwritten upon next build 2 | -- Automated listing for packages 3 | prompt @../packages/*.pks 4 | @../packages/*.pks 5 | prompt @../packages/*.pkb 6 | @../packages/*.pkb 7 | -------------------------------------------------------------------------------- /release/all_triggers.sql: -------------------------------------------------------------------------------- 1 | -- GENERATED from build/build.sh DO NOT modify this file directly as all changes will be overwritten upon next build 2 | -- Automated listing for triggers -------------------------------------------------------------------------------- /release/all_views.sql: -------------------------------------------------------------------------------- 1 | -- GENERATED from build/build.sh DO NOT modify this file directly as all changes will be overwritten upon next build 2 | -- Automated listing for views 3 | prompt @../views/*.sql 4 | @../views/*.sql 5 | -------------------------------------------------------------------------------- /release/code/_run_code.sql: -------------------------------------------------------------------------------- 1 | -- Release specific references to files in this folder 2 | -- This file is automatically executed from the /release/_release.sql file 3 | -- 4 | -- Ex: @code/issue-123.sql 5 | -------------------------------------------------------------------------------- /release/code/backout.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/release/code/backout.sql -------------------------------------------------------------------------------- /release/load_env_vars.sql: -------------------------------------------------------------------------------- 1 | -- GENERATED from build/build.sh DO NOT modify this file directly as all changes will be overwritten upon next build\n\n 2 | define env_schema_name= 3 | define env_apex_app_ids= 4 | define env_apex_workspace= 5 | 6 | 7 | prompt ENV variables 8 | select 9 | '&env_schema_name.' env_schema_name, 10 | '&env_apex_app_ids.' env_apex_app_ids, 11 | '&env_apex_workspace.' env_apex_workspace 12 | from dual; 13 | 14 | 15 | -------------------------------------------------------------------------------- /scripts/.gitignore: -------------------------------------------------------------------------------- 1 | user-config.sh -------------------------------------------------------------------------------- /scripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/scripts/.gitkeep -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # Scripts 2 | 3 | Files in this folder can be used for multiple purposes such as VSCode compilation, build scripts, etc 4 | 5 | - [Files](#files) 6 | - [`apex_disable.sql`](#apex_disablesql) 7 | - [`apex_export_app.sql`](#apex_export_appsql) 8 | - [`helper.sh`](#helpersh) 9 | - [Environment Variables](#environment-variables) 10 | - [Functions](#functions) 11 | - [`export_apex_app`](#export_apex_app) 12 | - [`gen_object`](#gen_object) 13 | - [`list_all_files`](#list_all_files) 14 | - [`reset_release`](#reset_release) 15 | - ['merge_sql_files`](#merge_sql_files) 16 | - [`project-config.sh`](#project-configsh) 17 | - [`user-config.sh`](#user-configsh) 18 | 19 | ## Files 20 | 21 | **You will need to configure both `project-config.sh` and `user-config.sh` upon first use** 22 | 23 | File | Description 24 | --- | --- 25 | [`apex_export_app.sql`](#apex_export_appsql) | Exports an APEX application 26 | [`helper.sh`](#helpersh) | Helper functions that all other scripts should call. Loads `config.sh` 27 | `project-config.sh` | Project configuration 28 | [`user-config.sh`](#user-configsh) | This file will be automatically generated when any bash script is run for the first time. It is self documenting. 29 | 30 | 31 | ## `apex_disable.sql` 32 | 33 | This script will disable (sets the application's status to Unavailable). It's main purpose is to disable the application at the start of a release so users don't use it while the schema is being upgraded. By default this is called in [`../release/_release.sql`]. 34 | 35 | **This script performs a `commit` at the end.** 36 | 37 | Parameter | Description 38 | --- | --- 39 | `1` | Comma delimited list of application IDs 40 | 41 | Example: 42 | 43 | ```bash 44 | echo exit | sqlcl martin/password123@localhost:32118/xe @apex_disable.sql 100,200 45 | ``` 46 | 47 | 48 | 49 | ## `apex_export_app.sql` 50 | 51 | This script requires [SQLcl](https://www.oracle.com/ca-en/database/technologies/appdev/sqlcl.html) 52 | 53 | Parameter | Description 54 | --- | --- 55 | `1` | Application ID 56 | `2` | *Optional* APEX Export options (ex: `-split`) 57 | 58 | 59 | Examples: 60 | 61 | ```bash 62 | # Export to f100.sql 63 | echo exit | sqlcl martin/password123@localhost:32118/xe @apex_export_app.sql 100 64 | 65 | # Export to Application 100 as split files 66 | echo exit | sqlcl martin/password123@localhost:32118/xe @apex_export_app.sql 100 -split 67 | ``` 68 | 69 | 70 | ## `helper.sh` 71 | 72 | Documentation below references `project root folder`. This is the base folder that the git project is located on your computer. Ex: `/Users/martin/git/insum/starter-project-template/` 73 | 74 | To load the helper functions run: `source scripts/helper.sh` (*assuming you are in the project's root folder*). This will load some environment variables and load/verify configurations for this release. 75 | 76 | ### Environment Variables 77 | 78 | Name | Description 79 | --- | --- 80 | `SCRIPT_DIR` | Directory that the helper file is located in. Using the example above this will return: `/Users/martin/git/insum/starter-project-template/scripts` 81 | `PROJECT_DIR` | Root directory of the git repo that is associated to this file. Ex: `/Users/martin/git/insum/starter-project-template/` 82 | 83 | 84 | ### Functions 85 | 86 | #### `export_apex_app` 87 | 88 | Exports APEX applications and also splits the export file. APEX exports will be stored in `/apex` folder. The list of applications to export is defined in `scripts/project-config.sh` variable `APEX_APP_IDS` 89 | 90 | **Parameters** 91 | Position | Required | Description 92 | --- | --- | --- 93 | `$1` | Optional | Application version. If defined will search the exported application file for `%RELEASE_VERSION%` and replaced it with this variable. See the [`apex`](../apex/) documentation for more information. 94 | 95 | **Example** 96 | 97 | ```bash 98 | # No app version 99 | export_apex_app 100 | 101 | # App version 102 | export_apex_app 1.2.3 103 | ``` 104 | 105 | #### `gen_object` 106 | 107 | Generates a file based on template file. (see example below for more description) 108 | 109 | **Parameters** 110 | Position | Required | Description 111 | --- | --- | --- 112 | `$1` | Required | Object type. By default this is `package, view, data`) 113 | `$2` | Required | Object name. Will be new file name along with replacing all reference of `CHANGEME` in file 114 | 115 | 116 | **Example** 117 | 118 | Suppose you wanted to quickly create a new package (`pkg_emp`). By default in the [`templates`](../templates) folder there exists two files [`template_pkg.pks`](../templates/template_pkg.pks) and [`template_pkg.pkb`](../templates/template_pkg.pkb). In the past you'd need to copy these two files, rename them, then modify the `CHANGEME`s and replace with your package name. Now you can simply: 119 | 120 | ```bash 121 | source ./scripts/helper.sh 122 | gen_object package pkg_emp 123 | ``` 124 | 125 | This will then automatically create two new files `packages/pkg_emp.pks` and `packages/pkg_emp.pkb`. In VSCode there's also a task for this to avoid any command line 126 | 127 | **Configuration** 128 | 129 | To modify the different types of available templates modify [`scripts/project-config.sh`](project-config.sh) and look for `OBJECT_FILE_TEMPLATE_MAP` (it is self documenting) 130 | 131 | #### `list_all_files` 132 | 133 | It is very rare that you'd need to run this function on it's own as it's called as part of the [`build`](../build) process. This function will list all the files in a folder and output the results with `@../` prefix in a specified output file. This is useful when wanting to automatically compile all packages and views as part of the build. 134 | 135 | **Parameters** 136 | Position | Required | Description 137 | --- | --- | --- 138 | `$1` | Required | Folder that you want to list files from. **Note:** this folder is the folder name **relative** to the project's root folder. I.e. for `views` you would specify `views` and **not** `/Users/martin/git/insum/starter-project-template/views` 139 | `$2` | Required | File to store results in 140 | `$3` | Optional | Comma delimited list of file extensions to search for. Default: `sql`. Note the order of the list matters. For example if `pks,pkb` all the `pks` (spec) files will be listed first then the `pkb` (body) files will be listed second. 141 | 142 | **Example** 143 | ```bash 144 | # Generate all the views 145 | list_all_files views release/all_views.sql sql 146 | 147 | # Generate all the packages 148 | # Note pks is before pkb so that the specs get listed before the body 149 | list_all_files packages release/all_packages.sql pks,pkb 150 | ``` 151 | 152 | #### `reset_release` 153 | 154 | Resets the project's root release folder. Because resetting will erase everything in the `release/code` folder and reset `release/code/_run_code.sql` this function requires that an additional parameter is passed in to ensure that nothing is deleted by mistake. 155 | 156 | **Parameters** 157 | Position | Required | Description 158 | --- | --- | --- 159 | `$1` | Required | project root directory name. If this root folder is `/Users/martin/git/insum/starter-project-template/` then this parameter will be `starter-project-template` 160 | 161 | **Example** 162 | 163 | ```bash 164 | # Show what happens when no parameter is passed in 165 | # Note the error message will show what call to make 166 | reset_release 167 | 168 | Error: confirmation directory missing or not matching. Run: reset_release starter-project-template 169 | 170 | # Correct run 171 | 172 | reset_release starter-project-template 173 | ``` 174 | 175 | 176 | #### 'merge_sql_files` 177 | 178 | Merges multiple files into a single file. This is useful when you can't reference multiple files easily in a release (ex: deploying to apex.oracle.com). 179 | This will keep any existing commands (ex `alter table, update, etc`) but will expand any line that starts with `@s`. 180 | 181 | **Parameters** 182 | Position | Required | Description 183 | --- | --- | --- 184 | `$1` | Required | "root" input file 185 | `$2` | Required | Output (merged) file 186 | 187 | **Example** 188 | 189 | Suppose your file structure is as follows: 190 | 191 | ``` 192 | /release 193 | _release.sql 194 | update config set release_date = sysdate; 195 | @all_packages.sql 196 | @all_packages.sql 197 | @..packages/pkg_emp.pks 198 | @..packages.pkg_emp.pkb 199 | ``` 200 | 201 | If you then run: 202 | 203 | ```bash 204 | cd /release 205 | merge_sql_files _release.sql merged_release.sql 206 | ``` 207 | 208 | It will then create `merged_release.sql` with the following: 209 | ```sql 210 | -- _release.sql 211 | update config set release_date = sysdate; 212 | -- 213 | -- referencing @all_packages.sql 214 | -- 215 | -- referencing @..packages/pkg_emp.pks 216 | -- 217 | create or replace package pkg_emp 218 | ... 219 | -- 220 | -- referencing @..packages/pkg_emp.pkb 221 | create or replace package body pkg_emp 222 | ... 223 | ``` 224 | 225 | ## `project-config.sh` 226 | This file contains information about your project (such as schema name, APEX applications, etc.). It is common for all developers and changes are saved in git. **Do not** put any sensitive information in this file (`user-config.sh` is for sensitive information). 227 | 228 | ## `user-config.sh` 229 | The first time you run any bash script an error will be displayed and a new file (`scripts/user-config.sh`) will be created. `user-config.sh` is self documented and requires some configuration before the build will work. 230 | 231 | `user-config.sh` is in the `.gitignore` file so you can store more sensitive information without it being checked in. -------------------------------------------------------------------------------- /scripts/apex_disable.sql: -------------------------------------------------------------------------------- 1 | -- Disables comma delimited list of APEX applications 2 | -- This is primarily used at the start of an APEX release process 3 | -- Commit is applied at the end of this file. If not then the app won't be disabled for end users 4 | -- 5 | -- Parameters 6 | -- 1: Comma delimited list of application IDs. Ex: 100,200 7 | -- 8 | -- 9 | prompt Disable APEX Application(s) 10 | declare 11 | c_app_ids constant varchar2(500) := '&1.'; 12 | c_username constant varchar2(30) := user; 13 | 14 | l_apex_app_ids apex_t_varchar2; 15 | begin 16 | l_apex_app_ids := apex_string.split(p_str => c_app_ids, p_sep => ','); 17 | 18 | -- Note if getting error "ORA_20987 to catch the error: ORA-20987: APEX - An API call has been prohibited." 19 | -- Change your Application Security Settings 20 | -- Shared Components > Security Attributes > Runtime API Usage: 21 | -- - Check "Modify This Application" 22 | 23 | for i in l_apex_app_ids.first .. l_apex_app_ids.last loop 24 | 25 | apex_session.create_session ( 26 | p_app_id => l_apex_app_ids(i) , 27 | p_page_id => 1, 28 | p_username => c_username ); 29 | 30 | apex_util.set_application_status( 31 | p_application_id => l_apex_app_ids(i) , 32 | p_application_status => 'UNAVAILABLE', 33 | p_unavailable_value => 'Scheduled update of application.'); 34 | 35 | -- See https://github.com/insum-labs/starter-project-template/issues/28 for full description 36 | apex_session.detach; 37 | 38 | end loop; 39 | 40 | commit; -- Commit required to ensure the disabling of application is applied 41 | end; 42 | / 43 | -------------------------------------------------------------------------------- /scripts/apex_export.sql: -------------------------------------------------------------------------------- 1 | 2 | -- Run in SQLcl 3 | -- 4 | -- Parameters: 5 | -- 1: Application ID 6 | -- 2: Export options (optional). For example: "-split" 7 | -- 8 | -- Examples 9 | -- 10 | -- Export application 100 into f100.sql 11 | -- @apex_export_app 100 12 | -- 13 | -- Export application 100 into a split file 14 | -- @apex_export_app 100 -split 15 | -- 16 | set termout off 17 | set verify off 18 | 19 | 20 | -- From: https://stackoverflow.com/questions/13474899/default-value-for-paramteters-not-passed-sqlplus-script 21 | -- and: http://vbegun.blogspot.com/2008/04/on-sqlplus-defines.html 22 | -- Allow for optional value of 2 23 | column 1 new_value 1 24 | column 2 new_value 2 25 | select '' "1", '' "2" 26 | from dual 27 | where rownum = 0; 28 | 29 | define 1 30 | define 2 31 | 32 | define APP_ID = "&1" 33 | define EXPORT_OPTIONS = "&2" 34 | 35 | set termout on 36 | set serveroutput on 37 | begin 38 | dbms_output.put_line ( 'App ID: &APP_ID' ); 39 | dbms_output.put_line ( 'Export Options: &EXPORT_OPTIONS' ); 40 | dbms_output.put_line ( '------------------' ); 41 | end; 42 | / 43 | set serveroutput off 44 | -- end 45 | 46 | -- spool f&APP_ID..sql 47 | apex export -applicationid &APP_ID -dir apex &EXPORT_OPTIONS 48 | -- spool off 49 | -------------------------------------------------------------------------------- /scripts/apex_install.sql: -------------------------------------------------------------------------------- 1 | -- Installs an APEX application 2 | -- 3 | -- Parameters: 4 | -- 1: Schema to install into 5 | -- 2: Workspace to install into 6 | -- 3: Application ID to run 7 | 8 | set serveroutput on size unlimited; 9 | set timing off; 10 | set define on 11 | 12 | declare 13 | begin 14 | 15 | apex_application_install.set_application_id(&3.); 16 | apex_application_install.set_schema(upper('&1.')); 17 | apex_application_install.set_workspace(upper('&2.')); 18 | -- apex_application_install.generate_offset; [IGST-37] https://insumsolutions.atlassian.net/browse/IGST-37 19 | 20 | end; 21 | / 22 | 23 | @../apex/f&3..sql -------------------------------------------------------------------------------- /scripts/helper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Global variables 4 | # Find the current path this script is in 5 | # This needs to be run outside of any functions as $0 has different meaning in a function 6 | # If this script is being called from using "source ..." then ${BASH_SOURCE[0]} evaluates to null Use $0 instead 7 | if [ -z "${BASH_SOURCE[0]}" ] ; then 8 | SCRIPT_DIR="$( cd "$( dirname "$0" )" >/dev/null 2>&1 && pwd )" 9 | else 10 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 11 | fi 12 | # Root folder in project directory 13 | PROJECT_DIR="$(dirname "$SCRIPT_DIR")" 14 | # echo "SCRIPT_DIR: $SCRIPT_DIR" 15 | # echo "PROJECT_DIR: $PROJECT_DIR" 16 | 17 | 18 | # Load colors 19 | # To use colors: 20 | # echo -e "${COLOR_RED}this is red${COLOR_RESET}" 21 | load_colors(){ 22 | # Colors for bash. See: http://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux 23 | COLOR_LIGHT_GREEN='\033[0;92m' 24 | COLOR_ORANGE='\033[0;33m' 25 | COLOR_RED='\033[0;31m' 26 | COLOR_RESET='\033[0m' # No Color 27 | 28 | FONT_BOLD='\033[1m' 29 | FONT_RESET='\033[22m' 30 | } # load_colors 31 | 32 | # Load the config file stored in scripts/config 33 | load_config(){ 34 | USER_CONFIG_FILE=$PROJECT_DIR/scripts/user-config.sh 35 | PROJECT_CONFIG_FILE=$PROJECT_DIR/scripts/project-config.sh 36 | # echo "USER_CONFIG_FILE: $USER_CONFIG_FILE" 37 | # echo "PROJECT_CONFIG_FILE: $PROJECT_CONFIG_FILE" 38 | 39 | if [[ ! -f $USER_CONFIG_FILE ]] ; then 40 | echo -e "${COLOR_RED}Warning: database connection configuration is missing ${COLOR_RESET}" 41 | echo -e "${FONT_BOLD}Modify $USER_CONFIG_FILE${FONT_RESET} with your DB connection string and APEX applications" 42 | cat > $USER_CONFIG_FILE < $PROJECT_DIR/release/code/_run_code.sql 177 | echo "-- This file is automatically executed from the /release/_release.sql file" >>$PROJECT_DIR/release/code/_run_code.sql 178 | echo "-- \n-- Ex: @code/issue-123.sql \n" >>$PROJECT_DIR/release/code/_run_code.sql 179 | fi 180 | } # reset_release 181 | 182 | 183 | 184 | # List all files in directory 185 | # 186 | # Parameters 187 | # $1: Folder (relative to root project folder) to list all the files from. Ex: views 188 | # $2: File (relative to root project folder) to store the list of files in: ex: release/all_views.sql 189 | # $3: Comma delimited list of file extensions to search for. Ex: pks,pkb. Default sql 190 | list_all_files(){ 191 | 192 | local FOLDER_NAME=$1 193 | local OUTPUT_FILE=$2 194 | local FILE_EXTENSION_ARR=$3 195 | 196 | local RUN_HELP="get_all_files 197 | The following example will list all the .sql files in ./views and reference them in release/all_views.sql 198 | 199 | get_all_files views release/all_views.sql sql 200 | 201 | For packages it's useful to list the extensions in order as they should be compiled. Ex: pks,pkb to compile spec before body 202 | " 203 | 204 | # Validation 205 | if [ -z "$FOLDER_NAME" ]; then 206 | echo "${COLOR_RED}Error: ${COLOR_RESET} Missing folder name" 207 | echo "\n$RUN_HELP" 208 | return 1 209 | elif [ -z "$OUTPUT_FILE" ]; then 210 | echo "${COLOR_RED}Error: ${COLOR_RESET} Missing output file" 211 | echo "\n$RUN_HELP" 212 | return 1 213 | fi 214 | 215 | # Defaulting extensions 216 | if [ -z "$FILE_EXTENSION_ARR" ]; then 217 | FILE_EXTENSION_ARR="sql" 218 | fi 219 | 220 | echo "-- GENERATED from build/build.sh DO NOT modify this file directly as all changes will be overwritten upon next build" > $PROJECT_DIR/$OUTPUT_FILE 221 | echo "-- Automated listing for $FOLDER_NAME" >> $PROJECT_DIR/$OUTPUT_FILE 222 | for FILE_EXT in $(echo $FILE_EXTENSION_ARR | sed "s/,/ /g"); do 223 | 224 | echo "Listing files in: $PROJECT_DIR/$FOLDER_NAME extension: $FILE_EXT" 225 | for file in $PROJECT_DIR//$FOLDER_NAME//*.$FILE_EXT; do 226 | # for file in $PROJECT_DIR/$FOLDER_NAME/*.sql; do 227 | # for file in $(ls $PROJECT_DIR/$FOLDER_NAME/*.sql ); do 228 | echo "prompt @../$FOLDER_NAME/${file##*/}" >> $PROJECT_DIR/$OUTPUT_FILE 229 | echo "@../$FOLDER_NAME/${file##*/}" >> $PROJECT_DIR/$OUTPUT_FILE 230 | done 231 | done 232 | 233 | } # list_all_files 234 | 235 | 236 | 237 | # Builds files required for the release 238 | # Should be called in build/build.sh 239 | # 240 | # Issue: #28 241 | gen_release_sql(){ 242 | local loc_env_vars="$PROJECT_DIR/release/load_env_vars.sql" 243 | local loc_apex_install_all="$PROJECT_DIR/release/all_apex.sql" 244 | # Build helper sql file to load specific env variables into SQL*Plus session 245 | echo "-- GENERATED from build/build.sh DO NOT modify this file directly as all changes will be overwritten upon next build\n\n" > $loc_env_vars 246 | echo "define env_schema_name=$SCHEMA_NAME" >> $loc_env_vars 247 | echo "define env_apex_app_ids=$APEX_APP_IDS" >> $loc_env_vars 248 | echo "define env_apex_workspace=$APEX_WORKSPACE" >> $loc_env_vars 249 | echo "" >> $loc_env_vars 250 | echo " 251 | prompt ENV variables 252 | select 253 | '&env_schema_name.' env_schema_name, 254 | '&env_apex_app_ids.' env_apex_app_ids, 255 | '&env_apex_workspace.' env_apex_workspace 256 | from dual; 257 | 258 | " >> $loc_env_vars 259 | 260 | # Build helper file to install all APEX applications 261 | echo "-- GENERATED from build/build.sh DO NOT modify this file." > $loc_apex_install_all 262 | echo "prompt *** APEX Installation ***" >> $loc_apex_install_all 263 | for APEX_APP_ID in $(echo $APEX_APP_IDS | sed "s/,/ /g"); do 264 | echo "prompt *** App: $APEX_APP_ID ***" >> $loc_apex_install_all 265 | echo "@../scripts/apex_install.sql $SCHEMA_NAME $APEX_WORKSPACE $APEX_APP_ID" >> $loc_apex_install_all 266 | done 267 | } #gen_release_sql 268 | 269 | 270 | 271 | # #36 Create new files quickly based on template files 272 | # 273 | # See scripts/project-config.sh on how to define the various object types 274 | # 275 | # Actions: 276 | # - Create a new file in defined destination folder 277 | # - Based on template 278 | # - Replace all referneces to CHANGEME with the object name 279 | # 280 | # Parameters 281 | # $1 Object type 282 | # $2 Object Name 283 | gen_object(){ 284 | # Parameters 285 | local p_object_type=$1 286 | local p_object_name=$2 287 | 288 | # Loop variables 289 | local object_type_arr 290 | local object_type 291 | local object_template 292 | local object_dest_folder 293 | local object_dest_file 294 | 295 | # OBJECT_FILE_TEMPLATE_MAP is defined in scripts/project-config.sh 296 | for object_type in $(echo $OBJECT_FILE_TEMPLATE_MAP | sed "s/,/ /g"); do 297 | 298 | object_type_arr=(`echo "$object_type" | sed 's/:/ /g'`) 299 | 300 | # In bash arrays start at 0 whereas in zsh they start at 1 301 | # Only way to make array reference compatible with both is to specify the offset and length 302 | # See: https://stackoverflow.com/questions/50427449/behavior-of-arrays-in-bash-scripting-and-zsh-shell-start-index-0-or-1/50433774 303 | object_type=${object_type_arr[@]:0:1} 304 | object_template=${object_type_arr[@]:1:1} 305 | object_file_exts=${object_type_arr[@]:2:1} 306 | object_dest_folder=${object_type_arr[@]:3:1} 307 | 308 | if [[ "$p_object_type" == "$object_type" ]]; then 309 | 310 | for file_ext in $(echo $object_file_exts | sed "s/;/ /g"); do 311 | object_dest_file=$PROJECT_DIR/$object_dest_folder/$p_object_name.$file_ext 312 | 313 | if [[ -f $object_dest_file ]]; then 314 | echo "${COLOR_ORANGE}File already exists:${COLOR_RESET} $object_dest_file" 315 | else 316 | cp $object_template.$file_ext $object_dest_file 317 | sed -i-bak "s/CHANGEME/$p_object_name/g" $object_dest_file 318 | # Remove backup versin of file 319 | rm $object_dest_file-bak 320 | echo "Created: $object_dest_file" 321 | 322 | # Open file in code 323 | code $object_dest_file 324 | fi 325 | done 326 | 327 | break # No longer need to loop through other definitions 328 | fi 329 | 330 | done # OBJECT_FILE_TEMPLATE_MAP 331 | 332 | } # gen_object 333 | 334 | 335 | # Merge a SQL file into one file 336 | # Copied and modified from: https://github.com/insum-labs/conference-manager/blob/master/release/build_release_script.sh 337 | # 338 | # This script received a .sql file as input and will create an output file 339 | # that can be processed by SQL Workshop on apex.oracle.com 340 | # This means that single commands can be executed as they are (for example 341 | # alter, create table, update, inserts, etc..). 342 | # When a script is found with the form @../file, ie: 343 | # @../views/ks_users_v.sql 344 | # It will be "expanded" into the output file (defined by OUT_FILE) 345 | # 346 | # Note this will recursively expand files. 347 | # For example if calling "merge_sql_files _release.sql merged_release.sql" and: 348 | # _release.sql references _all_packages.sql 349 | # and _all_packages.sql references pkg_emp.pks 350 | # Then both _all_packages.sql and pkg_emp.pks will be exampled at the the points they were referenced in each file 351 | # 352 | # Issue: #42 353 | # Example: 354 | # source helper.sh 355 | # merge_sql_files all_packages.sql merged_all_packages.sql 356 | # 357 | # Parameters 358 | # $1 From/In File 359 | # $2 To/Out File 360 | merge_sql_files(){ 361 | local IN_FILE=$1 362 | local OUT_FILE=$2 363 | 364 | # Logging function. Calling "logger" so there's no name conflict as "log" is a function in bash 365 | logger() { 366 | echo "`date`: $1" 367 | } # logger 368 | 369 | #***************************************************************************** 370 | # Expand Script Lines or output regular lines 371 | # Parameters 372 | # $1 FILE_LINE: This is the current line from the $IN_FILE 373 | #****************************************************************************** 374 | process_line (){ 375 | local FILE_LINE=$1 376 | 377 | # logger "Is $1 a script?" 378 | # ${1:1} https://stackoverflow.com/questions/30197247/using-11-in-bash 379 | # In this case it's removing the "@" from each line in the script 380 | 381 | if [ -f "${FILE_LINE:1}" ] 382 | then 383 | logger "Expanding file: ${FILE_LINE:1}" 384 | echo "-- $FILE_LINE" >> $OUT_FILE 385 | 386 | # Recursively open each file as they themselves may reference other files 387 | process_file ${FILE_LINE:1} 388 | 389 | # Print blank lines 390 | echo >> $OUT_FILE 391 | echo >> $OUT_FILE 392 | else 393 | echo "$line" >> $OUT_FILE 394 | fi 395 | 396 | } # process_line 397 | 398 | 399 | # Will loop over a file and process each line 400 | # 401 | # Note: process_line will recursively call this function 402 | # 403 | # Parameters 404 | # $1 file_name 405 | process_file(){ 406 | echo "Processing: $file_name" 407 | local file_name=$1 408 | 409 | while IFS='' read -r line || [[ -n "$line" ]]; do 410 | process_line $line 411 | done < "$file_name" 412 | } 413 | 414 | 415 | logger "Procesing $IN_FILE into $OUT_FILE" 416 | 417 | echo "-- =============================================================================" > $OUT_FILE 418 | echo "-- ========================== Full $IN_FILE file" >> $OUT_FILE 419 | echo "-- =============================================================================" >> $OUT_FILE 420 | echo -n >> $OUT_FILE 421 | 422 | # Start merging the original file which will recursively find other files 423 | process_file $IN_FILE 424 | } # merge_sql_files 425 | 426 | 427 | # Initialize 428 | init(){ 429 | local PROJECT_DIR_FOLDER_NAME=$(basename $PROJECT_DIR) 430 | local VSCODE_TASK_FILE=$PROJECT_DIR/.vscode/tasks.json 431 | 432 | 433 | # #36 Change the VSCode Labels 434 | # See: https://unix.stackexchange.com/questions/13711/differences-between-sed-on-mac-osx-and-other-standard-sed/131940#131940 435 | sed -i-bak "s/CHANGEME_TASKLABEL/$PROJECT_DIR_FOLDER_NAME/g" $VSCODE_TASK_FILE 436 | # Remove backup versin of file 437 | rm $VSCODE_TASK_FILE-bak 438 | 439 | 440 | # Initializing Helper 441 | load_colors 442 | load_config 443 | verify_config 444 | } 445 | 446 | init -------------------------------------------------------------------------------- /scripts/project-config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Name of Schema 4 | SCHEMA_NAME=CHANGE_ME 5 | # Name of default workspace that applications are associated with 6 | APEX_WORKSPACE=CHANGE_ME 7 | # Comma delimited list of APEX Applications to export. Ex: 100,200 8 | APEX_APP_IDS=CHANGE_ME 9 | 10 | 11 | # File extensions 12 | # Will be used throughought the scripts to generate lists of packages, views, etc from the filesystem 13 | EXT_PACKAGE_SPEC=pks 14 | EXT_PACKAGE_BODY=pkb 15 | EXT_VIEW=sql 16 | 17 | 18 | # File Mappings 19 | # This will be used in VSCode to allow for quick generate of a given file based on template data 20 | # Format: 21 | # ::: 22 | # 23 | # Definitions: 24 | # - name: Name that will be mapped to VSCode task 25 | # - template file: Template file prefix to use (no extension) 26 | # - file extensions: ";" delimited list of file extensions to reference each template file 27 | # - destination directory: where to store the new file 28 | OBJECT_FILE_TEMPLATE_MAP="" 29 | OBJECT_FILE_TEMPLATE_MAP="$OBJECT_FILE_TEMPLATE_MAP,package:templates/template_pkg:$EXT_PACKAGE_SPEC;$EXT_PACKAGE_BODY:packages" 30 | OBJECT_FILE_TEMPLATE_MAP="$OBJECT_FILE_TEMPLATE_MAP,view:templates/template_view:$EXT_VIEW:views" 31 | OBJECT_FILE_TEMPLATE_MAP="$OBJECT_FILE_TEMPLATE_MAP,data_array:templates/template_data_array:sql:data" 32 | OBJECT_FILE_TEMPLATE_MAP="$OBJECT_FILE_TEMPLATE_MAP,data_json:templates/template_data_json:sql:data" -------------------------------------------------------------------------------- /synonyms/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/synonyms/.gitkeep -------------------------------------------------------------------------------- /templates/README.md: -------------------------------------------------------------------------------- 1 | # Project Code Templates 2 | 3 | This folder contains code templates for use for your project. Some starter templates are provided and should be adjusted for each project. 4 | 5 | Most of the templates have a keyword `CHANGEME` in them that can easily be replaced with the name of the object being created. [Vistual Studio Code](https://code.visualstudio.com/)(VSCode) has a [`multi-cursor`](https://code.visualstudio.com/docs/editor/codebasics#_multiple-selections-multicursor) selection shortcut that will allow for quick text replacement. 6 | 7 | If using VSCode you may want to move these templates into the [`.vscode`](../.vscode) folder and setup project based [snippets](https://code.visualstudio.com/docs/editor/userdefinedsnippets) for your team to use. See [Project snippet scope](https://code.visualstudio.com/docs/editor/userdefinedsnippets) for more info. 8 | 9 | 10 | File | Description 11 | --- | --- 12 | `template_data.sql` | For re-runnable data scripts (usually for lookup tables). 13 | `template_pkg.pkb` | Package body and procedure/function template 14 | `template_pkg.pks` | Package spec 15 | `template_table.sql` | Table creation. *Note: it's expected this file will be changed for each project as naming standards differ* -------------------------------------------------------------------------------- /templates/template_data_array.sql: -------------------------------------------------------------------------------- 1 | set define off; 2 | 3 | PROMPT CHANGE_ME data 4 | 5 | declare 6 | type rec_data is varray(3) of varchar2(4000); 7 | type tab_data is table of rec_data index by pls_integer; 8 | l_data tab_data; 9 | l_row CHANGE_ME%rowtype; 10 | begin 11 | 12 | -- Column order: 13 | -- CHANGEME 14 | -- 1: CHANGE_ME_code 15 | -- 2: CHANGE_ME_name 16 | -- 3: CHANGE_ME_seq 17 | 18 | -- Template 19 | -- l_data(l_data.count + 1) := rec_data('CHANGEME', 'CHANGEME', 1, ...); 20 | 21 | 22 | for i in 1..l_data.count loop 23 | l_row.CHANGE_ME_code := l_data(i)(1); 24 | l_row.CHANGE_ME_name := l_data(i)(2); 25 | l_row.CHANGE_ME_seq := l_data(i)(3); 26 | 27 | merge into CHANGE_ME dest 28 | using ( 29 | select 30 | l_row.CHANGE_ME_code CHANGE_ME_code 31 | from dual 32 | ) src 33 | on (1=1 34 | and dest.CHANGE_ME_code = src.CHANGE_ME_code 35 | ) 36 | when matched then 37 | update 38 | set 39 | -- Don't update the value as it's probably a key/secure value 40 | -- Deletions are handled above 41 | dest.CHANGE_ME_name = l_row.CHANGE_ME_name, 42 | dest.CHANGE_ME_seq = l_row.CHANGE_ME_seq 43 | when not matched then 44 | insert ( 45 | CHANGE_ME_code, 46 | CHANGE_ME_name, 47 | CHANGE_ME_seq, 48 | created_on, 49 | created_by) 50 | values( 51 | l_row.CHANGE_ME_code, 52 | l_row.CHANGE_ME_name, 53 | l_row.CHANGE_ME_seq, 54 | current_timestamp, 55 | 'SYSTEM') 56 | ; 57 | end loop; 58 | 59 | end; 60 | / 61 | -------------------------------------------------------------------------------- /templates/template_data_json.sql: -------------------------------------------------------------------------------- 1 | set define off; 2 | 3 | PROMPT CHANGE_ME data 4 | 5 | declare 6 | l_json clob; 7 | begin 8 | 9 | -- Load data in JSON object 10 | l_json := q'! 11 | [ 12 | { 13 | "CHANGE_ME_code": "CHANGEME", 14 | "CHANGE_ME_name": "CHANGEME", 15 | "CHANGE_ME_seq": 1 16 | } 17 | ] 18 | !'; 19 | 20 | 21 | for data in ( 22 | select * 23 | from json_table(l_json, '$[*]' columns 24 | CHANGE_ME_code varchar2(4000) path '$.CHANGE_ME_code', 25 | CHANGE_ME_name varchar2(4000) path '$.CHANGE_ME_name', 26 | CHANGE_ME_seq number path '$.CHANGE_ME_seq' 27 | ) 28 | ) loop 29 | 30 | -- Note: looping over each entry to make it easier to debug in case one entry is invalid 31 | -- If performance is an issue can move the loop's select statement into the merge statement 32 | merge into CHANGE_ME dest 33 | using ( 34 | select 35 | data.CHANGE_ME_code CHANGE_ME_code 36 | from dual 37 | ) src 38 | on (1=1 39 | and dest.CHANGE_ME_code = src.CHANGE_ME_code 40 | ) 41 | when matched then 42 | update 43 | set 44 | -- Don't update the value as it's probably a key/secure value 45 | -- Deletions are handled above 46 | dest.CHANGE_ME_name = data.CHANGE_ME_name, 47 | dest.CHANGE_ME_seq = data.CHANGE_ME_seq 48 | when not matched then 49 | insert ( 50 | CHANGE_ME_code, 51 | CHANGE_ME_name, 52 | CHANGE_ME_seq, 53 | created_on, 54 | created_by) 55 | values( 56 | data.CHANGE_ME_code, 57 | data.CHANGE_ME_name, 58 | data.CHANGE_ME_seq, 59 | current_timestamp, 60 | 'SYSTEM') 61 | ; 62 | end loop; 63 | 64 | end; 65 | / 66 | -------------------------------------------------------------------------------- /templates/template_pkg.pkb: -------------------------------------------------------------------------------- 1 | create or replace package body CHANGEME as 2 | 3 | gc_scope_prefix constant varchar2(31) := lower($$plsql_unit) || '.'; 4 | 5 | 6 | /** 7 | * Description 8 | * 9 | * 10 | * @example 11 | * 12 | * @issue TODO 13 | * 14 | * @author TODO 15 | * @created TODO 16 | * @param TODO 17 | * @return 18 | */ 19 | procedure P_CHANGEME( 20 | p_param1_todo in varchar2) 21 | as 22 | l_scope logger_logs.scope%type := gc_scope_prefix || 'P_CHANGEME'; 23 | l_params logger.tab_param; 24 | 25 | begin 26 | logger.append_param(l_params, 'p_param1_todo', p_param1_todo); 27 | logger.log('START', l_scope, null, l_params); 28 | 29 | ... 30 | -- All calls to logger should pass in the scope 31 | ... 32 | 33 | logger.log('END', l_scope); 34 | exception 35 | when others then 36 | logger.log_error('Unhandled Exception', l_scope, null, l_params); 37 | raise; 38 | end P_CHANGEME; 39 | 40 | 41 | end CHANGEME; 42 | / 43 | -------------------------------------------------------------------------------- /templates/template_pkg.pks: -------------------------------------------------------------------------------- 1 | create or replace package CHANGEME as 2 | 3 | 4 | end CHANGEME; 5 | / 6 | -------------------------------------------------------------------------------- /templates/template_table.sql: -------------------------------------------------------------------------------- 1 | prompt change_me 2 | create table change_me ( 3 | change_me_id number generated always as identity not null, 4 | change_me_code varchar2(30) not null, 5 | change_me_name varchar2(30) not null, 6 | change_me_seq number not null, 7 | created_on date default sysdate not null, 8 | created_by varchar2(255 byte) default 9 | coalesce( 10 | sys_context('APEX$SESSION','app_user'), 11 | regexp_substr(sys_context('userenv','client_identifier'),'^[^:]*'), 12 | sys_context('userenv','session_user') 13 | ) 14 | not null 15 | , 16 | updated_on date, 17 | updated_by varchar2(255 byte) 18 | ); 19 | 20 | comment on table change_me is 'CHANGEME'; 21 | comment on column change_me.CHANGEME is 'CHANGEME'; 22 | 23 | alter table change_me add constraint change_me_pk primary key (change_me_id); 24 | 25 | alter table change_me add constraint change_me_uk1 unique(change_me_code); 26 | 27 | alter table change_me add constraint change_me_ck1 check(change_me_code = trim(upper(change_me_code))); 28 | alter table change_me add constraint change_me_ck2 check(change_me_seq = trunc(change_me_seq)); 29 | 30 | alter table change_me add constraint change_me_fk1 foreign key (changeme) references changeme_dest(changeme); 31 | 32 | create index change_me_idx1 on change_me(changeme); 33 | -------------------------------------------------------------------------------- /templates/template_view.sql: -------------------------------------------------------------------------------- 1 | create or replace force view CHANGEME as 2 | 3 | ; -------------------------------------------------------------------------------- /triggers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/triggers/.gitkeep -------------------------------------------------------------------------------- /views/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/views/.gitkeep -------------------------------------------------------------------------------- /www/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/www/.gitkeep -------------------------------------------------------------------------------- /www/src/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/www/src/.gitkeep -------------------------------------------------------------------------------- /www/src/css/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/www/src/css/.gitkeep -------------------------------------------------------------------------------- /www/src/img/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/www/src/img/.gitkeep -------------------------------------------------------------------------------- /www/src/js/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/www/src/js/.gitkeep -------------------------------------------------------------------------------- /www/src/lib/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insum-labs/starter-project-template/0a7e0c7fc48f1178bc422813289d7547fed865c7/www/src/lib/.gitkeep --------------------------------------------------------------------------------